Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[java][spring] Add option optionalAcceptNullable to accept null values #20746

Merged
merged 3 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bin/configs/spring-cloud-3-with-optional.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ additionalProperties:
useSwaggerUI: "false"
hideGenerationTimestamp: "true"
documentationProvider: none
#optionalAcceptNullable: "false" # default to true
1 change: 1 addition & 0 deletions docs/generators/java-camel.md
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down
1 change: 1 addition & 0 deletions docs/generators/spring.md
Original file line number Diff line number Diff line change
Expand Up @@ -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|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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."),
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5324,4 +5324,46 @@ public void shouldAnnotateNonRequiredFieldsAsNullableWhileNotUsingOpenApiNullabl
" @Nullable List<String> 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<String, File> 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<String, File> 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);"
);
}
}
Loading