@loopback/repository 4.1.2 → 5.0.2

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 (250) hide show
  1. package/LICENSE +1 -1
  2. package/dist/connectors/connector.js +1 -1
  3. package/dist/connectors/connector.js.map +1 -1
  4. package/dist/connectors/crud.connector.js +1 -1
  5. package/dist/connectors/crud.connector.js.map +1 -1
  6. package/dist/connectors/index.js +1 -1
  7. package/dist/connectors/index.js.map +1 -1
  8. package/dist/connectors/kv.connector.js +1 -1
  9. package/dist/connectors/kv.connector.js.map +1 -1
  10. package/dist/datasource.js +1 -1
  11. package/dist/datasource.js.map +1 -1
  12. package/dist/decorators/index.js +1 -1
  13. package/dist/decorators/index.js.map +1 -1
  14. package/dist/decorators/metadata.js +1 -1
  15. package/dist/decorators/metadata.js.map +1 -1
  16. package/dist/decorators/model.decorator.js +1 -1
  17. package/dist/decorators/model.decorator.js.map +1 -1
  18. package/dist/decorators/repository.decorator.js +1 -1
  19. package/dist/decorators/repository.decorator.js.map +1 -1
  20. package/dist/define-model-class.js +1 -1
  21. package/dist/define-model-class.js.map +1 -1
  22. package/dist/define-repository-class.js +1 -1
  23. package/dist/define-repository-class.js.map +1 -1
  24. package/dist/errors/entity-not-found.error.js +1 -1
  25. package/dist/errors/entity-not-found.error.js.map +1 -1
  26. package/dist/errors/index.d.ts +1 -0
  27. package/dist/errors/index.js +2 -1
  28. package/dist/errors/index.js.map +1 -1
  29. package/dist/errors/invalid-polymorphism.error.d.ts +5 -0
  30. package/dist/errors/invalid-polymorphism.error.js +22 -0
  31. package/dist/errors/invalid-polymorphism.error.js.map +1 -0
  32. package/dist/errors/invalid-relation.error.d.ts +1 -1
  33. package/dist/errors/invalid-relation.error.js +1 -1
  34. package/dist/errors/invalid-relation.error.js.map +1 -1
  35. package/dist/index.js +1 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/mixins/index.js +1 -1
  38. package/dist/mixins/index.js.map +1 -1
  39. package/dist/mixins/repository.mixin.d.ts +49 -50
  40. package/dist/mixins/repository.mixin.js +1 -1
  41. package/dist/mixins/repository.mixin.js.map +1 -1
  42. package/dist/model.d.ts +7 -1
  43. package/dist/model.js +15 -1
  44. package/dist/model.js.map +1 -1
  45. package/dist/relations/belongs-to/belongs-to.accessor.d.ts +6 -2
  46. package/dist/relations/belongs-to/belongs-to.accessor.js +19 -6
  47. package/dist/relations/belongs-to/belongs-to.accessor.js.map +1 -1
  48. package/dist/relations/belongs-to/belongs-to.decorator.js +1 -1
  49. package/dist/relations/belongs-to/belongs-to.decorator.js.map +1 -1
  50. package/dist/relations/belongs-to/belongs-to.helpers.d.ts +3 -0
  51. package/dist/relations/belongs-to/belongs-to.helpers.js +33 -9
  52. package/dist/relations/belongs-to/belongs-to.helpers.js.map +1 -1
  53. package/dist/relations/belongs-to/belongs-to.inclusion-resolver.d.ts +5 -2
  54. package/dist/relations/belongs-to/belongs-to.inclusion-resolver.js +82 -9
  55. package/dist/relations/belongs-to/belongs-to.inclusion-resolver.js.map +1 -1
  56. package/dist/relations/belongs-to/belongs-to.repository.d.ts +28 -5
  57. package/dist/relations/belongs-to/belongs-to.repository.js +50 -11
  58. package/dist/relations/belongs-to/belongs-to.repository.js.map +1 -1
  59. package/dist/relations/belongs-to/index.d.ts +2 -2
  60. package/dist/relations/belongs-to/index.js +3 -3
  61. package/dist/relations/belongs-to/index.js.map +1 -1
  62. package/dist/relations/has-many/has-many-through.helpers.d.ts +3 -0
  63. package/dist/relations/has-many/has-many-through.helpers.js +26 -2
  64. package/dist/relations/has-many/has-many-through.helpers.js.map +1 -1
  65. package/dist/relations/has-many/has-many-through.inclusion-resolver.d.ts +3 -1
  66. package/dist/relations/has-many/has-many-through.inclusion-resolver.js +93 -22
  67. package/dist/relations/has-many/has-many-through.inclusion-resolver.js.map +1 -1
  68. package/dist/relations/has-many/has-many-through.repository-factory.d.ts +3 -1
  69. package/dist/relations/has-many/has-many-through.repository-factory.js +8 -2
  70. package/dist/relations/has-many/has-many-through.repository-factory.js.map +1 -1
  71. package/dist/relations/has-many/has-many-through.repository.d.ts +66 -11
  72. package/dist/relations/has-many/has-many-through.repository.js +212 -36
  73. package/dist/relations/has-many/has-many-through.repository.js.map +1 -1
  74. package/dist/relations/has-many/has-many.decorator.js +1 -1
  75. package/dist/relations/has-many/has-many.decorator.js.map +1 -1
  76. package/dist/relations/has-many/has-many.helpers.js +1 -1
  77. package/dist/relations/has-many/has-many.helpers.js.map +1 -1
  78. package/dist/relations/has-many/has-many.inclusion-resolver.js +1 -1
  79. package/dist/relations/has-many/has-many.inclusion-resolver.js.map +1 -1
  80. package/dist/relations/has-many/has-many.repository-factory.js +1 -1
  81. package/dist/relations/has-many/has-many.repository-factory.js.map +1 -1
  82. package/dist/relations/has-many/has-many.repository.js +1 -1
  83. package/dist/relations/has-many/has-many.repository.js.map +1 -1
  84. package/dist/relations/has-many/index.d.ts +2 -2
  85. package/dist/relations/has-many/index.js +3 -3
  86. package/dist/relations/has-many/index.js.map +1 -1
  87. package/dist/relations/has-one/has-one.decorator.js +1 -1
  88. package/dist/relations/has-one/has-one.decorator.js.map +1 -1
  89. package/dist/relations/has-one/has-one.helpers.d.ts +3 -0
  90. package/dist/relations/has-one/has-one.helpers.js +35 -9
  91. package/dist/relations/has-one/has-one.helpers.js.map +1 -1
  92. package/dist/relations/has-one/has-one.inclusion-resolver.d.ts +6 -3
  93. package/dist/relations/has-one/has-one.inclusion-resolver.js +83 -8
  94. package/dist/relations/has-one/has-one.inclusion-resolver.js.map +1 -1
  95. package/dist/relations/has-one/has-one.repository-factory.d.ts +9 -3
  96. package/dist/relations/has-one/has-one.repository-factory.js +14 -4
  97. package/dist/relations/has-one/has-one.repository-factory.js.map +1 -1
  98. package/dist/relations/has-one/has-one.repository.d.ts +58 -11
  99. package/dist/relations/has-one/has-one.repository.js +119 -16
  100. package/dist/relations/has-one/has-one.repository.js.map +1 -1
  101. package/dist/relations/has-one/index.js +1 -1
  102. package/dist/relations/has-one/index.js.map +1 -1
  103. package/dist/relations/index.d.ts +2 -0
  104. package/dist/relations/index.js +3 -1
  105. package/dist/relations/index.js.map +1 -1
  106. package/dist/relations/references-many/index.d.ts +4 -0
  107. package/dist/relations/references-many/index.js +12 -0
  108. package/dist/relations/references-many/index.js.map +1 -0
  109. package/dist/relations/references-many/references-many.accessor.d.ts +17 -0
  110. package/dist/relations/references-many/references-many.accessor.js +40 -0
  111. package/dist/relations/references-many/references-many.accessor.js.map +1 -0
  112. package/dist/relations/references-many/references-many.decorator.d.ts +11 -0
  113. package/dist/relations/references-many/references-many.decorator.js +73 -0
  114. package/dist/relations/references-many/references-many.decorator.js.map +1 -0
  115. package/dist/relations/references-many/references-many.helpers.d.ts +17 -0
  116. package/dist/relations/references-many/references-many.helpers.js +63 -0
  117. package/dist/relations/references-many/references-many.helpers.js.map +1 -0
  118. package/dist/relations/references-many/references-many.inclusion-resolver.d.ts +14 -0
  119. package/dist/relations/references-many/references-many.inclusion-resolver.js +42 -0
  120. package/dist/relations/references-many/references-many.inclusion-resolver.js.map +1 -0
  121. package/dist/relations/references-many/references-many.repository.d.ts +28 -0
  122. package/dist/relations/references-many/references-many.repository.js +33 -0
  123. package/dist/relations/references-many/references-many.repository.js.map +1 -0
  124. package/dist/relations/relation.decorator.d.ts +0 -6
  125. package/dist/relations/relation.decorator.js +2 -14
  126. package/dist/relations/relation.decorator.js.map +1 -1
  127. package/dist/relations/relation.filter.solver.d.ts +2 -0
  128. package/dist/relations/relation.filter.solver.js +57 -0
  129. package/dist/relations/relation.filter.solver.js.map +1 -0
  130. package/dist/relations/relation.helpers.js +18 -3
  131. package/dist/relations/relation.helpers.js.map +1 -1
  132. package/dist/relations/relation.types.d.ts +52 -1
  133. package/dist/relations/relation.types.js +1 -1
  134. package/dist/relations/relation.types.js.map +1 -1
  135. package/dist/repositories/constraint-utils.js +1 -1
  136. package/dist/repositories/constraint-utils.js.map +1 -1
  137. package/dist/repositories/index.d.ts +2 -2
  138. package/dist/repositories/index.js +3 -3
  139. package/dist/repositories/index.js.map +1 -1
  140. package/dist/repositories/kv.repository.bridge.js +1 -1
  141. package/dist/repositories/kv.repository.bridge.js.map +1 -1
  142. package/dist/repositories/kv.repository.d.ts +2 -2
  143. package/dist/repositories/kv.repository.js +1 -1
  144. package/dist/repositories/kv.repository.js.map +1 -1
  145. package/dist/repositories/legacy-juggler-bridge.d.ts +36 -9
  146. package/dist/repositories/legacy-juggler-bridge.js +38 -16
  147. package/dist/repositories/legacy-juggler-bridge.js.map +1 -1
  148. package/dist/repositories/repository.js +1 -1
  149. package/dist/repositories/repository.js.map +1 -1
  150. package/dist/type-resolver.js +1 -1
  151. package/dist/type-resolver.js.map +1 -1
  152. package/dist/types/any.js +1 -1
  153. package/dist/types/any.js.map +1 -1
  154. package/dist/types/array.js +1 -1
  155. package/dist/types/array.js.map +1 -1
  156. package/dist/types/boolean.js +1 -1
  157. package/dist/types/boolean.js.map +1 -1
  158. package/dist/types/buffer.js +1 -1
  159. package/dist/types/buffer.js.map +1 -1
  160. package/dist/types/date.js +1 -1
  161. package/dist/types/date.js.map +1 -1
  162. package/dist/types/index.js +1 -1
  163. package/dist/types/index.js.map +1 -1
  164. package/dist/types/model.js +1 -1
  165. package/dist/types/model.js.map +1 -1
  166. package/dist/types/null.js +1 -1
  167. package/dist/types/null.js.map +1 -1
  168. package/dist/types/number.js +1 -1
  169. package/dist/types/number.js.map +1 -1
  170. package/dist/types/object.js +1 -1
  171. package/dist/types/object.js.map +1 -1
  172. package/dist/types/string.js +1 -1
  173. package/dist/types/string.js.map +1 -1
  174. package/dist/types/type.js +1 -1
  175. package/dist/types/type.js.map +1 -1
  176. package/dist/types/union.js +1 -1
  177. package/dist/types/union.js.map +1 -1
  178. package/package.json +15 -15
  179. package/src/connectors/connector.ts +1 -1
  180. package/src/connectors/crud.connector.ts +1 -1
  181. package/src/connectors/index.ts +1 -1
  182. package/src/connectors/kv.connector.ts +1 -1
  183. package/src/datasource.ts +1 -1
  184. package/src/decorators/index.ts +1 -1
  185. package/src/decorators/metadata.ts +1 -1
  186. package/src/decorators/model.decorator.ts +1 -1
  187. package/src/decorators/repository.decorator.ts +1 -1
  188. package/src/define-model-class.ts +1 -1
  189. package/src/define-repository-class.ts +1 -1
  190. package/src/errors/entity-not-found.error.ts +1 -1
  191. package/src/errors/index.ts +2 -1
  192. package/src/errors/invalid-polymorphism.error.ts +28 -0
  193. package/src/errors/invalid-relation.error.ts +2 -2
  194. package/src/index.ts +1 -1
  195. package/src/mixins/index.ts +1 -1
  196. package/src/mixins/repository.mixin.ts +1 -6
  197. package/src/model.ts +20 -1
  198. package/src/relations/belongs-to/belongs-to.accessor.ts +36 -7
  199. package/src/relations/belongs-to/belongs-to.decorator.ts +2 -2
  200. package/src/relations/belongs-to/belongs-to.helpers.ts +37 -10
  201. package/src/relations/belongs-to/belongs-to.inclusion-resolver.ts +109 -18
  202. package/src/relations/belongs-to/belongs-to.repository.ts +78 -18
  203. package/src/relations/belongs-to/index.ts +3 -3
  204. package/src/relations/has-many/has-many-through.helpers.ts +28 -2
  205. package/src/relations/has-many/has-many-through.inclusion-resolver.ts +118 -27
  206. package/src/relations/has-many/has-many-through.repository-factory.ts +22 -4
  207. package/src/relations/has-many/has-many-through.repository.ts +344 -77
  208. package/src/relations/has-many/has-many.decorator.ts +1 -1
  209. package/src/relations/has-many/has-many.helpers.ts +1 -1
  210. package/src/relations/has-many/has-many.inclusion-resolver.ts +1 -1
  211. package/src/relations/has-many/has-many.repository-factory.ts +1 -1
  212. package/src/relations/has-many/has-many.repository.ts +1 -1
  213. package/src/relations/has-many/index.ts +3 -3
  214. package/src/relations/has-one/has-one.decorator.ts +1 -1
  215. package/src/relations/has-one/has-one.helpers.ts +41 -14
  216. package/src/relations/has-one/has-one.inclusion-resolver.ts +110 -16
  217. package/src/relations/has-one/has-one.repository-factory.ts +35 -7
  218. package/src/relations/has-one/has-one.repository.ts +189 -36
  219. package/src/relations/has-one/index.ts +1 -1
  220. package/src/relations/index.ts +3 -1
  221. package/src/relations/references-many/index.ts +9 -0
  222. package/src/relations/references-many/references-many.accessor.ts +76 -0
  223. package/src/relations/references-many/references-many.decorator.ts +100 -0
  224. package/src/relations/references-many/references-many.helpers.ts +82 -0
  225. package/src/relations/references-many/references-many.inclusion-resolver.ts +80 -0
  226. package/src/relations/references-many/references-many.repository.ts +55 -0
  227. package/src/relations/relation.decorator.ts +1 -13
  228. package/src/relations/relation.filter.solver.ts +56 -0
  229. package/src/relations/relation.helpers.ts +18 -3
  230. package/src/relations/relation.types.ts +52 -1
  231. package/src/repositories/constraint-utils.ts +1 -1
  232. package/src/repositories/index.ts +3 -3
  233. package/src/repositories/kv.repository.bridge.ts +1 -1
  234. package/src/repositories/kv.repository.ts +3 -3
  235. package/src/repositories/legacy-juggler-bridge.ts +80 -16
  236. package/src/repositories/repository.ts +1 -1
  237. package/src/type-resolver.ts +1 -1
  238. package/src/types/any.ts +1 -1
  239. package/src/types/array.ts +1 -1
  240. package/src/types/boolean.ts +1 -1
  241. package/src/types/buffer.ts +1 -1
  242. package/src/types/date.ts +1 -1
  243. package/src/types/index.ts +1 -1
  244. package/src/types/model.ts +1 -1
  245. package/src/types/null.ts +1 -1
  246. package/src/types/number.ts +1 -1
  247. package/src/types/object.ts +1 -1
  248. package/src/types/string.ts +1 -1
  249. package/src/types/type.ts +1 -1
  250. package/src/types/union.ts +1 -1
