Skip to content

Add logic to handle any type (3.1) with additional properties - #24559

Merged
wing328 merged 6 commits into
masterfrom
better-nullable
Aug 1, 2026
Merged

wing328 merged 6 commits into
masterfrom
better-nullable

Conversation

@wing328

@wing328 wing328 commented Aug 1, 2026 •

Copy link
Copy Markdown
Member

a follow up PR to #24558

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

Summary by cubic

Handle OpenAPI 3.1 “any type” schemas by converting them to empty schemas while preserving additionalProperties and metadata (title/description/examples), removing misleading nullable: false logs, and tightening null-type detection.

  • Bug Fixes
    • Preserve additionalProperties and copy metadata when normalizing 3.1 “any type” (no type/types) to an empty schema in OpenAPINormalizer.
    • Remove error/warn logs about nullable: false on “any type” in DefaultCodegen for models and properties.
    • In ModelUtils.isNullTypeSchema, treat schemas with properties or additionalProperties: true as not null-type.
    • Fix ModelUtils.copyMetadata to copy examples via setExamples(...).

Written for commit f832ba7. Summary will update on new commits.

Review in cubic

@wing328
wing328 marked this pull request as ready for review August 1, 2026 15:53
@wing328 wing328 changed the title add logic to handle any type (3.1) with additional properties Add logic to handle any type (3.1) with additional properties Aug 1, 2026
@wing328 wing328 added this to the 7.25.0 milestone Aug 1, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java:3162">
P2: 3.1 any-type models with no explicit `nullable` flag are still marked non-nullable here, even though an empty JSON Schema accepts `null` and `updatePropertyForAnyType` marks the corresponding property nullable. Including the any-type condition would keep model metadata consistent and prevent generators that use `CodegenModel.isNullable` from producing non-nullable wrappers or serialization behavior.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

m.dataType = getSchemaType(schema);
}
if (!ModelUtils.isAnyType(schema) && Boolean.TRUE.equals(schema.getNullable())) {
if (ModelUtils.isNullable(schema)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: 3.1 any-type models with no explicit nullable flag are still marked non-nullable here, even though an empty JSON Schema accepts null and updatePropertyForAnyType marks the corresponding property nullable. Including the any-type condition would keep model metadata consistent and prevent generators that use CodegenModel.isNullable from producing non-nullable wrappers or serialization behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java, line 3162:

<comment>3.1 any-type models with no explicit `nullable` flag are still marked non-nullable here, even though an empty JSON Schema accepts `null` and `updatePropertyForAnyType` marks the corresponding property nullable. Including the any-type condition would keep model metadata consistent and prevent generators that use `CodegenModel.isNullable` from producing non-nullable wrappers or serialization behavior.</comment>

<file context>
@@ -3165,7 +3159,7 @@ public CodegenModel fromModel(String name, Schema schema) {
             m.dataType = getSchemaType(schema);
         }
-        if (!ModelUtils.isAnyType(schema) && Boolean.TRUE.equals(schema.getNullable())) {
+        if (ModelUtils.isNullable(schema)) {
             m.isNullable = Boolean.TRUE;
         }
</file context>
Suggested change
if (ModelUtils.isNullable(schema)) {
if (ModelUtils.isNullable(schema) || ModelUtils.isAnyType(schema)) {

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java:3163">
P1: The new condition `ModelUtils.isNullable(schema) || ModelUtils.isAnyType(schema)` marks `m.isNullable = TRUE` for every schema where `isAnyType` is true, and `isAnyType` returns true for any schema without an explicit `type` keyword — including all `allOf`/`oneOf`/`anyOf` composed models that carry only the composed keyword. Since `fromModel` runs this line before the composed-schema dispatch and `updateModelForComposedSchema` only ever sets nullable to true (never resets it), all inheritance/union models now become nullable even when the spec does not define them as nullable. This is much broader than the intended '3.1 any type is nullable' behavior and will alter generated output for many existing models; it also silently overrides an explicit `nullable: false`. Consider limiting the anyType nullable assignment to non-composed any-type schemas (and handling composed nullability through the existing composed-schema path) rather than applying it via the broad `isAnyType` check in `fromModel`.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

}
if (!ModelUtils.isAnyType(schema) && Boolean.TRUE.equals(schema.getNullable())) {
// nullable or any type (which is nullable by default in 3.1 spec)
if (ModelUtils.isNullable(schema) || ModelUtils.isAnyType(schema)) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The new condition ModelUtils.isNullable(schema) || ModelUtils.isAnyType(schema) marks m.isNullable = TRUE for every schema where isAnyType is true, and isAnyType returns true for any schema without an explicit type keyword — including all allOf/oneOf/anyOf composed models that carry only the composed keyword. Since fromModel runs this line before the composed-schema dispatch and updateModelForComposedSchema only ever sets nullable to true (never resets it), all inheritance/union models now become nullable even when the spec does not define them as nullable. This is much broader than the intended '3.1 any type is nullable' behavior and will alter generated output for many existing models; it also silently overrides an explicit nullable: false. Consider limiting the anyType nullable assignment to non-composed any-type schemas (and handling composed nullability through the existing composed-schema path) rather than applying it via the broad isAnyType check in fromModel.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java, line 3163:

<comment>The new condition `ModelUtils.isNullable(schema) || ModelUtils.isAnyType(schema)` marks `m.isNullable = TRUE` for every schema where `isAnyType` is true, and `isAnyType` returns true for any schema without an explicit `type` keyword — including all `allOf`/`oneOf`/`anyOf` composed models that carry only the composed keyword. Since `fromModel` runs this line before the composed-schema dispatch and `updateModelForComposedSchema` only ever sets nullable to true (never resets it), all inheritance/union models now become nullable even when the spec does not define them as nullable. This is much broader than the intended '3.1 any type is nullable' behavior and will alter generated output for many existing models; it also silently overrides an explicit `nullable: false`. Consider limiting the anyType nullable assignment to non-composed any-type schemas (and handling composed nullability through the existing composed-schema path) rather than applying it via the broad `isAnyType` check in `fromModel`.</comment>

<file context>
@@ -3165,7 +3159,8 @@ public CodegenModel fromModel(String name, Schema schema) {
         }
-        if (!ModelUtils.isAnyType(schema) && Boolean.TRUE.equals(schema.getNullable())) {
+        // nullable or any type (which is nullable by default in 3.1 spec)
+        if (ModelUtils.isNullable(schema) || ModelUtils.isAnyType(schema)) {
             m.isNullable = Boolean.TRUE;
         }
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java:2193">
P2: Any-type schemas that only declare `additionalProperties` still bypass this new preservation logic: `normalizeSchema` classifies them as null schemas and returns the original before `processNormalize31Spec` runs. This leaves the reported OAS 3.1 case unhandled unless the schema also has a description or another field that prevents the early return; the null-schema guard should exclude schemas with `additionalProperties` or the handling should occur before that return.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Schema sc = new Schema<>();

// additional properties set?
if (schema.getAdditionalProperties() != null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Any-type schemas that only declare additionalProperties still bypass this new preservation logic: normalizeSchema classifies them as null schemas and returns the original before processNormalize31Spec runs. This leaves the reported OAS 3.1 case unhandled unless the schema also has a description or another field that prevents the early return; the null-schema guard should exclude schemas with additionalProperties or the handling should occur before that return.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java, line 2193:

<comment>Any-type schemas that only declare `additionalProperties` still bypass this new preservation logic: `normalizeSchema` classifies them as null schemas and returns the original before `processNormalize31Spec` runs. This leaves the reported OAS 3.1 case unhandled unless the schema also has a description or another field that prevents the early return; the null-schema guard should exclude schemas with `additionalProperties` or the handling should occur before that return.</comment>

<file context>
@@ -2182,13 +2182,19 @@ protected Schema processNormalize31Spec(Schema schema, Set<Schema> visitedSchema
+            Schema sc = new Schema<>();
+
+            // additional properties set?
+            if (schema.getAdditionalProperties() != null) {
+                sc.setAdditionalProperties(schema.getAdditionalProperties());
+            }
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java:2624">
P1: Schemas using the `Schema.booleanSchemaValue(false)` representation of `additionalProperties: false` can now be rewritten as maps with nullable any values: this check lets the normalizer process the parent, then misclassifies the boolean-false child as a null schema. Preserving the false form, or excluding boolean-schema values from the null-value conversion, keeps additional properties disallowed.</violation>
</file>

<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java:2193">
P3: When the normalizer converts a type-less 3.1 schema that has `additionalProperties` (e.g. a free-form map with a `description`, `title`, `default` or `example`), it returns a brand-new empty `Schema` and copies only `additionalProperties`, silently dropping all other sibling metadata such as description, title, default, example, and extensions. For a map schema like `{ description: 'a map', additionalProperties: { type: string } }` this means the description no longer reaches generated code. Since this branch is being extended specifically to preserve `additionalProperties`, it would be more consistent to carry over the remaining metadata too (for example, reusing an existing metadata-copy helper).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

// schema with properties
if (schema.getProperties() != null) {
// schema with properties or additional properties
if (schema.getProperties() != null || schema.getAdditionalProperties() != null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Schemas using the Schema.booleanSchemaValue(false) representation of additionalProperties: false can now be rewritten as maps with nullable any values: this check lets the normalizer process the parent, then misclassifies the boolean-false child as a null schema. Preserving the false form, or excluding boolean-schema values from the null-value conversion, keeps additional properties disallowed.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java, line 2624:

<comment>Schemas using the `Schema.booleanSchemaValue(false)` representation of `additionalProperties: false` can now be rewritten as maps with nullable any values: this check lets the normalizer process the parent, then misclassifies the boolean-false child as a null schema. Preserving the false form, or excluding boolean-schema values from the null-value conversion, keeps additional properties disallowed.</comment>

<file context>
@@ -2620,8 +2620,8 @@ public static boolean isNullTypeSchema(OpenAPI openAPI, Schema schema) {
-        // schema with properties
-        if (schema.getProperties() != null) {
+        // schema with properties or additional properties
+        if (schema.getProperties() != null || schema.getAdditionalProperties() != null) {
             return false;
         }
</file context>

Schema sc = new Schema<>();

// additional properties set?
if (schema.getAdditionalProperties() != null) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: When the normalizer converts a type-less 3.1 schema that has additionalProperties (e.g. a free-form map with a description, title, default or example), it returns a brand-new empty Schema and copies only additionalProperties, silently dropping all other sibling metadata such as description, title, default, example, and extensions. For a map schema like { description: 'a map', additionalProperties: { type: string } } this means the description no longer reaches generated code. Since this branch is being extended specifically to preserve additionalProperties, it would be more consistent to carry over the remaining metadata too (for example, reusing an existing metadata-copy helper).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java, line 2193:

<comment>When the normalizer converts a type-less 3.1 schema that has `additionalProperties` (e.g. a free-form map with a `description`, `title`, `default` or `example`), it returns a brand-new empty `Schema` and copies only `additionalProperties`, silently dropping all other sibling metadata such as description, title, default, example, and extensions. For a map schema like `{ description: 'a map', additionalProperties: { type: string } }` this means the description no longer reaches generated code. Since this branch is being extended specifically to preserve `additionalProperties`, it would be more consistent to carry over the remaining metadata too (for example, reusing an existing metadata-copy helper).</comment>

<file context>
@@ -2182,13 +2182,19 @@ protected Schema processNormalize31Spec(Schema schema, Set<Schema> visitedSchema
+            Schema sc = new Schema<>();
+
+            // additional properties set?
+            if (schema.getAdditionalProperties() != null) {
+                sc.setAdditionalProperties(schema.getAdditionalProperties());
+            }
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java:2193">
P2: Schemas using `examples` are normalized with those values in the singular `example` field, so generated examples and downstream consumers can interpret the metadata incorrectly. The copy should preserve `examples` via `setExamples` (and leave `example` sourced only from `getExample`).</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Schema sc = new Schema<>();

// copy description, title, etc
ModelUtils.copyMetadata(schema, sc);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Schemas using examples are normalized with those values in the singular example field, so generated examples and downstream consumers can interpret the metadata incorrectly. The copy should preserve examples via setExamples (and leave example sourced only from getExample).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java, line 2193:

<comment>Schemas using `examples` are normalized with those values in the singular `example` field, so generated examples and downstream consumers can interpret the metadata incorrectly. The copy should preserve `examples` via `setExamples` (and leave `example` sourced only from `getExample`).</comment>

<file context>
@@ -2182,13 +2182,22 @@ protected Schema processNormalize31Spec(Schema schema, Set<Schema> visitedSchema
+            Schema sc = new Schema<>();
+
+            // copy description, title, etc
+            ModelUtils.copyMetadata(schema, sc);
+
+            // additional properties set?
</file context>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 3 files

Re-trigger cubic

@wing328
wing328 merged commit 92936fd into master Aug 1, 2026
15 checks passed
@wing328
wing328 deleted the better-nullable branch August 1, 2026 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant