Skip to content

Commit

Permalink
Merge branch 'main' into 82-duplicate-function-names-in-contractverif…
Browse files Browse the repository at this point in the history
…iertest

# Conflicts:
#	README.md
  • Loading branch information
jemacineiras committed Feb 8, 2024
2 parents 7041405 + d265456 commit a8880b7
Show file tree
Hide file tree
Showing 9 changed files with 412 additions and 121 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ After you have these installed, you need to add the Spring Cloud Contract Maven
<dependency>
<groupId>com.sngular</groupId>
<artifactId>scc-multiapi-converter</artifactId>
<version>3.0.1</version>
<version>3.4.1</version>
<scope>compile</scope>
</dependency>
</dependencies>
Expand Down
66 changes: 52 additions & 14 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.sngular</groupId>
<artifactId>scc-multiapi-converter</artifactId>
<version>3.4.0</version>
<version>3.4.1</version>
<name>SCC-MultiApi-Converter</name>
<description>Generates Spring Cloud Contracts based on an OpenApi and AsyncApi document</description>
<url>https://github.com/sngular/scc-multiapi-converter</url>
Expand All @@ -16,7 +16,7 @@
<name>Mozilla Public License 2.0</name>
<url>https://github.com/sngular/scc-multiapi-converter/blob/main/LICENSE</url>
<distribution>repo</distribution>
<comments></comments>
<comments/>
</license>
</licenses>

Expand Down Expand Up @@ -137,23 +137,44 @@
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-verifier</artifactId>
<version>3.1.3</version>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.sonatype.plexus</groupId>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock-jre8-standalone</artifactId>
<version>2.35.1</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.4.12</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.4.12</version>
</dependency>
<dependency>
<groupId>org.codehaus.plexus</groupId>
<artifactId>plexus-build-api</artifactId>
<version>0.0.7</version>
<version>1.2.0</version>
</dependency>

