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

Adds @Nullable annotation to Spring Boot generator #20345

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,7 @@ public void processOpts() {
convertPropertyToStringAndWriteBack(RESOURCE_FOLDER, this::setResourceFolder);

typeMapping.put("file", "org.springframework.core.io.Resource");
importMapping.put("Nullable", "org.springframework.lang.Nullable");
importMapping.put("org.springframework.core.io.Resource", "org.springframework.core.io.Resource");
importMapping.put("DateTimeFormat", "org.springframework.format.annotation.DateTimeFormat");
importMapping.put("ApiIgnore", "springfox.documentation.annotations.ApiIgnore");
Expand Down Expand Up @@ -952,6 +953,11 @@ public void postProcessModelProperty(CodegenModel model, CodegenProperty propert
if (model.getVendorExtensions().containsKey("x-jackson-optional-nullable-helpers")) {
model.imports.add("Arrays");
}

// to prevent inheritors (JavaCamelServerCodegen etc.) mistakenly use it
if (getName().contains("spring")) {
model.imports.add("Nullable");
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{^required}}{{^defaultValue}}{{^useOptional}}{{#openApiNullable}}{{^isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{^openApiNullable}}@Nullable {{/openApiNullable}}{{/useOptional}}{{/defaultValue}}{{#defaultValue}}{{^openApiNullable}}{{#isNullable}}@Nullable {{/isNullable}}{{/openApiNullable}}{{/defaultValue}}{{/required}}
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ public class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}{{^parent}}
{{#isContainer}}
{{#useBeanValidation}}@Valid{{/useBeanValidation}}
{{#openApiNullable}}
private {{#isNullable}}{{>nullableDataTypeBeanValidation}} {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined();{{/isNullable}}{{^required}}{{^isNullable}}{{>nullableDataTypeBeanValidation}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/isNullable}}{{/required}}{{#required}}{{^isNullable}}{{>nullableDataTypeBeanValidation}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/isNullable}}{{/required}}
private {{>nullableAnnotation}}{{#isNullable}}{{>nullableDataTypeBeanValidation}} {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined();{{/isNullable}}{{^required}}{{^isNullable}}{{>nullableDataTypeBeanValidation}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/isNullable}}{{/required}}{{#required}}{{^isNullable}}{{>nullableDataTypeBeanValidation}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/isNullable}}{{/required}}
{{/openApiNullable}}
{{^openApiNullable}}
private {{>nullableDataType}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};
private {{>nullableAnnotation}}{{>nullableDataType}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};
{{/openApiNullable}}
{{/isContainer}}
{{^isContainer}}
Expand All @@ -86,10 +86,10 @@ public class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}{{^parent}}
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
{{/isDateTime}}
{{#openApiNullable}}
private {{#isNullable}}{{>nullableDataTypeBeanValidation}} {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined();{{/isNullable}}{{^required}}{{^isNullable}}{{>nullableDataTypeBeanValidation}} {{name}}{{#useOptional}} = Optional.{{^defaultValue}}empty(){{/defaultValue}}{{#defaultValue}}of({{{.}}}){{/defaultValue}};{{/useOptional}}{{^useOptional}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/useOptional}}{{/isNullable}}{{/required}}{{^isNullable}}{{#required}}{{>nullableDataTypeBeanValidation}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/required}}{{/isNullable}}
private {{>nullableAnnotation}}{{#isNullable}}{{>nullableDataTypeBeanValidation}} {{name}} = JsonNullable.<{{{datatypeWithEnum}}}>undefined();{{/isNullable}}{{^required}}{{^isNullable}}{{>nullableDataTypeBeanValidation}} {{name}}{{#useOptional}} = Optional.{{^defaultValue}}empty(){{/defaultValue}}{{#defaultValue}}of({{{.}}}){{/defaultValue}};{{/useOptional}}{{^useOptional}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/useOptional}}{{/isNullable}}{{/required}}{{^isNullable}}{{#required}}{{>nullableDataTypeBeanValidation}} {{name}}{{#defaultValue}} = {{{.}}}{{/defaultValue}};{{/required}}{{/isNullable}}
{{/openApiNullable}}
{{^openApiNullable}}
private {{>nullableDataType}} {{name}}{{#isNullable}} = null{{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}};
private {{>nullableAnnotation}}{{>nullableDataType}} {{name}}{{#isNullable}} = null{{/isNullable}}{{^isNullable}}{{#defaultValue}} = {{{.}}}{{/defaultValue}}{{/isNullable}};
{{/openApiNullable}}
{{/isContainer}}
{{/vars}}
Expand Down Expand Up @@ -130,7 +130,7 @@ public class {{classname}}{{#parent}} extends {{{parent}}}{{/parent}}{{^parent}}
/**
* Constructor with all args parameters
*/
public {{classname}}({{#vendorExtensions.x-java-all-args-constructor-vars}}{{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-java-all-args-constructor-vars}}) {
public {{classname}}({{#vendorExtensions.x-java-all-args-constructor-vars}}{{>nullableAnnotation}}{{{datatypeWithEnum}}} {{name}}{{^-last}}, {{/-last}}{{/vendorExtensions.x-java-all-args-constructor-vars}}) {
{{#parent}}
super({{#parentVars}}{{name}}{{^-last}}, {{/-last}}{{/parentVars}});
{{/parent}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,18 @@ public PropertyAssert withType(final String expectedType) {
public PropertyAnnotationsAssert assertPropertyAnnotations() {
return new PropertyAnnotationsAssert(this, actual.getAnnotations());
}

public PropertyAnnotationsAssert doesNotHaveAnnotation(String annotationName) {
return new PropertyAnnotationsAssert(
this,
actual.getAnnotations()
).doesNotContainWithName(annotationName);
}

public PropertyAnnotationsAssert hasAnnotation(String annotationName) {
return new PropertyAnnotationsAssert(
this,
actual.getAnnotations()
).containsWithName(annotationName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4893,14 +4893,14 @@ public void testCollectionTypesWithDefaults_issue_18102() throws IOException {
.collect(Collectors.toMap(File::getName, Function.identity()));

JavaFileAssert.assertThat(files.get("PetDto.java"))
.fileContains("private List<@Valid TagDto> tags")
.fileContains("private @Nullable List<@Valid TagDto> tags")
.fileContains("private List<@Valid TagDto> tagsDefaultList = new ArrayList<>()")
.fileContains("private Set<@Valid TagDto> tagsUnique")
.fileContains("private @Nullable Set<@Valid TagDto> tagsUnique")
.fileContains("private Set<@Valid TagDto> tagsDefaultSet = new LinkedHashSet<>();")
.fileContains("private List<String> stringList")
.fileContains("private @Nullable List<String> stringList")
.fileContains("private List<String> stringDefaultList = new ArrayList<>(Arrays.asList(\"A\", \"B\"));")
.fileContains("private List<String> stringEmptyDefaultList = new ArrayList<>();")
.fileContains("Set<String> stringSet")
.fileContains("@Nullable Set<String> stringSet")
.fileContains("private Set<String> stringDefaultSet = new LinkedHashSet<>(Arrays.asList(\"A\", \"B\"));")
.fileContains("private Set<String> stringEmptyDefaultSet = new LinkedHashSet<>();")
.fileDoesNotContain("private List<@Valid TagDto> tags = new ArrayList<>()")
Expand Down Expand Up @@ -5099,4 +5099,90 @@ public void testEnumUnknownDefaultCaseDeserializationNotSet_issue13241() throws
.assertMethod("build")
.doesNotHaveAnnotation("Deprecated");
}

@Test
public void shouldAnnotateNonRequiredFieldsAsNullable() throws IOException {
SpringCodegen codegen = new SpringCodegen();
codegen.setLibrary(SPRING_BOOT);
codegen.setGenerateConstructorWithAllArgs(true);

Map<String, File> files = generateFiles(codegen, "src/test/resources/3_0/nullable-annotation.yaml");
var file = files.get("Item.java");

JavaFileAssert.assertThat(file)
.assertProperty("mandatoryName")
.doesNotHaveAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.assertProperty("optionalDescription")
.hasAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.assertProperty("optionalOneWithDefault")
.doesNotHaveAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.assertProperty("nullableStr")
.doesNotHaveAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.fileContains(
"public Item(String mandatoryName, @Nullable String optionalDescription," +
" String optionalOneWithDefault, String nullableStr)"
);
}

@Test
public void shouldNotAnnotateNonRequiredFieldsAsNullableWhileUseOptional() throws IOException {
SpringCodegen codegen = new SpringCodegen();
codegen.setLibrary(SPRING_BOOT);
codegen.setGenerateConstructorWithAllArgs(true);
codegen.setUseOptional(true);

Map<String, File> files = generateFiles(codegen, "src/test/resources/3_0/nullable-annotation.yaml");
var file = files.get("Item.java");

JavaFileAssert.assertThat(file)
.assertProperty("mandatoryName")
.doesNotHaveAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.assertProperty("optionalDescription")
.doesNotHaveAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.assertProperty("optionalOneWithDefault")
.doesNotHaveAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.assertProperty("nullableStr")
.doesNotHaveAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.fileContains(
"public Item(String mandatoryName, String optionalDescription," +
" String optionalOneWithDefault, String nullableStr)"
);
}

@Test
public void shouldAnnotateNonRequiredFieldsAsNullableWhileNotUsingOpenApiNullable() throws IOException {
SpringCodegen codegen = new SpringCodegen();
codegen.setLibrary(SPRING_BOOT);
codegen.setGenerateConstructorWithAllArgs(true);
codegen.setOpenApiNullable(false);

Map<String, File> files = generateFiles(codegen, "src/test/resources/3_0/nullable-annotation.yaml");
var file = files.get("Item.java");

JavaFileAssert.assertThat(file)
.assertProperty("mandatoryName")
.doesNotHaveAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.assertProperty("optionalDescription")
.hasAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.assertProperty("optionalOneWithDefault")
.doesNotHaveAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.assertProperty("nullableStr")
.hasAnnotation("Nullable");
JavaFileAssert.assertThat(file)
.fileContains(
"public Item(String mandatoryName, @Nullable String optionalDescription," +
" String optionalOneWithDefault, @Nullable String nullableStr)"
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
openapi: 3.0.0
components:
schemas:
Item:
type: object
required:
- mandatoryName
properties:
mandatoryName:
type: String
optionalDescription:
type: string
optionalOneWithDefault:
type: string
default: "someDefaultValue"
nullableStr:
type: String
nullable: true
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.time.LocalDate;
import java.time.OffsetDateTime;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.lang.Nullable;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import org.springframework.lang.Nullable;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
Expand All @@ -22,9 +23,9 @@
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.11.0-SNAPSHOT")
public class Category {

private Long id;
private @Nullable Long id;

private String name;
private @Nullable String name;

public Category id(Long id) {
this.id = id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonTypeName;
import org.springframework.lang.Nullable;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
Expand All @@ -24,11 +25,11 @@
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.11.0-SNAPSHOT")
public class ModelApiResponse {

private Integer code;
private @Nullable Integer code;

private String type;
private @Nullable String type;

private String message;
private @Nullable String message;

public ModelApiResponse code(Integer code) {
this.code = code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.fasterxml.jackson.annotation.JsonValue;
import java.time.OffsetDateTime;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.lang.Nullable;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
Expand All @@ -27,14 +28,14 @@
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.11.0-SNAPSHOT")
public class Order {

private Long id;
private @Nullable Long id;

private Long petId;
private @Nullable Long petId;

private Integer quantity;
private @Nullable Integer quantity;

@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME)
private OffsetDateTime shipDate;
private @Nullable OffsetDateTime shipDate;

/**
* Order Status
Expand Down Expand Up @@ -73,7 +74,7 @@ public static StatusEnum fromValue(String value) {
}
}

private StatusEnum status;
private @Nullable StatusEnum status;

private Boolean complete = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.List;
import org.openapitools.model.Category;
import org.openapitools.model.Tag;
import org.springframework.lang.Nullable;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
Expand All @@ -28,9 +29,9 @@
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.11.0-SNAPSHOT")
public class Pet {

private Long id;
private @Nullable Long id;

private Category category;
private @Nullable Category category;

private JsonNullable<String> name = JsonNullable.<String>undefined();

Expand Down Expand Up @@ -78,7 +79,7 @@ public static StatusEnum fromValue(String value) {
}
}

private StatusEnum status;
private @Nullable StatusEnum status;

public Pet() {
super();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import org.springframework.lang.Nullable;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
Expand All @@ -22,9 +23,9 @@
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.11.0-SNAPSHOT")
public class Tag {

private Long id;
private @Nullable Long id;

private String name;
private @Nullable String name;

public Tag id(Long id) {
this.id = id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import org.springframework.lang.Nullable;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
Expand All @@ -22,21 +23,21 @@
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.11.0-SNAPSHOT")
public class User {

private Long id;
private @Nullable Long id;

private String username;
private @Nullable String username;

private String firstName;
private @Nullable String firstName;

private String lastName;
private @Nullable String lastName;

private String email;
private @Nullable String email;

private String password;
private @Nullable String password;

private String phone;
private @Nullable String phone;

private Integer userStatus;
private @Nullable Integer userStatus;

public User id(Long id) {
this.id = id;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.lang.Nullable;
import org.openapitools.jackson.nullable.JsonNullable;
import java.time.OffsetDateTime;
import javax.validation.Valid;
Expand All @@ -23,9 +24,9 @@
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", comments = "Generator version: 7.11.0-SNAPSHOT")
public class Category {

private Long id;
private @Nullable Long id;

private String name;
private @Nullable String name;

public Category id(Long id) {
this.id = id;
Expand Down
Loading