Skip to content

Commit 3ecf708

Browse files
KalleVdhmlau
authored andcommitted
fix(repository-json-schema): fix circular chains when using 'includeRelations'
Signed-off-by: KalleV <kvirtaneva@gmail.com>
1 parent 7ea0d92 commit 3ecf708

3 files changed

Lines changed: 65 additions & 35 deletions

File tree

packages/repository-json-schema/src/__tests__/integration/build-schema.integration.ts

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,19 +1142,12 @@ describe('build-schema', () => {
11421142

11431143
const expectedSchema: JsonSchema = {
11441144
definitions: {
1145-
ProductWithRelations: {
1146-
title: 'ProductWithRelations',
1145+
Product: {
1146+
title: 'Product',
11471147
type: 'object',
1148-
description:
1149-
`(tsType: ProductWithRelations, ` +
1150-
`schemaOptions: { includeRelations: true })`,
11511148
properties: {
11521149
id: {type: 'number'},
11531150
categoryId: {type: 'number'},
1154-
category: {
1155-
$ref: '#/definitions/CategoryWithRelations',
1156-
},
1157-
foreignKey: 'categoryId' as JsonSchema,
11581151
},
11591152
additionalProperties: false,
11601153
},
@@ -1164,7 +1157,7 @@ describe('build-schema', () => {
11641157
products: {
11651158
type: 'array',
11661159
items: {
1167-
$ref: '#/definitions/ProductWithRelations',
1160+
$ref: '#/definitions/Product',
11681161
},
11691162
},
11701163
},
@@ -1196,27 +1189,20 @@ describe('build-schema', () => {
11961189
}
11971190
const expectedSchema: JsonSchema = {
11981191
definitions: {
1199-
ProductWithRelations: {
1200-
title: 'ProductWithRelations',
1192+
Product: {
1193+
title: 'Product',
12011194
type: 'object',
1202-
description:
1203-
`(tsType: ProductWithRelations, ` +
1204-
`schemaOptions: { includeRelations: true })`,
12051195
properties: {
12061196
id: {type: 'number'},
12071197
categoryId: {type: 'number'},
1208-
category: {
1209-
$ref: '#/definitions/CategoryWithoutPropWithRelations',
1210-
},
1211-
foreignKey: 'categoryId' as JsonSchema,
12121198
},
12131199
additionalProperties: false,
12141200
},
12151201
},
12161202
properties: {
12171203
products: {
12181204
type: 'array',
1219-
items: {$ref: '#/definitions/ProductWithRelations'},
1205+
items: {$ref: '#/definitions/Product'},
12201206
},
12211207
},
12221208
additionalProperties: false,
@@ -1234,6 +1220,52 @@ describe('build-schema', () => {
12341220
expect(jsonSchemaWithoutProp).to.deepEqual(expectedSchema);
12351221
});
12361222

1223+
it('does not produce circular $ref chains for bidirectional relations', () => {
1224+
@model()
1225+
class Order extends Entity {
1226+
@property({id: true})
1227+
id: number;
1228+
1229+
@belongsTo(() => Customer)
1230+
customerId: number;
1231+
}
1232+
1233+
@model()
1234+
class Customer extends Entity {
1235+
@property({id: true})
1236+
id: number;
1237+
1238+
@hasMany(() => Order)
1239+
orders?: Order[];
1240+
}
1241+
1242+
const schema = getJsonSchema(Customer, {includeRelations: true});
1243+
1244+
// orders items should reference plain Order, not OrderWithRelations
1245+
expect(schema.properties).to.containDeep({
1246+
orders: {
1247+
type: 'array',
1248+
items: {$ref: '#/definitions/Order'},
1249+
},
1250+
});
1251+
1252+
// The Order definition should be plain — no nested definitions,
1253+
// no customer navigational property
1254+
const orderDef = schema.definitions?.Order as JsonSchema;
1255+
expect(orderDef).to.be.ok();
1256+
expect(orderDef.definitions).to.be.undefined();
1257+
expect(orderDef.properties).to.not.have.property('customer');
1258+
1259+
// No WithRelations keys should appear in definitions
1260+
const defKeys = Object.keys(schema.definitions ?? {});
1261+
for (const key of defKeys) {
1262+
expect(key).to.not.match(
1263+
/WithRelations/,
1264+
`definitions should not contain WithRelations keys, found: ${key}`,
1265+
);
1266+
}
1267+
});
1268+
12371269
it('gets cached JSON schema with relation links if one exists', () => {
12381270
@model()
12391271
class Product extends Entity {

packages/repository-json-schema/src/__tests__/unit/build-schema.unit.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -325,30 +325,27 @@ describe('build-schema', () => {
325325
expect(schema.properties).to.containEql({
326326
children: {
327327
type: 'array',
328-
// The reference here should be `ChildWithRelations`,
329-
// instead of `ParentWithItsChildren`
330-
items: {$ref: '#/definitions/ChildWithRelations'},
328+
// The reference here should be `Child` (not `ParentWithItsChildren`)
329+
// because includeRelations is not cascaded to relation targets.
330+
items: {$ref: '#/definitions/Child'},
331331
},
332332
benchmarkId: {type: 'string'},
333333
color: {type: 'string'},
334334
});
335-
// The recursive calls should NOT inherit
336-
// `title` from the previous call's `options`.
337-
// So the `title` here is `ChildWithRelations`
338-
// instead of `ParentWithItsChildren`.
335+
// The recursive calls should NOT inherit `title` from the previous
336+
// call's `options`, and `includeRelations` is not propagated to
337+
// relation targets. So the definition is plain `Child`.
339338
expect(schema.definitions).to.containEql({
340-
ChildWithRelations: {
341-
title: 'ChildWithRelations',
339+
Child: {
340+
title: 'Child',
342341
type: 'object',
343-
description:
344-
'(tsType: ChildWithRelations, schemaOptions: { includeRelations: true })',
345342
properties: {name: {type: 'string'}},
346343
additionalProperties: false,
347344
},
348345
});
349346
});
350347

351-
it('includeRelations is not propagated to properties decorated with @property()', () => {
348+
it('includeRelations is not propagated to nested properties or relation targets', () => {
352349
@model()
353350
class FooModel extends Entity {
354351
@property({
@@ -379,14 +376,14 @@ describe('build-schema', () => {
379376
name: {
380377
type: 'string',
381378
},
382-
// Decorated with @property() Model should NOT be 'WithRelations'
379+
// Decorated with @property() — no includeRelations propagation
383380
foo: {
384381
$ref: '#/definitions/FooModel',
385382
},
386-
// Decorated with @hasMany() Model should be 'WithRelations'
383+
// Decorated with @hasMany() — also no includeRelations propagation
387384
relatedFoo: {
388385
type: 'array',
389-
items: {$ref: '#/definitions/FooModelWithRelations'},
386+
items: {$ref: '#/definitions/FooModel'},
390387
},
391388
});
392389
});

packages/repository-json-schema/src/build-schema.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,7 @@ export function modelToJsonSchema<T extends object>(
607607
// when generating the relation or property schemas
608608
const targetOptions = {...options};
609609
delete targetOptions.title;
610+
delete targetOptions.includeRelations;
610611

611612
const targetSchema = getJsonSchema(targetType, targetOptions);
612613
const targetRef = {$ref: `#/definitions/${targetSchema.title}`};

0 commit comments

Comments
 (0)