<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>3.8.5</version>
<version>3.9.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>3.6.4</version>
<version>3.8.1</version>
<scope>provided</scope>
</dependency>
<dependency>
Expand All @@ -165,38 +186,55 @@
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.13.3</version>
<version>2.15.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4.2</version>
<version>2.15.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<version>1.18.26</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<version>3.8.5</version>
<version>3.9.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.0.0-jre</version>
</dependency>
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator</artifactId>
<version>5.4.0</version>
<version>6.6.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-spec-java</artifactId>
<version>3.1.3</version>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.23.1</version>
<version>3.24.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/net.javacrumbs.json-unit/json-unit-assertj -->
<dependency>
<groupId>net.javacrumbs.json-unit</groupId>
<artifactId>json-unit-assertj</artifactId>
<version>3.2.4</version>
<scope>test</scope>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public Collection<Contract> convertFrom(final File file) {
private void processPublishOperation(
final Contract contract, final String operationId, final ResponseBodyMatchers responseBodyMatchers, final Map<String, Object> bodyProcessed, final String topicName) {
final Input input = new Input();
input.triggeredBy(operationId + "()");
input.triggeredBy(operationId + "Send()");
contract.setInput(input);

final OutputMessage outputMessage = new OutputMessage();
Expand All @@ -90,11 +90,14 @@ private void processPublishOperation(

private void processSubscribeOperation(final Contract contract, final Map<String, Object> bodyProcessed, final String topicName, final String operationId) {
final Input input = new Input();
input.messageFrom(topicName);
input.messageBody(bodyProcessed);
input.messageHeaders(headers -> headers.accept("application/json"));
input.assertThat(operationId + "Validation()");
input.triggeredBy(operationId + "Send()");
contract.setInput(input);
final OutputMessage outputMessage = new OutputMessage();
outputMessage.sentTo(topicName);
outputMessage.body(bodyProcessed);
outputMessage.headers(headers -> headers.accept("application/json"));
outputMessage.assertThat(operationId + "Validation()");
contract.setOutputMessage(outputMessage);
}

private Map<String, Object> processMessage(
Expand Down Expand Up @@ -451,7 +454,7 @@ private Pair<JsonNode, Map<String, Object>> fillObjectPropertiesFromAvro(final R

for (int i = 0; i < properties.size(); i++) {
var type = AsyncApiContractConverterUtils.getType(properties.get(i));
if (type.equals("")) {
if (type.isEmpty()) {
type = properties.get(i).get(BasicTypeConstants.TYPE).get(BasicTypeConstants.TYPE).asText();
}
final String fieldName = properties.get(i).get(BasicTypeConstants.NAME).asText();
Expand Down Expand Up @@ -508,7 +511,7 @@ private Pair<JsonNode, Map<String, Object>> fillObjectPropertiesFromAvro(final R
case BasicTypeConstants.ENUM:
final var symbols = (ArrayNode) properties.get(i).get("type").get("symbols");
final var symbol = symbols.get(BasicTypeConstants.RANDOM.nextInt(symbols.size())).textValue();
responseBodyMatchers.jsonPath("$." + path, responseBodyMatchers.byRegex("^(" + joinValues(symbols, "|") + ")$"));
responseBodyMatchers.jsonPath("$." + path, responseBodyMatchers.byRegex("^(" + joinValues(symbols) + ")$"));
messageBody.put(fieldName, symbol);
break;
case BasicTypeConstants.FLOAT:
Expand All @@ -529,13 +532,13 @@ private Pair<JsonNode, Map<String, Object>> fillObjectPropertiesFromAvro(final R
return new MutablePair<>(result, messageBody);
}

private String joinValues(final ArrayNode symbols, final String splitter) {
private String joinValues(final ArrayNode symbols) {
final var iterator = symbols.elements();
final var builder = new StringBuilder();
while (iterator.hasNext()) {
builder.append(iterator.next().textValue());
if (iterator.hasNext()) {
builder.append(splitter);
builder.append("|");
}
}
return builder.toString();
Expand All @@ -554,6 +557,7 @@ private Pair<JsonNode, Map<String, Object>> processAvro(final ResponseBodyMatche
} catch (final IOException e) {
log.error("Error", e);
}
return fillObjectPropertiesFromAvro(responseBodyMatchers, (ArrayNode) fileTree.get("fields"), "");
assert fileTree != null;
return fillObjectPropertiesFromAvro(responseBodyMatchers, (ArrayNode) fileTree.get("fields"), "");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,57 +7,33 @@

package com.sngular.multiapi.converter.openapi;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import com.sngular.multiapi.converter.exception.MultiApiContractConverterException;
import com.sngular.multiapi.converter.openapi.model.ConverterPathItem;
import com.sngular.multiapi.converter.openapi.model.OperationType;
import com.sngular.multiapi.converter.utils.BasicTypeConstants;
import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.MapSchema;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.parser.core.models.ParseOptions;
import io.swagger.v3.parser.core.models.SwaggerParseResult;
import io.swagger.v3.parser.exception.ReadContentException;
import lombok.extern.slf4j.Slf4j;
import com.sngular.multiapi.converter.utils.BasicTypeConstants;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.spec.internal.Body;
import org.springframework.cloud.contract.spec.internal.BodyMatcher;
import org.springframework.cloud.contract.spec.internal.BodyMatchers;
import org.springframework.cloud.contract.spec.internal.DslProperty;
import org.springframework.cloud.contract.spec.internal.Headers;
import org.springframework.cloud.contract.spec.internal.QueryParameters;
import org.springframework.cloud.contract.spec.internal.Request;
import org.springframework.cloud.contract.spec.internal.Response;
import org.springframework.cloud.contract.spec.internal.ResponseBodyMatchers;
import org.springframework.cloud.contract.spec.internal.Url;
import org.springframework.cloud.contract.spec.internal.UrlPath;
import org.springframework.cloud.contract.spec.internal.*;

import java.io.File;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

@Slf4j
public final class OpenApiContractConverter {
Expand Down Expand Up @@ -142,7 +118,7 @@ private Collection<Contract> getContracts(final OpenAPI openApi) {
for (Entry<String, PathItem> pathItem : openApi.getPaths().entrySet()) {
extractPathItem(pathItem.getValue())
.forEach(converterPathItem ->
processContract(contracts, pathItem, converterPathItem.getOperation(), converterPathItem.getOperationType()));
processContract(contracts, pathItem, converterPathItem.getOperation(), converterPathItem.getOperationType()));
}
return contracts;
}
Expand Down Expand Up @@ -177,8 +153,8 @@ private void processContract(final List<Contract> contracts, final Entry<String,
final String contractDescription = pathItem.getValue().getSummary();
final var requestList = processRequest(pathItem, operation, name.name());
final var responseList = processResponse(apiResponse.getKey(), apiResponse.getValue());
final var counter = new AtomicInteger(0);
for (var request : requestList) {
final var counter = new AtomicInteger(0);
for (var response : responseList) {
contracts.add(createContract(contractName, contractDescription, request, response, counter));
}
Expand Down Expand Up @@ -306,11 +282,10 @@ private Consumer<Request> enrichRequest(final Entry<String, PathItem> pathItem,
private List<Request> processRequestContent(final Operation operation) {
final List<Request> requestList = new LinkedList<>();
for (Entry<String, MediaType> content : operation.getRequestBody().getContent().entrySet()) {
final List<Pair<Body, BodyMatchers>> bodyMap;
final MediaType mediaType = content.getValue();
final Headers headers = new Headers();
headers.header("Content-Type", content.getKey());
bodyMap = processRequestBody(mediaType.getSchema());
final List<Pair<Body, BodyMatchers>> bodyMap = new ArrayList<>(processRequestBody(mediaType.getSchema()));
if (Objects.nonNull(mediaType.getExample())) {
bodyMap.add(buildFromExample(mediaType.getExample()));
} else if (Objects.nonNull(mediaType.getExamples())) {
Expand Down Expand Up @@ -380,11 +355,11 @@ private List<Pair<Body, BodyMatchers>> createBodyForProperty(final String ref, f
final Schema<?> subSchema = getSchemaFromComponent(subRef);
if (Objects.nonNull(subSchema.getProperties())) {
bodyList = applyMapToBodyList(propertyBodyList, property.getKey(), processComplexBodyAndMatchers(property.getKey(), subSchema.getProperties()));
} else if (((ArraySchema) subSchema).getItems() instanceof ComposedSchema) {
final Schema<?> arraySchema = ((ArraySchema) subSchema).getItems();
} else if (subSchema.getItems() instanceof ComposedSchema) {
final Schema<?> arraySchema = subSchema.getItems();
bodyList = applyBodyToList(propertyBodyList, property.getKey(), processComposedSchema((ComposedSchema) arraySchema));
} else {
final Schema<?> arraySchema = ((ArraySchema) subSchema).getItems();
final Schema<?> arraySchema = subSchema.getItems();
bodyList = this.applyObjectToBodyList(propertyBodyList, ref, writeBodyMatcher(null, ref, arraySchema, arraySchema.getType()));
}
} else if (Objects.nonNull(property.getValue().getEnum())) {
Expand Down Expand Up @@ -447,7 +422,7 @@ private Pair<Body, BodyMatchers> combineProperties(final Pair<Body, BodyMatchers
orgMatchers.matchers().addAll(bodyValue.getRight().matchers());
return Pair.of(
new Body(new DslProperty(addProperty(orgBody.getClientValue(), property, bodyValue.getLeft().getClientValue()), addProperty(orgBody.getServerValue(), property,
bodyValue.getLeft().getServerValue()))),
bodyValue.getLeft().getServerValue()))),
orgMatchers);
}

Expand Down Expand Up @@ -591,7 +566,7 @@ private Pair<Object, BodyMatchers> writeBodyMatcher(final Entry<String, Schema>
}

private static Object getSafeExample(final Entry<String, Schema> property, final Schema schema) {
final var propertyExample = Objects.nonNull(property) ? property.getValue().getExample() : null;
final var propertyExample = Objects.nonNull(property) ? property.getValue().getExample() : null;
final var schemaExample = Objects.nonNull(schema) ? schema.getExample() : null;

return ObjectUtils.defaultIfNull(propertyExample, schemaExample);
Expand Down Expand Up @@ -632,7 +607,7 @@ private Pair<Object, BodyMatchers> processObjectBodyMatcher(final Entry<String,
} else if (Objects.nonNull(internalRef.getProperties())) {
final Map<String, Schema> subProperties = internalRef.getProperties();
result = processComplexBodyAndMatchers(fieldName, subProperties);
} else if (Objects.nonNull(internalRef.getAdditionalProperties())){
} else if (Objects.nonNull(internalRef.getAdditionalProperties())) {
final Schema subProperties = (Schema) internalRef.getAdditionalProperties();
result = writeBodyMatcher(null, fieldName, subProperties, BasicTypeConstants.MAP);
} else {
Expand Down Expand Up @@ -718,7 +693,7 @@ private Pair<Object, BodyMatchers> processComplexBodyAndMatchers(final String ob
propertyMap.put(property.getKey(), processedBody.getLeft());
bodyMatchers.matchers().addAll(processedBody.getRight().matchers());
} else {
final var subProperties = ((ArraySchema) getSchemaFromComponent(ref)).getItems();
final var subProperties = (getSchemaFromComponent(ref)).getItems();
final Pair<List<Object>, BodyMatchers> processedArray = processArray(subProperties, objectName);
propertyMap.put(property.getKey(), processedArray.getLeft());
bodyMatchers.matchers().addAll(processedArray.getRight().matchers());
Expand All @@ -743,7 +718,7 @@ private Schema<?> getReferencedProperties(final Schema<?> schema) {
final String ref = OpenApiContractConverterUtils.mapRefName(schema);
referencedSchema = getSchemaFromComponent(ref);
if (!existSchemaWithPropertiesInComponent(ref)) {
referencedSchema = ((ArraySchema) referencedSchema).getItems();
referencedSchema = (referencedSchema).getItems();
}
return referencedSchema;
}
Expand Down Expand Up @@ -776,7 +751,7 @@ private Pair<List<Object>, BodyMatchers> processArray(final Schema<?> arraySchem
case BasicTypeConstants.ARRAY:
if (arraySchema instanceof ArraySchema) {
final var calculatedValue = processArrayArray((ArraySchema) arraySchema, objectName);
if (Objects.nonNull(((ArraySchema) arraySchema).getItems().getType())) {
if (Objects.nonNull((arraySchema).getItems().getType())) {
tempValue = calculatedValue;
} else {
tempValue = Pair.of(calculatedValue.getLeft().get(0), calculatedValue.getRight());
Expand Down Expand Up @@ -809,7 +784,7 @@ private Pair<List<Object>, BodyMatchers> processArray(final Schema<?> arraySchem
}

private Pair<Object, BodyMatchers> processObjectArray(final Schema<?> arraySchema, final String objectName) {
final HashMap<String, Schema> subObject = (HashMap<String, Schema>) arraySchema.getProperties();
final Map<String, Schema> subObject = arraySchema.getProperties();
final Pair<Object, BodyMatchers> result;
if (Objects.nonNull(subObject)) {
result = processComplexBodyAndMatchers(objectName, subObject);
Expand Down Expand Up @@ -990,6 +965,7 @@ private Pair<Body, BodyMatchers> bodyJoin(final Pair<Body, BodyMatchers> tempBod
bodyMatchers.matchers().addAll(matchers.matchers());
return Pair.of(newBody, bodyMatchers);
}

private List<Schema<?>> combineSchema(final List<Schema> anyOfThis) {
final List<Schema<?>> finalList = new LinkedList<>();
if (!anyOfThis.isEmpty()) {
Expand Down
Loading

0 comments on commit a8880b7

Please sign in to comment.