Skip to content

fix(core): avoid dangling request-model import for skipped form models - #24807

Open
SubhamAshok wants to merge 5 commits into
OpenAPITools:masterfrom
SubhamAshok:fix/24727-dangling-request-model-import
Open

fix(core): avoid dangling request-model import for skipped form models#24807
SubhamAshok wants to merge 5 commits into
OpenAPITools:masterfrom
SubhamAshok:fix/24727-dangling-request-model-import

Conversation

@SubhamAshok

@SubhamAshok SubhamAshok commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

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
    
  • Target language technical committee: @wing328 @diyfr @jpfinne

Description of the change

Fixes #24727.

When an operation's requestBody defines multiple content types (for example, application/json and multipart/form-data), InlineModelResolver creates an inline model for the form schema. In DefaultCodegen#getContent, all media types in requestBody.getContent() were unconditionally adding their schema types to imports.

When skipFormModel is enabled (default true), DefaultGenerator skips generating the model class for schemas used only in form parameters. This caused the generated API interface to emit a dangling import for a non-existent model class and fail compilation.

This fix updates DefaultCodegen#getContent to avoid registering imports for form/multipart media types when skipFormModel is enabled.

Tests

  • Added testMultipleRequestBodyContentTypesDanglingImport_issue24727 and testMultipleRequestBodyContentTypesWithSkipFormModelFalse_issue24727 in SpringCodegenTest.java.
  • Verified SpringCodegenTest, DefaultCodegenTest, and ModelUtilsTest all pass.

Summary by cubic

Fixes #24727 so DefaultCodegen#getContent no longer emits dangling imports for form models skipped by skipFormModel (default true). Previously every form/multipart schema type was added to imports even when its model class wasn't generated, which broke compilation; now only schemas used exclusively in form parameters are filtered out, and shared form models still generate.

Bug Fixes

  • Adds isFormContentType, isSkipFormModel, and a cached getSchemasUsedOnlyInFormParam lookup in DefaultCodegen.
  • DefaultGenerator resolves skipFormModel once in configureGeneratorProperties, stores the normalized value in additionalProperties, and generateModels reads it from there so generation and import filtering use the same value.
  • Adds three SpringCodegenTest cases and fixtures issue_24727.yaml and issue_24727_shared.yaml covering default behavior, skipFormModel=false, and shared form models.

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

Review in cubic

@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/DefaultCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java:8311">
P2: The import-suppression signal here diverges from how DefaultGenerator decides whether a form model is actually generated. DefaultGenerator skips a model only when it is listed in `unusedModels` (used exclusively as a form parameter), and it resolves the `skipFormModel` default through `getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true)`, which honors per-generator `generatorPropertyDefaults`. The new `isSkipFormModel()` instead always falls back to `GlobalSettings` default `"true"` and the condition suppresses imports purely on content type, ignoring whether the model is in `unusedModels`. When a multipart request-body schema references a model that is genuinely generated (used elsewhere, so not in `unusedModels`), this condition drops its import, producing a missing-import failure — the inverse of the bug being fixed. The two signals should match.</violation>
</file>

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

Re-trigger cubic


cmtContent.put(contentType, codegenMt);
if (schemaProp != null) {
if (schemaProp != null && (!isFormContentType(contentType) || !isSkipFormModel())) {

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: The import-suppression signal here diverges from how DefaultGenerator decides whether a form model is actually generated. DefaultGenerator skips a model only when it is listed in unusedModels (used exclusively as a form parameter), and it resolves the skipFormModel default through getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true), which honors per-generator generatorPropertyDefaults. The new isSkipFormModel() instead always falls back to GlobalSettings default "true" and the condition suppresses imports purely on content type, ignoring whether the model is in unusedModels. When a multipart request-body schema references a model that is genuinely generated (used elsewhere, so not in unusedModels), this condition drops its import, producing a missing-import failure — the inverse of the bug being fixed. The two signals should match.

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 8311:

<comment>The import-suppression signal here diverges from how DefaultGenerator decides whether a form model is actually generated. DefaultGenerator skips a model only when it is listed in `unusedModels` (used exclusively as a form parameter), and it resolves the `skipFormModel` default through `getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true)`, which honors per-generator `generatorPropertyDefaults`. The new `isSkipFormModel()` instead always falls back to `GlobalSettings` default `"true"` and the condition suppresses imports purely on content type, ignoring whether the model is in `unusedModels`. When a multipart request-body schema references a model that is genuinely generated (used elsewhere, so not in `unusedModels`), this condition drops its import, producing a missing-import failure — the inverse of the bug being fixed. The two signals should match.</comment>

<file context>
@@ -8285,7 +8308,7 @@ protected LinkedHashMap<String, CodegenMediaType> getContent(Content content, Se
 
             cmtContent.put(contentType, codegenMt);
-            if (schemaProp != null) {
+            if (schemaProp != null && (!isFormContentType(contentType) || !isSkipFormModel())) {
                 addImports(imports, schemaProp.getImports(true, importBaseType, generatorMetadata.getFeatureSet()));
             }
</file context>

@HDPark95 HDPark95 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.

Verified at 3e336bb: reverting only DefaultCodegen.java fails the new dangling-import test, so it is pinned.

isSkipFormModel() reads GlobalSettings only, but DefaultGenerator resolves skipFormModel as GlobalSettings -> generatorPropertyDefaults -> true. On the issue spec with setGeneratorPropertyDefault(SKIP_FORM_MODEL, "false") I measured: model generated, import dropped, the opposite of what your second test asserts. That test also passes on unmodified master, so it does not cover this path.

Could the guard reuse that resolution, or getSchemasUsedOnlyInFormParam?

@SubhamAshok

Copy link
Copy Markdown
Contributor Author

Thanks for the feedback! I updated the implementation:

  1. Aligned \skipFormModel\ handling across \DefaultGenerator\ and \DefaultCodegen\ so settings passed via \generatorPropertyDefaults\ take effect properly.
  2. In \DefaultCodegen.getContent(), imports are now filtered against \ModelUtils.getSchemasUsedOnlyInFormParam()\ so shared models referenced in form payloads keep their imports.
  3. Added test cases covering default \skipFormModel=true, \skipFormModel=false\ via generator properties, and shared models.

@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.

All reported issues were addressed across 4 files (changes from recent commits).

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

Re-trigger cubic

@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.

All reported issues were addressed across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

@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.

All reported issues were addressed across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

@jpfinne jpfinne 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.

Create a general utility method to retrieve the best configuration

Boolean skipFormModel = GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL) != null ?
Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL)) :
getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true);
Boolean skipFormModel = 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.

@SubhamAshok I had the same kind of issue when I wrote DefaultCodegen.convertPropertyToBooleanAndWriteBack

The configuration could come from a cliOption or from additionalProperties. Before there were discrepancies, leading to side effects if the code or the template used additionalProperties or getter in the CodeGen.

It think we could have the same for GlobalSettings.

Maybe we can have something like:
boolean convertGlobalSettingsPropertyToBooleanAndWriteBack(String propertyKey, Consumer<Boolean> booleanSetter, Supplier<Boolean> defaultIfMissing)

@SubhamAshok SubhamAshok Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion! Added convertGlobalPropertyToBooleanAndWriteBack in DefaultGenerator to resolve the global/generator defaults and write them back into additionalProperties. configureGeneratorProperties and generateModels now use this single standard path.

Boolean skipFormModel = GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL) != null ?
Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL)) :
getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true);
Boolean skipFormModel = config.additionalProperties().containsKey(CodegenConstants.SKIP_FORM_MODEL) ?

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.

@SubhamAshok Same as above. This looks way too convulated

@SubhamAshok
SubhamAshok force-pushed the fix/24727-dangling-request-model-import branch from 9ba5af1 to b16feb6 Compare August 30, 2026 18:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Multiple request-body content types generate a dangling request-model import

3 participants