Skip to content

[BUG][JAVA] Client generator ignores 'nullable' property #4530

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

Open
nocon opened this issue Nov 18, 2019 · 6 comments
Open

[BUG][JAVA] Client generator ignores 'nullable' property #4530

nocon opened this issue Nov 18, 2019 · 6 comments

Comments

@nocon
Copy link

nocon commented Nov 18, 2019

Description

Client generator ignores 'nullable' property of the object schema. Instead, javax.annotation.Nullable generation seems to be based on 'required' property. This behaviour is incorrect - required fields have to be provided, but not necessarily non-null, i.e { "field" : null } satisfies the 'required'.

Nullable is part of OpenAPI 3.0 spec. To make it more robust, x-nullable could be handled, too.

OpenAPI declaration file content or url
"components": {
   "schemas": {
      "SomeEntity": {
        "type": "object",
        "properties": {
          "name": {
            "id": "string",
            "nullable": false
          }
      }
   }
}
Related issues/PRs

#3264

@fenuks
Copy link

fenuks commented Jun 1, 2020

I will post it here, as it is somehow related (I think). I can see that support for @Nullable annotation was added in #3409, but using the latest version of openapi-generator at the moment (4.3.1) with petstore example, @Nullable is not added, even generated pom.xml doesn't contain findbugs dependency providing this annotation.

openapi-generator generate -g spring -i modules/openapi-generator/target/test-classes/3_0/petstore.yaml -o out

For example, in pet model @NotNull annotations are added, but @Nullable are completely missing. In this case, Pet schema does not explicitly mark values as nullable": true (according to specification false is default value), but since they are not in required list, and don't have default value, I think it should be implied they are nullable?

package org.openapitools.model;

import java.util.Objects;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.util.ArrayList;
import java.util.List;
import org.openapitools.model.Category;
import org.openapitools.model.Tag;
import org.openapitools.jackson.nullable.JsonNullable;
import javax.validation.Valid;
import javax.validation.constraints.*;

/**
 * A pet for sale in the pet store
 */
@ApiModel(description = "A pet for sale in the pet store")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2020-06-01T12:00:04.904781+02:00[Europe/Warsaw]")

public class Pet   {
  @JsonProperty("id")
  private Long id;

  @JsonProperty("category")
  private Category category;

  @JsonProperty("name")
  private String name;

  @JsonProperty("photoUrls")
  @Valid
  private List<String> photoUrls = new ArrayList<>();

  @JsonProperty("tags")
  @Valid
  private List<Tag> tags = null;

  /**
   * pet status in the store
   */
  public enum StatusEnum {
    AVAILABLE("available"),
    
    PENDING("pending"),
    
    SOLD("sold");

    private String value;

    StatusEnum(String value) {
      this.value = value;
    }

    @JsonValue
    public String getValue() {
      return value;
    }

    @Override
    public String toString() {
      return String.valueOf(value);
    }

    @JsonCreator
    public static StatusEnum fromValue(String value) {
      for (StatusEnum b : StatusEnum.values()) {
        if (b.value.equals(value)) {
          return b;
        }
      }
      throw new IllegalArgumentException("Unexpected value '" + value + "'");
    }
  }

  @JsonProperty("status")
  private StatusEnum status;

  public Pet id(Long id) {
    this.id = id;
    return this;
  }

  /**
   * Get id
   * @return id
  */
  @ApiModelProperty(value = "")


  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public Pet category(Category category) {
    this.category = category;
    return this;
  }

  /**
   * Get category
   * @return category
  */
  @ApiModelProperty(value = "")

  @Valid

  public Category getCategory() {
    return category;
  }

  public void setCategory(Category category) {
    this.category = category;
  }

  public Pet name(String name) {
    this.name = name;
    return this;
  }

  /**
   * Get name
   * @return name
  */
  @ApiModelProperty(example = "doggie", required = true, value = "")
  @NotNull


  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Pet photoUrls(List<String> photoUrls) {
    this.photoUrls = photoUrls;
    return this;
  }

  public Pet addPhotoUrlsItem(String photoUrlsItem) {
    this.photoUrls.add(photoUrlsItem);
    return this;
  }

  /**
   * Get photoUrls
   * @return photoUrls
  */
  @ApiModelProperty(required = true, value = "")
  @NotNull


  public List<String> getPhotoUrls() {
    return photoUrls;
  }

  public void setPhotoUrls(List<String> photoUrls) {
    this.photoUrls = photoUrls;
  }

  public Pet tags(List<Tag> tags) {
    this.tags = tags;
    return this;
  }

  public Pet addTagsItem(Tag tagsItem) {
    if (this.tags == null) {
      this.tags = new ArrayList<>();
    }
    this.tags.add(tagsItem);
    return this;
  }

  /**
   * Get tags
   * @return tags
  */
  @ApiModelProperty(value = "")

  @Valid

  public List<Tag> getTags() {
    return tags;
  }

  public void setTags(List<Tag> tags) {
    this.tags = tags;
  }

  public Pet status(StatusEnum status) {
    this.status = status;
    return this;
  }

  /**
   * pet status in the store
   * @return status
  */
  @ApiModelProperty(value = "pet status in the store")


  public StatusEnum getStatus() {
    return status;
  }

  public void setStatus(StatusEnum status) {
    this.status = status;
  }
  // rest ommited
}

@SimonScholz
Copy link

I can confirm that.
Pretty unfortunate, because this would be very helpful for using the generated model in Kotlin code.

@kkosmrli
Copy link

I also ran into this behaviour today. At the moment it seems the only option is to use required to mark fields with @NotNull. Unfortunately this means that the generated code does not match the specification. According to OAS 3 required just marks a field as mandatory in the object e.g. {"field" : null} and {"field" : "value"} is valid and {} is not.

@alwaysbemark
Copy link

The semantics posted by @kkosmrli are what I would expect too. This issue is also impacting us and requires manual post-processing of the generated files. It would be nice to get a fix for this. Thank you!

@ipotseluev
Copy link

I can see the same issue with python aiohttp generator

@gwi42
Copy link

gwi42 commented Oct 30, 2024

We see the same behaviour in the Java generator.
Properties marked as required and nullable: true end up beeing generated:

  @NotNull 
  @Schema(name = "propertyName", requiredMode = Schema.RequiredMode.REQUIRED)

We'd as well be happy, if this could be addressed :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants