Skip to content

[java][client] oneOf support for jackson clients #5120

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

Merged
merged 1 commit into from
Jan 27, 2020

Conversation

bkabrda
Copy link
Contributor

@bkabrda bkabrda commented Jan 27, 2020

This is exactly the same code as #4785 but submitted for 4.3.x branch (all the commits from that referenced PR were squashed to a single commit here).

PR checklist

  • Read the contribution guidelines.
  • If contributing template-only or documentation-only changes which will change sample output, build the project before.
  • Run the shell script(s) under ./bin/ (or Windows batch scripts under.\bin\windows) to update Petstore samples related to your fix. This is important, as CI jobs will verify all generator outputs of your HEAD commit, and these must match the expectations made by your contribution. You only need to run ./bin/{LANG}-petstore.sh, ./bin/openapi3/{LANG}-petstore.sh if updating the code or mustache templates for a language ({LANG}) (e.g. php, ruby, python, etc).
  • File the PR against the correct branch: master, 4.3.x, 5.0.x. Default: master.
  • Copy the technical committee to review the pull request if your PR is targeting a particular programming language.

@bbdouglas (2017/07) @sreeshas (2017/08) @jfiala (2017/08) @lukoyanov (2017/09) @cbornet (2017/09) @jeff9finger (2018/01) @karismann (2019/03) @Zomzog (2019/04) @lwlee2608 (2019/10)

@auto-labeler
Copy link

auto-labeler bot commented Jan 27, 2020

👍 Thanks for opening this issue!
🏷 I have applied any labels matching special text in your issue.

The team will review the labels and make any necessary changes.

Copy link
Member

@jimschubert jimschubert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thanks for re-targeting against 4.3.x to be on the safe side.

Potential (but not expected) breaking change: @JsonTypeInfo Annotations have changed the include type. Any code which might be using reflection against these should be updated.

Note that this change may overwrite existing generated documentation to include a new "Implemented Interfaces" section. If unwanted, a user will need to override the doc template.

@jimschubert jimschubert added this to the 4.3.0 milestone Jan 27, 2020
@jimschubert jimschubert merged commit a42ff37 into OpenAPITools:4.3.x Jan 27, 2020
keenanpepper added a commit to keenanpepper/openapi-generator that referenced this pull request Jan 29, 2020
instead of PROPERTY. This extends
OpenAPITools#5120 to JavaSpring.
keenanpepper added a commit to keenanpepper/openapi-generator that referenced this pull request Jan 31, 2020
This fixes issue OpenAPITools#3796 for JavaSpring. It's a very straightfoward extension
of OpenAPITools#5120 for the JavaSpring generator (that PR was just for the Java generator).
keenanpepper added a commit to keenanpepper/openapi-generator that referenced this pull request Feb 8, 2020
This fixes issue OpenAPITools#3796 for JavaSpring. It's a very straightfoward extension
of OpenAPITools#5120 for the JavaSpring generator (that PR was just for the Java generator).
jimschubert pushed a commit that referenced this pull request Feb 8, 2020
This fixes issue #3796 for JavaSpring. It's a very straightfoward extension
of #5120 for the JavaSpring generator (that PR was just for the Java generator).
@rkoehn
Copy link

rkoehn commented Feb 17, 2020

Hi @bkabrda, many thanks for addressing this long outstanding issue!

I tried generating code for a model using oneOf with master version acf8592 today, and ran into the issue that a number of classes OneOfXxxxYyyyy are imported and used in the generated code, but there are no Java files defining these types.

I looked at your changes, and was expecting those to be generated by the oneof_interface.mustache template, but it seems this template is never called.

Is this a known issue with this patch, or am I doing something wrong?

@bkabrda
Copy link
Contributor Author

bkabrda commented Feb 18, 2020

@rkoehn hey 👋 the OneOfXxxYyy should no longer get generated at all. What library (--library command line option) are you using? Do note that this was only implemented for clients that use Jackson as serialization library, the ones that use gson were left untouched by this change and so their behavior is probably still broken.

@jorgerod
Copy link
Contributor

jorgerod commented Feb 18, 2020

As @rkoehn, I tried generating code for a model using oneOf with master version and OneOfXxxYyy are imported and used in the generated code but there aren't these files.
I use Jackson as serialization library.

On the other hand, does this issue also solve the Spring code generator?

Thank you

@bkabrda
Copy link
Contributor Author

bkabrda commented Feb 18, 2020

So this change only affects Java clients. I don't use server codegens, so I usually don't touch them (however the approach that I used should be easily transferrable for server codegens IMO). Could one of you folks share the full commandline used for generating?

@rkoehn
Copy link

rkoehn commented Feb 19, 2020

I think I understand the problem I ran into a bit better now.

The command I used is:
generate -i openapi.yaml -g java --library jersey2 -o /tmp/javasdk

Everything works fine if I use oneOf at the top level of a schema with no additional propeties, effectively making this an interface. However, when I build an object with multiple properties, and one of them is a oneOf, then the generated code is broken.

A complete (broken) example is here:

openapi: "3.0.1"
info:
  version: "1.0.0"
  title: "oneOf Test"
