Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4d30c61
First version parser: Only info document Arazzo. Not Working
daniellopera15 Apr 2, 2026
072562e
Read document arazzo
daniellopera15 Apr 2, 2026
f6c0ebb
Validation Arazzo. Incomplete
daniellopera15 Apr 6, 2026
6c43da5
#UP validate workflow and step. Incomplete
daniellopera15 Apr 10, 2026
c5cfff8
Validation with reference and
daniellopera15 Apr 17, 2026
885bd16
Clean Overlay
daniellopera15 Apr 17, 2026
24abb2b
Parser with validation. Not working
daniellopera15 Apr 17, 2026
025a1e0
Arazzo Only Parser. Test not working
daniellopera15 Apr 18, 2026
825738f
Arazzo parser working with references. Without Validations
daniellopera15 Apr 19, 2026
edc5a7e
Resolver refs on inputs in Workflows
daniellopera15 Apr 20, 2026
4428c01
Delete validation and fixes
daniellopera15 Apr 20, 2026
e4d50d7
clean import
daniellopera15 Apr 21, 2026
cf0fb9c
Fix old code
daniellopera15 Apr 21, 2026
cc951d3
Create Parser in module core-extra
daniellopera15 May 1, 2026
ccdf357
Merge branch 'WebFuzzing:master' into feature/arazzo-parser
daniellopera15 May 1, 2026
f2f7161
Clean core of Arazzo Parser
daniellopera15 May 1, 2026
8d614e8
Add @java-docs v1
daniellopera15 May 2, 2026
075f53c
Rename package of Arazzo
daniellopera15 Jun 26, 2026
03fd9c5
Fix refactor package
daniellopera15 Jun 27, 2026
d1783d1
Review PR V1
daniellopera15 Jun 27, 2026
86bd83d
Update EvoMaster
daniellopera15 Jun 27, 2026
714a8ac
Update Juni 4 to 5 in ArazzoParser
daniellopera15 Jun 27, 2026
cf6f033
Revert changes core/pom.xml
daniellopera15 Jun 27, 2026
10f0ff4
Review PR V2
daniellopera15 Jun 27, 2026
03cd6ab
Review PR V3
daniellopera15 Jun 28, 2026
dbe070f
Review PR V4
daniellopera15 Jun 28, 2026
796be29
Rename DTO's to Unresolved
daniellopera15 Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
68 changes: 68 additions & 0 deletions core-extra/arazzo-parser/pom.xml
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))) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IllegalArgumentException

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

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);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see previous comments

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The 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 {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i do not understand this distinction between DTO and model here. would need to add more info in the comments, and possibly discuss it.

why are using DTOs? are we "transfering" objects between systems/processes here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
}
}
Loading