Skip to content

Commit 7801f59

Browse files
samarpanBnabdelgadir
authored andcommitted
feat(repository-json-schema): enhance getJsonSchema to describe navigational properties
Enhance `getJsonSchema` to describe navigational properties fix #2630
1 parent 579f247 commit 7801f59

File tree

14 files changed

+348
-28
lines changed

14 files changed

+348
-28
lines changed

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

Lines changed: 194 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
JsonSchema,
1818
JSON_SCHEMA_KEY,
1919
modelToJsonSchema,
20+
MODEL_TYPE_KEYS,
2021
} from '../..';
2122
import {expectValidJsonSchema} from '../helpers/expect-valid-json-schema';
2223

@@ -648,7 +649,7 @@ describe('build-schema', () => {
648649
};
649650
MetadataInspector.defineMetadata(
650651
JSON_SCHEMA_KEY,
651-
cachedSchema,
652+
{[MODEL_TYPE_KEYS.ModelOnly]: cachedSchema},
652653
TestModel,
653654
);
654655
const jsonSchema = getJsonSchema(TestModel);
@@ -717,5 +718,197 @@ describe('build-schema', () => {
717718
expect(schema).to.deepEqual(expectedSchemaForCategory);
718719
});
719720
});
721+
722+
it('converts HasMany and BelongsTo relation links', () => {
723+
@model()
724+
class Product extends Entity {
725+
@property({id: true})
726+
id: number;
727+
728+
@belongsTo(() => Category)
729+
categoryId: number;
730+
}
731+
732+
@model()
733+
class Category extends Entity {
734+
@property({id: true})
735+
id: number;
736+
737+
@hasMany(() => Product)
738+
products?: Product[];
739+
}
740+
741+
const expectedSchema: JsonSchema = {
742+
definitions: {
743+
ProductWithRelations: {
744+
title: 'ProductWithRelations',
745+
properties: {
746+
id: {type: 'number'},
747+
categoryId: {type: 'number'},
748+
category: {$ref: '#/definitions/CategoryWithRelations'},
749+
},
750+
},
751+
},
752+
properties: {
753+
id: {type: 'number'},
754+
products: {
755+
type: 'array',
756+
items: {$ref: '#/definitions/ProductWithRelations'},
757+
},
758+
},
759+
title: 'CategoryWithRelations',
760+
};
761+
const jsonSchema = getJsonSchema(Category, {includeRelations: true});
762+
expect(jsonSchema).to.deepEqual(expectedSchema);
763+
});
764+
765+
it('converts relation links when no other properties there', () => {
766+
@model()
767+
class Product extends Entity {
768+
@property({id: true})
769+
id: number;
770+
771+
@belongsTo(() => CategoryWithoutProp)
772+
categoryId: number;
773+
}
774+
775+
@model()
776+
class CategoryWithoutProp extends Entity {
777+
@hasMany(() => Product)
778+
products?: Product[];
779+
}
780+
const expectedSchema: JsonSchema = {
781+
definitions: {
782+
ProductWithRelations: {
783+
title: 'ProductWithRelations',
784+
properties: {
785+
id: {type: 'number'},
786+
categoryId: {type: 'number'},
787+
category: {
788+
$ref: '#/definitions/CategoryWithoutPropWithRelations',
789+
},
790+
},
791+
},
792+
},
793+
properties: {
794+
products: {
795+
type: 'array',
796+
items: {$ref: '#/definitions/ProductWithRelations'},
797+
},
798+
},
799+
title: 'CategoryWithoutPropWithRelations',
800+
};
801+
802+
// To check for case when there are no other properties than relational
803+
const jsonSchemaWithoutProp = getJsonSchema(CategoryWithoutProp, {
804+
includeRelations: true,
805+
});
806+
expect(jsonSchemaWithoutProp).to.deepEqual(expectedSchema);
807+
});
808+
809+
it('gets cached JSON schema with relation links if one exists', () => {
810+
@model()
811+
class Product extends Entity {
812+
@property({id: true})
813+
id: number;
814+
815+
@belongsTo(() => Category)
816+
categoryId: number;
817+
}
818+
819+
@model()
820+
class Category extends Entity {
821+
@property({id: true})
822+
id: number;
823+
824+
@hasMany(() => Product)
825+
products?: Product[];
826+
}
827+
828+
const cachedSchema: JsonSchema = {
829+
definitions: {
830+
ProductWithRelations: {
831+
title: 'ProductWithRelations',
832+
properties: {
833+
id: {type: 'number'},
834+
categoryId: {type: 'number'},
835+
category: {$ref: '#/definitions/CategoryWithRelations'},
836+
},
837+
},
838+
},
839+
properties: {
840+
id: {type: 'number'},
841+
cachedProp: {type: 'string'},
842+
products: {
843+
type: 'array',
844+
items: {$ref: '#/definitions/ProductWithRelations'},
845+
},
846+
},
847+
title: 'CategoryWithRelations',
848+
};
849+
MetadataInspector.defineMetadata(
850+
JSON_SCHEMA_KEY,
851+
{[MODEL_TYPE_KEYS.ModelWithRelations]: cachedSchema},
852+
Category,
853+
);
854+
const jsonSchema = getJsonSchema(Category, {includeRelations: true});
855+
expect(jsonSchema).to.eql(cachedSchema);
856+
});
857+
858+
it('updates same cache with new key if one exists for model', () => {
859+
@model()
860+
class Product extends Entity {
861+
@property({id: true})
862+
id: number;
863+
864+
@belongsTo(() => Category)
865+
categoryId: number;
866+
}
867+
868+
@model()
869+
class Category extends Entity {
870+
@property({id: true})
871+
id: number;
872+
873+
@hasMany(() => Product)
874+
products?: Product[];
875+
}
876+
877+
const cachedSchema: JsonSchema = {
878+
definitions: {
879+
ProductWithRelations: {
880+
title: 'ProductWithRelations',
881+
properties: {
882+
id: {type: 'number'},
883+
categoryId: {type: 'number'},
884+
category: {$ref: '#/definitions/CategoryWithRelations'},
885+
},
886+
},
887+
},
888+
properties: {
889+
id: {type: 'number'},
890+
cachedProp: {type: 'string'},
891+
products: {
892+
type: 'array',
893+
items: {$ref: '#/definitions/ProductWithRelations'},
894+
},
895+
},
896+
title: 'CategoryWithRelations',
897+
};
898+
MetadataInspector.defineMetadata(
899+
JSON_SCHEMA_KEY,
900+
{[MODEL_TYPE_KEYS.ModelWithRelations]: cachedSchema},
901+
Category,
902+
);
903+
const jsonSchema = getJsonSchema(Category);
904+
// Make sure it's not pulling the withrelations key
905+
expect(jsonSchema).to.not.eql(cachedSchema);
906+
expect(jsonSchema).to.eql({
907+
properties: {
908+
id: {type: 'number'},
909+
},
910+
title: 'Category',
911+
});
912+
});
720913
});
721914
});

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,17 @@
33
// This file is licensed under the MIT License.
44
// License text available at https://opensource.org/licenses/MIT
55

6-
import {PropertyDefinition} from '@loopback/repository';
6+
import {
7+
PropertyDefinition,
8+
RelationDefinitionBase,
9+
RelationType,
10+
} from '@loopback/repository';
711
import {expect} from '@loopback/testlab';
8-
import {metaToJsonProperty, stringTypeToWrapper} from '../..';
12+
import {
13+
getNavigationalPropertyForRelation,
14+
metaToJsonProperty,
15+
stringTypeToWrapper,
16+
} from '../..';
917

1018
describe('build-schema', () => {
1119
class CustomType {}
@@ -190,4 +198,20 @@ describe('build-schema', () => {
190198
});
191199
});
192200
});
201+
202+
describe('getNavigationalPropertyForRelation', () => {
203+
it('errors out if targetsMany is undefined', () => {
204+
expect(() =>
205+
getNavigationalPropertyForRelation(
206+
{
207+
type: RelationType.hasMany,
208+
name: 'Test',
209+
} as RelationDefinitionBase,
210+
{
211+
$ref: `#/definitions/Test`,
212+
},
213+
),
214+
).to.throw(/targetsMany attribute missing for Test/);
215+
});
216+
});
193217
});

0 commit comments

Comments
 (0)