From cbc64e86d33d5a2e1a3736b2d6fd8caadb665ac2 Mon Sep 17 00:00:00 2001 From: William Cheng Date: Wed, 30 Oct 2024 11:50:29 +0800 Subject: [PATCH] Update inline model resolver to flatten responses (#19992) * update inline model resolver flatten responses * minor update * minor update * minor update * minor update --- .../openapitools/codegen/DefaultCodegen.java | 8 ++-- .../codegen/InlineModelResolver.java | 17 ++++++- .../codegen/OpenAPINormalizer.java | 44 ++++++++++++++----- .../codegen/InlineModelResolverTest.java | 14 ++++-- .../codegen/OpenAPINormalizerTest.java | 22 +++++++++- .../resources/3_0/inline_model_resolver.yaml | 12 +++++ .../test/resources/3_1/common-parameters.yaml | 12 +++++ 7 files changed, 109 insertions(+), 20 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index d4e30f305595..3199e40577c1 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -4774,7 +4774,7 @@ public CodegenOperation fromOperation(String path, op.hasRequiredParams = op.requiredParams.size() > 0; // check if the operation has only a single parameter - op.hasSingleParam = op.allParams.size() == 1; + op.hasSingleParam = op.allParams.size() == 1; // set Restful Flag op.isRestfulShow = op.isRestfulShow(); @@ -5724,7 +5724,7 @@ public void addOperationToGroup(String tag, String resourcePath, Operation opera // check for operationId uniqueness String uniqueName = co.operationId; int counter = seenOperationIds.getOrDefault(uniqueName, 0); - while(seenOperationIds.containsKey(uniqueName)) { + while (seenOperationIds.containsKey(uniqueName)) { uniqueName = co.operationId + "_" + counter; counter++; } @@ -6120,7 +6120,7 @@ private String uniqueCaseInsensitiveString(String value, Map see return seenValues.get(value); } - Optional> foundEntry = seenValues.entrySet().stream().filter(v -> v.getValue().toLowerCase(Locale.ROOT).equals(value.toLowerCase(Locale.ROOT))).findAny(); + Optional> foundEntry = seenValues.entrySet().stream().filter(v -> v.getValue().toLowerCase(Locale.ROOT).equals(value.toLowerCase(Locale.ROOT))).findAny(); if (foundEntry.isPresent()) { int counter = 0; String uniqueValue = value + "_" + counter; @@ -8181,7 +8181,7 @@ protected boolean executePostProcessor(String[] commandArr) { int exitValue = p.exitValue(); if (exitValue != 0) { try (InputStreamReader inputStreamReader = new InputStreamReader(p.getErrorStream(), StandardCharsets.UTF_8); - BufferedReader br = new BufferedReader(inputStreamReader)) { + BufferedReader br = new BufferedReader(inputStreamReader)) { StringBuilder sb = new StringBuilder(); String line; while ((line = br.readLine()) != null) { diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java index f9a1915ffc6f..530e20c73dfa 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java @@ -107,6 +107,7 @@ void flatten(OpenAPI openAPI) { flattenPaths(); flattenComponents(); + flattenComponentResponses(); } /** @@ -352,7 +353,7 @@ private void gatherInlineModels(Schema schema, String modelPrefix) { } if (items == null) { - LOGGER.debug("prefixItems in array schema is not supported at the moment: {}", schema.toString()); + LOGGER.debug("prefixItems in array schema is not supported at the moment: {}", schema.toString()); return; } String schemaName = resolveModelName(items.getTitle(), modelPrefix + this.inlineSchemaOptions.get("ARRAY_ITEM_SUFFIX")); @@ -568,6 +569,20 @@ private void flattenResponses(String modelName, Operation operation) { } } + /** + * Flatten inline models in the responses section in the components. + */ + private void flattenComponentResponses() { + Map apiResponses = openAPI.getComponents().getResponses(); + if (apiResponses == null) { + return; + } + + for (Map.Entry entry : apiResponses.entrySet()) { + flattenContent(entry.getValue().getContent(), null); + } + } + /** * Flattens properties of inline object schemas that belong to a composed schema into a * single flat list of properties. This is useful to generate a single or multiple diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java index 05077a68cf06..4ee87555e80d 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java @@ -287,6 +287,7 @@ void normalize() { normalizeInfo(); normalizePaths(); normalizeComponentsSchemas(); + normalizeComponentsResponses(); } /** @@ -445,12 +446,19 @@ private void normalizeResponses(Operation operation) { } for (Map.Entry responsesEntry : responses.entrySet()) { - if (responsesEntry.getValue() == null) { - continue; - } else { - normalizeContent(ModelUtils.getReferencedApiResponse(openAPI, responsesEntry.getValue()).getContent()); - normalizeHeaders(ModelUtils.getReferencedApiResponse(openAPI, responsesEntry.getValue()).getHeaders()); - } + normalizeResponse(responsesEntry.getValue()); + } + } + + /** + * Normalizes schemas in ApiResponse + * + * @param apiResponse API response + */ + private void normalizeResponse(ApiResponse apiResponse) { + if (apiResponse != null) { + normalizeContent(ModelUtils.getReferencedApiResponse(openAPI, apiResponse).getContent()); + normalizeHeaders(ModelUtils.getReferencedApiResponse(openAPI, apiResponse).getHeaders()); } } @@ -502,11 +510,25 @@ private void normalizeComponentsSchemas() { } } + /** + * Normalizes schemas in component's responses. + */ + private void normalizeComponentsResponses() { + Map apiResponses = openAPI.getComponents().getResponses(); + if (apiResponses == null) { + return; + } + + for (Map.Entry entry : apiResponses.entrySet()) { + normalizeResponse(entry.getValue()); + } + } + /** * Auto fix a self referencing schema using any type to replace the self-referencing sub-item. * - * @param name Schema name - * @param schema Schema + * @param name Schema name + * @param schema Schema */ public void fixSelfReferenceSchema(String name, Schema schema) { if (ModelUtils.isArraySchema(schema)) { @@ -993,7 +1015,6 @@ private Schema processSimplifyAnyOfStringAndEnumString(Schema schema) { } - /** * If the schema is oneOf and the sub-schemas is null, set `nullable: true` * instead. @@ -1012,7 +1033,7 @@ private Schema processSimplifyOneOf(Schema schema) { // simplify any type with 6 sub-schemas (string, integer, etc) in oneOf if (oneOfSchemas.size() == 6) { TreeSet ts = new TreeSet<>(); - for (Schema s: oneOfSchemas) { + for (Schema s : oneOfSchemas) { s = ModelUtils.getReferencedSchema(openAPI, s); String type = ModelUtils.getType(s); if (type == null) { @@ -1113,6 +1134,7 @@ private Schema setNullable(Schema schema) { schema.setNullable(true); return schema; } + /** * Set nullable to true in map if needed. * @@ -1148,7 +1170,7 @@ private Schema processSimplifyAnyOf(Schema schema) { // simplify any type with 6 sub-schemas (string, integer, etc) in anyOf if (anyOfSchemas.size() == 6) { TreeSet ts = new TreeSet<>(); - for (Schema s: anyOfSchemas) { + for (Schema s : anyOfSchemas) { s = ModelUtils.getReferencedSchema(openAPI, s); String type = ModelUtils.getType(s); if (type == null) { diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/InlineModelResolverTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/InlineModelResolverTest.java index 133f9b1c5129..4e297f5b0af1 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/InlineModelResolverTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/InlineModelResolverTest.java @@ -352,6 +352,14 @@ public void resolveInlineArraySchemaWithTitle() { assertTrue(user.getProperties().get("city") instanceof StringSchema); } + @Test + public void resolveComponentsResponses() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/inline_model_resolver.yaml"); + new InlineModelResolver().flatten(openAPI); + ApiResponse apiResponse = openAPI.getComponents().getResponses().get("JustAnotherResponse"); + assertEquals(apiResponse.getContent().get("application/json").getSchema().get$ref(), "#/components/schemas/inline_object"); + } + @Test public void resolveRequestBodyInvalidRef() { OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/invalid_ref_request_body.yaml"); @@ -1043,7 +1051,7 @@ public void testInlineSchemaNameMapping() { public void testInlineSchemaOptions() { OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/inline_model_resolver.yaml"); InlineModelResolver resolver = new InlineModelResolver(); - Map inlineSchemaOptions= new HashMap<>(); + Map inlineSchemaOptions = new HashMap<>(); inlineSchemaOptions.put("ARRAY_ITEM_SUFFIX", "_something"); resolver.setInlineSchemaOptions(inlineSchemaOptions); resolver.flatten(openAPI); @@ -1135,7 +1143,7 @@ public void testNestedAnyOf() { public void resolveOperationInlineEnum() { OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/inline_model_resolver.yaml"); Parameter parameter = openAPI.getPaths().get("/resolve_parameter_inline_enum").getGet().getParameters().get(0); - assertNull(((ArraySchema) parameter.getSchema()).getItems().get$ref() ); + assertNull(((ArraySchema) parameter.getSchema()).getItems().get$ref()); InlineModelResolver resolver = new InlineModelResolver(); Map inlineSchemaOptions = new HashMap<>(); @@ -1145,7 +1153,7 @@ public void resolveOperationInlineEnum() { Parameter parameter2 = openAPI.getPaths().get("/resolve_parameter_inline_enum").getGet().getParameters().get(0); assertEquals("#/components/schemas/resolveParameterInlineEnum_status_inline_enum_parameter_inner", - ((ArraySchema) parameter2.getSchema()).getItems().get$ref() ); + ((ArraySchema) parameter2.getSchema()).getItems().get$ref()); } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java index e7eb0b02f35b..f8a4c15c8804 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/OpenAPINormalizerTest.java @@ -16,10 +16,12 @@ package org.openapitools.codegen; +import io.swagger.annotations.Api; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.media.*; +import io.swagger.v3.oas.models.responses.ApiResponse; import org.openapitools.codegen.utils.ModelUtils; import org.testng.annotations.Test; @@ -549,7 +551,7 @@ public void testSetPrimitiveTypesToNullable() { } @Test - public void testOpenAPINormalizerSimplifyOneOfAnyOf31SpecForIssue18184 () { + public void testOpenAPINormalizerSimplifyOneOfAnyOf31SpecForIssue18184() { // to test the rule SIMPLIFY_ONEOF_ANYOF in 3.1 spec OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/issue_18184.yaml"); // test spec contains anyOf with a ref to enum and another scheme type is null @@ -794,4 +796,22 @@ public void testOpenAPINormalizerProcessingAllOfSchema31Spec() { assertEquals(((Schema) schema2.getProperties().get("property2")).getAllOf(), null); assertEquals(((Schema) schema2.getProperties().get("property2")).getAllOf(), null); } + + @Test + public void testOpenAPINormalizerComponentsResponses31Spec() { + OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_1/common-parameters.yaml"); + ApiResponse apiResponse = openAPI.getComponents().getResponses().get("JustAnotherResponse"); + assertEquals(((Schema) apiResponse.getContent().get("application/json").getSchema().getProperties().get("uuid")).getType(), null); + assertEquals(((Schema) apiResponse.getContent().get("application/json").getSchema().getProperties().get("label")).getType(), null); + + Map inputRules = Map.of( + "NORMALIZE_31SPEC", "true" + ); + OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, inputRules); + openAPINormalizer.normalize(); + + ApiResponse apiResponse2 = openAPI.getComponents().getResponses().get("JustAnotherResponse"); + assertEquals(((Schema) apiResponse2.getContent().get("application/json").getSchema().getProperties().get("uuid")).getType(), "integer"); + assertEquals(((Schema) apiResponse2.getContent().get("application/json").getSchema().getProperties().get("label")).getType(), "string"); + } } diff --git a/modules/openapi-generator/src/test/resources/3_0/inline_model_resolver.yaml b/modules/openapi-generator/src/test/resources/3_0/inline_model_resolver.yaml index 6b0b89d36ef3..3ecb0396d9f1 100644 --- a/modules/openapi-generator/src/test/resources/3_0/inline_model_resolver.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/inline_model_resolver.yaml @@ -455,6 +455,18 @@ paths: type: string components: requestBodies: {} + responses: + JustAnotherResponse: + description: just another response + content: + application/json: + schema: + type: object + properties: + uuid: + type: integer + label: + type: string schemas: Users: type: array diff --git a/modules/openapi-generator/src/test/resources/3_1/common-parameters.yaml b/modules/openapi-generator/src/test/resources/3_1/common-parameters.yaml index 6052f3d450f6..847b84a0959d 100644 --- a/modules/openapi-generator/src/test/resources/3_1/common-parameters.yaml +++ b/modules/openapi-generator/src/test/resources/3_1/common-parameters.yaml @@ -76,6 +76,18 @@ paths: security: - api_key: [] components: + responses: + JustAnotherResponse: + description: JustAnotherResponse + content: + application/json: + schema: + type: object + properties: + uuid: + type: integer + label: + type: string requestBodies: Pet: content: