OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.8k stars 6.58k forks source link

[BUG][JAVA] Generated model contains double JsonTypeName annotation #8559

Open andersflemmen opened 3 years ago

andersflemmen commented 3 years ago

Bug Report Checklist

Description

The attached spec seems to be causing some problems with version 5.0.0 of the openapi-generator-maven-plugin. It creates this model class, which has two JsonTypeName annotations, causing a compilation error:

@JsonPropertyOrder({
  First.JSON_PROPERTY_PROP
})
@JsonTypeName("first")
@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen", date = "2021-01-28T07:47:45.336926+01:00[Europe/Oslo]")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "discriminator", visible = true)
@JsonTypeName("first")

public class First extends Parent {
...
}

generated-sources/openapi/src/main/java/test/model/First.java:[40,1] com.fasterxml.jackson.annotation.JsonTypeName is not a repeatable annotation type

openapi-generator version

openapi-generator-maven-plugin, version 5.0.0

OpenAPI declaration file content or url

https://gist.github.com/andersflemmen/8e4eddc90b7293fd55f4d75c637b3502

Generation Details

Maven configuration

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>5.0.0</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
                <inputSpec>${project.basedir}/openapi.json</inputSpec>
                <generatorName>java</generatorName>
                <configOptions>
                    <dateLibrary>java8</dateLibrary>
                    <feignVersion>10.x</feignVersion>
                    <java8>true</java8>
                </configOptions>
                <modelPackage>test.model</modelPackage>
                <apiPackage>test.api</apiPackage>
                <invokerPackage>test.invoker</invokerPackage>
                <generateApiTests>false</generateApiTests>
                <generateModelTests>false</generateModelTests>
                <generateSupportingFiles>true</generateSupportingFiles>
                <library>feign</library>
                <addCompileSourceRoot>true</addCompileSourceRoot>
            </configuration>
        </execution>
    </executions>
</plugin>
stefluhh commented 3 years ago

Happens with 5.1.0 as well and happens only when I am using the modelNameSuffix directive.

yasammez commented 3 years ago

Can confirm on 5.1.0 with modelNamePrefix as well.

AppyArora commented 3 years ago

I am getting the same issue with the following config:

                        <plugin>
            <groupId>org.openapitools</groupId>
            <artifactId>openapi-generator-maven-plugin</artifactId>
            <version>5.1.0</version>
            <executions>
                <execution>
                    <id>test specs</id>
                    <goals>
                        <goal>generate</goal>
                    </goals>
                    <configuration>
                        <generatorName>java</generatorName>
                        <generateApis>false</generateApis>
                        <generateModels>true</generateModels>
                        <generateModelDocumentation>false</generateModelDocumentation>
                        <generateModelTests>false</generateModelTests>
                        <generateSupportingFiles>false</generateSupportingFiles>
                        <inputSpec>${project.basedir}/src/main/resources/yaml/test.yaml</inputSpec>
                        <output>${project.basedir}/target/generated-sources</output>
                        <modelPackage>test.model</modelPackage>
                        <configOptions>
                            <dateLibrary>java8</dateLibrary>
                            <sourceFolder>src/main/java</sourceFolder>
                            <java8>true</java8>
                            <library>webclient</library>
                            <hideGenerationTimestamp>true</hideGenerationTimestamp>
                        </configOptions>
                    </configuration>
marcelstoer commented 3 years ago

I get this with 5.1.0 on ~all~ some of those generated xxxAllOf model classes (have yet to figure out what they are anyway).

marcelstoer commented 3 years ago

This is caused by the fact that @JsonTypeName is present twice:

I suspect this bug has been present for about 10 months. @wing328 with #6995 you added it to pojo.mustache - about a month after it was added to typeInfoAnnotation.mustache with #6551.

sarumont commented 3 years ago

If anyone needs to work around this, here's a workaround. You can season to taste for the files you're seeing this in. 😎

sed -i '0,/@JsonTypeName(\"Org_2\")/s///' ./src/main/java/com/devsupply/dana/model/Org2.java

marcelstoer commented 3 years ago

We work around it at build time using the replacer Maven plugin like so:

