From 4d30c611ba90cac0603779e160ffc5a800c9016f Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Wed, 1 Apr 2026 22:06:24 -0300 Subject: [PATCH 01/15] 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/15] 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/15] 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/15] #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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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/15] 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; } From cc951d38064283f4bbfc9f0aa905d17fcc272eb0 Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Fri, 1 May 2026 19:26:41 -0300 Subject: [PATCH 13/15] Create Parser in module core-extra --- core-extra/arazzo-parser/pom.xml | 84 ++ .../evomaster/arazzo/access/ArazzoAccess.java | 36 + .../AnyExpressionDeserializer.java | 25 + .../CriterionTypeDeserializer.java | 30 + .../FailureReusableDeserializer.java | 26 + .../ParameterReusableDeserializer.java | 27 + .../SuccessReusableDeserializer.java | 29 + .../evomaster/arazzo/mapper/ArazzoMapper.java | 86 ++ .../arazzo/models/domain/AnyExpression.java | 33 + .../models/domain/ArazzoSpecifications.java | 62 + .../arazzo/models/domain/Components.java | 46 + .../arazzo/models/domain/Criterion.java | 39 + .../models/domain/CriterionExpression.java | 25 + .../arazzo/models/domain/CriterionType.java | 31 + .../arazzo/models/domain/FailureAction.java | 72 + .../arazzo/models/domain/FailureReusable.java | 31 + .../arazzo/models/domain/InfoArazzo.java | 43 + .../arazzo/models/domain/Parameter.java | 39 + .../models/domain/ParameterReusable.java | 31 + .../models/domain/PayloadReplacement.java | 30 + .../arazzo/models/domain/RequestBody.java | 38 + .../arazzo/models/domain/Reusable.java | 25 + .../models/domain/SourceDescription.java | 34 + .../evomaster/arazzo/models/domain/Step.java | 110 ++ .../arazzo/models/domain/SuccessAction.java | 54 + .../arazzo/models/domain/SuccessReusable.java | 31 + .../arazzo/models/domain/Workflow.java | 105 ++ .../models/dto/ArazzoSpecificationsDTO.java | 58 + .../evomaster/arazzo/models/dto/StepDTO.java | 121 ++ .../arazzo/models/dto/WorkflowDTO.java | 115 ++ .../evomaster/arazzo/parser/ArazzoParser.java | 45 + .../resolver/ArazzoReferenceResolver.java | 173 +++ .../test/java/arazzo/ArazzoParserTest.java | 39 + .../src/test/resources/arazzo/arazzo_pet.json | 248 ++++ .../src/test/resources/arazzo/arazzo_pet.yaml | 160 +++ .../test/resources/openapi/openapi_pet.json | 1225 +++++++++++++++++ core-extra/pom.xml | 1 + 37 files changed, 3407 insertions(+) create mode 100644 core-extra/arazzo-parser/pom.xml create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/access/ArazzoAccess.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/AnyExpressionDeserializer.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/CriterionTypeDeserializer.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/FailureReusableDeserializer.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/ParameterReusableDeserializer.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/SuccessReusableDeserializer.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/mapper/ArazzoMapper.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/AnyExpression.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ArazzoSpecifications.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Components.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Criterion.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionExpression.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionType.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureAction.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureReusable.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/InfoArazzo.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Parameter.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ParameterReusable.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/PayloadReplacement.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/RequestBody.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Reusable.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SourceDescription.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Step.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessAction.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessReusable.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Workflow.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/ArazzoSpecificationsDTO.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/StepDTO.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/WorkflowDTO.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/parser/ArazzoParser.java create mode 100644 core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/resolver/ArazzoReferenceResolver.java create mode 100644 core-extra/arazzo-parser/src/test/java/arazzo/ArazzoParserTest.java create mode 100644 core-extra/arazzo-parser/src/test/resources/arazzo/arazzo_pet.json create mode 100644 core-extra/arazzo-parser/src/test/resources/arazzo/arazzo_pet.yaml create mode 100644 core-extra/arazzo-parser/src/test/resources/openapi/openapi_pet.json diff --git a/core-extra/arazzo-parser/pom.xml b/core-extra/arazzo-parser/pom.xml new file mode 100644 index 0000000000..13b6528ee2 --- /dev/null +++ b/core-extra/arazzo-parser/pom.xml @@ -0,0 +1,84 @@ + + + 4.0.0 + + org.evomaster + evomaster-core-extra + 5.1.1-SNAPSHOT + + + arazzo-parser + + + + io.swagger.core.v3 + swagger-models + 2.2.34 + compile + + + org.mock-server + mockserver-client-java-no-dependencies + compile + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + io.swagger.core.v3 + swagger-core + 2.2.34 + compile + + + com.github.tomakehurst + wiremock-jre8-standalone + compile + + + junit + junit + 4.13.1 + test + + + io.swagger.parser.v3 + swagger-parser-v3 + 2.1.33 + test + + + com.graphql-java + java-dataloader + 2.2.3 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + + + + org.apache.maven.plugins + maven-surefire-plugin + + @{argLine} -ea -Xms512m -Xmx1536m -Xss2m -Dfile.encoding=UTF-8 -Duser.language=en -Duser.country=GB + + + + + + \ No newline at end of file diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/access/ArazzoAccess.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/access/ArazzoAccess.java new file mode 100644 index 0000000000..235d466cfd --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/access/ArazzoAccess.java @@ -0,0 +1,36 @@ +package org.evomaster.arazzo.access; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +public class ArazzoAccess { + public static String readFromDisk(String arazzoLocation) throws Exception { + String fileScheme = "file:"; + + Path path; + try { + if (arazzoLocation.toLowerCase().startsWith(fileScheme)) { + path = Paths.get(URI.create(arazzoLocation)); + } else { + path = Paths.get(arazzoLocation); + } + } catch (Exception e) { + throw new Exception("The file path provided for the Arazzo Schema " + arazzoLocation + " ended up with the following error: " + e.getMessage()); + } + + if ((!Files.exists(path))) { + throw new Exception("The provided Arazzo file does not exist: " + arazzoLocation); + } + + try { + byte[] bytes = Files.readAllBytes(path); + return new String(bytes, StandardCharsets.UTF_8); + } catch (Exception e) { + throw new Exception("Error reading the Arazzo file: " + e.getMessage()); + } + + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/AnyExpressionDeserializer.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/AnyExpressionDeserializer.java new file mode 100644 index 0000000000..5f1896f503 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/AnyExpressionDeserializer.java @@ -0,0 +1,25 @@ +package org.evomaster.arazzo.deserializer; + +import com.fasterxml.jackson.core.JacksonException; +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.arazzo.models.domain.AnyExpression; + +import java.io.IOException; + +public class AnyExpressionDeserializer extends JsonDeserializer { + + @Override + public AnyExpression deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + if (node.isTextual()) { + return new AnyExpression.Expression(node.asText()); + } + JsonNode any = jsonParser.getCodec().treeToValue(node, JsonNode.class); + return new AnyExpression.Any(any); + } + +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/CriterionTypeDeserializer.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/CriterionTypeDeserializer.java new file mode 100644 index 0000000000..8de2b4701b --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/CriterionTypeDeserializer.java @@ -0,0 +1,30 @@ +package org.evomaster.arazzo.deserializer; + +import com.fasterxml.jackson.core.JacksonException; +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.arazzo.models.domain.CriterionExpression; +import org.evomaster.arazzo.models.domain.CriterionType; + +import java.io.IOException; + +public class CriterionTypeDeserializer extends JsonDeserializer { + + @Override + public CriterionType deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + if (node.isTextual()) { + return new CriterionType.Simple(node.asText()); + } else if (node.isObject()) { + CriterionExpression complex = jsonParser.getCodec().treeToValue(node, CriterionExpression.class); + return new CriterionType.Complex(complex); + } else { + throw new IllegalArgumentException("Arazzo Parsing Error: Invalid " + node.getNodeType() + ". Expected string or Criterion Expression"); + } + + } + +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/FailureReusableDeserializer.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/FailureReusableDeserializer.java new file mode 100644 index 0000000000..441c789236 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/FailureReusableDeserializer.java @@ -0,0 +1,26 @@ +package org.evomaster.arazzo.deserializer; + +import com.fasterxml.jackson.core.JacksonException; +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.arazzo.models.domain.*; + +import java.io.IOException; + +public class FailureReusableDeserializer extends JsonDeserializer { + + @Override + public FailureReusable deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + if (node.has("reference")) { + Reusable reusable = jsonParser.getCodec().treeToValue(node, Reusable.class); + return new FailureReusable.ReusableObj(reusable); + } + + FailureAction action = jsonParser.getCodec().treeToValue(node, FailureAction.class); + return new FailureReusable.Failure(action); + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/ParameterReusableDeserializer.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/ParameterReusableDeserializer.java new file mode 100644 index 0000000000..80a47f52af --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/ParameterReusableDeserializer.java @@ -0,0 +1,27 @@ +package org.evomaster.arazzo.deserializer; + +import com.fasterxml.jackson.core.JacksonException; +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.arazzo.models.domain.*; + +import java.io.IOException; + +public class ParameterReusableDeserializer extends JsonDeserializer { + + @Override + public ParameterReusable deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + if (node.has("reference")) { + Reusable reusable = jsonParser.getCodec().treeToValue(node, Reusable.class); + return new ParameterReusable.ReusableObj(reusable); + } + + Parameter parameter = jsonParser.getCodec().treeToValue(node, Parameter.class); + return new ParameterReusable.Param(parameter); + } + +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/SuccessReusableDeserializer.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/SuccessReusableDeserializer.java new file mode 100644 index 0000000000..e1381da9b2 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/SuccessReusableDeserializer.java @@ -0,0 +1,29 @@ +package org.evomaster.arazzo.deserializer; + +import com.fasterxml.jackson.core.JacksonException; +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.arazzo.models.domain.Reusable; +import org.evomaster.arazzo.models.domain.SuccessAction; +import org.evomaster.arazzo.models.domain.SuccessReusable; + +import java.io.IOException; + +public class SuccessReusableDeserializer extends JsonDeserializer { + + @Override + public SuccessReusable deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + if (node.has("reference")) { + Reusable reusable = jsonParser.getCodec().treeToValue(node, Reusable.class); + return new SuccessReusable.ReusableObj(reusable); + } + + SuccessAction action = jsonParser.getCodec().treeToValue(node, SuccessAction.class); + return new SuccessReusable.Success(action); + } + +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/mapper/ArazzoMapper.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/mapper/ArazzoMapper.java new file mode 100644 index 0000000000..55f35400c8 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/mapper/ArazzoMapper.java @@ -0,0 +1,86 @@ +package org.evomaster.arazzo.mapper; + +import org.evomaster.arazzo.models.domain.ArazzoSpecifications; +import org.evomaster.arazzo.models.domain.Step; +import org.evomaster.arazzo.models.domain.Workflow; +import org.evomaster.arazzo.models.dto.ArazzoSpecificationsDTO; +import org.evomaster.arazzo.models.dto.StepDTO; +import org.evomaster.arazzo.models.dto.WorkflowDTO; +import org.evomaster.arazzo.resolver.ArazzoReferenceResolver; +import io.swagger.v3.oas.models.media.Schema; + +import java.util.Map; +import java.util.stream.Collectors; + +public class ArazzoMapper { + private ArazzoReferenceResolver resolver; + + public ArazzoMapper(ArazzoReferenceResolver resolver) { + this.resolver = resolver; + } + + public ArazzoSpecifications toDomain(ArazzoSpecificationsDTO arazzoSpecificationsDTO) { + return ArazzoSpecifications.builder() + .arazzo(arazzoSpecificationsDTO.getArazzo()) + .info(arazzoSpecificationsDTO.getInfo()) + .sourceDescriptions(arazzoSpecificationsDTO.getSourceDescriptions()) + .workflows(arazzoSpecificationsDTO.getWorkflows().stream() + .map(this::toDomain) + .collect(Collectors.toList())) + .components(arazzoSpecificationsDTO.getComponents()) + .build(); + } + + public Workflow toDomain(WorkflowDTO workflowDTO) { + return Workflow.builder() + .workflowId(workflowDTO.getWorkflowId()) + .summary(workflowDTO.getSummary()) + .description(workflowDTO.getDescription()) + .inputs(this.toDomain(workflowDTO.getInputs())) + .dependsOn(workflowDTO.getDependsOn()) + .steps(workflowDTO.getSteps().stream() + .map(this::toDomain) + .collect(Collectors.toList())) + .successActions(resolver.resolveSuccessReusable(workflowDTO.getSuccessActions())) + .failureActions(resolver.resolveFailureReusable(workflowDTO.getFailureActions())) + .outputs(workflowDTO.getOutputs()) + .parameters(resolver.resolveParametersReusable(workflowDTO.getParameters())) + .build(); + } + + public Step toDomain(StepDTO stepDTO) { + return Step.builder() + .description(stepDTO.getDescription()) + .stepId(stepDTO.getStepId()) + .operationId(stepDTO.getOperationId()) + .operationPath(stepDTO.getOperationPath()) + .workflowId(stepDTO.getWorkflowId()) + .parameters(resolver.resolveParametersReusable(stepDTO.getParameters())) + .requestBody(stepDTO.getRequestBody()) + .successCriteria(stepDTO.getSuccessCriteria()) + .onSuccess(resolver.resolveSuccessReusable(stepDTO.getOnSuccess())) + .onFailure(resolver.resolveFailureReusable(stepDTO.getOnFailure())) + .outputs(stepDTO.getOutputs()) + .build(); + } + + public Schema toDomain(Schema schema) { + if (schema != null && schema.get$ref() != null && !schema.get$ref().trim().isEmpty()) { + Schema reference = toDomain(resolver.resolveJsonPointer(schema.get$ref())); + if (reference != null) { + if (reference.getProperties() != null) { + Map updatedProperties = reference.getProperties().entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, entry -> toDomain(entry.getValue()) + )); + + reference.setProperties(updatedProperties); + } + + return reference; + } + } + + return schema; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/AnyExpression.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/AnyExpression.java new file mode 100644 index 0000000000..a2353cf230 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/AnyExpression.java @@ -0,0 +1,33 @@ +package org.evomaster.arazzo.models.domain; + +import com.fasterxml.jackson.databind.JsonNode; + +public abstract class AnyExpression { + + public AnyExpression() { + } + + public static class Any extends AnyExpression { + private final JsonNode jsonNode; + + public Any(JsonNode jsonNode) { + this.jsonNode = jsonNode; + } + + public JsonNode getJsonNode() { + return jsonNode; + } + } + + public static class Expression extends AnyExpression { + private final String expression; + + public Expression(String expression) { + this.expression = expression; + } + + public String getExpression() { + return expression; + } + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ArazzoSpecifications.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ArazzoSpecifications.java new file mode 100644 index 0000000000..89281e49dd --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ArazzoSpecifications.java @@ -0,0 +1,62 @@ +package org.evomaster.arazzo.models.domain; + +import java.util.List; + +public class ArazzoSpecifications { + private String arazzo; + private InfoArazzo info; + private List sourceDescriptions; + private List workflows; + private Components components; + + private ArazzoSpecifications(Builder builder) { + this.arazzo = builder.arazzo; + this.info = builder.info; + this.sourceDescriptions = builder.sourceDescriptions; + this.workflows = builder.workflows; + this.components = builder.components; + } + + public String getArazzo() { + return arazzo; + } + + public InfoArazzo getInfo() { + return info; + } + + public List getSourceDescriptions() { + return sourceDescriptions; + } + + public List getWorkflows() { + return workflows; + } + + public Components getComponents() { + return components; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String arazzo; + private InfoArazzo info; + private List sourceDescriptions; + private List workflows; + private Components components; + + public Builder arazzo(String arazzo) { this.arazzo = arazzo; return this; } + public Builder info(InfoArazzo info) { this.info = info; return this; } + public Builder sourceDescriptions(List sourceDescriptions) { this.sourceDescriptions = sourceDescriptions; return this; } + public Builder workflows(List workflows) { this.workflows = workflows; return this; } + public Builder components(Components components) { this.components = components; return this; } + + public ArazzoSpecifications build() { + return new ArazzoSpecifications(this); + } + + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Components.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Components.java new file mode 100644 index 0000000000..e87719c5a7 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Components.java @@ -0,0 +1,46 @@ +package org.evomaster.arazzo.models.domain; + +import java.util.Map; +import io.swagger.v3.oas.models.media.Schema; + +public class Components { + private Map> inputs; + private Map parameters; + private Map successAction; + private Map failureAction; + + public Components() { + } + + public Map> getInputs() { + return inputs; + } + + public void setInputs(Map> inputs) { + this.inputs = inputs; + } + + public Map getParameters() { + return parameters; + } + + public void setParameters(Map parameters) { + this.parameters = parameters; + } + + public Map getSuccessAction() { + return successAction; + } + + public void setSuccessAction(Map successAction) { + this.successAction = successAction; + } + + public Map getFailureAction() { + return failureAction; + } + + public void setFailureAction(Map failureAction) { + this.failureAction = failureAction; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Criterion.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Criterion.java new file mode 100644 index 0000000000..8117bb5ab8 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Criterion.java @@ -0,0 +1,39 @@ +package org.evomaster.arazzo.models.domain; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.evomaster.arazzo.deserializer.CriterionTypeDeserializer; + +public class Criterion { + private String context; + private String condition; + + @JsonDeserialize(using = CriterionTypeDeserializer.class) + private CriterionType type; + + public Criterion() { + } + + public String getContext() { + return context; + } + + public void setContext(String context) { + this.context = context; + } + + public String getCondition() { + return condition; + } + + public void setCondition(String condition) { + this.condition = condition; + } + + public CriterionType getType() { + return type; + } + + public void setType(CriterionType type) { + this.type = type; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionExpression.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionExpression.java new file mode 100644 index 0000000000..f7977b7690 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionExpression.java @@ -0,0 +1,25 @@ +package org.evomaster.arazzo.models.domain; + +public class CriterionExpression { + private String type; + private String version; + + public CriterionExpression() { + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionType.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionType.java new file mode 100644 index 0000000000..f573abb563 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionType.java @@ -0,0 +1,31 @@ +package org.evomaster.arazzo.models.domain; + +public abstract class CriterionType { + + public CriterionType() { + } + + public static class Simple extends CriterionType { + private final String value; + + public Simple(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public static class Complex extends CriterionType { + private final CriterionExpression expression; + + public Complex(CriterionExpression expression) { + this.expression = expression; + } + + public CriterionExpression getExpression() { + return expression; + } + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureAction.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureAction.java new file mode 100644 index 0000000000..70d0524117 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureAction.java @@ -0,0 +1,72 @@ +package org.evomaster.arazzo.models.domain; + +import java.util.List; + +public class FailureAction { + private String name; + private String type; + private String workflowId; + private String stepId; + private Number retryAfter; + private Integer retryLimit; + private List criteria; + + public FailureAction() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getWorkflowId() { + return workflowId; + } + + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + public Number getRetryAfter() { + return retryAfter; + } + + public void setRetryAfter(Number retryAfter) { + this.retryAfter = retryAfter; + } + + public String getStepId() { + return stepId; + } + + public void setStepId(String stepId) { + this.stepId = stepId; + } + + public Integer getRetryLimit() { + return retryLimit; + } + + public void setRetryLimit(Integer retryLimit) { + this.retryLimit = retryLimit; + } + + public List getCriteria() { + return criteria; + } + + public void setCriteria(List criteria) { + this.criteria = criteria; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureReusable.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureReusable.java new file mode 100644 index 0000000000..fcf259d4aa --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureReusable.java @@ -0,0 +1,31 @@ +package org.evomaster.arazzo.models.domain; + +public abstract class FailureReusable { + + public FailureReusable() { + } + + public static class Failure extends FailureReusable { + private final FailureAction action; + + public Failure(FailureAction action) { + this.action = action; + } + + public FailureAction getAction() { + return action; + } + } + + public static class ReusableObj extends FailureReusable { + private final Reusable reusable; + + public ReusableObj(Reusable reusable) { + this.reusable = reusable; + } + + public Reusable getReusable() { + return reusable; + } + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/InfoArazzo.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/InfoArazzo.java new file mode 100644 index 0000000000..84b7350ca6 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/InfoArazzo.java @@ -0,0 +1,43 @@ +package org.evomaster.arazzo.models.domain; + +public class InfoArazzo { + private String title; + private String summary; + private String description; + private String version; + + public InfoArazzo() { + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Parameter.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Parameter.java new file mode 100644 index 0000000000..fd2047e629 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Parameter.java @@ -0,0 +1,39 @@ +package org.evomaster.arazzo.models.domain; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.evomaster.arazzo.deserializer.AnyExpressionDeserializer; + +public class Parameter { + private String name; + private String in; + + @JsonDeserialize(using = AnyExpressionDeserializer.class) + private AnyExpression value; + + public Parameter() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getIn() { + return in; + } + + public void setIn(String in) { + this.in = in; + } + + public AnyExpression getValue() { + return value; + } + + public void setValue(AnyExpression value) { + this.value = value; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ParameterReusable.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ParameterReusable.java new file mode 100644 index 0000000000..b243cdf27f --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ParameterReusable.java @@ -0,0 +1,31 @@ +package org.evomaster.arazzo.models.domain; + +public abstract class ParameterReusable { + + public ParameterReusable() { + } + + public static class Param extends ParameterReusable { + private final Parameter parameter; + + public Param(Parameter parameter) { + this.parameter = parameter; + } + + public Parameter getParameter() { + return parameter; + } + } + + public static class ReusableObj extends ParameterReusable { + private final Reusable reusable; + + public ReusableObj(Reusable reusable) { + this.reusable = reusable; + } + + public Reusable getReusable() { + return reusable; + } + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/PayloadReplacement.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/PayloadReplacement.java new file mode 100644 index 0000000000..c431bc1882 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/PayloadReplacement.java @@ -0,0 +1,30 @@ +package org.evomaster.arazzo.models.domain; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.evomaster.arazzo.deserializer.AnyExpressionDeserializer; + +public class PayloadReplacement { + private String target; + + @JsonDeserialize(using = AnyExpressionDeserializer.class) + private AnyExpression value; + + public PayloadReplacement() { + } + + public String getTarget() { + return target; + } + + public void setTarget(String target) { + this.target = target; + } + + public AnyExpression getValue() { + return value; + } + + public void setValue(AnyExpression value) { + this.value = value; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/RequestBody.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/RequestBody.java new file mode 100644 index 0000000000..ad36ef918f --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/RequestBody.java @@ -0,0 +1,38 @@ +package org.evomaster.arazzo.models.domain; + +import com.fasterxml.jackson.databind.JsonNode; + +import java.util.List; + +public class RequestBody { + private String contentType; + private JsonNode payload; + private List replacements; + + public RequestBody() { + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public JsonNode getPayload() { + return payload; + } + + public void setPayload(JsonNode payload) { + this.payload = payload; + } + + public List getReplacements() { + return replacements; + } + + public void setReplacements(List replacements) { + this.replacements = replacements; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Reusable.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Reusable.java new file mode 100644 index 0000000000..8665bc7249 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Reusable.java @@ -0,0 +1,25 @@ +package org.evomaster.arazzo.models.domain; + +public class Reusable { + private String reference; + private String value; + + public Reusable() { + } + + public String getReference() { + return reference; + } + + public void setReference(String reference) { + this.reference = reference; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SourceDescription.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SourceDescription.java new file mode 100644 index 0000000000..b1437f86d5 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SourceDescription.java @@ -0,0 +1,34 @@ +package org.evomaster.arazzo.models.domain; + +public class SourceDescription { + private String name; + private String url; + private String type; + + public SourceDescription() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Step.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Step.java new file mode 100644 index 0000000000..e1c4eec402 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Step.java @@ -0,0 +1,110 @@ +package org.evomaster.arazzo.models.domain; + +import java.util.List; +import java.util.Map; + +public class Step { + private String description; + private String stepId; + private String operationId; + private String operationPath; + private String workflowId; + private List parameters; + private RequestBody requestBody; + private List successCriteria; + private List onSuccess; + private List onFailure; + private Map outputs; + + public Step(Builder builder) { + this.description = builder.description; + this.stepId = builder.stepId; + this.operationId = builder.operationId; + this.operationPath = builder.operationPath; + this.workflowId = builder.workflowId; + this.parameters = builder.parameters; + this.requestBody = builder.requestBody; + this.successCriteria = builder.successCriteria; + this.onSuccess = builder.onSuccess; + this.onFailure = builder.onFailure; + this.outputs = builder.outputs; + } + + public String getDescription() { + return description; + } + + public String getStepId() { + return stepId; + } + + public String getOperationId() { + return operationId; + } + + public String getOperationPath() { + return operationPath; + } + + public String getWorkflowId() { + return workflowId; + } + + public List getParameters() { + return parameters; + } + + public RequestBody getRequestBody() { + return requestBody; + } + + public List getSuccessCriteria() { + return successCriteria; + } + + public List getOnSuccess() { + return onSuccess; + } + + public List getOnFailure() { + return onFailure; + } + + public Map getOutputs() { + return outputs; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String description; + private String stepId; + private String operationId; + private String operationPath; + private String workflowId; + private List parameters; + private RequestBody requestBody; + private List successCriteria; + private List onSuccess; + private List onFailure; + private Map outputs; + + public Builder description(String description) { this.description = description; return this; } + public Builder stepId(String stepId) { this.stepId = stepId; return this; } + public Builder operationId(String operationId) { this.operationId = operationId; return this; } + public Builder operationPath(String operationPath) { this.operationPath = operationPath; return this; } + public Builder workflowId(String workflowId) { this.workflowId = workflowId; return this; } + public Builder parameters(List parameters) { this.parameters = parameters; return this; } + public Builder requestBody(RequestBody requestBody) { this.requestBody = requestBody; return this; } + public Builder successCriteria(List successCriteria) { this.successCriteria = successCriteria; return this; } + public Builder onSuccess(List onSuccess) { this.onSuccess = onSuccess; return this; } + public Builder onFailure(List onFailure) { this.onFailure = onFailure; return this; } + public Builder outputs(Map outputs) { this.outputs = outputs; return this; } + + public Step build() { + return new Step(this); + } + } +} \ No newline at end of file diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessAction.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessAction.java new file mode 100644 index 0000000000..4df5875a8d --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessAction.java @@ -0,0 +1,54 @@ +package org.evomaster.arazzo.models.domain; + +import java.util.List; + +public class SuccessAction { + private String name; + private String type; + private String workflowId; + private String stepId; + private List criteria; + + public SuccessAction() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getWorkflowId() { + return workflowId; + } + + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + public String getStepId() { + return stepId; + } + + public void setStepId(String stepId) { + this.stepId = stepId; + } + + public List getCriteria() { + return criteria; + } + + public void setCriteria(List criteria) { + this.criteria = criteria; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessReusable.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessReusable.java new file mode 100644 index 0000000000..a9a0d0e4da --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessReusable.java @@ -0,0 +1,31 @@ +package org.evomaster.arazzo.models.domain; + +public abstract class SuccessReusable { + + public SuccessReusable() { + } + + public static class Success extends SuccessReusable { + private final SuccessAction action; + + public Success(SuccessAction action) { + this.action = action; + } + + public SuccessAction getAction() { + return action; + } + } + + public static class ReusableObj extends SuccessReusable { + private final Reusable reusable; + + public ReusableObj(Reusable reusable) { + this.reusable = reusable; + } + + public Reusable getReusable() { + return reusable; + } + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Workflow.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Workflow.java new file mode 100644 index 0000000000..90d992589c --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Workflow.java @@ -0,0 +1,105 @@ +package org.evomaster.arazzo.models.domain; + +import io.swagger.v3.oas.models.media.Schema; + +import java.util.List; +import java.util.Map; + +public class Workflow { + private String workflowId; + private String summary; + private String description; + private Schema inputs; + private List dependsOn; + private List steps; + private List successActions; + private List failureActions; + private Map outputs; + private List parameters; + + private Workflow(Builder builder) { + this.workflowId = builder.workflowId; + this.summary = builder.summary; + this.description = builder.description; + this.inputs = builder.inputs; + this.dependsOn = builder.dependsOn; + this.steps = builder.steps; + this.successActions = builder.successActions; + this.failureActions = builder.failureActions; + this.outputs = builder.outputs; + this.parameters = builder.parameters; + } + + public String getWorkflowId() { + return workflowId; + } + + public String getSummary() { + return summary; + } + + public String getDescription() { + return description; + } + + public Schema getInputs() { + return inputs; + } + + public List getDependsOn() { + return dependsOn; + } + + public List getSteps() { + return steps; + } + + public List getSuccessActions() { + return successActions; + } + + public List getFailureActions() { + return failureActions; + } + + public Map getOutputs() { + return outputs; + } + + public List getParameters() { + return parameters; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String workflowId; + private String summary; + private String description; + private Schema inputs; + private List dependsOn; + private List steps; + private List successActions; + private List failureActions; + private Map outputs; + private List parameters; + + public Builder workflowId(String workflowId) { this.workflowId = workflowId; return this; } + public Builder summary(String summary) { this.summary = summary; return this; } + public Builder description(String description) { this.description = description; return this; } + public Builder inputs(Schema inputs) { this.inputs = inputs; return this; } + public Builder dependsOn(List dependsOn) { this.dependsOn = dependsOn; return this; } + public Builder steps(List steps) { this.steps = steps; return this; } + public Builder successActions(List successActions) { this.successActions = successActions; return this; } + public Builder failureActions(List failureActions) { this.failureActions = failureActions; return this; } + public Builder outputs(Map outputs) { this.outputs = outputs; return this; } + public Builder parameters(List parameters) { this.parameters = parameters; return this; } + + public Workflow build() { + return new Workflow(this); + } + } + +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/ArazzoSpecificationsDTO.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/ArazzoSpecificationsDTO.java new file mode 100644 index 0000000000..1cbc7f812e --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/ArazzoSpecificationsDTO.java @@ -0,0 +1,58 @@ +package org.evomaster.arazzo.models.dto; + +import org.evomaster.arazzo.models.domain.Components; +import org.evomaster.arazzo.models.domain.InfoArazzo; +import org.evomaster.arazzo.models.domain.SourceDescription; + +import java.util.List; + +public class ArazzoSpecificationsDTO { + private String arazzo; + private InfoArazzo info; + private List sourceDescriptions; + private List workflows; + private Components components; + + public ArazzoSpecificationsDTO() { + } + + public String getArazzo() { + return arazzo; + } + + public void setArazzo(String arazzo) { + this.arazzo = arazzo; + } + + public InfoArazzo getInfo() { + return info; + } + + public void setInfo(InfoArazzo info) { + this.info = info; + } + + public List getSourceDescriptions() { + return sourceDescriptions; + } + + public void setSourceDescriptions(List sourceDescriptions) { + this.sourceDescriptions = sourceDescriptions; + } + + public List getWorkflows() { + return workflows; + } + + public void setWorkflows(List workflows) { + this.workflows = workflows; + } + + public Components getComponents() { + return components; + } + + public void setComponents(Components components) { + this.components = components; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/StepDTO.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/StepDTO.java new file mode 100644 index 0000000000..9c0ed9538f --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/StepDTO.java @@ -0,0 +1,121 @@ +package org.evomaster.arazzo.models.dto; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.evomaster.arazzo.deserializer.FailureReusableDeserializer; +import org.evomaster.arazzo.deserializer.ParameterReusableDeserializer; +import org.evomaster.arazzo.deserializer.SuccessReusableDeserializer; +import org.evomaster.arazzo.models.domain.*; + +import java.util.List; +import java.util.Map; + +public class StepDTO { + private String description; + private String stepId; + private String operationId; + private String operationPath; + private String workflowId; + private RequestBody requestBody; + private List successCriteria; + private Map outputs; + + @JsonDeserialize(contentUsing = ParameterReusableDeserializer.class) + private List parameters; + + @JsonDeserialize(contentUsing = SuccessReusableDeserializer.class) + private List onSuccess; + + @JsonDeserialize(contentUsing = FailureReusableDeserializer.class) + private List onFailure; + + public StepDTO() { + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getStepId() { + return stepId; + } + + public void setStepId(String stepId) { + this.stepId = stepId; + } + + public String getOperationId() { + return operationId; + } + + public void setOperationId(String operationId) { + this.operationId = operationId; + } + + public String getOperationPath() { + return operationPath; + } + + public void setOperationPath(String operationPath) { + this.operationPath = operationPath; + } + + public String getWorkflowId() { + return workflowId; + } + + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + public RequestBody getRequestBody() { + return requestBody; + } + + public void setRequestBody(RequestBody requestBody) { + this.requestBody = requestBody; + } + + public List getSuccessCriteria() { + return successCriteria; + } + + public void setSuccessCriteria(List successCriteria) { + this.successCriteria = successCriteria; + } + + public Map getOutputs() { + return outputs; + } + + public void setOutputs(Map outputs) { + this.outputs = outputs; + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } + + public List getOnSuccess() { + return onSuccess; + } + + public void setOnSuccess(List onSuccess) { + this.onSuccess = onSuccess; + } + + public List getOnFailure() { + return onFailure; + } + + public void setOnFailure(List onFailure) { + this.onFailure = onFailure; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/WorkflowDTO.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/WorkflowDTO.java new file mode 100644 index 0000000000..a99d818509 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/WorkflowDTO.java @@ -0,0 +1,115 @@ +package org.evomaster.arazzo.models.dto; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import org.evomaster.arazzo.deserializer.FailureReusableDeserializer; +import org.evomaster.arazzo.deserializer.ParameterReusableDeserializer; +import org.evomaster.arazzo.deserializer.SuccessReusableDeserializer; +import org.evomaster.arazzo.models.domain.FailureReusable; +import org.evomaster.arazzo.models.domain.ParameterReusable; +import org.evomaster.arazzo.models.domain.SuccessReusable; +import io.swagger.v3.oas.models.media.Schema; + +import java.util.List; +import java.util.Map; + +public class WorkflowDTO { + private String workflowId; + private String summary; + private String description; + private Schema inputs; + private List dependsOn; + private List steps; + private Map outputs; + + @JsonDeserialize(contentUsing = SuccessReusableDeserializer.class) + private List successActions; + + @JsonDeserialize(contentUsing = FailureReusableDeserializer.class) + private List failureActions; + + @JsonDeserialize(contentUsing = ParameterReusableDeserializer.class) + private List parameters; + + public WorkflowDTO() { + } + + public String getWorkflowId() { + return workflowId; + } + + public void setWorkflowId(String workflowId) { + this.workflowId = workflowId; + } + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public Schema getInputs() { + return inputs; + } + + public void setInputs(Schema inputs) { + this.inputs = inputs; + } + + public List getDependsOn() { + return dependsOn; + } + + public void setDependsOn(List dependsOn) { + this.dependsOn = dependsOn; + } + + public List getSteps() { + return steps; + } + + public void setSteps(List steps) { + this.steps = steps; + } + + public Map getOutputs() { + return outputs; + } + + public void setOutputs(Map outputs) { + this.outputs = outputs; + } + + public List getSuccessActions() { + return successActions; + } + + public void setSuccessActions(List successActions) { + this.successActions = successActions; + } + + public List getFailureActions() { + return failureActions; + } + + public void setFailureActions(List failureActions) { + this.failureActions = failureActions; + } + + public List getParameters() { + return parameters; + } + + public void setParameters(List parameters) { + this.parameters = parameters; + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/parser/ArazzoParser.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/parser/ArazzoParser.java new file mode 100644 index 0000000000..6af8f4f1d9 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/parser/ArazzoParser.java @@ -0,0 +1,45 @@ +package org.evomaster.arazzo.parser; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import io.swagger.v3.oas.models.OpenAPI; +import javafx.util.Pair; +import org.evomaster.arazzo.mapper.ArazzoMapper; +import org.evomaster.arazzo.models.domain.ArazzoSpecifications; +import org.evomaster.arazzo.models.dto.ArazzoSpecificationsDTO; +import org.evomaster.arazzo.resolver.ArazzoReferenceResolver; + +public class ArazzoParser { + + private static final ObjectMapper JSON_MAPPER = new ObjectMapper().findAndRegisterModules(); + private static final ObjectMapper YAML_MAPPER = new ObjectMapper(new YAMLFactory()).findAndRegisterModules(); + + public static ArazzoSpecifications parse(String schemaText, OpenAPI openAPI) { + Pair parsed = parseSchemaText(schemaText); + ArazzoReferenceResolver resolver = new ArazzoReferenceResolver(parsed.getKey().getComponents(), parsed.getValue(), openAPI); + ArazzoMapper mapper = new ArazzoMapper(resolver); + return mapper.toDomain(parsed.getKey()); + } + + private static Pair parseSchemaText(String schemaText) { + String schemaTextClean = schemaText.replaceAll("^\\s+", ""); + + ArazzoSpecificationsDTO arazzoSpecificationsDTO; + JsonNode arazzoJsonNode; + + try { + if (schemaTextClean.startsWith("{")) { + arazzoSpecificationsDTO = JSON_MAPPER.readValue(schemaTextClean, ArazzoSpecificationsDTO.class); + arazzoJsonNode = JSON_MAPPER.readTree(schemaTextClean); + } else { + arazzoSpecificationsDTO = YAML_MAPPER.readValue(schemaTextClean, ArazzoSpecificationsDTO.class); + arazzoJsonNode = YAML_MAPPER.readTree(schemaTextClean); + } + } catch (Exception e) { + throw new IllegalArgumentException("Problems parsing the Arazzo document", e); + } + + return new Pair<>(arazzoSpecificationsDTO, arazzoJsonNode); + } +} diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/resolver/ArazzoReferenceResolver.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/resolver/ArazzoReferenceResolver.java new file mode 100644 index 0000000000..f4bdaccf62 --- /dev/null +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/resolver/ArazzoReferenceResolver.java @@ -0,0 +1,173 @@ +package org.evomaster.arazzo.resolver; + +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.arazzo.models.domain.*; + +import java.util.List; +import java.util.stream.Collectors; + +public class ArazzoReferenceResolver { + private Components components; + private JsonNode arazzoJsonNode; + private OpenAPI openApi; + + public ArazzoReferenceResolver(Components components, JsonNode arazzoJsonNode, OpenAPI openAPI) { + this.components = components; + this.arazzoJsonNode = arazzoJsonNode; + this.openApi = openAPI; + } + + public void setComponents(Components components) { + this.components = components; + } + + public void setArazzoJsonNode(JsonNode arazzoJsonNode) { + this.arazzoJsonNode = arazzoJsonNode; + } + + public void setOpenApi(OpenAPI openApi) { + this.openApi = openApi; + } + + public List resolveSuccessReusable(List items) { + if (items == null) { + return null; + } + + return items.stream().map(item -> { + if (item instanceof SuccessReusable.Success) { + SuccessReusable.Success action = (SuccessReusable.Success) item; + return action.getAction(); + } + SuccessReusable.ReusableObj reusableObj = (SuccessReusable.ReusableObj) item; + return (SuccessAction) resolveReusableWithPrefix(reusableObj.getReusable(), "successActions"); + }).collect(Collectors.toList()); + } + + public List resolveFailureReusable(List items) { + if (items == null) { + return null; + } + + return items.stream().map(item -> { + if (item instanceof FailureReusable.Failure) { + FailureReusable.Failure action = (FailureReusable.Failure) item; + return action.getAction(); + } + FailureReusable.ReusableObj reusableObj = (FailureReusable.ReusableObj) item; + return (FailureAction) resolveReusableWithPrefix(reusableObj.getReusable(), "failureActions"); + }).collect(Collectors.toList()); + } + + public List resolveParametersReusable(List items) { + if (items == null) { + return null; + } + + return items.stream().map(item -> { + if (item instanceof ParameterReusable.Param) { + ParameterReusable.Param param = (ParameterReusable.Param) item; + return param.getParameter(); + } + ParameterReusable.ReusableObj reusableObj = (ParameterReusable.ReusableObj) item; + return (Parameter) resolveReusableWithPrefix(reusableObj.getReusable(), "parameters"); + }).collect(Collectors.toList()); + } + + private Object resolveReusableWithPrefix(Reusable reusable, String expectedPrefix) { + if (components == null) { + throw new IllegalArgumentException("Arazzo Parsing Error: Can't reference with no Components"); + } + + String reference = reusable.getReference(); + if (!reference.startsWith("$components.")) { + throw new IllegalArgumentException("Arazzo Parsing Error: Invalid reference (" + reference + "). Expected to point to '$components.'"); + } + + String referenceWithoutComponents = reference.substring(("$components.").length()); + if (!referenceWithoutComponents.startsWith(expectedPrefix)) { + throw new IllegalArgumentException("Arazzo Parsing Error: Invalid reference (" + referenceWithoutComponents + "). Expected to point to '" + expectedPrefix +"'"); + } + + String referenceClean = referenceWithoutComponents.substring((expectedPrefix + ".").length()); + Object resolve; + switch (expectedPrefix) { + case "successActions": + resolve = (components.getSuccessAction() != null) ? components.getSuccessAction().get(referenceClean) : null; + break; + case "failureActions": + resolve = (components.getFailureAction() != null) ? components.getFailureAction().get(referenceClean) : null; + break; + case "parameters": + resolve = (components.getParameters() != null) ? components.getParameters().get(referenceClean) : null; + break; + default: + resolve = null; + break; + } + + if (resolve == null) { + throw new IllegalArgumentException("Arazzo Parsing Error: The " + expectedPrefix + ": '" + referenceClean + "' is not in the components."); + } + + return resolve; + + } + + public Schema resolveJsonPointer(String reference) { + if (reference.startsWith("#/")) { + return resolveJsonPointerLocal(reference); + } + return resolveJsonPointerExternal(reference); + } + + private Schema resolveJsonPointerLocal(String reference) { + if (arazzoJsonNode == null) { + throw new IllegalArgumentException("Arazzo Parsing Error: Can't reference with no Arazzo Document"); + } + + String jsonPointer = reference.substring(1); + + JsonNode result = arazzoJsonNode.at(jsonPointer); + + if (result.isMissingNode()) { + throw new IllegalArgumentException("Arazzo Parsing Error: Can't reference '" + reference + "'"); + } + + return Json.mapper().convertValue(result, Schema.class); + } + + private Schema resolveJsonPointerExternal(String reference) { + if (openApi == null) { + throw new IllegalArgumentException("Arazzo Parsing Error: Can't reference with no OpenApi Document"); + } + + String[] tokens = reference.split("#"); + if (tokens.length < 2) { + throw new IllegalArgumentException("Arazzo Parsing Error: Error reference (" + reference + "). '#' Is mandatory"); + } + + String jsonPointer = tokens[1]; + String expectedPrefix = "/components/schemas/"; + + if (!jsonPointer.startsWith(expectedPrefix)) { + throw new IllegalArgumentException("Arazzo Parsing Error: Error reference (" + reference + "). \"/components/schemas/\" Is mandatory for references to OpenApi"); + } + + String schemaName = jsonPointer.substring((expectedPrefix).length()); + Schema result = null; + if (openApi.getComponents() != null && openApi.getComponents().getSchemas() != null) { + result = openApi.getComponents().getSchemas().get(schemaName); + } + + if (result == null) { + throw new IllegalArgumentException("Arazzo Parsing Error: (" + reference + ") reference does not exist in the OpenApi document"); + } + + return result; + } + +} diff --git a/core-extra/arazzo-parser/src/test/java/arazzo/ArazzoParserTest.java b/core-extra/arazzo-parser/src/test/java/arazzo/ArazzoParserTest.java new file mode 100644 index 0000000000..2798af4d3f --- /dev/null +++ b/core-extra/arazzo-parser/src/test/java/arazzo/ArazzoParserTest.java @@ -0,0 +1,39 @@ +package arazzo; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.parser.OpenAPIV3Parser; +import org.dataloader.impl.Assertions; +import org.evomaster.arazzo.access.ArazzoAccess; +import org.evomaster.arazzo.models.domain.ArazzoSpecifications; +import org.evomaster.arazzo.parser.ArazzoParser; +import org.junit.Assert; +import org.junit.Test; + +public class ArazzoParserTest { + + private final String BASE_RESOURCE_ARAZZO = "src/test/resources/arazzo"; + private final String BASE_RESOURCE_OPENAPI = "src/test/resources/openapi"; + + @Test + public void shouldParseArazzoYamlSuccessfully() throws Exception { + String schemaText = ArazzoAccess.readFromDisk(BASE_RESOURCE_ARAZZO + "/arazzo_pet.yaml"); + OpenAPI openAPI = new OpenAPIV3Parser().read(BASE_RESOURCE_OPENAPI + "/openapi_pet.json"); + + ArazzoSpecifications arazzo = ArazzoParser.parse(schemaText, openAPI); + + Assert.assertEquals(3, arazzo.getWorkflows().size()); + Assert.assertEquals("Petstore - Apply Coupons", arazzo.getInfo().getTitle()); + } + + @Test + public void shouldParseArazzoJsonSuccessfully() throws Exception { + String schemaText = ArazzoAccess.readFromDisk(BASE_RESOURCE_ARAZZO + "/arazzo_pet.json"); + OpenAPI openAPI = new OpenAPIV3Parser().read(BASE_RESOURCE_OPENAPI + "/openapi_pet.json"); + + ArazzoSpecifications arazzo = ArazzoParser.parse(schemaText, openAPI); + + Assert.assertEquals(3, arazzo.getWorkflows().size()); + Assert.assertEquals("Petstore - Apply Coupons", arazzo.getInfo().getTitle()); + } + +} diff --git a/core-extra/arazzo-parser/src/test/resources/arazzo/arazzo_pet.json b/core-extra/arazzo-parser/src/test/resources/arazzo/arazzo_pet.json new file mode 100644 index 0000000000..47b817654c --- /dev/null +++ b/core-extra/arazzo-parser/src/test/resources/arazzo/arazzo_pet.json @@ -0,0 +1,248 @@ +{ + "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" + } + }, + { + "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 diff --git a/core-extra/arazzo-parser/src/test/resources/arazzo/arazzo_pet.yaml b/core-extra/arazzo-parser/src/test/resources/arazzo/arazzo_pet.yaml new file mode 100644 index 0000000000..8a573b645e --- /dev/null +++ b/core-extra/arazzo-parser/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 diff --git a/core-extra/arazzo-parser/src/test/resources/openapi/openapi_pet.json b/core-extra/arazzo-parser/src/test/resources/openapi/openapi_pet.json new file mode 100644 index 0000000000..1b546da4a4 --- /dev/null +++ b/core-extra/arazzo-parser/src/test/resources/openapi/openapi_pet.json @@ -0,0 +1,1225 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Swagger Petstore - OpenAPI 3.0", + "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "email": "apiteam@swagger.io" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0.17" + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + }, + "servers": [ + { + "url": "/api/v3" + } + ], + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Access to Petstore orders", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + }, + { + "name": "user", + "description": "Operations about user" + } + ], + "paths": { + "/pet": { + "put": { + "tags": [ + "pet" + ], + "summary": "Update an existing pet", + "description": "Update an existing pet by Id", + "operationId": "updatePet", + "requestBody": { + "description": "Update an existent pet in the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + }, + "405": { + "description": "Validation exception" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Add a new pet to the store", + "description": "Add a new pet to the store", + "operationId": "addPet", + "requestBody": { + "description": "Create a new pet in the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": false, + "explode": true, + "schema": { + "type": "string", + "default": "available", + "enum": [ + "available", + "pending", + "sold" + ] + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid status value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ + "pet" + ], + "summary": "Finds Pets by tags", + "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": false, + "explode": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Pet" + } + } + } + } + }, + "400": { + "description": "Invalid tag value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}": { + "get": { + "tags": [ + "pet" + ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Pet not found" + } + }, + "security": [ + { + "api_key": [] + }, + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "post": { + "tags": [ + "pet" + ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "name", + "in": "query", + "description": "Name of pet that needs to be updated", + "schema": { + "type": "string" + } + }, + { + "name": "status", + "in": "query", + "description": "Status of pet that needs to be updated", + "schema": { + "type": "string" + } + } + ], + "responses": { + "405": { + "description": "Invalid input" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + }, + "delete": { + "tags": [ + "pet" + ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "parameters": [ + { + "name": "api_key", + "in": "header", + "description": "", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "400": { + "description": "Invalid pet value" + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ + "pet" + ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "additionalMetadata", + "in": "query", + "description": "Additional Metadata", + "required": false, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiResponse" + } + } + } + } + }, + "security": [ + { + "petstore_auth": [ + "write:pets", + "read:pets" + ] + } + ] + } + }, + "/store/inventory": { + "get": { + "tags": [ + "store" + ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + } + } + }, + "security": [ + { + "api_key": [] + } + ] + } + }, + "/store/order": { + "post": { + "tags": [ + "store" + ], + "summary": "Place an order for a pet", + "description": "Place a new order in the store", + "operationId": "placeOrder", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "405": { + "description": "Invalid input" + } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ + "store" + ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generate exceptions.", + "operationId": "getOrderById", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of order that needs to be fetched", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Order" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/Order" + } + } + } + }, + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + }, + "delete": { + "tags": [ + "store" + ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "operationId": "deleteOrder", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "400": { + "description": "Invalid ID supplied" + }, + "404": { + "description": "Order not found" + } + } + } + }, + "/user": { + "post": { + "tags": [ + "user" + ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "requestBody": { + "description": "Created user object", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "responses": { + "default": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ + "user" + ], + "summary": "Creates list of users with given input array", + "description": "Creates list of users with given input array", + "operationId": "createUsersWithListInput", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + } + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "default": { + "description": "successful operation" + } + } + } + }, + "/user/login": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": false, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "X-Rate-Limit": { + "description": "calls per hour allowed by the user", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "X-Expires-After": { + "description": "date in UTC when token expires", + "schema": { + "type": "string", + "format": "date-time" + } + } + }, + "content": { + "application/xml": { + "schema": { + "type": "string" + } + }, + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Invalid username/password supplied" + } + } + } + }, + "/user/logout": { + "get": { + "tags": [ + "user" + ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "parameters": [], + "responses": { + "default": { + "description": "successful operation" + } + } + } + }, + "/user/{username}": { + "get": { + "tags": [ + "user" + ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + }, + "put": { + "tags": [ + "user" + ], + "summary": "Update user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that needs to be updated", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "description": "Update an existent user in the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/User" + } + }, + "application/x-www-form-urlencoded": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "responses": { + "default": { + "description": "successful operation" + } + } + }, + "delete": { + "tags": [ + "user" + ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "400": { + "description": "Invalid username supplied" + }, + "404": { + "description": "User not found" + } + } + } + } + }, + "components": { + "schemas": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "petId": { + "type": "integer", + "format": "int64", + "example": 198772 + }, + "quantity": { + "type": "integer", + "format": "int32", + "example": 7 + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "example": "approved", + "enum": [ + "placed", + "approved", + "delivered" + ] + }, + "complete": { + "type": "boolean" + } + }, + "xml": { + "name": "order" + } + }, + "Customer": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 100000 + }, + "username": { + "type": "string", + "example": "fehguy" + }, + "address": { + "type": "array", + "xml": { + "name": "addresses", + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Address" + } + } + }, + "xml": { + "name": "customer" + } + }, + "Address": { + "type": "object", + "properties": { + "street": { + "type": "string", + "example": "437 Lytton" + }, + "city": { + "type": "string", + "example": "Palo Alto" + }, + "state": { + "type": "string", + "example": "CA" + }, + "zip": { + "type": "string", + "example": "94301" + } + }, + "xml": { + "name": "address" + } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "name": { + "type": "string", + "example": "Dogs" + } + }, + "xml": { + "name": "category" + } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "username": { + "type": "string", + "example": "theUser" + }, + "firstName": { + "type": "string", + "example": "John" + }, + "lastName": { + "type": "string", + "example": "James" + }, + "email": { + "type": "string", + "example": "john@email.com" + }, + "password": { + "type": "string", + "example": "12345" + }, + "phone": { + "type": "string", + "example": "12345" + }, + "userStatus": { + "type": "integer", + "description": "User Status", + "format": "int32", + "example": 1 + } + }, + "xml": { + "name": "user" + } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "xml": { + "name": "tag" + } + }, + "Pet": { + "required": [ + "name", + "photoUrls" + ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "name": { + "type": "string", + "example": "doggie" + }, + "category": { + "$ref": "#/components/schemas/Category" + }, + "photoUrls": { + "type": "array", + "xml": { + "wrapped": true + }, + "items": { + "type": "string", + "xml": { + "name": "photoUrl" + } + } + }, + "tags": { + "type": "array", + "xml": { + "wrapped": true + }, + "items": { + "$ref": "#/components/schemas/Tag" + } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ + "available", + "pending", + "sold" + ] + } + }, + "xml": { + "name": "pet" + } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "xml": { + "name": "##default" + } + } + }, + "requestBodies": { + "Pet": { + "description": "Pet object that needs to be added to the store", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/Pet" + } + } + } + }, + "UserArray": { + "description": "List of user object", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + } + } + } + } + }, + "securitySchemes": { + "petstore_auth": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + } + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + } + } +} \ No newline at end of file diff --git a/core-extra/pom.xml b/core-extra/pom.xml index 839ad7a801..196088b35b 100644 --- a/core-extra/pom.xml +++ b/core-extra/pom.xml @@ -16,5 +16,6 @@ dbconstraint solver + arazzo-parser \ No newline at end of file From f2f716118ccfeb78a8ed9a1de3da9b12a86e6774 Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Fri, 1 May 2026 20:03:28 -0300 Subject: [PATCH 14/15] Clean core of Arazzo Parser --- .gitignore | 1 + core/pom.xml | 66 ++++---- .../deserializer/AnyExpressionDeserializer.kt | 27 --- .../deserializer/CriterionTypeDeserializer.kt | 25 --- .../FailureReusableDeserializer.kt | 26 --- .../ParameterReusableDeserializer.kt | 26 --- .../RuntimeExpressionDeserializer.kt | 22 --- .../SuccessReusableDeserializer.kt | 26 --- .../rest/arazzo/mapper/ArazzoMapper.kt | 53 ------ .../rest/arazzo/models/AnyExpression.kt | 9 - .../arazzo/models/ArazzoSpecifications.kt | 8 - .../problem/rest/arazzo/models/Components.kt | 10 -- .../problem/rest/arazzo/models/Criterion.kt | 13 -- .../rest/arazzo/models/CriterionExpression.kt | 7 - .../rest/arazzo/models/CriterionType.kt | 7 - .../rest/arazzo/models/FailureAction.kt | 12 -- .../rest/arazzo/models/FailureReusable.kt | 7 - .../problem/rest/arazzo/models/InfoArazzo.kt | 8 - .../problem/rest/arazzo/models/Parameter.kt | 14 -- .../rest/arazzo/models/ParameterReusable.kt | 7 - .../rest/arazzo/models/PayloadReplacement.kt | 9 - .../problem/rest/arazzo/models/RequestBody.kt | 10 -- .../problem/rest/arazzo/models/Reusable.kt | 11 -- .../rest/arazzo/models/RuntimeExpression.kt | 17 -- .../core/problem/rest/arazzo/models/Source.kt | 8 - .../rest/arazzo/models/SourceDescription.kt | 7 - .../core/problem/rest/arazzo/models/Step.kt | 10 -- .../rest/arazzo/models/SuccessAction.kt | 10 -- .../rest/arazzo/models/SuccessReusable.kt | 7 - .../problem/rest/arazzo/models/Workflow.kt | 13 -- .../commons/ArazzoSpecificationsCommon.kt | 12 -- .../rest/arazzo/models/commons/StepCommon.kt | 16 -- .../arazzo/models/commons/WorkflowCommon.kt | 12 -- .../models/raws/ArazzoSpecificationsRaw.kt | 14 -- .../rest/arazzo/models/raws/StepRaw.kt | 36 ---- .../rest/arazzo/models/raws/WorkflowRaw.kt | 35 ---- .../rest/arazzo/parser/ArazzoParser.kt | 49 ------ .../rest/arazzo/parser/ExpressionParser.kt | 79 --------- .../arazzo/parser/SimpleConditionParser.kt | 103 ----------- .../resolver/ArazzoReferenceResolver.kt | 130 -------------- .../core/problem/rest/schema/ArazzoAccess.kt | 68 -------- .../core/problem/rest/schema/SchemaArazzo.kt | 22 --- .../rest/schema/ArazzoLocalURLIssueTest.kt | 90 ---------- .../src/test/resources/arazzo/arazzo_pet.yaml | 160 ------------------ 44 files changed, 32 insertions(+), 1270 deletions(-) delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/AnyExpressionDeserializer.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/CriterionTypeDeserializer.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/FailureReusableDeserializer.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/ParameterReusableDeserializer.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/RuntimeExpressionDeserializer.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/SuccessReusableDeserializer.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/mapper/ArazzoMapper.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/AnyExpression.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Criterion.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionExpression.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionType.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureAction.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureReusable.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/InfoArazzo.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Parameter.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ParameterReusable.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/PayloadReplacement.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RequestBody.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Reusable.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RuntimeExpression.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Source.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SourceDescription.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessAction.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessReusable.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/ArazzoSpecificationsCommon.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/StepCommon.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/WorkflowCommon.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/ArazzoSpecificationsRaw.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/StepRaw.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/WorkflowRaw.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ExpressionParser.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/SimpleConditionParser.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt delete mode 100644 core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt delete mode 100644 core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt delete mode 100644 core/src/test/resources/arazzo/arazzo_pet.yaml diff --git a/.gitignore b/.gitignore index eb67759e09..88f6119088 100644 --- a/.gitignore +++ b/.gitignore @@ -114,6 +114,7 @@ Migrations/ /core-tests/client-java/distance-heuristics/target/ /em.toml +/core-extra/arazzo-parser/target/ /core-extra/solver/target/ /em.yaml /core-tests/e2e-tests/spring/spring-rest-openapi-v2/em.yaml diff --git a/core/pom.xml b/core/pom.xml index 419557712a..c02c021e17 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -201,10 +201,6 @@ com.fasterxml.jackson.dataformat jackson-dataformat-yaml - - com.fasterxml.jackson.module - jackson-module-kotlin - org.antlr antlr4-runtime @@ -358,37 +354,37 @@ maven-compiler-plugin - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 deleted file mode 100644 index 55127d4cd2..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/AnyExpressionDeserializer.kt +++ /dev/null @@ -1,27 +0,0 @@ -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 deleted file mode 100644 index fe9a9d6443..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/CriterionTypeDeserializer.kt +++ /dev/null @@ -1,25 +0,0 @@ -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 deleted file mode 100644 index 227b797ac6..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/FailureReusableDeserializer.kt +++ /dev/null @@ -1,26 +0,0 @@ -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.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 deleted file mode 100644 index ed2e868351..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/ParameterReusableDeserializer.kt +++ /dev/null @@ -1,26 +0,0 @@ -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.Param(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 deleted file mode 100644 index d74d586024..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/RuntimeExpressionDeserializer.kt +++ /dev/null @@ -1,22 +0,0 @@ -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 deleted file mode 100644 index e2bb141bb6..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/deserializer/SuccessReusableDeserializer.kt +++ /dev/null @@ -1,26 +0,0 @@ -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.Success(action) - } - } -} \ No newline at end of file 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 deleted file mode 100644 index 22ea043a43..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/mapper/ArazzoMapper.kt +++ /dev/null @@ -1,53 +0,0 @@ -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 -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, - inputs = toDomain(raw.inputs), - 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) - ) - } - - 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/AnyExpression.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/AnyExpression.kt deleted file mode 100644 index 9ff00a0d6c..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/AnyExpression.kt +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 5e861b2732..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ArazzoSpecifications.kt +++ /dev/null @@ -1,8 +0,0 @@ -package org.evomaster.core.problem.rest.arazzo.models - -import org.evomaster.core.problem.rest.arazzo.models.commons.ArazzoSpecificationsCommon - -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 deleted file mode 100644 index 6e46999bb0..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Components.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.evomaster.core.problem.rest.arazzo.models - -import io.swagger.v3.oas.models.media.Schema - -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 deleted file mode 100644 index 8f86da8de4..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Criterion.kt +++ /dev/null @@ -1,13 +0,0 @@ -package org.evomaster.core.problem.rest.arazzo.models - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import org.evomaster.core.problem.rest.arazzo.deserializer.CriterionTypeDeserializer - -class Criterion( - val context: RuntimeExpression?, - val condition: String, - @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/CriterionExpression.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionExpression.kt deleted file mode 100644 index 2bdaba7fb2..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionExpression.kt +++ /dev/null @@ -1,7 +0,0 @@ -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/CriterionType.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionType.kt deleted file mode 100644 index cd79c7a6fd..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/CriterionType.kt +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index cc06eabc77..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureAction.kt +++ /dev/null @@ -1,12 +0,0 @@ -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: 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 deleted file mode 100644 index baf57aa91f..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/FailureReusable.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.evomaster.core.problem.rest.arazzo.models - -sealed class 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/InfoArazzo.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/InfoArazzo.kt deleted file mode 100644 index f2ba4cc826..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/InfoArazzo.kt +++ /dev/null @@ -1,8 +0,0 @@ -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 deleted file mode 100644 index aafec604b2..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Parameter.kt +++ /dev/null @@ -1,14 +0,0 @@ -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?, - @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 deleted file mode 100644 index 50e9ea0f5b..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/ParameterReusable.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.evomaster.core.problem.rest.arazzo.models - -sealed class 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/PayloadReplacement.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/PayloadReplacement.kt deleted file mode 100644 index 38fe828999..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/PayloadReplacement.kt +++ /dev/null @@ -1,9 +0,0 @@ -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 deleted file mode 100644 index 1529590f19..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RequestBody.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.evomaster.core.problem.rest.arazzo.models - -import com.fasterxml.jackson.databind.JsonNode - -class RequestBody( - val contentType: String?, - 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/Reusable.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Reusable.kt deleted file mode 100644 index 4ccdf9e3d4..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Reusable.kt +++ /dev/null @@ -1,11 +0,0 @@ -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( - @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 deleted file mode 100644 index 02aec2b280..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/RuntimeExpression.kt +++ /dev/null @@ -1,17 +0,0 @@ -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() -} 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 deleted file mode 100644 index a83039e354..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Source.kt +++ /dev/null @@ -1,8 +0,0 @@ -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/SourceDescription.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SourceDescription.kt deleted file mode 100644 index effa0bb685..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SourceDescription.kt +++ /dev/null @@ -1,7 +0,0 @@ -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 deleted file mode 100644 index 032fa7fdcd..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Step.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.evomaster.core.problem.rest.arazzo.models - -import org.evomaster.core.problem.rest.arazzo.models.commons.StepCommon - -class Step( - 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/SuccessAction.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessAction.kt deleted file mode 100644 index 6eed27166b..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessAction.kt +++ /dev/null @@ -1,10 +0,0 @@ -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/SuccessReusable.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessReusable.kt deleted file mode 100644 index 97234022bb..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/SuccessReusable.kt +++ /dev/null @@ -1,7 +0,0 @@ -package org.evomaster.core.problem.rest.arazzo.models - -sealed class 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/models/Workflow.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt deleted file mode 100644 index 6d6bb82675..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/Workflow.kt +++ /dev/null @@ -1,13 +0,0 @@ -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?, - 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 deleted file mode 100644 index 0c5076dad6..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/ArazzoSpecificationsCommon.kt +++ /dev/null @@ -1,12 +0,0 @@ -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 deleted file mode 100644 index 7540ec8922..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/StepCommon.kt +++ /dev/null @@ -1,16 +0,0 @@ -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? - 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 deleted file mode 100644 index b3bfb06c45..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/commons/WorkflowCommon.kt +++ /dev/null @@ -1,12 +0,0 @@ -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 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 deleted file mode 100644 index c6bf02bac1..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/ArazzoSpecificationsRaw.kt +++ /dev/null @@ -1,14 +0,0 @@ -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 deleted file mode 100644 index 858be32d97..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/StepRaw.kt +++ /dev/null @@ -1,36 +0,0 @@ -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 - -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?, - - @JsonDeserialize(contentUsing = RuntimeExpressionDeserializer::class) - 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 deleted file mode 100644 index bf68807ab6..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/models/raws/WorkflowRaw.kt +++ /dev/null @@ -1,35 +0,0 @@ -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 dependsOn: List?, - val inputs: Schema<*>?, - 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 deleted file mode 100644 index 2edc532014..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ArazzoParser.kt +++ /dev/null @@ -1,49 +0,0 @@ -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.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 - -object ArazzoParser { - - val jsonMapper = ObjectMapper().findAndRegisterModules() - val yamlMapper = ObjectMapper(YAMLFactory()).findAndRegisterModules() - - 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 arazzoJsonNode: JsonNode? - var arazzoSpecificationsRaw: ArazzoSpecificationsRaw? - - try { - if (schemaTextClean.startsWith("{")) { - arazzoSpecificationsRaw = jsonMapper.readValue(schemaTextClean) - arazzoJsonNode = jsonMapper.readTree(schemaTextClean) - } else { - arazzoSpecificationsRaw = yamlMapper.readValue(schemaTextClean) - arazzoJsonNode = yamlMapper.readTree(schemaTextClean) - } - } catch (e: Exception) { - throw IllegalArgumentException("Problems parsing the Arazzo document", e) - } - - return Pair(arazzoSpecificationsRaw, arazzoJsonNode) - - } - -} \ 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 deleted file mode 100644 index 8d22eb3cf8..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/ExpressionParser.kt +++ /dev/null @@ -1,79 +0,0 @@ -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.") -> 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 deleted file mode 100644 index f27e1f8933..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/parser/SimpleConditionParser.kt +++ /dev/null @@ -1,103 +0,0 @@ -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/arazzo/resolver/ArazzoReferenceResolver.kt b/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt deleted file mode 100644 index 332c5e8fcc..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/arazzo/resolver/ArazzoReferenceResolver.kt +++ /dev/null @@ -1,130 +0,0 @@ -package org.evomaster.core.problem.rest.arazzo.resolver - -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.* - -class ArazzoReferenceResolver( - val components: Components?, - val arazzoJsonNode: JsonNode?, - val openApi: OpenAPI? -) { - - fun resolveSuccessReusable(items: List?): List? { - if (items == null) return null - - return items.map { item -> - when (item) { - is SuccessReusable.Success -> item.action - is SuccessReusable.ReusableObj -> resolveReusableWithPrefix(item.reusable, "successActions") as SuccessAction - } - } - } - - fun resolveFailureReusable(items: List?): List? { - if (items == null) return null - - return items.map { item -> - when (item) { - is FailureReusable.Failure -> item.action - is FailureReusable.ReusableObj -> resolveReusableWithPrefix(item.reusable, "failureActions") as FailureAction - } - } - } - - fun resolveParametersReusable(items: List?): List? { - if (items == null) return null - - return items.map { item -> - when (item) { - is ParameterReusable.Param -> item.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 - } - - 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 deleted file mode 100644 index f7131808db..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/ArazzoAccess.kt +++ /dev/null @@ -1,68 +0,0 @@ -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 -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, schemaOpenAPI: SchemaOpenAPI): SchemaArazzo { - return SchemaArazzo(schemaText, ArazzoParser.parse(schemaText, schemaOpenAPI), sourceLocation) - } - - fun getArazzoFromLocation( - arazzoLocation: String, - schemaOpenAPI: SchemaOpenAPI - ): 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, schemaOpenAPI); - } - - 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 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 deleted file mode 100644 index 069578e2cb..0000000000 --- a/core/src/main/kotlin/org/evomaster/core/problem/rest/schema/SchemaArazzo.kt +++ /dev/null @@ -1,22 +0,0 @@ -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 - -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/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt b/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt deleted file mode 100644 index 096ac87712..0000000000 --- a/core/src/test/kotlin/org/evomaster/core/problem/rest/schema/ArazzoLocalURLIssueTest.kt +++ /dev/null @@ -1,90 +0,0 @@ -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 Arazzo: -1. A local file which exists and the provided URL is valid. - */ -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 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")) { - "file:/$arazzoTestDirectory/arazzo_pet.yaml" - } else { - "file://$arazzoTestDirectory/arazzo_pet.yaml" - } - - // create arazzo from URL - 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) - } - -} \ 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 deleted file mode 100644 index 8a573b645e..0000000000 --- a/core/src/test/resources/arazzo/arazzo_pet.yaml +++ /dev/null @@ -1,160 +0,0 @@ -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 8d614e83434860b5d9c06bfe018f25351f3fb60f Mon Sep 17 00:00:00 2001 From: Daniel Lopera Date: Fri, 1 May 2026 23:31:13 -0300 Subject: [PATCH 15/15] Add @java-docs v1 --- .../org/evomaster/arazzo/access/ArazzoAccess.java | 3 +++ .../deserializer/AnyExpressionDeserializer.java | 5 +++++ .../deserializer/CriterionTypeDeserializer.java | 5 +++++ .../deserializer/FailureReusableDeserializer.java | 5 +++++ .../deserializer/ParameterReusableDeserializer.java | 5 +++++ .../deserializer/SuccessReusableDeserializer.java | 6 ++++++ .../org/evomaster/arazzo/mapper/ArazzoMapper.java | 4 ++++ .../arazzo/models/domain/AnyExpression.java | 6 ++++++ .../arazzo/models/domain/ArazzoSpecifications.java | 4 ++++ .../evomaster/arazzo/models/domain/Components.java | 6 ++++++ .../evomaster/arazzo/models/domain/Criterion.java | 5 +++++ .../arazzo/models/domain/CriterionExpression.java | 4 ++++ .../arazzo/models/domain/CriterionType.java | 3 +++ .../arazzo/models/domain/FailureAction.java | 4 ++++ .../arazzo/models/domain/FailureReusable.java | 3 +++ .../evomaster/arazzo/models/domain/InfoArazzo.java | 4 ++++ .../evomaster/arazzo/models/domain/Parameter.java | 4 ++++ .../arazzo/models/domain/ParameterReusable.java | 3 +++ .../arazzo/models/domain/PayloadReplacement.java | 4 ++++ .../evomaster/arazzo/models/domain/RequestBody.java | 4 ++++ .../org/evomaster/arazzo/models/domain/Reusable.java | 4 ++++ .../arazzo/models/domain/SourceDescription.java | 5 +++++ .../org/evomaster/arazzo/models/domain/Step.java | 9 +++++++++ .../arazzo/models/domain/SuccessAction.java | 4 ++++ .../arazzo/models/domain/SuccessReusable.java | 3 +++ .../org/evomaster/arazzo/models/domain/Workflow.java | 10 ++++++++++ .../arazzo/models/dto/ArazzoSpecificationsDTO.java | 4 ++++ .../org/evomaster/arazzo/models/dto/StepDTO.java | 5 +++++ .../org/evomaster/arazzo/models/dto/WorkflowDTO.java | 5 +++++ .../org/evomaster/arazzo/parser/ArazzoParser.java | 3 +++ .../arazzo/resolver/ArazzoReferenceResolver.java | 12 ++++++++++++ 31 files changed, 151 insertions(+) diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/access/ArazzoAccess.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/access/ArazzoAccess.java index 235d466cfd..4ee909dd9b 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/access/ArazzoAccess.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/access/ArazzoAccess.java @@ -6,6 +6,9 @@ import java.nio.file.Path; import java.nio.file.Paths; +/** + * Read Arrazzo documents + */ public class ArazzoAccess { public static String readFromDisk(String arazzoLocation) throws Exception { String fileScheme = "file:"; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/AnyExpressionDeserializer.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/AnyExpressionDeserializer.java index 5f1896f503..53dfef2da1 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/AnyExpressionDeserializer.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/AnyExpressionDeserializer.java @@ -9,6 +9,11 @@ import java.io.IOException; +/** + * Custom Jackson deserializer for {@link AnyExpression}. + * It resolves dynamic JSON payloads by mapping plain text to a {@link AnyExpression.Expression}, + * and any other complex structure to a generic JSON node. + */ public class AnyExpressionDeserializer extends JsonDeserializer { @Override diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/CriterionTypeDeserializer.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/CriterionTypeDeserializer.java index 8de2b4701b..46adea4e67 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/CriterionTypeDeserializer.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/CriterionTypeDeserializer.java @@ -10,6 +10,11 @@ import java.io.IOException; +/** + * Custom Jackson deserializer for {@link CriterionType}. + * It resolves dynamic JSON payloads by mapping plain text to a {@link CriterionType.Simple}, + * and a JSON object to a {@link CriterionType.Complex}. + */ public class CriterionTypeDeserializer extends JsonDeserializer { @Override diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/FailureReusableDeserializer.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/FailureReusableDeserializer.java index 441c789236..f0443ba9d6 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/FailureReusableDeserializer.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/FailureReusableDeserializer.java @@ -9,6 +9,11 @@ import java.io.IOException; +/** + * Custom Jackson deserializer for {@link FailureReusable}. + * It differentiates the incoming JSON payload based on the presence of the "reference" field, + * mapping it to a {@link FailureReusable.ReusableObj} if present, or to a {@link FailureReusable.Failure} otherwise. + */ public class FailureReusableDeserializer extends JsonDeserializer { @Override diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/ParameterReusableDeserializer.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/ParameterReusableDeserializer.java index 80a47f52af..ae456f8fd9 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/ParameterReusableDeserializer.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/ParameterReusableDeserializer.java @@ -9,6 +9,11 @@ import java.io.IOException; +/** + * Custom Jackson deserializer for {@link ParameterReusable}. + * It differentiates the incoming JSON payload based on the presence of the "reference" field, + * mapping it to a {@link ParameterReusable.ReusableObj} if present, or to a {@link ParameterReusable.Param} otherwise. + */ public class ParameterReusableDeserializer extends JsonDeserializer { @Override diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/SuccessReusableDeserializer.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/SuccessReusableDeserializer.java index e1381da9b2..6f72fb3e9a 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/SuccessReusableDeserializer.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/deserializer/SuccessReusableDeserializer.java @@ -5,12 +5,18 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonNode; +import org.evomaster.arazzo.models.domain.ParameterReusable; import org.evomaster.arazzo.models.domain.Reusable; import org.evomaster.arazzo.models.domain.SuccessAction; import org.evomaster.arazzo.models.domain.SuccessReusable; import java.io.IOException; +/** + * Custom Jackson deserializer for {@link SuccessReusable}. + * It differentiates the incoming JSON payload based on the presence of the "reference" field, + * mapping it to a {@link SuccessReusable.ReusableObj} if present, or to a {@link SuccessReusable.Success} otherwise. + */ public class SuccessReusableDeserializer extends JsonDeserializer { @Override diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/mapper/ArazzoMapper.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/mapper/ArazzoMapper.java index 55f35400c8..9a10d32bf0 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/mapper/ArazzoMapper.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/mapper/ArazzoMapper.java @@ -12,6 +12,10 @@ import java.util.Map; import java.util.stream.Collectors; +/** + * Mapper class responsible for converting Arazzo Specification (DTOs) + * into their corresponding domain models. + */ public class ArazzoMapper { private ArazzoReferenceResolver resolver; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/AnyExpression.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/AnyExpression.java index a2353cf230..64054adf4c 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/AnyExpression.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/AnyExpression.java @@ -2,6 +2,12 @@ import com.fasterxml.jackson.databind.JsonNode; +/** + * Representing the model (Any | {expression}) + * The value to pass in the parameter. + * The value can be a constant or a Runtime Expression to be evaluated + * and passed to the referenced operation or workflow + */ public abstract class AnyExpression { public AnyExpression() { diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ArazzoSpecifications.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ArazzoSpecifications.java index 89281e49dd..b8220f9e20 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ArazzoSpecifications.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ArazzoSpecifications.java @@ -2,6 +2,10 @@ import java.util.List; +/** + * Representing the model Arazzo Specification Object + * This is the root object of the Arazzo Description + */ public class ArazzoSpecifications { private String arazzo; private InfoArazzo info; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Components.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Components.java index e87719c5a7..b422254ac5 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Components.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Components.java @@ -3,6 +3,12 @@ import java.util.Map; import io.swagger.v3.oas.models.media.Schema; +/** + * Representing the model Components Object + * Holds a set of reusable objects for different aspects of the Arazzo Specification. + * All objects defined within the components object will have no effect on the Arazzo Description + * unless they are explicitly referenced from properties outside the components object. + */ public class Components { private Map> inputs; private Map parameters; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Criterion.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Criterion.java index 8117bb5ab8..88ee23d551 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Criterion.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Criterion.java @@ -3,6 +3,11 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.evomaster.arazzo.deserializer.CriterionTypeDeserializer; +/** + * Representing the model Criterion Object + * An object used to specify the context, conditions, + * and condition types that can be used to prove or satisfy assertions specified + */ public class Criterion { private String context; private String condition; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionExpression.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionExpression.java index f7977b7690..10700f03cf 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionExpression.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionExpression.java @@ -1,5 +1,9 @@ package org.evomaster.arazzo.models.domain; +/** + * Representing the model Criterion Expression Type Object + * An object used to describe the type and version of an expression used within a Criterion Object + */ public class CriterionExpression { private String type; private String version; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionType.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionType.java index f573abb563..faf268ac6e 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionType.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/CriterionType.java @@ -1,5 +1,8 @@ package org.evomaster.arazzo.models.domain; +/** + * It represents an object that can be a String or a {@link CriterionExpression}. + */ public abstract class CriterionType { public CriterionType() { diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureAction.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureAction.java index 70d0524117..614575a4d1 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureAction.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureAction.java @@ -2,6 +2,10 @@ import java.util.List; +/** + * Representing the model Failure Action Object + * A single failure action which describes an action to take upon failure of a workflow step + */ public class FailureAction { private String name; private String type; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureReusable.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureReusable.java index fcf259d4aa..070067251d 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureReusable.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/FailureReusable.java @@ -1,5 +1,8 @@ package org.evomaster.arazzo.models.domain; +/** + * It represents an object that can be a {@link FailureAction} or a {@link Reusable}. + */ public abstract class FailureReusable { public FailureReusable() { diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/InfoArazzo.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/InfoArazzo.java index 84b7350ca6..5ad6681989 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/InfoArazzo.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/InfoArazzo.java @@ -1,5 +1,9 @@ package org.evomaster.arazzo.models.domain; +/** + * Representing the model Info Object + * This is the root object of the Arazzo Description + */ public class InfoArazzo { private String title; private String summary; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Parameter.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Parameter.java index fd2047e629..f62e88b60d 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Parameter.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Parameter.java @@ -3,6 +3,10 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.evomaster.arazzo.deserializer.AnyExpressionDeserializer; +/** + * Representing the model Parameter Object + * Describes a single step parameter + */ public class Parameter { private String name; private String in; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ParameterReusable.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ParameterReusable.java index b243cdf27f..38cc80f43f 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ParameterReusable.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/ParameterReusable.java @@ -1,5 +1,8 @@ package org.evomaster.arazzo.models.domain; +/** + * It represents an object that can be a {@link Parameter} or a {@link Reusable}. + */ public abstract class ParameterReusable { public ParameterReusable() { diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/PayloadReplacement.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/PayloadReplacement.java index c431bc1882..ab3dae9f47 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/PayloadReplacement.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/PayloadReplacement.java @@ -3,6 +3,10 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import org.evomaster.arazzo.deserializer.AnyExpressionDeserializer; +/** + * Representing the model Payload Replacement Object + * Describes a location within a payload (e.g., a request body) and a value to set within the location. + */ public class PayloadReplacement { private String target; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/RequestBody.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/RequestBody.java index ad36ef918f..a2a8a94d1a 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/RequestBody.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/RequestBody.java @@ -4,6 +4,10 @@ import java.util.List; +/** + * Representing the model Request Body Object + * A single request body describing the Content-Type and request body content to be passed by a step to an operation. + */ public class RequestBody { private String contentType; private JsonNode payload; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Reusable.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Reusable.java index 8665bc7249..cded5118e7 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Reusable.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Reusable.java @@ -1,5 +1,9 @@ package org.evomaster.arazzo.models.domain; +/** + * Representing the model Reusable Object + * A simple object to allow referencing of objects contained within the {@link Components} + */ public class Reusable { private String reference; private String value; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SourceDescription.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SourceDescription.java index b1437f86d5..8d8c336652 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SourceDescription.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SourceDescription.java @@ -1,5 +1,10 @@ package org.evomaster.arazzo.models.domain; +/** + * Representing the model Source Description Object + * Describes a source description (such as an OpenAPI description) + * that will be referenced by one or more workflows described within an Arazzo Description + */ public class SourceDescription { private String name; private String url; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Step.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Step.java index e1c4eec402..ac52e8da41 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Step.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Step.java @@ -1,8 +1,17 @@ package org.evomaster.arazzo.models.domain; +import org.evomaster.arazzo.resolver.ArazzoReferenceResolver; + import java.util.List; import java.util.Map; +/** + * Representing the model Step Object + * Describes a single workflow step which MAY be a call + * to an API operation (OpenAPI Operation Object) or another Workflow Object + * This model only have SuccessAction, FailureAction and Parameter. + * The references are expected to be resolved by {@link ArazzoReferenceResolver}. + */ public class Step { private String description; private String stepId; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessAction.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessAction.java index 4df5875a8d..cd87d77a4f 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessAction.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessAction.java @@ -2,6 +2,10 @@ import java.util.List; +/** + * Representing the model Success Action Object + * A single success action which describes an action to take upon success of a workflow step + */ public class SuccessAction { private String name; private String type; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessReusable.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessReusable.java index a9a0d0e4da..2e5660eae2 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessReusable.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/SuccessReusable.java @@ -1,5 +1,8 @@ package org.evomaster.arazzo.models.domain; +/** + * It represents an object that can be a {@link SuccessAction} or a {@link Reusable}. + */ public abstract class SuccessReusable { public SuccessReusable() { diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Workflow.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Workflow.java index 90d992589c..a83755fbfe 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Workflow.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/domain/Workflow.java @@ -1,10 +1,20 @@ package org.evomaster.arazzo.models.domain; import io.swagger.v3.oas.models.media.Schema; +import org.evomaster.arazzo.resolver.ArazzoReferenceResolver; import java.util.List; import java.util.Map; +/** + * Representing the model Workflow Object + * Describes the steps to be taken across one or more APIs to achieve an objective. + * The workflow object MAY define inputs needed in order to execute workflow steps, + * where the defined steps represent a call to an API operation or another workflow, + * and a set of outputs. + * This model only have SuccessAction, FailureAction and Parameter. + * The references are expected to be resolved by {@link ArazzoReferenceResolver}. + */ public class Workflow { private String workflowId; private String summary; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/ArazzoSpecificationsDTO.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/ArazzoSpecificationsDTO.java index 1cbc7f812e..355a4b278e 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/ArazzoSpecificationsDTO.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/ArazzoSpecificationsDTO.java @@ -6,6 +6,10 @@ import java.util.List; +/** + * Representing the ArazzoSpecifications (DTO) + * Used for direct document parsing + */ public class ArazzoSpecificationsDTO { private String arazzo; private InfoArazzo info; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/StepDTO.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/StepDTO.java index 9c0ed9538f..9a80d6628d 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/StepDTO.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/StepDTO.java @@ -9,6 +9,11 @@ import java.util.List; import java.util.Map; +/** + * Representing the Step (DTO) + * Used for direct document parsing + * Use SuccessReusable, FailureReusable and ParameterReusable, for representing (Object | Reusable Object) + */ public class StepDTO { private String description; private String stepId; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/WorkflowDTO.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/WorkflowDTO.java index a99d818509..37898cbc33 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/WorkflowDTO.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/models/dto/WorkflowDTO.java @@ -12,6 +12,11 @@ import java.util.List; import java.util.Map; +/** + * Representing the Workflow (DTO) + * Used for direct document parsing + * Use SuccessReusable, FailureReusable and ParameterReusable, for representing (Object | Reusable Object) + */ public class WorkflowDTO { private String workflowId; private String summary; diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/parser/ArazzoParser.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/parser/ArazzoParser.java index 6af8f4f1d9..8ef2995054 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/parser/ArazzoParser.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/parser/ArazzoParser.java @@ -10,6 +10,9 @@ import org.evomaster.arazzo.models.dto.ArazzoSpecificationsDTO; import org.evomaster.arazzo.resolver.ArazzoReferenceResolver; +/** + * Parse a String containing an Arazzo document into a complete model in ArazzoSpecifications + */ public class ArazzoParser { private static final ObjectMapper JSON_MAPPER = new ObjectMapper().findAndRegisterModules(); diff --git a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/resolver/ArazzoReferenceResolver.java b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/resolver/ArazzoReferenceResolver.java index f4bdaccf62..bed05cfd7a 100644 --- a/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/resolver/ArazzoReferenceResolver.java +++ b/core-extra/arazzo-parser/src/main/java/org/evomaster/arazzo/resolver/ArazzoReferenceResolver.java @@ -9,6 +9,9 @@ import java.util.List; import java.util.stream.Collectors; +/** + * Resolver class responsible for resolving Arazzo references. + */ public class ArazzoReferenceResolver { private Components components; private JsonNode arazzoJsonNode; @@ -32,6 +35,9 @@ public void setOpenApi(OpenAPI openApi) { this.openApi = openApi; } + /** + * Resolve the {@link SuccessReusable} references to get a complete list of {@link SuccessAction} + */ public List resolveSuccessReusable(List items) { if (items == null) { return null; @@ -47,6 +53,9 @@ public List resolveSuccessReusable(List items) { }).collect(Collectors.toList()); } + /** + * Resolve the {@link FailureReusable} references to get a complete list of {@link FailureAction} + */ public List resolveFailureReusable(List items) { if (items == null) { return null; @@ -62,6 +71,9 @@ public List resolveFailureReusable(List items) { }).collect(Collectors.toList()); } + /** + * Resolve the {@link ParameterReusable} references to get a complete list of {@link Parameter} + */ public List resolveParametersReusable(List items) { if (items == null) { return null;