From 4d30c611ba90cac0603779e160ffc5a800c9016f Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Wed, 1 Apr 2026 22:06:24 -0300 Subject: [PATCH 01/12] First version parser: Only info document Arazzo. Not Working --- .../arazzo/models/ArazzoSpecifications.kt | 11 +++ .../problem/rest/arazzo/models/Components.kt | 11 +++ .../problem/rest/arazzo/models/Criterion.kt | 11 +++ .../rest/arazzo/models/CriterionExpression.kt | 7 ++ .../rest/arazzo/models/FailureAction.kt | 12 ++++ .../problem/rest/arazzo/models/InfoArazzo.kt | 9 +++ .../problem/rest/arazzo/models/Parameter.kt | 11 +++ .../rest/arazzo/models/PayloadReplacement.kt | 9 +++ .../problem/rest/arazzo/models/RequestBody.kt | 8 +++ .../problem/rest/arazzo/models/Reusable.kt | 7 ++ .../rest/arazzo/models/SourceDescription.kt | 8 +++ .../core/problem/rest/arazzo/models/Step.kt | 18 +++++ .../rest/arazzo/models/SuccessAction.kt | 10 +++ .../problem/rest/arazzo/models/Workflow.kt | 17 +++++ .../rest/arazzo/parser/ArazzoParser.kt | 4 ++ .../core/problem/rest/schema/ArazzoAccess.kt | 71 +++++++++++++++++++ 16 files changed, 224 insertions(+) create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Criterion.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionExpression.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureAction.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/InfoArazzo.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Parameter.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/PayloadReplacement.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RequestBody.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Reusable.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SourceDescription.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessAction.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt new file mode 100644 index 0000000000..2e1418b681 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt @@ -0,0 +1,11 @@ +package org.evomaster.core.problem.rest.arazzo.models + +class ArazzoSpecifications( + val arazzo: String, + val info: InfoArazzo, + val sourceDescriptions: MutableList, + val workflows: MutableList, + val components: Components? +) { + +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt new file mode 100644 index 0000000000..04ccf0a528 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt @@ -0,0 +1,11 @@ +package org.evomaster.core.problem.rest.arazzo.models + +import com.fasterxml.jackson.databind.JsonNode + +class Components( + val inputs: Map?, + val parameters: Map?, + val successActions: Map?, + val failureActions: Map? +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Criterion.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Criterion.kt new file mode 100644 index 0000000000..b9bf12c6e6 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Criterion.kt @@ -0,0 +1,11 @@ +package org.evomaster.core.problem.rest.arazzo.models + +import com.fasterxml.jackson.databind.JsonNode + +class Criterion( + val context: String?, + val condition: String, + val type: JsonNode? +) { + +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionExpression.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionExpression.kt new file mode 100644 index 0000000000..2bdaba7fb2 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionExpression.kt @@ -0,0 +1,7 @@ +package org.evomaster.core.problem.rest.arazzo.models + +class CriterionExpression( + val type: String, + val version: String +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureAction.kt new file mode 100644 index 0000000000..0ccbe9cd71 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureAction.kt @@ -0,0 +1,12 @@ +package org.evomaster.core.problem.rest.arazzo.models + +class FailureAction( + val name: String, + val type: String, + val workflowId: String?, + val stepId: String?, + val retryAfter: Number?, + val retryLimit: Integer?, + val criteria: Criterion +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/InfoArazzo.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/InfoArazzo.kt new file mode 100644 index 0000000000..2da94e10b0 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/InfoArazzo.kt @@ -0,0 +1,9 @@ +package org.evomaster.core.problem.rest.arazzo.models + +class InfoArazzo( + val title: String, + val summary: String?, + val description: String?, + val version: String +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Parameter.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Parameter.kt new file mode 100644 index 0000000000..5cae69e297 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Parameter.kt @@ -0,0 +1,11 @@ +package org.evomaster.core.problem.rest.arazzo.models + +import com.fasterxml.jackson.annotation.JsonProperty + +class Parameter( + val name: String, + @JsonProperty("in") + val location: String?, + val value: String +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/PayloadReplacement.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/PayloadReplacement.kt new file mode 100644 index 0000000000..38fe828999 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/PayloadReplacement.kt @@ -0,0 +1,9 @@ +package org.evomaster.core.problem.rest.arazzo.models + +import com.fasterxml.jackson.databind.JsonNode + +class PayloadReplacement( + val target: String, + val value: JsonNode +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RequestBody.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RequestBody.kt new file mode 100644 index 0000000000..7bfad2152a --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RequestBody.kt @@ -0,0 +1,8 @@ +package org.evomaster.core.problem.rest.arazzo.models + +class RequestBody( + val contentType: String?, + val payload: Any?, + val replacements: List? +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Reusable.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Reusable.kt new file mode 100644 index 0000000000..7373ac115c --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Reusable.kt @@ -0,0 +1,7 @@ +package org.evomaster.core.problem.rest.arazzo.models + +class Reusable( + val reference: String, + val value: String? +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SourceDescription.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SourceDescription.kt new file mode 100644 index 0000000000..3ecb428357 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SourceDescription.kt @@ -0,0 +1,8 @@ +package org.evomaster.core.problem.rest.arazzo.models + +class SourceDescription( + val name: String, + val url: String, + val type: String? +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt new file mode 100644 index 0000000000..799f3cf564 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt @@ -0,0 +1,18 @@ +package org.evomaster.core.problem.rest.arazzo.models + +import com.fasterxml.jackson.databind.JsonNode + +class Step( + val description: String?, + val stepId: String, + val operationId: String?, + val operationPath: String?, + val workflowId: String?, + val parameters: List?, + val requestBody: RequestBody?, + val successCriteria: List?, + val onSuccess: List?, + val onFailure: List?, + val outputs: Map? +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessAction.kt new file mode 100644 index 0000000000..6ec4d5ca0e --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessAction.kt @@ -0,0 +1,10 @@ +package org.evomaster.core.problem.rest.arazzo.models + +class SuccessAction( + val name: String, + val type: String, + val workflowId: String?, + val stepId: String?, + val criteria: List +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt new file mode 100644 index 0000000000..6a551e663d --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt @@ -0,0 +1,17 @@ +package org.evomaster.core.problem.rest.arazzo.models + +import com.fasterxml.jackson.databind.JsonNode + +class Workflow( + val workflowId: String, + val summary: String?, + val description: String?, + val inputs: String?, + val dependsOn: List?, + val steps: List, + val successActions: List?, + val failureActions: List?, + val outputs: Map?, + val parameters: List? +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt new file mode 100644 index 0000000000..c54576b28e --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt @@ -0,0 +1,4 @@ +package org.evomaster.core.problem.rest.arazzo.parser + +object ArazzoParser { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt new file mode 100644 index 0000000000..ddc38721d6 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt @@ -0,0 +1,71 @@ +package org.evomaster.core.problem.rest.schema + +import org.evomaster.core.problem.rest.schema.SchemaArazzo +import org.evomaster.core.remote.SutProblemException +import org.slf4j.LoggerFactory +import java.net.URI +import java.nio.file.Files +import java.nio.file.Paths +import kotlin.io.readText +import kotlin.jvm.java +import kotlin.text.startsWith + +object ArazzoAccess { + + private val log = LoggerFactory.getLogger(ArazzoAccess::class.java) + + fun parseArazzo(schemaText: String, sourceLocation: SchemaLocation): SchemaArazzo { + + //Parser + + //Schema + + return SchemaArazzo(schemaText, schema, sourceLocation) + } + + fun getArazzoFromLocation( + arazzoLocation: String + ): SchemaArazzo { + + //could be either JSON or YAML + val data: String + val location: SchemaLocation + + data = readFromDisk(arazzoLocation); + location = SchemaLocation(arazzoLocation, SchemaLocationType.LOCAL) + + return parseArazzo(data, location); + } + + private fun readFromDisk(arazzoLocation: String) : String { + // file schema + val fileScheme = "file:" + + // create paths + val path = try { + if (arazzoLocation.startsWith(fileScheme, true)) { + Paths.get(URI.create(arazzoLocation)) + } + else { + Paths.get(arazzoLocation) + } + } + // Exception is thrown if the path is not valid + catch (e: Exception) { + // state the exception with the error message + throw SutProblemException( + "The file path provided for the Arazzo Schema $arazzoLocation" + + " ended up with the following error: " + e.message + ) + } + + // If the path is valid but the file does not exist, an exception is thrown + if (!Files.exists(path)) { + throw SutProblemException("The provided Arazzo file does not exist: $arazzoLocation") + } + + // return the schema text + return path.toFile().readText() + } + +} \ No newline at end of file From 072562e0ac6524c320d8d264ed42c11a8567bedf Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Thu, 2 Apr 2026 20:54:25 -0300 Subject: [PATCH 02/12] Read document arazzo --- .../api/dto/problem/RestProblemDto.java | 5 + core/pom.xml | 4 + .../problem/rest/arazzo/models/Workflow.kt | 2 +- .../rest/arazzo/parser/ArazzoParser.kt | 35 ++++ .../core/problem/rest/schema/ArazzoAccess.kt | 7 +- .../core/problem/rest/schema/SchemaArazzo.kt | 21 +++ .../service/sampler/AbstractRestSampler.kt | 6 + .../rest/schema/ArazzoLocalURLIssueTest.kt | 118 +++++++++++++ .../src/test/resources/arazzo/arazzo_pet.yaml | 160 ++++++++++++++++++ 9 files changed, 353 insertions(+), 5 deletions(-) create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt create mode 100644 core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt create mode 100644 core/src/test/resources/arazzo/arazzo_pet.yaml diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/RestProblemDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/RestProblemDto.java index fba150597a..34cb3170f5 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/RestProblemDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/RestProblemDto.java @@ -30,4 +30,9 @@ public class RestProblemDto extends ProblemInfoDto{ public List derivedParams; + + /** + * The full URL of where the Arazzo Specifications schema can be located. + */ + public String arazzoURL; } diff --git a/core/pom.xml b/core/pom.xml index 4b8365257f..df85c23354 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -201,6 +201,10 @@ com.fasterxml.jackson.dataformat jackson-dataformat-yaml + + com.fasterxml.jackson.module + jackson-module-kotlin + org.antlr antlr4-runtime diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt index 6a551e663d..172fcd1332 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt @@ -6,7 +6,7 @@ class Workflow( val workflowId: String, val summary: String?, val description: String?, - val inputs: String?, + val inputs: JsonNode?, val dependsOn: List?, val steps: List, val successActions: List?, diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt index c54576b28e..013adcdb9e 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt @@ -1,4 +1,39 @@ package org.evomaster.core.problem.rest.arazzo.parser +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import org.evomaster.core.problem.rest.arazzo.models.ArazzoSpecifications +import com.fasterxml.jackson.module.kotlin.readValue +import io.swagger.v3.oas.models.OpenAPI +import org.evomaster.core.problem.rest.schema.SchemaArazzo +import org.evomaster.core.problem.rest.schema.SchemaOpenAPI + object ArazzoParser { + + val jsonMapper = ObjectMapper().findAndRegisterModules() + val yamlMapper = ObjectMapper(YAMLFactory()).findAndRegisterModules() + + fun parserSchemaText(schemaText: String): ArazzoSpecifications { + val schemaTextClean = schemaText.trimStart() + + var arazzoSpecifications: ArazzoSpecifications? + + try { + if (schemaTextClean.startsWith("{")) { + arazzoSpecifications = jsonMapper.readValue(schemaTextClean) + } else { + arazzoSpecifications = yamlMapper.readValue(schemaTextClean) + } + } catch (e: Exception) { + throw IllegalArgumentException("Problems parsing the Arazzo document", e) + } + + return arazzoSpecifications + + } + + fun validateSchema(schemaArazzo: SchemaArazzo, schemaOpenAPI: SchemaOpenAPI) { + + } + } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt index ddc38721d6..b218a58bbe 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt @@ -1,5 +1,6 @@ package org.evomaster.core.problem.rest.schema +import org.evomaster.core.problem.rest.arazzo.parser.ArazzoParser import org.evomaster.core.problem.rest.schema.SchemaArazzo import org.evomaster.core.remote.SutProblemException import org.slf4j.LoggerFactory @@ -16,11 +17,9 @@ object ArazzoAccess { fun parseArazzo(schemaText: String, sourceLocation: SchemaLocation): SchemaArazzo { - //Parser + val schemaParsed = ArazzoParser.parserSchemaText(schemaText) + return SchemaArazzo(schemaText, schemaParsed, sourceLocation) - //Schema - - return SchemaArazzo(schemaText, schema, sourceLocation) } fun getArazzoFromLocation( diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt new file mode 100644 index 0000000000..8e742f5b04 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt @@ -0,0 +1,21 @@ +package org.evomaster.core.problem.rest.schema + +import io.swagger.v3.oas.models.OpenAPI +import org.evomaster.core.problem.rest.arazzo.models.ArazzoSpecifications + +class SchemaArazzo( + /** + * The actual raw value of the schema file, as a string + */ + val schemaRaw: String, + /** + * A parsed schema + */ + val schemaParsed: ArazzoSpecifications, + /** + * information about the location the schema was retrieved from, e.g., + * from file, URL or in memory in our tests. + */ + val sourceLocation: SchemaLocation +) { +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt index 344f2546bb..76103ad0bf 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt @@ -17,6 +17,7 @@ import org.evomaster.core.problem.externalservice.httpws.service.HttpWsExternalS import org.evomaster.core.problem.httpws.HttpWsAction import org.evomaster.core.problem.httpws.service.HttpWsSampler import org.evomaster.core.problem.rest.* +import org.evomaster.core.problem.rest.arazzo.parser.ArazzoParser import org.evomaster.core.problem.rest.builder.RestActionBuilderV3 import org.evomaster.core.problem.rest.builder.RestActionBuilderV3.buildActionBasedOnUrl import org.evomaster.core.problem.rest.data.HttpVerb @@ -24,6 +25,7 @@ import org.evomaster.core.problem.rest.data.RestCallAction import org.evomaster.core.problem.rest.data.RestIndividual import org.evomaster.core.problem.rest.param.HeaderParam import org.evomaster.core.problem.rest.param.QueryParam +import org.evomaster.core.problem.rest.schema.ArazzoAccess import org.evomaster.core.problem.rest.schema.OpenApiAccess import org.evomaster.core.problem.rest.schema.RestSchema import org.evomaster.core.problem.rest.schema.SchemaLocation @@ -118,6 +120,10 @@ abstract class AbstractRestSampler : HttpWsSampler() { schemaHolder = RestSchema(transformed) schemaHolder.validate() + val arazzoURL = problem.arazzoURL + val arazzo = ArazzoAccess.getArazzoFromLocation(arazzoURL) + ArazzoParser.validateSchema(arazzo, transformed) + // The code should never reach this line without a valid swagger. actionCluster.clear() val skip = EndpointFilter.getEndpointsToSkip(config, schemaHolder, infoDto) diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt new file mode 100644 index 0000000000..3bbd916dee --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt @@ -0,0 +1,118 @@ +package org.evomaster.core.problem.rest.schema + +import org.evomaster.core.remote.SutProblemException +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test +import java.util.* + +/* +Testing the local URL issue with OpenAPI, 4 test cases: +1. A local file which exists and the provided URL is valid. (FALTA) +2. A local file does not exist but the provided URL is valid. (FALTA) +3. A local file exists but the provided URL is not valid. (FALTA) +4. A local file does not exist and the provided URL is not valid. (FALTA) +5. A local file which exists but the relative file path is provided. (FALTA) +6. Two different exceptions in Windows and others (e.g., "URI Path Component is empty" is tested here). +7. The swagger is an existing valid json file, but it is not a valid swagger. (FALTA) +8: The swagger is an invalid json file. (FALTA) + */ +class ArazzoLocalURLIssueTest { + + // companion object to set up tests. + companion object { + + // execution path, it can be different from one machine to another + private var executionPath :String = System.getProperty("user.dir") + + // swagger object + private lateinit var swagger: SchemaOpenAPI + + // arazzo object + private lateinit var arazzo: SchemaArazzo + + // swagger test directory to find test files + private lateinit var swaggerTestDirectory: String + + // arazzo test directory to find test files + private lateinit var arazzoTestDirectory: String + + // host operating system + private lateinit var hostOs: String + + @JvmStatic + @BeforeAll + // This is to deal with differences in Windows and Linux paths + fun setSwaggerDirectoryBasedOnOS() { + + // get the name of the current operating system + hostOs = System.getProperty("os.name").lowercase() + + // if the operating system is Windows, then replace \ with / + if (hostOs.contains("win")) { + executionPath = executionPath.replace('\\', '/') + } + + // swagger files for testing + swaggerTestDirectory = "$executionPath/src/test/resources/swagger/urlissue" + + // arazzo files for testing + arazzoTestDirectory = "$executionPath/src/test/resources/arazzo" + } + } + + /* + Test Case 1: A local file which exists and the provided URL is valid + Check that the arazzo is created with a valid URL and an existing file + */ + @Test + fun testExistingFileValidURL() { + // get the current directory, in Mac or Linux, it starts with file:// + // but in Windows, it has to have just one file:/ + val urlArazzoToTest = if (hostOs.contains("win")) { + "file:/$arazzoTestDirectory/arazzo_pet.yaml" + } else { + "file://$arazzoTestDirectory/arazzo_pet.yaml" + } + + // create swagger from URL + val arazzo = ArazzoAccess.getArazzoFromLocation(urlArazzoToTest) + + // a valid swagger is created with 13 endpoints + Assertions.assertTrue(arazzo.schemaParsed.workflows.size == 3) + } + + @Test + fun testNonExistingFileValidURL() { + + } + + @Test + fun testExistingFileInvalidURL() { + + } + + @Test + fun testNonExistingFileInvalidURL() { + + } + + + @Test + fun testRelativeFilePathExistingFile() { + + } + + @Test + fun testFileNameOnlyNonExistingFile() { + + } + + @Test + fun testInvalidSwagger() { + } + + @Test + fun testInvalidJSON() { + } +} \ No newline at end of file diff --git a/core/src/test/resources/arazzo/arazzo_pet.yaml b/core/src/test/resources/arazzo/arazzo_pet.yaml new file mode 100644 index 0000000000..8a573b645e --- /dev/null +++ b/core/src/test/resources/arazzo/arazzo_pet.yaml @@ -0,0 +1,160 @@ +arazzo: 1.0.0 +info: + title: Petstore - Apply Coupons + version: 1.0.0 + description: >- + Illustrates a workflow whereby a client a) finds a pet in the petstore, + b) finds coupons for that pet, and finally + c) orders the pet while applying the coupons from step b. +sourceDescriptions: + - name: pet-coupons + url: ./pet-coupons.openapi.yaml + type: openapi +workflows: + - workflowId: apply-coupon + summary: Apply a coupon to a pet order. + description: >- + This is how you can find a pet, find an applicable coupon, and apply that coupon in your order. + The workflow concludes by outputting the ID of the placed order. + inputs: + $ref: "#/components/inputs/apply_coupon_input" + steps: + - stepId: find-pet + description: Find a pet based on the provided tags. + operationId: findPetsByTags + parameters: + - name: pet_tags + in: query + value: $inputs.my_pet_tags + successCriteria: + - condition: $statusCode == 200 + outputs: + my_pet_id: $response.body#/0/id + # there is some implied selection here - findPetsByTags responds with a list of pets, + # but the client only wants to choose one, and that's what will be provided to the next step. + # not totally sure how to indicate that. + - stepId: find-coupons + description: Find a coupon available for the selected pet. + operationId: getPetCoupons + parameters: + - name: pet_id + in: path + value: $steps.find-pet.outputs.my_pet_id + successCriteria: + - condition: $statusCode == 200 + outputs: + my_coupon_code: $response.body#/couponCode + - stepId: place-order + description: Place an order for the pet, applying the coupon. + workflowId: place-order + parameters: + - name: pet_id + value: $steps.find-pet.outputs.my_pet_id + - name: coupon_code + value: $steps.find-coupons.outputs.my_coupon_code + successCriteria: + - condition: $statusCode == 200 + outputs: + my_order_id: $outputs.workflow_order_id + outputs: + apply_coupon_pet_order_id: $steps.place-order.outputs.my_order_id + - workflowId: buy-available-pet + summary: Buy an available pet if one is available. + description: + This workflow demonstrates a workflow very similar to `apply-coupon`, by intention. + It's meant to indicate how to reuse a step (`place-order`) as well as a parameter (`page`, `pageSize`). + inputs: + $ref: "#/components/inputs/buy_available_pet_input" + steps: + - stepId: find-pet + description: Find a pet that is available for purchase. + operationId: findPetsByStatus + parameters: + - name: status + in: query + value: "available" + - reference: $components.parameters.page + value: 1 + - reference: $components.parameters.pageSize + value: 10 + successCriteria: + - condition: $statusCode == 200 + outputs: + my_pet_id: $response.body#/0/id + - stepId: place-order + description: Place an order for the pet. + workflowId: place-order + parameters: + - name: pet_id + value: $steps.find-pet.outputs.my_pet_id + successCriteria: + - condition: $statusCode == 200 + outputs: + my_order_id: $outputs.workflow_order_id + outputs: + buy_pet_order_id: $steps.place-order.outputs.my_order_id + - workflowId: place-order + summary: Place an order for a pet. + description: + This workflow places an order for a pet. It may be reused by other workflows as the "final step" in a purchase. + inputs: + type: object + properties: + pet_id: + type: integer + format: int64 + description: The ID of the pet to place in the order. + quantity: + type: integer + format: int32 + description: The number of pets to place in the order. + coupon_code: + type: string + description: The coupon code to apply to the order. + steps: + - stepId: place-order + description: Place an order for the pet. + operationId: placeOrder + requestBody: + contentType: application/json + payload: + petId: $inputs.pet_id + quantity: $inputs.quantity + couponCode: $inputs.coupon_code + status: placed + complete: false + successCriteria: + - condition: $statusCode == 200 + outputs: + step_order_id: $response.body#/id + outputs: + workflow_order_id: $steps.place-order.outputs.step_order_id +components: + inputs: + apply_coupon_input: + type: object + properties: + my_pet_tags: + type: array + items: + type: string + description: Desired tags to use when searching for a pet, in CSV format (e.g. "puppy, dalmatian") + store_id: + $ref: "#/components/inputs/store_id" + buy_available_pet_input: + type: object + properties: + store_id: + $ref: "#/components/inputs/store_id" + store_id: + type: string + description: Indicates the domain name of the store where the customer is browsing or buying pets, e.g. "pets.example.com" or "pets.example.co.uk". + parameters: + page: + name: page + in: query + value: 1 + pageSize: + name: pageSize + in: query + value: 100 \ No newline at end of file From f6c0ebb46ec028905874e48ae7f5db61f28d06bd Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Sun, 5 Apr 2026 23:44:15 -0300 Subject: [PATCH 03/12] Validation Arazzo. Incomplete --- .../deserializer/AnyExpressionDeserializer.kt | 27 ++ .../deserializer/CriterionTypeDeserializer.kt | 25 ++ .../FailureReusableDeserializer.kt | 26 ++ .../ParameterReusableDeserializer.kt | 26 ++ .../RuntimeExpressionDeserializer.kt | 22 ++ .../SuccessReusableDeserializer.kt | 26 ++ .../rest/arazzo/models/AnyExpression.kt | 9 + .../arazzo/models/ArazzoSpecifications.kt | 2 +- .../problem/rest/arazzo/models/Criterion.kt | 8 +- .../rest/arazzo/models/CriterionType.kt | 7 + .../rest/arazzo/models/FailureAction.kt | 2 +- .../rest/arazzo/models/FailureReusable.kt | 7 + .../problem/rest/arazzo/models/Parameter.kt | 5 +- .../rest/arazzo/models/ParameterReusable.kt | 7 + .../problem/rest/arazzo/models/Reusable.kt | 6 +- .../rest/arazzo/models/RuntimeExpression.kt | 18 ++ .../core/problem/rest/arazzo/models/Source.kt | 8 + .../rest/arazzo/models/SuccessAction.kt | 2 +- .../rest/arazzo/models/SuccessReusable.kt | 7 + .../problem/rest/arazzo/models/Workflow.kt | 17 +- .../rest/arazzo/parser/ArazzoParser.kt | 9 +- .../rest/arazzo/parser/ArazzoValidator.kt | 294 ++++++++++++++++++ .../rest/arazzo/parser/ExpressionParser.kt | 81 +++++ .../arazzo/parser/SimpleConditionParser.kt | 103 ++++++ .../core/problem/rest/schema/ArazzoAccess.kt | 3 +- .../rest/schema/ArazzoLocalURLIssueTest.kt | 4 +- 26 files changed, 732 insertions(+), 19 deletions(-) create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/AnyExpressionDeserializer.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/CriterionTypeDeserializer.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/FailureReusableDeserializer.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/ParameterReusableDeserializer.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/RuntimeExpressionDeserializer.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/SuccessReusableDeserializer.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/AnyExpression.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionType.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureReusable.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ParameterReusable.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RuntimeExpression.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Source.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessReusable.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ExpressionParser.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/SimpleConditionParser.kt diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/AnyExpressionDeserializer.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/AnyExpressionDeserializer.kt new file mode 100644 index 0000000000..55127d4cd2 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/AnyExpressionDeserializer.kt @@ -0,0 +1,27 @@ +package org.evomaster.core.problem.rest.arazzo.deserializer + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import org.evomaster.core.problem.rest.arazzo.models.AnyExpression +import org.evomaster.core.problem.rest.arazzo.parser.ExpressionParser + +class AnyExpressionDeserializer : JsonDeserializer() { + override fun deserialize( + p0: JsonParser, + p1: DeserializationContext + ): AnyExpression { + val node: JsonNode = p0.codec.readTree(p0) + if (node.isTextual && node.asText().startsWith("$")) { + return try { + val parsedExpression = ExpressionParser.parse(node.asText()) + AnyExpression.Expression(parsedExpression) + } catch (e: Exception) { + throw IllegalArgumentException("Arazzo Parsing Error: Invalid ${node.asText()}", e) + } + } + + return AnyExpression.Constant(node) + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/CriterionTypeDeserializer.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/CriterionTypeDeserializer.kt new file mode 100644 index 0000000000..fe9a9d6443 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/CriterionTypeDeserializer.kt @@ -0,0 +1,25 @@ +package org.evomaster.core.problem.rest.arazzo.deserializer + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import org.evomaster.core.problem.rest.arazzo.models.CriterionExpression +import org.evomaster.core.problem.rest.arazzo.models.CriterionType + +class CriterionTypeDeserializer : JsonDeserializer() { + override fun deserialize( + p0: JsonParser, + p1: DeserializationContext + ): CriterionType { + val node: JsonNode = p0.codec.readTree(p0) + return when { + node.isTextual -> CriterionType.Simple(node.asText()) + node.isObject -> { + val complexObj = p0.codec.treeToValue(node, CriterionExpression::class.java) + CriterionType.Complex(complexObj) + } + else -> throw IllegalArgumentException("Arazzo Parsing Error: Invalid ${node.nodeType}. Expected string or Criterion Expression") + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/FailureReusableDeserializer.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/FailureReusableDeserializer.kt new file mode 100644 index 0000000000..effd75640a --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/FailureReusableDeserializer.kt @@ -0,0 +1,26 @@ +package org.evomaster.core.problem.rest.arazzo.deserializer + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import org.evomaster.core.problem.rest.arazzo.models.FailureAction +import org.evomaster.core.problem.rest.arazzo.models.FailureReusable +import org.evomaster.core.problem.rest.arazzo.models.Reusable + +class FailureReusableDeserializer : JsonDeserializer() { + override fun deserialize( + p0: JsonParser, + p1: DeserializationContext + ): FailureReusable? { + val node: JsonNode = p0.codec.readTree(p0) + + return if (node.has("reference")) { + val reusable = p0.codec.treeToValue(node, Reusable::class.java) + FailureReusable.ReusableObj(reusable) + } else { + val action = p0.codec.treeToValue(node, FailureAction::class.java) + FailureReusable.Inline(action) + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/ParameterReusableDeserializer.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/ParameterReusableDeserializer.kt new file mode 100644 index 0000000000..2ee24242a2 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/ParameterReusableDeserializer.kt @@ -0,0 +1,26 @@ +package org.evomaster.core.problem.rest.arazzo.deserializer + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import org.evomaster.core.problem.rest.arazzo.models.Parameter +import org.evomaster.core.problem.rest.arazzo.models.ParameterReusable +import org.evomaster.core.problem.rest.arazzo.models.Reusable + +class ParameterReusableDeserializer : JsonDeserializer() { + override fun deserialize( + p0: JsonParser, + p1: DeserializationContext + ): ParameterReusable? { + val node: JsonNode = p0.codec.readTree(p0) + + return if (node.has("reference")) { + val reusable = p0.codec.treeToValue(node, Reusable::class.java) + ParameterReusable.ReusableObj(reusable) + } else { + val parameter = p0.codec.treeToValue(node, Parameter::class.java) + ParameterReusable.Inline(parameter) + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/RuntimeExpressionDeserializer.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/RuntimeExpressionDeserializer.kt new file mode 100644 index 0000000000..d74d586024 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/RuntimeExpressionDeserializer.kt @@ -0,0 +1,22 @@ +package org.evomaster.core.problem.rest.arazzo.deserializer + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import org.evomaster.core.problem.rest.arazzo.models.RuntimeExpression +import org.evomaster.core.problem.rest.arazzo.parser.ExpressionParser + +class RuntimeExpressionDeserializer : JsonDeserializer() { + override fun deserialize( + p0: JsonParser, + p1: DeserializationContext + ): RuntimeExpression { + val expressionString = p0.text + + return try { + ExpressionParser.parse(expressionString) + } catch (e: Exception) { + throw IllegalArgumentException("Arazzo Parsing Error: Invalid $expressionString", e) + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/SuccessReusableDeserializer.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/SuccessReusableDeserializer.kt new file mode 100644 index 0000000000..11bdf9de5a --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/SuccessReusableDeserializer.kt @@ -0,0 +1,26 @@ +package org.evomaster.core.problem.rest.arazzo.deserializer + +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonNode +import org.evomaster.core.problem.rest.arazzo.models.Reusable +import org.evomaster.core.problem.rest.arazzo.models.SuccessAction +import org.evomaster.core.problem.rest.arazzo.models.SuccessReusable + +class SuccessReusableDeserializer : JsonDeserializer() { + override fun deserialize( + p0: JsonParser, + p1: DeserializationContext + ): SuccessReusable? { + val node: JsonNode = p0.codec.readTree(p0) + + return if (node.has("reference")) { + val reusable = p0.codec.treeToValue(node, Reusable::class.java) + SuccessReusable.ReusableObj(reusable) + } else { + val action = p0.codec.treeToValue(node, SuccessAction::class.java) + SuccessReusable.Inline(action) + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/AnyExpression.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/AnyExpression.kt new file mode 100644 index 0000000000..9ff00a0d6c --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/AnyExpression.kt @@ -0,0 +1,9 @@ +package org.evomaster.core.problem.rest.arazzo.models + +import com.fasterxml.jackson.databind.JsonNode + +sealed class AnyExpression { + data class Constant(val data: JsonNode) : AnyExpression() + + data class Expression(val expression: RuntimeExpression) : AnyExpression() +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt index 2e1418b681..a3672bcf3d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt @@ -3,7 +3,7 @@ package org.evomaster.core.problem.rest.arazzo.models class ArazzoSpecifications( val arazzo: String, val info: InfoArazzo, - val sourceDescriptions: MutableList, + val sourceDescriptions: List, val workflows: MutableList, val components: Components? ) { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Criterion.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Criterion.kt index b9bf12c6e6..8f86da8de4 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Criterion.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Criterion.kt @@ -1,11 +1,13 @@ package org.evomaster.core.problem.rest.arazzo.models -import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import org.evomaster.core.problem.rest.arazzo.deserializer.CriterionTypeDeserializer class Criterion( - val context: String?, + val context: RuntimeExpression?, val condition: String, - val type: JsonNode? + @JsonDeserialize(using = CriterionTypeDeserializer::class) + val type: CriterionType? ) { } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionType.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionType.kt new file mode 100644 index 0000000000..cd79c7a6fd --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionType.kt @@ -0,0 +1,7 @@ +package org.evomaster.core.problem.rest.arazzo.models + +sealed class CriterionType { + data class Simple(val value: String) : CriterionType() + + data class Complex(val expr: CriterionExpression) : CriterionType() +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureAction.kt index 0ccbe9cd71..cc06eabc77 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureAction.kt @@ -7,6 +7,6 @@ class FailureAction( val stepId: String?, val retryAfter: Number?, val retryLimit: Integer?, - val criteria: Criterion + val criteria: List? ) { } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureReusable.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureReusable.kt new file mode 100644 index 0000000000..73d9df3aa8 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureReusable.kt @@ -0,0 +1,7 @@ +package org.evomaster.core.problem.rest.arazzo.models + +sealed class FailureReusable { + data class Inline(val action: FailureAction) : FailureReusable() + + data class ReusableObj(val reusable: Reusable) : FailureReusable() +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Parameter.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Parameter.kt index 5cae69e297..aafec604b2 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Parameter.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Parameter.kt @@ -1,11 +1,14 @@ package org.evomaster.core.problem.rest.arazzo.models import com.fasterxml.jackson.annotation.JsonProperty +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import org.evomaster.core.problem.rest.arazzo.deserializer.AnyExpressionDeserializer class Parameter( val name: String, @JsonProperty("in") val location: String?, - val value: String + @JsonDeserialize(using = AnyExpressionDeserializer::class) + val value: AnyExpression ) { } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ParameterReusable.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ParameterReusable.kt new file mode 100644 index 0000000000..9b425522af --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ParameterReusable.kt @@ -0,0 +1,7 @@ +package org.evomaster.core.problem.rest.arazzo.models + +sealed class ParameterReusable { + data class Inline(val parameter: Parameter) : ParameterReusable() + + data class ReusableObj(val reusable: Reusable) : ParameterReusable() +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Reusable.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Reusable.kt index 7373ac115c..4ccdf9e3d4 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Reusable.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Reusable.kt @@ -1,7 +1,11 @@ package org.evomaster.core.problem.rest.arazzo.models +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import org.evomaster.core.problem.rest.arazzo.deserializer.RuntimeExpressionDeserializer + class Reusable( - val reference: String, + @JsonDeserialize(using = RuntimeExpressionDeserializer::class) + val reference: RuntimeExpression, val value: String? ) { } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RuntimeExpression.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RuntimeExpression.kt new file mode 100644 index 0000000000..1e050c97a1 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RuntimeExpression.kt @@ -0,0 +1,18 @@ +package org.evomaster.core.problem.rest.arazzo.models + +sealed class RuntimeExpression { + object Url : RuntimeExpression() + object Method : RuntimeExpression() + object StatusCode : RuntimeExpression() + + data class Request(val source: Source) : RuntimeExpression() + data class Response(val source: Source) : RuntimeExpression() + + data class Inputs(val name: String) : RuntimeExpression() + data class Outputs(val name: String) : RuntimeExpression() + data class Steps(val name: String) : RuntimeExpression() + data class Workflows(val name: String) : RuntimeExpression() + data class SourceDescriptions(val name: String) : RuntimeExpression() + data class Components(val name: String) : RuntimeExpression() + data class ComponentParameters(val parameterName: String) : RuntimeExpression() +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Source.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Source.kt new file mode 100644 index 0000000000..a83039e354 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Source.kt @@ -0,0 +1,8 @@ +package org.evomaster.core.problem.rest.arazzo.models + +sealed class Source { + data class Header(val token: String) : Source() + data class Query(val name: String) : Source() + data class Path(val name: String) : Source() + data class Body(val jsonPointer: String? = null) : Source() +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessAction.kt index 6ec4d5ca0e..6eed27166b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessAction.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessAction.kt @@ -5,6 +5,6 @@ class SuccessAction( val type: String, val workflowId: String?, val stepId: String?, - val criteria: List + val criteria: List? ) { } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessReusable.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessReusable.kt new file mode 100644 index 0000000000..d870b0c153 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessReusable.kt @@ -0,0 +1,7 @@ +package org.evomaster.core.problem.rest.arazzo.models + +sealed class SuccessReusable { + data class Inline(val action: SuccessAction) : SuccessReusable() + + data class ReusableObj(val reusable: Reusable) : SuccessReusable() +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt index 172fcd1332..42a37d2d1b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt @@ -1,6 +1,11 @@ package org.evomaster.core.problem.rest.arazzo.models import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import org.evomaster.core.problem.rest.arazzo.deserializer.FailureReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.deserializer.ParameterReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.deserializer.RuntimeExpressionDeserializer +import org.evomaster.core.problem.rest.arazzo.deserializer.SuccessReusableDeserializer class Workflow( val workflowId: String, @@ -9,9 +14,13 @@ class Workflow( val inputs: JsonNode?, val dependsOn: List?, val steps: List, - val successActions: List?, - val failureActions: List?, - val outputs: Map?, - val parameters: List? + @JsonDeserialize(contentUsing = SuccessReusableDeserializer::class) + val successActions: List?, + @JsonDeserialize(contentUsing = FailureReusableDeserializer::class) + val failureActions: List?, + @JsonDeserialize(contentUsing = RuntimeExpressionDeserializer::class) + val outputs: Map?, + @JsonDeserialize(contentUsing = ParameterReusableDeserializer::class) + val parameters: List? ) { } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt index 013adcdb9e..a0e36c599d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt @@ -4,7 +4,6 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import org.evomaster.core.problem.rest.arazzo.models.ArazzoSpecifications import com.fasterxml.jackson.module.kotlin.readValue -import io.swagger.v3.oas.models.OpenAPI import org.evomaster.core.problem.rest.schema.SchemaArazzo import org.evomaster.core.problem.rest.schema.SchemaOpenAPI @@ -13,7 +12,7 @@ object ArazzoParser { val jsonMapper = ObjectMapper().findAndRegisterModules() val yamlMapper = ObjectMapper(YAMLFactory()).findAndRegisterModules() - fun parserSchemaText(schemaText: String): ArazzoSpecifications { + fun parseSchemaText(schemaText: String): ArazzoSpecifications { val schemaTextClean = schemaText.trimStart() var arazzoSpecifications: ArazzoSpecifications? @@ -33,7 +32,11 @@ object ArazzoParser { } fun validateSchema(schemaArazzo: SchemaArazzo, schemaOpenAPI: SchemaOpenAPI) { - + schemaArazzo.schemaParsed.sourceDescriptions.forEach { + sourceDescription -> ArazzoValidator.validateSourceDescriptions(sourceDescription) + } + ArazzoValidator.validateWorkflows(schemaArazzo.schemaParsed.workflows) + ArazzoValidator.validateComponents(schemaArazzo.schemaParsed.components) } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt new file mode 100644 index 0000000000..fb2e705222 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt @@ -0,0 +1,294 @@ +package org.evomaster.core.problem.rest.arazzo.parser + +import org.evomaster.core.problem.rest.arazzo.models.Components +import org.evomaster.core.problem.rest.arazzo.models.Criterion +import org.evomaster.core.problem.rest.arazzo.models.CriterionExpression +import org.evomaster.core.problem.rest.arazzo.models.CriterionType +import org.evomaster.core.problem.rest.arazzo.models.FailureAction +import org.evomaster.core.problem.rest.arazzo.models.Parameter +import org.evomaster.core.problem.rest.arazzo.models.SourceDescription +import org.evomaster.core.problem.rest.arazzo.models.Step +import org.evomaster.core.problem.rest.arazzo.models.SuccessAction +import org.evomaster.core.problem.rest.arazzo.models.Workflow +import wiremock.com.jayway.jsonpath.JsonPath +import javax.xml.xpath.XPathFactory + +object ArazzoValidator { + + private val possibleParameters = listOf("path","query","header","cookie") + private val possibleTypeSuccessActions = listOf("end","goto") + private val possibleTypeFailureActions = listOf("end","goto","retry") + private val possibleCriterionExpresionTypes = listOf("jsonpath","xpath") + private val possibleCriterionVersionXpath = listOf("xpath-30","xpath-20","xpath-10") + + private const val POSSIBLE_CRITERION_VERSION_JSON = "draft-goessner-dispatch-jsonpath-00" + + fun validateSourceDescriptions(sourceDescription: SourceDescription) { + val patternName = Regex("[A-Za-z0-9_\\-]+") + + if (!sourceDescription.name.matches(patternName)) { + throw IllegalArgumentException("Arazzo Parsing Error: The name should conform to the regular expression [A-Za-z0-9_\\-]+.") + } + } + + fun validateWorkflows(workflows: List) { + if (workflows.size != workflows.distinctBy { it.workflowId }.size) { + throw IllegalArgumentException("Arazzo Parsing Error: The id MUST be unique amongst all workflows described in the Arazzo Description. ") + } + workflows.forEach { workflow -> validateWorkflow(workflow) } + validateDependsOnWorkflows(workflows) + } + + private fun validateWorkflow(workflow: Workflow) { + val patternName = Regex("[A-Za-z0-9_\\-]+") + + // workflowId + if (!workflow.workflowId.matches(patternName)) { + throw IllegalArgumentException("Arazzo Parsing Error: The name should conform to the regular expression [A-Za-z0-9_\\-]+.") + } + //TODO: $sourceDescriptions..) Leer en la docu + + // Steps + if (workflow.steps.isEmpty()) { + throw IllegalArgumentException("Arazzo Parsing Error: The steps must have at least one step.") + } + + workflow.steps.forEach { step -> validateStep(step) } + + // successActions + + // failureActions + + // outputs + val keyOutputRegex = Regex("^[a-zA-Z0-9\\.\\-_]+$") + workflow.outputs?.keys?.forEach { key -> + if (!keyOutputRegex.matches(key)) { + throw IllegalArgumentException( + "Arazzo Parsing Error: Output name in workflow: ${workflow.workflowId}: '$key' is invalid. " + + "Only alphanumeric characters, periods (.), hyphens (-) and underscores (_) are allowed." + ) + } + } + + // parameters + + } + + private fun validateStep(step: Step) { + + } + + private fun validateDependsOnWorkflows(workflows: List) { + val workflowsMap = workflows.associateBy { it.workflowId } + + // Validate existence + workflows.forEach { workflow -> + workflow.dependsOn?.forEach { dependencyId -> + if (!workflowsMap.containsKey(dependencyId)) { + throw IllegalArgumentException( + "Arazzo Parsing Error: The workflow '${workflow.workflowId}' depends on '$dependencyId', " + + "but that workflowId does not exist in the document." + ) + } + } + } + + // DFS for dependencies + // The visit states map helps track our progress: + // 0 or null = Unvisited + // 1 = Visiting (currently in the recursion stack) + // 2 = Fully Validated (safe, no cycles detected from here) + val visitStates = mutableMapOf() + + fun detectCycleDFS(currentWorkflowId: String, currentPath: List) { + val state = visitStates[currentWorkflowId] ?: 0 + + if (state == 1) { + val cyclePath = currentPath.joinToString(" -> ") + " -> $currentWorkflowId" + throw IllegalArgumentException("Arazzo Parsing Error: Cyclic dependency detected. Workflows cannot depend on themselves in a closed loop.") + } + + if (state == 2) { + return + } + + visitStates[currentWorkflowId] = 1 + + val dependencies = workflowsMap[currentWorkflowId]?.dependsOn ?: emptyList() + dependencies.forEach { dependencyId -> + detectCycleDFS(dependencyId, currentPath + currentWorkflowId) + } + + visitStates[currentWorkflowId] = 2 + } + + // Execute the validator for all workflows + workflowsMap.keys.forEach { workflowId -> + if ((visitStates[workflowId] ?: 0) == 0) { + detectCycleDFS(workflowId, emptyList()) + } + } + } + + fun validateComponents(components: Components?) { + if (components == null) return + + val componentKeyRegex = Regex("^[a-zA-Z0-9\\.\\-_]+$") + fun validateMapKeys(map: Map?, componentType: String) { + map?.keys?.forEach { key -> + if (!componentKeyRegex.matches(key)) { + throw IllegalArgumentException( + "Arazzo Parsing Error: Component name:'$key' in '$componentType' is invalid. " + + "Only alphanumeric characters, periods (.), hyphens (-) and underscores (_) are allowed." + ) + } + } + } + + validateMapKeys(components.inputs, "inputs") + validateMapKeys(components.parameters, "parameters") + validateMapKeys(components.successActions, "successActions") + validateMapKeys(components.failureActions, "failureActions") + + //TODO: Validar Json Schema. inputs + components.parameters?.let { paremeters -> validateParameters(paremeters) } + components.successActions?.forEach { successAction -> validateSuccessAction(successAction.value) } + components.failureActions?.forEach { failureActions -> validateFailureAction(failureActions.value) } + } + + private fun validateParameters(parameters: Map?) { + if (parameters == null) return; + + //A unique parameter is defined by the combination of a name and in fields + val duplicates = parameters.values.groupBy { Pair(it.name, it.location) } + .filter { (_, list) -> list.size > 1 } + + if (duplicates.isNotEmpty()) { + val errorMessages = duplicates.map { (key, list) -> + "The Parameter [name='${key.first}', in='${key.second}'] is repeated ${list.size} times." + } + throw IllegalArgumentException("Arazzo Parsing Error: Parameters repeated. " + errorMessages.joinToString("\n")) + } + + parameters.values.forEach { parameter -> + if (parameter.location != null && parameter.location !in possibleParameters) { + throw IllegalArgumentException("Arazzo Parsing Error: Parameters must be one of the list: \"path\",\"query\",\"header\",\"cookie\"") + } + } + } + + private fun validateParameters(parameters: List) { + //A unique parameter is defined by the combination of a name and in fields + val duplicates = parameters.groupBy { Pair(it.name, it.location) } + .filter { (_, list) -> list.size > 1 } + + if (duplicates.isNotEmpty()) { + val errorMessages = duplicates.map { (key, list) -> + "The Parameter [name='${key.first}', in='${key.second}'] is repeated ${list.size} times." + } + throw IllegalArgumentException("Arazzo Parsing Error: Parameters repeated. " + errorMessages.joinToString("\n")) + } + + //Only Parameters in workflow context, option "in" is mandatory + parameters.forEach { parameter -> + if (parameter.location == null || parameter.location !in possibleParameters) { + throw IllegalArgumentException("Arazzo Parsing Error: Parameters must be one of the list: \"path\",\"query\",\"header\",\"cookie\"") + } + } + } + + private fun validateSuccessAction(successAction: SuccessAction) { + if (successAction.type !in possibleTypeSuccessActions) { + throw IllegalArgumentException("Arazzo Parsing Error: successAction.type must be one of the list: \"end\",\"goto\"") + } + + if (successAction.type == "goto") { + if (!((successAction.workflowId != null) xor (successAction.stepId != null))) { + throw IllegalArgumentException("Arazzo Parsing Error: SuccesAction: workflowId and stepId are mutually exclusive.") + } + //TODO Añadir la referencia del workflow y stepId + } + + successAction.criteria?.forEach { criterion -> validateCriterion(criterion) } + + } + + private fun validateFailureAction(failureAction: FailureAction) { + if (failureAction.type !in possibleTypeFailureActions) { + throw IllegalArgumentException("Arazzo Parsing Error: failureAction.type must be one of the list: \"end\",\"goto\",\"retry\"") + } + + if (failureAction.type == "goto" || failureAction.type == "retry") { + if (!((failureAction.workflowId != null) xor (failureAction.stepId != null))) { + throw IllegalArgumentException("Arazzo Parsing Error: FailureAction: workflowId and stepId are mutually exclusive.") + } + //TODO Añadir la referencia del workflow y stepId + } + + if (failureAction.type == "retry") { + if (failureAction.retryAfter != null && failureAction.retryAfter.toDouble() <= 0) { + throw IllegalArgumentException("Arazzo Parsing Error: FailureAction: retryAfter must be non-negative decimal number.") + } + + if (failureAction.retryLimit != null && failureAction.retryLimit <= 0) { + throw IllegalArgumentException("Arazzo Parsing Error: FailureAction: retryLimit must be non-negative integer number.") + } + } + + failureAction.criteria?.forEach { criterion -> validateCriterion(criterion) } + } + + private fun validateCriterion(criterion: Criterion?) { + if (criterion == null) return + + if (criterion.type != null && criterion.context == null) { + throw IllegalArgumentException("Arazzo Parsing Error: Criterion Object. If \"type\" is specified, then the context MUST be provided.") + } + + val type: String? + when(val criterionType = criterion.type) { + is CriterionType.Simple -> type = criterionType.value + is CriterionType.Complex -> { + validateCriterionExpresion(criterionType.expr) + type = criterionType.expr.type + } + null -> type = "simple" + } + + try { + when (type.lowercase()) { + "regex" -> Regex(criterion.condition) + "jsonpath" -> JsonPath.compile(criterion.condition) + "simple" -> SimpleConditionParser(criterion.condition).validateOrThrow() + "xpath" -> { + val xpathFactory = XPathFactory.newInstance() + val xpath = xpathFactory.newXPath() + xpath.compile(criterion.condition) + } + else -> throw IllegalArgumentException("Invalid Criteron Type") + } + } catch(e: Exception) { + throw IllegalArgumentException("Arazzo Parsing Error: Criterion condition error - ${criterion.condition}: ${e.message}") + } + } + + private fun validateCriterionExpresion(criterionExpresion: CriterionExpression) { + if (criterionExpresion.type !in possibleCriterionExpresionTypes) { + throw IllegalArgumentException("Arazzo Parsing Error: Criterion Expresion type invalid. The options allowed are jsonpath or xpath.") + } + + when(criterionExpresion.type) { + "jsonpath" -> { + if (!criterionExpresion.version.equals(POSSIBLE_CRITERION_VERSION_JSON)) { + throw IllegalArgumentException("Arazzo Parsing Error: The allowed values for JSONPath are draft-goessner-dispatch-jsonpath-00") + } + } + "xpath" -> { + if (criterionExpresion.version !in possibleCriterionVersionXpath) { + throw IllegalArgumentException("Arazzo Parsing Error: The allowed values for XPath are xpath-30, xpath-20, or xpath-10.") + } + } + } + } + +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ExpressionParser.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ExpressionParser.kt new file mode 100644 index 0000000000..eb26fcbf65 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ExpressionParser.kt @@ -0,0 +1,81 @@ +package org.evomaster.core.problem.rest.arazzo.parser + +import org.evomaster.core.problem.rest.arazzo.models.RuntimeExpression +import org.evomaster.core.problem.rest.arazzo.models.Source + +object ExpressionParser { + private val tokenRegex = Regex("^[a-zA-Z0-9!#\\\$%&'*+\\-.^_`|~]+$") + private val nameRegex = Regex("^[\\x21-\\x7E]+$") + private val jsonPointerRegex = Regex("^(?:/(?:[^/~]|~0|~1)*)*$") + + private fun validateToken(token: String): String { + if (!tokenRegex.matches(token)) { + throw IllegalArgumentException("Arazzo Parsing Error: The 'token' [$token] must be a 'tchar' of ABNF.") + } + return token + } + + private fun validateName(name: String): String { + if (!nameRegex.matches(name)) { + throw IllegalArgumentException("Arazzo Parsing Error: The 'name' [$name] must be a valid ABNF's character.") + } + return name + } + + private fun validateJsonPointer(pointer: String): String { + if (!jsonPointerRegex.matches(pointer)) { + throw IllegalArgumentException("Arazzo Parsing Error: The 'json-pointer' [$pointer] must be a valid RFC 6901.") + } + return pointer + } + + fun parse(input: String): RuntimeExpression { + return when { + input == "\$url" -> RuntimeExpression.Url + input == "\$method" -> RuntimeExpression.Method + input == "\$statusCode" -> RuntimeExpression.StatusCode + + input.startsWith("\$request.") -> RuntimeExpression.Request(parseSource(input.removePrefix("\$request."))) + input.startsWith("\$response.") -> RuntimeExpression.Response(parseSource(input.removePrefix("\$response."))) + + input.startsWith("\$inputs.") -> RuntimeExpression.Inputs(validateName(input.removePrefix("\$inputs."))) + input.startsWith("\$outputs.") -> RuntimeExpression.Outputs(validateName(input.removePrefix("\$outputs."))) + input.startsWith("\$steps.") -> RuntimeExpression.Steps(validateName(input.removePrefix("\$steps."))) + input.startsWith("\$workflows.") -> RuntimeExpression.Workflows(validateName(input.removePrefix("\$workflows."))) + input.startsWith("\$sourceDescriptions.") -> RuntimeExpression.SourceDescriptions(validateName(input.removePrefix("\$sourceDescriptions."))) + + input.startsWith("\$components.parameters.") -> RuntimeExpression.ComponentParameters(validateName(input.removePrefix("\$components.parameters."))) + input.startsWith("\$components.") -> RuntimeExpression.Components(validateName(input.removePrefix("\$components."))) + + else -> throw IllegalArgumentException("Arazzo Parsing Error: Expression '$input' is not recognize for Arazzo.") + } + } + + private fun parseSource(source: String): Source { + return when { + source.startsWith("header.") -> { + val token = source.removePrefix("header.") + Source.Header(validateToken(token)) + } + source.startsWith("query.") -> { + val name = source.removePrefix("query.") + Source.Query(validateName(name)) + } + source.startsWith("path.") -> { + val name = source.removePrefix("path.") + Source.Path(validateName(name)) + } + source.startsWith("body") -> { + if (source == "body") { + Source.Body(null) + } else if (source.startsWith("body#")) { + val pointer = source.removePrefix("body#") + Source.Body(validateJsonPointer(pointer)) + } else { + throw IllegalArgumentException("Arazzo Parsing Error: Bad 'body' reference: $source") + } + } + else -> throw IllegalArgumentException("Arazzo Parsing Error: Invalid source data: $source") + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/SimpleConditionParser.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/SimpleConditionParser.kt new file mode 100644 index 0000000000..f27e1f8933 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/SimpleConditionParser.kt @@ -0,0 +1,103 @@ +package org.evomaster.core.problem.rest.arazzo.parser + +class SimpleConditionParser(condition: String) { + private val tokens: List + private var currentPosition = 0 + + init { + // Esta Regex identifica todo el vocabulario válido de Arazzo + val arazzoRegex = Regex( + """\$[a-zA-Z0-9_\.\[\]]+|""" + // Runtime variables + """'([^']|'')*'|""" + // Strings + """-?\d+(\.\d+)?|""" + // Numbers + """true|false|null|""" + // Literals + """==|!=|<=|>=|<|>|""" + // Operators + """&&|\|\||!|""" + // Logical Operators + """\(|\)""" // Parenthesis + ) + tokens = arazzoRegex.findAll(condition).map { it.value }.toList() + } + + private fun peek(): String? = tokens.getOrNull(currentPosition) + private fun advance(): String? = tokens.getOrNull(currentPosition++) + private fun match(vararg expected: String): Boolean { + if (peek() in expected) { + advance() + return true + } + return false + } + + fun validateOrThrow(): Boolean { + if (tokens.isEmpty()) throw IllegalArgumentException("Empty expression") + + parseOrExpression() + + if (currentPosition != tokens.size) { + throw IllegalArgumentException("Raw tokens") + } + + return true + } + + // OR Operator (||) + private fun parseOrExpression() { + parseAndExpression() + while (match("||")) { + parseAndExpression() + } + } + + // AND Operator (&&) + private fun parseAndExpression() { + parseComparison() + while (match("&&")) { + parseComparison() + } + } + + // Comparison Operators (==, !=, <, etc.) + private fun parseComparison() { + parseTerm() + if (match("==", "!=", "<=", ">=", "<", ">")) { + parseTerm() + } + } + + // Terms + private fun parseTerm() { + val token = peek() ?: throw IllegalArgumentException("Unexpected end of expression") + + when { + match("!") -> parseTerm() + match("(") -> { + parseOrExpression() + if (!match(")")) throw IllegalArgumentException("Unclosed parentheses") + } + isPrimitiveValue(token) -> { + advance() + } + else -> throw IllegalArgumentException("Unexpected or invalid token: $token") + } + } + + // PrimitiveValue + private fun isPrimitiveValue(token: String): Boolean { + if (token.startsWith("'") || + token in listOf("true", "false", "null") || + token.matches(Regex("-?\\d+(\\.\\d+)?"))) { + return true + } + + if (token.startsWith("$")) { + try { + ExpressionParser.parse(token) + return true + } catch (e: Exception) { + throw IllegalArgumentException("Invalid condition: $token", e) + } + } + + return false + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt index b218a58bbe..9fc2868667 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt @@ -1,7 +1,6 @@ package org.evomaster.core.problem.rest.schema import org.evomaster.core.problem.rest.arazzo.parser.ArazzoParser -import org.evomaster.core.problem.rest.schema.SchemaArazzo import org.evomaster.core.remote.SutProblemException import org.slf4j.LoggerFactory import java.net.URI @@ -17,7 +16,7 @@ object ArazzoAccess { fun parseArazzo(schemaText: String, sourceLocation: SchemaLocation): SchemaArazzo { - val schemaParsed = ArazzoParser.parserSchemaText(schemaText) + val schemaParsed = ArazzoParser.parseSchemaText(schemaText) return SchemaArazzo(schemaText, schemaParsed, sourceLocation) } diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt index 3bbd916dee..8e3cba3ab4 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt @@ -75,10 +75,10 @@ class ArazzoLocalURLIssueTest { "file://$arazzoTestDirectory/arazzo_pet.yaml" } - // create swagger from URL + // create arazzo from URL val arazzo = ArazzoAccess.getArazzoFromLocation(urlArazzoToTest) - // a valid swagger is created with 13 endpoints + // a valid arazzo is created with 3 workflows Assertions.assertTrue(arazzo.schemaParsed.workflows.size == 3) } From 6c43da5281db23c13f2f192db0641f0dc13dcef7 Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Thu, 9 Apr 2026 23:11:55 -0300 Subject: [PATCH 04/12] #UP validate workflow and step. Incomplete --- .../FailureReusableDeserializer.kt | 2 +- .../ParameterReusableDeserializer.kt | 2 +- .../SuccessReusableDeserializer.kt | 2 +- .../rest/arazzo/models/FailureReusable.kt | 2 +- .../rest/arazzo/models/ParameterReusable.kt | 2 +- .../core/problem/rest/arazzo/models/Step.kt | 13 ++- .../rest/arazzo/models/SuccessReusable.kt | 2 +- .../rest/arazzo/parser/ArazzoValidator.kt | 97 ++++++++++++++++++- .../resolver/ArazzoReferenceResolver.kt | 48 +++++++++ 9 files changed, 159 insertions(+), 11 deletions(-) create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/FailureReusableDeserializer.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/FailureReusableDeserializer.kt index effd75640a..227b797ac6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/FailureReusableDeserializer.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/FailureReusableDeserializer.kt @@ -20,7 +20,7 @@ class FailureReusableDeserializer : JsonDeserializer() { FailureReusable.ReusableObj(reusable) } else { val action = p0.codec.treeToValue(node, FailureAction::class.java) - FailureReusable.Inline(action) + FailureReusable.Failure(action) } } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/ParameterReusableDeserializer.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/ParameterReusableDeserializer.kt index 2ee24242a2..ed2e868351 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/ParameterReusableDeserializer.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/ParameterReusableDeserializer.kt @@ -20,7 +20,7 @@ class ParameterReusableDeserializer : JsonDeserializer() { ParameterReusable.ReusableObj(reusable) } else { val parameter = p0.codec.treeToValue(node, Parameter::class.java) - ParameterReusable.Inline(parameter) + ParameterReusable.Param(parameter) } } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/SuccessReusableDeserializer.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/SuccessReusableDeserializer.kt index 11bdf9de5a..e2bb141bb6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/SuccessReusableDeserializer.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/SuccessReusableDeserializer.kt @@ -20,7 +20,7 @@ class SuccessReusableDeserializer : JsonDeserializer() { SuccessReusable.ReusableObj(reusable) } else { val action = p0.codec.treeToValue(node, SuccessAction::class.java) - SuccessReusable.Inline(action) + SuccessReusable.Success(action) } } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureReusable.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureReusable.kt index 73d9df3aa8..baf57aa91f 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureReusable.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureReusable.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rest.arazzo.models sealed class FailureReusable { - data class Inline(val action: FailureAction) : FailureReusable() + data class Failure(val action: FailureAction) : FailureReusable() data class ReusableObj(val reusable: Reusable) : FailureReusable() } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ParameterReusable.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ParameterReusable.kt index 9b425522af..50e9ea0f5b 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ParameterReusable.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ParameterReusable.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rest.arazzo.models sealed class ParameterReusable { - data class Inline(val parameter: Parameter) : ParameterReusable() + data class Param(val parameter: Parameter) : ParameterReusable() data class ReusableObj(val reusable: Reusable) : ParameterReusable() } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt index 799f3cf564..c2dceb754f 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt @@ -1,6 +1,10 @@ package org.evomaster.core.problem.rest.arazzo.models import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import org.evomaster.core.problem.rest.arazzo.deserializer.FailureReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.deserializer.ParameterReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.deserializer.SuccessReusableDeserializer class Step( val description: String?, @@ -8,11 +12,14 @@ class Step( val operationId: String?, val operationPath: String?, val workflowId: String?, - val parameters: List?, + @JsonDeserialize(contentUsing = ParameterReusableDeserializer::class) + val parameters: List?, val requestBody: RequestBody?, val successCriteria: List?, - val onSuccess: List?, - val onFailure: List?, + @JsonDeserialize(contentUsing = SuccessReusableDeserializer::class) + val onSuccess: List?, + @JsonDeserialize(contentUsing = FailureReusableDeserializer::class) + val onFailure: List?, val outputs: Map? ) { } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessReusable.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessReusable.kt index d870b0c153..97234022bb 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessReusable.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessReusable.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rest.arazzo.models sealed class SuccessReusable { - data class Inline(val action: SuccessAction) : SuccessReusable() + data class Success(val action: SuccessAction) : SuccessReusable() data class ReusableObj(val reusable: Reusable) : SuccessReusable() } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt index fb2e705222..9aa61c5bc1 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt @@ -10,6 +10,7 @@ import org.evomaster.core.problem.rest.arazzo.models.SourceDescription import org.evomaster.core.problem.rest.arazzo.models.Step import org.evomaster.core.problem.rest.arazzo.models.SuccessAction import org.evomaster.core.problem.rest.arazzo.models.Workflow +import org.evomaster.core.problem.rest.arazzo.resolver.ArazzoReferenceResolver import wiremock.com.jayway.jsonpath.JsonPath import javax.xml.xpath.XPathFactory @@ -24,9 +25,9 @@ object ArazzoValidator { private const val POSSIBLE_CRITERION_VERSION_JSON = "draft-goessner-dispatch-jsonpath-00" fun validateSourceDescriptions(sourceDescription: SourceDescription) { - val patternName = Regex("[A-Za-z0-9_\\-]+") + val patterName = Regex("[A-Za-z0-9_\\-]+") - if (!sourceDescription.name.matches(patternName)) { + if (!sourceDescription.name.matches(patterName)) { throw IllegalArgumentException("Arazzo Parsing Error: The name should conform to the regular expression [A-Za-z0-9_\\-]+.") } } @@ -56,8 +57,12 @@ object ArazzoValidator { workflow.steps.forEach { step -> validateStep(step) } // successActions + val successActions = ArazzoReferenceResolver.resolveSuccessActions(workflow.successActions) + validateSuccessActions(successActions) // failureActions + val failureActions = ArazzoReferenceResolver.resolveFailureActions(workflow.failureActions) + validateFailureActions(failureActions) // outputs val keyOutputRegex = Regex("^[a-zA-Z0-9\\.\\-_]+$") @@ -71,11 +76,71 @@ object ArazzoValidator { } // parameters + val parameters = ArazzoReferenceResolver.resolveParameters(workflow.parameters) + validateParameters(parameters) + } + + private fun validateSteps(steps: List) { + if (steps.isEmpty()) { + throw IllegalArgumentException("Arazzo Parsing Error: The steps must have at least one step.") + } + + //A unique successAction is defined by a name + val duplicates = steps + .groupBy { step -> step.stepId } + .filter { it.value.size > 1 } + .keys + if (duplicates.isNotEmpty()) { + throw IllegalArgumentException("Arazzo Parsing Error: Steps repeated. $duplicates") + } + + steps.forEach { step -> validateStep(step) } } private fun validateStep(step: Step) { + val pattern = Regex("[A-Za-z0-9_\\-]+") + + if (!step.stepId.matches(pattern)) { + throw IllegalArgumentException("Arazzo Parsing Error: The stepId: ${step.stepId} should conform to the regular expression [A-Za-z0-9_\\-]+.") + } + + if (!((step.operationId != null) xor (step.operationPath != null))) { + throw IllegalArgumentException("Arazzo Parsing Error: Step: operationId and operationPath are mutually exclusive.") + } + + //TODO: Validar operationId y operationPath + + if (!((step.workflowId != null) xor (step.operationId != null))) { + throw IllegalArgumentException("Arazzo Parsing Error: Step: workflowId and operationId are mutually exclusive.") + } + + if (!((step.workflowId != null) xor (step.operationPath != null))) { + throw IllegalArgumentException("Arazzo Parsing Error: Step: workflowId and operationPath are mutually exclusive.") + } + + val parameters = ArazzoReferenceResolver.resolveParameters(step.parameters) + validateParameters(parameters) + + //TODO: Validar requestBody + + step.successCriteria?.forEach { criterion -> validateCriterion(criterion) } + val onSuccess = ArazzoReferenceResolver.resolveSuccessActions(step.onSuccess) + validateSuccessActions(onSuccess) + + val onFailure = ArazzoReferenceResolver.resolveFailureActions(step.onFailure) + validateFailureActions(onFailure) + + val keyOutputRegex = Regex("^[a-zA-Z0-9\\.\\-_]+$") + step.outputs?.keys?.forEach { key -> + if (!keyOutputRegex.matches(key)) { + throw IllegalArgumentException( + "Arazzo Parsing Error: Output name in step: ${step.stepId}: '$key' is invalid. " + + "Only alphanumeric characters, periods (.), hyphens (-) and underscores (_) are allowed." + ) + } + } } private fun validateDependsOnWorkflows(workflows: List) { @@ -197,6 +262,20 @@ object ArazzoValidator { } } + private fun validateSuccessActions(successActions: List) { + //A unique successAction is defined by a name + val duplicates = successActions + .groupBy { action -> action.name } + .filter { it.value.size > 1 } + .keys + + if (duplicates.isNotEmpty()) { + throw IllegalArgumentException("Arazzo Parsing Error: SuccesActions repeated. $duplicates") + } + + successActions.forEach { action -> validateSuccessAction(action) } + } + private fun validateSuccessAction(successAction: SuccessAction) { if (successAction.type !in possibleTypeSuccessActions) { throw IllegalArgumentException("Arazzo Parsing Error: successAction.type must be one of the list: \"end\",\"goto\"") @@ -213,6 +292,20 @@ object ArazzoValidator { } + private fun validateFailureActions(failureActions: List) { + //A unique successAction is defined by a name + val duplicates = failureActions + .groupBy { action -> action.name } + .filter { it.value.size > 1 } + .keys + + if (duplicates.isNotEmpty()) { + throw IllegalArgumentException("Arazzo Parsing Error: FailureActions repeated. $duplicates") + } + + failureActions.forEach { action -> validateFailureAction(action) } + } + private fun validateFailureAction(failureAction: FailureAction) { if (failureAction.type !in possibleTypeFailureActions) { throw IllegalArgumentException("Arazzo Parsing Error: failureAction.type must be one of the list: \"end\",\"goto\",\"retry\"") diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt new file mode 100644 index 0000000000..d1d32252e1 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt @@ -0,0 +1,48 @@ +package org.evomaster.core.problem.rest.arazzo.resolver + +import org.evomaster.core.problem.rest.arazzo.models.FailureAction +import org.evomaster.core.problem.rest.arazzo.models.FailureReusable +import org.evomaster.core.problem.rest.arazzo.models.Parameter +import org.evomaster.core.problem.rest.arazzo.models.ParameterReusable +import org.evomaster.core.problem.rest.arazzo.models.SuccessAction +import org.evomaster.core.problem.rest.arazzo.models.SuccessReusable + +object ArazzoReferenceResolver { + + fun resolveSuccessActions(items: List?): List { + if (items == null) return emptyList() + + return items.map { item -> + when (item) { + is SuccessReusable.Success -> item.action + //TODO: Implementar referencia del reusable a paremetro + is SuccessReusable.ReusableObj -> null + } as SuccessAction + } + } + + fun resolveFailureActions(items: List?): List { + if (items == null) return emptyList() + + return items.map { item -> + when (item) { + is FailureReusable.Failure -> item.action + //TODO: Implementar referencia del reusable a paremetro + is FailureReusable.ReusableObj -> null + } as FailureAction + } + } + + fun resolveParameters(items: List?): List { + if (items == null) return emptyList() + + return items.map { item -> + when (item) { + is ParameterReusable.Param -> item.parameter + //TODO: Implementar referencia del reusable a paremetro + is ParameterReusable.ReusableObj -> null + } as Parameter + } + } + +} \ No newline at end of file From c5cfff89d8c14e1caa47ec66a29438b379baafd1 Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Thu, 16 Apr 2026 22:12:37 -0300 Subject: [PATCH 05/12] Validation with reference and --- .../problem/rest/arazzo/models/Components.kt | 4 +- .../problem/rest/arazzo/models/RequestBody.kt | 4 +- .../core/problem/rest/arazzo/models/Step.kt | 1 - .../problem/rest/arazzo/models/Workflow.kt | 4 +- .../rest/arazzo/parser/ArazzoParser.kt | 20 +- .../rest/arazzo/parser/ArazzoValidator.kt | 20 +- .../rest/arazzo/parser/OverlayJVM.java | 195 ++++++++++++++++++ .../resolver/ArazzoReferenceResolver.kt | 111 +++++++++- .../core/problem/rest/schema/ArazzoAccess.kt | 2 +- .../core/problem/rest/schema/SchemaArazzo.kt | 5 + 10 files changed, 339 insertions(+), 27 deletions(-) create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/OverlayJVM.java diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt index 04ccf0a528..c250417c02 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt @@ -1,9 +1,9 @@ package org.evomaster.core.problem.rest.arazzo.models -import com.fasterxml.jackson.databind.JsonNode +import io.swagger.v3.oas.models.media.Schema class Components( - val inputs: Map?, + val inputs: Map>?, val parameters: Map?, val successActions: Map?, val failureActions: Map? diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RequestBody.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RequestBody.kt index 7bfad2152a..1529590f19 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RequestBody.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RequestBody.kt @@ -1,8 +1,10 @@ package org.evomaster.core.problem.rest.arazzo.models +import com.fasterxml.jackson.databind.JsonNode + class RequestBody( val contentType: String?, - val payload: Any?, + val payload: JsonNode?, val replacements: List? ) { } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt index c2dceb754f..5b5742789c 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt @@ -1,6 +1,5 @@ package org.evomaster.core.problem.rest.arazzo.models -import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.annotation.JsonDeserialize import org.evomaster.core.problem.rest.arazzo.deserializer.FailureReusableDeserializer import org.evomaster.core.problem.rest.arazzo.deserializer.ParameterReusableDeserializer diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt index 42a37d2d1b..0506dbe2d2 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt @@ -1,7 +1,7 @@ package org.evomaster.core.problem.rest.arazzo.models -import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import io.swagger.v3.oas.models.media.Schema import org.evomaster.core.problem.rest.arazzo.deserializer.FailureReusableDeserializer import org.evomaster.core.problem.rest.arazzo.deserializer.ParameterReusableDeserializer import org.evomaster.core.problem.rest.arazzo.deserializer.RuntimeExpressionDeserializer @@ -11,7 +11,7 @@ class Workflow( val workflowId: String, val summary: String?, val description: String?, - val inputs: JsonNode?, + val inputs: Schema<*>?, val dependsOn: List?, val steps: List, @JsonDeserialize(contentUsing = SuccessReusableDeserializer::class) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt index a0e36c599d..bee1d5a7f9 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt @@ -1,9 +1,11 @@ package org.evomaster.core.problem.rest.arazzo.parser +import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import org.evomaster.core.problem.rest.arazzo.models.ArazzoSpecifications import com.fasterxml.jackson.module.kotlin.readValue +import org.evomaster.core.problem.rest.arazzo.resolver.ArazzoReferenceResolver import org.evomaster.core.problem.rest.schema.SchemaArazzo import org.evomaster.core.problem.rest.schema.SchemaOpenAPI @@ -12,22 +14,25 @@ object ArazzoParser { val jsonMapper = ObjectMapper().findAndRegisterModules() val yamlMapper = ObjectMapper(YAMLFactory()).findAndRegisterModules() - fun parseSchemaText(schemaText: String): ArazzoSpecifications { + fun parseSchemaText(schemaText: String): Pair { val schemaTextClean = schemaText.trimStart() var arazzoSpecifications: ArazzoSpecifications? + var arazzoJsonNode: JsonNode? try { if (schemaTextClean.startsWith("{")) { arazzoSpecifications = jsonMapper.readValue(schemaTextClean) + arazzoJsonNode = jsonMapper.readTree(schemaTextClean) } else { arazzoSpecifications = yamlMapper.readValue(schemaTextClean) + arazzoJsonNode = yamlMapper.readTree(schemaTextClean) } } catch (e: Exception) { throw IllegalArgumentException("Problems parsing the Arazzo document", e) } - return arazzoSpecifications + return Pair(arazzoSpecifications, arazzoJsonNode) } @@ -35,8 +40,17 @@ object ArazzoParser { schemaArazzo.schemaParsed.sourceDescriptions.forEach { sourceDescription -> ArazzoValidator.validateSourceDescriptions(sourceDescription) } - ArazzoValidator.validateWorkflows(schemaArazzo.schemaParsed.workflows) + ArazzoValidator.validateComponents(schemaArazzo.schemaParsed.components) + + val resolver = ArazzoReferenceResolver( + schemaArazzo.schemaParsed.components, + schemaArazzo.schemaJsonNode, + schemaOpenAPI.schemaParsed + ) + ArazzoValidator.configResolver(resolver) + + ArazzoValidator.validateWorkflows(schemaArazzo.schemaParsed.workflows) } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt index 9aa61c5bc1..aa4d745338 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt @@ -10,12 +10,15 @@ import org.evomaster.core.problem.rest.arazzo.models.SourceDescription import org.evomaster.core.problem.rest.arazzo.models.Step import org.evomaster.core.problem.rest.arazzo.models.SuccessAction import org.evomaster.core.problem.rest.arazzo.models.Workflow +import org.evomaster.core.problem.rest.arazzo.parser.ArazzoValidator.resolver import org.evomaster.core.problem.rest.arazzo.resolver.ArazzoReferenceResolver import wiremock.com.jayway.jsonpath.JsonPath import javax.xml.xpath.XPathFactory object ArazzoValidator { + var resolver: ArazzoReferenceResolver = ArazzoReferenceResolver(null, null, null) + private val possibleParameters = listOf("path","query","header","cookie") private val possibleTypeSuccessActions = listOf("end","goto") private val possibleTypeFailureActions = listOf("end","goto","retry") @@ -24,6 +27,10 @@ object ArazzoValidator { private const val POSSIBLE_CRITERION_VERSION_JSON = "draft-goessner-dispatch-jsonpath-00" + fun configResolver(resolver: ArazzoReferenceResolver) { + ArazzoValidator.resolver = resolver + } + fun validateSourceDescriptions(sourceDescription: SourceDescription) { val patterName = Regex("[A-Za-z0-9_\\-]+") @@ -57,11 +64,11 @@ object ArazzoValidator { workflow.steps.forEach { step -> validateStep(step) } // successActions - val successActions = ArazzoReferenceResolver.resolveSuccessActions(workflow.successActions) + val successActions = resolver.resolveSuccessActions(workflow.successActions) validateSuccessActions(successActions) // failureActions - val failureActions = ArazzoReferenceResolver.resolveFailureActions(workflow.failureActions) + val failureActions = resolver.resolveFailureActions(workflow.failureActions) validateFailureActions(failureActions) // outputs @@ -76,7 +83,7 @@ object ArazzoValidator { } // parameters - val parameters = ArazzoReferenceResolver.resolveParameters(workflow.parameters) + val parameters = resolver.resolveParameters(workflow.parameters) validateParameters(parameters) } @@ -119,17 +126,17 @@ object ArazzoValidator { throw IllegalArgumentException("Arazzo Parsing Error: Step: workflowId and operationPath are mutually exclusive.") } - val parameters = ArazzoReferenceResolver.resolveParameters(step.parameters) + val parameters = resolver.resolveParameters(step.parameters) validateParameters(parameters) //TODO: Validar requestBody step.successCriteria?.forEach { criterion -> validateCriterion(criterion) } - val onSuccess = ArazzoReferenceResolver.resolveSuccessActions(step.onSuccess) + val onSuccess = resolver.resolveSuccessActions(step.onSuccess) validateSuccessActions(onSuccess) - val onFailure = ArazzoReferenceResolver.resolveFailureActions(step.onFailure) + val onFailure = resolver.resolveFailureActions(step.onFailure) validateFailureActions(onFailure) val keyOutputRegex = Regex("^[a-zA-Z0-9\\.\\-_]+$") @@ -215,7 +222,6 @@ object ArazzoValidator { validateMapKeys(components.successActions, "successActions") validateMapKeys(components.failureActions, "failureActions") - //TODO: Validar Json Schema. inputs components.parameters?.let { paremeters -> validateParameters(paremeters) } components.successActions?.forEach { successAction -> validateSuccessAction(successAction.value) } components.failureActions?.forEach { failureActions -> validateFailureAction(failureActions.value) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/OverlayJVM.java b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/OverlayJVM.java new file mode 100644 index 0000000000..ac3f6f1099 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/OverlayJVM.java @@ -0,0 +1,195 @@ +package com.webfuzzing.overlayjvm; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.webfuzzing.overlayjvm.model.Action; +import com.webfuzzing.overlayjvm.model.Overlay; +import org.noear.snack4.ONode; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Main entry point of this library. + */ +public class OverlayJVM { + + + private static final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + + static { + mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + } + + + public static Overlay parseOverlay(File file){ + try { + return mapper.readValue(file, Overlay.class); + } catch (Exception e) { + throw new IllegalArgumentException(e); + } + } + + public static Overlay parseOverlay(String overlayContent){ + try { + return mapper.readValue(overlayContent, Overlay.class); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException(e); + } + } + + + /** + * Apply an Overlay set of transformations to an OpenAPI schema. + * These file representations are passed as String, representing either JSON or YAML formats. + * The transformed result format depends on the input OpenAPI format. + * For example, if OpenAPI is in YAML, and Overlay is in JSON, the transformed result is in YAML. + *

+ * There is no validity check on the input OpenAPI schema, besides it being a syntactically valid + * JSON or YAML file representation. + * So, technically speaking, the Overlay transformation can be applied to any valid JSON/YAML file. + * + * @param openApiSchema String representation for a JSON/YAML file, in which transformations are applied on. + * @param overlayContent String representation for a JSON/YAML Overlay set of transformations. + * @return TransformationResult with the result of the transformation, including possible warning messages, if any. + * @throws IllegalArgumentException if the inputs are not valid JSON/YAML files, and if the Overlay is not correct + * according the Overlay's specs. No validation is done on the OpenAPI specs. + */ + public static TransformationResult applyOverlay(String openApiSchema, String overlayContent){ + + List warnings = new ArrayList<>(); + + Overlay overlay = parseOverlay(overlayContent); + OpenAPIInfo schemaInfo = OpenAPIInfo.fromSchema(openApiSchema); + Format format = schemaInfo.getType(); + + if(format != Format.JSON && format != Format.YAML) { + //shouldn't really happen, unless we add new type and forgot to handle it + throw new IllegalArgumentException("Unsupported type: " + schemaInfo.getType()); + } + + ONode schema = ONode.ofJson(schemaInfo.getJson()); + List actions = overlay.getActions(); + + for(int i=0;i k : y.getObjectUnsafe().entrySet()) { + if (!x.hasKey(k.getKey())) { + //whole insertion, as entry was not present + x.set(k.getKey(), k.getValue()); + } else { + //need to merge, recursively + if(k.getValue().isObject()) { + mergeObjects(x.get(k.getKey()), k.getValue()); + } else if(k.getValue().isValue()){ + x.set(k.getKey(), k.getValue()); + } else if(k.getValue().isArray()){ + x.get(k.getKey()).addAll(k.getValue().getArrayUnsafe()); + } else { + throw new IllegalArgumentException("Invalid type for: " + k.getKey()); + } + } + } + } + + private static void handleUpdate(ONode openApi, Action a) { + ONode selection = openApi.select(a.getTarget()); + ONode update = ONode.ofJson(a.getUpdate().toString()); + applyUpdate(selection, update); + } + + private static void applyUpdate(ONode selection, ONode update) { + if(selection.isValue()){ + selection.setValue(update.getValue()); + return; + } + + if(selection.isObject()){ + mergeObjects(selection, update); + return; + } + + if(selection.isArray()) { + /* + currently, in snackjson there is no clear way to distinguish between a selected array + and an array of results, apart from checking the parent + */ + ONode parent = selection.parent(); + if(parent == null) { + // newly created array containing result nodes + for (ONode node : selection.getArray()) { + applyUpdate(node, update); + } + } else { + //the target is an array itself + if(update.isArray()) { + selection.addAll(update.getArrayUnsafe()); + } else { + selection.add(update); + } + } + } + } + + private static void handleRemove(ONode openApi, Action a) { + boolean deleted = openApi.delete(a.getTarget()); + assert(deleted); + } +} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt index d1d32252e1..e3fc7d2501 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt @@ -1,13 +1,25 @@ package org.evomaster.core.problem.rest.arazzo.resolver +import com.fasterxml.jackson.core.type.TypeReference +import com.fasterxml.jackson.databind.JsonNode +import io.swagger.v3.core.util.Json +import io.swagger.v3.oas.models.OpenAPI +import io.swagger.v3.oas.models.media.Schema +import org.evomaster.core.problem.rest.arazzo.models.Components import org.evomaster.core.problem.rest.arazzo.models.FailureAction import org.evomaster.core.problem.rest.arazzo.models.FailureReusable import org.evomaster.core.problem.rest.arazzo.models.Parameter import org.evomaster.core.problem.rest.arazzo.models.ParameterReusable +import org.evomaster.core.problem.rest.arazzo.models.Reusable +import org.evomaster.core.problem.rest.arazzo.models.RuntimeExpression import org.evomaster.core.problem.rest.arazzo.models.SuccessAction import org.evomaster.core.problem.rest.arazzo.models.SuccessReusable -object ArazzoReferenceResolver { +class ArazzoReferenceResolver( + val components: Components?, + val arazzoJsonNode: JsonNode?, + val openApi: OpenAPI? +) { fun resolveSuccessActions(items: List?): List { if (items == null) return emptyList() @@ -15,9 +27,8 @@ object ArazzoReferenceResolver { return items.map { item -> when (item) { is SuccessReusable.Success -> item.action - //TODO: Implementar referencia del reusable a paremetro - is SuccessReusable.ReusableObj -> null - } as SuccessAction + is SuccessReusable.ReusableObj -> resolveReusableWithPrefix(item.reusable, "successActions") as SuccessAction + } } } @@ -27,9 +38,8 @@ object ArazzoReferenceResolver { return items.map { item -> when (item) { is FailureReusable.Failure -> item.action - //TODO: Implementar referencia del reusable a paremetro - is FailureReusable.ReusableObj -> null - } as FailureAction + is FailureReusable.ReusableObj -> resolveReusableWithPrefix(item.reusable, "failureActions") as FailureAction + } } } @@ -39,10 +49,91 @@ object ArazzoReferenceResolver { return items.map { item -> when (item) { is ParameterReusable.Param -> item.parameter - //TODO: Implementar referencia del reusable a paremetro - is ParameterReusable.ReusableObj -> null - } as Parameter + is ParameterReusable.ReusableObj -> resolveReusableWithPrefix(item.reusable, "parameters") as Parameter + } } } + private fun resolveReusableWithPrefix(reusable: Reusable, prefixExpected: String) : Any { + if (components == null) { + throw IllegalArgumentException("Arazzo Parsing Error: Can't reference with no Components") + } + + val reference = reusable.reference + + if (reference !is RuntimeExpression.Components) { + throw IllegalArgumentException( + "Arazzo Parsing Error: A reference to Components was expected." + ) + } + + if (!reference.name.startsWith(prefixExpected)) { + throw IllegalArgumentException( + "Arazzo Parsing Error: Invalid reference (${reference.name}). Expected to point to '${prefixExpected}'" + ) + } + + val actionName = reference.name.removePrefix(prefixExpected) + val resolve = when(prefixExpected) { + "successActions" -> components.successActions?.get(actionName) + "failureActions" -> components.failureActions?.get(actionName) + "parameters" -> components.parameters?.get(actionName) + else -> null + } + + if (resolve == null) { + throw IllegalArgumentException( + "Arazzo Parsing Error: The ${prefixExpected}: '$actionName' is not in the components." + ) + } + + return resolve + } + + private fun resolveJsonPointer(reference: String) : Schema<*>? { + if (reference.startsWith("#/")) { + return resolveJsonPointerLocal(reference) + } + return resolveJsonPointerExternal(reference) + } + + private fun resolveJsonPointerLocal(reference: String) : Schema<*>? { + if (arazzoJsonNode == null) { + throw IllegalArgumentException("Arazzo Parsing Error: Can't reference with no Arazzo Document") + } + + val jsonPointer = reference.substring(1) + + val result = arazzoJsonNode.at(jsonPointer) + + if (result.isMissingNode) { + throw IllegalArgumentException("Arazzo Parsing Error: Can't reference '${reference}'") + } + + return Json.mapper().convertValue(result, Schema::class.java) + } + + private fun resolveJsonPointerExternal(reference: String) : Schema<*> { + if (openApi == null) { + throw IllegalArgumentException("Arazzo Parsing Error: Can't reference with no OpenApi Document") + } + + val tokens = reference.split("#") + if (tokens.size < 2) { + throw IllegalArgumentException("Arazzo Parsing Error: Error reference (${reference}). '#' Is mandatory") + } + + val jsonPointer = tokens[1] + val expectedPrefix = "/components/schemas/" + if (!jsonPointer.startsWith(expectedPrefix)) { + throw IllegalArgumentException("Arazzo Parsing Error: Error reference (${reference}). \"/components/schemas/\" Is mandatory for references to OpenApi") + } + + val schemaName = jsonPointer.removePrefix(expectedPrefix) + val result = openApi.components?.schemas?.get(schemaName) + ?: throw IllegalArgumentException("Arazzo Parsing Error: (${reference}) reference does not exist in the OpenApi document") + + return result + } + } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt index 9fc2868667..663dfb3f26 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt @@ -17,7 +17,7 @@ object ArazzoAccess { fun parseArazzo(schemaText: String, sourceLocation: SchemaLocation): SchemaArazzo { val schemaParsed = ArazzoParser.parseSchemaText(schemaText) - return SchemaArazzo(schemaText, schemaParsed, sourceLocation) + return SchemaArazzo(schemaText, schemaParsed.first, schemaParsed.second, sourceLocation) } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt index 8e742f5b04..36ec408269 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt @@ -1,5 +1,6 @@ package org.evomaster.core.problem.rest.schema +import com.fasterxml.jackson.databind.JsonNode import io.swagger.v3.oas.models.OpenAPI import org.evomaster.core.problem.rest.arazzo.models.ArazzoSpecifications @@ -12,6 +13,10 @@ class SchemaArazzo( * A parsed schema */ val schemaParsed: ArazzoSpecifications, + /** + * A parsed schema + */ + val schemaJsonNode: JsonNode, /** * information about the location the schema was retrieved from, e.g., * from file, URL or in memory in our tests. From 885bd161e9273a1f68219b1d02c505e42bea1bd1 Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Thu, 16 Apr 2026 22:13:06 -0300 Subject: [PATCH 06/12] Clean Overlay --- .../rest/arazzo/parser/OverlayJVM.java | 195 ------------------ .../core/problem/rest/schema/SchemaOpenAPI.kt | 1 - 2 files changed, 196 deletions(-) delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/OverlayJVM.java diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/OverlayJVM.java b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/OverlayJVM.java deleted file mode 100644 index ac3f6f1099..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/OverlayJVM.java +++ /dev/null @@ -1,195 +0,0 @@ -package com.webfuzzing.overlayjvm; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import com.webfuzzing.overlayjvm.model.Action; -import com.webfuzzing.overlayjvm.model.Overlay; -import org.noear.snack4.ONode; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -/** - * Main entry point of this library. - */ -public class OverlayJVM { - - - private static final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - - static { - mapper.enable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); - } - - - public static Overlay parseOverlay(File file){ - try { - return mapper.readValue(file, Overlay.class); - } catch (Exception e) { - throw new IllegalArgumentException(e); - } - } - - public static Overlay parseOverlay(String overlayContent){ - try { - return mapper.readValue(overlayContent, Overlay.class); - } catch (JsonProcessingException e) { - throw new IllegalArgumentException(e); - } - } - - - /** - * Apply an Overlay set of transformations to an OpenAPI schema. - * These file representations are passed as String, representing either JSON or YAML formats. - * The transformed result format depends on the input OpenAPI format. - * For example, if OpenAPI is in YAML, and Overlay is in JSON, the transformed result is in YAML. - *

- * There is no validity check on the input OpenAPI schema, besides it being a syntactically valid - * JSON or YAML file representation. - * So, technically speaking, the Overlay transformation can be applied to any valid JSON/YAML file. - * - * @param openApiSchema String representation for a JSON/YAML file, in which transformations are applied on. - * @param overlayContent String representation for a JSON/YAML Overlay set of transformations. - * @return TransformationResult with the result of the transformation, including possible warning messages, if any. - * @throws IllegalArgumentException if the inputs are not valid JSON/YAML files, and if the Overlay is not correct - * according the Overlay's specs. No validation is done on the OpenAPI specs. - */ - public static TransformationResult applyOverlay(String openApiSchema, String overlayContent){ - - List warnings = new ArrayList<>(); - - Overlay overlay = parseOverlay(overlayContent); - OpenAPIInfo schemaInfo = OpenAPIInfo.fromSchema(openApiSchema); - Format format = schemaInfo.getType(); - - if(format != Format.JSON && format != Format.YAML) { - //shouldn't really happen, unless we add new type and forgot to handle it - throw new IllegalArgumentException("Unsupported type: " + schemaInfo.getType()); - } - - ONode schema = ONode.ofJson(schemaInfo.getJson()); - List actions = overlay.getActions(); - - for(int i=0;i k : y.getObjectUnsafe().entrySet()) { - if (!x.hasKey(k.getKey())) { - //whole insertion, as entry was not present - x.set(k.getKey(), k.getValue()); - } else { - //need to merge, recursively - if(k.getValue().isObject()) { - mergeObjects(x.get(k.getKey()), k.getValue()); - } else if(k.getValue().isValue()){ - x.set(k.getKey(), k.getValue()); - } else if(k.getValue().isArray()){ - x.get(k.getKey()).addAll(k.getValue().getArrayUnsafe()); - } else { - throw new IllegalArgumentException("Invalid type for: " + k.getKey()); - } - } - } - } - - private static void handleUpdate(ONode openApi, Action a) { - ONode selection = openApi.select(a.getTarget()); - ONode update = ONode.ofJson(a.getUpdate().toString()); - applyUpdate(selection, update); - } - - private static void applyUpdate(ONode selection, ONode update) { - if(selection.isValue()){ - selection.setValue(update.getValue()); - return; - } - - if(selection.isObject()){ - mergeObjects(selection, update); - return; - } - - if(selection.isArray()) { - /* - currently, in snackjson there is no clear way to distinguish between a selected array - and an array of results, apart from checking the parent - */ - ONode parent = selection.parent(); - if(parent == null) { - // newly created array containing result nodes - for (ONode node : selection.getArray()) { - applyUpdate(node, update); - } - } else { - //the target is an array itself - if(update.isArray()) { - selection.addAll(update.getArrayUnsafe()); - } else { - selection.add(update); - } - } - } - } - - private static void handleRemove(ONode openApi, Action a) { - boolean deleted = openApi.delete(a.getTarget()); - assert(deleted); - } -} diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaOpenAPI.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaOpenAPI.kt index c4427d0099..a57a95c363 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaOpenAPI.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaOpenAPI.kt @@ -1,6 +1,5 @@ package org.evomaster.core.problem.rest.schema -import com.webfuzzing.overlayjvm.OverlayJVM import io.swagger.v3.oas.models.OpenAPI import org.slf4j.LoggerFactory From 025a1e04ce28e17b8fedd5d139439dbbd67ae4cf Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Sat, 18 Apr 2026 00:11:01 -0300 Subject: [PATCH 07/12] Arazzo Only Parser. Test not working --- .../rest/arazzo/mapper/ArazzoMapper.kt | 41 +++++++++++++++++++ .../arazzo/models/ArazzoSpecifications.kt | 13 +++--- .../problem/rest/arazzo/models/Components.kt | 3 +- .../problem/rest/arazzo/models/InfoArazzo.kt | 3 +- .../rest/arazzo/models/SourceDescription.kt | 3 +- .../core/problem/rest/arazzo/models/Step.kt | 26 +++--------- .../problem/rest/arazzo/models/Workflow.kt | 27 +++--------- .../commons/ArazzoSpecificationsCommon.kt | 12 ++++++ .../rest/arazzo/models/commons/StepCommon.kt | 15 +++++++ .../arazzo/models/commons/WorkflowCommon.kt | 13 ++++++ .../models/raws/ArazzoSpecificationsRaw.kt | 14 +++++++ .../rest/arazzo/models/raws/StepRaw.kt | 32 +++++++++++++++ .../rest/arazzo/models/raws/WorkflowRaw.kt | 35 ++++++++++++++++ .../rest/arazzo/parser/ArazzoParser.kt | 37 +++++++---------- .../rest/arazzo/parser/ArazzoValidator.kt | 25 ++++++----- .../resolver/ArazzoReferenceResolver.kt | 23 ++++------- .../core/problem/rest/schema/ArazzoAccess.kt | 13 +++--- .../core/problem/rest/schema/SchemaArazzo.kt | 4 -- .../core/problem/rest/schema/SchemaOpenAPI.kt | 1 + .../service/sampler/AbstractRestSampler.kt | 3 +- .../rest/schema/ArazzoLocalURLIssueTest.kt | 14 ++++++- 21 files changed, 237 insertions(+), 120 deletions(-) create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/mapper/ArazzoMapper.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/ArazzoSpecificationsCommon.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/StepCommon.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/WorkflowCommon.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/ArazzoSpecificationsRaw.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/StepRaw.kt create mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/WorkflowRaw.kt diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/mapper/ArazzoMapper.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/mapper/ArazzoMapper.kt new file mode 100644 index 0000000000..00c57a1212 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/mapper/ArazzoMapper.kt @@ -0,0 +1,41 @@ +package org.evomaster.core.problem.rest.arazzo.mapper + +import org.evomaster.core.problem.rest.arazzo.models.ArazzoSpecifications +import org.evomaster.core.problem.rest.arazzo.models.Step +import org.evomaster.core.problem.rest.arazzo.models.Workflow +import org.evomaster.core.problem.rest.arazzo.models.raws.ArazzoSpecificationsRaw +import org.evomaster.core.problem.rest.arazzo.models.raws.StepRaw +import org.evomaster.core.problem.rest.arazzo.models.raws.WorkflowRaw +import org.evomaster.core.problem.rest.arazzo.resolver.ArazzoReferenceResolver + +class ArazzoMapper( + val resolver: ArazzoReferenceResolver +) { + + fun toDomain(raw: ArazzoSpecificationsRaw) : ArazzoSpecifications { + return ArazzoSpecifications( + common = raw, + workflows = raw.workflows.map { toDomain(it) } + ) + } + + fun toDomain(raw: WorkflowRaw) : Workflow { + return Workflow( + common = raw, + steps = raw.steps.map { toDomain(it) }, + successActions = resolver.resolveSuccessReusable(raw.successActions), + failureActions = resolver.resolveFailureReusable(raw.failureActions), + parameters = resolver.resolveParametersReusable(raw.parameters) + ) + } + + fun toDomain(raw: StepRaw) : Step { + return Step( + common = raw, + parameters = resolver.resolveParametersReusable(raw.parameters), + onSuccess = resolver.resolveSuccessReusable(raw.onSuccess), + onFailure = resolver.resolveFailureReusable(raw.onFailure) + ) + } + +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt index a3672bcf3d..5e861b2732 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt @@ -1,11 +1,8 @@ package org.evomaster.core.problem.rest.arazzo.models -class ArazzoSpecifications( - val arazzo: String, - val info: InfoArazzo, - val sourceDescriptions: List, - val workflows: MutableList, - val components: Components? -) { +import org.evomaster.core.problem.rest.arazzo.models.commons.ArazzoSpecificationsCommon -} \ No newline at end of file +class ArazzoSpecifications( + common: ArazzoSpecificationsCommon, + val workflows: List, +) : ArazzoSpecificationsCommon by common \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt index c250417c02..6e46999bb0 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt @@ -7,5 +7,4 @@ class Components( val parameters: Map?, val successActions: Map?, val failureActions: Map? -) { -} \ No newline at end of file +) \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/InfoArazzo.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/InfoArazzo.kt index 2da94e10b0..f2ba4cc826 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/InfoArazzo.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/InfoArazzo.kt @@ -5,5 +5,4 @@ class InfoArazzo( val summary: String?, val description: String?, val version: String -) { -} \ No newline at end of file +) \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SourceDescription.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SourceDescription.kt index 3ecb428357..effa0bb685 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SourceDescription.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SourceDescription.kt @@ -4,5 +4,4 @@ class SourceDescription( val name: String, val url: String, val type: String? -) { -} \ No newline at end of file +) \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt index 5b5742789c..032fa7fdcd 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt @@ -1,24 +1,10 @@ package org.evomaster.core.problem.rest.arazzo.models -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import org.evomaster.core.problem.rest.arazzo.deserializer.FailureReusableDeserializer -import org.evomaster.core.problem.rest.arazzo.deserializer.ParameterReusableDeserializer -import org.evomaster.core.problem.rest.arazzo.deserializer.SuccessReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.models.commons.StepCommon class Step( - val description: String?, - val stepId: String, - val operationId: String?, - val operationPath: String?, - val workflowId: String?, - @JsonDeserialize(contentUsing = ParameterReusableDeserializer::class) - val parameters: List?, - val requestBody: RequestBody?, - val successCriteria: List?, - @JsonDeserialize(contentUsing = SuccessReusableDeserializer::class) - val onSuccess: List?, - @JsonDeserialize(contentUsing = FailureReusableDeserializer::class) - val onFailure: List?, - val outputs: Map? -) { -} \ No newline at end of file + common: StepCommon, + val parameters: List?, + val onSuccess: List?, + val onFailure: List?, +) : StepCommon by common \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt index 0506dbe2d2..4a3496d362 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt @@ -1,26 +1,11 @@ package org.evomaster.core.problem.rest.arazzo.models -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import io.swagger.v3.oas.models.media.Schema -import org.evomaster.core.problem.rest.arazzo.deserializer.FailureReusableDeserializer -import org.evomaster.core.problem.rest.arazzo.deserializer.ParameterReusableDeserializer -import org.evomaster.core.problem.rest.arazzo.deserializer.RuntimeExpressionDeserializer -import org.evomaster.core.problem.rest.arazzo.deserializer.SuccessReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.models.commons.WorkflowCommon class Workflow( - val workflowId: String, - val summary: String?, - val description: String?, - val inputs: Schema<*>?, - val dependsOn: List?, + common: WorkflowCommon, val steps: List, - @JsonDeserialize(contentUsing = SuccessReusableDeserializer::class) - val successActions: List?, - @JsonDeserialize(contentUsing = FailureReusableDeserializer::class) - val failureActions: List?, - @JsonDeserialize(contentUsing = RuntimeExpressionDeserializer::class) - val outputs: Map?, - @JsonDeserialize(contentUsing = ParameterReusableDeserializer::class) - val parameters: List? -) { -} \ No newline at end of file + val successActions: List?, + val failureActions: List?, + val parameters: List? +) : WorkflowCommon by common \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/ArazzoSpecificationsCommon.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/ArazzoSpecificationsCommon.kt new file mode 100644 index 0000000000..0c5076dad6 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/ArazzoSpecificationsCommon.kt @@ -0,0 +1,12 @@ +package org.evomaster.core.problem.rest.arazzo.models.commons + +import org.evomaster.core.problem.rest.arazzo.models.Components +import org.evomaster.core.problem.rest.arazzo.models.InfoArazzo +import org.evomaster.core.problem.rest.arazzo.models.SourceDescription + +interface ArazzoSpecificationsCommon { + val arazzo: String + val info: InfoArazzo + val sourceDescriptions: List + val components: Components? +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/StepCommon.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/StepCommon.kt new file mode 100644 index 0000000000..5087561d1a --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/StepCommon.kt @@ -0,0 +1,15 @@ +package org.evomaster.core.problem.rest.arazzo.models.commons + +import org.evomaster.core.problem.rest.arazzo.models.Criterion +import org.evomaster.core.problem.rest.arazzo.models.RequestBody + +interface StepCommon { + val description: String? + val stepId: String + val operationId: String? + val operationPath: String? + val workflowId: String? + val requestBody: RequestBody? + val successCriteria: List? + val outputs: Map? +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/WorkflowCommon.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/WorkflowCommon.kt new file mode 100644 index 0000000000..20346089c3 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/WorkflowCommon.kt @@ -0,0 +1,13 @@ +package org.evomaster.core.problem.rest.arazzo.models.commons + +import io.swagger.v3.oas.models.media.Schema +import org.evomaster.core.problem.rest.arazzo.models.RuntimeExpression + +interface WorkflowCommon { + val workflowId: String + val summary: String? + val description: String? + val inputs: Schema<*>? + val dependsOn: List? + val outputs: Map? +} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/ArazzoSpecificationsRaw.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/ArazzoSpecificationsRaw.kt new file mode 100644 index 0000000000..c6bf02bac1 --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/ArazzoSpecificationsRaw.kt @@ -0,0 +1,14 @@ +package org.evomaster.core.problem.rest.arazzo.models.raws + +import org.evomaster.core.problem.rest.arazzo.models.Components +import org.evomaster.core.problem.rest.arazzo.models.InfoArazzo +import org.evomaster.core.problem.rest.arazzo.models.SourceDescription +import org.evomaster.core.problem.rest.arazzo.models.commons.ArazzoSpecificationsCommon + +class ArazzoSpecificationsRaw( + override val arazzo: String, + override val info: InfoArazzo, + override val sourceDescriptions: List, + override val components: Components?, + val workflows: List +) : ArazzoSpecificationsCommon \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/StepRaw.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/StepRaw.kt new file mode 100644 index 0000000000..5bc6cbaa5a --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/StepRaw.kt @@ -0,0 +1,32 @@ +package org.evomaster.core.problem.rest.arazzo.models.raws + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import org.evomaster.core.problem.rest.arazzo.deserializer.FailureReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.deserializer.ParameterReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.deserializer.SuccessReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.models.Criterion +import org.evomaster.core.problem.rest.arazzo.models.FailureReusable +import org.evomaster.core.problem.rest.arazzo.models.ParameterReusable +import org.evomaster.core.problem.rest.arazzo.models.RequestBody +import org.evomaster.core.problem.rest.arazzo.models.SuccessReusable +import org.evomaster.core.problem.rest.arazzo.models.commons.StepCommon + +class StepRaw( + override val description: String?, + override val stepId: String, + override val operationId: String?, + override val operationPath: String?, + override val workflowId: String?, + override val requestBody: RequestBody?, + override val successCriteria: List?, + override val outputs: Map?, + + @JsonDeserialize(contentUsing = ParameterReusableDeserializer::class) + val parameters: List?, + + @JsonDeserialize(contentUsing = SuccessReusableDeserializer::class) + val onSuccess: List?, + + @JsonDeserialize(contentUsing = FailureReusableDeserializer::class) + val onFailure: List? +) : StepCommon \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/WorkflowRaw.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/WorkflowRaw.kt new file mode 100644 index 0000000000..7716a1d6fd --- /dev/null +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/WorkflowRaw.kt @@ -0,0 +1,35 @@ +package org.evomaster.core.problem.rest.arazzo.models.raws + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import io.swagger.v3.oas.models.media.Schema +import org.evomaster.core.problem.rest.arazzo.deserializer.FailureReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.deserializer.ParameterReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.deserializer.RuntimeExpressionDeserializer +import org.evomaster.core.problem.rest.arazzo.deserializer.SuccessReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.models.FailureReusable +import org.evomaster.core.problem.rest.arazzo.models.ParameterReusable +import org.evomaster.core.problem.rest.arazzo.models.RuntimeExpression +import org.evomaster.core.problem.rest.arazzo.models.Step +import org.evomaster.core.problem.rest.arazzo.models.SuccessReusable +import org.evomaster.core.problem.rest.arazzo.models.commons.WorkflowCommon + +class WorkflowRaw( + override val workflowId: String, + override val summary: String?, + override val description: String?, + override val inputs: Schema<*>?, + override val dependsOn: List?, + val steps: List, + + @JsonDeserialize(contentUsing = SuccessReusableDeserializer::class) + val successActions: List?, + + @JsonDeserialize(contentUsing = FailureReusableDeserializer::class) + val failureActions: List?, + + @JsonDeserialize(contentUsing = RuntimeExpressionDeserializer::class) + override val outputs: Map?, + + @JsonDeserialize(contentUsing = ParameterReusableDeserializer::class) + val parameters: List? +) : WorkflowCommon \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt index bee1d5a7f9..2edc532014 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt @@ -5,6 +5,9 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import org.evomaster.core.problem.rest.arazzo.models.ArazzoSpecifications import com.fasterxml.jackson.module.kotlin.readValue +import org.evomaster.core.problem.rest.arazzo.mapper.ArazzoMapper +import org.evomaster.core.problem.rest.arazzo.models.Workflow +import org.evomaster.core.problem.rest.arazzo.models.raws.ArazzoSpecificationsRaw import org.evomaster.core.problem.rest.arazzo.resolver.ArazzoReferenceResolver import org.evomaster.core.problem.rest.schema.SchemaArazzo import org.evomaster.core.problem.rest.schema.SchemaOpenAPI @@ -14,43 +17,33 @@ object ArazzoParser { val jsonMapper = ObjectMapper().findAndRegisterModules() val yamlMapper = ObjectMapper(YAMLFactory()).findAndRegisterModules() - fun parseSchemaText(schemaText: String): Pair { + fun parse(schemaText: String, schemaOpenAPI: SchemaOpenAPI) : ArazzoSpecifications { + val (raw, schemaJsonNode) = parseSchemaText(schemaText) + val resolver = ArazzoReferenceResolver(raw.components,schemaJsonNode,schemaOpenAPI.schemaParsed) + val mapper = ArazzoMapper(resolver) + return mapper.toDomain(raw) + } + + private fun parseSchemaText(schemaText: String): Pair { val schemaTextClean = schemaText.trimStart() - var arazzoSpecifications: ArazzoSpecifications? var arazzoJsonNode: JsonNode? + var arazzoSpecificationsRaw: ArazzoSpecificationsRaw? try { if (schemaTextClean.startsWith("{")) { - arazzoSpecifications = jsonMapper.readValue(schemaTextClean) + arazzoSpecificationsRaw = jsonMapper.readValue(schemaTextClean) arazzoJsonNode = jsonMapper.readTree(schemaTextClean) } else { - arazzoSpecifications = yamlMapper.readValue(schemaTextClean) + arazzoSpecificationsRaw = yamlMapper.readValue(schemaTextClean) arazzoJsonNode = yamlMapper.readTree(schemaTextClean) } } catch (e: Exception) { throw IllegalArgumentException("Problems parsing the Arazzo document", e) } - return Pair(arazzoSpecifications, arazzoJsonNode) - - } - - fun validateSchema(schemaArazzo: SchemaArazzo, schemaOpenAPI: SchemaOpenAPI) { - schemaArazzo.schemaParsed.sourceDescriptions.forEach { - sourceDescription -> ArazzoValidator.validateSourceDescriptions(sourceDescription) - } - - ArazzoValidator.validateComponents(schemaArazzo.schemaParsed.components) - - val resolver = ArazzoReferenceResolver( - schemaArazzo.schemaParsed.components, - schemaArazzo.schemaJsonNode, - schemaOpenAPI.schemaParsed - ) - ArazzoValidator.configResolver(resolver) + return Pair(arazzoSpecificationsRaw, arazzoJsonNode) - ArazzoValidator.validateWorkflows(schemaArazzo.schemaParsed.workflows) } } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt index aa4d745338..630d950c76 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt @@ -10,7 +10,6 @@ import org.evomaster.core.problem.rest.arazzo.models.SourceDescription import org.evomaster.core.problem.rest.arazzo.models.Step import org.evomaster.core.problem.rest.arazzo.models.SuccessAction import org.evomaster.core.problem.rest.arazzo.models.Workflow -import org.evomaster.core.problem.rest.arazzo.parser.ArazzoValidator.resolver import org.evomaster.core.problem.rest.arazzo.resolver.ArazzoReferenceResolver import wiremock.com.jayway.jsonpath.JsonPath import javax.xml.xpath.XPathFactory @@ -64,12 +63,12 @@ object ArazzoValidator { workflow.steps.forEach { step -> validateStep(step) } // successActions - val successActions = resolver.resolveSuccessActions(workflow.successActions) - validateSuccessActions(successActions) + //val successActions = resolver.resolveSuccessReusable(workflow.successActions) + //validateSuccessActions(successActions) // failureActions - val failureActions = resolver.resolveFailureActions(workflow.failureActions) - validateFailureActions(failureActions) + //val failureActions = resolver.resolveFailureReusable(workflow.failureActions) + //validateFailureActions(failureActions) // outputs val keyOutputRegex = Regex("^[a-zA-Z0-9\\.\\-_]+$") @@ -83,8 +82,8 @@ object ArazzoValidator { } // parameters - val parameters = resolver.resolveParameters(workflow.parameters) - validateParameters(parameters) + //val parameters = resolver.resolveParametersReusable(workflow.parameters) + //validateParameters(parameters) } private fun validateSteps(steps: List) { @@ -126,18 +125,18 @@ object ArazzoValidator { throw IllegalArgumentException("Arazzo Parsing Error: Step: workflowId and operationPath are mutually exclusive.") } - val parameters = resolver.resolveParameters(step.parameters) - validateParameters(parameters) + //val parameters = resolver.resolveParametersReusable(step.parameters) + //validateParameters(parameters) //TODO: Validar requestBody step.successCriteria?.forEach { criterion -> validateCriterion(criterion) } - val onSuccess = resolver.resolveSuccessActions(step.onSuccess) - validateSuccessActions(onSuccess) + //val onSuccess = resolver.resolveSuccessReusable(step.onSuccess) + //validateSuccessActions(onSuccess) - val onFailure = resolver.resolveFailureActions(step.onFailure) - validateFailureActions(onFailure) + //val onFailure = resolver.resolveFailureReusable(step.onFailure) + //validateFailureActions(onFailure) val keyOutputRegex = Regex("^[a-zA-Z0-9\\.\\-_]+$") step.outputs?.keys?.forEach { key -> diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt index e3fc7d2501..40debd3337 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt @@ -1,19 +1,10 @@ package org.evomaster.core.problem.rest.arazzo.resolver -import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.databind.JsonNode import io.swagger.v3.core.util.Json import io.swagger.v3.oas.models.OpenAPI import io.swagger.v3.oas.models.media.Schema -import org.evomaster.core.problem.rest.arazzo.models.Components -import org.evomaster.core.problem.rest.arazzo.models.FailureAction -import org.evomaster.core.problem.rest.arazzo.models.FailureReusable -import org.evomaster.core.problem.rest.arazzo.models.Parameter -import org.evomaster.core.problem.rest.arazzo.models.ParameterReusable -import org.evomaster.core.problem.rest.arazzo.models.Reusable -import org.evomaster.core.problem.rest.arazzo.models.RuntimeExpression -import org.evomaster.core.problem.rest.arazzo.models.SuccessAction -import org.evomaster.core.problem.rest.arazzo.models.SuccessReusable +import org.evomaster.core.problem.rest.arazzo.models.* class ArazzoReferenceResolver( val components: Components?, @@ -21,8 +12,8 @@ class ArazzoReferenceResolver( val openApi: OpenAPI? ) { - fun resolveSuccessActions(items: List?): List { - if (items == null) return emptyList() + fun resolveSuccessReusable(items: List?): List? { + if (items == null) return null return items.map { item -> when (item) { @@ -32,8 +23,8 @@ class ArazzoReferenceResolver( } } - fun resolveFailureActions(items: List?): List { - if (items == null) return emptyList() + fun resolveFailureReusable(items: List?): List? { + if (items == null) return null return items.map { item -> when (item) { @@ -43,8 +34,8 @@ class ArazzoReferenceResolver( } } - fun resolveParameters(items: List?): List { - if (items == null) return emptyList() + fun resolveParametersReusable(items: List?): List? { + if (items == null) return null return items.map { item -> when (item) { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt index 663dfb3f26..f7131808db 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt @@ -1,6 +1,7 @@ package org.evomaster.core.problem.rest.schema import org.evomaster.core.problem.rest.arazzo.parser.ArazzoParser +import org.evomaster.core.problem.rest.arazzo.resolver.ArazzoReferenceResolver import org.evomaster.core.remote.SutProblemException import org.slf4j.LoggerFactory import java.net.URI @@ -14,15 +15,13 @@ object ArazzoAccess { private val log = LoggerFactory.getLogger(ArazzoAccess::class.java) - fun parseArazzo(schemaText: String, sourceLocation: SchemaLocation): SchemaArazzo { - - val schemaParsed = ArazzoParser.parseSchemaText(schemaText) - return SchemaArazzo(schemaText, schemaParsed.first, schemaParsed.second, sourceLocation) - + fun parseArazzo(schemaText: String, sourceLocation: SchemaLocation, schemaOpenAPI: SchemaOpenAPI): SchemaArazzo { + return SchemaArazzo(schemaText, ArazzoParser.parse(schemaText, schemaOpenAPI), sourceLocation) } fun getArazzoFromLocation( - arazzoLocation: String + arazzoLocation: String, + schemaOpenAPI: SchemaOpenAPI ): SchemaArazzo { //could be either JSON or YAML @@ -32,7 +31,7 @@ object ArazzoAccess { data = readFromDisk(arazzoLocation); location = SchemaLocation(arazzoLocation, SchemaLocationType.LOCAL) - return parseArazzo(data, location); + return parseArazzo(data, location, schemaOpenAPI); } private fun readFromDisk(arazzoLocation: String) : String { diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt index 36ec408269..069578e2cb 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt @@ -13,10 +13,6 @@ class SchemaArazzo( * A parsed schema */ val schemaParsed: ArazzoSpecifications, - /** - * A parsed schema - */ - val schemaJsonNode: JsonNode, /** * information about the location the schema was retrieved from, e.g., * from file, URL or in memory in our tests. diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaOpenAPI.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaOpenAPI.kt index a57a95c363..c4427d0099 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaOpenAPI.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaOpenAPI.kt @@ -1,5 +1,6 @@ package org.evomaster.core.problem.rest.schema +import com.webfuzzing.overlayjvm.OverlayJVM import io.swagger.v3.oas.models.OpenAPI import org.slf4j.LoggerFactory diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt index 76103ad0bf..d2d2f1a78d 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt @@ -121,8 +121,7 @@ abstract class AbstractRestSampler : HttpWsSampler() { schemaHolder.validate() val arazzoURL = problem.arazzoURL - val arazzo = ArazzoAccess.getArazzoFromLocation(arazzoURL) - ArazzoParser.validateSchema(arazzo, transformed) + val arazzo = ArazzoAccess.getArazzoFromLocation(arazzoURL, swagger) // The code should never reach this line without a valid swagger. actionCluster.clear() diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt index 8e3cba3ab4..4b29dcc3ad 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt @@ -67,6 +67,18 @@ class ArazzoLocalURLIssueTest { */ @Test fun testExistingFileValidURL() { + // get the current directory, in Mac or Linux, it starts with file:// + // but in Windows, it has to have just one file:/ + val urlToTest = if (hostOs.contains("win")) { + "file:/${swaggerTestDirectory}/openapi_pet.json" + } + else { + "file://${swaggerTestDirectory}/openapi_pet.json" + } + + // create swagger from URL + swagger = OpenApiAccess.getOpenAPIFromLocation(urlToTest) + // get the current directory, in Mac or Linux, it starts with file:// // but in Windows, it has to have just one file:/ val urlArazzoToTest = if (hostOs.contains("win")) { @@ -76,7 +88,7 @@ class ArazzoLocalURLIssueTest { } // create arazzo from URL - val arazzo = ArazzoAccess.getArazzoFromLocation(urlArazzoToTest) + arazzo = ArazzoAccess.getArazzoFromLocation(urlArazzoToTest, swagger) // a valid arazzo is created with 3 workflows Assertions.assertTrue(arazzo.schemaParsed.workflows.size == 3) From 825738f1081b7da80ab4ab0b9b05f18a09c8c2aa Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Sun, 19 Apr 2026 20:24:04 -0300 Subject: [PATCH 08/12] Arazzo parser working with references. Without Validations --- .../core/problem/rest/arazzo/models/RuntimeExpression.kt | 1 - .../core/problem/rest/arazzo/models/commons/StepCommon.kt | 3 ++- .../core/problem/rest/arazzo/models/raws/StepRaw.kt | 6 +++++- .../core/problem/rest/arazzo/parser/ExpressionParser.kt | 2 -- .../problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt | 2 +- .../core/problem/rest/schema/ArazzoLocalURLIssueTest.kt | 3 ++- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RuntimeExpression.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RuntimeExpression.kt index 1e050c97a1..02aec2b280 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RuntimeExpression.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RuntimeExpression.kt @@ -14,5 +14,4 @@ sealed class RuntimeExpression { data class Workflows(val name: String) : RuntimeExpression() data class SourceDescriptions(val name: String) : RuntimeExpression() data class Components(val name: String) : RuntimeExpression() - data class ComponentParameters(val parameterName: String) : RuntimeExpression() } diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/StepCommon.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/StepCommon.kt index 5087561d1a..7540ec8922 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/StepCommon.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/StepCommon.kt @@ -2,6 +2,7 @@ package org.evomaster.core.problem.rest.arazzo.models.commons import org.evomaster.core.problem.rest.arazzo.models.Criterion import org.evomaster.core.problem.rest.arazzo.models.RequestBody +import org.evomaster.core.problem.rest.arazzo.models.RuntimeExpression interface StepCommon { val description: String? @@ -11,5 +12,5 @@ interface StepCommon { val workflowId: String? val requestBody: RequestBody? val successCriteria: List? - val outputs: Map? + val outputs: Map? } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/StepRaw.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/StepRaw.kt index 5bc6cbaa5a..858be32d97 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/StepRaw.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/StepRaw.kt @@ -3,11 +3,13 @@ package org.evomaster.core.problem.rest.arazzo.models.raws import com.fasterxml.jackson.databind.annotation.JsonDeserialize import org.evomaster.core.problem.rest.arazzo.deserializer.FailureReusableDeserializer import org.evomaster.core.problem.rest.arazzo.deserializer.ParameterReusableDeserializer +import org.evomaster.core.problem.rest.arazzo.deserializer.RuntimeExpressionDeserializer import org.evomaster.core.problem.rest.arazzo.deserializer.SuccessReusableDeserializer import org.evomaster.core.problem.rest.arazzo.models.Criterion import org.evomaster.core.problem.rest.arazzo.models.FailureReusable import org.evomaster.core.problem.rest.arazzo.models.ParameterReusable import org.evomaster.core.problem.rest.arazzo.models.RequestBody +import org.evomaster.core.problem.rest.arazzo.models.RuntimeExpression import org.evomaster.core.problem.rest.arazzo.models.SuccessReusable import org.evomaster.core.problem.rest.arazzo.models.commons.StepCommon @@ -19,7 +21,9 @@ class StepRaw( override val workflowId: String?, override val requestBody: RequestBody?, override val successCriteria: List?, - override val outputs: Map?, + + @JsonDeserialize(contentUsing = RuntimeExpressionDeserializer::class) + override val outputs: Map?, @JsonDeserialize(contentUsing = ParameterReusableDeserializer::class) val parameters: List?, diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ExpressionParser.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ExpressionParser.kt index eb26fcbf65..8d22eb3cf8 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ExpressionParser.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ExpressionParser.kt @@ -43,8 +43,6 @@ object ExpressionParser { input.startsWith("\$steps.") -> RuntimeExpression.Steps(validateName(input.removePrefix("\$steps."))) input.startsWith("\$workflows.") -> RuntimeExpression.Workflows(validateName(input.removePrefix("\$workflows."))) input.startsWith("\$sourceDescriptions.") -> RuntimeExpression.SourceDescriptions(validateName(input.removePrefix("\$sourceDescriptions."))) - - input.startsWith("\$components.parameters.") -> RuntimeExpression.ComponentParameters(validateName(input.removePrefix("\$components.parameters."))) input.startsWith("\$components.") -> RuntimeExpression.Components(validateName(input.removePrefix("\$components."))) else -> throw IllegalArgumentException("Arazzo Parsing Error: Expression '$input' is not recognize for Arazzo.") diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt index 40debd3337..a2881b0125 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt @@ -64,7 +64,7 @@ class ArazzoReferenceResolver( ) } - val actionName = reference.name.removePrefix(prefixExpected) + val actionName = reference.name.removePrefix(prefixExpected + ".") val resolve = when(prefixExpected) { "successActions" -> components.successActions?.get(actionName) "failureActions" -> components.failureActions?.get(actionName) diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt index 4b29dcc3ad..6552352fff 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt @@ -88,10 +88,11 @@ class ArazzoLocalURLIssueTest { } // create arazzo from URL - arazzo = ArazzoAccess.getArazzoFromLocation(urlArazzoToTest, swagger) + val arazzo = ArazzoAccess.getArazzoFromLocation(urlArazzoToTest, swagger) // a valid arazzo is created with 3 workflows Assertions.assertTrue(arazzo.schemaParsed.workflows.size == 3) + Assertions.assertEquals("Petstore - Apply Coupons", arazzo.schemaParsed.info.title) } @Test From edc5a7e1def137f2f6a3cf5cd8b50f2f449e1f18 Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Sun, 19 Apr 2026 22:08:29 -0300 Subject: [PATCH 09/12] Resolver refs on inputs in Workflows --- .../core/problem/rest/arazzo/mapper/ArazzoMapper.kt | 12 ++++++++++++ .../core/problem/rest/arazzo/models/Workflow.kt | 2 ++ .../rest/arazzo/models/commons/WorkflowCommon.kt | 1 - .../problem/rest/arazzo/models/raws/WorkflowRaw.kt | 2 +- .../rest/arazzo/resolver/ArazzoReferenceResolver.kt | 2 +- 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/mapper/ArazzoMapper.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/mapper/ArazzoMapper.kt index 00c57a1212..22ea043a43 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/mapper/ArazzoMapper.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/mapper/ArazzoMapper.kt @@ -1,5 +1,6 @@ package org.evomaster.core.problem.rest.arazzo.mapper +import io.swagger.v3.oas.models.media.Schema import org.evomaster.core.problem.rest.arazzo.models.ArazzoSpecifications import org.evomaster.core.problem.rest.arazzo.models.Step import org.evomaster.core.problem.rest.arazzo.models.Workflow @@ -22,6 +23,7 @@ class ArazzoMapper( fun toDomain(raw: WorkflowRaw) : Workflow { return Workflow( common = raw, + inputs = toDomain(raw.inputs), steps = raw.steps.map { toDomain(it) }, successActions = resolver.resolveSuccessReusable(raw.successActions), failureActions = resolver.resolveFailureReusable(raw.failureActions), @@ -38,4 +40,14 @@ class ArazzoMapper( ) } + fun toDomain(schema: Schema<*>?) : Schema<*>? { + if (schema?.`$ref`?.isNotBlank() == true) { + val reference = toDomain(resolver.resolveJsonPointer(schema.`$ref`)) + return reference?.apply { + properties = properties?.mapValues { toDomain(it.value) } + } + } + return schema + } + } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt index 4a3496d362..6d6bb82675 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt @@ -1,9 +1,11 @@ package org.evomaster.core.problem.rest.arazzo.models +import io.swagger.v3.oas.models.media.Schema import org.evomaster.core.problem.rest.arazzo.models.commons.WorkflowCommon class Workflow( common: WorkflowCommon, + val inputs: Schema<*>?, val steps: List, val successActions: List?, val failureActions: List?, diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/WorkflowCommon.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/WorkflowCommon.kt index 20346089c3..b3bfb06c45 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/WorkflowCommon.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/WorkflowCommon.kt @@ -7,7 +7,6 @@ interface WorkflowCommon { val workflowId: String val summary: String? val description: String? - val inputs: Schema<*>? val dependsOn: List? val outputs: Map? } \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/WorkflowRaw.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/WorkflowRaw.kt index 7716a1d6fd..bf68807ab6 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/WorkflowRaw.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/WorkflowRaw.kt @@ -17,8 +17,8 @@ class WorkflowRaw( override val workflowId: String, override val summary: String?, override val description: String?, - override val inputs: Schema<*>?, override val dependsOn: List?, + val inputs: Schema<*>?, val steps: List, @JsonDeserialize(contentUsing = SuccessReusableDeserializer::class) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt index a2881b0125..332c5e8fcc 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt @@ -81,7 +81,7 @@ class ArazzoReferenceResolver( return resolve } - private fun resolveJsonPointer(reference: String) : Schema<*>? { + fun resolveJsonPointer(reference: String) : Schema<*>? { if (reference.startsWith("#/")) { return resolveJsonPointerLocal(reference) } From 4428c017af3479714484ebc3834201ae29d0c124 Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Mon, 20 Apr 2026 20:31:43 -0300 Subject: [PATCH 10/12] Delete validation and fixes --- .../rest/arazzo/parser/ArazzoValidator.kt | 392 ------------------ .../service/sampler/AbstractRestSampler.kt | 3 - .../rest/schema/ArazzoLocalURLIssueTest.kt | 49 +-- 3 files changed, 4 insertions(+), 440 deletions(-) delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt deleted file mode 100644 index 630d950c76..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoValidator.kt +++ /dev/null @@ -1,392 +0,0 @@ -package org.evomaster.core.problem.rest.arazzo.parser - -import org.evomaster.core.problem.rest.arazzo.models.Components -import org.evomaster.core.problem.rest.arazzo.models.Criterion -import org.evomaster.core.problem.rest.arazzo.models.CriterionExpression -import org.evomaster.core.problem.rest.arazzo.models.CriterionType -import org.evomaster.core.problem.rest.arazzo.models.FailureAction -import org.evomaster.core.problem.rest.arazzo.models.Parameter -import org.evomaster.core.problem.rest.arazzo.models.SourceDescription -import org.evomaster.core.problem.rest.arazzo.models.Step -import org.evomaster.core.problem.rest.arazzo.models.SuccessAction -import org.evomaster.core.problem.rest.arazzo.models.Workflow -import org.evomaster.core.problem.rest.arazzo.resolver.ArazzoReferenceResolver -import wiremock.com.jayway.jsonpath.JsonPath -import javax.xml.xpath.XPathFactory - -object ArazzoValidator { - - var resolver: ArazzoReferenceResolver = ArazzoReferenceResolver(null, null, null) - - private val possibleParameters = listOf("path","query","header","cookie") - private val possibleTypeSuccessActions = listOf("end","goto") - private val possibleTypeFailureActions = listOf("end","goto","retry") - private val possibleCriterionExpresionTypes = listOf("jsonpath","xpath") - private val possibleCriterionVersionXpath = listOf("xpath-30","xpath-20","xpath-10") - - private const val POSSIBLE_CRITERION_VERSION_JSON = "draft-goessner-dispatch-jsonpath-00" - - fun configResolver(resolver: ArazzoReferenceResolver) { - ArazzoValidator.resolver = resolver - } - - fun validateSourceDescriptions(sourceDescription: SourceDescription) { - val patterName = Regex("[A-Za-z0-9_\\-]+") - - if (!sourceDescription.name.matches(patterName)) { - throw IllegalArgumentException("Arazzo Parsing Error: The name should conform to the regular expression [A-Za-z0-9_\\-]+.") - } - } - - fun validateWorkflows(workflows: List) { - if (workflows.size != workflows.distinctBy { it.workflowId }.size) { - throw IllegalArgumentException("Arazzo Parsing Error: The id MUST be unique amongst all workflows described in the Arazzo Description. ") - } - workflows.forEach { workflow -> validateWorkflow(workflow) } - validateDependsOnWorkflows(workflows) - } - - private fun validateWorkflow(workflow: Workflow) { - val patternName = Regex("[A-Za-z0-9_\\-]+") - - // workflowId - if (!workflow.workflowId.matches(patternName)) { - throw IllegalArgumentException("Arazzo Parsing Error: The name should conform to the regular expression [A-Za-z0-9_\\-]+.") - } - //TODO: $sourceDescriptions..) Leer en la docu - - // Steps - if (workflow.steps.isEmpty()) { - throw IllegalArgumentException("Arazzo Parsing Error: The steps must have at least one step.") - } - - workflow.steps.forEach { step -> validateStep(step) } - - // successActions - //val successActions = resolver.resolveSuccessReusable(workflow.successActions) - //validateSuccessActions(successActions) - - // failureActions - //val failureActions = resolver.resolveFailureReusable(workflow.failureActions) - //validateFailureActions(failureActions) - - // outputs - val keyOutputRegex = Regex("^[a-zA-Z0-9\\.\\-_]+$") - workflow.outputs?.keys?.forEach { key -> - if (!keyOutputRegex.matches(key)) { - throw IllegalArgumentException( - "Arazzo Parsing Error: Output name in workflow: ${workflow.workflowId}: '$key' is invalid. " + - "Only alphanumeric characters, periods (.), hyphens (-) and underscores (_) are allowed." - ) - } - } - - // parameters - //val parameters = resolver.resolveParametersReusable(workflow.parameters) - //validateParameters(parameters) - } - - private fun validateSteps(steps: List) { - if (steps.isEmpty()) { - throw IllegalArgumentException("Arazzo Parsing Error: The steps must have at least one step.") - } - - //A unique successAction is defined by a name - val duplicates = steps - .groupBy { step -> step.stepId } - .filter { it.value.size > 1 } - .keys - - if (duplicates.isNotEmpty()) { - throw IllegalArgumentException("Arazzo Parsing Error: Steps repeated. $duplicates") - } - - steps.forEach { step -> validateStep(step) } - } - - private fun validateStep(step: Step) { - val pattern = Regex("[A-Za-z0-9_\\-]+") - - if (!step.stepId.matches(pattern)) { - throw IllegalArgumentException("Arazzo Parsing Error: The stepId: ${step.stepId} should conform to the regular expression [A-Za-z0-9_\\-]+.") - } - - if (!((step.operationId != null) xor (step.operationPath != null))) { - throw IllegalArgumentException("Arazzo Parsing Error: Step: operationId and operationPath are mutually exclusive.") - } - - //TODO: Validar operationId y operationPath - - if (!((step.workflowId != null) xor (step.operationId != null))) { - throw IllegalArgumentException("Arazzo Parsing Error: Step: workflowId and operationId are mutually exclusive.") - } - - if (!((step.workflowId != null) xor (step.operationPath != null))) { - throw IllegalArgumentException("Arazzo Parsing Error: Step: workflowId and operationPath are mutually exclusive.") - } - - //val parameters = resolver.resolveParametersReusable(step.parameters) - //validateParameters(parameters) - - //TODO: Validar requestBody - - step.successCriteria?.forEach { criterion -> validateCriterion(criterion) } - - //val onSuccess = resolver.resolveSuccessReusable(step.onSuccess) - //validateSuccessActions(onSuccess) - - //val onFailure = resolver.resolveFailureReusable(step.onFailure) - //validateFailureActions(onFailure) - - val keyOutputRegex = Regex("^[a-zA-Z0-9\\.\\-_]+$") - step.outputs?.keys?.forEach { key -> - if (!keyOutputRegex.matches(key)) { - throw IllegalArgumentException( - "Arazzo Parsing Error: Output name in step: ${step.stepId}: '$key' is invalid. " + - "Only alphanumeric characters, periods (.), hyphens (-) and underscores (_) are allowed." - ) - } - } - } - - private fun validateDependsOnWorkflows(workflows: List) { - val workflowsMap = workflows.associateBy { it.workflowId } - - // Validate existence - workflows.forEach { workflow -> - workflow.dependsOn?.forEach { dependencyId -> - if (!workflowsMap.containsKey(dependencyId)) { - throw IllegalArgumentException( - "Arazzo Parsing Error: The workflow '${workflow.workflowId}' depends on '$dependencyId', " + - "but that workflowId does not exist in the document." - ) - } - } - } - - // DFS for dependencies - // The visit states map helps track our progress: - // 0 or null = Unvisited - // 1 = Visiting (currently in the recursion stack) - // 2 = Fully Validated (safe, no cycles detected from here) - val visitStates = mutableMapOf() - - fun detectCycleDFS(currentWorkflowId: String, currentPath: List) { - val state = visitStates[currentWorkflowId] ?: 0 - - if (state == 1) { - val cyclePath = currentPath.joinToString(" -> ") + " -> $currentWorkflowId" - throw IllegalArgumentException("Arazzo Parsing Error: Cyclic dependency detected. Workflows cannot depend on themselves in a closed loop.") - } - - if (state == 2) { - return - } - - visitStates[currentWorkflowId] = 1 - - val dependencies = workflowsMap[currentWorkflowId]?.dependsOn ?: emptyList() - dependencies.forEach { dependencyId -> - detectCycleDFS(dependencyId, currentPath + currentWorkflowId) - } - - visitStates[currentWorkflowId] = 2 - } - - // Execute the validator for all workflows - workflowsMap.keys.forEach { workflowId -> - if ((visitStates[workflowId] ?: 0) == 0) { - detectCycleDFS(workflowId, emptyList()) - } - } - } - - fun validateComponents(components: Components?) { - if (components == null) return - - val componentKeyRegex = Regex("^[a-zA-Z0-9\\.\\-_]+$") - fun validateMapKeys(map: Map?, componentType: String) { - map?.keys?.forEach { key -> - if (!componentKeyRegex.matches(key)) { - throw IllegalArgumentException( - "Arazzo Parsing Error: Component name:'$key' in '$componentType' is invalid. " + - "Only alphanumeric characters, periods (.), hyphens (-) and underscores (_) are allowed." - ) - } - } - } - - validateMapKeys(components.inputs, "inputs") - validateMapKeys(components.parameters, "parameters") - validateMapKeys(components.successActions, "successActions") - validateMapKeys(components.failureActions, "failureActions") - - components.parameters?.let { paremeters -> validateParameters(paremeters) } - components.successActions?.forEach { successAction -> validateSuccessAction(successAction.value) } - components.failureActions?.forEach { failureActions -> validateFailureAction(failureActions.value) } - } - - private fun validateParameters(parameters: Map?) { - if (parameters == null) return; - - //A unique parameter is defined by the combination of a name and in fields - val duplicates = parameters.values.groupBy { Pair(it.name, it.location) } - .filter { (_, list) -> list.size > 1 } - - if (duplicates.isNotEmpty()) { - val errorMessages = duplicates.map { (key, list) -> - "The Parameter [name='${key.first}', in='${key.second}'] is repeated ${list.size} times." - } - throw IllegalArgumentException("Arazzo Parsing Error: Parameters repeated. " + errorMessages.joinToString("\n")) - } - - parameters.values.forEach { parameter -> - if (parameter.location != null && parameter.location !in possibleParameters) { - throw IllegalArgumentException("Arazzo Parsing Error: Parameters must be one of the list: \"path\",\"query\",\"header\",\"cookie\"") - } - } - } - - private fun validateParameters(parameters: List) { - //A unique parameter is defined by the combination of a name and in fields - val duplicates = parameters.groupBy { Pair(it.name, it.location) } - .filter { (_, list) -> list.size > 1 } - - if (duplicates.isNotEmpty()) { - val errorMessages = duplicates.map { (key, list) -> - "The Parameter [name='${key.first}', in='${key.second}'] is repeated ${list.size} times." - } - throw IllegalArgumentException("Arazzo Parsing Error: Parameters repeated. " + errorMessages.joinToString("\n")) - } - - //Only Parameters in workflow context, option "in" is mandatory - parameters.forEach { parameter -> - if (parameter.location == null || parameter.location !in possibleParameters) { - throw IllegalArgumentException("Arazzo Parsing Error: Parameters must be one of the list: \"path\",\"query\",\"header\",\"cookie\"") - } - } - } - - private fun validateSuccessActions(successActions: List) { - //A unique successAction is defined by a name - val duplicates = successActions - .groupBy { action -> action.name } - .filter { it.value.size > 1 } - .keys - - if (duplicates.isNotEmpty()) { - throw IllegalArgumentException("Arazzo Parsing Error: SuccesActions repeated. $duplicates") - } - - successActions.forEach { action -> validateSuccessAction(action) } - } - - private fun validateSuccessAction(successAction: SuccessAction) { - if (successAction.type !in possibleTypeSuccessActions) { - throw IllegalArgumentException("Arazzo Parsing Error: successAction.type must be one of the list: \"end\",\"goto\"") - } - - if (successAction.type == "goto") { - if (!((successAction.workflowId != null) xor (successAction.stepId != null))) { - throw IllegalArgumentException("Arazzo Parsing Error: SuccesAction: workflowId and stepId are mutually exclusive.") - } - //TODO Añadir la referencia del workflow y stepId - } - - successAction.criteria?.forEach { criterion -> validateCriterion(criterion) } - - } - - private fun validateFailureActions(failureActions: List) { - //A unique successAction is defined by a name - val duplicates = failureActions - .groupBy { action -> action.name } - .filter { it.value.size > 1 } - .keys - - if (duplicates.isNotEmpty()) { - throw IllegalArgumentException("Arazzo Parsing Error: FailureActions repeated. $duplicates") - } - - failureActions.forEach { action -> validateFailureAction(action) } - } - - private fun validateFailureAction(failureAction: FailureAction) { - if (failureAction.type !in possibleTypeFailureActions) { - throw IllegalArgumentException("Arazzo Parsing Error: failureAction.type must be one of the list: \"end\",\"goto\",\"retry\"") - } - - if (failureAction.type == "goto" || failureAction.type == "retry") { - if (!((failureAction.workflowId != null) xor (failureAction.stepId != null))) { - throw IllegalArgumentException("Arazzo Parsing Error: FailureAction: workflowId and stepId are mutually exclusive.") - } - //TODO Añadir la referencia del workflow y stepId - } - - if (failureAction.type == "retry") { - if (failureAction.retryAfter != null && failureAction.retryAfter.toDouble() <= 0) { - throw IllegalArgumentException("Arazzo Parsing Error: FailureAction: retryAfter must be non-negative decimal number.") - } - - if (failureAction.retryLimit != null && failureAction.retryLimit <= 0) { - throw IllegalArgumentException("Arazzo Parsing Error: FailureAction: retryLimit must be non-negative integer number.") - } - } - - failureAction.criteria?.forEach { criterion -> validateCriterion(criterion) } - } - - private fun validateCriterion(criterion: Criterion?) { - if (criterion == null) return - - if (criterion.type != null && criterion.context == null) { - throw IllegalArgumentException("Arazzo Parsing Error: Criterion Object. If \"type\" is specified, then the context MUST be provided.") - } - - val type: String? - when(val criterionType = criterion.type) { - is CriterionType.Simple -> type = criterionType.value - is CriterionType.Complex -> { - validateCriterionExpresion(criterionType.expr) - type = criterionType.expr.type - } - null -> type = "simple" - } - - try { - when (type.lowercase()) { - "regex" -> Regex(criterion.condition) - "jsonpath" -> JsonPath.compile(criterion.condition) - "simple" -> SimpleConditionParser(criterion.condition).validateOrThrow() - "xpath" -> { - val xpathFactory = XPathFactory.newInstance() - val xpath = xpathFactory.newXPath() - xpath.compile(criterion.condition) - } - else -> throw IllegalArgumentException("Invalid Criteron Type") - } - } catch(e: Exception) { - throw IllegalArgumentException("Arazzo Parsing Error: Criterion condition error - ${criterion.condition}: ${e.message}") - } - } - - private fun validateCriterionExpresion(criterionExpresion: CriterionExpression) { - if (criterionExpresion.type !in possibleCriterionExpresionTypes) { - throw IllegalArgumentException("Arazzo Parsing Error: Criterion Expresion type invalid. The options allowed are jsonpath or xpath.") - } - - when(criterionExpresion.type) { - "jsonpath" -> { - if (!criterionExpresion.version.equals(POSSIBLE_CRITERION_VERSION_JSON)) { - throw IllegalArgumentException("Arazzo Parsing Error: The allowed values for JSONPath are draft-goessner-dispatch-jsonpath-00") - } - } - "xpath" -> { - if (criterionExpresion.version !in possibleCriterionVersionXpath) { - throw IllegalArgumentException("Arazzo Parsing Error: The allowed values for XPath are xpath-30, xpath-20, or xpath-10.") - } - } - } - } - -} \ No newline at end of file diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt index d2d2f1a78d..f70f6cb8e4 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt @@ -120,9 +120,6 @@ abstract class AbstractRestSampler : HttpWsSampler() { schemaHolder = RestSchema(transformed) schemaHolder.validate() - val arazzoURL = problem.arazzoURL - val arazzo = ArazzoAccess.getArazzoFromLocation(arazzoURL, swagger) - // The code should never reach this line without a valid swagger. actionCluster.clear() val skip = EndpointFilter.getEndpointsToSkip(config, schemaHolder, infoDto) diff --git a/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt index 6552352fff..096ac87712 100644 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt +++ b/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt @@ -7,15 +7,8 @@ import org.junit.jupiter.api.Test import java.util.* /* -Testing the local URL issue with OpenAPI, 4 test cases: -1. A local file which exists and the provided URL is valid. (FALTA) -2. A local file does not exist but the provided URL is valid. (FALTA) -3. A local file exists but the provided URL is not valid. (FALTA) -4. A local file does not exist and the provided URL is not valid. (FALTA) -5. A local file which exists but the relative file path is provided. (FALTA) -6. Two different exceptions in Windows and others (e.g., "URI Path Component is empty" is tested here). -7. The swagger is an existing valid json file, but it is not a valid swagger. (FALTA) -8: The swagger is an invalid json file. (FALTA) +Testing the local URL issue with Arazzo: +1. A local file which exists and the provided URL is valid. */ class ArazzoLocalURLIssueTest { @@ -23,7 +16,7 @@ class ArazzoLocalURLIssueTest { companion object { // execution path, it can be different from one machine to another - private var executionPath :String = System.getProperty("user.dir") + private var executionPath: String = System.getProperty("user.dir") // swagger object private lateinit var swagger: SchemaOpenAPI @@ -71,8 +64,7 @@ class ArazzoLocalURLIssueTest { // but in Windows, it has to have just one file:/ val urlToTest = if (hostOs.contains("win")) { "file:/${swaggerTestDirectory}/openapi_pet.json" - } - else { + } else { "file://${swaggerTestDirectory}/openapi_pet.json" } @@ -95,37 +87,4 @@ class ArazzoLocalURLIssueTest { Assertions.assertEquals("Petstore - Apply Coupons", arazzo.schemaParsed.info.title) } - @Test - fun testNonExistingFileValidURL() { - - } - - @Test - fun testExistingFileInvalidURL() { - - } - - @Test - fun testNonExistingFileInvalidURL() { - - } - - - @Test - fun testRelativeFilePathExistingFile() { - - } - - @Test - fun testFileNameOnlyNonExistingFile() { - - } - - @Test - fun testInvalidSwagger() { - } - - @Test - fun testInvalidJSON() { - } } \ No newline at end of file From e4d50d7a44ecc4e3cee4e62c752c62003f280878 Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Mon, 20 Apr 2026 21:04:49 -0300 Subject: [PATCH 11/12] clean import --- .../core/problem/rest/service/sampler/AbstractRestSampler.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt index f70f6cb8e4..344f2546bb 100644 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt +++ b/core/src/main/kotlin/org/evomaster/core/problem/rest/service/sampler/AbstractRestSampler.kt @@ -17,7 +17,6 @@ import org.evomaster.core.problem.externalservice.httpws.service.HttpWsExternalS import org.evomaster.core.problem.httpws.HttpWsAction import org.evomaster.core.problem.httpws.service.HttpWsSampler import org.evomaster.core.problem.rest.* -import org.evomaster.core.problem.rest.arazzo.parser.ArazzoParser import org.evomaster.core.problem.rest.builder.RestActionBuilderV3 import org.evomaster.core.problem.rest.builder.RestActionBuilderV3.buildActionBasedOnUrl import org.evomaster.core.problem.rest.data.HttpVerb @@ -25,7 +24,6 @@ import org.evomaster.core.problem.rest.data.RestCallAction import org.evomaster.core.problem.rest.data.RestIndividual import org.evomaster.core.problem.rest.param.HeaderParam import org.evomaster.core.problem.rest.param.QueryParam -import org.evomaster.core.problem.rest.schema.ArazzoAccess import org.evomaster.core.problem.rest.schema.OpenApiAccess import org.evomaster.core.problem.rest.schema.RestSchema import org.evomaster.core.problem.rest.schema.SchemaLocation From cf0fb9c6c2b63b2e930a955ccdd4864f28744785 Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Mon, 20 Apr 2026 21:06:05 -0300 Subject: [PATCH 12/12] Fix old code --- .../java/controller/api/dto/problem/RestProblemDto.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/RestProblemDto.java b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/RestProblemDto.java index 34cb3170f5..fba150597a 100644 --- a/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/RestProblemDto.java +++ b/client-java/controller-api/src/main/java/org/evomaster/client/java/controller/api/dto/problem/RestProblemDto.java @@ -30,9 +30,4 @@ public class RestProblemDto extends ProblemInfoDto{ public List derivedParams; - - /** - * The full URL of where the Arazzo Specifications schema can be located. - */ - public String arazzoURL; }