npm install && npm run startthe generated openapi3 spec has the parent class itself as the related class.
see: 'components -> schema -> ParentWithChildren -> Properties -> children -> items -> $ref'
"components": {
"schemas": {
"ParentWithChildren": {
"title": "ParentWithChildren",
"description": "(Schema options: { title: 'ParentWithChildren', includeRelations: true })",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"children": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ParentWithChildren" // <--- expect "#/components/schemas/ParentWithChildren"
}
}
},
"required": [
"name"
],
"additionalProperties": false
}
}
}
the related class should be the class 'Children'
"components": {
"schemas": {
"ParentWithChildren": {
"title": "ParentWithChildren",
"description": "(Schema options: { title: 'ParentWithChildren', includeRelations: true })",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"children": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ParentWithChildren" // <--- expect "#/components/schemas/ParentWithChildren"
}
}
},
"required": [
"name"
],
"additionalProperties": false
}
}
}
https://github.com/iLem0n/loopback-relation-error
node -e 'console.log(process.platform, process.arch, process.versions.node)' --->
darwin x64 12.10.0
npm ls --prod --depth 0 | grep loopback --->
├── @loopback/[email protected]
├── @loopback/[email protected]
├── @loopback/[email protected]
├── @loopback/[email protected]
├── @loopback/[email protected]
├── @loopback/[email protected]
├── @loopback/[email protected]
├── @loopback/[email protected]
Hello @dougal83: Thanks for the quick reply. This would only lead to a 'Children' class with the structure (attributes) of a 'Parent'-Class. The $ref would be the right one, but the class naming would be wrong and misleading.
I would expect a class "ParentWithChildren" which has the parent structure (attributes of class 'Parent') and the resolved class of the items in the children's array should be 'Children'.
something like:
"ParentWithChildren": {
"title": "ParentWithChildren",
"description": "(Schema options: { title: 'ParentWithChildren', includeRelations: true })",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
},
"children": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Children"
}
}
},
"required": [
"name"
],
"additionalProperties": false
}
Or is there a missunderstanding from my side ?
Before I didn't used the 'title'-option but this leads into multiple classes with ugly naming:

without the title option the related class is generated the right way:

@iLem0n Sorry, I deleted my post earlier as I didn't have time to follow up with anything coherent. The last image with ChildWithRelations is what I would expect to see. I think that the use of title was overriding the naming of the child.
Are you all good now?
@dougal83: No problem. This would be the right behaviour, yes. but this only works without the 'title'-option. The options brakes this behavior. In this screenshot you see two times the same configuration schema, the only difference is the title attribute. the second one also changes the items $ref

I've build a workaround by adding the schemas manually in the @api() decorator of the class.
```import { getModelSchemaRef } from "@loopback/rest";
import { JsonSchemaOptions, } from '@loopback/repository-json-schema';
export const defaultSchemaOptions = {
exclude: ['ownerId']
};
export function getDefaultModelSchemaRef
modelCtor: Function & {prototype: T},
additionalOptions?: JsonSchemaOptions
propertyOverrides?: {} | null
) {
const options = Object.assign({}, defaultSchemaOptions, additionalOptions);
const schemaRef = getModelSchemaRef(modelCtor, options);
const firstKey = Object.keys(schemaRef.definitions)[0];
const def = schemaRef.definitions[firstKey];
return {
...def,
properties: {
...def.properties,
...propertyOverrides
}
};
}
then, on the controller:
@api({
basePath: '/plans',
paths: {},
components: {
schemas: {
"Plan": getDefaultModelSchemaRef(Plan, {
title: 'Plan'
}),
"PlanNew": getDefaultModelSchemaRef(Plan, {
title: 'PlanNew',
exclude: ['id']
}),
"PlanPartial": getDefaultModelSchemaRef(Plan, {
title: 'PlanPartial',
partial: true
}),
"PlanWithItems": getDefaultModelSchemaRef(Plan, {
title: 'PlanWithItems'
}, {
items: {
type: "array",
items: getModelSchemaRef(PlanItem)
}
})
}
},
})
export class PlanController { ... }
```
this actually works for me, but it is not the prettiest way to work with it.
This is just a workaround to mask the wrong behavior of the title-option.
UPDATE: just recognized that I can use this workaround directly in the endpoint spec. You don't have to put it in the @api() at the top.
This is just a workaround to mask the wrong behaviour of the title-option.
Yeah, the behaviour of the title option is a bug. Thank you for highlighting it.
So to clarify, when applying the title option to parent schema, the included relations also have the parent's title applied, which is a bug.
Hey @agnes512 Could you please take a look?
@dougal83: Yes that totally right. Thanks for your support.
@agnes512: Thank you too ;)
Another example of broken relation : Need to remove title property to get expected behaviour
This line causes the problem:
modelToJsonSchema takes in the options(which has the title property) to build the response schema.
Since the Parent model has a relation property children, it builds child model's schema starting from line 499, which calls modelToJsonSchema again and passes the same options to it.
I am looking at the JsonSchemaOptions interface, I still think the title is a valid option field, while it shouldn't be passed to the recursive calls inside modelToJsonSchema by any means:
Building the schema for relation properties OR properties with custom type(need to double check the behavior for this use case).
Submitting a PR to fix it.
Created a PR in https://github.com/strongloop/loopback-next/pull/5108 for it.
should be fixed by https://github.com/strongloop/loopback-next/pull/5108, feel free to reopen it if you still have the problem.
How about when the property is not a relation, but rather an embedded object (which is often the case when using MongoDB)?
Is there any harm in adding a "delete propOptions.title;" after line 475? This fixed the same problem as stated by the OP in our case (where the model is simply stored as part of the parent model).
Edit: After inspecting the schema I see that all nested objects are created with its own generated title (e.g. "XExcluding_id_"). I have not really studied the underlying code, but apparently there is something that is done with the title which does not work well with nested object properties which are not relations.
@Svettis2k Good catch. I think title in options should be deleted right after generating the title for the current schema: https://github.com/strongloop/loopback-next/blob/master/packages/repository-json-schema/src/build-schema.ts#L416.
Let me submit a PR for further discussion.
cc @Svettis2k PR created in https://github.com/strongloop/loopback-next/pull/5231 🙂 hope it fixes your problem.
@jannyHou I don't think we're quite there yet. I see the reason for the EDIT in my previous post is due to exclude being passed on to the generation of nested properties as well. All the nested objects will then get a title on the form "XExcluding_id_" (given that the propOptions.exclude = ['id']). I would assume that the exclude option should only be used for the parent model?
It might be worth taking a look at which of the JSON schema options should be kept solely at the parent level (or perhaps which are required to be passed down)?
Most helpful comment
This line causes the problem:
modelToJsonSchematakes in theoptions(which has thetitleproperty) to build the response schema.Since the
Parentmodel has a relation propertychildren, it builds child model's schema starting from line 499, which callsmodelToJsonSchemaagain and passes the same options to it.I am looking at the
JsonSchemaOptionsinterface, I still think thetitleis a valid option field, while it shouldn't be passed to the recursive calls insidemodelToJsonSchemaby any means:Building the schema for relation properties OR properties with custom type(need to double check the behavior for this use case).
Submitting a PR to fix it.