paths:
  /magic:
    get:
      responses:
        200:
          $ref: '#/components/schemas/Response'
components:
  schemas:
    ResultA:
      type: "object"
      properties:
        type:
          type: "string"
    ResultB:
      type: "object"
      properties:
        type:
          type: "string"
    Response:
      type: "object"
      properties:
        message:
          type: "string"
        result:
          oneOf:
            - $ref: '#/components/schemas/ResultA'
            - $ref: '#/components/schemas/ResultB'
          discriminator:
            propertyName: type
            mapping:
              resultA: '#/components/schemas/ResultA'
              resultB: '#/components/schemas/ResultB'

The error occurring during maven build of the generated models is:

[ERROR] /tmp/javasdk/src/main/java/org/openapitools/client/model/Response.java:[24,37] cannot find symbol
[ERROR]   symbol:   class OneOfResultAResultB
[ERROR]   location: package org.openapitools.client.model
[ERROR] /tmp/javasdk/src/main/java/org/openapitools/client/model/Response.java:[40,11] cannot find symbol
[ERROR]   symbol:   class OneOfResultAResultB
[ERROR]   location: class org.openapitools.client.model.Response
...

If I redefine Response in the following way it works, without generating any additional classes:

    Response:
        oneOf:
          - $ref: '#/components/schemas/ResultA'
          - $ref: '#/components/schemas/ResultB'
        discriminator:
          propertyName: type
          mapping:
            resultA: '#/components/schemas/ResultA'
            resultB: '#/components/schemas/ResultB'

@dkirrane
Copy link

dkirrane commented Feb 19, 2020

@bkabrda +1 would like to see this for servers too. I'm using Maven plugin to generate SpringBoot server side code with:

            <plugin>
                <groupId>org.openapitools</groupId>
                <artifactId>openapi-generator-maven-plugin</artifactId>
                <version>4.2.3</version>
                <executions>
                    <execution>
                        <id>generate</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                        <configuration>
                            <inputSpec>${project.basedir}/src/main/resources/api.yaml</inputSpec>
                            <generatorName>spring</generatorName>
                            <generateSupportingFiles>true</generateSupportingFiles>
                            <configOptions>
                                <sourceFolder>src/gen/java/main</sourceFolder>
                                <library>spring-boot</library>
                                <java8>true</java8>
                                <interfaceOnly>true</interfaceOnly>
                                <skipDefaultInterface>false</skipDefaultInterface>
                                <delegatePattern>true</delegatePattern>
                            </configOptions>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

@bkabrda
Copy link
Contributor Author

bkabrda commented Feb 20, 2020

@rkoehn ah, I see. I thought I did cover that case, but apparently I didn't. Could you please open a new issue to get that tracked, including the reproducer you provided? I'll try to fix that, if I manage to find some time. Thanks!

@dkirrane yeah, so my PR didn't affect server generation at all, so that still has the old beavior. That said, it should be possible to extend Java server codegen to also use my approach. Could you please also open a new issue to get that tracked? I'm not sure I'll be able to find time to work on that, but I can definitely provide some pointers on how to do this for anyone who would want to work on it.

@rkoehn
Copy link

rkoehn commented Feb 20, 2020

Created a new bug report #5382 to track the issue discussed above.

@bkabrda, thanks for clarifying!

@lyuanlai
Copy link

openapi-generator version 4.3.0-SNAPSHOT, resttemplate with jackson.
Input spec:

parameters:
   Kind:
      description: The kind of application.
      in: query
      name: kind
      schema:
        $ref: '#/components/schemas/AppResourceKind'
schemas:
   AppResourceKind:
      enum:
      - web
      - native
      - service
      type: string
    AppResponseCreateUpdate:
      discriminator:
        mapping:
          native: '#/components/schemas/NativeApp'
          service: '#/components/schemas/ServiceApp'
          web: '#/components/schemas/WebApp'
        propertyName: kind
      oneOf:
      - $ref: '#/components/schemas/ServiceApp'
      - $ref: '#/components/schemas/WebApp'
      - $ref: '#/components/schemas/NativeApp'
    AppResponseGetList:
      discriminator:
        mapping:
          native: '#/components/schemas/NativeApp'
          service: '#/components/schemas/ServiceApp'
          web: '#/components/schemas/WebApp'
        propertyName: kind
      oneOf:
      - $ref: '#/components/schemas/ServiceApp'
      - $ref: '#/components/schemas/WebApp'
      - $ref: '#/components/schemas/NativeApp'
    CreateAppRequest:
      discriminator:
        mapping:
          native: '#/components/schemas/NativeAppPOST'
          service: '#/components/schemas/ServiceAppPOST'
          web: '#/components/schemas/WebAppPOST'
        propertyName: kind
      oneOf:
      - $ref: '#/components/schemas/ServiceAppPOST'
      - $ref: '#/components/schemas/WebAppPOST'
      - $ref: '#/components/schemas/NativeAppPOST'

Generated model codes:
AppResponseCreateUpdate.java

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "kind", visible = true)
@JsonSubTypes({
  @JsonSubTypes.Type(value = NativeApp.class, name = "native"),
  @JsonSubTypes.Type(value = ServiceApp.class, name = "service"),
  @JsonSubTypes.Type(value = WebApp.class, name = "web"),
})