@@ -1,14 +1,14 @@
1
- // Copyright IBM Corp. 2018,2020. All Rights Reserved.
1
+ // Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
2
2
  // Node module: @loopback/repository
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
6
  import {Getter} from '@loopback/core';
7
+ import {cloneDeep} from 'lodash';
8
+ import {EntityNotFoundError, TypeResolver} from '../../';
7
9
  import {DataObject, Options} from '../../common-types';
8
- import {EntityNotFoundError} from '../../errors';
9
10
  import {Entity} from '../../model';
10
11
  import {constrainFilter, EntityCrudRepository} from '../../repositories';
11
-
12
12
  /**
13
13
  * CRUD operations for a target repository of a BelongsTo relation
14
14
  */
@@ -16,10 +16,16 @@ export interface BelongsToRepository<Target extends Entity> {
16
16
  /**
17
17
  * Gets the target model instance
18
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
19
23
  * @returns A promise resolved with the target object or rejected
20
24
  * with an EntityNotFoundError when target model instance was not found.
21
25
  */
22
- get(options?: Options): Promise<Target>;
26
+ get(
27
+ options?: Options & {polymorphicType?: string | string[]},
28
+ ): Promise<Target>;
23
29
  }
24
30
 
25
31
  export class DefaultBelongsToRepository<
@@ -30,26 +36,80 @@ export class DefaultBelongsToRepository<
30
36
  {
31
37
  /**
32
38
  * Constructor of DefaultBelongsToEntityCrudRepository
33
- * @param getTargetRepository - the getter of the related target model repository instance
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}"
34
44
  * @param constraint - the key value pair representing foreign key name to constrain
35
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"
36
48
  */
49
+
37
50
  constructor(
38
- public getTargetRepository: Getter<TargetRepository>,
51
+ public getTargetRepository:
52
+ | Getter<TargetRepository>
53
+ | {
54
+ [repoType: string]: Getter<TargetRepository>;
55
+ },
39
56
  public constraint: DataObject<TargetEntity>,
40
- ) {}
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
+ };
41
74
 
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);
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
+ }
52
110
  }
53
- return result[0];
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);
54
114
  }
55
115
  }
@@ -1,9 +1,9 @@
1
- // Copyright IBM Corp. 2018,2020. All Rights Reserved.
1
+ // Copyright IBM Corp. and LoopBack contributors 2018,2020. All Rights Reserved.
2
2
  // Node module: @loopback/repository
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
- export * from './belongs-to.decorator';
7
- export * from './belongs-to.repository';
8
6
  export * from './belongs-to.accessor';
7
+ export * from './belongs-to.decorator';
9
8
  export * from './belongs-to.inclusion-resolver';
9
+ export * from './belongs-to.repository';
@@ -1,4 +1,4 @@
1
- // Copyright IBM Corp. 2020. All Rights Reserved.
1
+ // Copyright IBM Corp. and LoopBack contributors 2020. All Rights Reserved.
2
2
  // Node module: @loopback/repository
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
@@ -25,6 +25,7 @@ export type HasManyThroughResolvedDefinition = HasManyDefinition & {
25
25
  through: {
26
26
  keyTo: string;
27
27
  keyFrom: string;
28
+ polymorphic: false | {discriminator: string};
28
29
  };
29
30
  };
30
31
 
@@ -308,7 +309,10 @@ export function resolveHasManyThroughMetadata(
308
309
  relationMeta.through.keyFrom &&
309
310
  throughModelProperties[relationMeta.through.keyFrom] &&
310
311
  relationMeta.keyTo &&
311
- targetModelProperties[relationMeta.keyTo]
312
+ targetModelProperties[relationMeta.keyTo] &&
313
+ (relationMeta.through.polymorphic === false ||
314
+ (typeof relationMeta.through.polymorphic === 'object' &&
315
+ relationMeta.through.polymorphic.discriminator.length > 0))
312
316
  ) {
313
317
  // The explicit cast is needed because of a limitation of type inference
314
318
  return relationMeta as HasManyThroughResolvedDefinition;
@@ -349,6 +353,27 @@ export function resolveHasManyThroughMetadata(
349
353
  throw new InvalidRelationError(reason, relationMeta);
350
354
  }
351
355
 
356
+ let throughPolymorphic: false | {discriminator: string};
357
+ if (
358
+ relationMeta.through.polymorphic === undefined ||
359
+ relationMeta.through.polymorphic === false ||
360
+ !relationMeta.through.polymorphic
361
+ ) {
362
+ const polymorphicFalse = false as const;
363
+ throughPolymorphic = polymorphicFalse;
364
+ } else {
365
+ if (relationMeta.through.polymorphic === true) {
366
+ const polymorphicObject: {discriminator: string} = {
367
+ discriminator: camelCase(relationMeta.target().name + '_type'),
368
+ };
369
+ throughPolymorphic = polymorphicObject;
370
+ } else {
371
+ const polymorphicObject: {discriminator: string} = relationMeta.through
372
+ .polymorphic as {discriminator: string};
373
+ throughPolymorphic = polymorphicObject;
374
+ }
375
+ }
376
+
352
377
  return Object.assign(relationMeta, {
353
378
  keyTo: targetPrimaryKey,
354
379
  keyFrom: relationMeta.keyFrom!,
@@ -356,6 +381,7 @@ export function resolveHasManyThroughMetadata(
356
381
  ...relationMeta.through,
357
382
  keyTo: targetFkName,
358
383
  keyFrom: sourceFkName,
384
+ polymorphic: throughPolymorphic,
359
385
  },
360
386
  });
361
387
  }
