Repository: swagger-api/swagger-parser
Pull request #1384 (2020-05-31 14:38:47)

Flatten ComposedSchemas at Array items in responses, parameters, requestBodies 
Flatten ComposedSchemas at Array Items in responses, parameters, requestBodies.
refs issue #1382


Patch:
diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/InlineModelResolver.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/InlineModelResolver.java
index 2669c23d..c4559754 100644
--- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/InlineModelResolver.java
+++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/InlineModelResolver.java
@@ -86,10 +86,13 @@ public class InlineModelResolver {
                                             addGenerated(modelName, model);
                                             openAPI.getComponents().addSchemas(modelName, model);
                                         } else if (model instanceof ComposedSchema) {
-                                            String modelName = resolveModelName(model.getTitle(), "body");
-                                            mediaType.setSchema(new Schema().$ref(modelName));
-                                            addGenerated(modelName, model);
-                                            openAPI.getComponents().addSchemas(modelName, model);
+                                            flattenComposedSchema(model, pathname);
+                                            if (model.get$ref() == null) {
+                                                String modelName = resolveModelName(model.getTitle(), "body");
+                                                mediaType.setSchema(this.makeRefProperty(modelName, model));
+                                                addGenerated(modelName, model);
+                                                openAPI.getComponents().addSchemas(modelName, model);
+                                            }
                                         } else if (model instanceof ArraySchema) {
                                             ArraySchema am = (ArraySchema) model;
                                             Schema inner = am.getItems();
@@ -106,6 +109,14 @@ public class InlineModelResolver {
                                                         addGenerated(modelName, inner);
                                                         openAPI.getComponents().addSchemas(modelName, inner);
                                                     }
+                                                }else if (inner instanceof ComposedSchema && this.flattenComposedSchemas){
+                                                    flattenComposedSchema(inner,key);
+                                                    if (inner.get$ref() == null) {
+                                                        String modelName = resolveModelName(inner.getTitle(), "inline_body_items_" + key + "_" + pathname);
+                                                        am.setItems(this.makeRefProperty(modelName, inner));
+                                                        addGenerated(modelName, inner);
+                                                        openAPI.getComponents().addSchemas(modelName, inner);
+                                                    }
                                                 }
                                             }
                                         }
@@ -149,6 +160,14 @@ public class InlineModelResolver {
                                                 addGenerated(modelName, am);
                                                 openAPI.getComponents().addSchemas(modelName, am);
                                             }
+                                        }else if (inner instanceof ComposedSchema && this.flattenComposedSchemas){
+                                            flattenComposedSchema(inner, parameter.getName());
+                                            if (inner.get$ref() == null) {
+                                                String modelName = resolveModelName(inner.getTitle(), "inline_parameter_items_" + parameter.getName());
+                                                am.setItems(this.makeRefProperty(modelName, inner));
+                                                addGenerated(modelName, inner);
+                                                openAPI.getComponents().addSchemas(modelName, inner);
+                                            }
                                         }
                                     }
                                 }
@@ -177,16 +196,16 @@ public class InlineModelResolver {
                                                         addGenerated(modelName, mediaSchema);
                                                         openAPI.getComponents().addSchemas(modelName, mediaSchema);
                                                     }
