UNPKG

4.48 kBPlain TextView Raw
1// Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
2// Node module: @loopback/repository
3// This file is licensed under the MIT License.
4// License text available at https://opensource.org/licenses/MIT
5
6import {Getter} from '@loopback/core';
7import {cloneDeep} from 'lodash';
8import {EntityNotFoundError, TypeResolver} from '../../';
9import {DataObject, Options} from '../../common-types';
10import {Entity} from '../../model';
11import {constrainFilter, EntityCrudRepository} from '../../repositories';
12/**
13 * CRUD operations for a target repository of a BelongsTo relation
14 */
15export interface BelongsToRepository<Target extends Entity> {
16 /**
17 * Gets the target model instance
18 * @param options
19 * options.polymorphicType - a string or a string array of polymorphic type names
20 * to specify which repositories should are expected to be searched
21 * It is highly recommended to contain this param especially for
22 * datasources using deplicated ids across tables
23 * @returns A promise resolved with the target object or rejected
24 * with an EntityNotFoundError when target model instance was not found.
25 */
26 get(
27 options?: Options & {polymorphicType?: string | string[]},
28 ): Promise<Target>;
29}
30
31export class DefaultBelongsToRepository<
32 TargetEntity extends Entity,
33 TargetId,
34 TargetRepository extends EntityCrudRepository<TargetEntity, TargetId>,
35> implements BelongsToRepository<TargetEntity>
36{
37 /**
38 * Constructor of DefaultBelongsToEntityCrudRepository
39 * @param getTargetRepository - either a dictionary of target model type - target repository instance
40 * or a single target repository instance
41 * e.g. if the target is of a non-polymorphic type "Student", put the studentRepositoryGetterInstance
42 * if the target is of a polymorphic type "Person" which can be either a "Student" or a "Teacher"
43 * then put "{Student: studentRepositoryGetterInstance, Teacher: teacherRepositoryGetterInstance}"
44 * @param constraint - the key value pair representing foreign key name to constrain
45 * the target repository instance
46 * @param targetResolver - () => Target to resolve the target class
47 * e.g. if the target is of type "Student", then put "() => Student"
48 */
49
50 constructor(
51 public getTargetRepository:
52 | Getter<TargetRepository>
53 | {
54 [repoType: string]: Getter<TargetRepository>;
55 },
56 public constraint: DataObject<TargetEntity>,
57 public targetResolver: TypeResolver<Entity, typeof Entity>,
58 ) {
59 if (typeof getTargetRepository === 'function') {
60 this.getTargetRepositoryDict = {
61 [targetResolver().name]:
62 getTargetRepository as Getter<TargetRepository>,
63 };
64 } else {
65 this.getTargetRepositoryDict = getTargetRepository as {
66 [repoType: string]: Getter<TargetRepository>;
67 };
68 }
69 }
70
71 public getTargetRepositoryDict: {
72 [repoType: string]: Getter<TargetRepository>;
73 };
74
75 async get(
76 options?: Options & {polymorphicType?: string | string[]},
77 ): Promise<TargetEntity> {
78 let polymorphicTypes = options?.polymorphicType;
79 let allKeys: string[];
80 if (Object.keys(this.getTargetRepositoryDict).length <= 1) {
81 allKeys = Object.keys(this.getTargetRepositoryDict);
82 } else if (!polymorphicTypes || polymorphicTypes.length === 0) {
83 console.warn(
84 'It is highly recommended to specify the polymorphicTypes param when using polymorphic types.',
85 );
86 allKeys = Object.keys(this.getTargetRepositoryDict);
87 } else {
88 if (typeof polymorphicTypes === 'string') {
89 polymorphicTypes = [polymorphicTypes];
90 }
91 allKeys = [];
92 new Set(polymorphicTypes!).forEach(element => {
93 if (Object.keys(this.getTargetRepositoryDict).includes(element)) {
94 allKeys.push(element);
95 }
96 });
97 }
98 let result: TargetEntity[] = [];
99 for (const key of allKeys) {
100 const targetRepository = await this.getTargetRepositoryDict[key]();
101 result = result.concat(
102 await targetRepository.find(
103 constrainFilter(undefined, this.constraint),
104 Object.assign(cloneDeep(options ?? {}), {polymorphicType: key}),
105 ),
106 );
107 if (result.length >= 1) {
108 return result[0];
109 }
110 }
111 // We don't have a direct access to the foreign key value here :(
112 const id = 'constraint ' + JSON.stringify(this.constraint);
113 throw new EntityNotFoundError(this.targetResolver().name, id);
114 }
115}