diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java index 7a95434248a5..d16da5a634a9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/OpenAPINormalizer.java @@ -726,8 +726,12 @@ public Schema normalizeSchema(Schema schema, Set visitedSchemas) { normalizeSchema(result.getItems(), visitedSchemas); return result; } else if (schema.getAdditionalProperties() instanceof Schema) { // map - normalizeMapSchema(schema); - normalizeSchema((Schema) schema.getAdditionalProperties(), visitedSchemas); + if(!ModelUtils.isModelWithPropertiesOnly(schema)) { + normalizeMapSchema(schema); + normalizeSchema((Schema) schema.getAdditionalProperties(), visitedSchemas); + } else { + normalizeProperties(schema.getProperties(), visitedSchemas); + } } else if (ModelUtils.isOneOf(schema)) { // oneOf return normalizeOneOf(schema, visitedSchemas); } else if (ModelUtils.isAnyOf(schema)) { // anyOf diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java index ed4287ab0fc0..55be06da8003 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java @@ -40,6 +40,7 @@ import org.junit.jupiter.api.Assertions; import org.openapitools.codegen.config.CodegenConfigurator; import org.openapitools.codegen.config.GlobalSettings; +import org.openapitools.codegen.java.assertions.JavaFileAssert; import org.openapitools.codegen.languages.SpringCodegen; import org.openapitools.codegen.model.ModelMap; import org.openapitools.codegen.model.ModelsMap; @@ -55,6 +56,7 @@ import java.nio.file.Files; import java.util.*; import java.util.concurrent.*; +import java.util.function.Function; import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; 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 1d41104f8dc0..d7fb7d51ef3d 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 @@ -939,6 +939,11 @@ public void testOpenAPINormalizerSimplifyOneOfAnyOf31Spec() { assertEquals(schema22.getAnyOf(), null); assertEquals(schema22.getTypes(), Set.of("string")); assertEquals(schema22.getEnum().size(), 2); + + Schema schema23 = openAPI.getComponents().getSchemas().get("AnyOfNullableAdditionalPropertiesTest"); + assertEquals(((Schema) schema23.getProperties().get("str")).getAnyOf(), null); + assertTrue(((Schema) schema23.getProperties().get("str")).getNullable()); + assertEquals(schema22.getTypes(), Set.of("string")); } @Test diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java index 0b84b410fae0..ed0388fb3e5a 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaClientCodegenTest.java @@ -67,6 +67,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.InstanceOfAssertFactories.FILE; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.openapitools.codegen.CodegenConstants.*; import static org.openapitools.codegen.TestUtils.*; import static org.openapitools.codegen.languages.JavaClientCodegen.*; @@ -3833,6 +3834,30 @@ public void queryParameterJsonSerialization(String library) { ); } + @Test(description = "Issue #20213") + public void givenModelHasFalseAdditionalPropertiesAndPropertyHasNullAsAnyOfTypeThenModelIsCorrect() throws Exception { + File output = Files.createTempDirectory("test").toFile(); + output.deleteOnExit(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setGeneratorName("java") + .setInputSpec("src/test/resources/3_1/issue_20213.yaml") + .setOutputDir(output.getAbsolutePath().replace("\\", "/")); + + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + DefaultGenerator defaultGenerator = new DefaultGenerator(); + Map fileMap = defaultGenerator.opts(clientOptInput).generate() + .stream().collect(Collectors.toMap(File::getName, Function.identity())); + + JavaFileAssert.assertThat(fileMap.get("SampleObjectWithAdditionalFalse.java")) + .assertProperty("someString") + .withType("String") + .assertPropertyAnnotations() + .containsWithName("javax.annotation.Nullable"); + assertFalse(fileMap.containsKey("SampleObjectWithAdditionalFalseSomeString.java")); + + } + @DataProvider(name = "springClients") public static Object[] springClients() { return new Object[]{RESTCLIENT, WEBCLIENT}; diff --git a/modules/openapi-generator/src/test/resources/3_1/issue_20213.yaml b/modules/openapi-generator/src/test/resources/3_1/issue_20213.yaml new file mode 100644 index 000000000000..2726053d0a8b --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_1/issue_20213.yaml @@ -0,0 +1,30 @@ +# filename: nullable-properties-with-additional-properties-false.yaml +openapi: 3.1.0 +info: + title: "" + version: 1.0.0 +components: + schemas: + # For reference, an object without additionalProperties: false, but with nullable properties + SampleObject: + properties: + someString: + anyOf: + - type: string + - type: 'null' + + # For reference, an object with additionalProperties: false, but without any nullable properties + ReferenceObject: + additionalProperties: false + properties: + someString: + type: string + + # The broken case: an object with additionalProperties: false and nullable properties + SampleObjectWithAdditionalFalse: + additionalProperties: false + properties: + someString: + anyOf: + - type: string + - type: 'null' \ No newline at end of file diff --git a/modules/openapi-generator/src/test/resources/3_1/simplifyOneOfAnyOf_test.yaml b/modules/openapi-generator/src/test/resources/3_1/simplifyOneOfAnyOf_test.yaml index 588abf3e84c2..cbc387d8c514 100644 --- a/modules/openapi-generator/src/test/resources/3_1/simplifyOneOfAnyOf_test.yaml +++ b/modules/openapi-generator/src/test/resources/3_1/simplifyOneOfAnyOf_test.yaml @@ -137,4 +137,14 @@ components: OneOfNullAndRef3: oneOf: - $ref: '#/components/schemas/Parent' - - type: "null" \ No newline at end of file + - type: "null" + AnyOfNullableAdditionalPropertiesTest: + description: to test anyOf with additional properties + additionalProperties: false + properties: + str: + anyOf: + - type: string + - type: 'null' + - type: null + - $ref: null \ No newline at end of file