From a6bb18b509491105e4a945fd1ba896e0e1a79d5a Mon Sep 17 00:00:00 2001 From: Ewa Ostrowska Date: Tue, 25 Aug 2026 16:00:06 +0200 Subject: [PATCH 1/2] fix: Fix relative references inside external path items (#1948, #2066) --- .../v3/parser/processors/PathsProcessor.java | 53 ++++++--- .../parser/processors/PathsProcessorTest.java | 112 +++++++++++++++++- .../test/resources/issue-1948/openapi.yaml | 7 ++ .../issue-1948/product/product-api.yaml | 16 +++ .../product/product-components.yaml | 19 +++ .../test/resources/issue-2066/openapi.json | 12 ++ .../resources/issue-2066/sub-dir/params.json | 14 +++ .../sub-dir/sub-dir2/pagination_params_1.json | 7 ++ .../issue-2066/sub-dir/sub-dir3/an-int.json | 4 + 9 files changed, 224 insertions(+), 20 deletions(-) create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-1948/openapi.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-1948/product/product-api.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-1948/product/product-components.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2066/openapi.json create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/params.json create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/sub-dir2/pagination_params_1.json create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/sub-dir3/an-int.json diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/PathsProcessor.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/PathsProcessor.java index 1aacbe7d81..6127bd3101 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/PathsProcessor.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/PathsProcessor.java @@ -18,6 +18,8 @@ import io.swagger.v3.parser.ResolverCache; import io.swagger.v3.parser.models.RefFormat; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -191,7 +193,7 @@ protected void updateRefs(ApiResponse response, String pathRef) { protected void updateRefs(Example example, String pathRef) { if(example.get$ref() != null) { - example.set$ref(computeRef(example.get$ref(), pathRef)); + example.set$ref(computePreprocessedRef(example.get$ref(), pathRef)); } } @@ -239,7 +241,7 @@ protected void updateRefs(RequestBody body, String pathRef) { protected void updateRefs(Schema model, String pathRef) { if(model.get$ref() != null) { - model.set$ref(computeRef(model.get$ref(), pathRef)); + model.set$ref(computePreprocessedRef(model.get$ref(), pathRef)); } else if(model.getProperties() != null) { // process properties @@ -297,42 +299,55 @@ else if(model instanceof ArraySchema) { protected boolean isLocalRef(String ref) { - if(ref.startsWith("#")) { - return true; - } - return false; + return ref.startsWith("#"); } protected boolean isAbsoluteRef(String ref) { - if(!ref.startsWith(".")) { - return true; + try { + URI uri = new URI(ref); + return uri.isAbsolute(); + } catch (URISyntaxException e) { + return true; } - return false; } private boolean isInternalSchemaRef(String $ref) { - if($ref.startsWith("#/components/schemas")) { - return true; - } - return false; + return $ref.startsWith("#/components/schemas"); } protected String computeRef(String ref, String prefix) { if(isLocalRef(ref)&& !isInternalSchemaRef(ref)) return computeLocalRef(ref, prefix); + if (ref.isEmpty()) return ref; if(isAbsoluteRef(ref)) return ref; if(isInternalSchemaRef(ref)) return ref; return computeRelativeRef(ref, prefix); } protected String computeRelativeRef(String ref, String prefix) { - if(ref.startsWith("./")) { + try { + URI resolved = new URI(prefix).resolve(new URI(ref)).normalize(); + String resolvedRef = resolved.toString(); + if (prefix.startsWith("./") && !resolved.isAbsolute() && !resolvedRef.startsWith(".") && !resolvedRef.startsWith("/")) { + return "./" + resolvedRef; + } + return resolvedRef; + } catch (URISyntaxException e) { return ref; } - int iIdxOfSlash = prefix.lastIndexOf('/'); - if(iIdxOfSlash != -1) { - return prefix.substring(0, iIdxOfSlash+1) + ref; - } - return prefix + ref; + } + + private String computePreprocessedRef(String ref, String prefix) { + if (isLocalRef(ref) && !isInternalSchemaRef(ref)) { + return computeLocalRef(ref, prefix); + } + if (!ref.startsWith(".") || ref.startsWith("./") || isInternalSchemaRef(ref)) { + return ref; + } + int lastSlash = prefix.lastIndexOf('/'); + if (lastSlash != -1) { + return prefix.substring(0, lastSlash + 1) + ref; + } + return prefix + ref; } protected String computeLocalRef(String ref, String prefix) { diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/processors/PathsProcessorTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/processors/PathsProcessorTest.java index 000ae5adf0..7603dcbdfe 100644 --- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/processors/PathsProcessorTest.java +++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/processors/PathsProcessorTest.java @@ -4,16 +4,104 @@ import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.PathItem.HttpMethod; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.parser.OpenAPIV3Parser; +import io.swagger.v3.parser.core.models.ParseOptions; +import io.swagger.v3.parser.core.models.SwaggerParseResult; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.util.Map.Entry; import static java.lang.String.format; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertNotNull; public class PathsProcessorTest { + @DataProvider + public Object[][] rebasedReferences() { + return new Object[][]{ + {"./sub-dir/params.json", "./sub-dir2/p.json", "./sub-dir/sub-dir2/p.json"}, + {"./sub-dir/params.json", "sub-dir2/p.json", "./sub-dir/sub-dir2/p.json"}, + {"./sub-dir/params.json", "../parameters/p.json", "./parameters/p.json"}, + {"./sub-dir/params.json", "p.json#/components/parameters/Foo", "./sub-dir/p.json#/components/parameters/Foo"}, + {"paths/users.yaml", "../parameters/page.yaml", "parameters/page.yaml"}, + {"product/product-api.yaml", "product-components.yaml#/components/parameters/id", "product/product-components.yaml#/components/parameters/id"}, + {"./sub-dir/params.json", "https://example.com/p.json", "https://example.com/p.json"}, + {"./sub-dir/params.json", "http://example.com/p.json#/Foo", "http://example.com/p.json#/Foo"}, + {"./sub-dir/params.json", "file:/tmp/p.json#/Foo", "file:/tmp/p.json#/Foo"} + }; + } + + @Test(dataProvider = "rebasedReferences") + public void testComputeRefRebasesAgainstContainingDocument(String base, String ref, String expected) { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + + assertEquals(processor.computeRef(ref, base), expected); + } + + @Test + public void testComputeRefKeepsInvalidChildReference() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + + assertEquals(processor.computeRef("invalid ref.yaml", "./sub-dir/params.json"), "invalid ref.yaml"); + } + + @Test + public void testFragmentOnlyParameterRefUsesExternalPathItemDocument() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + Parameter parameter = new Parameter().$ref("#/components/parameters/Foo"); + + processor.updateRefs(parameter, "product/product-api.yaml"); + + assertEquals(parameter.get$ref(), "product/product-api.yaml#/components/parameters/Foo"); + } + + @Test + public void testInternalSchemaRefKeepsRootDocumentCompatibility() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + Schema schema = new Schema().$ref("#/components/schemas/Foo"); + + processor.updateRefs(schema, "product/product-api.yaml"); + + assertEquals(schema.get$ref(), "#/components/schemas/Foo"); + } + + @Test + public void testIssue1948BareOperationParameterRefIsRelativeToExternalPathItem() { + SwaggerParseResult result = parse("issue-1948/openapi.yaml"); + Parameter parameter = result.getOpenAPI().getPaths().get("/products/{param1}") + .getGet().getParameters().get(0); + + assertParameter(parameter, "param1", "path", "string"); + Schema responseSchema = result.getOpenAPI().getPaths().get("/products/{param1}") + .getGet().getResponses().get("200").getContent().get("application/json").getSchema(); + assertNotNull(result.getOpenAPI().getComponents().getSchemas().get("Product")); + assertEquals(responseSchema.get$ref(), "#/components/schemas/Product"); + assertNoRelativeRefLoadFailure(result); + } + + @Test + public void testIssue2066DotSlashPathParameterRefIsRelativeToExternalPathItem() { + SwaggerParseResult result = parse("issue-2066/openapi.json"); + Parameter parameter = result.getOpenAPI().getPaths().get("/params") + .getGet().getParameters().get(0); + + assertNotNull(parameter); + assertEquals(parameter.getName(), "limit"); + assertEquals(parameter.getIn(), "query"); + assertNotNull(parameter.getSchema()); + assertEquals(parameter.getSchema().get$ref(), "#/components/schemas/an-int"); + Schema resolvedSchema = result.getOpenAPI().getComponents().getSchemas().get("an-int"); + assertNotNull(resolvedSchema); + assertEquals(resolvedSchema.getType(), "integer"); + assertEquals(resolvedSchema.getFormat(), "int32"); + assertNoRelativeRefLoadFailure(result); + } + @Test public void testProcessPaths_parameters_internalTopLevelDefinition() { OpenAPI openAPI = new OpenAPIV3Parser().read("src/test/resources/issue-1733/api.yaml"); @@ -54,4 +142,26 @@ private void assertOperationsHasParameters(OpenAPI openAPI, String path) { assertFalse(operation.getParameters() == null || operation.getParameters().isEmpty(), format("Expected parameters on %s operation for %s but found none", httpMethod, path)); } } -} \ No newline at end of file + + private SwaggerParseResult parse(String location) { + ParseOptions options = new ParseOptions(); + options.setResolve(true); + return new OpenAPIV3Parser().readLocation(location, null, options); + } + + private void assertParameter(Parameter parameter, String name, String in, String schemaType) { + assertNotNull(parameter); + assertEquals(parameter.getName(), name); + assertEquals(parameter.getIn(), in); + Schema schema = parameter.getSchema(); + assertNotNull(schema); + assertEquals(schema.getType(), schemaType); + } + + private void assertNoRelativeRefLoadFailure(SwaggerParseResult result) { + assertNotNull(result.getOpenAPI()); + for (String message : result.getMessages()) { + assertFalse(message.contains("Unable to load RELATIVE ref"), message); + } + } +} diff --git a/modules/swagger-parser-v3/src/test/resources/issue-1948/openapi.yaml b/modules/swagger-parser-v3/src/test/resources/issue-1948/openapi.yaml new file mode 100644 index 0000000000..0f7e9def61 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-1948/openapi.yaml @@ -0,0 +1,7 @@ +openapi: 3.0.0 +info: + title: Issue 1948 + version: 1.0.0 +paths: + /products/{param1}: + $ref: 'product/product-api.yaml#/paths/~1products~1{param1}' diff --git a/modules/swagger-parser-v3/src/test/resources/issue-1948/product/product-api.yaml b/modules/swagger-parser-v3/src/test/resources/issue-1948/product/product-api.yaml new file mode 100644 index 0000000000..0de1d5bc4a --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-1948/product/product-api.yaml @@ -0,0 +1,16 @@ +openapi: 3.0.0 +info: + title: Product API + version: 1.0.0 +paths: + /products/{param1}: + get: + parameters: + - $ref: 'product-components.yaml#/components/parameters/param1' + responses: + '200': + description: Success + content: + application/json: + schema: + $ref: 'product-components.yaml#/components/schemas/Product' diff --git a/modules/swagger-parser-v3/src/test/resources/issue-1948/product/product-components.yaml b/modules/swagger-parser-v3/src/test/resources/issue-1948/product/product-components.yaml new file mode 100644 index 0000000000..5a738a8736 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-1948/product/product-components.yaml @@ -0,0 +1,19 @@ +openapi: 3.0.0 +info: + title: Product components + version: 1.0.0 +paths: {} +components: + schemas: + Product: + type: object + properties: + productCode: + type: string + parameters: + param1: + name: param1 + in: path + required: true + schema: + type: string diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2066/openapi.json b/modules/swagger-parser-v3/src/test/resources/issue-2066/openapi.json new file mode 100644 index 0000000000..591c6237ba --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2066/openapi.json @@ -0,0 +1,12 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Issue 2066", + "version": "1.0.0" + }, + "paths": { + "/params": { + "$ref": "./sub-dir/params.json" + } + } +} diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/params.json b/modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/params.json new file mode 100644 index 0000000000..fd935317b9 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/params.json @@ -0,0 +1,14 @@ +{ + "parameters": [ + { + "$ref": "./sub-dir2/pagination_params_1.json" + } + ], + "get": { + "responses": { + "200": { + "description": "Success" + } + } + } +} diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/sub-dir2/pagination_params_1.json b/modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/sub-dir2/pagination_params_1.json new file mode 100644 index 0000000000..e943f502ab --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/sub-dir2/pagination_params_1.json @@ -0,0 +1,7 @@ +{ + "name": "limit", + "in": "query", + "schema": { + "$ref": "../sub-dir3/an-int.json" + } +} diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/sub-dir3/an-int.json b/modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/sub-dir3/an-int.json new file mode 100644 index 0000000000..703d5aea4e --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2066/sub-dir/sub-dir3/an-int.json @@ -0,0 +1,4 @@ +{ + "type": "integer", + "format": "int32" +} From c07a071096200aa59828144e1037d5808f2a3938 Mon Sep 17 00:00:00 2001 From: Ewa Ostrowska Date: Thu, 27 Aug 2026 08:58:59 +0200 Subject: [PATCH 2/2] fix root-level invalid base filenames are joined incorrectly --- .../processors/ExternalRefProcessor.java | 28 +- .../v3/parser/processors/PathsProcessor.java | 26 +- .../parser/processors/ReferencePathUtils.java | 151 +++++++++ .../parser/processors/PathsProcessorTest.java | 288 +++++++++++++++++- .../nested space/prefix-api.yaml | 12 + .../nested/braces-api.yaml | 12 + .../nested/percent-api.yaml | 12 + .../nested/space-api.yaml | 25 ++ .../issue-2393-regression/openapi.yaml | 15 + .../issue-2393-regression/root path-item.yaml | 12 + .../issue-2393-regression/root-parameter.yaml | 4 + .../safe/prefix-space.yaml | 4 + .../shared examples/example.yaml | 3 + .../shared headers/header.yaml | 3 + .../shared links/link.yaml | 2 + .../shared params/space.yaml | 4 + .../shared schemas/product.yaml | 4 + .../shared%params/percent.yaml | 4 + .../shared{params}/braces.yaml | 4 + 19 files changed, 561 insertions(+), 52 deletions(-) create mode 100644 modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/ReferencePathUtils.java create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested space/prefix-api.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/braces-api.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/percent-api.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/space-api.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/openapi.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/root path-item.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/root-parameter.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/safe/prefix-space.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared examples/example.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared headers/header.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared links/link.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared params/space.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared schemas/product.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared%params/percent.yaml create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared{params}/braces.yaml diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/ExternalRefProcessor.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/ExternalRefProcessor.java index 7d549985a1..89901737af 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/ExternalRefProcessor.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/ExternalRefProcessor.java @@ -1,7 +1,6 @@ package io.swagger.v3.parser.processors; -import java.net.URI; import java.nio.file.Paths; import java.util.Collection; import java.util.Collections; @@ -1007,32 +1006,7 @@ private void processRefLink(Link subRef, String externalFile) { // visible for testing public static String join(String source, String fragment) { - try { - boolean isRelative = false; - if(source.startsWith("/") || source.startsWith(".")) { - isRelative = true; - } - URI uri = new URI(source); - - if(!source.endsWith("/") && (fragment.startsWith("./") && "".equals(uri.getPath()))) { - uri = new URI(source + "/"); - } - else if("".equals(uri.getPath()) && !fragment.startsWith("/")) { - uri = new URI(source + "/"); - } - URI f = new URI(fragment); - - URI resolved = uri.resolve(f); - - URI normalized = resolved.normalize(); - if(Character.isAlphabetic(normalized.toString().charAt(0)) && isRelative) { - return "./" + normalized.toString(); - } - return normalized.toString(); - } - catch(Exception e) { - return source; - } + return ReferencePathUtils.resolve(source, fragment); } diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/PathsProcessor.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/PathsProcessor.java index 6127bd3101..6b5eb270dd 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/PathsProcessor.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/PathsProcessor.java @@ -18,8 +18,6 @@ import io.swagger.v3.parser.ResolverCache; import io.swagger.v3.parser.models.RefFormat; -import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -303,12 +301,7 @@ protected boolean isLocalRef(String ref) { } protected boolean isAbsoluteRef(String ref) { - try { - URI uri = new URI(ref); - return uri.isAbsolute(); - } catch (URISyntaxException e) { - return true; - } + return ReferencePathUtils.isAbsolute(ref); } private boolean isInternalSchemaRef(String $ref) { @@ -324,16 +317,7 @@ protected String computeRef(String ref, String prefix) { } protected String computeRelativeRef(String ref, String prefix) { - try { - URI resolved = new URI(prefix).resolve(new URI(ref)).normalize(); - String resolvedRef = resolved.toString(); - if (prefix.startsWith("./") && !resolved.isAbsolute() && !resolvedRef.startsWith(".") && !resolvedRef.startsWith("/")) { - return "./" + resolvedRef; - } - return resolvedRef; - } catch (URISyntaxException e) { - return ref; - } + return ReferencePathUtils.resolve(prefix, ref); } private String computePreprocessedRef(String ref, String prefix) { @@ -343,11 +327,7 @@ private String computePreprocessedRef(String ref, String prefix) { if (!ref.startsWith(".") || ref.startsWith("./") || isInternalSchemaRef(ref)) { return ref; } - int lastSlash = prefix.lastIndexOf('/'); - if (lastSlash != -1) { - return prefix.substring(0, lastSlash + 1) + ref; - } - return prefix + ref; + return ReferencePathUtils.resolve(prefix, ref); } protected String computeLocalRef(String ref, String prefix) { diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/ReferencePathUtils.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/ReferencePathUtils.java new file mode 100644 index 0000000000..ed5e053da0 --- /dev/null +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/processors/ReferencePathUtils.java @@ -0,0 +1,151 @@ +package io.swagger.v3.parser.processors; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayDeque; +import java.util.Deque; + +final class ReferencePathUtils { + + private ReferencePathUtils() { + } + + static boolean isAbsolute(String ref) { + if (ref == null) { + return false; + } + if (isAbsoluteFilePath(ref)) { + return true; + } + try { + return new URI(ref).isAbsolute(); + } catch (URISyntaxException e) { + return hasScheme(ref); + } + } + + static String resolve(String source, String ref) { + if (source == null || ref == null) { + return source; + } + try { + URI sourceUri = new URI(source); + if (!source.endsWith("/") && ref.startsWith("./") && "".equals(sourceUri.getPath())) { + sourceUri = new URI(source + "/"); + } else if ("".equals(sourceUri.getPath()) && !ref.startsWith("/")) { + sourceUri = new URI(source + "/"); + } + + URI resolved = sourceUri.resolve(new URI(ref)).normalize(); + String resolvedRef = resolved.toString(); + if (source.startsWith("./") && !resolved.isAbsolute() && + !resolvedRef.startsWith(".") && !resolvedRef.startsWith("/")) { + return "./" + resolvedRef; + } + return resolvedRef; + } catch (URISyntaxException | IllegalArgumentException e) { + return resolveRawPath(source, ref); + } + } + + private static String resolveRawPath(String source, String ref) { + if (ref.isEmpty()) { + return stripFragment(source); + } + if (isAbsolute(ref)) { + return ref; + } + if (ref.startsWith("#")) { + return stripFragment(source) + ref; + } + if (ref.startsWith("?")) { + return stripQueryAndFragment(source) + ref; + } + + String sourceFile = stripQueryAndFragment(source); + int lastSeparator = Math.max(sourceFile.lastIndexOf('/'), sourceFile.lastIndexOf('\\')); + if (lastSeparator == -1) { + return normalizeRelativePath(ref); + } + + String resolved = sourceFile.substring(0, lastSeparator + 1) + ref; + if (hasScheme(sourceFile)) { + return resolved; + } + return normalizeRelativePath(resolved); + } + + private static String normalizeRelativePath(String ref) { + int suffixStart = suffixStart(ref); + String path = suffixStart == -1 ? ref : ref.substring(0, suffixStart); + String suffix = suffixStart == -1 ? "" : ref.substring(suffixStart); + boolean leadingDotSlash = path.startsWith("./"); + boolean leadingSlash = path.startsWith("/"); + Deque segments = new ArrayDeque<>(); + + for (String segment : path.split("/")) { + if (segment.isEmpty() || ".".equals(segment)) { + continue; + } + if ("..".equals(segment)) { + if (!segments.isEmpty() && !"..".equals(segments.peekLast())) { + segments.removeLast(); + } else if (!leadingSlash) { + segments.addLast(segment); + } + } else { + segments.addLast(segment); + } + } + + String normalized = String.join("/", segments); + if (leadingSlash) { + normalized = "/" + normalized; + } else if (leadingDotSlash && !normalized.startsWith("..") && !normalized.isEmpty()) { + normalized = "./" + normalized; + } + return normalized + suffix; + } + + private static boolean isAbsoluteFilePath(String ref) { + return ref.startsWith("/") || ref.startsWith("\\") || + (ref.length() >= 3 && Character.isLetter(ref.charAt(0)) && ref.charAt(1) == ':' && + (ref.charAt(2) == '/' || ref.charAt(2) == '\\')); + } + + private static boolean hasScheme(String ref) { + int colon = ref.indexOf(':'); + if (colon <= 0 || !Character.isLetter(ref.charAt(0))) { + return false; + } + for (int i = 1; i < colon; i++) { + char character = ref.charAt(i); + if (!Character.isLetterOrDigit(character) && character != '+' && character != '-' && character != '.') { + return false; + } + } + return true; + } + + private static String stripFragment(String value) { + int fragment = value.indexOf('#'); + return fragment == -1 ? value : value.substring(0, fragment); + } + + private static String stripQueryAndFragment(String value) { + int suffixStart = suffixStart(value); + return suffixStart == -1 ? value : value.substring(0, suffixStart); + } + + private static int suffixStart(String value) { + int query = value.indexOf('?'); + int fragment = value.indexOf('#'); + if (query == -1) { + return fragment; + } + if (fragment == -1) { + return query; + } + return Math.min(query, fragment); + } +} diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/processors/PathsProcessorTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/processors/PathsProcessorTest.java index 7603dcbdfe..4c0b79b16f 100644 --- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/processors/PathsProcessorTest.java +++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/processors/PathsProcessorTest.java @@ -4,6 +4,8 @@ import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.PathItem.HttpMethod; +import io.swagger.v3.oas.models.examples.Example; +import io.swagger.v3.oas.models.media.ComposedSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.parser.OpenAPIV3Parser; @@ -18,6 +20,7 @@ import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertNull; public class PathsProcessorTest { @@ -36,6 +39,35 @@ public Object[][] rebasedReferences() { }; } + @DataProvider + public Object[][] uriIllegalReferences() { + return new Object[][]{ + {"\\"}, + {"%"}, + {"["}, + {"]"}, + {"{"}, + {"}"}, + {"^"}, + {"`"}, + {"|"}, + {"<"}, + {">"}, + {"\""}, + {" "} + }; + } + + @DataProvider + public Object[][] absoluteReferencesWithUriIllegalCharacters() { + return new Object[][]{ + {"/shared params/p.yaml"}, + {"C:\\shared params\\p.yaml"}, + {"\\\\server\\shared params\\p.yaml"}, + {"https://example .com/p.yaml"} + }; + } + @Test(dataProvider = "rebasedReferences") public void testComputeRefRebasesAgainstContainingDocument(String base, String ref, String expected) { PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); @@ -44,10 +76,63 @@ public void testComputeRefRebasesAgainstContainingDocument(String base, String r } @Test - public void testComputeRefKeepsInvalidChildReference() { + public void testComputeRefRebasesInvalidChildReference() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + + assertEquals(processor.computeRef("invalid ref.yaml", "./sub-dir/params.json"), "./sub-dir/invalid ref.yaml"); + } + + @Test(dataProvider = "uriIllegalReferences") + public void testComputeRefRebasesUriIllegalCharacters(String character) { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + String ref = "../shared" + character + "params/p.yaml#/components/parameters/Foo"; + + assertEquals(processor.computeRef(ref, "./sub-dir/params.json"), + "./shared" + character + "params/p.yaml#/components/parameters/Foo"); + } + + @Test + public void testComputeRefFallsBackWhenContainingDocumentIsNotAValidUri() { PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); - assertEquals(processor.computeRef("invalid ref.yaml", "./sub-dir/params.json"), "invalid ref.yaml"); + assertEquals(processor.computeRef("../parameters/p.yaml", "./path items/users.yaml"), + "./parameters/p.yaml"); + } + + @Test + public void testComputeRefFallsBackWhenContainingDocumentHasNoDirectory() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + + assertEquals(processor.computeRef("parameters.yaml", "path item.yaml"), "parameters.yaml"); + } + + @Test(dataProvider = "absoluteReferencesWithUriIllegalCharacters") + public void testComputeRefPreservesAbsoluteReferencesWithUriIllegalCharacters(String ref) { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + + assertEquals(processor.computeRef(ref, "./sub-dir/params.json"), ref); + } + + @Test + public void testExternalRefJoinFallsBackForUriIllegalChildReference() { + assertEquals(ExternalRefProcessor.join("nested/api.yaml#/paths/~1products", + "../shared schemas/product.yaml#/components/schemas/Product"), + "shared schemas/product.yaml#/components/schemas/Product"); + } + + @Test + public void testExternalRefJoinPreservesQueryOnlyAndFragmentOnlyReferences() { + assertEquals(ExternalRefProcessor.join("path items/api.yaml?version=1#/paths/~1products", "?version=2"), + "path items/api.yaml?version=2"); + assertEquals(ExternalRefProcessor.join("path items/api.yaml?version=1#/paths/~1products", + "#/components/schemas/Product"), + "path items/api.yaml?version=1#/components/schemas/Product"); + } + + @Test + public void testExternalRefJoinPreservesNullCompatibility() { + assertEquals(ExternalRefProcessor.join("source.yaml", null), "source.yaml"); + assertNull(ExternalRefProcessor.join(null, "child.yaml")); } @Test @@ -102,6 +187,194 @@ public void testIssue2066DotSlashPathParameterRefIsRelativeToExternalPathItem() assertNoRelativeRefLoadFailure(result); } + @Test + public void testUriIllegalReferencesAreRebasedEndToEnd() { + SwaggerParseResult result = parse("issue-2393-regression/openapi.yaml"); + + assertParameter(parameter(result, "/space"), "space", "query", "string"); + assertParameter(parameter(result, "/braces"), "braces", "query", "string"); + assertParameter(parameter(result, "/percent"), "percent", "query", "string"); + assertParameter(parameter(result, "/prefix-space"), "prefix-space", "query", "string"); + assertParameter(parameter(result, "/root-prefix"), "root-prefix", "query", "string"); + assertNotNull(result.getOpenAPI().getComponents().getSchemas().get("product")); + assertNotNull(result.getOpenAPI().getComponents().getExamples().get("example")); + assertNotNull(result.getOpenAPI().getComponents().getHeaders().get("header")); + assertNotNull(result.getOpenAPI().getComponents().getLinks().get("link")); + assertNoRelativeRefLoadFailure(result); + } + + @Test + public void testResolveFalseDoesNotResolveExternalPathItemWithUriIllegalNestedRef() { + ParseOptions options = new ParseOptions(); + options.setResolve(false); + SwaggerParseResult result = new OpenAPIV3Parser() + .readLocation("issue-2393-regression/openapi.yaml", null, options); + + assertNotNull(result.getOpenAPI()); + PathItem pathItem = result.getOpenAPI().getPaths().get("/space"); + assertNotNull(pathItem.get$ref()); + assertNull(pathItem.getGet()); + assertNoRelativeRefLoadFailure(result); + } + + @Test + public void testIssue1948ResolveFullyInlinesExternalPathItemParameter() { + SwaggerParseResult result = parseFully("issue-1948/openapi.yaml"); + Parameter parameter = result.getOpenAPI().getPaths().get("/products/{param1}") + .getGet().getParameters().get(0); + + assertParameter(parameter, "param1", "path", "string"); + Schema responseSchema = result.getOpenAPI().getPaths().get("/products/{param1}") + .getGet().getResponses().get("200").getContent().get("application/json").getSchema(); + assertNotNull(responseSchema); + assertEquals(responseSchema.getType(), "object"); + assertNoRelativeRefLoadFailure(result); + } + + @Test + public void testIssue2066ResolveFullyInlinesNestedExternalPathItemParameter() { + SwaggerParseResult result = parseFully("issue-2066/openapi.json"); + Parameter parameter = result.getOpenAPI().getPaths().get("/params") + .getGet().getParameters().get(0); + + assertNotNull(parameter); + assertEquals(parameter.getName(), "limit"); + assertEquals(parameter.getIn(), "query"); + Schema schema = parameter.getSchema(); + assertNotNull(schema); + assertEquals(schema.getType(), "integer"); + assertEquals(schema.getFormat(), "int32"); + assertNoRelativeRefLoadFailure(result); + } + + @DataProvider + public Object[][] bareRelativeRefsRebasedByComputeRef() { + return new Object[][]{ + {"schemas/product.yaml", "api/path-item.yaml", "api/schemas/product.yaml"}, + {"examples/product.yaml", "api/path-item.yaml", "api/examples/product.yaml"}, + {"product-components.yaml", "product/product-api.yaml", "product/product-components.yaml"}, + }; + } + + @Test(dataProvider = "bareRelativeRefsRebasedByComputeRef") + public void testComputeRefRebasesBareRelativeRef(String ref, String base, String expected) { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + + assertEquals(processor.computeRef(ref, base), expected); + } + + @Test + public void testUpdateRefsSchemaLeavesExternalPathItemBareRelativeRefUnchanged() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + Schema schema = new Schema().$ref("schemas/product.yaml"); + + processor.updateRefs(schema, "api/path-item.yaml"); + + assertEquals(schema.get$ref(), "schemas/product.yaml"); + } + + @Test + public void testUpdateRefsExampleLeavesExternalPathItemBareRelativeRefUnchanged() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + Example example = new Example().$ref("examples/product.yaml"); + + processor.updateRefs(example, "api/path-item.yaml"); + + assertEquals(example.get$ref(), "examples/product.yaml"); + } + + @Test + public void testUpdateRefsSchemaRebasesDotDotRelativeRef() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + Schema schema = new Schema().$ref("../shared schemas/product.yaml"); + + processor.updateRefs(schema, "nested/space-api.yaml"); + + assertEquals(schema.get$ref(), "shared schemas/product.yaml"); + } + + @Test + public void testUpdateRefsExampleRebasesDotDotRelativeRef() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + Example example = new Example().$ref("../shared examples/example.yaml"); + + processor.updateRefs(example, "nested/space-api.yaml"); + + assertEquals(example.get$ref(), "shared examples/example.yaml"); + } + + @Test + public void testUpdateRefsComposedSchemaAllOfEntriesRebaseDotDotRef() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + ComposedSchema composed = new ComposedSchema(); + Schema allOfEntry = new Schema().$ref("../shared schemas/product.yaml"); + composed.addAllOfItem(allOfEntry); + + processor.updateRefs(composed, "nested/space-api.yaml"); + + assertEquals(allOfEntry.get$ref(), "shared schemas/product.yaml"); + } + + @Test + public void testUpdateRefsComposedSchemaAnyOfEntriesRebaseDotDotRef() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + ComposedSchema composed = new ComposedSchema(); + Schema anyOfEntry = new Schema().$ref("../shared schemas/product.yaml"); + composed.addAnyOfItem(anyOfEntry); + + processor.updateRefs(composed, "nested/space-api.yaml"); + + assertEquals(anyOfEntry.get$ref(), "shared schemas/product.yaml"); + } + + @Test + public void testComputeRefRebasesRelativeWindowsPathWithoutNormalizingSeparators() { + PathsProcessor processor = new PathsProcessor(null, new OpenAPI()); + + assertEquals(processor.computeRef("schemas\\product.yaml", "api/path-item.yaml"), + "api/schemas\\product.yaml"); + } + + + @Test + public void testJoinWithMalformedRemoteBasePreservesDotSegmentsUnresolved() { + String result = ExternalRefProcessor.join( + "https://example.com/{version}/api.yaml", + "../schemas/product.yaml"); + + assertEquals(result, "https://example.com/{version}/../schemas/product.yaml"); + } + + + @Test + public void testBareRelativeSchemaRefInExternalPathItemResponseIsResolved() { + SwaggerParseResult result = parse("issue-1948/openapi.yaml"); + + Schema responseSchema = result.getOpenAPI().getPaths().get("/products/{param1}") + .getGet().getResponses().get("200").getContent().get("application/json").getSchema(); + assertNotNull(responseSchema); + assertEquals(responseSchema.get$ref(), "#/components/schemas/Product"); + Schema productSchema = result.getOpenAPI().getComponents().getSchemas().get("Product"); + assertNotNull(productSchema); + assertEquals(productSchema.getType(), "object"); + assertNotNull(productSchema.getProperties().get("productCode")); + assertNoRelativeRefLoadFailure(result); + } + + @Test + public void testDotDotRelativeSchemaAndExampleRefsInExternalPathItemAreResolved() { + SwaggerParseResult result = parse("issue-2393-regression/openapi.yaml"); + + Schema productSchema = result.getOpenAPI().getComponents().getSchemas().get("product"); + assertNotNull(productSchema); + assertEquals(productSchema.getType(), "object"); + assertNotNull(productSchema.getProperties().get("id")); + Example example = result.getOpenAPI().getComponents().getExamples().get("example"); + assertNotNull(example); + assertEquals(example.getSummary(), "Product example"); + assertNoRelativeRefLoadFailure(result); + } + @Test public void testProcessPaths_parameters_internalTopLevelDefinition() { OpenAPI openAPI = new OpenAPIV3Parser().read("src/test/resources/issue-1733/api.yaml"); @@ -149,6 +422,17 @@ private SwaggerParseResult parse(String location) { return new OpenAPIV3Parser().readLocation(location, null, options); } + private SwaggerParseResult parseFully(String location) { + ParseOptions options = new ParseOptions(); + options.setResolve(true); + options.setResolveFully(true); + return new OpenAPIV3Parser().readLocation(location, null, options); + } + + private Parameter parameter(SwaggerParseResult result, String path) { + return result.getOpenAPI().getPaths().get(path).getGet().getParameters().get(0); + } + private void assertParameter(Parameter parameter, String name, String in, String schemaType) { assertNotNull(parameter); assertEquals(parameter.getName(), name); diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested space/prefix-api.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested space/prefix-api.yaml new file mode 100644 index 0000000000..3377afe2b4 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested space/prefix-api.yaml @@ -0,0 +1,12 @@ +openapi: 3.0.3 +info: + title: Prefix space path item + version: 1.0.0 +paths: + /prefix-space: + get: + parameters: + - $ref: '../safe/prefix-space.yaml' + responses: + '200': + description: Success diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/braces-api.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/braces-api.yaml new file mode 100644 index 0000000000..41642637b9 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/braces-api.yaml @@ -0,0 +1,12 @@ +openapi: 3.0.3 +info: + title: Braces path item + version: 1.0.0 +paths: + /braces: + get: + parameters: + - $ref: '../shared{params}/braces.yaml' + responses: + '200': + description: Success diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/percent-api.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/percent-api.yaml new file mode 100644 index 0000000000..71c7a817fd --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/percent-api.yaml @@ -0,0 +1,12 @@ +openapi: 3.0.3 +info: + title: Percent path item + version: 1.0.0 +paths: + /percent: + get: + parameters: + - $ref: '../shared%params/percent.yaml' + responses: + '200': + description: Success diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/space-api.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/space-api.yaml new file mode 100644 index 0000000000..7cadb21bee --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/nested/space-api.yaml @@ -0,0 +1,25 @@ +openapi: 3.0.3 +info: + title: Space path item + version: 1.0.0 +paths: + /space: + get: + parameters: + - $ref: '../shared params/space.yaml' + responses: + '200': + description: Success + headers: + X-Test: + $ref: '../shared headers/header.yaml' + links: + next: + $ref: '../shared links/link.yaml' + content: + application/json: + schema: + $ref: '../shared schemas/product.yaml' + examples: + product: + $ref: '../shared examples/example.yaml' diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/openapi.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/openapi.yaml new file mode 100644 index 0000000000..da153fc363 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/openapi.yaml @@ -0,0 +1,15 @@ +openapi: 3.0.3 +info: + title: Issue 2393 URI character regression + version: 1.0.0 +paths: + /space: + $ref: 'nested/space-api.yaml#/paths/~1space' + /braces: + $ref: 'nested/braces-api.yaml#/paths/~1braces' + /percent: + $ref: 'nested/percent-api.yaml#/paths/~1percent' + /prefix-space: + $ref: 'nested space/prefix-api.yaml#/paths/~1prefix-space' + /root-prefix: + $ref: 'root path-item.yaml#/paths/~1root-prefix' diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/root path-item.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/root path-item.yaml new file mode 100644 index 0000000000..a745c40b03 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/root path-item.yaml @@ -0,0 +1,12 @@ +openapi: 3.0.3 +info: + title: Root-level path item with a space + version: 1.0.0 +paths: + /root-prefix: + get: + parameters: + - $ref: 'root-parameter.yaml' + responses: + '200': + description: Success diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/root-parameter.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/root-parameter.yaml new file mode 100644 index 0000000000..43625e366f --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/root-parameter.yaml @@ -0,0 +1,4 @@ +name: root-prefix +in: query +schema: + type: string diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/safe/prefix-space.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/safe/prefix-space.yaml new file mode 100644 index 0000000000..e4c2050291 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/safe/prefix-space.yaml @@ -0,0 +1,4 @@ +name: prefix-space +in: query +schema: + type: string diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared examples/example.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared examples/example.yaml new file mode 100644 index 0000000000..edc9070cde --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared examples/example.yaml @@ -0,0 +1,3 @@ +summary: Product example +value: + id: product-1 diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared headers/header.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared headers/header.yaml new file mode 100644 index 0000000000..24955eb22f --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared headers/header.yaml @@ -0,0 +1,3 @@ +description: Test header +schema: + type: string diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared links/link.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared links/link.yaml new file mode 100644 index 0000000000..49cd31aa04 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared links/link.yaml @@ -0,0 +1,2 @@ +operationId: getSpace +description: Resolved link from a path containing a space diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared params/space.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared params/space.yaml new file mode 100644 index 0000000000..bd3b9ba9cb --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared params/space.yaml @@ -0,0 +1,4 @@ +name: space +in: query +schema: + type: string diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared schemas/product.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared schemas/product.yaml new file mode 100644 index 0000000000..11e2b4b86a --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared schemas/product.yaml @@ -0,0 +1,4 @@ +type: object +properties: + id: + type: string diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared%params/percent.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared%params/percent.yaml new file mode 100644 index 0000000000..f9d79bc579 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared%params/percent.yaml @@ -0,0 +1,4 @@ +name: percent +in: query +schema: + type: string diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared{params}/braces.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared{params}/braces.yaml new file mode 100644 index 0000000000..997f5439c1 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2393-regression/shared{params}/braces.yaml @@ -0,0 +1,4 @@ +name: braces +in: query +schema: + type: string