From 223781290e7aa3ae9db203987d9d764232e191ad Mon Sep 17 00:00:00 2001 From: mullinaa Date: Sun, 7 Jun 2026 15:54:32 +0300 Subject: [PATCH 1/2] Add logic and test case to avoid stackOverflow exception for circular allOf --- .../codegen/examples/ExampleGenerator.java | 10 +++++- .../codegen/ExampleGeneratorTest.java | 32 ++++++++++++++++++- .../resources/3_0/example_generator_test.yaml | 19 +++++++++++ 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/examples/ExampleGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/examples/ExampleGenerator.java index 8b6d0a630eac..7a5dde85b0ca 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/examples/ExampleGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/examples/ExampleGenerator.java @@ -407,13 +407,21 @@ private void traverseSchemaProperties(String mediaType, Schema schema, Set processedModels, Map values) { List interfaces = schema.getAllOf(); for (Schema composed : interfaces) { - traverseSchemaProperties(mediaType, composed, processedModels, values); if (composed.get$ref() != null) { String ref = ModelUtils.getSimpleRef(composed.get$ref()); + + if (processedModels.contains(ref)) { + LOGGER.warn("Circular reference detected in allOf for $ref: {}. Skipping.", ref); + continue; + } + Schema resolved = ModelUtils.getSchema(openAPI, ref); if (resolved != null) { + processedModels.add(ref); traverseSchemaProperties(mediaType, resolved, processedModels, values); } + } else { + traverseSchemaProperties(mediaType, composed, processedModels, values); } } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/ExampleGeneratorTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/ExampleGeneratorTest.java index 41a61a4c32fc..1a379ca63c32 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/ExampleGeneratorTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/ExampleGeneratorTest.java @@ -8,7 +8,6 @@ import java.util.*; -import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import static org.testng.AssertJUnit.assertEquals; @@ -249,6 +248,37 @@ public void generateFromResponseSchemaWithAllOfComposedModel() throws Exception{ assertEquals("200", examples.get(0).get("statusCode")); } + @Test + public void generateFromResponseSchemaWithAllOfRecursionSchema() throws Exception{ + OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/example_generator_test.yaml"); + + new InlineModelResolver().flatten(openAPI); + + ExampleGenerator exampleGenerator = new ExampleGenerator(openAPI.getComponents().getSchemas(), openAPI); + Set mediaTypeKeys = new TreeSet<>(); + mediaTypeKeys.add("application/json"); + List> examples = exampleGenerator.generateFromResponseSchema( + "200", + openAPI + .getPaths() + .get("/generate_from_response_schema_with_allOf_circular_model") + .getGet() + .getResponses() + .get("200") + .getContent() + .get("application/json") + .getSchema(), + mediaTypeKeys + ); + + ObjectMapper mapper = new ObjectMapper(); + + assertEquals(1, examples.size()); + assertEquals("application/json", examples.get(0).get("contentType")); + assertEquals(mapper.readTree(String.format(Locale.ROOT, "{%n \"example_schema_property_alloff_circular\" : \"example schema property allOff circular\"%n}")), mapper.readTree(examples.get(0).get("example"))); + assertEquals("200", examples.get(0).get("statusCode")); + } + @Test public void generateFromResponseSchemaWithAllOfChildComposedModel() throws Exception { OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/example_generator_test.yaml"); diff --git a/modules/openapi-generator/src/test/resources/3_0/example_generator_test.yaml b/modules/openapi-generator/src/test/resources/3_0/example_generator_test.yaml index 020edb73b406..6f2ed06c361d 100644 --- a/modules/openapi-generator/src/test/resources/3_0/example_generator_test.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/example_generator_test.yaml @@ -101,6 +101,16 @@ paths: application/json: schema: $ref: '#/components/schemas/ExampleAllOfParentSchema' + /generate_from_response_schema_with_allOf_circular_model: + get: + operationId: generateFromResponseSchemaWithAllOfCircularModel + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ExampleAllOfCircularSchema' /generate_from_response_schema_with_anyOf_composed_model: get: operationId: generateFromResponseSchemaWithAnyOfModel @@ -160,6 +170,15 @@ components: example_schema_property_composed_parent: type: string example: example schema property value composed parent + ExampleAllOfCircularSchema: + type: object + allOf: + - $ref: '#/components/schemas/ExampleAllOfCircularSchema' + - type: object + properties: + example_schema_property_alloff_circular: + type: string + example: example schema property allOff circular ExampleAnyOfSchema: type: object anyOf: From d60cec524ccf6e37763a12935fe82b9d2b132f27 Mon Sep 17 00:00:00 2001 From: mullinaa Date: Sun, 7 Jun 2026 20:19:19 +0300 Subject: [PATCH 2/2] Add new test and fixes for sibling cases --- .../codegen/examples/ExampleGenerator.java | 5 +++ .../codegen/ExampleGeneratorTest.java | 33 ++++++++++++++++++- .../resources/3_0/example_generator_test.yaml | 26 +++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/examples/ExampleGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/examples/ExampleGenerator.java index 7a5dde85b0ca..010a0f93aaef 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/examples/ExampleGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/examples/ExampleGenerator.java @@ -406,6 +406,10 @@ private void traverseSchemaProperties(String mediaType, Schema schema, Set processedModels, Map values) { List interfaces = schema.getAllOf(); + if (interfaces == null) { + return; + } + for (Schema composed : interfaces) { if (composed.get$ref() != null) { String ref = ModelUtils.getSimpleRef(composed.get$ref()); @@ -419,6 +423,7 @@ private void resolveAllOfSchemaProperties(String mediaType, Schema schema, Set mediaTypeKeys = new TreeSet<>(); + mediaTypeKeys.add("application/json"); + List> examples = exampleGenerator.generateFromResponseSchema( + "200", + openAPI + .getPaths() + .get("/generate_from_response_schema_with_allOf_sibling_model") + .getGet() + .getResponses() + .get("200") + .getContent() + .get("application/json") + .getSchema(), + mediaTypeKeys + ); + + ObjectMapper mapper = new ObjectMapper(); + + assertEquals(1, examples.size()); + assertEquals("application/json", examples.get(0).get("contentType")); + assertEquals(mapper.readTree(String.format(Locale.ROOT, "{%n \"base\":{\"example_schema_property\":\"example schema property value\",\"example_schema_property_composed\":\"example schema property value composed\"}, \"sibling\":{\"example_schema_property\":\"example schema property value\",\"example_schema_property_composed\":\"example schema property value composed\"} %n}")), mapper.readTree(examples.get(0).get("example"))); + assertEquals("200", examples.get(0).get("statusCode")); + } + @Test public void generateFromResponseSchemaWithAllOfChildComposedModel() throws Exception { OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/example_generator_test.yaml"); diff --git a/modules/openapi-generator/src/test/resources/3_0/example_generator_test.yaml b/modules/openapi-generator/src/test/resources/3_0/example_generator_test.yaml index 6f2ed06c361d..441e4e80caaa 100644 --- a/modules/openapi-generator/src/test/resources/3_0/example_generator_test.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/example_generator_test.yaml @@ -111,6 +111,16 @@ paths: application/json: schema: $ref: '#/components/schemas/ExampleAllOfCircularSchema' + /generate_from_response_schema_with_allOf_sibling_model: + get: + operationId: generateFromResponseSchemaWithAllOfSiblingModel + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ExampleAllOfSiblingContainerSchema' /generate_from_response_schema_with_anyOf_composed_model: get: operationId: generateFromResponseSchemaWithAnyOfModel @@ -161,6 +171,15 @@ components: example_schema_property_composed: type: string example: example schema property value composed + ExampleAllOfSiblingSchema: + type: object + allOf: + - $ref: '#/components/schemas/ExampleSchema' + - type: object + properties: + example_schema_property_composed: + type: string + example: example schema property value composed ExampleAllOfParentSchema: type: object allOf: @@ -179,6 +198,13 @@ components: example_schema_property_alloff_circular: type: string example: example schema property allOff circular + ExampleAllOfSiblingContainerSchema: + type: object + properties: + base: + $ref: '#/components/schemas/ExampleAllOfSchema' + sibling: + $ref: '#/components/schemas/ExampleAllOfSiblingSchema' ExampleAnyOfSchema: type: object anyOf: