@loopback/repository 4.1.2 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/errors/index.d.ts +1 -0
  2. package/dist/errors/index.js +1 -0
  3. package/dist/errors/index.js.map +1 -1
  4. package/dist/errors/invalid-polymorphism.error.d.ts +5 -0
  5. package/dist/errors/invalid-polymorphism.error.js +22 -0
  6. package/dist/errors/invalid-polymorphism.error.js.map +1 -0
  7. package/dist/model.d.ts +7 -1
  8. package/dist/model.js +14 -0
  9. package/dist/model.js.map +1 -1
  10. package/dist/relations/belongs-to/belongs-to.accessor.d.ts +6 -2
  11. package/dist/relations/belongs-to/belongs-to.accessor.js +18 -5
  12. package/dist/relations/belongs-to/belongs-to.accessor.js.map +1 -1
  13. package/dist/relations/belongs-to/belongs-to.helpers.d.ts +3 -0
  14. package/dist/relations/belongs-to/belongs-to.helpers.js +32 -8
  15. package/dist/relations/belongs-to/belongs-to.helpers.js.map +1 -1
  16. package/dist/relations/belongs-to/belongs-to.inclusion-resolver.d.ts +5 -2
  17. package/dist/relations/belongs-to/belongs-to.inclusion-resolver.js +81 -8
  18. package/dist/relations/belongs-to/belongs-to.inclusion-resolver.js.map +1 -1
  19. package/dist/relations/belongs-to/belongs-to.repository.d.ts +28 -5
  20. package/dist/relations/belongs-to/belongs-to.repository.js +49 -10
  21. package/dist/relations/belongs-to/belongs-to.repository.js.map +1 -1
  22. package/dist/relations/has-many/has-many-through.helpers.d.ts +3 -0
  23. package/dist/relations/has-many/has-many-through.helpers.js +25 -1
  24. package/dist/relations/has-many/has-many-through.helpers.js.map +1 -1
  25. package/dist/relations/has-many/has-many-through.inclusion-resolver.d.ts +3 -1
  26. package/dist/relations/has-many/has-many-through.inclusion-resolver.js +92 -21
  27. package/dist/relations/has-many/has-many-through.inclusion-resolver.js.map +1 -1
  28. package/dist/relations/has-many/has-many-through.repository-factory.d.ts +3 -1
  29. package/dist/relations/has-many/has-many-through.repository-factory.js +7 -1
  30. package/dist/relations/has-many/has-many-through.repository-factory.js.map +1 -1
  31. package/dist/relations/has-many/has-many-through.repository.d.ts +66 -11
  32. package/dist/relations/has-many/has-many-through.repository.js +211 -35
  33. package/dist/relations/has-many/has-many-through.repository.js.map +1 -1
  34. package/dist/relations/has-one/has-one.helpers.d.ts +3 -0
  35. package/dist/relations/has-one/has-one.helpers.js +34 -8
  36. package/dist/relations/has-one/has-one.helpers.js.map +1 -1
  37. package/dist/relations/has-one/has-one.inclusion-resolver.d.ts +6 -3
  38. package/dist/relations/has-one/has-one.inclusion-resolver.js +82 -7
  39. package/dist/relations/has-one/has-one.inclusion-resolver.js.map +1 -1
  40. package/dist/relations/has-one/has-one.repository-factory.d.ts +9 -3
  41. package/dist/relations/has-one/has-one.repository-factory.js +13 -3
  42. package/dist/relations/has-one/has-one.repository-factory.js.map +1 -1
  43. package/dist/relations/has-one/has-one.repository.d.ts +58 -11
  44. package/dist/relations/has-one/has-one.repository.js +118 -15
  45. package/dist/relations/has-one/has-one.repository.js.map +1 -1
  46. package/dist/relations/index.d.ts +2 -0
  47. package/dist/relations/index.js +2 -0
  48. package/dist/relations/index.js.map +1 -1
  49. package/dist/relations/references-many/index.d.ts +4 -0
  50. package/dist/relations/references-many/index.js +12 -0
  51. package/dist/relations/references-many/index.js.map +1 -0
  52. package/dist/relations/references-many/references-many.accessor.d.ts +17 -0
  53. package/dist/relations/references-many/references-many.accessor.js +40 -0
  54. package/dist/relations/references-many/references-many.accessor.js.map +1 -0
  55. package/dist/relations/references-many/references-many.decorator.d.ts +11 -0
  56. package/dist/relations/references-many/references-many.decorator.js +73 -0
  57. package/dist/relations/references-many/references-many.decorator.js.map +1 -0
  58. package/dist/relations/references-many/references-many.helpers.d.ts +17 -0
  59. package/dist/relations/references-many/references-many.helpers.js +63 -0
  60. package/dist/relations/references-many/references-many.helpers.js.map +1 -0
  61. package/dist/relations/references-many/references-many.inclusion-resolver.d.ts +14 -0
  62. package/dist/relations/references-many/references-many.inclusion-resolver.js +42 -0
  63. package/dist/relations/references-many/references-many.inclusion-resolver.js.map +1 -0
  64. package/dist/relations/references-many/references-many.repository.d.ts +28 -0
  65. package/dist/relations/references-many/references-many.repository.js +33 -0
  66. package/dist/relations/references-many/references-many.repository.js.map +1 -0
  67. package/dist/relations/relation.decorator.d.ts +0 -6
  68. package/dist/relations/relation.decorator.js +1 -13
  69. package/dist/relations/relation.decorator.js.map +1 -1
  70. package/dist/relations/relation.filter.solver.d.ts +2 -0
  71. package/dist/relations/relation.filter.solver.js +57 -0
  72. package/dist/relations/relation.filter.solver.js.map +1 -0
  73. package/dist/relations/relation.helpers.js +16 -1
  74. package/dist/relations/relation.helpers.js.map +1 -1
  75. package/dist/relations/relation.types.d.ts +52 -1
  76. package/dist/relations/relation.types.js.map +1 -1
  77. package/dist/repositories/legacy-juggler-bridge.d.ts +35 -8
  78. package/dist/repositories/legacy-juggler-bridge.js +36 -14
  79. package/dist/repositories/legacy-juggler-bridge.js.map +1 -1
  80. package/package.json +13 -13
  81. package/src/errors/index.ts +1 -0
  82. package/src/errors/invalid-polymorphism.error.ts +28 -0
  83. package/src/model.ts +19 -0
  84. package/src/relations/belongs-to/belongs-to.accessor.ts +35 -6
  85. package/src/relations/belongs-to/belongs-to.helpers.ts +36 -9
  86. package/src/relations/belongs-to/belongs-to.inclusion-resolver.ts +108 -17
  87. package/src/relations/belongs-to/belongs-to.repository.ts +77 -17
  88. package/src/relations/has-many/has-many-through.helpers.ts +27 -1
  89. package/src/relations/has-many/has-many-through.inclusion-resolver.ts +117 -26
  90. package/src/relations/has-many/has-many-through.repository-factory.ts +21 -3
  91. package/src/relations/has-many/has-many-through.repository.ts +343 -76
  92. package/src/relations/has-one/has-one.helpers.ts +40 -13
  93. package/src/relations/has-one/has-one.inclusion-resolver.ts +109 -15
  94. package/src/relations/has-one/has-one.repository-factory.ts +34 -6
  95. package/src/relations/has-one/has-one.repository.ts +188 -35
  96. package/src/relations/index.ts +2 -0
  97. package/src/relations/references-many/index.ts +9 -0
  98. package/src/relations/references-many/references-many.accessor.ts +76 -0
  99. package/src/relations/references-many/references-many.decorator.ts +100 -0
  100. package/src/relations/references-many/references-many.helpers.ts +82 -0
  101. package/src/relations/references-many/references-many.inclusion-resolver.ts +80 -0
  102. package/src/relations/references-many/references-many.repository.ts +55 -0
  103. package/src/relations/relation.decorator.ts +0 -12
  104. package/src/relations/relation.filter.solver.ts +56 -0
  105. package/src/relations/relation.helpers.ts +16 -1
  106. package/src/relations/relation.types.ts +51 -0
  107. package/src/repositories/legacy-juggler-bridge.ts +78 -14
