fix: ExampleGenerator for composed child schemas and array schemas (#18479)

* fix: ExampleGenerator for composed child schemas and array schemas correctly

* fix: refactor to remove code duplication

* fix: fixes test assertion encoding

* fix: adds doc to new method
This commit is contained in:
Phellippe Lima
2024-04-30 07:36:49 +01:00
committed by GitHub
parent 81fab15a33
commit e36172090e
16 changed files with 195 additions and 12 deletions

View File

@@ -19,7 +19,6 @@ package org.openapitools.codegen.examples;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.utils.ModelUtils;
@@ -364,17 +363,7 @@ public class ExampleGenerator {
return schema.getExample();
} else if (ModelUtils.isAllOf(schema) || ModelUtils.isAllOfWithProperties(schema)) {
LOGGER.debug("Resolving allOf model '{}' to example", name);
List<Schema> 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);
}
}
}
resolveAllOfSchemaProperties(mediaType, schema, processedModels, values);
schema.setExample(values);
return schema.getExample();
} else if (ModelUtils.isAnyOf(schema) || ModelUtils.isOneOf(schema)) {
@@ -388,6 +377,8 @@ public class ExampleGenerator {
return null;
}
return resolvePropertyToExample(name, mediaType, found.get(), processedModels);
} else if (ModelUtils.isArraySchema(schema)) {
return resolvePropertyToExample(schema.getName(), mediaType, schema, processedModels);
} else {
// TODO log an error message as the model does not have any properties
return null;
@@ -400,6 +391,29 @@ public class ExampleGenerator {
Schema property = (Schema) schema.getProperties().get(propertyName.toString());
values.put(propertyName.toString(), resolvePropertyToExample(propertyName.toString(), mediaType, property, processedModels));
}
} else if (ModelUtils.isAllOf(schema) || ModelUtils.isAllOfWithProperties(schema)) {
resolveAllOfSchemaProperties(mediaType, schema, processedModels, values);
}
}
/**
* Transverse and resolves all property examples for `allOf` composed schemas into `values` map object
* @param mediaType MIME type
* @param schema OAS schema
* @param processedModels Set containing all processed models
* @param values Example value map
*/
private void resolveAllOfSchemaProperties(String mediaType, Schema schema, Set<String> processedModels, Map<String, Object> values) {
List<Schema> 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);
}
}
}
}

View File

@@ -152,6 +152,35 @@ public class ExampleGeneratorTest {
assertEquals("200", examples.get(0).get("statusCode"));
}
@Test
public void generateFromResponseSchemaWithArraySchema() {
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<String> mediaTypeKeys = new TreeSet<>();
mediaTypeKeys.add("application/json");
List<Map<String, String>> examples = exampleGenerator.generateFromResponseSchema(
"200",
openAPI
.getPaths()
.get("/generate_from_response_schema_array_reference")
.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\" : \"example schema property value\"%n}, {%n \"example_schema_property\" : \"example schema property value\"%n} ]"), examples.get(0).get("example"));
assertEquals("200", examples.get(0).get("statusCode"));
}
@Test
public void generateFromResponseSchemaWithModel() {
OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/example_generator_test.yaml");
@@ -210,6 +239,35 @@ public class ExampleGeneratorTest {
assertEquals("200", examples.get(0).get("statusCode"));
}
@Test
public void generateFromResponseSchemaWithAllOfChildComposedModel() {
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<String> mediaTypeKeys = new TreeSet<>();
mediaTypeKeys.add("application/json");
List<Map<String, String>> examples = exampleGenerator.generateFromResponseSchema(
"200",
openAPI
.getPaths()
.get("/generate_from_response_schema_with_allOf_child_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_composed_parent\" : \"example schema property value composed parent\",%n \"example_schema_property\" : \"example schema property value\"%n}"), examples.get(0).get("example"));
assertEquals("200", examples.get(0).get("statusCode"));
}
@Test
public void generateFromResponseSchemaWithOneOfComposedModel() {
OpenAPI openAPI = TestUtils.parseFlattenSpec("src/test/resources/3_0/example_generator_test.yaml");

View File

@@ -61,6 +61,16 @@ paths:
items:
type: string
example: primitive types example value
/generate_from_response_schema_array_reference:
get:
operationId: generateFromResponseSchemaArrayReference
responses:
'200':
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/ExampleArraySchema'
/generate_from_response_schema_with_model:
get:
operationId: generateFromResponseSchemaWithModel
@@ -81,6 +91,16 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExampleAllOfSchema'
/generate_from_response_schema_with_allOf_child_composed_model:
get:
operationId: generateFromResponseSchemaWithAllOfModel
responses:
'200':
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/ExampleAllOfParentSchema'
/generate_from_response_schema_with_anyOf_composed_model:
get:
operationId: generateFromResponseSchemaWithAnyOfModel
@@ -131,6 +151,15 @@ components:
example_schema_property_composed:
type: string
example: example schema property value composed
ExampleAllOfParentSchema:
type: object
allOf:
- $ref: '#/components/schemas/ExampleAllOfSchema'
- type: object
properties:
example_schema_property_composed_parent:
type: string
example: example schema property value composed parent
ExampleAnyOfSchema:
type: object
anyOf:
@@ -149,3 +178,7 @@ components:
example_schema_property_composed:
type: string
example: example schema property value composed
ExampleArraySchema:
type: array
items:
$ref: '#/components/schemas/ExampleSchema'

View File

@@ -1247,6 +1247,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2714,6 +2718,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1278,6 +1278,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2862,6 +2866,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1278,6 +1278,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2862,6 +2866,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1278,6 +1278,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2862,6 +2866,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1278,6 +1278,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2862,6 +2866,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1278,6 +1278,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2862,6 +2866,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1304,6 +1304,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2894,6 +2898,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1304,6 +1304,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2894,6 +2898,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1304,6 +1304,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2894,6 +2898,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1278,6 +1278,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2862,6 +2866,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1278,6 +1278,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2862,6 +2866,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1304,6 +1304,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2894,6 +2898,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string

View File

@@ -1278,6 +1278,10 @@ components:
type: array
RolesReportsHash:
description: Role report Hash
example:
role:
name: name
role_uuid: 046b6c7f-0b8a-43b9-b35d-6489e6daee91
properties:
role_uuid:
format: uuid
@@ -2862,6 +2866,8 @@ components:
- country
type: object
RolesReportsHash_role:
example:
name: name
properties:
name:
type: string