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.69k stars 6.55k forks source link

[BUG][typescript] `oneOf` + `allOf` breaks serialization #19868

Open simon-abbott opened 1 week ago

simon-abbott commented 1 week ago

Bug Report Checklist

Description

When using an API that uses a oneOf union containing several allOf elements, trying to send/receive data will fail with the error typeMap[type].getAttributeTypeMap is not a function. The underlying reason is that no discriminator value or mapping is generated for this value.

Prior to #19494 this worked fine, but as of 7.9.0 trying to run this schema will crash.

openapi-generator version

7.9.0, this is a regression.

OpenAPI declaration file content or url
openapi: 3.0.1
info:
  title: fruity
  version: 0.0.1
paths:
  /:
    get:
      responses:
        "200":
          description: get a fruit
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Fruit"
components:
  schemas:
    Apple:
      title: Apple
      type: object
      properties:
        variety:
          type: string
          description: The type of apple
      required:
        - variety
    Banana:
      title: Banana
      type: object
      properties:
        ripeness:
          type: number
          description: How ripe the banana is from 0 to 1
    Fruit:
      title: Fruit
      oneOf:
        - type: object
          allOf:
            - type: object
              properties:
                type:
                  type: string
                  enum:
                    - apple
            - $ref: '#/components/schemas/Apple'
          required:
            - type
        - type: object
          allOf:
            - type: object
              properties:
                type:
                  type: string
                  enum:
                    - banana
            - $ref: '#/components/schemas/Banana'
          required:
            - type
Generation Details

Generated with v7.9.0.

Steps to reproduce

Generate the above yaml using the typescript generator, then try to use the API. It will fail with typeMap[type].getAttributeTypeMap is not a function

Related issues/PRs

Not as far as I could tell.

Suggest a fix

I don't have an exact fix in mind since I don't fully understand how this mapping logic is supposed to work.

ksvirkou-hubspot commented 5 days ago

This issue occurs because the schema described doesn't include a discriminator. Without a discriminator, it's challenging to find out which object the API returns. If you add discriminator, it will work correctly Example mapping with oneOf objects:

discriminator:
  propertyName: type
  mapping:
    apple: '#/components/schemas/FruitOneOf'
    banana: '#/components/schemas/FruitOneOf1'

Example mapping with Apple and Banana:

discriminator:
  propertyName: type
  mapping:
    apple: '#/components/schemas/Apple'
    banana: '#/components/schemas/Banana'

if type property contains name of classes "Apple" and "Banana" it will work without mapping Example of response:

{"type":"Apple","variety":"good apples"}

Swagger file:

discriminator:
  propertyName: type
ksvirkou-hubspot commented 5 days ago
Example of full Swagger file ```yaml openapi: 3.0.1 info: title: fruity version: 0.0.1 paths: /: get: responses: "200": description: get a fruit content: application/json: schema: $ref: "#/components/schemas/Fruit" components: schemas: Apple: title: Apple type: object properties: variety: type: string description: The type of apple required: - variety Banana: title: Banana type: object properties: ripeness: type: number description: How ripe the banana is from 0 to 1 Fruit: title: Fruit oneOf: - type: object allOf: - type: object properties: type: type: string enum: - apple - $ref: '#/components/schemas/Apple' required: - type - type: object allOf: - type: object properties: type: type: string enum: - banana - $ref: '#/components/schemas/Banana' required: - type discriminator: propertyName: type mapping: apple: '#/components/schemas/FruitOneOf' banana: '#/components/schemas/FruitOneOf1' ```
simon-abbott commented 4 days ago

Yeah, I know that's a workaround, but unfortunately I am dealing with a swagger file in the wild that doesn't specify one. I could patch it but I'd rather not maintain a patch against a complicated and changing 3rd party API if I don't have to. Additionally the OpenAPI v3 spec explicitly allows for discriminator-less schemas (note the use of MAY vs MUST):

In OAS 3.0, a response payload MAY be described to be exactly one of any number of types:

MyResponseType:
  oneOf:
  - $ref: '#/components/schemas/Cat'
  - $ref: '#/components/schemas/Dog'
  - $ref: '#/components/schemas/Lizard'

which means the payload MUST, by validation, match exactly one of the schemas described by Cat, Dog, or Lizard. In this case, a discriminator MAY act as a "hint" to shortcut validation and selection of the matching schema which may be a costly operation, depending on the complexity of the schema.

As seen here, if the discriminator is provided, it can (and probably should) be used to shortcut validation. But if it is missing, like in the example shown above, it should still run validation against the provided options.