From 4c1fb82c61e58e2a962dd4b53c64747ef3dc946e Mon Sep 17 00:00:00 2001 From: Alexis Couvreur Date: Fri, 29 Dec 2023 14:16:00 -0500 Subject: [PATCH] fix: ExampleGenerator correctly generates allOf composed schemas Changes the previous behavior of generating `null` examples for allOf composed schemas. Fixes #17497 --- .../codegen/examples/ExampleGenerator.java | 27 +++++++++++++++-- .../codegen/ExampleGeneratorTest.java | 29 +++++++++++++++++++ .../resources/3_0/example_generator_test.yaml | 21 +++++++++++++- .../java/apache-httpclient/api/openapi.yaml | 4 +++ .../petstore/java/feign/api/openapi.yaml | 4 +++ .../petstore/java/resteasy/api/openapi.yaml | 4 +++ .../resttemplate-withXml/api/openapi.yaml | 4 +++ .../java/resttemplate/api/openapi.yaml | 4 +++ .../petstore/java/vertx/api/openapi.yaml | 4 +++ .../java/webclient-jakarta/api/openapi.yaml | 4 +++ .../java/webclient-swagger2/api/openapi.yaml | 4 +++ .../petstore/java/webclient/api/openapi.yaml | 4 +++ .../api/openapi.yaml | 3 ++ .../src/main/resources/openapi.yaml | 4 +++ .../src/main/resources/openapi.yaml | 4 +++ .../src/main/resources/META-INF/openapi.yml | 4 +++ .../src/main/resources/META-INF/openapi.yml | 4 +++ .../src/main/resources/openapi.yaml | 4 +++ .../src/main/resources/openapi.yaml | 4 +++ .../src/main/resources/openapi.yaml | 4 +++ .../src/main/resources/openapi.yaml | 4 +++ .../src/main/resources/openapi.yaml | 4 +++ .../src/main/resources/openapi.yaml | 4 +++ .../src/main/resources/openapi.yaml | 4 +++ .../src/main/resources/openapi.yaml | 4 +++ .../src/main/resources/openapi.yaml | 4 +++ 26 files changed, 164 insertions(+), 4 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 a1c16819fb03..0ffc94956e7d 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 @@ -351,9 +351,21 @@ private Object resolveModelToExample(String name, String mediaType, Schema schem return schema.getExample(); } else if (schema.getProperties() != null) { LOGGER.debug("Creating example from model values"); - for (Object propertyName : schema.getProperties().keySet()) { - Schema property = (Schema) schema.getProperties().get(propertyName.toString()); - values.put(propertyName.toString(), resolvePropertyToExample(propertyName.toString(), mediaType, property, processedModels)); + traverseSchemaProperties(mediaType, schema, processedModels, values); + schema.setExample(values); + return schema.getExample(); + } else if (ModelUtils.isAllOf(schema) || ModelUtils.isAllOfWithProperties(schema)) { + LOGGER.debug("Resolving allOf model '{}' to example", name); + 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()); + Schema resolved = ModelUtils.getSchema(openAPI, ref); + if (resolved != null) { + traverseSchemaProperties(mediaType, resolved, processedModels, values); + } + } } schema.setExample(values); return schema.getExample(); @@ -362,4 +374,13 @@ private Object resolveModelToExample(String name, String mediaType, Schema schem return null; } } + + private void traverseSchemaProperties(String mediaType, Schema schema, Set processedModels, Map values) { + if (schema.getProperties() != null) { + for (Object propertyName : schema.getProperties().keySet()) { + Schema property = (Schema) schema.getProperties().get(propertyName.toString()); + values.put(propertyName.toString(), resolvePropertyToExample(propertyName.toString(), mediaType, property, processedModels)); + } + } + } } 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 45b9260830df..dcd8c42feaa0 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 @@ -151,4 +151,33 @@ public void generateFromResponseSchemaWithModel() { assertEquals(String.format(Locale.ROOT, "{%n \"example_schema_property\" : \"example schema property value\"%n}"), examples.get(0).get("example")); assertEquals("200", examples.get(0).get("statusCode")); } + + @Test + public void generateFromResponseSchemaWithAllOfComposedModel() { + 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_composed_model") + .getGet() + .getResponses() + .get("200") + .getContent() + .get("application/json") + .getSchema(), + mediaTypeKeys + ); + + assertEquals(1, examples.size()); + assertEquals("application/json", examples.get(0).get("contentType")); + assertEquals(String.format(Locale.ROOT, "{%n \"example_schema_property_composed\" : \"example schema property value composed\",%n \"example_schema_property\" : \"example schema property value\"%n}"), examples.get(0).get("example")); + assertEquals("200", examples.get(0).get("statusCode")); + } } 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 c8f2187e69b7..a2432a909b96 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 @@ -53,7 +53,7 @@ paths: example: primitive types example value /generate_from_response_schema_with_model: get: - operationId: generateFromResponseSchemaWithArrayOfPrimitiveTypes + operationId: generateFromResponseSchemaWithModel responses: '200': description: successful operation @@ -61,6 +61,16 @@ paths: application/json: schema: $ref: '#/components/schemas/ExampleSchema' + /generate_from_response_schema_with_allOf_composed_model: + get: + operationId: generateFromResponseSchemaWithComposedModel + responses: + '200': + description: successful operation + content: + application/json: + schema: + $ref: '#/components/schemas/ExampleComposedSchema' components: schemas: StringSchema: @@ -72,3 +82,12 @@ components: example_schema_property: type: string example: example schema property value + ExampleComposedSchema: + type: object + allOf: + - $ref: '#/components/schemas/ExampleSchema' + - type: object + properties: + example_schema_property_composed: + type: string + example: example schema property value composed diff --git a/samples/client/petstore/java/apache-httpclient/api/openapi.yaml b/samples/client/petstore/java/apache-httpclient/api/openapi.yaml index 991caf590017..6bfd40b86582 100644 --- a/samples/client/petstore/java/apache-httpclient/api/openapi.yaml +++ b/samples/client/petstore/java/apache-httpclient/api/openapi.yaml @@ -1919,6 +1919,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/client/petstore/java/feign/api/openapi.yaml b/samples/client/petstore/java/feign/api/openapi.yaml index 991caf590017..6bfd40b86582 100644 --- a/samples/client/petstore/java/feign/api/openapi.yaml +++ b/samples/client/petstore/java/feign/api/openapi.yaml @@ -1919,6 +1919,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/client/petstore/java/resteasy/api/openapi.yaml b/samples/client/petstore/java/resteasy/api/openapi.yaml index 991caf590017..6bfd40b86582 100644 --- a/samples/client/petstore/java/resteasy/api/openapi.yaml +++ b/samples/client/petstore/java/resteasy/api/openapi.yaml @@ -1919,6 +1919,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/client/petstore/java/resttemplate-withXml/api/openapi.yaml b/samples/client/petstore/java/resttemplate-withXml/api/openapi.yaml index 991caf590017..6bfd40b86582 100644 --- a/samples/client/petstore/java/resttemplate-withXml/api/openapi.yaml +++ b/samples/client/petstore/java/resttemplate-withXml/api/openapi.yaml @@ -1919,6 +1919,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/client/petstore/java/resttemplate/api/openapi.yaml b/samples/client/petstore/java/resttemplate/api/openapi.yaml index 991caf590017..6bfd40b86582 100644 --- a/samples/client/petstore/java/resttemplate/api/openapi.yaml +++ b/samples/client/petstore/java/resttemplate/api/openapi.yaml @@ -1919,6 +1919,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/client/petstore/java/vertx/api/openapi.yaml b/samples/client/petstore/java/vertx/api/openapi.yaml index 991caf590017..6bfd40b86582 100644 --- a/samples/client/petstore/java/vertx/api/openapi.yaml +++ b/samples/client/petstore/java/vertx/api/openapi.yaml @@ -1919,6 +1919,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/client/petstore/java/webclient-jakarta/api/openapi.yaml b/samples/client/petstore/java/webclient-jakarta/api/openapi.yaml index 991caf590017..6bfd40b86582 100644 --- a/samples/client/petstore/java/webclient-jakarta/api/openapi.yaml +++ b/samples/client/petstore/java/webclient-jakarta/api/openapi.yaml @@ -1919,6 +1919,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/client/petstore/java/webclient-swagger2/api/openapi.yaml b/samples/client/petstore/java/webclient-swagger2/api/openapi.yaml index 991caf590017..6bfd40b86582 100644 --- a/samples/client/petstore/java/webclient-swagger2/api/openapi.yaml +++ b/samples/client/petstore/java/webclient-swagger2/api/openapi.yaml @@ -1919,6 +1919,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/client/petstore/java/webclient/api/openapi.yaml b/samples/client/petstore/java/webclient/api/openapi.yaml index 991caf590017..6bfd40b86582 100644 --- a/samples/client/petstore/java/webclient/api/openapi.yaml +++ b/samples/client/petstore/java/webclient/api/openapi.yaml @@ -1919,6 +1919,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/openapi3/client/petstore/java/jersey2-java8-special-characters/api/openapi.yaml b/samples/openapi3/client/petstore/java/jersey2-java8-special-characters/api/openapi.yaml index 2bfcc8d5e155..4593aa61a371 100644 --- a/samples/openapi3/client/petstore/java/jersey2-java8-special-characters/api/openapi.yaml +++ b/samples/openapi3/client/petstore/java/jersey2-java8-special-characters/api/openapi.yaml @@ -49,4 +49,7 @@ components: \ characters. The sanitization rules should make it possible to generate a\ \ language-specific classname with allowed characters in that programming\ \ language." + example: + prop2: prop2 + objectType: objectType diff --git a/samples/openapi3/server/petstore/springboot-delegate/src/main/resources/openapi.yaml b/samples/openapi3/server/petstore/springboot-delegate/src/main/resources/openapi.yaml index 3ebfb97645f2..adb3ff3f6021 100644 --- a/samples/openapi3/server/petstore/springboot-delegate/src/main/resources/openapi.yaml +++ b/samples/openapi3/server/petstore/springboot-delegate/src/main/resources/openapi.yaml @@ -1902,6 +1902,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/openapi3/server/petstore/springboot-implicitHeaders/src/main/resources/openapi.yaml b/samples/openapi3/server/petstore/springboot-implicitHeaders/src/main/resources/openapi.yaml index 3ebfb97645f2..adb3ff3f6021 100644 --- a/samples/openapi3/server/petstore/springboot-implicitHeaders/src/main/resources/openapi.yaml +++ b/samples/openapi3/server/petstore/springboot-implicitHeaders/src/main/resources/openapi.yaml @@ -1902,6 +1902,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/server/petstore/java-helidon-server/mp/src/main/resources/META-INF/openapi.yml b/samples/server/petstore/java-helidon-server/mp/src/main/resources/META-INF/openapi.yml index efc990069c1b..1323a1a7f3e3 100644 --- a/samples/server/petstore/java-helidon-server/mp/src/main/resources/META-INF/openapi.yml +++ b/samples/server/petstore/java-helidon-server/mp/src/main/resources/META-INF/openapi.yml @@ -1919,6 +1919,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/server/petstore/java-helidon-server/se/src/main/resources/META-INF/openapi.yml b/samples/server/petstore/java-helidon-server/se/src/main/resources/META-INF/openapi.yml index efc990069c1b..1323a1a7f3e3 100644 --- a/samples/server/petstore/java-helidon-server/se/src/main/resources/META-INF/openapi.yml +++ b/samples/server/petstore/java-helidon-server/se/src/main/resources/META-INF/openapi.yml @@ -1919,6 +1919,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/server/petstore/springboot-beanvalidation-no-nullable/src/main/resources/openapi.yaml b/samples/server/petstore/springboot-beanvalidation-no-nullable/src/main/resources/openapi.yaml index 3ebfb97645f2..adb3ff3f6021 100644 --- a/samples/server/petstore/springboot-beanvalidation-no-nullable/src/main/resources/openapi.yaml +++ b/samples/server/petstore/springboot-beanvalidation-no-nullable/src/main/resources/openapi.yaml @@ -1902,6 +1902,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/server/petstore/springboot-beanvalidation/src/main/resources/openapi.yaml b/samples/server/petstore/springboot-beanvalidation/src/main/resources/openapi.yaml index 3ebfb97645f2..adb3ff3f6021 100644 --- a/samples/server/petstore/springboot-beanvalidation/src/main/resources/openapi.yaml +++ b/samples/server/petstore/springboot-beanvalidation/src/main/resources/openapi.yaml @@ -1902,6 +1902,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/server/petstore/springboot-delegate-j8/src/main/resources/openapi.yaml b/samples/server/petstore/springboot-delegate-j8/src/main/resources/openapi.yaml index 3ebfb97645f2..adb3ff3f6021 100644 --- a/samples/server/petstore/springboot-delegate-j8/src/main/resources/openapi.yaml +++ b/samples/server/petstore/springboot-delegate-j8/src/main/resources/openapi.yaml @@ -1902,6 +1902,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/server/petstore/springboot-delegate/src/main/resources/openapi.yaml b/samples/server/petstore/springboot-delegate/src/main/resources/openapi.yaml index 3ebfb97645f2..adb3ff3f6021 100644 --- a/samples/server/petstore/springboot-delegate/src/main/resources/openapi.yaml +++ b/samples/server/petstore/springboot-delegate/src/main/resources/openapi.yaml @@ -1902,6 +1902,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/server/petstore/springboot-implicitHeaders/src/main/resources/openapi.yaml b/samples/server/petstore/springboot-implicitHeaders/src/main/resources/openapi.yaml index 3ebfb97645f2..adb3ff3f6021 100644 --- a/samples/server/petstore/springboot-implicitHeaders/src/main/resources/openapi.yaml +++ b/samples/server/petstore/springboot-implicitHeaders/src/main/resources/openapi.yaml @@ -1902,6 +1902,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/server/petstore/springboot-reactive-noResponseEntity/src/main/resources/openapi.yaml b/samples/server/petstore/springboot-reactive-noResponseEntity/src/main/resources/openapi.yaml index 3ebfb97645f2..adb3ff3f6021 100644 --- a/samples/server/petstore/springboot-reactive-noResponseEntity/src/main/resources/openapi.yaml +++ b/samples/server/petstore/springboot-reactive-noResponseEntity/src/main/resources/openapi.yaml @@ -1902,6 +1902,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/server/petstore/springboot-reactive/src/main/resources/openapi.yaml b/samples/server/petstore/springboot-reactive/src/main/resources/openapi.yaml index 3ebfb97645f2..adb3ff3f6021 100644 --- a/samples/server/petstore/springboot-reactive/src/main/resources/openapi.yaml +++ b/samples/server/petstore/springboot-reactive/src/main/resources/openapi.yaml @@ -1902,6 +1902,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/server/petstore/springboot-useoptional/src/main/resources/openapi.yaml b/samples/server/petstore/springboot-useoptional/src/main/resources/openapi.yaml index 3ebfb97645f2..adb3ff3f6021 100644 --- a/samples/server/petstore/springboot-useoptional/src/main/resources/openapi.yaml +++ b/samples/server/petstore/springboot-useoptional/src/main/resources/openapi.yaml @@ -1902,6 +1902,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean diff --git a/samples/server/petstore/springboot-virtualan/src/main/resources/openapi.yaml b/samples/server/petstore/springboot-virtualan/src/main/resources/openapi.yaml index 3ebfb97645f2..adb3ff3f6021 100644 --- a/samples/server/petstore/springboot-virtualan/src/main/resources/openapi.yaml +++ b/samples/server/petstore/springboot-virtualan/src/main/resources/openapi.yaml @@ -1902,6 +1902,10 @@ components: otherProperty: type: string type: object + example: + otherProperty: otherProperty + nullableProperty: nullableProperty + type: ChildWithNullable StringBooleanMap: additionalProperties: type: boolean