diff --git a/bin/configs/spring-cloud-3-with-optional.yaml b/bin/configs/spring-cloud-3-with-optional.yaml index d9b875f072d3..7efd4ce223e0 100644 --- a/bin/configs/spring-cloud-3-with-optional.yaml +++ b/bin/configs/spring-cloud-3-with-optional.yaml @@ -12,3 +12,4 @@ additionalProperties: useSwaggerUI: "false" hideGenerationTimestamp: "true" documentationProvider: none + #optionalAcceptNullable: "false" # default to true diff --git a/docs/generators/java-camel.md b/docs/generators/java-camel.md index b0285c683b72..f2714febe563 100644 --- a/docs/generators/java-camel.md +++ b/docs/generators/java-camel.md @@ -73,6 +73,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |licenseUrl|The URL of the license| |http://unlicense.org| |modelPackage|package for generated models| |org.openapitools.model| |openApiNullable|Enable OpenAPI Jackson Nullable library. Not supported by `microprofile` library.| |true| +|optionalAcceptNullable|Use `ofNullable` instead of just `of` to accept null values when using Optional.| |true| |parentArtifactId|parent artifactId in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null| |parentGroupId|parent groupId in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null| |parentVersion|parent version in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null| diff --git a/docs/generators/spring.md b/docs/generators/spring.md index d44b91c69be6..954b786ebd13 100644 --- a/docs/generators/spring.md +++ b/docs/generators/spring.md @@ -66,6 +66,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl |licenseUrl|The URL of the license| |http://unlicense.org| |modelPackage|package for generated models| |org.openapitools.model| |openApiNullable|Enable OpenAPI Jackson Nullable library. Not supported by `microprofile` library.| |true| +|optionalAcceptNullable|Use `ofNullable` instead of just `of` to accept null values when using Optional.| |true| |parentArtifactId|parent artifactId in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null| |parentGroupId|parent groupId in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null| |parentVersion|parent version in generated pom N.B. parentGroupId, parentArtifactId and parentVersion must all be specified for any of them to take effect| |null| diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java index d5466ef16db4..d0184b803d4b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringCodegen.java @@ -114,6 +114,7 @@ public class SpringCodegen extends AbstractJavaCodegen public static final String USE_REQUEST_MAPPING_ON_CONTROLLER = "useRequestMappingOnController"; public static final String USE_REQUEST_MAPPING_ON_INTERFACE = "useRequestMappingOnInterface"; public static final String USE_SEALED = "useSealed"; + public static final String OPTIONAL_ACCEPT_NULLABLE = "optionalAcceptNullable"; @Getter public enum RequestMappingMode { api_interface("Generate the @RequestMapping annotation on the generated Api Interface."), @@ -167,6 +168,8 @@ public class SpringCodegen extends AbstractJavaCodegen protected boolean generatedConstructorWithRequiredArgs = true; @Getter @Setter protected RequestMappingMode requestMappingMode = RequestMappingMode.controller; + @Getter @Setter + protected boolean optionalAcceptNullable = true; public SpringCodegen() { super(); @@ -271,6 +274,9 @@ public SpringCodegen() { "Whether to generate constructors with required args for models", generatedConstructorWithRequiredArgs)); cliOptions.add(new CliOption(RESOURCE_FOLDER, RESOURCE_FOLDER_DESC).defaultValue(this.getResourceFolder())); + cliOptions.add(CliOption.newBoolean(OPTIONAL_ACCEPT_NULLABLE, + "Use `ofNullable` instead of just `of` to accept null values when using Optional.", + optionalAcceptNullable)); supportedLibraries.put(SPRING_BOOT, "Spring-boot Server application."); supportedLibraries.put(SPRING_CLOUD_LIBRARY, @@ -434,6 +440,8 @@ public void processOpts() { convertPropertyToBooleanAndWriteBack(UNHANDLED_EXCEPTION_HANDLING, this::setUnhandledException); convertPropertyToBooleanAndWriteBack(USE_RESPONSE_ENTITY, this::setUseResponseEntity); + convertPropertyToBooleanAndWriteBack(OPTIONAL_ACCEPT_NULLABLE, this::setOptionalAcceptNullable); + additionalProperties.put("springHttpStatus", new SpringHttpStatusLambda()); convertPropertyToBooleanAndWriteBack(USE_ENUM_CASE_INSENSITIVE, this::setUseEnumCaseInsensitive); diff --git a/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache b/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache index 9ad38706d6ab..ac4d556fd22a 100644 --- a/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache +++ b/modules/openapi-generator/src/main/resources/JavaSpring/pojo.mustache @@ -156,7 +156,7 @@ public {{>sealed}}class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}} {{! begin feature: fluent setter methods }} public {{classname}} {{name}}({{{datatypeWithEnum}}} {{name}}) { {{#openApiNullable}} - this.{{name}} = {{#isNullable}}JsonNullable.of({{/isNullable}}{{#useOptional}}{{^required}}{{^isNullable}}{{^isContainer}}Optional.ofNullable({{/isContainer}}{{/isNullable}}{{/required}}{{/useOptional}}{{name}}{{#isNullable}}){{/isNullable}}{{#useOptional}}{{^required}}{{^isNullable}}{{^isContainer}}){{/isContainer}}{{/isNullable}}{{/required}}{{/useOptional}}; + this.{{name}} = {{#isNullable}}JsonNullable.of({{/isNullable}}{{#useOptional}}{{^required}}{{^isNullable}}{{^isContainer}}Optional.of{{#optionalAcceptNullable}}Nullable{{/optionalAcceptNullable}}({{/isContainer}}{{/isNullable}}{{/required}}{{/useOptional}}{{name}}{{#isNullable}}){{/isNullable}}{{#useOptional}}{{^required}}{{^isNullable}}{{^isContainer}}){{/isContainer}}{{/isNullable}}{{/required}}{{/useOptional}}; {{/openApiNullable}} {{^openApiNullable}} this.{{name}} = {{name}}; 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 ef7230270fb8..378ec66140e6 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 @@ -5324,4 +5324,46 @@ public void shouldAnnotateNonRequiredFieldsAsNullableWhileNotUsingOpenApiNullabl " @Nullable List nullableContainer)" ); } -} + + @Test + public void shouldNotAcceptNullValues() throws IOException { + SpringCodegen codegen = new SpringCodegen(); + codegen.setLibrary(SPRING_BOOT); + codegen.setUseSpringBoot3(true); + codegen.setUseOptional(true); + codegen.setOptionalAcceptNullable(false); + + Map files = generateFiles(codegen, "src/test/resources/3_0/petstore.yaml"); + var file = files.get("Category.java"); + + JavaFileAssert.assertThat(file) + .fileContains( + "this.name = Optional.of(name);" + ); + JavaFileAssert.assertThat(file) + .fileDoesNotContain( + "this.name = Optional.ofNullable(name);" + ); + } + + @Test + public void shouldAcceptNullValues() throws IOException { + SpringCodegen codegen = new SpringCodegen(); + codegen.setLibrary(SPRING_BOOT); + codegen.setUseSpringBoot3(true); + codegen.setUseOptional(true); + //codegen.setOptionalAcceptNullable(true); // default to true + + Map files = generateFiles(codegen, "src/test/resources/3_0/petstore.yaml"); + var file = files.get("Category.java"); + + JavaFileAssert.assertThat(file) + .fileContains( + "this.name = Optional.ofNullable(name);" + ); + JavaFileAssert.assertThat(file) + .fileDoesNotContain( + "this.name = Optional.of(name);" + ); + } +} \ No newline at end of file