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

[BUG] Description #16091

Open
6 tasks
quesojeany opened this issue Jul 13, 2023 · 1 comment
Open
6 tasks

[BUG] Description #16091

quesojeany opened this issue Jul 13, 2023 · 1 comment

Comments

@quesojeany
Copy link

Bug Report Checklist

  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • Have you tested with the latest master to confirm the issue still exists?
  • Have you searched for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Sponsorship to speed up the bug fix or feature request (example)
Description

Using 7.0.0-beta of the openapi-generator-maven-plugin (library = spring-boot):

Enum Type as discriminator feature #13846 (oneOf interface polymorphism) ignores modelNameSuffix specified in the plugin configurations when generating the abstract get{Discriminator}() method for the interface, but is considering it while generating the enum and concrete class' relevant discriminator field and set/get methods. This results in a compile time error (incompatible types).

Presume this is an issue for modelNamePrefix as well.

openapi-generator version

7.0.0-beta

OpenAPI declaration file content or url

Yaml:

    FruitType:
      enum:
      - APPLE
      - BANANA
      type: string
    Fruit:
      discriminator:
        mapping:
          APPLE: '#/components/schemas/Apple'
          BANANA: '#/components/schemas/Banana'
        propertyName: fruitType
      oneOf:
      - $ref: '#/components/schemas/Apple'
      - $ref: '#/components/schemas/Banana'
      properties:
        fruitType:
          $ref: '#/components/schemas/FruitType'
      required:
      - fruitType
      type: object
    Apple:
      properties:
        seeds:
          type: integer
      required:
      - seeds
      type: object
    Banana:
      properties:
        length:
          type: integer
      required:
      - length
      type: object
Generation Details

Maven plugin (the bits that matter):

<configuration>
	<generatorName>spring</generatorName>
	<library>spring-boot</library>
	<modelNameSuffix>Dto</modelNameSuffix>
	....
</configuration>
Generated Discriminator Enum, Interface and Concrete classes

Enum:


@Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
public enum FruitTypeDto {

  APPLE("APPLE"),

  BANANA("BANANA");

  private String value;

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

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

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

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

Interface:


@JsonIgnoreProperties(
  value = "fruitType", // ignore manually set fruitType, it will be automatically generated by Jackson during serialization
  allowSetters = true // allows the fruitType to be set during deserialization
)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "fruitType", visible = true)
@JsonSubTypes({
  @JsonSubTypes.Type(value = Apple.class, name = "APPLE"),
  @JsonSubTypes.Type(value = Apple.class, name = "Apple"),
  @JsonSubTypes.Type(value = Banana.class, name = "BANANA"),
  @JsonSubTypes.Type(value = Banana.class, name = "Banana")
})

@Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
public interface Fruit {
    public FruitType getFruitType();
}

Concrete class (Banana is generated the same considering the model suffix)


@Generated(value = "org.openapitools.codegen.languages.SpringCodegen")
public class Apple implements Fruit {

  private Integer seeds;

  private FruitTypeDto fruitType;

  public Apple() {
    super();
  }

  /**
   * Constructor with only required parameters
   */
  public Apple(Integer seeds) {
    this.seeds = seeds;
    this.fruitType = fruitType;
  }

  public Apple seeds(Integer seeds) {
    this.seeds = seeds;
    return this;
  }

  /**
   * Get seeds
   * @return seeds
  */
  @NotNull 
  @Schema(name = "seeds", requiredMode = Schema.RequiredMode.REQUIRED)
  @JsonProperty("seeds")
  public Integer getSeeds() {
    return seeds;
  }

  public void setSeeds(Integer seeds) {
    this.seeds = seeds;
  }

  public Apple fruitType(FruitTypeDto fruitType) {
    this.fruitType = fruitType;
    return this;
  }

  /**
   * Get fruitType
   * @return fruitType
  */
  @NotNull @Valid 
  @Schema(name = "fruitType", requiredMode = Schema.RequiredMode.REQUIRED)
  @JsonProperty("fruitType")
  public FruitTypeDto getFruitType() {
    return fruitType;
  }

  public void setFruitType(FruitTypeDto fruitType) {
    this.fruitType = fruitType;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    Apple apple = (Apple) o;
    return Objects.equals(this.seeds, apple.seeds) &&
        Objects.equals(this.fruitType, apple.fruitType);
  }

  @Override
  public int hashCode() {
    return Objects.hash(seeds, fruitType);
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("class Apple {\n");
    sb.append("    seeds: ").append(toIndentedString(seeds)).append("\n");
    sb.append("    fruitType: ").append(toIndentedString(fruitType)).append("\n");
    sb.append("}");
    return sb.toString();
  }

  /**
   * Convert the given object to string with each line indented by 4 spaces
   * (except the first line).
   */
  private String toIndentedString(Object o) {
    if (o == null) {
      return "null";
    }
    return o.toString().replace("\n", "\n    ");
  }
}
Steps to reproduce
Related issues/PRs

#13846

Suggest a fix/workaround

current workarounds:

  1. if one wants to leverage oneOf which generates Interface and concrete classes: don't configure modelNameSuffix/modelNamePrefix
  2. if one wants to keep the modelNameSuffix/Prefix: Leverage composition instead--> Generate base concrete class and subclasses by removing oneOf and specify allOf in the child schemas. The resulting generated code retains the DTO in the base concrete model class.

sample yaml for option two workaround


    FruitType:
      enum:
        - APPLE
        - BANANA
      type: string
    Fruit:
      discriminator:
        mapping:
          APPLE: '#/components/schemas/Apple'
          BANANA: '#/components/schemas/Banana'
        propertyName: fruitType
      properties:
        fruitType:
          $ref: '#/components/schemas/FruitType'
      required:
        - fruitType
      type: object
    Apple:
      allOf:
        - $ref: '#/components/schemas/Fruit'
        - properties:
            seeds:
              type: integer
          required:
            - seeds
          type: object
    Banana:
      allOf:
        - $ref: '#/components/schemas/Fruit'
        - properties:
            length:
              type: integer
          required:
            - length
          type: object
  
@aparoha
Copy link

aparoha commented Jul 13, 2023

Thanks @quesojeany for reporting the bug. I am also facing the same issue. <modelNameSuffix>Dto</modelNameSuffix> stopped working after upgrading plugin version to "7.0.0-beta"

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

2 participants