UNPKG

3.44 kBPlain TextView Raw
1// Copyright IBM Corp. and LoopBack contributors 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 {camelCase} from 'lodash';
8import {InvalidRelationError} from '../../errors';
9import {isTypeResolver} from '../../type-resolver';
10import {BelongsToDefinition, RelationType} from '../relation.types';
11
12const debug = debugFactory('loopback:repository:relations:belongs-to:helpers');
13
14/**
15 * Relation definition with optional metadata (e.g. `keyTo`) filled in.
16 * @internal
17 */
18export type BelongsToResolvedDefinition = BelongsToDefinition & {
19 keyFrom: string;
20 keyTo: string;
21 polymorphic: false | {discriminator: string};
22};
23
24/**
25 * Resolves given belongsTo metadata if target is specified to be a resolver.
26 * Mainly used to infer what the `keyTo` property should be from the target's
27 * property id metadata
28 * @param relationMeta - belongsTo metadata to resolve
29 * @internal
30 */
31export function resolveBelongsToMetadata(
32 relationMeta: BelongsToDefinition,
33): BelongsToResolvedDefinition {
34 if ((relationMeta.type as RelationType) !== RelationType.belongsTo) {
35 const reason = 'relation type must be BelongsTo';
36 throw new InvalidRelationError(reason, relationMeta);
37 }
38
39 if (!isTypeResolver(relationMeta.target)) {
40 const reason = 'target must be a type resolver';
41 throw new InvalidRelationError(reason, relationMeta);
42 }
43
44 const sourceModel = relationMeta.source;
45 if (!sourceModel?.modelName) {
46 const reason = 'source model must be defined';
47 throw new InvalidRelationError(reason, relationMeta);
48 }
49
50 const targetModel = relationMeta.target();
51 const targetName = targetModel.modelName;
52 debug('Resolved model %s from given metadata: %o', targetName, targetModel);
53
54 let keyFrom;
55 if (
56 relationMeta.keyFrom &&
57 relationMeta.source.definition.properties[relationMeta.keyFrom]
58 ) {
59 keyFrom = relationMeta.keyFrom;
60 } else {
61 keyFrom = camelCase(targetName + '_id');
62 }
63
64 const targetProperties = targetModel.definition.properties;
65 debug('relation metadata from %o: %o', targetName, targetProperties);
66
67 let keyTo;
68 if (relationMeta.keyTo && targetProperties[relationMeta.keyTo]) {
69 // The explicit cast is needed because of a limitation of type inference
70 keyTo = relationMeta.keyTo;
71 } else {
72 keyTo = targetModel.definition.idProperties()[0];
73 if (!keyTo) {
74 const reason = `${targetName} does not have any primary key (id property)`;
75 throw new InvalidRelationError(reason, relationMeta);
76 }
77 }
78
79 let polymorphic: false | {discriminator: string};
80 if (
81 relationMeta.polymorphic === undefined ||
82 relationMeta.polymorphic === false ||
83 !relationMeta.polymorphic
84 ) {
85 const polymorphicFalse = false as const;
86 polymorphic = polymorphicFalse;
87 } else {
88 if (relationMeta.polymorphic === true) {
89 const polymorphicObject: {discriminator: string} = {
90 discriminator: camelCase(relationMeta.target().name + '_type'),
91 };
92 polymorphic = polymorphicObject;
93 } else {
94 const polymorphicObject: {discriminator: string} =
95 relationMeta.polymorphic as {discriminator: string};
96 polymorphic = polymorphicObject;
97 }
98 }
99
100 return Object.assign(relationMeta, {
101 keyFrom,
102 keyTo: keyTo,
103 polymorphic: polymorphic,
104 });
105}