public interface AppResponseCreateUpdate  {
    public String getKind();
}

NativeApp.java

@ApiModel(description = "A native kind app.")
@javax.annotation.Generated(.....)
public class NativeApp {
  ...
  @JsonProperty("kind")
  private AppResourceKind kind;
  ...

 /**
   * Get kind
   * @return kind
  **/
  @ApiModelProperty(required = true, value = "")
  public AppResourceKind getKind() {
    return kind;
  }

(WebApp.java, ServiceApp.java, and other related files all have the same problem, skipped pasting here)
Problems

  1. oneOf classes do not implement the interface
  2. interface and class has different return type, interface uses String, while class returns the correct type (AppResourceKind).

Is this related to implementations of this PR?

@bkabrda
Copy link
Contributor Author

bkabrda commented Feb 20, 2020

@lyuanlai yeah, I think the code now assumes that the discriminator property is always of type string, but in your PR it's an enum, which generates a new class in Java. Feel free to open a new issue report for that, I'll try to fix it if time permits.

@lyuanlai
Copy link

Understood. I think your reply answers 2), what about 1)? Is that the same root cause too?

@bkabrda
Copy link
Contributor Author

bkabrda commented Feb 20, 2020

@lyuanlai I think these two issues might be related. Feel free to put them in the same issue report and I'll try to investigate.

alexsuperdev added a commit to alexsuperdev/openapi-generator that referenced this pull request Mar 15, 2020
copy methods from OpenAPITools#5120 (oneOf support for jackson clients) to implement same in spring
alexsuperdev added a commit to alexsuperdev/openapi-generator that referenced this pull request Mar 15, 2020
copy methods from OpenAPITools#5120 (oneOf support for jackson clients) to implement same in spring
generate oneOf Class that has all properties from inherited classes
fix property name of inherited model for oneOf fix imports for oneOf
create oneOf stuff only if useOneOfInterfaces is setted
MikailBag pushed a commit to MikailBag/openapi-generator that referenced this pull request Mar 23, 2020
This fixes issue OpenAPITools#3796 for JavaSpring. It's a very straightfoward extension
of OpenAPITools#5120 for the JavaSpring generator (that PR was just for the Java generator).
@wing328
Copy link
Member

wing328 commented Apr 17, 2020

Hi all,

I've come up with a different implementation to support oneOf in the Java client (jersey2-experimental). Please give it a try and let me know if you've any feedback.

Example:

git clone https://github.com/OpenAPITools/openapi-generator
git checkout improve-java
mvn clean package -DskipTests
java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java --library jersey2-experimental -i https://raw.githubusercontent.com/OpenAPITools/openapi-generator/master/modules/openapi-generator/src/test/resources/3_0/oneOf.yaml -o /tmp/oneOfTest/ --skip-validate-spec

I did run some tests locally and the result is good so far.

UPDATE: added anyOf support as well.

@axredblack
Copy link

@wing328 This seems to be working. Thanks!
When can we expect it to be available in which release?

@wing328
Copy link
Member

wing328 commented Apr 22, 2020

Since it's an experimental implementation, we should be able to get it into 4.3.1 release.

As discussed with @bkabrda , we'll officially switch to this implementation in the 5.0.0 release (upcoming major release with breaking changes).

@axredblack
Copy link

dhanyawaad

@skycmoon
Copy link

Since it's an experimental implementation, we should be able to get it into 4.3.1 release.

As discussed with @bkabrda , we'll officially switch to this implementation in the 5.0.0 release (upcoming major release with breaking changes).

Awesome, what is the ETA for the 4.3.1 and 5.0.0?

@sbsmirnoff
Copy link

In 4.3.1 I still have the issue. I tried building 5.0.0-SNAPSHOT, still the same: OneOfx classes are not generated.

@wing328
Copy link
Member

wing328 commented Jun 11, 2020

Please try the latest master with jersey2 library of the java client generator.

@cicidi
Copy link

cicidi commented Sep 6, 2021

Please try the latest master with jersey2 library of the java client generator.

Can you give more detail I

Hi all,

I've come up with a different implementation to support oneOf in the Java client (jersey2-experimental). Please give it a try and let me know if you've any feedback.

Example:

git clone https://github.com/OpenAPITools/openapi-generator
git checkout improve-java
mvn clean package -DskipTests
java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java --library jersey2-experimental -i https://raw.githubusercontent.com/OpenAPITools/openapi-generator/master/modules/openapi-generator/src/test/resources/3_0/oneOf.yaml -o /tmp/oneOfTest/ --skip-validate-spec

I did run some tests locally and the result is good so far.

UPDATE: added anyOf support as well.

I followed this example, but it seems this branch no longer exists.
I try to install openapi-generator-cli and the cmd parameter to generate the cde,

sudo openapi-generator-cli  generate  -i src/main/resources/api.yml -g java --library jersey2

but it creates lots of useless file, which mess up my dev environment.

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

Successfully merging this pull request may close these issues.