@@ -1,10 +1,11 @@
1
- // Copyright IBM Corp. 2020. All Rights Reserved.
1
+ // Copyright IBM Corp. and LoopBack contributors 2020. All Rights Reserved.
2
2
  // Node module: @loopback/repository
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
6
  import {Filter, InclusionFilter} from '@loopback/filter';
7
7
  import debugFactory from 'debug';
8
+ import {InvalidPolymorphismError} from '../..';
8
9
  import {AnyObject, Options} from '../../common-types';
9
10
  import {Entity} from '../../model';
10
11
  import {EntityCrudRepository} from '../../repositories';
@@ -14,7 +15,7 @@ import {
14
15
  StringKeyOf,
15
16
  } from '../relation.helpers';
16
17
  import {Getter, HasManyDefinition, InclusionResolver} from '../relation.types';
17
- import {resolveHasManyMetadata} from './has-many.helpers';
18
+ import {resolveHasManyThroughMetadata} from './has-many-through.helpers';
18
19
 
19
20
  const debug = debugFactory(
20
21
  'loopback:repository:relations:has-many-through:inclusion-resolver',
@@ -44,17 +45,25 @@ export function createHasManyThroughInclusionResolver<
44
45
  getThroughRepo: Getter<
45
46
  EntityCrudRepository<Through, ThroughID, ThroughRelations>
46
47
  >,
47
- getTargetRepo: Getter<
48
- EntityCrudRepository<Target, TargetID, TargetRelations>
49
- >,
48
+ getTargetRepoDict: {
49
+ [repoType: string]: Getter<
50
+ EntityCrudRepository<Target, TargetID, TargetRelations>
51
+ >;
52
+ },
50
53
  ): InclusionResolver<Entity, Target> {
51
- const relationMeta = resolveHasManyMetadata(meta);
54
+ const relationMeta = resolveHasManyThroughMetadata(meta);
52
55
 
53
56
  return async function fetchHasManyThroughModels(
54
57
  entities: Entity[],
55
58
  inclusion: InclusionFilter,
56
59
  options?: Options,
57
60
  ): Promise<((Target & TargetRelations)[] | undefined)[]> {
61
+ if (!relationMeta.through) {
62
+ throw new Error(
63
+ `relationMeta.through must be defined on ${relationMeta}`,
64
+ );
65
+ }
66
+
58
67
  if (!entities.length) return [];
59
68
 
60
69
  debug('Fetching target models for entities:', entities);
@@ -85,7 +94,6 @@ export function createHasManyThroughInclusionResolver<
85
94
  );
86
95
 
87
96
  const throughRepo = await getThroughRepo();
88
- const targetRepo = await getTargetRepo();
89
97
 
90
98
  // find through models
91
99
  const throughFound = await findByForeignKeys(
@@ -102,34 +110,117 @@ export function createHasManyThroughInclusionResolver<
102
110
  throughKeyFrom,
103
111
  );
104
112
 
105
- const result = [];
106
-
107
113
  const scope =
108
114
  typeof inclusion === 'string' ? {} : (inclusion.scope as Filter<Target>);
109
115
 
110
- // convert from through entities to the target entities
111
- for (const entityList of throughResult) {
112
- if (entityList) {
113
- // get target ids from the through entities by foreign key
114
- const targetIds = entityList.map(entity => entity[throughKeyTo]);
115
-
116
- // the explicit types and casts are needed
116
+ // whether the polymorphism is configured
117
+ const targetDiscriminator: keyof (Through & ThroughRelations) | undefined =
118
+ relationMeta.through!.polymorphic
119
+ ? (relationMeta.through!.polymorphic.discriminator as keyof (Through &
120
+ ThroughRelations))
121
+ : undefined;
122
+ if (targetDiscriminator) {
123
+ // put through results into arrays based on the target polymorphic types
124
+ const throughArrayByTargetType: {
125
+ [targetType: string]: (Through & ThroughRelations)[];
126
+ } = {};
127
+ for (const throughArray of throughResult) {
128
+ if (throughArray) {
129
+ for (const throughItem of throughArray) {
130
+ const targetType = String(throughItem[targetDiscriminator]);
131
+ if (!getTargetRepoDict[targetType]) {
132
+ throw new InvalidPolymorphismError(
133
+ targetType,
134
+ String(targetDiscriminator),
135
+ );
136
+ }
137
+ if (!throughArrayByTargetType[targetType]) {
138
+ throughArrayByTargetType[targetType] = [];
139
+ }
140
+ throughArrayByTargetType[targetType].push(throughItem);
141
+ }
142
+ }
143
+ }
144
+ // get targets based on their polymorphic types
145
+ const targetOfTypes: {
146
+ [targetType: string]: (Target & TargetRelations)[];
147
+ } = {};
148
+ for (const targetType of Object.keys(throughArrayByTargetType)) {
149
+ const targetIds = throughArrayByTargetType[targetType].map(
150
+ throughItem => throughItem[throughKeyTo],
151
+ );
152
+ const targetRepo = await getTargetRepoDict[targetType]();
117
153
  const targetEntityList = await findByForeignKeys<
118
154
  Target,
119
155
  TargetRelations,
120
156
  StringKeyOf<Target>
121
- >(targetRepo, targetKey, targetIds as unknown as [], scope, {
122
- ...options,
123
- isThroughModelInclude: true,
124
- });
125
- result.push(targetEntityList);
126
- } else {
127
- // no entities found, add undefined to results
128
- result.push(entityList);
157
+ >(targetRepo, targetKey, targetIds as unknown as [], scope, options);
158
+ targetOfTypes[targetType] = targetEntityList;
159
+ }
160
+ // put targets into arrays reflecting their throughs
161
+ // Why the order is correct:
162
+ // e.g. through model = T(target instance), target model 1 = a, target model 2 = b
163
+ // all entities: [S1, S2, S2]
164
+ // through-result: [[T(b-11), T(a-12), T(b-13), T(b-14)], [T(a-21), T(a-22), T(b-23)], [T(b-31), T(b-32), T(a-33)]]
165
+ // through-array-by-target-type: {a:[T(a-12), T(a-21), T(a-22), T(a-33)] b: [T(b-11), T(b-13), T(b-14), T(b-23), T(b-31), T(b-32)]}
166
+ // target-array-by-target-type: {a:[a-12, a-21, a-22, a-33] b: [b-11, b-13, b-14, b-23, b-31, b-32]}
167
+ // merged:
168
+ // through-result[0][0]->b => targets: [[b-11 from b.shift()]]
169
+ // through-result[0][1]->a => targets: [[b-11, a-12 from a.shift()]]
170
+ // through-result[0][2]->b => targets: [[b-11, a-12, b-13 from b.shift()]]
171
+ // through-result[0][3]->b => targets: [[b-11, a-12, b-13, b-14 from b.shift()]]
172
+ // through-result[1][0]->a => targets: [[b-11, a-12, b-13, b-14], [a-21, from a.shift()]]
173
+ // through-result[1][1]->a => targets: [[b-11, a-12, b-13, b-14], [a-21, a-22 from a.shift()]]
174
+ // through-result[1][2]->b => targets: [[b-11, a-12, b-13, b-14], [a-21, a-22, b-23 from b.shift()]]
175
+ // through-result[2][0]->b => targets: [[b-11, a-12, b-13, b-14], [a-21, a-22, b-23], [b-31, from b.shift()]]
176
+ // through-result[2][1]->b => targets: [[b-11, a-12, b-13, b-14], [a-21, a-22, b-23], [b-31, b-32 from b.shift()]]
177
+ // through-result[2][1]->b => targets: [[b-11, a-12, b-13, b-14], [a-21, a-22, b-23], [b-31, b-32, a-33 from a.shift()]]
178
+ const allTargetsOfThrough: ((Target & TargetRelations)[] | undefined)[] =
179
+ [];
180
+ for (const throughArray of throughResult) {
181
+ if (throughArray && throughArray.length > 0) {
182
+ const currentTargetThroughArray: (Target & TargetRelations)[] = [];
183
+ for (const throughItem of throughArray) {
184
+ const itemToAdd =
185
+ targetOfTypes[String(throughItem[targetDiscriminator])].shift();
186
+ if (itemToAdd) {
187
+ currentTargetThroughArray.push(itemToAdd);
188
+ }
189
+ }
190
+ allTargetsOfThrough.push(currentTargetThroughArray);
191
+ } else {
192
+ allTargetsOfThrough.push(undefined);
193
+ }
194
+ }
195
+ return allTargetsOfThrough;
196
+ } else {
197
+ const targetRepo = await getTargetRepoDict[relationMeta.target().name]();
198
+ const result = [];
199
+
200
+ // convert from through entities to the target entities
201
+ for (const entityList of throughResult) {
202
+ if (entityList) {
203
+ // get target ids from the through entities by foreign key
204
+ const targetIds = entityList.map(entity => entity[throughKeyTo]);
205
+
206
+ // the explicit types and casts are needed
207
+ const targetEntityList = await findByForeignKeys<
208
+ Target,
209
+ TargetRelations,
210
+ StringKeyOf<Target>
211
+ >(targetRepo, targetKey, targetIds as unknown as [], scope, {
212
+ ...options,
213
+ isThroughModelInclude: true,
214
+ });
215
+ result.push(targetEntityList);
216
+ } else {
217
+ // no entities found, add undefined to results
218
+ result.push(entityList);
219
+ }
129
220
  }
130
- }
131
221
 
132
- debug('fetchHasManyThroughModels result', result);
133
- return result;
222
+ debug('fetchHasManyThroughModels result', result);
223
+ return result;
224
+ }
134
225
  };
135
226
  }
