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.91k stars 6.58k forks source link

[BUG] 5.2 Typescript not generating inheritances for allOf #10125

Closed angelaki closed 1 year ago

angelaki commented 3 years ago

When using the latest version (5.2) with this command:

docker run -v "[output]":/out openapitools/openapi-generator-cli generate -i http://host.docker.internal:64483/swagger/v1/swagger.json -g typescript-angular -o ./out

...
  "IObject": {
    "type": "object",
    "allOf": [
      {
        "$ref": "#/components/schemas/IBase1"
      },
      {
        "$ref": "#/components/schemas/IBase2"
      }
    ],
    "properties": {
      "x": {
        "type": "string",
        "nullable": true
      }
    },
    "additionalProperties": false
  },
  "IBase1": {
    "type": "object",
    "properties": {
      "a": {
        "type": "string",
        "nullable": true
      }
    },
    "additionalProperties": false
  },
  "IBase2": {
    "type": "object",
    "properties": {
      "b": {
        "type": "string",
        "nullable": true
      }
    },
    "additionalProperties": false
  }
...

IObject gets the related interfaces linked, but does not extend them:

import { IBase1 } from './iBase1';
import { IBase2 } from './iBase2';

export interface IObject { 
    x?: string | null;
    a?: string | null;
    b?: string | null;
}

but that's the result I'd love to see:

import { IBase1 } from './iBase1';
import { IBase2 } from './iBase2';

export interface IObject extends IBase1, IBase2 { 
    x?: string | null;
}

Please don't tell me this isn't possible! It took me already a hell of work to make my own Swashbuckle fork understand Interface inheritances.

angelaki commented 3 years ago

After some testing I found an even worse issue:

...
  "IObject": {
    "type": "object",
    "allOf": [
      {
        "$ref": "#/components/schemas/IBase1"
      },
      {
        "$ref": "#/components/schemas/IBase2"
      }
    ],
    "properties": {
      "x": {
        "type": "string",
        "nullable": true
      }
    },
    "additionalProperties": false
  },
  "IBase1": {
    "type": "object",
    "allOf": [
      {
        "$ref": "#/components/schemas/IHiddenBase"
      }
    ],
    "properties": {
      "a": {
        "type": "string",
        "nullable": true
      }
    },
    "additionalProperties": false
  },
  "IBase2": {
    "type": "object",
    "properties": {
      "b": {
        "type": "string",
        "nullable": true
      }
    },
    "additionalProperties": false
  },
  "IHiddenBase": {
    "type": "object",
    "properties": {
      "hid": {
        "type": "string",
        "nullable": true
      }
    },
    "additionalProperties": false
  },
 ...

Leads to

import { IBase1 } from './iBase1';
import { IBase2 } from './iBase2';

export interface IObject extends IBase1 { 
    b?: string;
    x?: string;
}

Only Base1 is imported, but interfaces can extend multiple interfaces. Why are they missing?

angelaki commented 3 years ago

For easier traceability I've got the full Json:

{
  "openapi": "3.0.1",
  "info": {
    "title": "Swagger API",
    "version": "v1"
  },
  "paths": {
    "/Authentication/Logout": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "operationId": "Logout",
        "responses": {
          "200": {
            "description": "Success"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "IObject": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "readOnly": true
          }
        },
        "additionalProperties": false
      },
      "IHiddenBase": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/IObject"
          }
        ],
        "properties": {
          "hid": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "IBase1": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/IHiddenBase"
          }
        ],
        "properties": {
          "a": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "IBase2": {
        "type": "object",
        "properties": {
          "b": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      },
      "IObjectX": {
        "type": "object",
        "allOf": [
          {
            "$ref": "#/components/schemas/IBase1"
          },
          {
            "$ref": "#/components/schemas/IBase2"
          }
        ],
        "properties": {
          "x": {
            "type": "string",
            "nullable": true
          }
        },
        "additionalProperties": false
      }
    }
  }
}
angelaki commented 3 years ago

Sorry for bumping, but this issue seams quite obvious for me - and probably fast fixed for one knowing the code. Isn't there an easy solution for this?

b-gyula commented 3 years ago

This is by intention : inheritance without use of 'discriminator.propertyName' has been deprecated in the 5.x release. The last working verion: 4.3.0. It seems to be a specification (mis)interpretation

angelaki commented 3 years ago

But we are just talking about models here! If they are used with a discriminator behind the curtain is a different thing! I just want my models to be generated clean & corrent!

No hope for it to come back? Is there anything I can do about it?

Tynamix commented 2 years ago

The specification tells nothing about discriminator is mandatory! https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/

Quote: OpenAPI lets you combine and extend model definitions using the allOf keyword. allOf takes an array of object definitions that are used for independent validation but together compose a single object. Still, it does not imply a hierarchy between the models. For that purpose, you should include the discriminator.

So, when hierarchy is not important or the allof array does only have one element no discriminator is needed.

For me this is a bug!

wing328 commented 1 year ago

please use openapi-normalizer with the rule REF_AS_PARENT_IN_ALLOF to meet your requirement.