<plugin>
    <groupId>com.google.code.maven-replacer-plugin</groupId>
    <artifactId>replacer</artifactId>
    <version>1.5.3</version>
    <executions>
        <!-- to address https://github.com/OpenAPITools/openapi-generator/issues/8559 -->
        <!-- removes the 2nd @JsonTypeName in the below snippet
        @JsonTypeName("Classname_allOf")
        ...some more annotations...
        @JsonTypeName("Classname_allOf")

        public class ClassnameAllOf {
        -->
        <execution>
            <id>remove-2nd-json-type-name-for-allof-types</id>
            <phase>process-resources</phase>
            <goals>
                <goal>replace</goal>
            </goals>
            <configuration>
                <basedir>${basedir}/target/generated-sources/openapi</basedir>
                <filesToInclude>**/*AllOf.java</filesToInclude>
                <regex>true</regex>
                <token>@JsonTypeName\(".*_allOf"\)\n\n</token>
                <value />
            </configuration>
        </execution>
    </executions>
</plugin>
sarumont commented 3 years ago

Oh, I should mention that I'm seeing this with multiple levels of inheritance - Foo extends Bar extends Baz. In OpenAPI parlance, Foo has allOf Bar has allOf Baz. Prefix and Suffix are unspecified, so those are the defaults.

takkiraz commented 3 years ago

Same problem with modelNameSuffix and resttemplate library in version 5.1.0. Changing the library to jersey2 works for me.

laidani commented 2 years ago

Same problem with modelNameSuffix and modelNamePrefix in version 5.1.1

ghost commented 2 years ago

Same problem with modelNameSuffix and modelNamePrefix in version 5.3.0

sebastianblesgen commented 2 years ago

Same with modelNameSuffix or modelNamePrefix and version 5.3.1

gervaisb commented 2 years ago

Like @zakamai we do have the same issue with the scala-play-server-template. The generated classes are correct, with the suffix added only once. But all properties have the suffix added twice:

case class AddressDto(
  streetName: Option[StringDtoDto],
  // ...
) 

The double suffix happens for all properties (of any type).

stefluhh commented 2 years ago

Also happens in 5.4.0

stefluhh commented 2 years ago

We are using the suggested solution from @marcelstoer in 2 of our applications and it works, however with the suggested configuration above it didn't work, but with this one:

<configuration>
...
...
<filesToInclude>**/*.java</filesToInclude>
<regex>true</regex>
<token>@JsonTypeName\(".*"\)\n\n</token>
<value />
...
...
</configuration>
jorgerod commented 2 years ago

It's work for me in 6.0.0

I think that this PR fix this issue.

/*
 * title
 * No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
 *
 * The version of the OpenAPI document: 1
 * 
 *
 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
 * https://openapi-generator.tech
 * Do not edit the class manually.
 */

package org.openapitools.client.model;

import java.util.Objects;
import java.util.Arrays;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.annotation.JsonValue;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.openapitools.client.model.FirstAllOf;
import org.openapitools.client.model.Parent;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonTypeName;

/**
 * First
 */
@JsonPropertyOrder({
  First.JSON_PROPERTY_PROP
})
@JsonTypeName("first")
@javax.annotation.Generated(value = "com.inditex.aqsw.framework.common.rest.client.codegen.AmigaJavaClientCodegen", date = "2022-07-05T13:28:03.231286+02:00[Europe/Madrid]")
@JsonIgnoreProperties(
  value = "discriminator", // ignore manually set discriminator, it will be automatically generated by Jackson during serialization
  allowSetters = true // allows the discriminator to be set during deserialization
)
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "discriminator", visible = true)

public class First extends Parent {
  public static final String JSON_PROPERTY_PROP = "prop";
  private String prop;

  public First() { 
  }

  public First prop(String prop) {

    this.prop = prop;
    return this;
  }

   /**
   * Get prop
   * @return prop
  **/
  @javax.annotation.Nullable
  @ApiModelProperty(value = "")
  @JsonProperty(JSON_PROPERTY_PROP)
  @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)

  public String getProp() {
    return prop;
  }

  @JsonProperty(JSON_PROPERTY_PROP)
  @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS)
  public void setProp(String prop) {
    this.prop = prop;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }
    First first = (First) o;
    return Objects.equals(this.prop, first.prop) &&
        super.equals(o);
  }

  @Override
  public int hashCode() {
    return Objects.hash(prop, super.hashCode());
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("class First {\n");
    sb.append("    ").append(toIndentedString(super.toString())).append("\n");
    sb.append("    prop: ").append(toIndentedString(prop)).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    ");
  }

}