-
Notifications
You must be signed in to change notification settings - Fork 1.1k
jsonSchema composition for inheritance and includeRelations #5584
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
Conversation
|
Hi, |
|
@Smixi It won't address reusable enums yet. Also OpenAPI generators are not good at enums either. |
Ok thanks for the answer ! |
|
@InvictusMB It seems my LB4 version is one minor version more than yours, and the patch thus cannot be applied (2.4.2 v 2.4.3), i'll try to downgrade if they are no breaking changes I'll tell you if that works fine. |
|
@InvictusMB I just Installed the patch but unfortunately it seems to keep the inlining of the enums. Here is a property of my model Weapon : export enum CategoryType {
A = "A",
B = "B",
C = "C",
D = "D",
}
@property({
type: 'string',
required: false,
postgresql: { columnName: 'category', dataType: 'text', dataLength: null, dataPrecision: null, dataScale: null, nullable: 'YES' },
jsonSchema: {
type: ["string", "null"],
enum: Object.values(CategoryType)
}
})
category?: CategoryType;Here is a part of the controller : @authenticate('jwt')
@post('/weapons', {
responses: {
'200': {
description: 'Weapon model instance',
content: { 'application/json': { schema: getModelSchemaRef(Weapon) } },
},
},
})
async create(
@requestBody({
content: {
'application/json': {
schema: getModelSchemaRef(Weapon, {
title: 'NewWeapon',
exclude: ['id'],
}),
},
},
})
weapon: Omit<Weapon, 'id'>,
): Promise<Weapon> {...}
@authenticate("jwt")
@get('/weapons/{id}', {
responses: {
'200': {
description: 'Weapon model instance',
content: {
'application/json': {
schema: getModelSchemaRef(Weapon, { includeRelations: true }),
},
},
},
},
})
async findById(
@param.path.string('id') id: string,
@param.query.object('filter', getFilterSchemaFor(Weapon)) filter?: Filter<Weapon>
): Promise<Weapon> {
return this.weaponRepository.findById(id, filter);
}And here is the associated part of the JSON "NewWeapon":{ "title":"NewWeapon",
"description":"(tsType: Omit<Weapon, 'id'>, schemaOptions: { title: 'NewWeapon', exclude: [ 'id' ] })",
"properties":{ "serialnumber":{
"type":"string"
},
"model":{
"type":"string"
},
"available":{
"type":"boolean"
},
"description":{
"type":"string"
},
"deleted":{
"type":"boolean"
},
"handtype":{ "type":"string",
"enum":[
"rightHanded",
"leftHanded",
"ambidextrous"
]
},
"weaponType":{ "type":"string",
"enum":[
"rifle",
"pistol",
"cannon",
"carbine",
"crossbow"
]
},
"caliber":{ "type":"string",
"enum":[
"4.5mm",
"7.5mm",
".22LR",
".38",
"arrow",
"lever"
]
},
"category":{ "type":"string",
"enum":[
"A",
"B",
"C",
"D"
]
},
"price":{
"type":"string"
},
"clubNumber":{
"type":"string"
}
},
"required":[
"serialnumber"
],
"additionalProperties":false,
"x-typescript-type":"Omit<Weapon, 'id'>"
},
"WeaponPartial":{ "title":"WeaponPartial",
"description":"(tsType: Partial<Weapon>, schemaOptions: { partial: true })",
"properties":{ "id":{
"type":"string"
},
"serialnumber":{
"type":"string"
},
"model":{
"type":"string"
},
"available":{
"type":"boolean"
},
"description":{
"type":"string"
},
"deleted":{
"type":"boolean"
},
"handtype":{ "type":"string",
"enum":[
"rightHanded",
"leftHanded",
"ambidextrous"
]
},
"weaponType":{ "type":"string",
"enum":[
"rifle",
"pistol",
"cannon",
"carbine",
"crossbow"
]
},
"caliber":{ "type":"string",
"enum":[
"4.5mm",
"7.5mm",
".22LR",
".38",
"arrow",
"lever"
]
},
"category":{ "type":"string",
"enum":[
"A",
"B",
"C",
"D"
]
},
"price":{
"type":"string"
},
"clubNumber":{
"type":"string"
}
},
"additionalProperties":false,
"x-typescript-type":"Partial<Weapon>"
}Am I doing something wrong ? |
|
This is a slightly different use case. I've only added special handling for |
|
@InvictusMB Ok I see ! Thus Weapon and WeaponWithRelation should have the same enum, I'll check this. Otherwise sorry, I got in fact confused with the use case fixed by your PR. Thanks you :) |
| definitions: { | ||
| ProductWithRelations: { | ||
| title: 'ProductWithRelations', | ||
| Category: { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it very difficult to review this part of the changeset. Is there a way how to preserve the old order of definitions entries, to keep the amount of changed lines as small as possible please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bajtos I don't know how to proceed with this PR tbh.
The original goal was to eliminate all duplication that blows up taxonomy in OpenAPI definitions. The reason is that each model creates 4+ definitions (Thing, ThingPartial, ThingWithRelations, NewThing, etc.) and this causes issues in the client-side generated code as TypeScript does not consider all those incarnations of the same model compatible.
This code seems to work fine on my projects but I've observed more cases where extra definitions are created. Other variations on JsonSchemaOptions (e.g. excluding properties) create new definitions. I'm not sure if this code is future-proof. A proper fix would be to construct an inheritance chain/tree in terms of OpenAPI schema but I don't have enough experience with lb4 to account for all scenarios.
Also I'm not sure if the isInherited check and getParentModel implemented here are sufficient.
My suggestion for moving this forward would be to expose modelToJsonSchema to userland customization/override instead. So that we can explore the advanced scenarios and come up with patterns that work before writing them in stone.
I like the idea of using inheritance for different variants of model schemas. I think Thing, ThingWithRelations and NewThing may be reasonably easy to implement (
I think that's the best approach to get started 👍🏻 . Would you like to open a new pull request for that? Another idea to consider: #4365 landed a feature that consolidates OpenAPI schema to move repeated in-place schema definitions to shared |
On the second thought, I think it's better to implement this feature as a new OASEnhancer that should be executed after |
|
We just switch the contribution method from CLA to DCO, making your contribution easier in the future. Please sign the commits with DCO by amending your commit messages with Please refer to this docs page for details. Thanks! |
|
This pull request has been marked stale because it has not seen activity within two months. It will be closed within 14 days of being stale unless there is new activity. |
|
This pull request has been marked stale because it has not seen activity within two months. It will be closed within 14 days of being stale unless there is new activity. |
|
This pull request has been closed due to continued inactivity. If you are interested in finishing the proposed changes, then feel free to re-open this pull request or open a new one. |
Implement a workaround for enum duplication in a generated OpenAPI spec mentioned here.
As of
@loopback/repository-json-schema@2.4.2both inheritance andgetModelSchemaRef(SomeModel, {includeRelations: true})produce duplication in definitions. If an enum type property is present inSomeModeland a client is generated from such a spec (via typescript-fetch in my case),SomeModelandSomeModelWithRelationswill have incompatible types in generated code. Same happens if a model inherits another one with an enum in it.This PR replaces all inherited props with
allOfcomposition and a$refto a parent schema in both cases.Checklist
👉 Read and sign the CLA (Contributor License Agreement) 👈
npm testpasses on your machinepackages/cliwere updatedexamples/*were updated👉 Check out how to submit a PR 👈