UNPKG

3.69 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 {DecoratorFactory, MetadataInspector} from '@loopback/core';
7import {property} from '../../decorators';
8import {Entity, EntityResolver, PropertyDefinition} from '../../model';
9import {relation} from '../relation.decorator';
10import {ReferencesManyDefinition, RelationType} from '../relation.types';
11
12/**
13 * Decorator for referencesMany
14 * @param targetResolver - A resolver function that returns the target model for
15 * a referencesMany relation
16 * @param definition - Optional metadata for setting up a referencesMany relation
17 * @param propertyDefinition - Optional metadata for setting up the property
18 * @returns A property decorator
19 */
20export function referencesMany<T extends Entity>(
21 targetResolver: EntityResolver<T>,
22 definition?: Partial<ReferencesManyDefinition>,
23 propertyDefinition?: Partial<PropertyDefinition>,
24) {
25 return function (decoratedTarget: Entity, decoratedKey: string) {
26 const propType =
27 MetadataInspector.getDesignTypeForProperty(
28 decoratedTarget,
29 decoratedKey,
30 ) ?? propertyDefinition?.type;
31
32 if (!propType) {
33 const fullPropName = DecoratorFactory.getTargetName(
34 decoratedTarget,
35 decoratedKey,
36 );
37 throw new Error(
38 `Cannot infer type of model property ${fullPropName} because ` +
39 'TypeScript compiler option `emitDecoratorMetadata` is not set. ' +
40 'Please enable `emitDecoratorMetadata` or use the third argument of ' +
41 '`@referencesMany` decorator to specify the property type explicitly.',
42 );
43 }
44
45 const sourceKeyType = MetadataInspector.getDesignTypeForProperty(
46 targetResolver().prototype,
47 definition?.keyTo ?? 'id',
48 );
49
50 if (!sourceKeyType) {
51 const fullPropName = DecoratorFactory.getTargetName(
52 targetResolver().prototype,
53 definition?.keyTo ?? 'id',
54 );
55 throw new Error(
56 `Cannot infer type of model property ${fullPropName} because ` +
57 'TypeScript compiler option `emitDecoratorMetadata` is not set. ' +
58 'Please enable `emitDecoratorMetadata` or use the second argument of ' +
59 '`@referencesMany` decorator to specify the property type explicitly.',
60 );
61 }
62
63 const propMeta: PropertyDefinition = Object.assign(
64 {},
65 // properties provided by the caller
66 propertyDefinition,
67 // properties enforced by the decorator
68 {
69 type: propType,
70 itemType: sourceKeyType,
71 // TODO(bajtos) Make the foreign key required once our REST API layer
72 // allows controller methods to exclude required properties
73 // required: true,
74 },
75 );
76 property(propMeta)(decoratedTarget, decoratedKey);
77
78 // @referencesMany() is typically decorating the foreign key property,
79 // e.g. customerIds. We need to strip the trailing "Ids" suffix from the name.
80 const relationName = decoratedKey.replace(/Ids$/, 's');
81
82 const meta: ReferencesManyDefinition = Object.assign(
83 // default values, can be customized by the caller
84 {
85 keyFrom: decoratedKey,
86 name: relationName,
87 },
88 // properties provided by the caller
89 definition,
90 // properties enforced by the decorator
91 {
92 type: RelationType.referencesMany,
93 targetsMany: true,
94 source: decoratedTarget.constructor,
95 target: targetResolver,
96 },
97 );
98 relation(meta)(decoratedTarget, decoratedKey);
99 };
100}