diff --git a/src/main/java/io/swagger/codegen/v3/generators/DefaultCodegenConfig.java b/src/main/java/io/swagger/codegen/v3/generators/DefaultCodegenConfig.java index b18e9a3e0e..cd1009c5ba 100644 --- a/src/main/java/io/swagger/codegen/v3/generators/DefaultCodegenConfig.java +++ b/src/main/java/io/swagger/codegen/v3/generators/DefaultCodegenConfig.java @@ -109,6 +109,8 @@ public abstract class DefaultCodegenConfig implements CodegenConfig { public static final String DEFAULT_CONTENT_TYPE = "application/json"; public static final String REQUEST_BODY_NAME = "body"; public static final String DEFAULT_TEMPLATE_DIR = "handlebars"; + public static final String IS_NULLABLE_FALSE = "x-nullable-false"; + public static final String IS_NULLABLE_TRUE = "x-nullable-true"; protected OpenAPI openAPI; protected OpenAPI unflattenedOpenAPI; @@ -1466,6 +1468,8 @@ else if (schema instanceof ComposedSchema) { } } codegenModel.getVendorExtensions().put(CodegenConstants.IS_NULLABLE_EXT_NAME, Boolean.TRUE.equals(schema.getNullable())); + codegenModel.getVendorExtensions().put(IS_NULLABLE_FALSE, Boolean.FALSE.equals(schema.getNullable())); + codegenModel.getVendorExtensions().put(IS_NULLABLE_TRUE, Boolean.TRUE.equals(schema.getNullable())); addVars(codegenModel, schema.getProperties(), schema.getRequired()); } @@ -1613,6 +1617,8 @@ protected void setSchemaProperties(String name, CodegenProperty codegenProperty, codegenProperty.jsonSchema = Json.pretty(schema); codegenProperty.nullable = Boolean.TRUE.equals(schema.getNullable()); codegenProperty.getVendorExtensions().put(CodegenConstants.IS_NULLABLE_EXT_NAME, Boolean.TRUE.equals(schema.getNullable())); + codegenProperty.getVendorExtensions().put(IS_NULLABLE_FALSE, Boolean.FALSE.equals(schema.getNullable())); + codegenProperty.getVendorExtensions().put(IS_NULLABLE_TRUE, Boolean.TRUE.equals(schema.getNullable())); if (schema.getReadOnly() != null) { codegenProperty.getVendorExtensions().put(CodegenConstants.IS_READ_ONLY_EXT_NAME, schema.getReadOnly()); } diff --git a/src/main/java/io/swagger/codegen/v3/generators/java/AbstractJavaCodegen.java b/src/main/java/io/swagger/codegen/v3/generators/java/AbstractJavaCodegen.java index 07aa3cf3f7..ebc1388158 100644 --- a/src/main/java/io/swagger/codegen/v3/generators/java/AbstractJavaCodegen.java +++ b/src/main/java/io/swagger/codegen/v3/generators/java/AbstractJavaCodegen.java @@ -58,6 +58,7 @@ public abstract class AbstractJavaCodegen extends DefaultCodegenConfig { public static final String SUPPORT_JAVA6 = "supportJava6"; public static final String ERROR_ON_UNKNOWN_ENUM = "errorOnUnknownEnum"; public static final String CHECK_DUPLICATED_MODEL_NAME = "checkDuplicatedModelName"; + public static final String USE_NULLABLE_FOR_NOTNULL = "useNullableForNotNull"; public static final String WIREMOCK_OPTION = "wiremock"; @@ -96,6 +97,7 @@ public abstract class AbstractJavaCodegen extends DefaultCodegenConfig { protected boolean supportJava6= false; protected boolean jakarta = false; private NotNullAnnotationFeatures notNullOption; + protected boolean useNullableForNotNull = true; public AbstractJavaCodegen() { super(); @@ -205,6 +207,8 @@ public AbstractJavaCodegen() { jeeSpecModeOptions.put("false", "Use Java EE (javax.*)"); jeeSpec.setEnum(jeeSpecModeOptions); cliOptions.add(jeeSpec); + + cliOptions.add(CliOption.newBoolean(USE_NULLABLE_FOR_NOTNULL, "Add @NotNull depending on `nullable` property instead of `required`")); } @Override @@ -387,6 +391,11 @@ public void processOpts() { } } + if (additionalProperties.containsKey(USE_NULLABLE_FOR_NOTNULL)) { + this.setUseNullableForNotnull(Boolean.valueOf(additionalProperties.get(USE_NULLABLE_FOR_NOTNULL).toString())); + } + writePropertyBack(USE_NULLABLE_FOR_NOTNULL, this.useNullableForNotNull); + if (fullJavaUtil) { javaUtilPrefix = "java.util."; } @@ -1511,6 +1520,10 @@ public void setScmUrl(String scmUrl) { this.scmUrl = scmUrl; } + public void setUseNullableForNotnull(Boolean useNullableForNotNull) { + this.useNullableForNotNull = useNullableForNotNull; + } + public void setDeveloperName(String developerName) { this.developerName = developerName; } diff --git a/src/main/resources/handlebars/Java/beanValidation.mustache b/src/main/resources/handlebars/Java/beanValidation.mustache index 165af94c59..673a3b14e8 100644 --- a/src/main/resources/handlebars/Java/beanValidation.mustache +++ b/src/main/resources/handlebars/Java/beanValidation.mustache @@ -1,6 +1,9 @@ -{{#required}} +{{#required}}{{^useNullableForNotNull}} @NotNull -{{/required}} +{{/useNullableForNotNull}}{{/required}} +{{#useNullableForNotNull}}{{^nullable}} + @NotNull +{{/nullable}}{{/useNullableForNotNull}} {{#is this 'container'}} {{#isNot this 'primitive-type'}} {{#isNot this 'enum'}} diff --git a/src/main/resources/handlebars/JavaJaxRS/beanValidation.mustache b/src/main/resources/handlebars/JavaJaxRS/beanValidation.mustache index 25e9ce282b..e5c9f5e8fd 100644 --- a/src/main/resources/handlebars/JavaJaxRS/beanValidation.mustache +++ b/src/main/resources/handlebars/JavaJaxRS/beanValidation.mustache @@ -1,6 +1,9 @@ -{{#required}} +{{#required}}{{^useNullableForNotNull}} @NotNull -{{/required}} +{{/useNullableForNotNull}}{{/required}} +{{#useNullableForNotNull}}{{^nullable}} + @NotNull +{{/nullable}}{{/useNullableForNotNull}} {{#is this 'container'}} {{#isNot this 'primitive-type'}} {{#isNot this 'enum'}} diff --git a/src/main/resources/handlebars/JavaJaxRS/cxf-cdi/beanValidation.mustache b/src/main/resources/handlebars/JavaJaxRS/cxf-cdi/beanValidation.mustache index bb8ecd0b94..bccc5f0e17 100644 --- a/src/main/resources/handlebars/JavaJaxRS/cxf-cdi/beanValidation.mustache +++ b/src/main/resources/handlebars/JavaJaxRS/cxf-cdi/beanValidation.mustache @@ -1,6 +1,9 @@ -{{#required}} +{{#required}}{{^useNullableForNotNull}} @NotNull -{{/required}} +{{/useNullableForNotNull}}{{/required}} +{{#useNullableForNotNull}}{{^nullable}} + @NotNull +{{/nullable}}{{/useNullableForNotNull}} {{#isContainer}} {{^isPrimitiveType}} {{^isEnum}} diff --git a/src/main/resources/handlebars/JavaJaxRS/cxf/beanValidation.mustache b/src/main/resources/handlebars/JavaJaxRS/cxf/beanValidation.mustache index c8c6946fef..42d2afbbf5 100644 --- a/src/main/resources/handlebars/JavaJaxRS/cxf/beanValidation.mustache +++ b/src/main/resources/handlebars/JavaJaxRS/cxf/beanValidation.mustache @@ -1,4 +1,7 @@ -{{#required}} +{{#required}}{{^useNullableForNotNull}} @NotNull -{{/required}} +{{/useNullableForNotNull}}{{/required}} +{{#useNullableForNotNull}}{{^nullable}} + @NotNull +{{/nullable}}{{/useNullableForNotNull}} {{>beanValidationCore}} \ No newline at end of file diff --git a/src/main/resources/handlebars/JavaJaxRS/resteasy/beanValidation.mustache b/src/main/resources/handlebars/JavaJaxRS/resteasy/beanValidation.mustache index c8c6946fef..42d2afbbf5 100644 --- a/src/main/resources/handlebars/JavaJaxRS/resteasy/beanValidation.mustache +++ b/src/main/resources/handlebars/JavaJaxRS/resteasy/beanValidation.mustache @@ -1,4 +1,7 @@ -{{#required}} +{{#required}}{{^useNullableForNotNull}} @NotNull -{{/required}} +{{/useNullableForNotNull}}{{/required}} +{{#useNullableForNotNull}}{{^nullable}} + @NotNull +{{/nullable}}{{/useNullableForNotNull}} {{>beanValidationCore}} \ No newline at end of file diff --git a/src/main/resources/handlebars/JavaJaxRS/resteasy/eap/beanValidation.mustache b/src/main/resources/handlebars/JavaJaxRS/resteasy/eap/beanValidation.mustache index cba488e41b..76dd9f44e8 100644 --- a/src/main/resources/handlebars/JavaJaxRS/resteasy/eap/beanValidation.mustache +++ b/src/main/resources/handlebars/JavaJaxRS/resteasy/eap/beanValidation.mustache @@ -1,6 +1,9 @@ -{{#required}} +{{#required}}{{^useNullableForNotNull}} @NotNull -{{/required}} +{{/useNullableForNotNull}}{{/required}} +{{#useNullableForNotNull}}{{^nullable}} + @NotNull +{{/nullable}}{{/useNullableForNotNull}} {{#pattern}} @Pattern(regexp="{{{pattern}}}") {{/pattern}} diff --git a/src/main/resources/handlebars/JavaJaxRS/spec/beanValidation.mustache b/src/main/resources/handlebars/JavaJaxRS/spec/beanValidation.mustache index c8c6946fef..42d2afbbf5 100644 --- a/src/main/resources/handlebars/JavaJaxRS/spec/beanValidation.mustache +++ b/src/main/resources/handlebars/JavaJaxRS/spec/beanValidation.mustache @@ -1,4 +1,7 @@ -{{#required}} +{{#required}}{{^useNullableForNotNull}} @NotNull -{{/required}} +{{/useNullableForNotNull}}{{/required}} +{{#useNullableForNotNull}}{{^nullable}} + @NotNull +{{/nullable}}{{/useNullableForNotNull}} {{>beanValidationCore}} \ No newline at end of file diff --git a/src/main/resources/handlebars/JavaMicronaut/beanValidation.mustache b/src/main/resources/handlebars/JavaMicronaut/beanValidation.mustache index 0894c7cdbc..f4ead0fb27 100644 --- a/src/main/resources/handlebars/JavaMicronaut/beanValidation.mustache +++ b/src/main/resources/handlebars/JavaMicronaut/beanValidation.mustache @@ -1,6 +1,9 @@ -{{#required}} +{{#required}}{{^useNullableForNotNull}} @NotNull -{{/required}}{{#isContainer}}{{^isPrimitiveType}}{{^isEnum}} +{{/useNullableForNotNull}}{{/required}} +{{#useNullableForNotNull}}{{^nullable}} + @NotNull +{{/nullable}}{{/useNullableForNotNull}}{{#isContainer}}{{^isPrimitiveType}}{{^isEnum}} @Valid{{/isEnum}}{{/isPrimitiveType}}{{/isContainer}}{{#isNotContainer}}{{^isPrimitiveType}} @Valid{{/isPrimitiveType}}{{/isNotContainer}} {{>beanValidationCore}} \ No newline at end of file diff --git a/src/main/resources/handlebars/JavaSpring/beanValidation.mustache b/src/main/resources/handlebars/JavaSpring/beanValidation.mustache index 3e4ef612a4..de87c134e0 100644 --- a/src/main/resources/handlebars/JavaSpring/beanValidation.mustache +++ b/src/main/resources/handlebars/JavaSpring/beanValidation.mustache @@ -1,6 +1,9 @@ -{{#required}} +{{#required}}{{^useNullableForNotNull}} @NotNull -{{/required}}{{#isContainer}}{{^isPrimitiveType}}{{^isEnum}} +{{/useNullableForNotNull}}{{/required}} +{{#useNullableForNotNull}}{{^nullable}} + @NotNull +{{/nullable}}{{/useNullableForNotNull}}{{#isContainer}}{{^isPrimitiveType}}{{^isEnum}} @Valid{{/isEnum}}{{/isPrimitiveType}}{{/isContainer}}{{#isNotContainer}}{{^isPrimitiveType}} @Valid{{/isPrimitiveType}}{{/isNotContainer}} {{>beanValidationCore}} diff --git a/src/main/resources/handlebars/JavaVertXServer/beanValidation.mustache b/src/main/resources/handlebars/JavaVertXServer/beanValidation.mustache index 3e4ef612a4..de87c134e0 100644 --- a/src/main/resources/handlebars/JavaVertXServer/beanValidation.mustache +++ b/src/main/resources/handlebars/JavaVertXServer/beanValidation.mustache @@ -1,6 +1,9 @@ -{{#required}} +{{#required}}{{^useNullableForNotNull}} @NotNull -{{/required}}{{#isContainer}}{{^isPrimitiveType}}{{^isEnum}} +{{/useNullableForNotNull}}{{/required}} +{{#useNullableForNotNull}}{{^nullable}} + @NotNull +{{/nullable}}{{/useNullableForNotNull}}{{#isContainer}}{{^isPrimitiveType}}{{^isEnum}} @Valid{{/isEnum}}{{/isPrimitiveType}}{{/isContainer}}{{#isNotContainer}}{{^isPrimitiveType}} @Valid{{/isPrimitiveType}}{{/isNotContainer}} {{>beanValidationCore}} diff --git a/src/test/java/io/swagger/codegen/v3/generators/java/SpringGeneratorCodegenTest.java b/src/test/java/io/swagger/codegen/v3/generators/java/SpringGeneratorCodegenTest.java index 2717890eca..8f38fa39da 100644 --- a/src/test/java/io/swagger/codegen/v3/generators/java/SpringGeneratorCodegenTest.java +++ b/src/test/java/io/swagger/codegen/v3/generators/java/SpringGeneratorCodegenTest.java @@ -197,4 +197,24 @@ public void testJava8Javax() throws Exception { folder.delete(); } + @Test(description = "verify that schema with required and nullable=true is generated correctly") + public void testNullableTrue() throws Exception { + final TemporaryFolder folder = new TemporaryFolder(); + folder.create(); + final File output = folder.getRoot(); + + final CodegenConfigurator configurator = new CodegenConfigurator() + .setLang("spring") + .setInputSpecURL("src/test/resources/3_0_0/cc32744.yaml") + .setOutputDir(output.getAbsolutePath()); + + // configurator.addAdditionalProperty("useNullableForNotNull", true); // DEFAULT + final ClientOptInput clientOptInput = configurator.toClientOptInput(); + new DefaultGenerator().opts(clientOptInput).generate(); + + final File file = new File(output, "src/main/java/io/swagger/model/NullableRoot.java"); + final String content = FileUtils.readFileToString(file); + Assert.assertFalse(content.contains("@NotNull")); + folder.delete(); + } } diff --git a/src/test/resources/3_0_0/cc32744.yaml b/src/test/resources/3_0_0/cc32744.yaml new file mode 100644 index 0000000000..0a08554aac --- /dev/null +++ b/src/test/resources/3_0_0/cc32744.yaml @@ -0,0 +1,32 @@ +openapi: 3.0.3 +info: + title: Title + version: "2.0.0" +paths: + '/test': + put: + description: description + operationId: updateTest + responses: + 200: + description: Successfully updated + content: + application/json: + schema: + $ref: '#/components/schemas/NullableRoot' +components: + schemas: + NullableRoot: + type: object + required: + - name + properties: + name: + $ref: '#/components/schemas/NullableRef' + NullableRef: + type: string + nullable: true + description: Nullable string + minLength: 0 + maxLength: 100 + example: 'example' \ No newline at end of file