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][java] oneOf in "anonymous" schemas: model not generated. #5903

Open
5 of 6 tasks
jfeltesse-mdsol opened this issue Apr 13, 2020 · 16 comments
Open
5 of 6 tasks

[BUG][java] oneOf in "anonymous" schemas: model not generated. #5903

jfeltesse-mdsol opened this issue Apr 13, 2020 · 16 comments
Labels
Client: Java Inline Schema Handling Schema contains a complex schema in items/additionalProperties/allOf/oneOf/anyOf Issue: Bug

Comments

@jfeltesse-mdsol
Copy link
Contributor

jfeltesse-mdsol commented Apr 13, 2020

Bug Report Checklist
  • Have you provided a full/minimal spec to reproduce the issue?
  • Have you validated the input using an OpenAPI validator (example)?
  • What's the version of OpenAPI Generator used?
  • Have you search for related issues/PRs?
  • What's the actual output vs expected output?
  • [Optional] Bounty to sponsor the fix (example)
Description

As mentioned in #5706, if the openapi spec file defines a oneOf schema in responses "as is", the generator references a model in the DefaultApi but the model doesn't get generated. See examples below.
edit: the same issue happens to all the places where oneOf is used without being directly under a named schema (e.g. responses, array items etc.). See the comments.

openapi-generator version

latest master: cc09118

OpenAPI declaration file content or url

OpenApi spec file:

openapi: 3.0.3
info:
  title: oneOf test
  version: '1.0'
paths:
  /state:
    get:
      operationId: getState
      responses:
        '200':
          description: get states
          content:
            application/json:
              schema: # doesn't work
                oneOf:
                  - $ref: '#/components/schemas/ObjA'
                  - $ref: '#/components/schemas/ObjB'
                discriminator:
                  propertyName: realtype
  /state_ref:
    get:
      operationId: getStateRef
      responses:
        '200':
          description: 'get states using $ref'
          content:
            application/json:
              schema: # logically same as above but works
                $ref: '#/components/schemas/ObjAOrObjB'
components:
  schemas:
    ObjA:
      type: object
      properties:
        realtype:
          type: string
        message:
          type: string
    ObjB:
      type: object
      properties:
        realtype:
          type: string
        code:
          type: integer
    ObjAOrObjB:
      oneOf:
        - $ref: '#/components/schemas/ObjA'
        - $ref: '#/components/schemas/ObjB'
      discriminator:
        propertyName: realtype
Command line used for generation

openapi-generator generate -i spec.yaml -g java --library native -o ./out/java_oneof

Steps to reproduce
  • use spec file above
  • run command above
  • notice how src/main/java/org/openapitools/client/api/DefaultApi.java features
    import org.openapitools.client.model.GetState200;
    import org.openapitools.client.model.ObjAOrObjB;
    
    but the GetState200 model is not present in src/main/java/org/openapitools/client/model while ObjAOrObjB is.

@bbdouglas @sreeshas @jfiala @lukoyanov @cbornet @jeff9finger @karismann @Zomzog @lwlee2608 @bkabrda

@auto-labeler
Copy link

auto-labeler bot commented Apr 13, 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.

@sebastien-rosset
Copy link
Contributor

In the title you mentioned "interface not generated" and in the description you state "the model doesn't get generated". What do you have in mind? Were you thinking about generating Java interfaces? One way to think about it is for each oneOf child schema, a Java interface should be generated; the oneOf class should implement each of these child interfaces. But that's just one way to support oneOf. In fact, IMO, it should not be done that way because it won't work if two oneOf child schemas have the same property name but different type.

@jfeltesse-mdsol
Copy link
Contributor Author

I'm talking about the interface that is generated in the model folder as per #5120

Here for example ObjAOrObjB gets generated as

// imports and annotations here, trimmed for brevity

public interface ObjAOrObjB  {
    public String getRealtype();
}

@cnsphst
Copy link

cnsphst commented Apr 27, 2020

I'm facing the same issue with the typescript-fetch generator.
The *Api.ts file gets generated but the corresponding models are not. Resulting in an invalid import statement in the *Api.ts file.

import {
    object | Array<object>,
    object | Array<object>FromJSON,
    object | Array<object>ToJSON,
} from '../models';

@guaranadev
Copy link

I have the same problem

@jfeltesse-mdsol
Copy link
Contributor Author

Any takers? I wish I could raise my hand but my java is a bit too rusty to allow me to dive into the generator internals and provide a solid fix. cc @wing328

@sebastien-rosset
Copy link
Contributor

IMO the Java client library that has the most feature-rich support is jersey2, not "native". It has better support for oneOf/anyOf/allOf, it has better support for security schemes (including API keys, basic auth, OAuth2 and HTTP signature). I am using jersey2 and making enhancements for that library, not native.

@jfeltesse-mdsol
Copy link
Contributor Author

Thanks @sebastien-rosset. We went with native so as to reduce the number of dependencies. OkHttp switching to Kotlin finished convincing us.

@jfeltesse-mdsol
Copy link
Contributor Author

Any chance to get this looked at?

@sebastien-rosset
Copy link
Contributor

Any chance to get this looked at?

For our projects we decided to go through the jersey2 library path. Multiple PRs were raised and merged, not all of them related to oneOf, but you could take a look at some of them to see how the work was done for jersey2 and propose something similar for the native library.

