From 6fdd2f9c7c19c7fd40ffb6935fc68d7f489da695 Mon Sep 17 00:00:00 2001 From: Jose Enrique Garcia Date: Thu, 8 Feb 2024 03:59:17 +0100 Subject: [PATCH] Fix Duplicate operation Id --- README.md | 2 +- pom.xml | 64 ++++- .../asyncapi/AsyncApiContractConverter.java | 24 +- .../openapi/OpenApiContractConverter.java | 70 ++--- .../AsyncApiContractConverterTest.java | 70 ++--- ...AsyncApiContractConverterTestFixtures.java | 11 +- .../openapi/OpenApiContractConverterTest.java | 20 ++ .../OpenApiContractConverterTestFixtures.java | 6 + .../resources/openapi/testDuplicateIds.yml | 264 ++++++++++++++++++ 9 files changed, 411 insertions(+), 120 deletions(-) create mode 100644 src/test/resources/openapi/testDuplicateIds.yml diff --git a/README.md b/README.md index 53b7df5..3d172df 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Codacy Badge](https://app.codacy.com/project/badge/Grade/0d331d782ff849f1bdf6d71f60203eff)](https://www.codacy.com/gh/corunet/scc-multiapi-converter/dashboard?utm_source=github.com&utm_medium=referral&utm_content=corunet/scc-multiapi-converter&utm_campaign=Badge_Grade) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/931bef8587a147c3b20adc2f39408200)](https://app.codacy.com/gh/sngular/scc-multiapi-converter/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Maven Central](https://img.shields.io/maven-central/v/net.coru/scc-multiapi-converter.svg?label=Maven%20Central)](https://search.maven.org/search?q=g:%22net.coru%22%20AND%20a:%22scc-multiapi-converter%22) diff --git a/pom.xml b/pom.xml index e7c1072..0ceb562 100644 --- a/pom.xml +++ b/pom.xml @@ -16,7 +16,7 @@ Mozilla Public License 2.0 https://github.com/sngular/scc-multiapi-converter/blob/main/LICENSE repo - + @@ -137,23 +137,44 @@ org.springframework.cloud spring-cloud-contract-verifier - 3.1.3 + 4.0.3 - org.sonatype.plexus + com.jayway.jsonpath + json-path + 2.9.0 + + + com.github.tomakehurst + wiremock-jre8-standalone + 2.35.1 + + + ch.qos.logback + logback-classic + 1.4.12 + + + ch.qos.logback + logback-core + 1.4.12 + + + org.codehaus.plexus plexus-build-api - 0.0.7 + 1.2.0 + org.apache.maven maven-plugin-api - 3.8.5 + 3.9.1 provided org.apache.maven.plugin-tools maven-plugin-annotations - 3.6.4 + 3.8.1 provided @@ -165,38 +186,55 @@ com.fasterxml.jackson.dataformat jackson-dataformat-yaml - 2.13.3 + 2.15.0 com.fasterxml.jackson.core jackson-databind - 2.13.4.2 + 2.15.2 org.projectlombok lombok - 1.18.24 + 1.18.26 org.apache.maven maven-core - 3.8.5 + 3.9.3 provided + + com.google.guava + guava + 32.0.0-jre + org.openapitools openapi-generator - 5.4.0 + 6.6.0 + + + commons-codec + commons-codec + 1.15 org.springframework.cloud spring-cloud-contract-spec-java - 3.1.3 + 4.0.3 org.assertj assertj-core - 3.23.1 + 3.24.2 + test + + + + net.javacrumbs.json-unit + json-unit-assertj + 3.2.4 test diff --git a/src/main/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverter.java b/src/main/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverter.java index 6f04365..5634f0a 100644 --- a/src/main/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverter.java +++ b/src/main/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverter.java @@ -77,7 +77,7 @@ public Collection convertFrom(final File file) { private void processPublishOperation( final Contract contract, final String operationId, final ResponseBodyMatchers responseBodyMatchers, final Map bodyProcessed, final String topicName) { final Input input = new Input(); - input.triggeredBy(operationId + "()"); + input.triggeredBy(operationId + "Send()"); contract.setInput(input); final OutputMessage outputMessage = new OutputMessage(); @@ -90,11 +90,14 @@ private void processPublishOperation( private void processSubscribeOperation(final Contract contract, final Map 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 processMessage( @@ -451,7 +454,7 @@ private Pair> 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(); @@ -508,7 +511,7 @@ private Pair> 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: @@ -529,13 +532,13 @@ private Pair> 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(); @@ -554,6 +557,7 @@ private Pair> 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"), ""); } } diff --git a/src/main/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverter.java b/src/main/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverter.java index 2f6d2fc..6528cf7 100644 --- a/src/main/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverter.java +++ b/src/main/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverter.java @@ -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 { @@ -142,7 +118,7 @@ private Collection getContracts(final OpenAPI openApi) { for (Entry 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; } @@ -177,8 +153,8 @@ private void processContract(final List contracts, final Entry enrichRequest(final Entry pathItem, private List processRequestContent(final Operation operation) { final List requestList = new LinkedList<>(); for (Entry content : operation.getRequestBody().getContent().entrySet()) { - final List> bodyMap; final MediaType mediaType = content.getValue(); final Headers headers = new Headers(); headers.header("Content-Type", content.getKey()); - bodyMap = processRequestBody(mediaType.getSchema()); + final List> bodyMap = new ArrayList<>(processRequestBody(mediaType.getSchema())); if (Objects.nonNull(mediaType.getExample())) { bodyMap.add(buildFromExample(mediaType.getExample())); } else if (Objects.nonNull(mediaType.getExamples())) { @@ -380,11 +355,11 @@ private List> 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())) { @@ -447,7 +422,7 @@ private Pair combineProperties(final Pair writeBodyMatcher(final Entry } private static Object getSafeExample(final Entry 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); @@ -632,7 +607,7 @@ private Pair processObjectBodyMatcher(final Entry 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 { @@ -718,7 +693,7 @@ private Pair 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, BodyMatchers> processedArray = processArray(subProperties, objectName); propertyMap.put(property.getKey(), processedArray.getLeft()); bodyMatchers.matchers().addAll(processedArray.getRight().matchers()); @@ -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; } @@ -776,7 +751,7 @@ private Pair, 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()); @@ -809,7 +784,7 @@ private Pair, BodyMatchers> processArray(final Schema arraySchem } private Pair processObjectArray(final Schema arraySchema, final String objectName) { - final HashMap subObject = (HashMap) arraySchema.getProperties(); + final Map subObject = arraySchema.getProperties(); final Pair result; if (Objects.nonNull(subObject)) { result = processComplexBodyAndMatchers(objectName, subObject); @@ -990,6 +965,7 @@ private Pair bodyJoin(final Pair tempBod bodyMatchers.matchers().addAll(matchers.matchers()); return Pair.of(newBody, bodyMatchers); } + private List> combineSchema(final List anyOfThis) { final List> finalList = new LinkedList<>(); if (!anyOfThis.isEmpty()) { diff --git a/src/test/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverterTest.java b/src/test/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverterTest.java index 818c1e7..c55a033 100644 --- a/src/test/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverterTest.java +++ b/src/test/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverterTest.java @@ -50,7 +50,7 @@ void convertFromTest() { Contract subscribeContract = contractList.get(1); assertThat(subscribeContract.getInput()).isNotNull(); - assertThat(subscribeContract.getOutputMessage()).isNull(); + assertThat(subscribeContract.getOutputMessage()).isNotNull(); assertThat(subscribeContract.getName()).isInstanceOf(String.class).isEqualTo(asyncApiContractConverterTestFixtures.SUBSCRIBE_NAME); assertThat(subscribeContract.getLabel()).isEqualTo(asyncApiContractConverterTestFixtures.SUBSCRIBE_NAME); } @@ -59,15 +59,14 @@ void convertFromTest() { @DisplayName("AsyncApi: Check if Input is being processed okay") void testInput() { var contractList = getContracts(asyncApiContractConverterTestFixtures.EVENT_API_FILE); - Map order = asyncApiContractConverterTestFixtures.createOrder(); Contract publishContract = contractList.get(0); assertThat(publishContract.getInput().getTriggeredBy().getExecutionCommand()).isEqualTo(asyncApiContractConverterTestFixtures.TRIGGERED_BY); Contract subscribeContract = contractList.get(1); - assertThat(subscribeContract.getInput().getMessageFrom().getClientValue()).isEqualTo(asyncApiContractConverterTestFixtures.MESSAGE_FROM); - assertThat(subscribeContract.getInput().getMessageBody().getClientValue()).isNotNull().isEqualTo(order); - Header inputHeader = subscribeContract.getInput().getMessageHeaders().getEntries().stream().iterator().next(); + assertThat(subscribeContract.getOutputMessage().getSentTo()).isEqualTo(asyncApiContractConverterTestFixtures.MESSAGE_FROM); + assertThat(subscribeContract.getOutputMessage().getBody()).isNotNull().isEqualTo(asyncApiContractConverterTestFixtures.createOrder()); + Header inputHeader = subscribeContract.getOutputMessage().getHeaders().getEntries().stream().iterator().next(); assertThat(inputHeader.getName()).isEqualTo(asyncApiContractConverterTestFixtures.HEADER_NAME); assertThat(inputHeader.getClientValue()).isEqualTo(asyncApiContractConverterTestFixtures.HEADER_VALUE); } @@ -76,7 +75,6 @@ void testInput() { @DisplayName("AsyncApi: Check if OutputMessage is being processed okay") void testOutputMessage() { var contractList = getContracts(asyncApiContractConverterTestFixtures.EVENT_API_FILE); - Map order = asyncApiContractConverterTestFixtures.createOrder(); Contract publishContract = contractList.get(0); assertThat(publishContract.getOutputMessage().getSentTo().getClientValue()).isEqualTo(asyncApiContractConverterTestFixtures.PUBLISH_SEND_TO); @@ -89,11 +87,11 @@ void testOutputMessage() { assertThat(publishContract.getOutputMessage().getBodyMatchers().matchers().get(0).path()).isEqualTo(asyncApiContractConverterTestFixtures.INT_ARRAY_BODY_MATCHER); Contract subscribeContract = contractList.get(1); - assertThat(subscribeContract.getInput().getMessageFrom().getClientValue()).isEqualTo(asyncApiContractConverterTestFixtures.MESSAGE_FROM); - Header outputHeader = subscribeContract.getInput().getMessageHeaders().getEntries().iterator().next(); + assertThat(subscribeContract.getOutputMessage().getSentTo()).isEqualTo(asyncApiContractConverterTestFixtures.MESSAGE_FROM); + Header outputHeader = subscribeContract.getOutputMessage().getHeaders().getEntries().iterator().next(); assertThat(outputHeader.getName()).isEqualTo(asyncApiContractConverterTestFixtures.HEADER_NAME); assertThat(outputHeader.getClientValue()).isEqualTo(asyncApiContractConverterTestFixtures.HEADER_VALUE); - assertThat(subscribeContract.getInput().getMessageBody().getClientValue()).isNotNull().isEqualTo(order); + assertThat(subscribeContract.getOutputMessage().getBody()).isNotNull().isEqualTo(asyncApiContractConverterTestFixtures.createOrder()); } @Test @@ -105,11 +103,9 @@ void testEnums() { Contract contract = contractList.get(i); Map bodyValue; - if (Objects.nonNull(contract.getOutputMessage())) { - bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); - } else { - bodyValue = (Map) contract.getInput().getMessageBody().getClientValue(); - } + + bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); + Map orderValue = (Map) bodyValue.get(asyncApiContractConverterTestFixtures.ORDER); String enumName = (String) orderValue.get(asyncApiContractConverterTestFixtures.NAME); @@ -131,11 +127,8 @@ void testComplexObjects() { Map bodyValue; - if (Objects.nonNull(contract.getOutputMessage())) { - bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); - } else { - bodyValue = (Map) contract.getInput().getMessageBody().getClientValue(); - } + bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); + assertThat(bodyValue.get(asyncApiContractConverterTestFixtures.ORDER)).isNotNull(); Map orderValue = (Map) bodyValue.get(asyncApiContractConverterTestFixtures.ORDER); assertThat(orderValue.get(asyncApiContractConverterTestFixtures.NAME)).isNotNull(); @@ -165,11 +158,9 @@ void testArrays() { Map bodyValue; - if (Objects.nonNull(contract.getOutputMessage())) { - bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); - } else { - bodyValue = (Map) contract.getInput().getMessageBody().getClientValue(); - } + + bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); + assertThat(bodyValue.get(asyncApiContractConverterTestFixtures.ORDER)).isNotNull(); Map orderValue = (Map) bodyValue.get(asyncApiContractConverterTestFixtures.ORDER); assertThat(orderValue.get(asyncApiContractConverterTestFixtures.NAME)).isNotNull(); @@ -201,11 +192,8 @@ void testArraysWithRef() { for (int i = 0; i < contractList.size(); i++) { Contract contract = contractList.get(i); - if (Objects.nonNull(contract.getOutputMessage())) { - bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); - } else { - bodyValue = (Map) contract.getInput().getMessageBody().getClientValue(); - } + bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); + assertThat(bodyValue.get(asyncApiContractConverterTestFixtures.ORDER)).isNotNull(); orderValue = (Map) bodyValue.get(asyncApiContractConverterTestFixtures.ORDER); assertThat(orderValue.get(asyncApiContractConverterTestFixtures.EMPLOYEES)).isNotNull(); @@ -233,11 +221,9 @@ void testBasicTypes() { for (int i = 0; i < contractList.size(); i++) { Contract contract = contractList.get(i); - if (Objects.nonNull(contract.getOutputMessage())) { - bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); - } else { - bodyValue = (Map) contract.getInput().getMessageBody().getClientValue(); - } + + bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); + assertThat(bodyValue.get(asyncApiContractConverterTestFixtures.ORDER)).isNotNull(); orderValue = (Map) bodyValue.get(asyncApiContractConverterTestFixtures.ORDER); if (i == 0) { @@ -271,11 +257,9 @@ void testExternalFiles() { Map bodyValue; Map orderValue; List amount; - if (Objects.nonNull(contract.getOutputMessage())) { - bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); - } else { - bodyValue = (Map) contract.getInput().getMessageBody().getClientValue(); - } + + bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); + assertThat(bodyValue.get(asyncApiContractConverterTestFixtures.ORDER)).isNotNull(); orderValue = (Map) bodyValue.get(asyncApiContractConverterTestFixtures.ORDER); amount = (ArrayList) orderValue.get(asyncApiContractConverterTestFixtures.AMOUNT); @@ -302,11 +286,9 @@ void testExternalFilesWithMultipleSchemas() { Map bodyValue; - if (Objects.nonNull(contract.getOutputMessage())) { - bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); - } else { - bodyValue = (Map) contract.getInput().getMessageBody().getClientValue(); - } + + bodyValue = (Map) contract.getOutputMessage().getBody().getClientValue(); + assertThat(bodyValue.get(asyncApiContractConverterTestFixtures.ORDERS)).isNotNull(); Map ordersValue = (Map) bodyValue.get(asyncApiContractConverterTestFixtures.ORDERS); assertThat(ordersValue.get(asyncApiContractConverterTestFixtures.ORDER_LINE)).isNotNull(); diff --git a/src/test/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverterTestFixtures.java b/src/test/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverterTestFixtures.java index 4fb47df..f536a0e 100644 --- a/src/test/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverterTestFixtures.java +++ b/src/test/java/com/sngular/multiapi/converter/asyncapi/AsyncApiContractConverterTestFixtures.java @@ -6,6 +6,8 @@ package com.sngular.multiapi.converter.asyncapi; +import org.springframework.cloud.contract.spec.internal.DslProperty; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -30,13 +32,13 @@ public class AsyncApiContractConverterTestFixtures { protected final static String PUBLISH_NAME = "publishOperation"; - protected final static String TRIGGERED_BY = "publishOperation()"; + protected final static String TRIGGERED_BY = "publishOperationSend()"; protected final static String PUBLISH_SEND_TO = "orderCreated"; protected final static String SUBSCRIBE_NAME = "subscribeOperation"; - protected final static String MESSAGE_FROM = "createOrder"; + protected final static DslProperty MESSAGE_FROM = new DslProperty<>("createOrder", "createOrder"); protected final static String INT_ARRAY_BODY_MATCHER = "order.intArray[0]"; @@ -94,13 +96,12 @@ public class AsyncApiContractConverterTestFixtures { final static String[] ENUM_VALUES = {"Corunet", "Sngular"}; - protected final Map createOrder() { + protected final DslProperty createOrder() { Map order = new HashMap<>(); Map array = new HashMap<>(); array.put(INT_ARRAY, INT_ARRAY_VALUES); order.put(ORDER, array); - - return order; + return new DslProperty<>(order, order); } } diff --git a/src/test/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverterTest.java b/src/test/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverterTest.java index 37d8519..e794b56 100644 --- a/src/test/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverterTest.java +++ b/src/test/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverterTest.java @@ -524,4 +524,24 @@ void testAnyOfWithMapsInside() { assertThat(bodyServerValueMap.get("players")).isInstanceOf(Body.class); } + @Test + @DisplayName("OpenApi: Check that no duplicate function names are created") + void testDuplicateIds() { + final File file = new File(OpenApiContractConverterTestFixtures.OPENAPI_DUPLICATE_IDS); + Contract[] contractList = multiApiContractConverter.convertFrom(file).toArray(new Contract[4]); + assertThat(contractList).hasSize(8); + Contract contract = contractList[0]; + assertThat(contract).isNotNull(); + assertThat(contract.getResponse()).isNotNull(); + Map bodyServerValueMap = (Map) contract.getResponse().getBody().getServerValue(); + assertThat(bodyServerValueMap) + .containsOnlyKeys(OpenApiContractConverterTestFixtures.KEYSET); + contract = contractList[1]; + assertThat(contract).isNotNull(); + assertThat(contract.getResponse()).isNotNull(); + final var responseValue = (ObjectNode) contract.getResponse().getBody().getServerValue(); + assertThat(responseValue).isNotEmpty(); + + } + } \ No newline at end of file diff --git a/src/test/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverterTestFixtures.java b/src/test/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverterTestFixtures.java index 158be5a..332bdde 100644 --- a/src/test/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverterTestFixtures.java +++ b/src/test/java/com/sngular/multiapi/converter/openapi/OpenApiContractConverterTestFixtures.java @@ -1,9 +1,13 @@ package com.sngular.multiapi.converter.openapi; import java.util.List; +import java.util.Set; public final class OpenApiContractConverterTestFixtures { + public static final Set KEYSET = Set.of("country", "address", "birthdate", "nameKanaHankaku", "gender", + "streetNumber", "nameKanaZenkaku", "idDocument", "givenName", "postalCode", "locality", "middleNames", + "familyNameAtBirth", "houseNumberExtension", "streetName", "phoneNumber", "familyName", "name", "region", "email"); static final String PLAYERS = "players"; static final String[] OPENAPI_TEXT_EXTERNAL_REF_KEYS = {"schemaRegistryName", "topic", "kafkaName", "schemaName", "repetitions"}; @@ -92,6 +96,8 @@ public final class OpenApiContractConverterTestFixtures { static final String OPENAPI_ANY_OF_WITH_MAPS = "src/test/resources/openapi/testAnyOfWithMaps.yml"; + static final String OPENAPI_DUPLICATE_IDS = "src/test/resources/openapi/testDuplicateIds.yml"; + static final String NAME = "name"; static final String CONTENT_TYPE = "Content-Type"; diff --git a/src/test/resources/openapi/testDuplicateIds.yml b/src/test/resources/openapi/testDuplicateIds.yml new file mode 100644 index 0000000..5d76aa1 --- /dev/null +++ b/src/test/resources/openapi/testDuplicateIds.yml @@ -0,0 +1,264 @@ +openapi: 3.0.3 +info: + title: intermediary fraud checker for multiple teleop + version: 1.0.0 +servers: + - url: http://localhost:8080/v1 + + +paths: + + /verification/match: + post: + tags: + - verification + + summary: corroborate customer data + + operationId: match + + requestBody: + required: true + content: + application/json: + + schema: + $ref: '#/components/schemas/MatchRequestBody' + + examples: + MatchRequestBodyExample: + value: + phoneNumber: '+34629255833' + idDocument: 66666666q + name: Federica Sanchez Arjona + giventName: Federica + familyName: Sanchez Arjona + nameKanaHankaku: federica + nameKanaZenkaku: Federica + middleNames: Sanchez + familyNameAtBirth: YYYY + address: Tokyo-to Chiyoda-ku Iidabashi 3-10-10 + streetName: Nicolas Salmeron + streetNumber: 4 + postalCode: 1028460 + region: Tokyo + locality: ZZZZ + country: Japan + houseNumberExtension: VVVV + birthdate: '1978-08-22' + email: abc@example.com + gender: male + + responses: + '200': + description: OK + + content: + application/json: + + schema: + $ref: '#/components/schemas/MatchResponse' + example: + Match200Example: + value: + phoneNumberMatch: true + idDocumentMatch: true + nameMatch: true + giventNameMatch: null + familyNameMatch: null + nameKanaHankakuMatch: true + nameKanaZenkakuMatch: false + middleNamesMatch: true + familyNameAtBirthMatch: false + addressMatch: true + streetNameMatch: true + streetNumberMatch: true + postalCodeMatch: true + regionMatch: true + localityMatch: null + countryMatch: true + houseNumberExtensionMatch: null + birthdateMatch: false + emailMatch: false + genderMatch: false + + + '201': + description: Null response + default: + description: unexpected error + content: + application/json: + schema: + $ref: "#/components/schemas/Error" + +components: + + schemas: + + MatchRequestBody: + type: object + description: Payload to validate the customer data. + + properties: + phoneNumber: + type: string + pattern: '^[+][0-9]{1,4}?[0-9]{9}$' + description: A public identifier addressing a telephone subscription. In mobile networks it corresponds to the MSISDN (Mobile Station International Subscriber Directory Number). In order to be globally unique it has to be formatted in international format, according to E.164 standard, optionally prefixed with '+'. + + + idDocument: + type: string + description: Id number associated to the id_document. The value may also contain letters. + + name: + type: string + description: Full name of the customer. + + givenName: + type: string + description: First name/given name of the customer. It may include a compound first name or a second/middle name. + + familyName: + type: string + description: Surname/family name of the customer. It may include a compound last name or an additional last name. + + nameKanaHankaku: + type: string + description: Reading of the full name of the customer in full-width Kana format. + + nameKanaZenkaku: + type: string + description: Reading of the full name of the customer in half-width Kana format. + + middleNames: + type: string + description: Middle names of the customer + + familyNameAtBirth: + type: string + description: Family name at birth of the customer + + address: + type: string + description: Address of the customer + + streetName: + type: string + description: Name of the street or other street type. It should not include street type + + streetNumber: + type: integer + minimum: 0 + description: Generally a number identifying a specific property on the `street_name`, but it may be a range of numbers (10-12) or include some letter (10b) + + postalCode: + type: integer + minimum: 0 + description: Zip code or postal code + + region: + type: string + description: Regin/prefecture of the customer's address + + locality: + type: string + description: Locality of the customer's address + + country: + type: string + description: Country of the customer's address + + houseNumberExtension: + type: string + description: House number extension of the customer's address + + birthdate: + type: string + pattern: '^[0-9]{4}-[0-9]{2}-[0-9]{2}$' + description: The birthdate of the customer, in ISO 8601 calendar date format. + + email: + type: string + pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+[.][a-zA-Z]{2,}$' + description: Email address of the customer. + + gender: + type: string + description: Gender of the customer. + + MatchResponse: + type: object + properties: + + phoneNumber: + type: boolean + + idDocument: + type: boolean + + name: + type: boolean + + givenName: + type: boolean + + familyName: + type: boolean + + nameKanaHankaku: + type: boolean + + nameKanaZenkaku: + type: boolean + + middleNames: + type: boolean + + familyNameAtBirth: + type: boolean + + address: + type: boolean + + streetName: + type: boolean + + streetNumber: + type: boolean + + postalCode: + type: boolean + + region: + type: boolean + + locality: + type: boolean + + country: + type: boolean + + houseNumberExtension: + type: boolean + + birthdate: + type: boolean + + email: + type: boolean + + gender: + type: boolean + + Error: + type: object + required: + - code + - message + properties: + code: + type: integer + format: int32 + message: + type: string \ No newline at end of file