Basic inheritance, as described in the OpenAPI spec does not work.
Instead of creating a class / interface hierarchy using inheritance to represent the composition, all properties are flattened into the subclass.
4.0.0-20190508.072036-627 or master
schema.yaml:
openapi: "3.0.1"
info:
version: 1.0.0
title: Inheritance test
paths:
/order:
get:
summary: Product order details
operationId: getOrder
responses:
'500':
description: Successful load
content:
application/json:
schema:
$ref: '#/components/schemas/ExtendedErrorModel'
components:
schemas:
BasicErrorModel:
type: object
required:
- message
- code
properties:
message:
type: string
code:
type: integer
minimum: 100
maximum: 600
ExtendedErrorModel:
allOf:
- $ref: '#/components/schemas/BasicErrorModel'
- type: object
required:
- rootCause
properties:
rootCause:
type: string
The following class/interface hierarchy: to be generated:
BasicErrorModel <- ExtendedErrorModelThree unrelated classes/interfaces:
BasicErrorModelExtendedErrorModelExtendedErrorModelAllOfjava -jar openapi-generator-cli.jar generate -i ./schema.yaml -g typescript-angular -o src
I have also tried with the Java generator, and it creates the same hierarchy, so it is not just a Typescript problem.
schema.yaml locally.I'm not familiar with the codebase, but it appears that getParentName in ModelUtils.java only considers a class as a viable parent for inheritance if it has a discriminator, but the above inheritance pattern does not use discriminators.
馃憤 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.
As mentioned, this is not just a TypeScript issue, as I can also reproduce it with the Java generator, so the above label is wrong.
See also this inheritance example, again taken directly from the OpenAPI docs:
openapi: "3.0.1"
info:
version: 1.0.0
title: Inheritance test
paths:
/pets:
patch:
requestBody:
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/Cat'
- $ref: '#/components/schemas/Dog'
discriminator:
propertyName: pet_type
responses:
'200':
description: Updated
components:
schemas:
Pet:
type: object
required:
- pet_type
properties:
pet_type:
type: string
discriminator:
propertyName: pet_type
Dog: # "Dog" is a value for the pet_type property (the discriminator value)
allOf: # Combines the main `Pet` schema with `Dog`-specific properties
- $ref: '#/components/schemas/Pet'
- type: object
# all other properties specific to a `Dog`
properties:
bark:
type: boolean
breed:
type: string
enum: [Dingo, Husky, Retriever, Shepherd]
Cat: # "Cat" is a value for the pet_type property (the discriminator value)
allOf: # Combines the main `Pet` schema with `Cat`-specific properties
- $ref: '#/components/schemas/Pet'
- type: object
# all other properties specific to a `Cat`
properties:
hunts:
type: boolean
age:
type: integer
The code generated for this spec is broken and unusable.
I am seeing the same issues for the test files in the project. These generate broken code:
Etc.
How on Earth is the project passing all its tests when the output is this state?
EDIT: I see now that the petstore.yaml that is being used to regression test every generator doesn't contain _any_ use of anyOf, allOf, oneOf, discriminators, etc. There's no way you can catch issues with these features, even when they're massively broken, like now.
@jonrimmer you are right, there should be some more tests. would you like to give a hand?
cc @wing328
you could e.g. add an integration-test similar to https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/typescript-angular-v6-provided-in-root/pom.xml with a separate yaml-file.
Could this be a related issue? https://github.com/OpenAPITools/openapi-generator/issues/2580
We just started adding better support for allOf, anyOf, oneOf to different generators and I foresee it will take a while for all generators to support these new features in OAS v3.
If you or anyone wants to help with the implementation of these new features in any generators, let me know and I'll show you some good starting points.
There's already issues referencing multiple path parameters as $ref at the same time to have them merged. allOf, anyOf and oneOf would be nice to have, but the generators get confused with way less complex input already.
Personally, I'm simply looking for the functionality that complies with the OAS spec and outputs the generated code. I'm patient, and in the meantime I will simply try to discard/replace parts of my yaml spec that are not supported or broken by openapi-generator.
Thank you for making this public library and tooling resource and I'm looking forward to the first upcoming 4.0 point release.
@wing328 This is not about enhancing generators, it's an issue of fixing DefaultCodegen.java to create the models correctly in the first place. The changes made to support multiple inheritance and OA3 appear to have made the situation worse, not better. For example, basic Swagger 2.0 allOf aggregation works in the current release version, but is broken in the snapshot, because the code for resolving parents now only understands discriminator-based parent relationships. If 4.0 is released as-is, it is going to break with a lot of specs that previously worked.
There should have been a comprehensive regression test for what aggregation was supported before the attempts to implement OA3 began, to ensure those changes didn't break anything. As things stand, the codebase is already broken WRT inheritance, so the task now is to both implement a comprehensive regression test while simultaneously fixing the default codegen. I'm happy to try to help with this, but it's going to take me a while to understand everything that's happening in DefaultCodegen.java.
I'll retest tomorrow and next week to repeat the problem you reported about allOf
Expected output
The following class/interface hierarchy: to be generated:BasicErrorModel <- ExtendedErrorModel
@jonrimmer I read your issue report again and notice there may be an incorrect interpretation of allOf (composition vs inheritance).
In the example you provided, BasicErrorModel does not contain discriminator (example) so model composition is used instead
Ref:
@wing328 The discriminator is intended to tell API consumers how they can polymorphically distinguish the type of an incoming object, it doesn't imply that a code generator must flatten any relationship without a discriminator in order to represent the composition.
At the very least, this decision should be left up to the language-specific generator rather than being made prematurely in the default codegen. TypeScript for example has absolutely no problem representing this kind of discriminator-less composition via interfaces, which do not imply any kind of runtime inheritance hierarchy, but allow for types to represent an extension/composition of one or more parent types.
And, indeed, this is exactly how the current release of openapi-generator works!
Run the example I provided through the release available in homebrew, and you will get an interface ExtendedErrorModel that inherits from an interface BasicErrorModel, exactly as you would expect. It is only with the current snapshot that you get these three, unrelated interfaces:
BasicErrorModel
ExtendedErrorModel
ExtendedErrorModelAllOf
This is just not right, and is happening because DefaultCodegen is prematurely deciding to throw away the child-parent relationship expressed by the allOf, perhaps based on the incorrect assumption that Java-style class hierarchies are the only type of extension mechanism in existence?
For the impatient:
To fix my problem, I've used https://github.com/APIDevTools/json-schema-ref-parser (after turning my main yaml spec into json) and de-referenced all the $ref's automatically with the aforementioned tool.
Then, I was able to generate the client code :)
PSA: This is a hotfix and any code generated should be well tested.
allOf takes an array of object definitions that are validated independently but together compose a single object.
While composition offers model extensibility, it does not imply a hierarchy between the models. To support polymorphism, the OpenAPI Specification adds the discriminator field
Ref: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#composition-and-inheritance-polymorphism
I'm afraid we've to stick with the definitions defined in the specification.
In your case, why not update the OpenAPI documents to use the discriminator field?
@wing328 You don't appear to be even trying to read or understand what I'm writing, so I don't see much point in continuing this.
Hi,
I am using OpenAPI generator v3.3.4 to generate a server in Java, Java clients, a python client, and an angular client. With v3.3.4, I have issues with the python client that does not handle correctly polymorphism. I have just tried to migrate to v4.0.0 to fix this python client issue. Migration does fix the issue with the python client, but breaks inheritance in the typescript/angular client.
Here is the relevant part of my OpenAPI spec yaml file:
JobEvent:
type: object
description: A job event.
properties:
date:
type: integer
format: int64
description: date of this event
jobId:
type: string
description: identifier of the job this event is refering to
objectType:
type: string
required:
- date
- objectType
discriminator:
propertyName: objectType
LogEvent:
allOf:
- $ref: '#/components/schemas/JobEvent'
- type: object
properties:
message:
type: string
level:
type: string
required:
- message
- level
With generator v3.3.4, I get the following typescript classes generated:
import { JobStatusEvent } from './jobStatusEvent';
import { KpiEvent } from './kpiEvent';
import { LogEvent } from './logEvent';
import { MessageEvent } from './messageEvent';
import { ProgressEvent } from './progressEvent';
/**
* A job event.
*/
export type JobEvent = MessageEvent | ProgressEvent | KpiEvent | JobStatusEvent | LogEvent;
export interface LogEvent {
/**
* date of this event
*/
date: number;
/**
* identifier of the job this event is refering to
*/
jobId?: string;
objectType: 'LogEvent';
message: string;
level: string;
}
md5-d474f61f64b58a990f53c83427fa31e7
import { JobStatusEvent } from './jobStatusEvent';
import { KpiEvent } from './kpiEvent';
import { LogEvent } from './logEvent';
import { MessageEvent } from './messageEvent';
import { ProgressEvent } from './progressEvent';
/**
* A job event.
*/
export type JobEvent = MessageEvent | ProgressEvent | KpiEvent | JobStatusEvent | LogEvent;
md5-251852979f505d2c9236531501f8ee54
import { LogEventAllOf } from './logEventAllOf';
export interface LogEvent {
/**
* date of this event
*/
date: number;
/**
* identifier of the job this event is refering to
*/
jobId?: string;
objectType: 'LogEvent';
}
md5-251852979f505d2c9236531501f8ee54
export interface LogEventAllOf {
message: string;
level: string;
}
These new classes do not allow to call logEvent.message for example, because a 'LogEvent' does not have anything to do with a 'LogEventAllOf '.
Do you plan to fix this issue in v4.0.1, which is supposed to be released at the end of the month, if I understood well?
Thanks for these great improvements of inheritance support for generated python clients!
Regards
Inheritance using discriminator is working with 4.0.0-beta3. Releases after that are broken. Is this recognized as a bug or has the yaml to be adapted to work again?
Inheritance is broken for Java as well. And it worked in 3.3.4. Currently both extra class "...AllOf" is generated and inheritance is missing. I tried all of the versions starting from 4.0.0-beta1 up to 4.0.2, it is broken everywhere.
@wing328 could you have a look at this one again? I think this is a regression since it used to work correctly
@macjohnny thanks for bringing it to my attention. I definitely want to take another look at the issue as I thought discriminator should setup the model inheritance properly but @Patre suggested otherwise in https://github.com/OpenAPITools/openapi-generator/issues/2845#issuecomment-493863254.
However I'm very busy as usual and I'll try to look into it again after the Open Summit Japan 2019 next week.
If anyone wants to help on the issue, please feel free to submit a PR to start with.
Is there a previous version I can rollback to so I can get extends back?
In our project we had to temporarily switch to another Gradle plugin 'org.hidetake.swagger.generator' which works well with Gradle 5 and allows downgrading open-api generator to 3.3.4(org.openapitools:openapi-generator-cli:3.3.4). And 3.3.4 properly handles inheritance, so if you need it(inheritance i mean), then you definitely have to somehow use this particular version
This is blocking us from upgrading from 3.3.4 which is unfortunate as the desire to upgrade was prompted by an unrelated bug that is present in 3.3.4 but fixed in the latest release.
Our use case is we have a common model definition:
Page:
properties:
page:
type: integer
pageSize:
type: integer
totalPages:
type: integer
_links:
$ref: '#/components/schemas/PageLinks'
required:
- page
- pageSize
- totalPages
- _links
And then many models that extend from this, adding an items property. Eg:
PagedAnEntity:
allOf:
- $ref: '#/components/schemas/Page'
- properties:
items:
type: array
items:
$ref: '#/components/schemas/AnEntity'
required:
- items
None of the operations have a polymorphic return type, they will all return a particular PagedX model. However, we do have some generic code that we use to help construct the different paged entities, which relies on the inheritance of PagedX from Page
Eg:
doSomethingWithPage<T extends Page>(value: T)
Reading earlier in this thread it seems that the recommendation is to add a discriminator property, defined on the supertype, in this case Page. This doesn't seem like a great solution in this case, as suddenly our shared Page definition has to reference every page-able model in our system.
This would be a circular dependency between our separated definition files (or require putting every model into a single definition file) and feels wrong to me.
Is there going to be a restoration of the pre-4.0 functionality where allOf relationships of length 2 create an inheritance structure in the java generator?
Is there some other way I can express this relationship to achieve the desired effect?
@mnahkies it would be great if you could help us fix this in the generator, would you like to give it a try?
What @mnahkies mentioned is exactly the situation I am running into and can't upgrade due to it. I really don't understand the changes done in version 4.0 and above which broke the inheritance for allof. Workaround to add discriminator property in the super type isn't completely acceptable as it still doesn't avoid creation of PagedAnEntityAllOf.java
@macjohnny @abhijitvk I've opened https://github.com/OpenAPITools/openapi-generator/pull/3771 which I believe addresses this issue.
I've given it a quick test run over my real api definitions, and it appears to have had the desired effect. Will test the change more tomorrow.
See also this inheritance example, again taken directly from the OpenAPI docs:
openapi: "3.0.1" info: version: 1.0.0 title: Inheritance test paths: /pets: patch: requestBody: content: application/json: schema: oneOf: - $ref: '#/components/schemas/Cat' - $ref: '#/components/schemas/Dog' discriminator: propertyName: pet_type responses: '200': description: Updated components: schemas: Pet: type: object required: - pet_type properties: pet_type: type: string discriminator: propertyName: pet_type Dog: # "Dog" is a value for the pet_type property (the discriminator value) allOf: # Combines the main `Pet` schema with `Dog`-specific properties - $ref: '#/components/schemas/Pet' - type: object # all other properties specific to a `Dog` properties: bark: type: boolean breed: type: string enum: [Dingo, Husky, Retriever, Shepherd] Cat: # "Cat" is a value for the pet_type property (the discriminator value) allOf: # Combines the main `Pet` schema with `Cat`-specific properties - $ref: '#/components/schemas/Pet' - type: object # all other properties specific to a `Cat` properties: hunts: type: boolean age: type: integerThe code generated for this spec is broken and unusable.
Result with #3771:
export interface Pet {
pet_type: string;
}
import { DogAllOf } from './dog-all-of';
import { Pet } from './pet';
export interface Dog {
bark?: boolean;
breed?: Dog.BreedEnum;
}
export namespace Dog {
export type BreedEnum = 'Dingo' | 'Husky' | 'Retriever' | 'Shepherd';
export const BreedEnum = {
Dingo: 'Dingo' as BreedEnum,
Husky: 'Husky' as BreedEnum,
Retriever: 'Retriever' as BreedEnum,
Shepherd: 'Shepherd' as BreedEnum
};
}
@wing328 inheritance with discriminator seems broken, too
I've looked into this a little more (I happen to also use the typescript-angular generator) and it appears that the template was re-written to expect the supportsMultipleInheritance flag to be enabled in 8c599ebf12483495f889cfc07ba6988d68b5e3ca but that the flag wasn't actually enabled.
By enabling the flag I get the correct parent in @dbusacca's example.
Opened https://github.com/OpenAPITools/openapi-generator/pull/3812
What's the difference between supportsMultipleInheritance and supportsInheritance? Do all broken languages need to enable the flag or is something wrong with the default?
@wing328 do you know an answer?
supportsMultipleInheritance is something I added in the 4.x release if I remember correctly. Currently, we've it enabled in the Perl generator that I tested supportsMultipleInheritance with
As you saw in the code, I left a comment about enabling it for TypeScript generators and filed https://github.com/OpenAPITools/openapi-generator/pull/1684/files later but no one from the TS community has time to help move it forward.
Do all broken languages need to enable the flag or is something wrong with the default?
From what I know, not all languages support multiple inheritances.
@wing328 so as far as I can tell from the diff, you updated the typescript template to support the new mechanism you developed without enabling the flag
https://github.com/OpenAPITools/openapi-generator/commit/8c599ebf12483495f889cfc07ba6988d68b5e3ca#diff-8c3f9603ed75fb2ae70a40c24cd00539
This had the net effect of breaking typescript inheritance support, I didn't review your template change in detail but from my testing it appears to behave correctly onc modules/openapi-generator/src/main/resources/typescript-angular/modelGeneric.mustachee the generator was configured to enable it.
I don't see any other templates modified in that commit, but haven't checked for subsequent modifications that would require enabling the flag on other templates.
I've looked into this a little more (I happen to also use the
typescript-angulargenerator) and it appears that the template was re-written to expect thesupportsMultipleInheritanceflag to be enabled in 8c599eb but that the flag wasn't actually enabled.By enabling the flag I get the correct parent in @dbusacca's example.
I've tried the flag with an our real-life API and it seems to generate the inheritance correctly.
Since the changes were made to TypeScript - so now Java and Elm need to set supportsMultipleInheritance, too?
It depends on whether their templates have been updated to use the new properties that are populated when supports multiple inheritance is enabled.
I haven't checked this myself, but inheritance is still working fine for me with the java client generator as well as the spring boot server stubs generator.
Could you name the exact generators you believe need updating and I can take a look at their templates to see if they use the new properties?
I retested with the current version and the generated files are working fine with discriminator inheritance (elm and java/springboot).
For both languages there are additional files generated with an AllOf suffix that weren't before.
(MessageAllOf.elm / MessageAllOf.java). Not an issue, but I noticed it as a difference.
Thanks to everybody who took a look at the issue :)
For both languages there are additional files generated with an
AllOfsuffix that weren't before.
Thanks for fixing this! But what is the purpose for the suffixed classes? All fields are there and these suffixed classes are not even used.
Is there any way to disable the generation of these files? (im on JAVA spring)
I don't know the reason why they're existing. I "disabled" them with an .openapi-generator-ignore file.
Thank you for the quick reply @andys8 and excellent tip!
I managed to avoid generation of that file,
but it still added the non-existing class/file as an import so it wont compile :(
How did you avoid that?
I noticed the discussion about the *AllOf suffix here: https://github.com/OpenAPITools/openapi-generator/issues/3100
I'm trying to use this with the 4.2.2 version and can't compile because of the missing AnyOf references. This is failing for me with both c# and java. Is it fixed in that version?
This is still broken in 4.2.2 using 'spring' generator...when will this be fixed? The generated code does not contain the inheritance defined in the openapi schema. However I found that if I add a discriminator to the base class then things do work as expected. Note this is a regression as that was not needed in 3.3.4 and should not be needed as the schema already defines the inheritance structure.
@bricox I think we've only added better anyOf support (beta) to the Ruby client generator. We welcome contributions to make similar enhancements to other generators.
Note this is a regression as that was not needed in 3.3.4 and should not be needed as the schema already defines the inheritance structure.
That's something we fixed in 4.x (which means the previous implementation was incorrect with reference to the OAS v3 spec). Please use discriminator moving forward for inheritance.
Hod do you guys set supportsMultipleInheritance/supportsInheritance for typescript-angular generator?
I did try --additional-properties supportsMultipleInheritance/supportsInheritance=true with no success. There is also no mention about such a parametres in here: https://github.com/OpenAPITools/openapi-generator/blob/master/docs/generators/typescript-angular.md
Thanks.
Note this is a regression as that was not needed in 3.3.4 and should not be needed as the schema already defines the inheritance structure.
That's something we fixed in 4.x (which means the previous implementation was incorrect with reference to the OAS v3 spec). Please use discriminator moving forward for inheritance.
It turns out that using the discriminator in the base class did fix the code generation but then at runtime I get errors as it's apparently not valid to have a discriminator in only the base class, at runtime it complains about not being able to find an id or something in the derived class. I have not used discriminators before. As I read the online docs they don't seem to be relevant for allOf simple/regular inheritance. So if this is required now how to use it for the simple case of Ext1 & Ext2 both inheriting from Base. Where extend allOf Base?
Here is the runtime error "com.fasterxml.jackson.databind.exc.InvalidTypeIdException (Missing type id when trying to resolve subtype of [simple type, class com.polarisalpha.odin.services.colreq.model.CollectionRequestTaskingImmediateTCModel]: missing type id property 'CollectionRequestTaskingModel_type'n at..."
Couple more thoughts: The docs here https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/ seem quite clear that discriminator is not to be used for the simple allOf inheritance use case.
However I don't yet know what is causing the runtime error above, the CollectionRequestTaskingModel_type that is is complaining about is my new discriminator so its related but maybe this is because Jackson is not properly handing the new discriminator annotations I see in the generated code.
Or perhaps I need to do more when adding the discriminator ? I don't know how to be sure I have this correct for the allOf use case if its possible to do so.
So I'm believing this is a regression and it should not be required in this case. Please advise if there is a workaround else don't see any option for me but to revert back to 3.x plugin version.
Hod do you guys set supportsMultipleInheritance/supportsInheritance for typescript-angular generator?
There should be no need to set supportsMultipleInheritance, which is set to true for languages that support multiple inheritances (e.g. C++)
Couple more thoughts: The docs here https://swagger.io/docs/specification/data-models/inheritance-and-polymorphism/ seem quite clear that discriminator is not to be used for the simple allOf inheritance use case.
We do not maintain the doc under "swagger.io"
For the official definition of discriminator, please refer to https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#composition-and-inheritance-polymorphism
@wing328 Alright, but how to set supportsInheritance to true for typescript-angular ?
@wing328 According to the link you have provided for discriminator object, it's not required for simple inheritance. With latest version 1.0.8-4.2.2 the inheritance for typescript-angular is not generated at all. With 0.0.7-3.3.4 it works as expected. I'm a bit confused with the communication in this thread so could you please gimme a hint if I should stick with 3.3.4 or the inheritance is fixed in 4.2.2, but it needs to be somehow enabled in additional-properties? Thank you.
@skorunka you don't need to (or actually are able to) set supportsInheritance. It's defined inside the java classes responsible for supporting particular languages. The typescript-specific class already has it enabled.
However, note that you have to use discriminator in order for the emitted code to include interface inheritance, as per @wing328 's links above. Without a discriminator, allOf will emit a combined interface that includes all properties of referenced interfaces - without any inheritance / extending.
This getting UNKNOWN_BASE_TYPE UNKNOWN_BASE_TYPE with spring-boot generator for the Cat Dog example above
I think I have the same error that allOf is not supprted correctly. I use a derived type as request body. I麓m using maven generator "java" with different libraries - native, retrofit , ... 4.3.0-SNAPSHOT
/api/system/users:
post:
tags:
- users
description: CreateUser https://udemo.tixxt.com/docs/api/system/#api-Users-CreateUser
operationId: CreateUser
requestBody:
required: true
content:
application/x-www-form-urlencoded:
schema:
$ref: '#/components/schemas/UserCreate'
responses:
default:
description: default response
content:
application/json:
schema:
$ref: '#/components/schemas/User'
[...]
UserUpdate:
type: object
properties:
external_id:
type: string
[...]
UserCreate:
allOf:
- $ref: '#/components/schemas/UserUpdate'
- type: object
properties:
send_welcome_mail:
type: boolean
The class UserCreate is created with all fields from UserUpdate and the send_welcome_email property - but the method has no argument.
public User createUser() throws ApiException {
So why is this issue marked as closed?
Please reopen...
So, is there a workaround for this?
So, is there a workaround for this?
I personally don't think so. At least not for openapi -> java model
So why is this closed?
Enough people have commented on this. The old behavior is what we expect. Not the fixed one.
Inheritance via allOf (what we want) isn't the same as polymorphic serialization (what discriminator does). Could we at least have an option to restore the previous behavior that worked for a lot of people?
@lacksfish @jonrimmer Following the advice to leverage the incredible https://github.com/APIDevTools/json-schema-ref-parser tool, I threw together a little script that does a _hybrid_ de-reference of the openapi schemas (models). This way, you can still keep the non-inheritance/polymorphism-related references (for example, properties of an object that just reference another defined object), but still collapse any allOf references in a model.
I hope it helps you guys.
'use strict';
const fs = require('fs');
const $RefParser = require('json-schema-ref-parser');
const input = 'openapi.json';
const output = 'openapi--dereferenced.json';
(async () => {
try {
// parse the full spec file
let openapi = await $RefParser.parse(input);
// resolve the schema to get the complete set of references
let $refs = await $RefParser.resolve(openapi);
// loop over each schema (model) in the schema
for (const schema in openapi.components.schemas) {
// resolve the individual openapi schema object
let $schema = await $RefParser.resolve(openapi.components.schemas[schema]);
// loop over the references in the openapi schema object
for (const path in $schema.values()) {
// we need to manually 'de-reference' any composed models
if (!$schema.get(path).allOf) {
continue
}
// for each reference in the 'allOf' statement, make sure we actually have
// a reference (as opposed to just an inline model)
$schema.get(path).allOf.forEach(element => {
if (element.$ref) {
// if the reference element matches the reference in the schema,
// replace the reference object with the actual object, thereby
// 'de-referencing' the composed model
const predicate = i => i.$ref === element.$ref;
let index = 0
while (index <= openapi.components.schemas[schema].allOf.length - 1) {
if (predicate(openapi.components.schemas[schema].allOf[index])) {
// get the inherited model from the master collection of references
openapi.components.schemas[schema].allOf.splice(index, 1, $refs.get(path + element.$ref))
}
index++;
}
}
});
}
}
let bundle = await $RefParser.bundle(openapi);
fs.writeFileSync(output, JSON.stringify(bundle, null, 2));
}
catch (err) {
console.error(err);
}
})();
Still got this problem on 4.2.2.
@nexen505 could you please clarify which generator do you have the problem with, the input spec, the excepted and actual generated code. And it will probably help opening a new issue with those details.
This thread has become long, and it's not always clear what exactly people complain about.
I'm pretty sure there are valid reasons to complain - however, I'm not sure every commenter above faces the same issue.
Same here, just upgraded to the latest version 4, and interfaces generated with typescript-angular no longer extend other interfaces when using allOf without discriminator. I understand that this seems to be correct according to the spec:
Still, it does not imply a hierarchy between the models. For that purpose, you should include the discriminator.
But: This is a breaking change. IMHO, at the very least it should be pointed out / documented, at best we'd have a fallback option as @lemoinem suggested.
still broken with 4.3.0 for kotlin-spring generator, but somehow worked with 4.0.3, which doesn't support gradle 6.
wasted 2 days trying to figure it out already ...
@vgunda which version of openApi and gradle did it work for you ? I'm having the same problem with java and spring
@erohana gradle 5.5 and openapi 4.0.3 worked.
Gradle 6 needed openapi >4.2.1 afaik and with all those inheritance didn't work for me
This works perfectly in 4.3.0, but is broken again in 4.3.1
In 4.3.0 inheritance works as expected. In 4.3.1 the subclass does not inherit the superclass, but instead gets all properties from the superclass.
Any update on this?
I cannot manage to get inheritance when generating client for dart
typescript-fetch generator is also broken in 4.3.1
Inheritance without a discriminator works perfectly fine in 4.3.0.
In 4.3.1 a subclass gets all the properties of a superclass instead of inheritance and an extra class named SubclassAllOf is generated.
@wing328 Please reopen this
@InvictusMB according to the spec, discriminator should be used to denote inheritance relationship between models.
Without a discriminator, models are not extended - hence properties are copied.
Extra AllOf class is a known issue and is tracked separately here (at least, for typescript generators)
@amakhrov
The part about the spec I understood from the deprecation warning. However this is a breaking change between 4.3.0 and 4.3.1 which should be in a major release instead. And the warning should then say that inheritance without a discriminator has already been removed.
Also 4.3.1 still generates inheritance without a discriminator if there are no own properties in a child subclass.
\\ this will produce inheritance
"Child": {
"title": "Child",
"allOf": [
{"$ref": "#/components/schemas/Parent"},
{"properties": {}}
]
},
Also 4.3.1 still generates inheritance without a discriminator
I believe this is the case mentioned in the deprecation warning. This logic is still there (as it says - "will be removed in a future release").
I assume in 4.3.0 some related PRs have introduced undesired behavior, which was then reverted in 4.3.1.
I have no issues with the spec other than that I fail to understand what should be a discriminator in this case:
schema
json
{
"definitions": {
"Category": {
"title": "Category",
"additionalProperties": false,
"properties": {
"id": {
"type": "number"
},
"tags": {
"type": "array",
"items": {
"type": "string",
"enum": [
"foo",
"bar",
"baz"
]
}
}
}
},
"Product": {
"title": "Product",
"additionalProperties": false,
"properties": {
"id": {
"type": "number"
},
"categoryId": {
"type": "number"
}
}
}
},
"title": "CategoryWithRelations",
"allOf": [
{
"$ref": "#/definitions/Category"
},
{
"properties": {
"products": {
"type": "array",
"items": {
"$ref": "#/definitions/Product"
}
}
}
}
]
}
But that's OK, I can live with composition in TypeScript case. The problem I have is that I see no way to distinguish own properties and those pulled from ref in templates. isInherited flag is not set and there is no isMixedIn flag.
In the example above this makes the generator create 2 separate tags enums which leads to Category and CategoryWithRelations being not compatible TypeScript types.
2 separate tags enums which leads to Category and CategoryWithRelations being not compatible TypeScript types
Unfortunately it's a limitation of OAS2. Even without inheritance at all - OAS2 doesn't allow to define two fields in the same or different models sharing the same enum, because enum definition is always inlined and cannot be reused via ref.
I also see this issue in my TS project.
An ultimate solution for this enums problem is to migrate to OAS3.
Here is a related discussion about generated enums for TS - you might find it helpful: https://github.com/OpenAPITools/openapi-generator/pull/6360#pullrequestreview-414751467
As for inheritance/composition - you could probably fix it at your side by overriding templates like this: https://github.com/OpenAPITools/openapi-generator/pull/4805
As for inheritance/composition - you could probably fix it at your side by overriding templates like this: #4805
@amakhrov That worked well, thanks.
I have one more remark, which probably belongs to #5171
In case of allOf composition vars should not be populated with properties from refs. I believe allVars is intended for that. This way a model with allOf can have own properties as vars and there is no need for extra ModelAllOf class. And templates could still generate inheritance if they wish so and there is only one $ref.
python type is broken in 4.2.3
I am not able to get the following definition to generate java or type script correctly.
swagger: "2.0"
info:
title: Test Command model generation
description: Test Command model generation
version: 1.0.0
host: localhost:8080
schemes:
- https
definitions:
Command:
title: Command
description: The base object for all command objects.
x-swagger-router-model: CommandDto
type: object
properties: {}
RealCommand:
title: RealCommand
description: The real command.
x-swagger-router-model: RealCommandDto
allOf:
- $ref: '#/definitions/Command'
ApiError:
description: The base object for API errors.
x-swagger-router-model: ApiGeneralException
type: object
required:
- code
- message
properties:
code:
description: The error code. Usually, it is the HTTP error code.
type: string
readOnly: true
message:
description: The error message.
type: string
readOnly: true
title: ApiError
parameters:
b_real_command:
name: real_command
in: body
description: A payload for executing a real command.
required: true
schema:
$ref: '#/definitions/RealCommand'
paths:
/execute:
post:
produces: []
x-swagger-router-controller: FakeController
operationId: executeRealCommand
parameters:
- name: real_command
in: body
description: A payload for executing a real command.
required: true
schema:
$ref: '#/definitions/RealCommand'
responses:
'204':
description: Successful request. No content returned.
'400':
description: Bad request.
schema:
$ref: '#/definitions/ApiError'
'404':
description: Not found.
schema:
$ref: '#/definitions/ApiError'
default:
description: Unknown error.
schema:
$ref: '#/definitions/ApiError'
Have tried with 4.3.1.
In Java, RealCommand is generated as
public class RealCommand extends Command {
...
}
Notice that I did not specify a discriminator in Command. I expect this definition to generate a composition of Command and RealCommand.java and that Command.java would not be generated. Command.java file is not generated, but it is also expected as a base class in RealCommand.java, so this does not compile.
There should not be any inheritance here because there is no discriminator.
I am having similar issues with Spring generator: getting UNKNOWN_BASE_TYPE and "oneOf{Model1}{Model2}.java" is not generated..
I'm facing the same issues reported by @iekdosha
I solved the inheritance problem for the typescript/angular generator indenting the property object with the ref object.
From
ExtendedErrorModel:
allOf:
- $ref: '#/components/schemas/BasicErrorModel'
- type: object
required:
- rootCause
properties:
rootCause:
type: string
to
ExtendedErrorModel:
allOf:
- $ref: '#/components/schemas/BasicErrorModel'
type: object
required:
- rootCause
properties:
rootCause:
type: string
In this way the generated typescript interface is
export interface ExtendedErrorModel extends BaseErrorModel {
rootCause: string;
}
Most helpful comment
Enough people have commented on this. The old behavior is what we expect. Not the fixed one.
Inheritance via allOf (what we want) isn't the same as polymorphic serialization (what discriminator does). Could we at least have an option to restore the previous behavior that worked for a lot of people?