From 550245b7b60fdee765ea1ffe3cbc8fbdc6fc6029 Mon Sep 17 00:00:00 2001 From: seonwoo_jung <79202163+seonwooj0810@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:21:16 +0900 Subject: [PATCH] fix: resolve $ref inside allOf of unreferenced component schemas ResolverFully.resolveFully() only walked the paths object, so component schemas that are not reachable through any path were never resolved. A $ref nested inside the allOf of such a component schema was left unresolved even with resolveFully(true). Resolve the captured component schemas after walking the paths. resolveSchema is cached, so schemas already resolved via a path are returned as-is and never resolved twice. --- .../swagger/v3/parser/util/ResolverFully.java | 13 ++++++ .../swagger/v3/parser/test/Issue2270Test.java | 42 +++++++++++++++++++ .../test/resources/issue-2270/openapi.yaml | 19 +++++++++ 3 files changed, 74 insertions(+) create mode 100644 modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/Issue2270Test.java create mode 100644 modules/swagger-parser-v3/src/test/resources/issue-2270/openapi.yaml diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ResolverFully.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ResolverFully.java index 755e8b6d32..687878c5f6 100644 --- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ResolverFully.java +++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/ResolverFully.java @@ -133,6 +133,19 @@ public void resolveFully(OpenAPI openAPI) { resolvePath(pathItem); } } + + // Resolve component schemas that are not reachable through any path, so that + // e.g. a $ref inside an allOf of an otherwise-unreferenced component schema is + // still fully resolved. resolveSchema caches its results, so component schemas + // already resolved while walking the paths are returned as-is (no double resolution). + if (schemas != null) { + for (String schemaName : new ArrayList<>(schemas.keySet())) { + Schema resolved = resolveSchema(schemas.get(schemaName)); + if (resolved != null) { + schemas.put(schemaName, resolved); + } + } + } } public void resolvePath(PathItem pathItem){ diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/Issue2270Test.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/Issue2270Test.java new file mode 100644 index 0000000000..ad45bff9b2 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/Issue2270Test.java @@ -0,0 +1,42 @@ +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.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class Issue2270Test { + + @Test + public void shouldResolveRefInsideAllOfOfUnreferencedComponentSchema() { + ParseOptions options = new ParseOptions(); + options.setResolveFully(true); + // Keep the allOf structure so we can assert the inner $ref itself was resolved. + options.setResolveCombinators(false); + + SwaggerParseResult result = new OpenAPIV3Parser().readLocation("issue-2270/openapi.yaml", null, options); + + assertNotNull(result.getOpenAPI()); + Schema contact = result.getOpenAPI().getComponents().getSchemas().get("Contact"); + assertNotNull(contact); + + List allOf = contact.getAllOf(); + assertNotNull(allOf); + + // The first allOf member is a $ref to SObject and, although Contact is not reachable + // through any path, resolveFully(true) must still resolve it: the $ref must be gone + // and the referenced schema's properties must be inlined. + Schema firstMember = allOf.get(0); + assertNull("$ref inside allOf of an unreferenced component schema was not resolved", + firstMember.get$ref()); + assertNotNull(firstMember.getProperties()); + assertTrue(firstMember.getProperties().containsKey("id")); + } +} diff --git a/modules/swagger-parser-v3/src/test/resources/issue-2270/openapi.yaml b/modules/swagger-parser-v3/src/test/resources/issue-2270/openapi.yaml new file mode 100644 index 0000000000..cc797090d6 --- /dev/null +++ b/modules/swagger-parser-v3/src/test/resources/issue-2270/openapi.yaml @@ -0,0 +1,19 @@ +openapi: 3.0.1 +info: + title: issue-2270 + version: 1.0.0 +paths: {} +components: + schemas: + SObject: + type: object + properties: + id: + type: string + Contact: + allOf: + - $ref: '#/components/schemas/SObject' + - type: object + properties: + name: + type: string