-                                                }
-                                            } else if (mediaSchema instanceof ComposedSchema ){
-                                                String modelName = resolveModelName(mediaSchema.getTitle(), "inline_response_" + key);
-                                                String existing = matchGenerated(mediaSchema);
-                                                if (existing != null) {
-                                                    media.setSchema(this.makeRefProperty(existing, mediaSchema));
-                                                } else {
-                                                    media.setSchema(this.makeRefProperty(modelName, mediaSchema));
-                                                    addGenerated(modelName, mediaSchema);
-                                                    openAPI.getComponents().addSchemas(modelName, mediaSchema);
+                                                }else if (mediaSchema instanceof ComposedSchema) {
+                                                    String modelName = resolveModelName(mediaSchema.getTitle(), "inline_response_" + key);
+                                                    String existing = matchGenerated(mediaSchema);
+                                                    if (existing != null) {
+                                                        media.setSchema(this.makeRefProperty(existing, mediaSchema));
+                                                    } else {
+                                                        media.setSchema(this.makeRefProperty(modelName, mediaSchema));
+                                                        addGenerated(modelName, mediaSchema);
+                                                        openAPI.getComponents().addSchemas(modelName, mediaSchema);
+                                                    }
                                                 }
 
                                             }else if (mediaSchema instanceof ArraySchema) {
@@ -206,8 +225,17 @@ public class InlineModelResolver {
                                                             addGenerated(modelName, inner);
                                                             openAPI.getComponents().addSchemas(modelName, inner);
                                                         }
+                                                    }else if (inner instanceof ComposedSchema && this.flattenComposedSchemas){
+                                                        flattenComposedSchema(inner,key);
+                                                        if (inner.get$ref() == null) {
+                                                            String modelName = resolveModelName(inner.getTitle(), "inline_response_items" + key);
+                                                            ap.setItems(this.makeRefProperty(modelName, inner));
+                                                            addGenerated(modelName, inner);
+                                                            openAPI.getComponents().addSchemas(modelName, inner);
+                                                        }
                                                     }
                                                 }
+
                                             } else if (mediaSchema.getAdditionalProperties() != null && mediaSchema.getAdditionalProperties() instanceof Schema) {
 
                                                 Schema innerProperty = (Schema) mediaSchema.getAdditionalProperties();
@@ -260,6 +288,14 @@ public class InlineModelResolver {
                             } else {
                                 m.setItems(new Schema().$ref(existing));
                             }
+                        }else if (inner instanceof ComposedSchema && this.flattenComposedSchemas){
+                            flattenComposedSchema(inner,modelName);
+                            if (inner.get$ref() == null) {
+                                modelName = resolveModelName(inner.getTitle(), "inline_array_items_" + modelName);
+                                m.setItems(this.makeRefProperty(modelName, inner));
+                                addGenerated(modelName, inner);
+                                openAPI.getComponents().addSchemas(modelName, inner);
+                            }
                         }
                     }
                 } else if (model instanceof ComposedSchema) {
@@ -473,6 +509,40 @@ public class InlineModelResolver {
         }
     }
 