@@ -1,4 +1,4 @@
1
- // Copyright IBM Corp. 2020. All Rights Reserved.
1
+ // Copyright IBM Corp. and LoopBack contributors 2020. All Rights Reserved.
2
2
  // Node module: @loopback/repository
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
@@ -59,10 +59,22 @@ export function createHasManyThroughRepositoryFactory<
59
59
  SourceID,
60
60
  >(
61
61
  relationMetadata: HasManyDefinition,
62
- targetRepositoryGetter: Getter<EntityCrudRepository<Target, TargetID>>,
62
+ targetRepositoryGetter:
63
+ | Getter<EntityCrudRepository<Target, TargetID>>
64
+ | {
65
+ [repoType: string]: Getter<EntityCrudRepository<Target, TargetID>>;
66
+ },
63
67
  throughRepositoryGetter: Getter<EntityCrudRepository<Through, ThroughID>>,
64
68
  ): HasManyThroughRepositoryFactory<Target, TargetID, Through, SourceID> {
65
69
  const meta = resolveHasManyThroughMetadata(relationMetadata);
70
+ // resolve the repositoryGetter into a dictionary
71
+ if (typeof targetRepositoryGetter === 'function') {
72
+ targetRepositoryGetter = {
73
+ [meta.target().name]: targetRepositoryGetter as Getter<
74
+ EntityCrudRepository<Target, TargetID>
75
+ >,
76
+ };
77
+ }
66
78
  const result = function (fkValue: SourceID) {
67
79
  function getTargetConstraintFromThroughModels(
68
80
  throughInstances: Through[],
@@ -103,19 +115,25 @@ export function createHasManyThroughRepositoryFactory<
103
115
  ThroughID,
104
116
  EntityCrudRepository<Through, ThroughID>
105
117
  >(
106
- targetRepositoryGetter,
118
+ targetRepositoryGetter as {
119
+ [repoType: string]: Getter<EntityCrudRepository<Target, TargetID>>;
120
+ },
107
121
  throughRepositoryGetter,
108
122
  getTargetConstraintFromThroughModels,
109
123
  getTargetKeys,
110
124
  getThroughConstraintFromSource,
111
125
  getTargetIds,
112
126
  getThroughConstraintFromTarget,
127
+ relationMetadata.target,
128
+ relationMetadata.through!.model,
113
129
  );
114
130
  };
115
131
  result.inclusionResolver = createHasManyThroughInclusionResolver(
116
132
  meta,
117
133
  throughRepositoryGetter,
118
- targetRepositoryGetter,
134
+ targetRepositoryGetter as {
135
+ [repoType: string]: Getter<EntityCrudRepository<Target, TargetID>>;
136
+ },
119
137
  );
120
138
  return result;
121
139
  }