From 3e336bb83edddb69f1f9950a9606131520473f05 Mon Sep 17 00:00:00 2001 From: Subham Ashok Date: Sat, 29 Aug 2026 23:23:56 +0530 Subject: [PATCH 1/6] fix(core): avoid dangling request-model import for skipped form models --- .../openapitools/codegen/DefaultCodegen.java | 25 ++++++++++- .../java/spring/SpringCodegenTest.java | 41 +++++++++++++++++++ .../src/test/resources/3_0/issue_24727.yaml | 39 ++++++++++++++++++ 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/issue_24727.yaml diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index e8f7eb86e02a..d026cc2decf9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -8214,6 +8214,29 @@ private CodegenParameter headerToCodegenParameter(Header header, String headerNa return param; } + /** + * Check if the content type is form data (e.g. multipart/* or application/x-www-form-urlencoded). + * + * @param contentType Content type string + * @return true if form content type + */ + protected boolean isFormContentType(String contentType) { + if (contentType == null) { + return false; + } + String ct = contentType.toLowerCase(Locale.ROOT); + return ct.startsWith("application/x-www-form-urlencoded") || ct.startsWith("multipart"); + } + + /** + * Check if skipFormModel is enabled (defaults to true). + * + * @return true if skipFormModel is true + */ + public boolean isSkipFormModel() { + return Boolean.parseBoolean(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL, "true")); + } + protected LinkedHashMap getContent(Content content, Set imports, String mediaTypeSchemaSuffix) { if (content == null) { return null; @@ -8285,7 +8308,7 @@ protected LinkedHashMap 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())); } } diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index b7184f853396..f8dae89c2847 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9335,4 +9335,45 @@ public void issue_24232() throws IOException { JavaFileAssert.assertThat(files.get("Dummy.java")) .fileContains("import org.myorg.MyCustomId;", "import org.myorg.MyCustomKey;"); } + + @Test + public void testMultipleRequestBodyContentTypesDanglingImport_issue24727() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(USE_TAGS, "true"); + + Map files = generateFromContract( + "src/test/resources/3_0/issue_24727.yaml", SPRING_BOOT, + additionalProperties); + + JavaFileAssert.assertThat(files.get("DefaultApi.java")) + .fileContains("import org.openapitools.model.MultiArticleImporter;") + .fileDoesNotContain("import org.openapitools.model.CreateMultiArticleImporterRequest;"); + + Assert.assertNotNull(files.get("MultiArticleImporter.java")); + Assert.assertNull(files.get("CreateMultiArticleImporterRequest.java")); + } + + @Test + public void testMultipleRequestBodyContentTypesWithSkipFormModelFalse_issue24727() throws Exception { + GlobalSettings.setProperty("skipFormModel", "false"); + try { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(USE_TAGS, "true"); + + Map files = generateFromContract( + "src/test/resources/3_0/issue_24727.yaml", SPRING_BOOT, + additionalProperties); + + JavaFileAssert.assertThat(files.get("DefaultApi.java")) + .fileContains("import org.openapitools.model.MultiArticleImporter;") + .fileContains("import org.openapitools.model.CreateMultiArticleImporterRequest;"); + + Assert.assertNotNull(files.get("MultiArticleImporter.java")); + Assert.assertNotNull(files.get("CreateMultiArticleImporterRequest.java")); + } finally { + GlobalSettings.reset(); + } + } } diff --git a/modules/openapi-generator/src/test/resources/3_0/issue_24727.yaml b/modules/openapi-generator/src/test/resources/3_0/issue_24727.yaml new file mode 100644 index 000000000000..d2c5ef337e2b --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/issue_24727.yaml @@ -0,0 +1,39 @@ +openapi: 3.0.3 +info: + title: Multiple request-body content types repro + version: 1.0.0 +paths: + /api/multi_article_importer: + post: + operationId: createMultiArticleImporter + summary: Create a multi article importer + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MultiArticleImporter' + multipart/form-data: + schema: + type: object + properties: + file: + type: string + format: binary + responses: + '201': + description: Created + content: + application/json: + schema: + $ref: '#/components/schemas/MultiArticleImporter' +components: + schemas: + MultiArticleImporter: + type: object + properties: + id: + type: integer + format: int64 + name: + type: string From 620fdc99186cdba586ed0c56654a77d027051e15 Mon Sep 17 00:00:00 2001 From: Subham Ashok Date: Sun, 30 Aug 2026 14:58:13 +0530 Subject: [PATCH 2/6] fix(core): align skipFormModel resolution and preserve shared form models --- .../openapitools/codegen/DefaultCodegen.java | 39 ++++++++++++- .../codegen/DefaultGenerator.java | 5 ++ .../java/spring/SpringCodegenTest.java | 57 +++++++++++++------ .../resources/3_0/issue_24727_shared.yaml | 32 +++++++++++ 4 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 modules/openapi-generator/src/test/resources/3_0/issue_24727_shared.yaml diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index d026cc2decf9..92f3b3d093a1 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -1052,6 +1052,8 @@ public boolean specVersionGreaterThanOrEqualTo310(OpenAPI openAPI) { return specMajorVersion == 3 && specMinorVersion >= 1; } + private List schemasUsedOnlyInFormParam = null; + /** * Set the OpenAPI document. * This method is invoked when the input OpenAPI document has been parsed and validated. @@ -1062,6 +1064,7 @@ public void setOpenAPI(OpenAPI openAPI) { once(LOGGER).warn(UNSUPPORTED_V310_SPEC_MSG); } this.openAPI = openAPI; + this.schemasUsedOnlyInFormParam = null; // Set global settings such that helper functions in ModelUtils can lookup the value // of the CLI option. ModelUtils.setDisallowAdditionalPropertiesIfNotPresent(getDisallowAdditionalPropertiesIfNotPresent()); @@ -1070,6 +1073,21 @@ public void setOpenAPI(OpenAPI openAPI) { typeAliases = getAllAliases(ModelUtils.getSchemas(openAPI)); } + /** + * Returns the list of schema names that are used only in form parameters. + * + * @return list of schema names used only in form parameters + */ + public List getSchemasUsedOnlyInFormParam() { + if (this.openAPI == null) { + return Collections.emptyList(); + } + if (this.schemasUsedOnlyInFormParam == null) { + this.schemasUsedOnlyInFormParam = ModelUtils.getSchemasUsedOnlyInFormParam(this.openAPI); + } + return this.schemasUsedOnlyInFormParam; + } + // override with any message to be shown right before the process finishes @Override @SuppressWarnings("static-method") @@ -8234,6 +8252,13 @@ protected boolean isFormContentType(String contentType) { * @return true if skipFormModel is true */ public boolean isSkipFormModel() { + if (additionalProperties.containsKey(CodegenConstants.SKIP_FORM_MODEL)) { + final Object val = additionalProperties.get(CodegenConstants.SKIP_FORM_MODEL); + if (val instanceof Boolean) { + return (Boolean) val; + } + return Boolean.parseBoolean(val.toString()); + } return Boolean.parseBoolean(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL, "true")); } @@ -8308,8 +8333,18 @@ protected LinkedHashMap getContent(Content content, Se } cmtContent.put(contentType, codegenMt); - if (schemaProp != null && (!isFormContentType(contentType) || !isSkipFormModel())) { - addImports(imports, schemaProp.getImports(true, importBaseType, generatorMetadata.getFeatureSet())); + if (schemaProp != null) { + Set propImports = schemaProp.getImports(true, importBaseType, generatorMetadata.getFeatureSet()); + if (isFormContentType(contentType) && isSkipFormModel()) { + List formOnlySchemas = getSchemasUsedOnlyInFormParam(); + Set formOnlyModels = formOnlySchemas.stream() + .flatMap(s -> Stream.of(s, toModelName(s))) + .collect(Collectors.toSet()); + propImports = propImports.stream() + .filter(imp -> !formOnlyModels.contains(imp)) + .collect(Collectors.toSet()); + } + addImports(imports, propImports); } } return cmtContent; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 6636187fec0a..1b204d58c287 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -225,6 +225,10 @@ void configureGeneratorProperties() { generateApiDocumentation = GlobalSettings.getProperty(CodegenConstants.API_DOCS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.API_DOCS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.API_DOCS, true); generateRecursiveDependentModels = GlobalSettings.getProperty(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, false); + Boolean skipFormModel = GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL) != null ? + Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL)) : + getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true); + // Additional properties added for tests to exclude references in project related files config.additionalProperties().put(CodegenConstants.GENERATE_API_TESTS, generateApiTests); config.additionalProperties().put(CodegenConstants.GENERATE_MODEL_TESTS, generateModelTests); @@ -236,6 +240,7 @@ void configureGeneratorProperties() { config.additionalProperties().put(CodegenConstants.GENERATE_MODELS, generateModels); config.additionalProperties().put(CodegenConstants.GENERATE_WEBHOOKS, generateWebhooks); config.additionalProperties().put(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, generateRecursiveDependentModels); + config.additionalProperties().put(CodegenConstants.SKIP_FORM_MODEL, skipFormModel); if (!generateApiTests && !generateModelTests) { config.additionalProperties().put(CodegenConstants.EXCLUDE_TESTS, true); diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index f8dae89c2847..ca82fc1f1316 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9356,24 +9356,49 @@ public void testMultipleRequestBodyContentTypesDanglingImport_issue24727() throw @Test public void testMultipleRequestBodyContentTypesWithSkipFormModelFalse_issue24727() throws Exception { - GlobalSettings.setProperty("skipFormModel", "false"); - try { - Map additionalProperties = new HashMap<>(); - additionalProperties.put(INTERFACE_ONLY, "true"); - additionalProperties.put(USE_TAGS, "true"); + File output = Files.createTempDirectory("test").toFile().getCanonicalFile(); + output.deleteOnExit(); - Map files = generateFromContract( - "src/test/resources/3_0/issue_24727.yaml", SPRING_BOOT, - additionalProperties); + OpenAPI openAPI = new OpenAPIParser() + .readLocation("src/test/resources/3_0/issue_24727.yaml", null, new ParseOptions()).getOpenAPI(); - JavaFileAssert.assertThat(files.get("DefaultApi.java")) - .fileContains("import org.openapitools.model.MultiArticleImporter;") - .fileContains("import org.openapitools.model.CreateMultiArticleImporterRequest;"); + SpringCodegen codegen = new SpringCodegen(); + codegen.setOutputDir(output.getAbsolutePath()); + codegen.additionalProperties().put(INTERFACE_ONLY, "true"); + codegen.additionalProperties().put(USE_TAGS, "true"); - Assert.assertNotNull(files.get("MultiArticleImporter.java")); - Assert.assertNotNull(files.get("CreateMultiArticleImporterRequest.java")); - } finally { - GlobalSettings.reset(); - } + ClientOptInput input = new ClientOptInput(); + input.openAPI(openAPI); + input.config(codegen); + + DefaultGenerator generator = new DefaultGenerator(); + generator.setGeneratorPropertyDefault(CodegenConstants.SKIP_FORM_MODEL, "false"); + generator.setGenerateMetadata(false); + + Map files = generator.opts(input).generate().stream() + .collect(Collectors.toMap(this::getUniqueName, Function.identity())); + + JavaFileAssert.assertThat(files.get("DefaultApi.java")) + .fileContains("import org.openapitools.model.MultiArticleImporter;") + .fileContains("import org.openapitools.model.CreateMultiArticleImporterRequest;"); + + Assert.assertNotNull(files.get("MultiArticleImporter.java")); + Assert.assertNotNull(files.get("CreateMultiArticleImporterRequest.java")); + } + + @Test + public void testMultipleRequestBodyContentTypesWithSharedModel_issue24727() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(USE_TAGS, "true"); + + Map files = generateFromContract( + "src/test/resources/3_0/issue_24727_shared.yaml", SPRING_BOOT, + additionalProperties); + + JavaFileAssert.assertThat(files.get("DefaultApi.java")) + .fileContains("import org.openapitools.model.ArticlePayload;"); + + Assert.assertNotNull(files.get("ArticlePayload.java")); } } diff --git a/modules/openapi-generator/src/test/resources/3_0/issue_24727_shared.yaml b/modules/openapi-generator/src/test/resources/3_0/issue_24727_shared.yaml new file mode 100644 index 000000000000..0d8686b1b7cf --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/issue_24727_shared.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.3 +info: + title: Shared form model repro + version: 1.0.0 +paths: + /api/upload: + post: + operationId: uploadArticle + summary: Upload an article + requestBody: + required: true + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/ArticlePayload' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ArticlePayload' +components: + schemas: + ArticlePayload: + type: object + properties: + id: + type: integer + format: int64 + title: + type: string From 68bf8f8d0deafce4da915f91fbd72ca3294bd328 Mon Sep 17 00:00:00 2001 From: Subham Ashok Date: Sun, 30 Aug 2026 15:46:55 +0530 Subject: [PATCH 3/6] fix(core): preserve existing skipFormModel in additionalProperties --- .../codegen/DefaultGenerator.java | 25 ++++++++++++++----- .../java/spring/SpringCodegenTest.java | 19 ++++++++++++++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 1b204d58c287..5512f589215e 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -225,9 +225,20 @@ void configureGeneratorProperties() { generateApiDocumentation = GlobalSettings.getProperty(CodegenConstants.API_DOCS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.API_DOCS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.API_DOCS, true); generateRecursiveDependentModels = GlobalSettings.getProperty(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, false); - 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; + if (config.additionalProperties().containsKey(CodegenConstants.SKIP_FORM_MODEL)) { + final Object val = config.additionalProperties().get(CodegenConstants.SKIP_FORM_MODEL); + if (val instanceof Boolean) { + skipFormModel = (Boolean) val; + } else if (val != null) { + skipFormModel = Boolean.parseBoolean(val.toString()); + } + } + if (skipFormModel == null) { + skipFormModel = GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL) != null ? + Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL)) : + getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true); + } // Additional properties added for tests to exclude references in project related files config.additionalProperties().put(CodegenConstants.GENERATE_API_TESTS, generateApiTests); @@ -475,9 +486,11 @@ void generateModels(List files, List allModels, List unu // store all processed models Map allProcessedModels = new TreeMap<>((o1, o2) -> ObjectUtils.compare(config.toModelName(o1), config.toModelName(o2))); - 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) ? + Boolean.valueOf(config.additionalProperties().get(CodegenConstants.SKIP_FORM_MODEL).toString()) : + (GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL) != null ? + Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL)) : + getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true)); // process models only for (String name : modelKeys) { diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index ca82fc1f1316..1d03e97896a7 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9401,4 +9401,23 @@ public void testMultipleRequestBodyContentTypesWithSharedModel_issue24727() thro Assert.assertNotNull(files.get("ArticlePayload.java")); } + + @Test + public void testMultipleRequestBodyContentTypesWithSkipFormModelFalseInAdditionalProperties_issue24727() throws Exception { + Map additionalProperties = new HashMap<>(); + additionalProperties.put(INTERFACE_ONLY, "true"); + additionalProperties.put(USE_TAGS, "true"); + additionalProperties.put(CodegenConstants.SKIP_FORM_MODEL, "false"); + + Map files = generateFromContract( + "src/test/resources/3_0/issue_24727.yaml", SPRING_BOOT, + additionalProperties); + + JavaFileAssert.assertThat(files.get("DefaultApi.java")) + .fileContains("import org.openapitools.model.MultiArticleImporter;") + .fileContains("import org.openapitools.model.CreateMultiArticleImporterRequest;"); + + Assert.assertNotNull(files.get("MultiArticleImporter.java")); + Assert.assertNotNull(files.get("CreateMultiArticleImporterRequest.java")); + } } From 69db41ad0945887fbe05a63a091d814242bdb76d Mon Sep 17 00:00:00 2001 From: Subham Ashok Date: Sun, 30 Aug 2026 17:19:59 +0530 Subject: [PATCH 4/6] fix(core): read normalized skipFormModel from additionalProperties in generateModels --- .../codegen/DefaultGenerator.java | 21 ++++--------------- .../java/spring/SpringCodegenTest.java | 19 ----------------- 2 files changed, 4 insertions(+), 36 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 5512f589215e..b478b27505c4 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -225,20 +225,9 @@ void configureGeneratorProperties() { generateApiDocumentation = GlobalSettings.getProperty(CodegenConstants.API_DOCS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.API_DOCS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.API_DOCS, true); generateRecursiveDependentModels = GlobalSettings.getProperty(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, false); - Boolean skipFormModel = null; - if (config.additionalProperties().containsKey(CodegenConstants.SKIP_FORM_MODEL)) { - final Object val = config.additionalProperties().get(CodegenConstants.SKIP_FORM_MODEL); - if (val instanceof Boolean) { - skipFormModel = (Boolean) val; - } else if (val != null) { - skipFormModel = Boolean.parseBoolean(val.toString()); - } - } - if (skipFormModel == null) { - skipFormModel = GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL) != null ? - Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL)) : - getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true); - } + Boolean skipFormModel = GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL) != null ? + Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL)) : + getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true); // Additional properties added for tests to exclude references in project related files config.additionalProperties().put(CodegenConstants.GENERATE_API_TESTS, generateApiTests); @@ -488,9 +477,7 @@ void generateModels(List files, List allModels, List unu Boolean skipFormModel = config.additionalProperties().containsKey(CodegenConstants.SKIP_FORM_MODEL) ? Boolean.valueOf(config.additionalProperties().get(CodegenConstants.SKIP_FORM_MODEL).toString()) : - (GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL) != null ? - Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL)) : - getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true)); + Boolean.TRUE; // process models only for (String name : modelKeys) { diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java index 1d03e97896a7..ca82fc1f1316 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/spring/SpringCodegenTest.java @@ -9401,23 +9401,4 @@ public void testMultipleRequestBodyContentTypesWithSharedModel_issue24727() thro Assert.assertNotNull(files.get("ArticlePayload.java")); } - - @Test - public void testMultipleRequestBodyContentTypesWithSkipFormModelFalseInAdditionalProperties_issue24727() throws Exception { - Map additionalProperties = new HashMap<>(); - additionalProperties.put(INTERFACE_ONLY, "true"); - additionalProperties.put(USE_TAGS, "true"); - additionalProperties.put(CodegenConstants.SKIP_FORM_MODEL, "false"); - - Map files = generateFromContract( - "src/test/resources/3_0/issue_24727.yaml", SPRING_BOOT, - additionalProperties); - - JavaFileAssert.assertThat(files.get("DefaultApi.java")) - .fileContains("import org.openapitools.model.MultiArticleImporter;") - .fileContains("import org.openapitools.model.CreateMultiArticleImporterRequest;"); - - Assert.assertNotNull(files.get("MultiArticleImporter.java")); - Assert.assertNotNull(files.get("CreateMultiArticleImporterRequest.java")); - } } From b16feb6b7189f0a5bce863a88f19736a66f4e832 Mon Sep 17 00:00:00 2001 From: Subham Ashok Date: Mon, 31 Aug 2026 00:19:32 +0530 Subject: [PATCH 5/6] refactor(core): extract convertGlobalPropertyToBooleanAndWriteBack helper --- .../codegen/DefaultGenerator.java | 40 ++++++++++--------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index b478b27505c4..00865e4957dc 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -56,6 +56,7 @@ import java.time.ZonedDateTime; import java.util.*; import java.util.concurrent.ConcurrentSkipListSet; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -192,6 +193,21 @@ private Boolean getGeneratorPropertyDefaultSwitch(final String key, final Boolea return defaultValue; } + private Boolean getGlobalOrGeneratorPropertyDefault(final String key, final Boolean defaultValue) { + String global = GlobalSettings.getProperty(key); + if (global != null) { + return Boolean.valueOf(global); + } + return getGeneratorPropertyDefaultSwitch(key, defaultValue); + } + + private boolean convertGlobalPropertyToBooleanAndWriteBack(final String propertyKey, final Consumer booleanSetter, final boolean defaultValue) { + boolean value = getGlobalOrGeneratorPropertyDefault(propertyKey, defaultValue); + booleanSetter.accept(value); + config.additionalProperties().put(propertyKey, value); + return value; + } + void configureGeneratorProperties() { // allows generating only models by specifying a CSV of models to generate, or empty for all // NOTE: Boolean.TRUE is required below rather than `true` because of JVM boxing constraints and type inference. @@ -219,28 +235,16 @@ void configureGeneratorProperties() { } // model/api tests and documentation options rely on parent generate options (api or model) and no other options. // They default to true in all scenarios and can only be marked false explicitly - generateModelTests = GlobalSettings.getProperty(CodegenConstants.MODEL_TESTS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.MODEL_TESTS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.MODEL_TESTS, true); - generateModelDocumentation = GlobalSettings.getProperty(CodegenConstants.MODEL_DOCS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.MODEL_DOCS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.MODEL_DOCS, true); - generateApiTests = GlobalSettings.getProperty(CodegenConstants.API_TESTS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.API_TESTS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.API_TESTS, true); - generateApiDocumentation = GlobalSettings.getProperty(CodegenConstants.API_DOCS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.API_DOCS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.API_DOCS, true); - generateRecursiveDependentModels = GlobalSettings.getProperty(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, false); - - Boolean skipFormModel = GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL) != null ? - Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.SKIP_FORM_MODEL)) : - getGeneratorPropertyDefaultSwitch(CodegenConstants.SKIP_FORM_MODEL, true); - - // Additional properties added for tests to exclude references in project related files - config.additionalProperties().put(CodegenConstants.GENERATE_API_TESTS, generateApiTests); - config.additionalProperties().put(CodegenConstants.GENERATE_MODEL_TESTS, generateModelTests); - - config.additionalProperties().put(CodegenConstants.GENERATE_API_DOCS, generateApiDocumentation); - config.additionalProperties().put(CodegenConstants.GENERATE_MODEL_DOCS, generateModelDocumentation); + convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.MODEL_TESTS, val -> generateModelTests = val, true); + convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.MODEL_DOCS, val -> generateModelDocumentation = val, true); + convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.API_TESTS, val -> generateApiTests = val, true); + convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.API_DOCS, val -> generateApiDocumentation = val, true); + convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, val -> generateRecursiveDependentModels = val, false); + convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.SKIP_FORM_MODEL, val -> {}, true); config.additionalProperties().put(CodegenConstants.GENERATE_APIS, generateApis); config.additionalProperties().put(CodegenConstants.GENERATE_MODELS, generateModels); config.additionalProperties().put(CodegenConstants.GENERATE_WEBHOOKS, generateWebhooks); - config.additionalProperties().put(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, generateRecursiveDependentModels); - config.additionalProperties().put(CodegenConstants.SKIP_FORM_MODEL, skipFormModel); if (!generateApiTests && !generateModelTests) { config.additionalProperties().put(CodegenConstants.EXCLUDE_TESTS, true); From bde822c32031218c2badd8b27eaf067a8295ff44 Mon Sep 17 00:00:00 2001 From: Subham Ashok Date: Mon, 31 Aug 2026 01:24:24 +0530 Subject: [PATCH 6/6] fix(core): restore correct additionalProperties mapping for generator properties --- .../codegen/DefaultGenerator.java | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 00865e4957dc..309dead212ca 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -56,7 +56,6 @@ import java.time.ZonedDateTime; import java.util.*; import java.util.concurrent.ConcurrentSkipListSet; -import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -201,13 +200,6 @@ private Boolean getGlobalOrGeneratorPropertyDefault(final String key, final Bool return getGeneratorPropertyDefaultSwitch(key, defaultValue); } - private boolean convertGlobalPropertyToBooleanAndWriteBack(final String propertyKey, final Consumer booleanSetter, final boolean defaultValue) { - boolean value = getGlobalOrGeneratorPropertyDefault(propertyKey, defaultValue); - booleanSetter.accept(value); - config.additionalProperties().put(propertyKey, value); - return value; - } - void configureGeneratorProperties() { // allows generating only models by specifying a CSV of models to generate, or empty for all // NOTE: Boolean.TRUE is required below rather than `true` because of JVM boxing constraints and type inference. @@ -235,16 +227,25 @@ void configureGeneratorProperties() { } // model/api tests and documentation options rely on parent generate options (api or model) and no other options. // They default to true in all scenarios and can only be marked false explicitly - convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.MODEL_TESTS, val -> generateModelTests = val, true); - convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.MODEL_DOCS, val -> generateModelDocumentation = val, true); - convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.API_TESTS, val -> generateApiTests = val, true); - convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.API_DOCS, val -> generateApiDocumentation = val, true); - convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, val -> generateRecursiveDependentModels = val, false); - convertGlobalPropertyToBooleanAndWriteBack(CodegenConstants.SKIP_FORM_MODEL, val -> {}, true); + generateModelTests = getGlobalOrGeneratorPropertyDefault(CodegenConstants.MODEL_TESTS, true); + generateModelDocumentation = getGlobalOrGeneratorPropertyDefault(CodegenConstants.MODEL_DOCS, true); + generateApiTests = getGlobalOrGeneratorPropertyDefault(CodegenConstants.API_TESTS, true); + generateApiDocumentation = getGlobalOrGeneratorPropertyDefault(CodegenConstants.API_DOCS, true); + generateRecursiveDependentModels = getGlobalOrGeneratorPropertyDefault(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, false); + Boolean skipFormModel = getGlobalOrGeneratorPropertyDefault(CodegenConstants.SKIP_FORM_MODEL, true); + + // Additional properties added for tests to exclude references in project related files + config.additionalProperties().put(CodegenConstants.GENERATE_API_TESTS, generateApiTests); + config.additionalProperties().put(CodegenConstants.GENERATE_MODEL_TESTS, generateModelTests); + + config.additionalProperties().put(CodegenConstants.GENERATE_API_DOCS, generateApiDocumentation); + config.additionalProperties().put(CodegenConstants.GENERATE_MODEL_DOCS, generateModelDocumentation); config.additionalProperties().put(CodegenConstants.GENERATE_APIS, generateApis); config.additionalProperties().put(CodegenConstants.GENERATE_MODELS, generateModels); config.additionalProperties().put(CodegenConstants.GENERATE_WEBHOOKS, generateWebhooks); + config.additionalProperties().put(CodegenConstants.GENERATE_RECURSIVE_DEPENDENT_MODELS, generateRecursiveDependentModels); + config.additionalProperties().put(CodegenConstants.SKIP_FORM_MODEL, skipFormModel); if (!generateApiTests && !generateModelTests) { config.additionalProperties().put(CodegenConstants.EXCLUDE_TESTS, true);