diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java index 9b95974ef61..26b0b6539a9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java @@ -90,6 +90,7 @@ public class ModelUtils { * @return schemas a list of used schemas */ public static List getAllUsedSchemas(OpenAPI openAPI) { + Map> childrenMap = getChildrenMap(openAPI); List allUsedSchemas = new ArrayList(); visitOpenAPI(openAPI, (s, t) -> { if (s.get$ref() != null) { @@ -97,6 +98,13 @@ public class ModelUtils { if (!allUsedSchemas.contains(ref)) { allUsedSchemas.add(ref); } + if (childrenMap.containsKey(ref)) { + for (String child : childrenMap.get(ref)) { + if (!allUsedSchemas.contains(child)) { + allUsedSchemas.add(child); + } + } + } } }); return allUsedSchemas; @@ -109,6 +117,7 @@ public class ModelUtils { * @return schemas a list of unused schemas */ public static List getUnusedSchemas(OpenAPI openAPI) { + Map> childrenMap = getChildrenMap(openAPI); List unusedSchemas = new ArrayList(); Map schemas = getSchemas(openAPI); @@ -116,7 +125,11 @@ public class ModelUtils { visitOpenAPI(openAPI, (s, t) -> { if (s.get$ref() != null) { - unusedSchemas.remove(getSimpleRef(s.get$ref())); + String ref = getSimpleRef(s.get$ref()); + unusedSchemas.remove(ref); + if (childrenMap.containsKey(ref)) { + unusedSchemas.removeAll(childrenMap.get(ref)); + } } }); return unusedSchemas; @@ -859,6 +872,18 @@ public class ModelUtils { return null; } + public static Map> getChildrenMap(OpenAPI openAPI) { + Map allSchemas = getSchemas(openAPI); + + Map>> groupedByParent = allSchemas.entrySet().stream() + .filter(entry -> isComposedSchema(entry.getValue())) + .collect(Collectors.groupingBy(entry -> getParentName((ComposedSchema) entry.getValue(), allSchemas))); + + return groupedByParent.entrySet().stream() + .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue().stream().map(e -> e.getKey()).collect(Collectors.toList()))); + } + + /** * Get the interfaces from the schema (composed) * diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java index 4f428705a19..8e20b035a55 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/utils/ModelUtilsTest.java @@ -37,7 +37,7 @@ public class ModelUtilsTest { public void testGetAllUsedSchemas() { final OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/unusedSchemas.yaml"); List allUsedSchemas = ModelUtils.getAllUsedSchemas(openAPI); - Assert.assertEquals(allUsedSchemas.size(), 38); + Assert.assertEquals(allUsedSchemas.size(), 41); Assert.assertTrue(allUsedSchemas.contains("SomeObjShared"), "contains 'SomeObjShared'"); Assert.assertTrue(allUsedSchemas.contains("SomeObj1"), "contains 'UnusedObj1'"); @@ -77,21 +77,30 @@ public class ModelUtilsTest { Assert.assertTrue(allUsedSchemas.contains("SomeObj26"), "contains 'SomeObj26'"); Assert.assertTrue(allUsedSchemas.contains("Param27"), "contains 'Param27'"); Assert.assertTrue(allUsedSchemas.contains("Param28"), "contains 'Param28'"); + Assert.assertTrue(allUsedSchemas.contains("Parent30"), "contains 'Parent30'"); + Assert.assertTrue(allUsedSchemas.contains("AChild30"), "contains 'AChild30'"); + Assert.assertTrue(allUsedSchemas.contains("BChild30"), "contains 'BChild30'"); } @Test public void testGetUnusedSchemas() { final OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/3_0/unusedSchemas.yaml"); List unusedSchemas = ModelUtils.getUnusedSchemas(openAPI); - Assert.assertEquals(unusedSchemas.size(), 4); - //UnusedObj is not used at all: + Assert.assertEquals(unusedSchemas.size(), 7); + //UnusedObj1 is not used at all: Assert.assertTrue(unusedSchemas.contains("UnusedObj1"), "contains 'UnusedObj1'"); - //SomeObjUnused is used in a request body that is not used. + //UnusedObj2 is used in a request body that is not used. Assert.assertTrue(unusedSchemas.contains("UnusedObj2"), "contains 'UnusedObj2'"); - //SomeObjUnused is used in a response that is not used. + //UnusedObj3 is used in a response that is not used. Assert.assertTrue(unusedSchemas.contains("UnusedObj3"), "contains 'UnusedObj3'"); - //SomeObjUnused is used in a parameter that is not used. + //UnusedObj4 is used in a parameter that is not used. Assert.assertTrue(unusedSchemas.contains("UnusedObj4"), "contains 'UnusedObj4'"); + //Parent29 is not used at all (only unused children AChild29 and BChild29 are referencing him): + Assert.assertTrue(unusedSchemas.contains("Parent29"), "contains 'Parent29'"); + //AChild29 is not used at all: + Assert.assertTrue(unusedSchemas.contains("AChild29"), "contains 'AChild29'"); + //BChild29 is not used at all: + Assert.assertTrue(unusedSchemas.contains("BChild29"), "contains 'BChild29'"); } @Test diff --git a/modules/openapi-generator/src/test/resources/3_0/unusedSchemas.yaml b/modules/openapi-generator/src/test/resources/3_0/unusedSchemas.yaml index 57027f58027..b6da43e64a8 100644 --- a/modules/openapi-generator/src/test/resources/3_0/unusedSchemas.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/unusedSchemas.yaml @@ -352,6 +352,17 @@ paths: responses: '200': description: OK + /some/p30: + post: + operationId: op27 + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/Parent30" + responses: + '200': + description: OK components: schemas: UnusedObj1: @@ -641,6 +652,56 @@ components: type: string r2: type: string + Parent29: + type: object + required: + - childType + properties: + childType: + type: string + discriminator: + propertyName: childType + AChild29: + allOf: + - $ref: '#/components/schemas/Parent29' + - type: object + properties: + name: + type: string + BChild29: + allOf: + - $ref: '#/components/schemas/Parent29' + - type: object + properties: + firstName: + type: string + lastName: + type: string + Parent30: + type: object + required: + - childType + properties: + childType: + type: string + discriminator: + propertyName: childType + AChild30: + allOf: + - $ref: '#/components/schemas/Parent30' + - type: object + properties: + name: + type: string + BChild30: + allOf: + - $ref: '#/components/schemas/Parent30' + - type: object + properties: + firstName: + type: string + lastName: + type: string SomeObjShared: type: object properties: