UNPKG

3.88 kBPlain TextView Raw
1// Copyright IBM Corp. and LoopBack contributors 2018,2019. 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 debugFactory from 'debug';
7import {DataObject} from '../../common-types';
8import {Entity} from '../../model';
9import {EntityCrudRepository} from '../../repositories/repository';
10import {
11 BelongsToDefinition,
12 Getter,
13 InclusionResolver,
14} from '../relation.types';
15import {resolveBelongsToMetadata} from './belongs-to.helpers';
16import {createBelongsToInclusionResolver} from './belongs-to.inclusion-resolver';
17import {DefaultBelongsToRepository} from './belongs-to.repository';
18
19const debug = debugFactory('loopback:repository:relations:belongs-to:accessor');
20
21export interface BelongsToAccessor<Target extends Entity, SourceId> {
22 /**
23 * Invoke the function to obtain HasManyRepository.
24 */
25 (sourceId: SourceId, polymorphicTypes?: string | string[]): Promise<Target>;
26
27 /**
28 * Use `resolver` property to obtain an InclusionResolver for this relation.
29 */
30 inclusionResolver: InclusionResolver<Entity, Target>;
31}
32
33/**
34 * Enforces a BelongsTo constraint on a repository
35 * If the target model is polymorphic, i.e. stored within different repositories,
36 * supply the targetRepositoryGetter with a dictionary in the form of {[typeName: string]: repositoryGetter}
37 */
38export function createBelongsToAccessor<
39 Target extends Entity,
40 TargetId,
41 Source extends Entity,
42 SourceId,
43>(
44 belongsToMetadata: BelongsToDefinition,
45 targetRepositoryGetter:
46 | Getter<EntityCrudRepository<Target, TargetId>>
47 | {
48 [repoType: string]: Getter<EntityCrudRepository<Target, TargetId>>;
49 },
50 sourceRepository: EntityCrudRepository<Source, SourceId>,
51): BelongsToAccessor<Target, SourceId> {
52 const meta = resolveBelongsToMetadata(belongsToMetadata);
53 // resolve the repositoryGetter into a dictionary
54 if (typeof targetRepositoryGetter === 'function') {
55 targetRepositoryGetter = {
56 [meta.target().name]: targetRepositoryGetter as Getter<
57 EntityCrudRepository<Target, TargetId>
58 >,
59 };
60 }
61 debug('Resolved BelongsTo relation metadata: %o', meta);
62 const result: BelongsToAccessor<Target, SourceId> =
63 async function getTargetInstanceOfBelongsTo(
64 sourceId: SourceId,
65 polymorphicTypes?: string | string[],
66 ) {
67 if (meta.polymorphic !== false) {
68 if (!polymorphicTypes || polymorphicTypes.length === 0) {
69 console.warn(
70 'It is highly recommended to specify the polymorphicTypes param when using polymorphic types.',
71 );
72 }
73 }
74 const foreignKey = meta.keyFrom;
75 const primaryKey = meta.keyTo;
76 const sourceModel = await sourceRepository.findById(sourceId);
77 const foreignKeyValue = sourceModel[foreignKey as keyof Source];
78 // workaround to check referential integrity.
79 // should be removed once the memory connector ref integrity is done
80 // GH issue: https://github.com/loopbackio/loopback-next/issues/2333
81 if (!foreignKeyValue) {
82 return undefined as unknown as Target;
83 }
84 // eslint-disable-next-line @typescript-eslint/no-explicit-any
85 const constraint: any = {[primaryKey]: foreignKeyValue};
86 const constrainedRepo = new DefaultBelongsToRepository(
87 targetRepositoryGetter as {
88 [repoType: string]: Getter<EntityCrudRepository<Target, TargetId>>;
89 },
90 constraint as DataObject<Target>,
91 belongsToMetadata.target,
92 );
93 return constrainedRepo.get({polymorphicType: polymorphicTypes});
94 };
95
96 result.inclusionResolver = createBelongsToInclusionResolver(
97 meta,
98 targetRepositoryGetter as {
99 [repoType: string]: Getter<EntityCrudRepository<Target, TargetId>>;
100 },
101 );
102 return result;
103}