#7115
#6956
#6954
#6933
#6925
#6784
#6606
#6632
#6633
#6634
#6637
#6636
#6646
#6647
#6695
#6551
#6518
#6508
#6463
#6476
#6495

@jfeltesse-mdsol
Copy link
Contributor Author

@sebastien-rosset is there any kind of specific configuration that needs to be passed? because if I use the jersey2 templates from latest master (98c606c) then the results are similar...

openapi_issue_5903.yaml

openapi: 3.0.3
info:
  title: oneOf test
  version: '1.0'
paths:
  /state:
    get:
      operationId: getState
      responses:
        '200':
          description: get states
          content:
            application/json:
              schema: # doesn't work
                oneOf:
                  - $ref: '#/components/schemas/ObjA'
                  - $ref: '#/components/schemas/ObjB'
                discriminator:
                  propertyName: realtype
  /state_ref:
    get:
      operationId: getStateRef
      responses:
        '200':
          description: 'get states using $ref'
          content:
            application/json:
              schema: # logically same as above but works
                $ref: '#/components/schemas/ObjAOrObjB'
components:
  schemas:
    ObjA:
      type: object
      required:
        - realtype
      properties:
        realtype:
          type: string
        message:
          type: string
    ObjB:
      type: object
      required:
        - realtype
      properties:
        realtype:
          type: string
        code:
          type: integer
    ObjAOrObjB:
      oneOf:
        - $ref: '#/components/schemas/ObjA'
        - $ref: '#/components/schemas/ObjB'
      discriminator:
        propertyName: realtype

command

openapi-generator generate -i openapi_issue_5903.yaml -g java --library jersey2 -o openapi_issue_5903

api/DefaultApi.java

// (...)

import org.openapitools.client.model.ObjAOrObjB;
import org.openapitools.client.model.OneOfObjAObjB;

// (...)

But there's no such OneOfObjAObjB model created...

ls models/

AbstractOpenApiSchema.java	ObjA.java			ObjAOrObjB.java			ObjB.java

Am I missing something?

@sebastien-rosset
Copy link
Contributor

sebastien-rosset commented Aug 19, 2020

@sebastien-rosset is there any kind of specific configuration that needs to be passed? because if I use the jersey2 templates from latest master (98c606c) then the results are similar...

openapi_issue_5903.yaml

openapi: 3.0.3
info:
  title: oneOf test
  version: '1.0'
paths:
  /state:
    get:
      operationId: getState
      responses:
        '200':
          description: get states
          content:
            application/json:
              schema: # doesn't work
                oneOf:
                  - $ref: '#/components/schemas/ObjA'
                  - $ref: '#/components/schemas/ObjB'
                discriminator:
                  propertyName: realtype
  /state_ref:
    get:
      operationId: getStateRef
      responses:
        '200':
          description: 'get states using $ref'
          content:
            application/json:
              schema: # logically same as above but works
                $ref: '#/components/schemas/ObjAOrObjB'
components:
  schemas:
    ObjA:
      type: object
      required:
        - realtype
      properties:
        realtype:
          type: string
        message:
          type: string
    ObjB:
      type: object
      required:
        - realtype
      properties:
        realtype:
          type: string
        code:
          type: integer
    ObjAOrObjB:
      oneOf:
        - $ref: '#/components/schemas/ObjA'
        - $ref: '#/components/schemas/ObjB'
      discriminator:
        propertyName: realtype

command

openapi-generator generate -i openapi_issue_5903.yaml -g java --library jersey2 -o openapi_issue_5903

api/DefaultApi.java

// (...)

import org.openapitools.client.model.ObjAOrObjB;
import org.openapitools.client.model.OneOfObjAObjB;

// (...)

But there's no such OneOfObjAObjB model created...

ls models/

AbstractOpenApiSchema.java	ObjA.java			ObjAOrObjB.java			ObjB.java

Am I missing something?

For OneOf, a Java class is generated for the schema that has a oneOf. That class inherits from AbstractOpenApiSchema which has a getActualInstance method. IMO this is much better than creating very long class names. Some OpenAPI docs have a lot of items under oneOf.

@jfeltesse-mdsol
Copy link
Contributor Author

That's the thing, it is not generated.
In my previous comment I've shown the generated code featured an import for some OneOfObjAObjB class but such a class is not generated.

From what I can tell, if oneOf is used outside of a named schema the generator will come up with a name for it and reference it in the code but "forget" to generate the class itself.

@jfeltesse-mdsol
Copy link
Contributor Author

@sebastien-rosset ⬆️ are we on the same page? can you confirm this limitation from the generator? it happens on ruby too, leading me to think it's a core issue, not a language/template specific one.

@jfeltesse-mdsol jfeltesse-mdsol changed the title [BUG][java] oneOf in responses: interface not generated. [BUG][java] oneOf in "anonymous" schemas: model not generated. Aug 31, 2020
@wing328
Copy link
Member

wing328 commented Aug 31, 2020

@jfeltesse-mdsol I think it's a limitation in the inline model/schema resolver when handling oneOf that's defined inline.

@jfeltesse-mdsol
Copy link
Contributor Author

@wing328 @spacether should this issue also be labelled "inline schema handling"?

@spacether spacether added the Inline Schema Handling Schema contains a complex schema in items/additionalProperties/allOf/oneOf/anyOf label Feb 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Client: Java Inline Schema Handling Schema contains a complex schema in items/additionalProperties/allOf/oneOf/anyOf Issue: Bug
Projects
None yet
Development

No branches or pull requests

6 participants