-
Notifications
You must be signed in to change notification settings - Fork 114
Arazzo Parser v1 #1568
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Arazzo Parser v1 #1568
Changes from all commits
4d30c61
072562e
f6c0ebb
6c43da5
c5cfff8
885bd16
24abb2b
025a1e0
825738f
edc5a7e
4428c01
e4d50d7
cf0fb9c
cc951d3
ccdf357
f2f7161
8d614e8
075f53c
03fd9c5
d1783d1
86bd83d
714a8ac
cf6f033
10f0ff4
03cd6ab
dbe070f
796be29
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>org.evomaster</groupId> | ||
| <artifactId>evomaster-core-extra</artifactId> | ||
| <version>6.0.1-SNAPSHOT</version> | ||
| </parent> | ||
|
|
||
| <artifactId>arazzo-parser</artifactId> | ||
|
|
||
| <dependencies> | ||
| <dependency> | ||
| <groupId>io.swagger.core.v3</groupId> | ||
| <artifactId>swagger-models</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.fasterxml.jackson.core</groupId> | ||
| <artifactId>jackson-databind</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.fasterxml.jackson.dataformat</groupId> | ||
| <artifactId>jackson-dataformat-yaml</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>io.swagger.core.v3</groupId> | ||
| <artifactId>swagger-core</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.junit.jupiter</groupId> | ||
| <artifactId>junit-jupiter</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>io.swagger.parser.v3</groupId> | ||
| <artifactId>swagger-parser-v3</artifactId> | ||
| <version>2.1.33</version> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>com.graphql-java</groupId> | ||
| <artifactId>java-dataloader</artifactId> | ||
| <scope>test</scope> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| <build> | ||
| <plugins> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-compiler-plugin</artifactId> | ||
| <configuration> | ||
| <release>8</release> | ||
| </configuration> | ||
| </plugin> | ||
| <plugin> | ||
| <groupId>org.apache.maven.plugins</groupId> | ||
| <artifactId>maven-surefire-plugin</artifactId> | ||
| <configuration> | ||
| <argLine>@{argLine} -ea -Xms512m -Xmx1536m -Xss2m -Dfile.encoding=UTF-8 -Duser.language=en -Duser.country=GB</argLine> | ||
| </configuration> | ||
| </plugin> | ||
| </plugins> | ||
| </build> | ||
|
|
||
| </project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| package com.webfuzzing.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; | ||
|
|
||
| /** | ||
| * Read Arrazzo documents | ||
| */ | ||
| public class ArazzoAccess { | ||
|
|
||
| /** | ||
| * Read Arrazzo documents from disk (files in the project) | ||
| */ | ||
| public static String readFromDisk(String arazzoLocation) { | ||
| 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 IllegalArgumentException("The file path provided for the Arazzo Schema " + arazzoLocation + " ended up with the following error: " + e.getMessage()); | ||
| } | ||
|
|
||
| if ((!Files.exists(path))) { | ||
| throw new IllegalArgumentException("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 RuntimeException("Error reading the Arazzo file: " + e.getMessage()); | ||
| } | ||
|
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| package com.webfuzzing.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 com.webfuzzing.arazzo.models.domain.AnyExpression; | ||
|
|
||
| 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<AnyExpression> { | ||
|
|
||
| @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); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package com.webfuzzing.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 com.webfuzzing.arazzo.models.domain.CriterionExpression; | ||
| import com.webfuzzing.arazzo.models.domain.CriterionType; | ||
|
|
||
| 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<CriterionType> { | ||
|
|
||
| @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"); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package com.webfuzzing.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 com.webfuzzing.arazzo.models.domain.FailureAction; | ||
| import com.webfuzzing.arazzo.models.domain.FailureReusable; | ||
| import com.webfuzzing.arazzo.models.domain.Reusable; | ||
|
|
||
| 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<FailureReusable> { | ||
|
|
||
| @Override | ||
| public FailureReusable deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { | ||
| JsonNode node = jsonParser.getCodec().readTree(jsonParser); | ||
|
|
||
| if (node.has(Reusable.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); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package com.webfuzzing.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 com.webfuzzing.arazzo.models.domain.Parameter; | ||
| import com.webfuzzing.arazzo.models.domain.ParameterReusable; | ||
| import com.webfuzzing.arazzo.models.domain.Reusable; | ||
|
|
||
| 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<ParameterReusable> { | ||
|
|
||
| @Override | ||
| public ParameterReusable deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { | ||
| JsonNode node = jsonParser.getCodec().readTree(jsonParser); | ||
|
|
||
| if (node.has(Reusable.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); | ||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| package com.webfuzzing.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 com.webfuzzing.arazzo.models.domain.Reusable; | ||
| import com.webfuzzing.arazzo.models.domain.SuccessAction; | ||
| import com.webfuzzing.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<SuccessReusable> { | ||
|
|
||
| @Override | ||
| public SuccessReusable deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { | ||
| JsonNode node = jsonParser.getCodec().readTree(jsonParser); | ||
|
|
||
| if (node.has(Reusable.REFERENCE)) { | ||
| Reusable reusable = jsonParser.getCodec().treeToValue(node, Reusable.class); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. see previous comments
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
| return new SuccessReusable.ReusableObj(reusable); | ||
| } | ||
|
|
||
| SuccessAction action = jsonParser.getCodec().treeToValue(node, SuccessAction.class); | ||
| return new SuccessReusable.Success(action); | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,102 @@ | ||
| package com.webfuzzing.arazzo.mapper; | ||
|
|
||
| import com.webfuzzing.arazzo.models.domain.ArazzoSpecifications; | ||
| import com.webfuzzing.arazzo.models.domain.Step; | ||
| import com.webfuzzing.arazzo.models.domain.Workflow; | ||
| import com.webfuzzing.arazzo.models.unresolved.UnresolvedArazzoSpecifications; | ||
| import com.webfuzzing.arazzo.models.unresolved.UnresolvedStep; | ||
| import com.webfuzzing.arazzo.models.unresolved.UnresolvedWorkflow; | ||
| import com.webfuzzing.arazzo.resolver.ArazzoReferenceResolver; | ||
| import io.swagger.v3.oas.models.media.Schema; | ||
|
|
||
| import java.util.Map; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| /** | ||
| * Mapper class responsible for converting unresolved Arazzo Specification models | ||
| * into their corresponding domain models. | ||
| */ | ||
| public class ArazzoMapper { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i do not understand this distinction between why are using DTOs? are we "transfering" objects between systems/processes here?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I explained it at https://github.com/WebFuzzing/EvoMaster/pull/1568/changes#r3488603682 |
||
| private ArazzoReferenceResolver resolver; | ||
|
|
||
| public ArazzoMapper(ArazzoReferenceResolver resolver) { | ||
| this.resolver = resolver; | ||
| } | ||
|
|
||
| /** | ||
| * Maps UnresolvedArazzoSpecifications to ArazzoSpecifications | ||
| */ | ||
| public ArazzoSpecifications toDomain(UnresolvedArazzoSpecifications unresolvedArazzoSpecifications) { | ||
| return ArazzoSpecifications.builder() | ||
| .arazzo(unresolvedArazzoSpecifications.getArazzo()) | ||
| .info(unresolvedArazzoSpecifications.getInfo()) | ||
| .sourceDescriptions(unresolvedArazzoSpecifications.getSourceDescriptions()) | ||
| .workflows(unresolvedArazzoSpecifications.getWorkflows().stream() | ||
| .map(this::toDomain) | ||
| .collect(Collectors.toList())) | ||
| .components(unresolvedArazzoSpecifications.getComponents()) | ||
| .build(); | ||
| } | ||
|
|
||
| /** | ||
| * Maps UnresolvedWorkflow to Workflow | ||
| */ | ||
| public Workflow toDomain(UnresolvedWorkflow unresolvedWorkflow) { | ||
| return Workflow.builder() | ||
| .workflowId(unresolvedWorkflow.getWorkflowId()) | ||
| .summary(unresolvedWorkflow.getSummary()) | ||
| .description(unresolvedWorkflow.getDescription()) | ||
| .inputs(this.toDomain(unresolvedWorkflow.getInputs())) | ||
| .dependsOn(unresolvedWorkflow.getDependsOn()) | ||
| .steps(unresolvedWorkflow.getSteps().stream() | ||
| .map(this::toDomain) | ||
| .collect(Collectors.toList())) | ||
| .successActions(resolver.resolveSuccessReusable(unresolvedWorkflow.getSuccessActions())) | ||
| .failureActions(resolver.resolveFailureReusable(unresolvedWorkflow.getFailureActions())) | ||
| .outputs(unresolvedWorkflow.getOutputs()) | ||
| .parameters(resolver.resolveParametersReusable(unresolvedWorkflow.getParameters())) | ||
| .build(); | ||
| } | ||
|
|
||
| /** | ||
| * Maps UnresolvedStep to Step | ||
| */ | ||
| public Step toDomain(UnresolvedStep unresolvedStep) { | ||
| return Step.builder() | ||
| .description(unresolvedStep.getDescription()) | ||
| .stepId(unresolvedStep.getStepId()) | ||
| .operationId(unresolvedStep.getOperationId()) | ||
| .operationPath(unresolvedStep.getOperationPath()) | ||
| .workflowId(unresolvedStep.getWorkflowId()) | ||
| .parameters(resolver.resolveParametersReusable(unresolvedStep.getParameters())) | ||
| .requestBody(unresolvedStep.getRequestBody()) | ||
| .successCriteria(unresolvedStep.getSuccessCriteria()) | ||
| .onSuccess(resolver.resolveSuccessReusable(unresolvedStep.getOnSuccess())) | ||
| .onFailure(resolver.resolveFailureReusable(unresolvedStep.getOnFailure())) | ||
| .outputs(unresolvedStep.getOutputs()) | ||
| .build(); | ||
| } | ||
|
|
||
| /** | ||
| * Map Schema to Schema with reference resolved | ||
| */ | ||
| 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<String, Schema> updatedProperties = reference.getProperties().entrySet().stream() | ||
| .collect(Collectors.toMap( | ||
| Map.Entry::getKey, entry -> toDomain(entry.getValue()) | ||
| )); | ||
|
|
||
| reference.setProperties(updatedProperties); | ||
| } | ||
|
|
||
| return reference; | ||
| } | ||
| } | ||
|
|
||
| return schema; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IllegalArgumentException
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done