UNPKG

8.28 kBJavaScriptView Raw
1"use strict";
2// Copyright IBM Corp. and LoopBack contributors 2020. All Rights Reserved.
3// Node module: @loopback/repository
4// This file is licensed under the MIT License.
5// License text available at https://opensource.org/licenses/MIT
6Object.defineProperty(exports, "__esModule", { value: true });
7exports.createHasManyThroughInclusionResolver = void 0;
8const tslib_1 = require("tslib");
9const debug_1 = tslib_1.__importDefault(require("debug"));
10const __1 = require("../..");
11const relation_helpers_1 = require("../relation.helpers");
12const has_many_through_helpers_1 = require("./has-many-through.helpers");
13const debug = (0, debug_1.default)('loopback:repository:relations:has-many-through:inclusion-resolver');
14/**
15 * Creates InclusionResolver for HasManyThrough relation.
16 * Notice that this function only generates the inclusionResolver.
17 * It doesn't register it for the source repository.
18 *
19 *
20 * @param meta - metadata of the hasMany relation (including through)
21 * @param getThroughRepo - through repository getter i.e. where through
22 * instances are
23 * @param getTargetRepo - target repository getter i.e where target instances
24 * are
25 */
26function createHasManyThroughInclusionResolver(meta, getThroughRepo, getTargetRepoDict) {
27 const relationMeta = (0, has_many_through_helpers_1.resolveHasManyThroughMetadata)(meta);
28 return async function fetchHasManyThroughModels(entities, inclusion, options) {
29 if (!relationMeta.through) {
30 throw new Error(`relationMeta.through must be defined on ${relationMeta}`);
31 }
32 if (!entities.length)
33 return [];
34 debug('Fetching target models for entities:', entities);
35 debug('Relation metadata:', relationMeta);
36 const sourceKey = relationMeta.keyFrom;
37 const sourceIds = entities.map(e => e[sourceKey]);
38 const targetKey = relationMeta.keyTo;
39 if (!relationMeta.through) {
40 throw new Error(`relationMeta.through must be defined on ${relationMeta}`);
41 }
42 const throughKeyTo = relationMeta.through.keyTo;
43 const throughKeyFrom = relationMeta.through.keyFrom;
44 debug('Parameters:', {
45 sourceKey,
46 sourceIds,
47 targetKey,
48 throughKeyTo,
49 throughKeyFrom,
50 });
51 debug('sourceId types', sourceIds.map(i => typeof i));
52 const throughRepo = await getThroughRepo();
53 // find through models
54 const throughFound = await (0, relation_helpers_1.findByForeignKeys)(throughRepo, throughKeyFrom, sourceIds, {}, // scope will be applied at the target level
55 options);
56 const throughResult = (0, relation_helpers_1.flattenTargetsOfOneToManyRelation)(sourceIds, throughFound, throughKeyFrom);
57 const scope = typeof inclusion === 'string' ? {} : inclusion.scope;
58 // whether the polymorphism is configured
59 const targetDiscriminator = relationMeta.through.polymorphic
60 ? relationMeta.through.polymorphic.discriminator
61 : undefined;
62 if (targetDiscriminator) {
63 // put through results into arrays based on the target polymorphic types
64 const throughArrayByTargetType = {};
65 for (const throughArray of throughResult) {
66 if (throughArray) {
67 for (const throughItem of throughArray) {
68 const targetType = String(throughItem[targetDiscriminator]);
69 if (!getTargetRepoDict[targetType]) {
70 throw new __1.InvalidPolymorphismError(targetType, String(targetDiscriminator));
71 }
72 if (!throughArrayByTargetType[targetType]) {
73 throughArrayByTargetType[targetType] = [];
74 }
75 throughArrayByTargetType[targetType].push(throughItem);
76 }
77 }
78 }
79 // get targets based on their polymorphic types
80 const targetOfTypes = {};
81 for (const targetType of Object.keys(throughArrayByTargetType)) {
82 const targetIds = throughArrayByTargetType[targetType].map(throughItem => throughItem[throughKeyTo]);
83 const targetRepo = await getTargetRepoDict[targetType]();
84 const targetEntityList = await (0, relation_helpers_1.findByForeignKeys)(targetRepo, targetKey, targetIds, scope, options);
85 targetOfTypes[targetType] = targetEntityList;
86 }
87 // put targets into arrays reflecting their throughs
88 // Why the order is correct:
89 // e.g. through model = T(target instance), target model 1 = a, target model 2 = b
90 // all entities: [S1, S2, S2]
91 // 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)]]
92 // 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)]}
93 // 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]}
94 // merged:
95 // through-result[0][0]->b => targets: [[b-11 from b.shift()]]
96 // through-result[0][1]->a => targets: [[b-11, a-12 from a.shift()]]
97 // through-result[0][2]->b => targets: [[b-11, a-12, b-13 from b.shift()]]
98 // through-result[0][3]->b => targets: [[b-11, a-12, b-13, b-14 from b.shift()]]
99 // through-result[1][0]->a => targets: [[b-11, a-12, b-13, b-14], [a-21, from a.shift()]]
100 // through-result[1][1]->a => targets: [[b-11, a-12, b-13, b-14], [a-21, a-22 from a.shift()]]
101 // through-result[1][2]->b => targets: [[b-11, a-12, b-13, b-14], [a-21, a-22, b-23 from b.shift()]]
102 // 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()]]
103 // 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()]]
104 // 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()]]
105 const allTargetsOfThrough = [];
106 for (const throughArray of throughResult) {
107 if (throughArray && throughArray.length > 0) {
108 const currentTargetThroughArray = [];
109 for (const throughItem of throughArray) {
110 const itemToAdd = targetOfTypes[String(throughItem[targetDiscriminator])].shift();
111 if (itemToAdd) {
112 currentTargetThroughArray.push(itemToAdd);
113 }
114 }
115 allTargetsOfThrough.push(currentTargetThroughArray);
116 }
117 else {
118 allTargetsOfThrough.push(undefined);
119 }
120 }
121 return allTargetsOfThrough;
122 }
123 else {
124 const targetRepo = await getTargetRepoDict[relationMeta.target().name]();
125 const result = [];
126 // convert from through entities to the target entities
127 for (const entityList of throughResult) {
128 if (entityList) {
129 // get target ids from the through entities by foreign key
130 const targetIds = entityList.map(entity => entity[throughKeyTo]);
131 // the explicit types and casts are needed
132 const targetEntityList = await (0, relation_helpers_1.findByForeignKeys)(targetRepo, targetKey, targetIds, scope, {
133 ...options,
134 isThroughModelInclude: true,
135 });
136 result.push(targetEntityList);
137 }
138 else {
139 // no entities found, add undefined to results
140 result.push(entityList);
141 }
142 }
143 debug('fetchHasManyThroughModels result', result);
144 return result;
145 }
146 };
147}
148exports.createHasManyThroughInclusionResolver = createHasManyThroughInclusionResolver;
149//# sourceMappingURL=has-many-through.inclusion-resolver.js.map
\No newline at end of file