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 47bf588ae89f..ba4815d90f99 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 @@ -2661,8 +2661,18 @@ public static boolean isUnsupportedSchema(OpenAPI openAPI, Schema schema) { // dereference the schema schema = ModelUtils.getReferencedSchema(openAPI, schema); - if (schema.getTypes() == null && hasValidation(schema)) { + if (schema.getTypes() == null && schema.getType() == null && hasValidation(schema) + && (schema.getProperties() == null || schema.getProperties().isEmpty())) { // just validation without type + // + // A schema that carries properties is a model, even when it omits `type: object` -- + // which is very common in 3.0 specs, e.g. `minProperties: 2` next to `properties: {...}`. + // Checking `getTypes() == null && hasValidation(schema)` alone classified those as + // unsupported, and as an allOf member their properties were then dropped from the + // composed model without any warning. + // + // getTypes() is only populated for 3.1, so getType() has to be checked as well for the + // 3.0 case to be recognised. return true; } else if (schema.getIf() != null && schema.getThen() != null) { // if, then in 3.1 spec 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 e9e0ad0d9563..e8493bb00ff4 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 @@ -38,6 +38,41 @@ public class OpenAPINormalizerTest { private static final String X_PARENT = "x-parent"; private static final String X_INTERNAL = "x-internal"; + @Test + public void testAllOfMemberWithValidationAndPropertiesIsKept() { + // A schema carrying `properties` but omitting `type: object`, next to a validation keyword, + // is a model -- not "validation without a type". It used to be classified as unsupported and + // silently dropped from allOf, so every model inheriting it lost those properties. + OpenAPI openAPI = TestUtils.parseSpec( + "src/test/resources/3_0/allof-member-with-validation-and-properties.yaml"); + + Schema child = openAPI.getComponents().getSchemas().get("Child"); + assertNotNull(child.getAllOf()); + assertTrue(refsParent(child), "precondition: Child starts out referencing Parent"); + + OpenAPINormalizer openAPINormalizer = new OpenAPINormalizer(openAPI, new HashMap<>()); + openAPINormalizer.normalize(); + + Schema normalizedChild = openAPI.getComponents().getSchemas().get("Child"); + assertNotNull(normalizedChild.getAllOf(), + "the whole allOf was dropped, so Child lost the inherited properties"); + assertTrue(refsParent(normalizedChild), + "the allOf member carrying alpha/beta was dropped, so Child lost those properties"); + } + + private static boolean refsParent(Schema schema) { + if (schema.getAllOf() == null) { + return false; + } + for (Object item : schema.getAllOf()) { + String ref = ((Schema) item).get$ref(); + if (ref != null && ref.endsWith("/Parent")) { + return true; + } + } + return false; + } + @Test public void testOpenAPINormalizerOtherThanObjectWithProperties() { diff --git a/modules/openapi-generator/src/test/resources/3_0/allof-member-with-validation-and-properties.yaml b/modules/openapi-generator/src/test/resources/3_0/allof-member-with-validation-and-properties.yaml new file mode 100644 index 000000000000..0d34de1f7e7d --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/allof-member-with-validation-and-properties.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.3 +info: + title: allOf member with validation and properties + version: 1.0.0 +paths: + /child: + get: + operationId: getChild + responses: + "200": + description: ok + content: + application/json: + schema: + $ref: "#/components/schemas/Child" +components: + schemas: + # `properties` without an explicit `type: object`, next to a validation keyword. + # This is a model, not "validation without a type". + Parent: + minProperties: 2 + properties: + alpha: + type: string + beta: + type: string + Child: + allOf: + - $ref: "#/components/schemas/Parent" + properties: + gamma: + type: string