diff --git a/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java b/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java index 0b978cdff..e09a4557b 100644 --- a/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java +++ b/core/src/main/java/org/openapitools/openapidiff/core/compare/PathsDiff.java @@ -1,10 +1,13 @@ package org.openapitools.openapidiff.core.compare; +import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.IntStream; +import io.swagger.v3.oas.models.parameters.Parameter; import org.openapitools.openapidiff.core.model.Changed; import org.openapitools.openapidiff.core.model.ChangedPaths; import org.openapitools.openapidiff.core.model.DiffContext; @@ -50,7 +53,7 @@ public DeferredChanged diff( .filter(item -> normalizePath(item.getKey()).equals(template)) .min( (a, b) -> { - if (methodsIntersect(a.getValue(), b.getValue())) { + if (methodsAndParametersIntersect(a.getValue(), b.getValue())) { throw new IllegalArgumentException( "Two path items have the same signature: " + template); } @@ -99,13 +102,40 @@ public static Paths valOrEmpty(Paths path) { return path; } - private static boolean methodsIntersect(PathItem a, PathItem b) { + /** + * + * @param a a path form the open api spec + * @param b another path from the same open api spec + * @return true in case both paths are of the same method AND their templated parameters are of the same type; + * false otherwise + * + */ + private static boolean methodsAndParametersIntersect(PathItem a, PathItem b) { Set methodsA = a.readOperationsMap().keySet(); for (PathItem.HttpMethod method : b.readOperationsMap().keySet()) { if (methodsA.contains(method)) { - return true; + Operation left = a.readOperationsMap().get(method); + Operation right = b.readOperationsMap().get(method); + if (left.getParameters().size() == right.getParameters().size()) { + return parametersIntersect(left.getParameters(), right.getParameters()); + } + return false; } } return false; } + + /** + * + * @param left parameters from the first compared method + * @param right parameters from the second compared method + * @return true in case each parameter pair is of the same type; false otherwise + */ + private static boolean parametersIntersect(List left, List right) {; + int parametersSize = left.size(); + long intersectedParameters = IntStream.range(0, left.size()) + .filter(i -> left.get(i).getSchema().getType().equals(right.get(i).getSchema().getType())) + .count(); + return parametersSize == intersectedParameters; + } } diff --git a/core/src/test/java/org/openapitools/openapidiff/core/ParametersOverloadingTest.java b/core/src/test/java/org/openapitools/openapidiff/core/ParametersOverloadingTest.java new file mode 100644 index 000000000..e3c4bb53a --- /dev/null +++ b/core/src/test/java/org/openapitools/openapidiff/core/ParametersOverloadingTest.java @@ -0,0 +1,26 @@ +package org.openapitools.openapidiff.core; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +import static org.openapitools.openapidiff.core.TestUtils.assertOpenApiAreEquals; + +class ParametersOverloadingTest { + + private final String OVERLOADED_PARAMETERS = "parameters_overloading.yaml"; + private final String DUPLICATED_PARAMETER_TYPES = "parameters_overloading_2.yaml"; + + @Test + void testDiffWithOverloadedParameterTypes() { + assertDoesNotThrow(() -> OpenApiCompare.fromLocations(OVERLOADED_PARAMETERS, OVERLOADED_PARAMETERS)); + assertOpenApiAreEquals(OVERLOADED_PARAMETERS, OVERLOADED_PARAMETERS); + } + + @Test + void testDiffWithDuplicatedParameterTypes() { + assertThrows( + IllegalArgumentException.class, + () -> OpenApiCompare.fromLocations(DUPLICATED_PARAMETER_TYPES, DUPLICATED_PARAMETER_TYPES), + "Two path items have the same signature: /projects/{}"); + } +} \ No newline at end of file diff --git a/core/src/test/resources/parameters_overloading.yaml b/core/src/test/resources/parameters_overloading.yaml new file mode 100644 index 000000000..bd3bee96c --- /dev/null +++ b/core/src/test/resources/parameters_overloading.yaml @@ -0,0 +1,52 @@ +openapi: 3.0.2 +info: + title: Projects API + version: 1.0.0 +paths: + /projects/{id}: + get: + parameters: + - in: path + name: id + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: 'Success' + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' + /projects/{uid}: + get: + parameters: + - in: path + name: uid + required: true + schema: + type: string + format: uuid + responses: + '200': + description: 'Success' + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' +components: + schemas: + SampleResponse: + type: object + properties: + id: + type: integer + uid: + type: string + name: + type: string + required: + - id + - uid + - name \ No newline at end of file diff --git a/core/src/test/resources/parameters_overloading_2.yaml b/core/src/test/resources/parameters_overloading_2.yaml new file mode 100644 index 000000000..d91b7a0f6 --- /dev/null +++ b/core/src/test/resources/parameters_overloading_2.yaml @@ -0,0 +1,52 @@ +openapi: 3.0.2 +info: + title: Projects API + version: 1.0.0 +paths: + /projects/{id}: + get: + parameters: + - in: path + name: id + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: 'Success' + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' + /projects/{uid}: + get: + parameters: + - in: path + name: uid + required: true + schema: + type: integer + format: int64 + responses: + '200': + description: 'Success' + content: + application/json: + schema: + $ref: '#/components/schemas/SampleResponse' +components: + schemas: + SampleResponse: + type: object + properties: + id: + type: integer + uid: + type: string + name: + type: string + required: + - id + - uid + - name \ No newline at end of file