@@ -0,0 +1,100 @@
1
+ // Copyright IBM Corp. 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
+
6
+ import {DecoratorFactory, MetadataInspector} from '@loopback/core';
7
+ import {property} from '../../decorators';
8
+ import {relation} from '../relation.decorator';
9
+ import {Entity, EntityResolver, PropertyDefinition} from '../../model';
10
+ import {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
+ */
20
+ export 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
+ }
@@ -0,0 +1,82 @@
1
+ // Copyright IBM Corp. 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
+
6
+ import debugFactory from 'debug';
7
+ import {camelCase} from 'lodash';
8
+ import {InvalidRelationError} from '../../errors';
9
+ import {isTypeResolver} from '../../type-resolver';
10
+ import {ReferencesManyDefinition, RelationType} from '../relation.types';
11
+
12
+ const debug = debugFactory(
13
+ 'loopback:repository:relations:references-many:helpers',
14
+ );
15
+
16
+ /**
17
+ * Relation definition with optional metadata (e.g. `keyTo`) filled in.
18
+ * @internal
19
+ */
20
+ export type ReferencesManyResolvedDefinition = ReferencesManyDefinition & {
21
+ keyFrom: string;
22
+ keyTo: string;
23
+ };
24
+
25
+ /**
26
+ * Resolves given referencesMany metadata if target is specified to be a resolver.
27
+ * Mainly used to infer what the `keyTo` property should be from the target's
28
+ * property id metadata
29
+ * @param relationMeta - referencesMany metadata to resolve
30
+ * @internal
31
+ */
32
+ export function resolveReferencesManyMetadata(
33
+ relationMeta: ReferencesManyDefinition,
34
+ ) {
35
+ if ((relationMeta.type as RelationType) !== RelationType.referencesMany) {
36
+ const reason = 'relation type must be ReferencesMany';
37
+ throw new InvalidRelationError(reason, relationMeta);
38
+ }
39
+
40
+ if (!isTypeResolver(relationMeta.target)) {
41
+ const reason = 'target must be a type resolver';
42
+ throw new InvalidRelationError(reason, relationMeta);
43
+ }
44
+
45
+ const sourceModel = relationMeta.source;
46
+ if (!sourceModel || !sourceModel.modelName) {
47
+ const reason = 'source model must be defined';
48
+ throw new InvalidRelationError(reason, relationMeta);
49
+ }
50
+
51
+ const targetModel = relationMeta.target();
52
+ const targetName = targetModel.modelName;
53
+ debug('Resolved model %s from given metadata: %o', targetName, targetModel);
54
+
55
+ let keyFrom;
56
+ if (
57
+ relationMeta.keyFrom &&
58
+ relationMeta.source.definition.properties[relationMeta.keyFrom]
59
+ ) {
60
+ keyFrom = relationMeta.keyFrom;
61
+ } else {
62
+ keyFrom = camelCase(targetName + '_ids');
63
+ }
64
+
65
+ const targetProperties = targetModel.definition.properties;
66
+ debug('relation metadata from %o: %o', targetName, targetProperties);
67
+
68
+ if (relationMeta.keyTo && targetProperties[relationMeta.keyTo]) {
69
+ // The explicit cast is needed because of a limitation of type inference
70
+ return Object.assign(relationMeta, {
71
+ keyFrom,
72
+ }) as ReferencesManyResolvedDefinition;
73
+ }
74
+
75
+ const targetPrimaryKey = targetModel.definition.idProperties()[0];
76
+ if (!targetPrimaryKey) {
77
+ const reason = `${targetName} does not have any primary key (id property)`;
78
+ throw new InvalidRelationError(reason, relationMeta);
79
+ }
80
+
81
+ return Object.assign(relationMeta, {keyFrom, keyTo: targetPrimaryKey});
82
+ }
@@ -0,0 +1,80 @@
1
+ // Copyright IBM Corp. 2019,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
+
6
+ import {Filter, InclusionFilter} from '@loopback/filter';
7
+ import {AnyObject, Options} from '../../common-types';
8
+ import {Entity} from '../../model';
9
+ import {EntityCrudRepository} from '../../repositories';
10
+ import {
11
+ deduplicate,
12
+ findByForeignKeys,
13
+ flattenTargetsOfOneToOneRelation,
14
+ StringKeyOf,
15
+ } from '../relation.helpers';
16
+ import {
17
+ ReferencesManyDefinition,
18
+ Getter,
19
+ InclusionResolver,
20
+ } from '../relation.types';
21
+ import {resolveReferencesManyMetadata} from './references-many.helpers';
22
+
23
+ /**
24
+ * Creates InclusionResolver for ReferencesMany relation.
25
+ * Notice that this function only generates the inclusionResolver.
26
+ * It doesn't register it for the source repository.
27
+ *
28
+ * Notice: scope field for inclusion is not supported yet
29
+ *
30
+ * @param meta - resolved ReferencesManyMetadata
31
+ * @param getTargetRepo - target repository i.e where related instances are
32
+ */
33
+ export function createReferencesManyInclusionResolver<
34
+ Target extends Entity,
35
+ TargetIds,
36
+ TargetRelations extends object,
37
+ >(
38
+ meta: ReferencesManyDefinition,
39
+ getTargetRepo: Getter<
40
+ EntityCrudRepository<Target, TargetIds, TargetRelations>
41
+ >,
42
+ ): InclusionResolver<Entity, Target> {
43
+ const relationMeta = resolveReferencesManyMetadata(meta);
44
+
45
+ return async function fetchIncludedModels(
46
+ entities: Entity[],
47
+ inclusion: InclusionFilter,
48
+ options?: Options,
49
+ ): Promise<(Target & TargetRelations)[][]> {
50
+ if (!entities.length) return [];
51
+
52
+ const sourceKey = relationMeta.keyFrom;
53
+ const sourceMap = entities.map(e => (e as AnyObject)[sourceKey]);
54
+ const sourceIds = sourceMap.flat();
55
+ const targetKey = relationMeta.keyTo as StringKeyOf<Target>;
56
+ const dedupedSourceIds = deduplicate(sourceIds);
57
+
58
+ const scope =
59
+ typeof inclusion === 'string' ? {} : (inclusion.scope as Filter<Target>);
60
+
61
+ const targetRepo = await getTargetRepo();
62
+ const targetsFound = await findByForeignKeys(
63
+ targetRepo,
64
+ targetKey,
65
+ dedupedSourceIds.filter(e => e),
66
+ scope,
67
+ options,
68
+ );
69
+
70
+ return sourceMap.map(chainIds => {
71
+ if (!chainIds) return [];
72
+ const targets = flattenTargetsOfOneToOneRelation(
73
+ chainIds,
74
+ targetsFound,
75
+ targetKey,
76
+ );
77
+ return targets.filter((v): v is Target & TargetRelations => v != null);
78
+ });
79
+ };
80
+ }
@@ -0,0 +1,55 @@
1
+ // Copyright IBM Corp. 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
+
6
+ import {Getter} from '@loopback/core';
7
+ import {DataObject, Options} from '../../common-types';
8
+ import {EntityNotFoundError} from '../../errors';
9
+ import {Entity} from '../../model';
10
+ import {constrainFilter, EntityCrudRepository} from '../../repositories';
11
+
12
+ /**
13
+ * CRUD operations for a target repository of a ReferencesMany relation
14
+ */
15
+ export interface ReferencesManyRepository<Target extends Entity> {
16
+ /**
17
+ * Gets the target model instance
18
+ * @param options
19
+ * @returns A promise resolved with the target object or rejected
20
+ * with an EntityNotFoundError when target model instance was not found.
21
+ */
22
+ get(options?: Options): Promise<Target>;
23
+ }
24
+
25
+ export class DefaultReferencesManyRepository<
26
+ TargetEntity extends Entity,
27
+ TargetIds,
28
+ TargetRepository extends EntityCrudRepository<TargetEntity, TargetIds>,
29
+ > implements ReferencesManyRepository<TargetEntity>
30
+ {
31
+ /**
32
+ * Constructor of DefaultReferencesManyEntityCrudRepository
33
+ * @param getTargetRepository - the getter of the related target model repository instance
34
+ * @param constraint - the key value pair representing foreign key name to constrain
35
+ * the target repository instance
36
+ */
37
+ constructor(
38
+ public getTargetRepository: Getter<TargetRepository>,
39
+ public constraint: DataObject<TargetEntity>,
40
+ ) {}
41
+
42
+ async get(options?: Options): Promise<TargetEntity> {
43
+ const targetRepo = await this.getTargetRepository();
44
+ const result = await targetRepo.find(
45
+ constrainFilter(undefined, this.constraint),
46
+ options,
47
+ );
48
+ if (!result.length) {
49
+ // We don't have a direct access to the foreign key value here :(
50
+ const id = 'constraint ' + JSON.stringify(this.constraint);
51
+ throw new EntityNotFoundError(targetRepo.entityClass, id);
52
+ }
53
+ return result[0];
54
+ }
55
+ }
@@ -76,15 +76,3 @@ export function referencesOne(definition?: Object) {
76
76
  decoratorName: '@referencesOne',
77
77
  });
78
78
  }
79
-
80
- /**
81
- * Decorator for referencesMany
82
- * @param definition
83
- * @returns A property decorator
84
- */
85
- export function referencesMany(definition?: Object) {
86
- const rel = Object.assign({type: RelationType.referencesMany}, definition);
87
- return PropertyDecoratorFactory.createDecorator(RELATIONS_KEY, rel, {
88
- decoratorName: '@referencesMany',
89
- });
90
- }
@@ -0,0 +1,56 @@
1
+ import {Fields} from '@loopback/filter';
2
+ import {cloneDeep} from 'lodash';
3
+
4
+ export function includeFieldIfNot<MT>(
5
+ fields: Fields<MT> | undefined,
6
+ fieldToInclude: Extract<keyof MT, string>,
7
+ ): false | Fields<MT> {
8
+ if (!fields) {
9
+ return false;
10
+ } else if (Array.isArray(fields)) {
11
+ const fieldsCloned: {[P in keyof MT]?: boolean} = fields.reduce(
12
+ (prev, current) => ({...prev, [current]: true}),
13
+ {},
14
+ );
15
+ if (Object.keys(fieldsCloned).length > 0) {
16
+ if (fieldsCloned[fieldToInclude] === true) {
17
+ return false;
18
+ }
19
+ fieldsCloned[fieldToInclude] = true;
20
+ return fieldsCloned;
21
+ }
22
+ return false;
23
+ }
24
+
25
+ const fieldsCloned = cloneDeep(fields);
26
+ if (Object.keys(fieldsCloned).length > 0) {
27
+ let containsTrue = false;
28
+ for (const k in fieldsCloned) {
29
+ if (fieldsCloned[k] === true) {
30
+ containsTrue = true;
31
+ }
32
+ }
33
+ for (const k in fieldsCloned) {
34
+ if (k === fieldToInclude) {
35
+ if (fieldsCloned[k] === true) {
36
+ return false;
37
+ } else {
38
+ if (containsTrue) {
39
+ fieldsCloned[k] = true;
40
+ } else {
41
+ delete fieldsCloned[k];
42
+ }
43
+ return fieldsCloned;
44
+ }
45
+ }
46
+ }
47
+ if (containsTrue) {
48
+ fieldsCloned[fieldToInclude] = true;
49
+ return fieldsCloned;
50
+ } else {
51
+ return false;
52
+ }
53
+ } else {
54
+ return false;
55
+ }
56
+ }
@@ -122,7 +122,22 @@ export async function includeRelatedModels<
122
122
  options?: Options,
123
123
  ): Promise<(T & Relations)[]> {
124
124
  entities = cloneDeep(entities);
125
- include = cloneDeep(include);
125
+ if (options?.polymorphicType) {
126
+ include = include?.filter(inclusionFilter => {
127
+ if (typeof inclusionFilter === 'string') {
128
+ return true;
129
+ } else {
130
+ if (
131
+ inclusionFilter.targetType === undefined ||
132
+ inclusionFilter.targetType === options?.polymorphicType
133
+ ) {
134
+ return true;
135
+ }
136
+ }
137
+ });
138
+ } else {
139
+ include = cloneDeep(include);
140
+ }
126
141
  const result = entities as (T & Relations)[];
127
142
  if (!include) return result;
128
143
 
@@ -74,6 +74,12 @@ export interface HasManyDefinition extends RelationDefinitionBase {
74
74
  keyTo?: string;
75
75
  keyFrom?: string;
76
76
 
77
+ /**
78
+ * With current architecture design, polymorphic type cannot be supported without through
79
+ * Consider using Source-hasMany->Through->hasOne->Target(polymorphic) for one-to-many relations
80
+ */
81
+ // polymorphic?: boolean | {discriminator: string};
82
+
77
83
  /**
78
84
  * Description of the through model of the hasManyThrough relation.
79
85
  *
@@ -107,6 +113,19 @@ export interface HasManyDefinition extends RelationDefinitionBase {
107
113
  * The foreign key of the target model defined in the through model, e.g. CategoryProductLink#productId
108
114
  */
109
115
  keyTo?: string;
116
+
117
+ /**
118
+ * The polymorphism of the target model. The discriminator is a key of *through* model.
119
+ * If the target model is not polymorphic, then the value should be left undefined or false;
120
+ * If the key on through model indicating the concrete class of the through instance is default
121
+ * i.e. camelCase(classNameOf(targetModelInstance)) + "Id"
122
+ * then the discriminator field can be undefined
123
+ *
124
+ * With current architecture design, polymorphic type cannot be supported without through
125
+ * Consider using Source hasMany Through hasOne Target(polymorphic)
126
+ * or Source hasMany Through belongsTo Target(polymorphic) for one-to-many relations
127
+ */
128
+ polymorphic?: boolean | {discriminator: string};
110
129
  };
111
130
  }