+    private void flattenComposedSchema(Schema inner, String key) {
+
+        ComposedSchema composedSchema = (ComposedSchema) inner;
+        String inlineModelName = "";
+
+        List<Schema> list = null;
+        if (composedSchema.getAllOf() != null) {
+            list  = composedSchema.getAllOf();
+            inlineModelName = "AllOf";
+        }else if (composedSchema.getAnyOf() != null) {
+            list  = composedSchema.getAnyOf();
+            inlineModelName = "AnyOf";
+        }else if (composedSchema.getOneOf() != null) {
+            list  = composedSchema.getOneOf();
+            inlineModelName = "OneOf";
+        }
+
+        for(int i= 0; i<list.size();i++){
+            if (list.get(i).get$ref() == null){
+                Schema inline = list.get(i);
+                if (inline.getProperties()!= null){
+                    flattenProperties(inline.getProperties(), key);
+                }
+                if (this.flattenComposedSchemas) {
+                    int position = i+1;
+                    inlineModelName = resolveModelName(inline.getTitle(),  key + inlineModelName + "_" + position);
+                    list.set(i,new Schema().$ref(inlineModelName));
+                    addGenerated(inlineModelName, inline);
+                    openAPI.getComponents().addSchemas(inlineModelName, inline);
+                }
+            }
+        }
+    }
+
     @SuppressWarnings("static-method")
     public Schema modelFromProperty(ArraySchema object, @SuppressWarnings("unused") String path) {
         String description = object.getDescription();
@@ -593,7 +663,8 @@ public class InlineModelResolver {
     private boolean isObjectSchema(Schema schema) {
         return schema instanceof ObjectSchema
                 || "object".equalsIgnoreCase(schema.getType())
-                || (schema.getType() == null && schema.getProperties() != null && !schema.getProperties().isEmpty());
+                || (schema.getType() == null && schema.getProperties() != null && !schema.getProperties().isEmpty()
+                || schema instanceof ComposedSchema);
     }
 
     public boolean isSkipMatches() {


Test patch:
diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java
index 49f73bbf..2843327e 100644
--- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java
+++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserTest.java
@@ -82,6 +82,68 @@ public class OpenAPIV3ParserTest {
     protected int serverPort = getDynamicPort();
     protected WireMockServer wireMockServer;
 
+    @Test
+    public void testIssueFlattenArraySchemaItemsInlineModelFalse() {
+        OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();
+        ParseOptions options = new ParseOptions();
+        options.setResolve(true);
+        options.setFlatten(true);
+        options.setFlattenComposedSchemas(false);
+        options.setCamelCaseFlattenNaming(false);
+        SwaggerParseResult parseResult = openApiParser.readLocation("flattenArrayItems.yaml", null, options);
+        OpenAPI openAPI = parseResult.getOpenAPI();
+
+        //responses
+        assertNull(openAPI.getComponents().getSchemas().get("Inline_response_items200"));
+        assertNull(openAPI.getComponents().getSchemas().get("Inline_response_400"));
+
+        //parameters
+        assertNull(openAPI.getComponents().getSchemas().get("Inline_parameter_items_bodylimit"));
+        assertNull(openAPI.getComponents().getSchemas().get("Pagelimit"));
+
+        //requestBodies
+        assertNull(openAPI.getComponents().getSchemas().get("Body"));
+        assertNull(openAPI.getComponents().getSchemas().get("Inline_response_items200"));
+
+        //components
+        assertNull(openAPI.getComponents().getSchemas().get("Inline_array_items_ArrayTest"));
+
+    }
+
+    @Test
+    public void testIssueFlattenArraySchemaItemsInlineModelTrue() {
+        OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();
+        ParseOptions options = new ParseOptions();
+        options.setResolve(true);
+        options.setFlatten(true);
+        options.setFlattenComposedSchemas(true);
+        options.setCamelCaseFlattenNaming(true);
+        SwaggerParseResult parseResult = openApiParser.readLocation("flattenArrayItems.yaml", null, options);
+        OpenAPI openAPI = parseResult.getOpenAPI();
+
+        //responses
+        assertNotNull(openAPI.getComponents().getSchemas().get("Inline_response_items200"));
+        assertEquals(((ComposedSchema)openAPI.getComponents().getSchemas().get("Inline_response_items200")).getAnyOf().get(0).get$ref(),"#/components/schemas/Macaw");
+        assertNotNull(openAPI.getComponents().getSchemas().get("Inline_response_400"));
+        assertEquals(((ComposedSchema)openAPI.getComponents().getSchemas().get("Inline_response_400")).getAnyOf().get(0).get$ref(),"#/components/schemas/Macaw3");
+
+        //parameters
+        assertNotNull(openAPI.getComponents().getSchemas().get("Inline_parameter_items_bodylimit"));
+        assertEquals(((ComposedSchema)openAPI.getComponents().getSchemas().get("Inline_parameter_items_bodylimit")).getAnyOf().get(0).get$ref(),"#/components/schemas/Macaw1");
+        assertNotNull(openAPI.getComponents().getSchemas().get("Pagelimit"));
+        assertEquals(((ComposedSchema)openAPI.getComponents().getSchemas().get("Pagelimit")).getOneOf().get(0).get$ref(),"#/components/schemas/Macaw2");
+
+        //requestBodies
+        assertNotNull(openAPI.getComponents().getSchemas().get("Body"));
+        assertEquals(((ComposedSchema)openAPI.getComponents().getSchemas().get("Body")).getAllOf().get(1).get$ref(),"#/components/schemas/requestBodiesAllOf_2");
+        assertNotNull(openAPI.getComponents().getSchemas().get("Inline_response_items200"));
+        assertEquals(((ComposedSchema)openAPI.getComponents().getSchemas().get("Inline_body_items_applicationxml_requestBodies")).getAllOf().get(1).get$ref(),"#/components/schemas/ApplicationxmlAllOf_2");
+
+        //components
+        assertNotNull(openAPI.getComponents().getSchemas().get("Inline_array_items_ArrayTest"));
+        assertEquals(((ComposedSchema)openAPI.getComponents().getSchemas().get("Inline_array_items_ArrayTest")).getOneOf().get(1).get$ref(),"#/components/schemas/ArrayTestOneOf_2");
+    }
+
 
     @Test
     public void testCamelCaseFlattenNamingFalse() {
diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/util/InlineModelResolverTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/util/InlineModelResolverTest.java
index b4ad9d70..0004b675 100644
--- a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/util/InlineModelResolverTest.java
+++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/util/InlineModelResolverTest.java
@@ -938,13 +938,10 @@ public class InlineModelResolverTest {
 
         ApiResponses apiResponses = new ApiResponses().addApiResponse("200",apiResponse);
 
-
-
         openAPI.path("/foo/baz", new PathItem()
                 .get(new Operation()
                         .responses(apiResponses)));
 
-
         new InlineModelResolver().flatten(openAPI);
 
         ApiResponse response = openAPI.getPaths().get("/foo/baz").getGet().getResponses().get("200");
diff --git a/modules/swagger-parser-v3/src/test/resources/flattenArrayItems.yaml b/modules/swagger-parser-v3/src/test/resources/flattenArrayItems.yaml
new file mode 100644
index 00000000..f40156b4
--- /dev/null
+++ b/modules/swagger-parser-v3/src/test/resources/flattenArrayItems.yaml
@@ -0,0 +1,83 @@
+openapi: 3.0.2
+info:
+  title: test - OAS3
+  version: 1.0.0
+paths:
+  "/parameters":
+    get:
+      description: ...
+      parameters:
+        - name: bodylimit
+          in: query
+          schema:
+            type: array
+            items:
+              anyOf:
+                - "$ref": "#/components/schemas/Macaw1"
+                - "$ref": "#/components/schemas/Parakeet1"
+        - name: pagelimit
+          in: query
+          schema:
+            oneOf:
+              - "$ref": "#/components/schemas/Macaw2"
+              - "$ref": "#/components/schemas/Parakeet2"
+  /responses:
+    get:
+      responses:
+        '200':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                type: array
+                items:
+                  anyOf:
+                    - "$ref": "#/components/schemas/Macaw"
+                    - "$ref": "#/components/schemas/Parakeet"
+        '400':
+          description: successful operation
+          content:
+            application/json:
+              schema:
+                anyOf:
+                  - "$ref": "#/components/schemas/Macaw3"
+                  - "$ref": "#/components/schemas/Parakeet3"
+  /requestBodies:
+    post:
+      requestBody:
+        content:
+          "application/json":
+            schema:
+              type: object
+              allOf:
+                - $ref: '#/components/schemas/Address'
+                - type: object
+                  required:
+                    - gps
+                  properties:
+                    gps:
+                      type: string
+          "application/xml":
+            schema:
+              type: array
+              items:
+                allOf:
+                  - $ref: '#/components/schemas/Address2'
+                  - type: object
+                    required:
+                      - gps2
+                    properties:
+                      gps2:
+                        type: string
+components:
+  schemas:
+    ArrayTest:
+      type: array
+      items:
+        oneOf:
+          - $ref: "#/components/schemas/Foo"
+          - type: object
+            properties:
+              foo:
+                type: string
+