Skip to content

Commit

Permalink
[java-micronaut] Support Optional for non-required properties (#12144)
Browse files Browse the repository at this point in the history
The Micronaut generator by default adds the @nullable annotation to
non-required properties and allows using the Jackson JsonNullable
wrapper but it is not possible to use java.util.Optional as a wrapper
for optional properties.

This change adds support for using the Optional wrapper for non-required
properties.
  • Loading branch information
auke- authored Apr 21, 2022
1 parent e98c054 commit a8d4c00
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 4 deletions.
1 change: 1 addition & 0 deletions docs/generators/java-micronaut-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|testOutput|Set output folder for models and APIs tests| |${project.build.directory}/generated-test-sources/openapi|
|title|Client service name| |null|
|useBeanValidation|Use BeanValidation API annotations| |true|
|useOptional|Use Optional container for optional parameters| |false|
|withXml|whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)| |false|

## SUPPORTED VENDOR EXTENSIONS
Expand Down
1 change: 1 addition & 0 deletions docs/generators/java-micronaut-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
|title|Client service name| |null|
|useAuth|Whether to import authorization and to annotate controller methods accordingly| |true|
|useBeanValidation|Use BeanValidation API annotations| |true|
|useOptional|Use Optional container for optional parameters| |false|
|withXml|whether to include support for application/xml content type and include XML annotations in the model (works with libraries that provide support for JSON and XML)| |false|

## SUPPORTED VENDOR EXTENSIONS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.languages.features.BeanValidationFeatures;
import org.openapitools.codegen.languages.features.OptionalFeatures;
import org.openapitools.codegen.meta.features.DocumentationFeature;
import org.openapitools.codegen.meta.features.SecurityFeature;
import org.openapitools.codegen.model.ModelMap;
Expand All @@ -15,7 +16,7 @@

import static org.openapitools.codegen.CodegenConstants.INVOKER_PACKAGE;

public abstract class JavaMicronautAbstractCodegen extends AbstractJavaCodegen implements BeanValidationFeatures {
public abstract class JavaMicronautAbstractCodegen extends AbstractJavaCodegen implements BeanValidationFeatures, OptionalFeatures {
public static final String OPT_TITLE = "title";
public static final String OPT_BUILD = "build";
public static final String OPT_BUILD_GRADLE = "gradle";
Expand All @@ -34,6 +35,7 @@ public abstract class JavaMicronautAbstractCodegen extends AbstractJavaCodegen i

protected String title;
protected boolean useBeanValidation;
protected boolean useOptional;
protected String buildTool;
protected String testTool;
protected boolean requiredPropertiesInConstructor = true;
Expand All @@ -52,6 +54,7 @@ public JavaMicronautAbstractCodegen() {

// Set all the fields
useBeanValidation = true;
useOptional = false;
buildTool = OPT_BUILD_ALL;
testTool = OPT_TEST_JUNIT;
outputFolder = "generated-code/java-micronaut-client";
Expand Down Expand Up @@ -95,6 +98,7 @@ public JavaMicronautAbstractCodegen() {
cliOptions.add(new CliOption(OPT_TITLE, "Client service name").defaultValue(title));
cliOptions.add(new CliOption(OPT_MICRONAUT_VERSION, "Micronaut version, only >=3.0.0 versions are supported").defaultValue(micronautVersion));
cliOptions.add(CliOption.newBoolean(USE_BEANVALIDATION, "Use BeanValidation API annotations", useBeanValidation));
cliOptions.add(CliOption.newBoolean(USE_OPTIONAL, "Use Optional container for optional parameters", useOptional));
cliOptions.add(CliOption.newBoolean(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR, "Allow only to create models with all the required properties provided in constructor", requiredPropertiesInConstructor));

CliOption buildToolOption = new CliOption(OPT_BUILD, "Specify for which build tool to generate files").defaultValue(buildTool);
Expand Down Expand Up @@ -158,6 +162,11 @@ public void processOpts() {
}
writePropertyBack(USE_BEANVALIDATION, useBeanValidation);

if (additionalProperties.containsKey(USE_OPTIONAL)) {
this.setUseOptional(convertPropertyToBoolean(USE_OPTIONAL));
}
writePropertyBack(USE_OPTIONAL, useOptional);

if (additionalProperties.containsKey(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR)) {
this.requiredPropertiesInConstructor = convertPropertyToBoolean(OPT_REQUIRED_PROPERTIES_IN_CONSTRUCTOR);
}
Expand Down Expand Up @@ -306,6 +315,11 @@ public void setUseBeanValidation(boolean useBeanValidation) {
this.useBeanValidation = useBeanValidation;
}

@Override
public void setUseOptional(boolean useOptional) {
this.useOptional = useOptional;
}

@Override
public String toApiVarName(String name) {
String apiVarName = super.toApiVarName(name);
Expand All @@ -319,6 +333,10 @@ public boolean isUseBeanValidation() {
return useBeanValidation;
}

public boolean isUseOptional() {
return useOptional;
}

@Override
public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<ModelMap> allModels) {
objs = super.postProcessOperationsWithModels(objs, allModels);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ validate all pojos and enums
nullable & nonnull
}}{{#required}}{{#isNullable}} @Nullable
{{/isNullable}}{{^isNullable}} @NotNull
{{/isNullable}}{{/required}}{{^required}} @Nullable
{{/required}}{{!
{{/isNullable}}{{/required}}{{^required}}{{^useOptional}} @Nullable
{{/useOptional}}{{/required}}{{!
pattern
}}{{#pattern}}{{^isByteArray}} @Pattern(regexp="{{{pattern}}}")
{{/isByteArray}}{{/pattern}}{{!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import org.apache.commons.lang3.builder.HashCodeBuilder;
{{/useReflectionEqualsHashCode}}
import java.util.Objects;
import java.util.Arrays;
{{#useOptional}}
import java.util.Optional;
{{/useOptional}}
{{#imports}}
import {{import}};
{{/imports}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ Declare the class with extends and implements
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{^vendorExtensions.x-is-jackson-optional-nullable}}
{{#jackson}}
{{>common/model/jackson_annotations}}{{/jackson}}{{/vendorExtensions.x-is-jackson-optional-nullable}} public {{{datatypeWithEnum}}} {{getter}}() {
{{>common/model/jackson_annotations}}{{/jackson}}{{/vendorExtensions.x-is-jackson-optional-nullable}} public {{#useOptional}}{{^required}}Optional<{{/required}}{{/useOptional}}{{{datatypeWithEnum}}}{{#useOptional}}{{^required}}>{{/required}}{{/useOptional}} {{getter}}() {
{{#vendorExtensions.x-is-jackson-optional-nullable}}
{{#isReadOnly}}
{{! A readonly attribute doesn't have setter => jackson will set null directly if explicitly returned by API, so make sure we have an empty JsonNullable}} if ({{name}} == null) {
Expand All @@ -196,7 +196,17 @@ Declare the class with extends and implements
return {{name}}.orElse(null);
{{/vendorExtensions.x-is-jackson-optional-nullable}}
{{^vendorExtensions.x-is-jackson-optional-nullable}}
{{#useOptional}}
{{#required}}
return {{name}};
{{/required}}
{{^required}}
return Optional.ofNullable({{name}});
{{/required}}
{{/useOptional}}
{{^useOptional}}
return {{name}};
{{/useOptional}}
{{/vendorExtensions.x-is-jackson-optional-nullable}}
}
Expand Down

0 comments on commit a8d4c00

Please sign in to comment.