112
131
 
@@ -123,6 +142,14 @@ export interface BelongsToDefinition extends RelationDefinitionBase {
123
142
  * The primary key of the target model, e.g Customer#id.
124
143
  */
125
144
  keyTo?: string;
145
+ /**
146
+ * The polymorphism of the target model. The discriminator is a key of source model.
147
+ * If the target model is not polymorphic, then the value should be left undefined or false;
148
+ * If the key on source model indicating the concrete class of the target instance is default
149
+ * i.e. camelCase(classNameOf(throughModelInstance)) + "Id"
150
+ * Then the discriminator field can be undefined
151
+ */
152
+ polymorphic?: boolean | {discriminator: string};
126
153
  }
127
154
 
128
155
  export interface HasOneDefinition extends RelationDefinitionBase {
@@ -141,6 +168,29 @@ export interface HasOneDefinition extends RelationDefinitionBase {
141
168
  */
142
169
  keyTo?: string;
143
170
  keyFrom?: string;
171
+ /**
172
+ * The polymorphism of the target model. The discriminator is a key of source model.
173
+ * If the target model is not polymorphic, then the value should be left undefined or false;
174
+ * If the key on source model indicating the concrete class of the target instance is default
175
+ * i.e. camelCase(classNameOf(throughModelInstance)) + "Id"
176
+ * Then the discriminator field can be undefined
177
+ */
178
+ polymorphic?: boolean | {discriminator: string};
179
+ }
180
+
181
+ export interface ReferencesManyDefinition extends RelationDefinitionBase {
182
+ type: RelationType.referencesMany;
183
+ targetsMany: true;
184
+
185
+ /**
186
+ * keyTo: The foreign key used by the target model for this relation.
187
+ * keyFrom: The source key used by the source model for this relation.
188
+ *
189
+ * TODO(bajtos) Add relation description.
190
+ *
191
+ */
192
+ keyTo?: string;
193
+ keyFrom?: string;
144
194
  }
145
195
 
146
196
  /**
@@ -150,6 +200,7 @@ export type RelationMetadata =
150
200
  | HasManyDefinition
151
201
  | BelongsToDefinition
152
202
  | HasOneDefinition
203
+ | ReferencesManyDefinition
153
204
  // TODO(bajtos) add other relation types and remove RelationDefinitionBase once
154
205
  // all relation types are covered.
155
206
  | RelationDefinitionBase;
@@ -36,6 +36,7 @@ import {
36
36
  createHasManyRepositoryFactory,
37
37
  createHasManyThroughRepositoryFactory,
38
38
  createHasOneRepositoryFactory,
39
+ createReferencesManyAccessor,
39
40
  HasManyDefinition,
40
41
  HasManyRepositoryFactory,
41
42
  HasManyThroughRepositoryFactory,
@@ -43,6 +44,8 @@ import {
43
44
  HasOneRepositoryFactory,
44
45
  includeRelatedModels,
45
46
  InclusionResolver,
47
+ ReferencesManyAccessor,
48
+ ReferencesManyDefinition,
46
49
  } from '../relations';
47
50
  import {IsolationLevel, Transaction} from '../transaction';
48
51
  import {isTypeResolver, resolveType} from '../type-resolver';
@@ -240,11 +243,11 @@ export class DefaultCrudRepository<
240
243
  ForeignKeyType,
241
244
  >(
242
245
  relationName: string,
243
- targetRepoGetter: Getter<EntityCrudRepository<Target, TargetID>>,
246
+ targetRepositoryGetter: Getter<EntityCrudRepository<Target, TargetID>>,
244
247
  ): HasManyRepositoryFactory<Target, ForeignKeyType> {
245
248
  return this.createHasManyRepositoryFactoryFor(
246
249
  relationName,
247
- targetRepoGetter,
250
+ targetRepositoryGetter,
248
251
  );
249
252
  }
250
253
 
@@ -282,12 +285,12 @@ export class DefaultCrudRepository<
282
285
  ForeignKeyType,
283
286
  >(
284
287
  relationName: string,
285
- targetRepoGetter: Getter<EntityCrudRepository<Target, TargetID>>,
288
+ targetRepositoryGetter: Getter<EntityCrudRepository<Target, TargetID>>,
286
289
  ): HasManyRepositoryFactory<Target, ForeignKeyType> {
287
290
  const meta = this.entityClass.definition.relations[relationName];
288
291
  return createHasManyRepositoryFactory<Target, TargetID, ForeignKeyType>(
289
292
  meta as HasManyDefinition,
290
- targetRepoGetter,
293
+ targetRepositoryGetter,
291
294
  );
292
295
  }
293
296
 
@@ -329,8 +332,12 @@ export class DefaultCrudRepository<
329
332
  ForeignKeyType,
330
333
  >(
331
334
  relationName: string,
332
- targetRepoGetter: Getter<EntityCrudRepository<Target, TargetID>>,
333
- throughRepoGetter: Getter<EntityCrudRepository<Through, ThroughID>>,
335
+ targetRepositoryGetter:
336
+ | Getter<EntityCrudRepository<Target, TargetID>>
337
+ | {
338
+ [repoType: string]: Getter<EntityCrudRepository<Target, TargetID>>;
339
+ },
340
+ throughRepositoryGetter: Getter<EntityCrudRepository<Through, ThroughID>>,
334
341
  ): HasManyThroughRepositoryFactory<
335
342
  Target,
336
343
  TargetID,
@@ -344,7 +351,11 @@ export class DefaultCrudRepository<
344
351
  Through,
345
352
  ThroughID,
346
353
  ForeignKeyType
347
- >(meta as HasManyDefinition, targetRepoGetter, throughRepoGetter);
354
+ >(
355
+ meta as HasManyDefinition,
356
+ targetRepositoryGetter,
357
+ throughRepositoryGetter,
358
+ );
348
359
  }
349
360
 
350
361
  /**
@@ -358,9 +369,16 @@ export class DefaultCrudRepository<
358
369
  */
359
370
  protected _createBelongsToAccessorFor<Target extends Entity, TargetId>(
360
371
  relationName: string,
361
- targetRepoGetter: Getter<EntityCrudRepository<Target, TargetId>>,
372
+ targetRepositoryGetter:
373
+ | Getter<EntityCrudRepository<Target, TargetId>>
374
+ | {
375
+ [repoType: string]: Getter<EntityCrudRepository<Target, TargetId>>;
376
+ },
362
377
  ): BelongsToAccessor<Target, ID> {
363
- return this.createBelongsToAccessorFor(relationName, targetRepoGetter);
378
+ return this.createBelongsToAccessorFor(
379
+ relationName,
380
+ targetRepositoryGetter,
381
+ );
364
382
  }
365
383
 
366
384
  /**
@@ -371,12 +389,16 @@ export class DefaultCrudRepository<
371
389
  */
372
390
  protected createBelongsToAccessorFor<Target extends Entity, TargetId>(
373
391
  relationName: string,
374
- targetRepoGetter: Getter<EntityCrudRepository<Target, TargetId>>,
392
+ targetRepositoryGetter:
393
+ | Getter<EntityCrudRepository<Target, TargetId>>
394
+ | {
395
+ [repoType: string]: Getter<EntityCrudRepository<Target, TargetId>>;
396
+ },
375
397
  ): BelongsToAccessor<Target, ID> {
376
398
  const meta = this.entityClass.definition.relations[relationName];
377
399
  return createBelongsToAccessor<Target, TargetId, T, ID>(
378
400
  meta as BelongsToDefinition,
379
- targetRepoGetter,
401
+ targetRepositoryGetter,
380
402
  this,
381
403
  );
382
404
  }
@@ -394,11 +416,15 @@ export class DefaultCrudRepository<
394
416
  ForeignKeyType,
395
417
  >(
396
418
  relationName: string,
397
- targetRepoGetter: Getter<EntityCrudRepository<Target, TargetID>>,
419
+ targetRepositoryGetter:
420
+ | Getter<EntityCrudRepository<Target, TargetID>>
421
+ | {
422
+ [repoType: string]: Getter<EntityCrudRepository<Target, TargetID>>;
423
+ },
398
424
  ): HasOneRepositoryFactory<Target, ForeignKeyType> {
399
425
  return this.createHasOneRepositoryFactoryFor(
400
426
  relationName,
401
- targetRepoGetter,
427
+ targetRepositoryGetter,
402
428
  );
403
429
  }
404
430
 
@@ -414,12 +440,50 @@ export class DefaultCrudRepository<
414
440
  ForeignKeyType,
415
441
  >(
416
442
  relationName: string,
417
- targetRepoGetter: Getter<EntityCrudRepository<Target, TargetID>>,
443
+ targetRepositoryGetter:
444
+ | Getter<EntityCrudRepository<Target, TargetID>>
445
+ | {
446
+ [repoType: string]: Getter<EntityCrudRepository<Target, TargetID>>;
447
+ },
418
448
  ): HasOneRepositoryFactory<Target, ForeignKeyType> {
419
449
  const meta = this.entityClass.definition.relations[relationName];
420
450
  return createHasOneRepositoryFactory<Target, TargetID, ForeignKeyType>(
421
451
  meta as HasOneDefinition,
452
+ targetRepositoryGetter,
453
+ );
454
+ }
455
+
456
+ /**
457
+ * @deprecated
458
+ * Function to create a references many accessor
459
+ *
460
+ * Use `this.createReferencesManyAccessorFor()` instead
461
+ *
462
+ * @param relationName - Name of the relation defined on the source model
463
+ * @param targetRepo - Target repository instance
464
+ */
465
+ protected _createReferencesManyAccessorFor<Target extends Entity, TargetId>(
466
+ relationName: string,
467
+ targetRepoGetter: Getter<EntityCrudRepository<Target, TargetId>>,
468
+ ): ReferencesManyAccessor<Target, ID> {
469
+ return this.createReferencesManyAccessorFor(relationName, targetRepoGetter);
470
+ }
471
+
472
+ /**
473
+ * Function to create a references many accessor
474
+ *
475
+ * @param relationName - Name of the relation defined on the source model
476
+ * @param targetRepo - Target repository instance
477
+ */
478
+ protected createReferencesManyAccessorFor<Target extends Entity, TargetId>(
479
+ relationName: string,
480
+ targetRepoGetter: Getter<EntityCrudRepository<Target, TargetId>>,
481
+ ): ReferencesManyAccessor<Target, ID> {
482
+ const meta = this.entityClass.definition.relations[relationName];
483
+ return createReferencesManyAccessor<Target, TargetId, T, ID>(
484
+ meta as ReferencesManyDefinition,
422
485
  targetRepoGetter,
486
+ this,
423
487
  );
424
488
  }
425
489