From dd946ffb2a3a2782a7c01d466a3914366d17b814 Mon Sep 17 00:00:00 2001 From: Kejia Liu Date: Sat, 1 Aug 2026 13:23:30 +0800 Subject: [PATCH] fix: keep allOf members that have properties alongside a validation keyword A schema carrying `properties` but omitting `type: object`, next to a validation keyword, was classified as "validation without a type" and silently dropped from allOf. Every model inheriting it lost those properties -- no error, no warning, just a smaller model and a broken JSON contract: Parent: minProperties: 2 # validation keyword, no `type: object` properties: alpha: { type: string } beta: { type: string } Child: allOf: [{ $ref: '#/components/schemas/Parent' }] properties: gamma: { type: string } // before: Child has only `gamma` // after: Child has `alpha`, `beta`, `gamma` OpenAPINormalizer.removeUnsupportedSchemasFromAllOf() drops any allOf member for which ModelUtils.isUnsupportedSchema() returns true, and that check was: schema.getTypes() == null && hasValidation(schema) Omitting `type: object` next to `properties` is very common in 3.0 specs, and getTypes() is only populated for 3.1, so in practice any 3.0 schema with a validation keyword and no explicit type was judged unsupported -- including ones that are plainly models. Require that the schema also has no properties (and check getType() as well, so the 3.0 case is recognised at all). A schema with properties is a model, not bare validation. Regenerating all 787 sample configs produces no diff. --- .../codegen/utils/ModelUtils.java | 12 ++++++- .../codegen/OpenAPINormalizerTest.java | 35 +++++++++++++++++++ ...member-with-validation-and-properties.yaml | 32 +++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/allof-member-with-validation-and-properties.yaml 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