Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public class ResolverCache {
private Map<String, String> externalFileCache = new HashMap<>();
private Map<String, Object> canonicalResolutionCache = new HashMap<>();
private Map<String, String> canonicalExternalFileCache = new HashMap<>();
private Map<String, String> rootReferenceNameCache = new HashMap<>();
private List<String> referencedModelKeys = new ArrayList<>();
private Set<String> resolveValidationMessages;
private final ParseOptions parseOptions;
Expand Down Expand Up @@ -266,6 +267,8 @@ private <T> T resolveRootReference(String file, String definitionPath, String re
T result = expectedType.cast(rootTarget);
resolutionCache.put(ref, result);
canonicalResolutionCache.putIfAbsent(canonicalRef, result);
String definitionName = definitionPath.substring(definitionPath.lastIndexOf('/') + 1);
rootReferenceNameCache.putIfAbsent(canonicalRef, unescapePointer(definitionName));
return result;
}

Expand Down Expand Up @@ -508,6 +511,17 @@ public String getRenamedRef(String originalRef) {
return canonicalRenameCache.get(canonicalize(originalRef));
}

/**
* Returns the declared name of a reference resolved from the root document snapshot.
*
* @param ref reference to look up
* @return the declared root reference name, or {@code null} if the reference was not
* resolved from the root document snapshot
*/
public String getRootReferenceName(String ref) {
return rootReferenceNameCache.get(canonicalize(ref));
}

public void putRenamedRef(String originalRef, String newRef) {
renameCache.put(originalRef, newRef);
canonicalRenameCache.put(canonicalize(originalRef), newRef);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ private void warnUnableToLoadReference(String ref) {
LOGGER.warn("unable to load model reference from `{}`. It may not be available or the reference isn't a valid model schema", ref);
}

private String getRootReferenceName(String ref) {
String rootReferenceName = cache.getRootReferenceName(ref);
if (rootReferenceName != null) {
cache.putRenamedRef(ref, rootReferenceName);
}
return rootReferenceName;
}

private String allocateSchemaName(Map<String, Schema> schemas, String baseName,
Schema incoming, String incomingRef) {
return nameAllocator.allocate(schemas, baseName, incoming, incomingRef,
Expand All @@ -82,6 +90,11 @@ public String processRefToExternalSchema(String $ref, RefFormat refFormat) {
warnUnableToLoadReference($ref);
return $ref;
}
String rootReferenceName = getRootReferenceName($ref);
if (rootReferenceName != null) {
cache.addReferencedKey(rootReferenceName);
return rootReferenceName;
}
String newRef;

if (openAPI.getComponents() == null) {
Expand Down Expand Up @@ -403,6 +416,10 @@ public String processRefToExternalResponse(String $ref, RefFormat refFormat) {
warnUnableToLoadReference($ref);
return $ref;
}
String rootReferenceName = getRootReferenceName($ref);
if (rootReferenceName != null) {
return rootReferenceName;
}

String newRef;

Expand Down Expand Up @@ -479,6 +496,10 @@ public String processRefToExternalRequestBody(String $ref, RefFormat refFormat)
warnUnableToLoadReference($ref);
return $ref;
}
String rootReferenceName = getRootReferenceName($ref);
if (rootReferenceName != null) {
return rootReferenceName;
}
String newRef;

if (openAPI.getComponents() == null) {
Expand Down Expand Up @@ -528,6 +549,10 @@ public String processRefToExternalHeader(String $ref, RefFormat refFormat) {
warnUnableToLoadReference($ref);
return $ref;
}
String rootReferenceName = getRootReferenceName($ref);
if (rootReferenceName != null) {
return rootReferenceName;
}
String newRef;

if (openAPI.getComponents() == null) {
Expand Down Expand Up @@ -584,6 +609,10 @@ public String processRefToExternalSecurityScheme(String $ref, RefFormat refForma
warnUnableToLoadReference($ref);
return $ref;
}
String rootReferenceName = getRootReferenceName($ref);
if (rootReferenceName != null) {
return rootReferenceName;
}
String newRef;

if (openAPI.getComponents() == null) {
Expand Down Expand Up @@ -632,6 +661,10 @@ public String processRefToExternalLink(String $ref, RefFormat refFormat) {
warnUnableToLoadReference($ref);
return $ref;
}
String rootReferenceName = getRootReferenceName($ref);
if (rootReferenceName != null) {
return rootReferenceName;
}
String newRef;

if (openAPI.getComponents() == null) {
Expand Down Expand Up @@ -679,6 +712,10 @@ public String processRefToExternalExample(String $ref, RefFormat refFormat) {
warnUnableToLoadReference($ref);
return $ref;
}
String rootReferenceName = getRootReferenceName($ref);
if (rootReferenceName != null) {
return rootReferenceName;
}
String newRef;

if (openAPI.getComponents() == null) {
Expand Down Expand Up @@ -725,6 +762,10 @@ public String processRefToExternalParameter(String $ref, RefFormat refFormat) {
warnUnableToLoadReference($ref);
return $ref;
}
String rootReferenceName = getRootReferenceName($ref);
if (rootReferenceName != null) {
return rootReferenceName;
}
String newRef;

if (openAPI.getComponents() == null) {
Expand Down Expand Up @@ -799,6 +840,10 @@ public String processRefToExternalCallback(String $ref, RefFormat refFormat) {
warnUnableToLoadReference($ref);
return $ref;
}
String rootReferenceName = getRootReferenceName($ref);
if (rootReferenceName != null) {
return rootReferenceName;
}
String newRef;

if (openAPI.getComponents() == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,32 @@ public void testResponseAllocatorExaminesOccupiedSuffixesWithoutOverwritingPlace
assertSame(testedOpenAPI.getComponents().getResponses().get("sharedResponse_3"), incoming);
}

@Test
public void testResponseReusesRootComponentNameDiscoveredDuringLoad() {
final String ref = "https://example.test/api.yaml#/components/responses/Order-response";
ApiResponse rootSnapshotResponse = new ApiResponse().$ref("./responses/orderResponse.json");
ApiResponse currentResponse = new ApiResponse().description("resolved response");
OpenAPI testedOpenAPI = new OpenAPI().components(
new Components().addResponses("Order-response", currentResponse));

new Expectations() {{
cache.getRenamedRef(ref);
cache.loadRef(ref, RefFormat.URL, ApiResponse.class);
result = rootSnapshotResponse;
cache.getRootReferenceName(ref);
result = "Order-response";
cache.putRenamedRef(ref, "Order-response");
}};

String assignedName = new ExternalRefProcessor(cache, testedOpenAPI)
.processRefToExternalResponse(ref, RefFormat.URL);

assertEquals(assignedName, "Order-response");
assertEquals(testedOpenAPI.getComponents().getResponses().size(), 1);
assertSame(testedOpenAPI.getComponents().getResponses().get("Order-response"), currentResponse);
assertEquals(rootSnapshotResponse.get$ref(), "./responses/orderResponse.json");
}

@Test
public void testEqualResolvedResponsesReuseOneKey() {
final String ref = "https://example.test/incoming.yaml#/sharedResponse";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package io.swagger.v3.parser.test;

import io.swagger.v3.oas.models.media.Schema;
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.Test;

import java.util.Arrays;
import java.util.Map;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

public class Issue2399Test {

@Test
public void resolvingRootDocumentAliasRetainsReferencedSchema() {
ParseOptions options = new ParseOptions();
options.setResolve(true);

SwaggerParseResult result = new OpenAPIV3Parser().readLocation(
"src/test/resources/issue-2399/root-alias.json", null, options);

assertNotNull(result.getOpenAPI());
assertTrue(result.getMessages().isEmpty(), "Unexpected parser messages: " + result.getMessages());

Map<String, Schema> schemas = result.getOpenAPI().getComponents().getSchemas();
assertEquals(schemas.size(), 2);
assertEquals(schemas.get("Target").getType(), "string");
assertEquals(schemas.get("Alias").getType(), "string");
}

@Test
public void resolveFullyPreservesDeclaredComponentKeyForRootDocumentBackReference() {
ParseOptions options = new ParseOptions();
options.setResolveResponses(true);
options.setValidateExternalRefs(true);
options.setResolveFully(true);

SwaggerParseResult result = new OpenAPIV3Parser().readLocation(
"src/test/resources/issue-2399/api.json", null, options);

assertNotNull(result.getOpenAPI());
assertTrue(result.getMessages().isEmpty(), "Unexpected parser messages: " + result.getMessages());

Map<String, Schema> schemas = result.getOpenAPI().getComponents().getSchemas();
assertEquals(schemas.size(), 4);
assertTrue(schemas.keySet().containsAll(Arrays.asList(
"Order-item", "Order-status", "orderItem", "orderStatus")));
assertFalse(schemas.containsKey("Order-status_1"));

Schema<?> responseSchema = result.getOpenAPI().getPaths().get("/orders").getGet()
.getResponses().get("200").getContent().get("application/json").getSchema();
Schema<?> orderSchema = responseSchema.getItems();
Schema<?> statusSchema = (Schema<?>) orderSchema.getProperties().get("status");
assertEquals(statusSchema.getEnum(), Arrays.asList("PENDING", "SHIPPED"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ public void testRootReferenceUsesObjectPresentAtCacheConstruction() {
root + "#/components/schemas/Foo", RefFormat.URL, Schema.class);

assertSame(result, originalRootSchema);
assertEquals(cache.getRootReferenceName(root + "#/components/schemas/Foo"), "Foo");
}

@Test
Expand Down Expand Up @@ -430,17 +431,20 @@ public void testUnsupportedAbsentAndWrongTypePointersUseExternalDocument() {
}};

ResolverCache cache = new ResolverCache(openAPI, auths, root);
Schema absent = cache.loadRef(
root + "#/components/schemas/External", RefFormat.URL, Schema.class);
Schema deeper = cache.loadRef(
root + "#/components/schemas/Foo/properties/value", RefFormat.URL, Schema.class);
Parameter wrongType = cache.loadRef(
root + "#/components/schemas/Foo", RefFormat.URL, Parameter.class);
String absentRef = root + "#/components/schemas/External";
String deeperRef = root + "#/components/schemas/Foo/properties/value";
String wrongTypeRef = root + "#/components/schemas/Foo";
Schema absent = cache.loadRef(absentRef, RefFormat.URL, Schema.class);
Schema deeper = cache.loadRef(deeperRef, RefFormat.URL, Schema.class);
Parameter wrongType = cache.loadRef(wrongTypeRef, RefFormat.URL, Parameter.class);

assertEquals(absent.getType(), "string");
assertEquals(deeper.getType(), "integer");
assertNotNull(wrongType);
assertEquals(wrongType.getName(), "id");
assertNull(cache.getRootReferenceName(absentRef));
assertNull(cache.getRootReferenceName(deeperRef));
assertNull(cache.getRootReferenceName(wrongTypeRef));
}

@Test
Expand Down
38 changes: 38 additions & 0 deletions modules/swagger-parser-v3/src/test/resources/issue-2399/api.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"openapi": "3.0.3",
"info": {
"title": "Order API",
"version": "1.0.0"
},
"paths": {
"/orders": {
"get": {
"responses": {
"200": {
"description": "ok",
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Order-item"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"Order-item": {
"$ref": "./models/orderItem.json"
},
"Order-status": {
"$ref": "./models/common/orderStatus.json"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type": "string",
"enum": ["PENDING", "SHIPPED"]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"type": "object",
"required": ["id", "status"],
"properties": {
"id": {
"type": "string"
},
"status": {
"$ref": "../api.json#/components/schemas/Order-status"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"openapi": "3.0.3",
"info": {
"title": "Root alias API",
"version": "1.0.0"
},
"paths": {},
"components": {
"schemas": {
"Target": {
"type": "string"
},
"Alias": {
"$ref": "./root-alias.json#/components/schemas/Target"
}
}
}
}