Repository: swagger-api/swagger-parser
Pull request #1386 (2020-06-04 18:51:09)

Fix for Flatten Map Schema when Inline ComposedSchema
Fix and test for issue #1385


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 c4559754..a8c704c3 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
@@ -96,7 +96,6 @@ public class InlineModelResolver {
                                         } else if (model instanceof ArraySchema) {
                                             ArraySchema am = (ArraySchema) model;
                                             Schema inner = am.getItems();
-
                                             if (isObjectSchema(inner)) {
                                                 if (inner.getProperties() != null && inner.getProperties().size() > 0) {
                                                     flattenProperties(inner.getProperties(), pathname);
@@ -186,7 +185,7 @@ public class InlineModelResolver {
                                         if (media.getSchema() != null) {
                                             Schema mediaSchema = media.getSchema();
                                             if (isObjectSchema(mediaSchema)) {
-                                                if (mediaSchema.getProperties() != null && mediaSchema.getProperties().size() > 0) {
+                                                if (mediaSchema.getProperties() != null && mediaSchema.getProperties().size() > 0 || mediaSchema instanceof ComposedSchema) {
                                                     String modelName = resolveModelName(mediaSchema.getTitle(), "inline_response_" + key);
                                                     String existing = matchGenerated(mediaSchema);
                                                     if (existing != null) {
@@ -196,63 +195,31 @@ 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.getAdditionalProperties() != null && !(mediaSchema.getAdditionalProperties() instanceof Boolean)) {
+                                                    Schema innerProperty = (Schema) mediaSchema.getAdditionalProperties();
+                                                    if (isObjectSchema(innerProperty)) {
+                                                        key = "inline_response_map" + key;
+                                                        flattenMapSchema(innerProperty, key, pathname, mediaSchema);
+                                                    } else if (innerProperty instanceof ArraySchema) {
+                                                        ArraySchema arraySchema = (ArraySchema) innerProperty;
+                                                        Schema inner = arraySchema.getItems();
+                                                        if (isObjectSchema(inner)) {
+                                                            key = "inline_response_map_items" + key;
+                                                            flattenMapSchema(inner,key,pathname,mediaSchema);
+                                                        }
                                                     }
                                                 }
-
                                             }else if (mediaSchema instanceof ArraySchema) {
                                                 ArraySchema ap = (ArraySchema) mediaSchema;
                                                 Schema inner = ap.getItems();
-
                                                 if (isObjectSchema(inner)) {
-                                                    if (inner.getProperties() != null && inner.getProperties().size() > 0) {
-                                                        flattenProperties(inner.getProperties(), pathname);
-                                                        String modelName = resolveModelName(inner.getTitle(),
-                                                                "inline_response_" + key);
-                                                        String existing = matchGenerated(inner);
-                                                        if (existing != null) {
-                                                            ap.setItems(this.makeRefProperty(existing, inner));
-                                                        } else {
-                                                            ap.setItems(this.makeRefProperty(modelName, inner));
-                                                            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);
-                                                        }
-                                                    }
+                                                    flattenArraySchema(inner, key, pathname, ap);
                                                 }
-
-                                            } else if (mediaSchema.getAdditionalProperties() != null && mediaSchema.getAdditionalProperties() instanceof Schema) {
-
+                                            } else if (mediaSchema.getAdditionalProperties() != null && !(mediaSchema.getAdditionalProperties() instanceof Boolean)) {
                                                 Schema innerProperty = (Schema) mediaSchema.getAdditionalProperties();
                                                 if (isObjectSchema(innerProperty)) {
-                                                    if (innerProperty.getProperties() != null && innerProperty.getProperties().size() > 0) {
-                                                        flattenProperties(innerProperty.getProperties(), pathname);
-                                                        String modelName = resolveModelName(innerProperty.getTitle(),
-                                                                "inline_response_" + key);
-                                                        String existing = matchGenerated(innerProperty);
-                                                        if (existing != null) {
-                                                            mediaSchema.setAdditionalProperties(new Schema().$ref(existing));
-                                                        } else {
-                                                            mediaSchema.setAdditionalProperties(new Schema().$ref(modelName));
-                                                            addGenerated(modelName, innerProperty);
-                                                            openAPI.getComponents().addSchemas(modelName, innerProperty);
-                                                        }
-                                                    }
+                                                    key = "inline_response_map" + key;
+                                                    flattenMapSchema(innerProperty, key, pathname, mediaSchema);
                                                 }
                                             }
                                         }
@@ -301,7 +268,6 @@ public class InlineModelResolver {
                 } else if (model instanceof ComposedSchema) {
                     ComposedSchema composedSchema = (ComposedSchema) model;
                     String inlineModelName = "";
-
                     List<Schema> list = null;
                     if (composedSchema.getAllOf() != null) {
                         list  = composedSchema.getAllOf();
@@ -334,6 +300,54 @@ public class InlineModelResolver {
         }
     }
 
+    private void flattenArraySchema(Schema inner, String key, String pathname, ArraySchema ap) {
+        if (inner.getProperties() != null && inner.getProperties().size() > 0) {
+            flattenProperties(inner.getProperties(), pathname);
+            key = "inline_response_" + key;
+            String modelName = resolveModelName(inner.getTitle(), key);
+            String existing = matchGenerated(inner);
+            if (existing != null) {
+                ap.setItems(this.makeRefProperty(existing, inner));
+            } else {
+                ap.setItems(this.makeRefProperty(modelName, inner));
+                addGenerated(modelName, inner);
+                openAPI.getComponents().addSchemas(modelName, inner);
+            }
+        }else if (inner instanceof ComposedSchema && this.flattenComposedSchemas){
+            flattenComposedSchema(inner,key);
+            if (inner.get$ref() == null) {
+                key = "inline_response_items" + key;
+                String modelName = resolveModelName(inner.getTitle(), key );
+                ap.setItems(this.makeRefProperty(modelName, inner));
+                addGenerated(modelName, inner);
+                openAPI.getComponents().addSchemas(modelName, inner);
+            }
+        }
+    }
+
+
+    private void flattenMapSchema(Schema innerProperty, String key, String pathname, Schema mediaSchema) {
+        if (innerProperty.getProperties() != null && innerProperty.getProperties().size() > 0) {
+            flattenProperties(innerProperty.getProperties(), pathname);
+            String modelName = resolveModelName(innerProperty.getTitle(), key);
+            String existing = matchGenerated(innerProperty);
+            if (existing != null) {
+                mediaSchema.setAdditionalProperties(new Schema().$ref(existing));
+            } else {
+                mediaSchema.setAdditionalProperties(new Schema().$ref(modelName));
+                addGenerated(modelName, innerProperty);
+                openAPI.getComponents().addSchemas(modelName, innerProperty);
+            }
+        }else if (innerProperty instanceof ComposedSchema && this.flattenComposedSchemas){
+            flattenComposedSchema(innerProperty,key);
+            if (innerProperty.get$ref() == null) {
+                String modelName = resolveModelName(innerProperty.getTitle(), key);
+                mediaSchema.setAdditionalProperties(new Schema().$ref(modelName));
+                addGenerated(modelName, innerProperty);
+                openAPI.getComponents().addSchemas(modelName, innerProperty);
+            }
+        }
+    }
 
 
     /**
@@ -418,12 +432,9 @@ public class InlineModelResolver {
         for (String key : properties.keySet()) {
             Schema property = properties.get(key);
             if (isObjectSchema(property) && property.getProperties() != null && property.getProperties().size() > 0) {
-
                 String modelName = resolveModelName(property.getTitle(), path + "_" + key);
                 Schema model = createModelFromProperty(property, modelName);
-
                 String existing = matchGenerated(model);
-
                 if (existing != null) {
                     propsToUpdate.put(key, new Schema().$ref(existing));
                 } else {
@@ -435,7 +446,6 @@ public class InlineModelResolver {
             } else if (property instanceof ArraySchema) {
                 ArraySchema ap = (ArraySchema) property;
                 Schema inner = ap.getItems();
-
                 if (isObjectSchema(inner)) {
                     if (inner.getProperties() != null && inner.getProperties().size() > 0) {
                         flattenProperties(inner.getProperties(), path);
@@ -449,23 +459,9 @@ public class InlineModelResolver {
                             addGenerated(modelName, innerModel);
                             openAPI.getComponents().addSchemas(modelName, innerModel);
                         }
-                    }else if (inner instanceof ComposedSchema && this.flattenComposedSchemas){
-                        ComposedSchema composedSchema = (ComposedSchema) inner;
+                    }else if (inner instanceof ComposedSchema && this.flattenComposedSchemas) {
+                        flattenComposedSchema(inner,key);
                         String modelName = resolveModelName(inner.getTitle(), path + "_" + key);
-                        List<Schema> list = null;
-                        if (composedSchema.getAllOf() != null) {
-                            list  = composedSchema.getAllOf();
-                        }else if (composedSchema.getAnyOf() != null) {
-                            list  = composedSchema.getAnyOf();
-                        }else if (composedSchema.getOneOf() != null) {
-                            list  = composedSchema.getOneOf();
-                        }
-                        for(int i= 0; i<list.size();i++){
-                            if (list.get(i).getProperties()!= null){
-                                flattenProperties(list.get(i).getProperties(), modelName);
-                            }
-                        }
-
                         Schema innerModel = createModelFromProperty(inner, modelName);
                         String existing = matchGenerated(innerModel);
                         if (existing != null) {
@@ -476,11 +472,9 @@ public class InlineModelResolver {
                             openAPI.getComponents().addSchemas(modelName, innerModel);
                         }
                     }
-
                 }
-            } else if (property.getAdditionalProperties() != null && property.getAdditionalProperties() instanceof Schema) {
+            } else if (property.getAdditionalProperties() != null && !(property.getAdditionalProperties() instanceof Boolean)) {
                 Schema inner = (Schema) property.getAdditionalProperties();
-
                 if (isObjectSchema(inner)) {
                     if (inner.getProperties() != null && inner.getProperties().size() > 0) {
                         flattenProperties(inner.getProperties(), path);
@@ -623,11 +617,9 @@ public class InlineModelResolver {
         ArraySchema model = new ArraySchema();
         model.setDescription(description);
         model.setExample(example);
-        if (object.getAdditionalProperties() != null && object.getAdditionalProperties() instanceof Schema) {
+        if (object.getAdditionalProperties() != null && !(object.getAdditionalProperties() instanceof Boolean)) {
             model.setItems((Schema)  object.getAdditionalProperties());
         }
-
-
         return model;
     }
 


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 2843327e..2b780b7c 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,27 @@ public class OpenAPIV3ParserTest {
     protected int serverPort = getDynamicPort();
     protected WireMockServer wireMockServer;
 
+
+    @Test
+    public void testIssueFlattenAdditionalPropertiesSchemaInlineModelTrue() {
+        OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();
+        ParseOptions options = new ParseOptions();
+        options.setResolve(true);
+        options.setFlatten(true);
+        options.setFlattenComposedSchemas(true);
+        options.setCamelCaseFlattenNaming(true);
+        SwaggerParseResult parseResult = openApiParser.readLocation("additionalPropertiesFlatten.yaml", null, options);
+        OpenAPI openAPI = parseResult.getOpenAPI();
+
+        //responses
+        assertNotNull(openAPI.getComponents().getSchemas().get("Inline_response_map200"));
+        assertEquals(((ComposedSchema)openAPI.getComponents().getSchemas().get("Inline_response_map200")).getOneOf().get(0).get$ref(),"#/components/schemas/Macaw1");
+        assertNotNull(openAPI.getComponents().getSchemas().get("Inline_response_map_items404"));
+        assertEquals(((ComposedSchema)openAPI.getComponents().getSchemas().get("Inline_response_map_items404")).getAnyOf().get(0).get$ref(),"#/components/schemas/Macaw2");
+
+    }
+
+
     @Test
     public void testIssueFlattenArraySchemaItemsInlineModelFalse() {
         OpenAPIV3Parser openApiParser = new OpenAPIV3Parser();
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 0004b675..f306114f 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
@@ -951,7 +951,7 @@ public class InlineModelResolverTest {
         assertEquals("ext-prop", property.getExtensions().get("x-ext"));
         assertTrue(openAPI.getComponents().getSchemas().size() == 1);
 
-        Schema inline = openAPI.getComponents().getSchemas().get("inline_response_200");
+        Schema inline = openAPI.getComponents().getSchemas().get("inline_response_map200");
         assertTrue(inline instanceof Schema);
         assertNotNull(inline.getProperties().get("name"));
         assertTrue(inline.getProperties().get("name") instanceof StringSchema);
diff --git a/modules/swagger-parser-v3/src/test/resources/additionalPropertiesFlatten.yaml b/modules/swagger-parser-v3/src/test/resources/additionalPropertiesFlatten.yaml
new file mode 100644
index 00000000..c1157656
--- /dev/null
+++ b/modules/swagger-parser-v3/src/test/resources/additionalPropertiesFlatten.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.2
+info:
+  title: test - OAS3
+  version: 1.0.0
+paths:
+  /responses:
+    get:
+      responses:
+        '200':
+          description: it works!
+          content:
+            application/json:
+              schema:
+                additionalProperties:
+                  oneOf:
+                    - "$ref": "#/components/schemas/Macaw1"
+                    - "$ref": "#/components/schemas/Parakeet1"
+        '404':
+          description: it works!
+          content:
+            application/json:
+              schema:
+                additionalProperties:
+                  type: array
+                  items:
+                    anyOf:
+                      - "$ref": "#/components/schemas/Macaw2"
+                      - "$ref": "#/components/schemas/Parakeet2"
+
diff --git a/modules/swagger-parser-v3/src/test/resources/flattenArrayItems.yaml b/modules/swagger-parser-v3/src/test/resources/flattenArrayItems.yaml
index f40156b4..5063fa5b 100644
--- a/modules/swagger-parser-v3/src/test/resources/flattenArrayItems.yaml
+++ b/modules/swagger-parser-v3/src/test/resources/flattenArrayItems.yaml
@@ -42,6 +42,26 @@ paths:
                 anyOf:
                   - "$ref": "#/components/schemas/Macaw3"
                   - "$ref": "#/components/schemas/Parakeet3"
+        '404':
+          description: it works!
+          content:
+            application/json:
+              schema:
+                additionalProperties:
+                    oneOf:
+                      - "$ref": "#/components/schemas/Macaw4"
+                      - "$ref": "#/components/schemas/Parakeet4"
+        '500':
+          description: it works!
+          content:
+            application/json:
+              schema:
+                additionalProperties:
+                  type: array
+                  items:
+                    anyOf:
+                      - "$ref": "#/components/schemas/Macaw5"
+                      - "$ref": "#/components/schemas/Parakeet5"
   /requestBodies:
     post:
       requestBody: