Skip to content

[BUG][Ruby] Using oneOf generated code has unexpected results #3630

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

Closed
5 of 6 tasks
ssteeg-mdsol opened this issue Aug 13, 2019 · 11 comments · Fixed by #5706
Closed
5 of 6 tasks

[BUG][Ruby] Using oneOf generated code has unexpected results #3630

ssteeg-mdsol opened this issue Aug 13, 2019 · 11 comments · Fixed by #5706

Comments

@ssteeg-mdsol
Copy link

ssteeg-mdsol commented Aug 13, 2019

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

When a oneOf is used, the generated model only includes one of the possible classes. Using the client, objects for all class types are forced into the same class, even if they're a different class.

In the below example, if the API returns a AuthenticationMonitor MonitorResult object, the generated client forces it into the SmokeTestMonitor class type. This is a problem as it's the incorrect class type and as a result the model attributes are all nil.

Current output for the AuthenticationMonitor MonitorResult object:
...
@items=[#<ClientExample::SmokeTestResult:0x00007fb45220eaa0>]
...
Expected output for the AuthenticationMonitor MonitorResult object:
...
@items=[#<ClientExample::AuthenticationMonitorItems:0x00007fb45220eaa0 @result=true>]
...
openapi-generator version

4.1.0, seems to occur in every 4.x version. Haven't checked previous versions.

OpenAPI declaration file content or url
OpenAPI spec

AuthenticationMonitor and SmokeTestMonitor are the same but the property name inside items is different

openapi: 3.0.2
info:
  title: Example
  version: v1
paths:
  /endpoint:
    get:
      summary: Get All Monitors
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                type: array
                items:
                  $ref: '#/components/schemas/Monitor'
components:
  schemas:
    Monitor:
      type: object
      properties:
        results:
          $ref: '#/components/schemas/MonitorResults'
    MonitorResults:
      oneOf:
        - $ref: '#/components/schemas/AuthenticationMonitor'
        - $ref: '#/components/schemas/SmokeTestMonitor'
    AuthenticationMonitor:
      type: object
      properties:
        items:
          type: array
          items:
            type: object
            properties:
              result:
                type: boolean
    SmokeTestMonitor:
      type: object
      properties:
        items:
          type: array
          description: Collection of smoke test result checks.
          items:
            type: object
            properties:
              passed:
                type: boolean
Command line used for generation

openapi-generator generate -i <OPENAPI_SPEC> -g ruby -o <TEMP_DIR> -c <CONFIG_PATH>

Steps to reproduce
  1. Run the command to generate the Ruby client
  2. Look at the
Related issues/PRs

Maybe #15 is related, but this issue is more specific to the Ruby client.

Another issue related to oneOf for the Ruby client is the generation of objects within a response schema. If you have the following OpenAPI spec, the class type gets generated as ExampleClient::OneOfsimpleObjectcomplexObject, and that class does not exist.

OpenAPI spec snippet Here is the spec:
paths:
  /endpoint:
    get:
      summary: Get All Monitors
      responses:
        '200':
          description: Successful operation.
          content:
            application/json:
              schema:
                oneOf:
                  - $ref: '#/components/schemas/simpleObject'
                  - $ref: '#/components/schemas/complexObject'

components:
  schemas:
    simpleObject:
      type: object
      properties:
        simple_property1:
          type: string
    complexObject:
      type: object
      properties:
        complex_property1:
          type: string
        complex_property2:
          type: string
Suggest a fix

In https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/ruby-client/partial_model_generic.mustache#L49, the generated client only includes one option Array<SmokeTestResult>. I'm not sure about a specific fix, but has there been any Ruby-specific development into this area?

@auto-labeler
Copy link

auto-labeler bot commented Aug 13, 2019

👍 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.

@autopp autopp self-assigned this Aug 15, 2019
@autopp
Copy link
Contributor

autopp commented Aug 18, 2019

@ssteeg-mdsol Thank you for the report.
Does the following response reproduce this problem?

[
  {
    "results": {
      // AuthenticationMonitor
      "items": [
        {
          "result": true
        }
      ]
    }
  },
  {
    "results": {
      // SmokeTestMonitor
      "items": [
        {
          "passed": true
        }
      ]
    }
  }
]

@autopp
Copy link
Contributor

autopp commented Aug 18, 2019

@ssteeg-mdsol

Expected output for the AuthenticationMonitor MonitorResult object:
...
@items=[#<ClientExample::SmokeTestResult:0x00007fb45220eaa0 @result=true>]
...

And isn't the expected output in your description here?

...
@items=[#<ClientExample::AuthenticationMonitorItems:0x00007fb45220eaa0 @result=true>]
...

@autopp
Copy link
Contributor

autopp commented Aug 19, 2019

This is not just a problem for Ruby, but for example Python.
A Python client generated with the same input contains the following code in openapi_client/models/monitor_results.py:

class MonitorResults(object):
    """NOTE: This class is auto generated by OpenAPI Generator.
    Ref: https://openapi-generator.tech

    Do not edit the class manually.
    """

    """
    Attributes:
      openapi_types (dict): The key is attribute name
                            and the value is attribute type.
      attribute_map (dict): The key is attribute name
                            and the value is json key in definition.
    """
    openapi_types = {
        'items': 'list[SmokeTestMonitorItems]' # <- missing AuthenticationMonitorItems
    }

This looks like a problem with the Ruby client.
If there is the same field in the schema selected by oneOf (or AnyOf), only one of them will be reflected. (In this case, it is items)

@ssteeg-mdsol
Copy link
Author

ssteeg-mdsol commented Aug 26, 2019

@autopp Sorry for the slow reply! My Gmail filters let me down 🙇 Thanks for taking a look at this issue!

You're right about the expected output, I will update the issue description.

I believe the response you provided should reproduce the problem but will double-check this afternoon.

@ssteeg-mdsol
Copy link
Author

Confirmed that the response you provided reproduces the issue.

A response of

[
  {
    "results": {
      // AuthenticationMonitor
      "items": [
        {
          "result": true
        }
      ]
    }
  },
  {
    "results": {
      // SmokeTestMonitor
      "items": [
        {
          "passed": true
        }
      ]
    }
  }

results in the following (from the ruby console):

items[0].results.items
# => [#< ClientExample::SmokeTestResult:0x00007ff1261bb380>]
items[0].results.items.first.passed
# => nil
items[0].results.items.first.result
Traceback (most recent call last):
        1: from (irb):11
NoMethodError (undefined method `result` for #< ClientExample::SmokeTestResult:0x00007ff1261bb380>)
Did you mean?  rescue

items[1].results.items
# => [#< ClientExample::SmokeTestResult:0x00007f98f4b191f0 @passed=true>]
items[1].results.items.first.passed
# => true

@ssteeg-mdsol
Copy link
Author

@autopp Do you have ideas about how or where the logic to handle oneOf could be implemented? Is there anything I can do to help fix the issues? Sorry for the vague questions, I'm not really sure where to start. 😕

@Frontrider
Copy link
Contributor

It's a general issue with the generator. Java has the same issue, no fix has been provided so far.

@autopp
Copy link
Contributor

autopp commented Oct 15, 2019

@ssteeg-mdsol Sorry for the late reply.
As @Frontrider comments, this seems to be a general generator problem.
Unfortunately I still haven't been sure where to start.

@Frontrider
Copy link
Contributor

my start was to look into the java generator code, and I found absolutely no support for it in the example generator. i think it is just unimplemented.

@jfeltesse-mdsol
Copy link
Contributor

I tried to take a stab at it in #5706

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

Successfully merging a pull request may close this issue.

4 participants