Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
84 changes: 84 additions & 0 deletions core-extra/arazzo-parser/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?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>5.1.1-SNAPSHOT</version>
</parent>

<artifactId>arazzo-parser</artifactId>

<dependencies>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-models</artifactId>
<version>2.2.34</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-client-java-no-dependencies</artifactId>
<scope>compile</scope>
</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>
<version>2.2.34</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8-standalone</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<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>
<version>2.2.3</version>
<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,39 @@
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;

/**
* Read Arrazzo documents
*/
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());
}

}
}
Original file line number Diff line number Diff line change
@@ -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.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 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;

/**
* 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,31 @@
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;

/**
* 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("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,32 @@
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;

/**
* 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("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,35 @@
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.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<SuccessReusable> {

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

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
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;

/**
* Mapper class responsible for converting Arazzo Specification (DTOs)
* into their corresponding domain models.
*/
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<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