From 31f962fc336529acde22ebe5067f8d3c08eb4cd9 Mon Sep 17 00:00:00 2001 From: Auke Schrijnen Date: Fri, 15 Apr 2022 10:11:35 +0200 Subject: [PATCH] [java-micronaut] Support Optional for non-required properties 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. --- docs/generators/java-micronaut-client.md | 1 + docs/generators/java-micronaut-server.md | 1 + .../JavaMicronautAbstractCodegen.java | 20 ++++++++++++++++++- .../common/model/beanValidation.mustache | 4 ++-- .../common/model/model.mustache | 3 +++ .../java-micronaut/common/model/pojo.mustache | 12 ++++++++++- 6 files changed, 37 insertions(+), 4 deletions(-) diff --git a/docs/generators/java-micronaut-client.md b/docs/generators/java-micronaut-client.md index ac62abd17094..a7e3af091b8d 100644 --- a/docs/generators/java-micronaut-client.md +++ b/docs/generators/java-micronaut-client.md @@ -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 diff --git a/docs/generators/java-micronaut-server.md b/docs/generators/java-micronaut-server.md index a146585b20ce..f485644841a8 100644 --- a/docs/generators/java-micronaut-server.md +++ b/docs/generators/java-micronaut-server.md @@ -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 diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaMicronautAbstractCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaMicronautAbstractCodegen.java index f58e79c22666..6ef455d894fc 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaMicronautAbstractCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JavaMicronautAbstractCodegen.java @@ -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; @@ -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"; @@ -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; @@ -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"; @@ -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); @@ -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); } @@ -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); @@ -319,6 +333,10 @@ public boolean isUseBeanValidation() { return useBeanValidation; } + public boolean isUseOptional() { + return useOptional; + } + @Override public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List allModels) { objs = super.postProcessOperationsWithModels(objs, allModels); diff --git a/modules/openapi-generator/src/main/resources/java-micronaut/common/model/beanValidation.mustache b/modules/openapi-generator/src/main/resources/java-micronaut/common/model/beanValidation.mustache index ecf4809f9e3f..6c7dde3d9ea3 100644 --- a/modules/openapi-generator/src/main/resources/java-micronaut/common/model/beanValidation.mustache +++ b/modules/openapi-generator/src/main/resources/java-micronaut/common/model/beanValidation.mustache @@ -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}}{{! diff --git a/modules/openapi-generator/src/main/resources/java-micronaut/common/model/model.mustache b/modules/openapi-generator/src/main/resources/java-micronaut/common/model/model.mustache index f3119818d387..b016c0bcbb18 100644 --- a/modules/openapi-generator/src/main/resources/java-micronaut/common/model/model.mustache +++ b/modules/openapi-generator/src/main/resources/java-micronaut/common/model/model.mustache @@ -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}} diff --git a/modules/openapi-generator/src/main/resources/java-micronaut/common/model/pojo.mustache b/modules/openapi-generator/src/main/resources/java-micronaut/common/model/pojo.mustache index 7249f77729ff..254dfff365ff 100644 --- a/modules/openapi-generator/src/main/resources/java-micronaut/common/model/pojo.mustache +++ b/modules/openapi-generator/src/main/resources/java-micronaut/common/model/pojo.mustache @@ -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) { @@ -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}} }