@loopback/repository
Version:
Define and implement a common set of interfaces for interacting with databases
110 lines • 5.47 kB
JavaScript
// Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved.
// Node module: @loopback/repository
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
Object.defineProperty(exports, "__esModule", { value: true });
exports.createHasOneInclusionResolver = void 0;
const lodash_1 = require("lodash");
const __1 = require("../../");
const relation_helpers_1 = require("../relation.helpers");
const has_one_helpers_1 = require("./has-one.helpers");
/**
* Creates InclusionResolver for HasOne relation.
* Notice that this function only generates the inclusionResolver.
* It doesn't register it for the source repository.
*
* Notice: scope field for inclusion is not supported yet.
*
* @param meta - resolved HasOneMetadata
* @param getTargetRepoDict - dictionary of target model type - target repository
* i.e where related instances for different types are
*/
function createHasOneInclusionResolver(meta, getTargetRepoDict) {
const relationMeta = (0, has_one_helpers_1.resolveHasOneMetadata)(meta);
return async function fetchHasOneModel(entities, inclusion, options) {
if (!entities.length)
return [];
// Source ids are grouped by their target polymorphic types
// Each type search for target instances and then merge together in a merge-sort-like manner
const sourceKey = relationMeta.keyFrom;
const targetKey = relationMeta.keyTo;
const targetDiscriminator = relationMeta.polymorphic
? relationMeta.polymorphic.discriminator
: undefined;
const scope = typeof inclusion === 'string' ? {} : inclusion.scope;
// sourceIds in {targetType -> sourceId}
const sourceIdsCategorized = {};
if (targetDiscriminator) {
entities.forEach((value, index, allEntites) => {
const concreteType = String(value[targetDiscriminator]);
if (!getTargetRepoDict[concreteType]) {
throw new __1.InvalidPolymorphismError(concreteType, targetDiscriminator);
}
if (!sourceIdsCategorized[concreteType]) {
sourceIdsCategorized[concreteType] = [];
}
sourceIdsCategorized[concreteType].push(value[sourceKey]);
});
}
else {
const concreteType = relationMeta.target().name;
if (!getTargetRepoDict[concreteType]) {
throw new __1.InvalidPolymorphismError(concreteType);
}
entities.forEach((value, index, allEntites) => {
if (!sourceIdsCategorized[concreteType]) {
sourceIdsCategorized[concreteType] = [];
}
sourceIdsCategorized[concreteType].push(value[sourceKey]);
});
}
// Ensure targetKey is included otherwise flatten function cannot work
const changedTargetKeyField = (0, __1.includeFieldIfNot)(scope === null || scope === void 0 ? void 0 : scope.fields, targetKey);
let needToRemoveTargetKeyFieldLater = false;
if (changedTargetKeyField !== false) {
scope.fields = changedTargetKeyField;
needToRemoveTargetKeyFieldLater = true;
}
// Each sourceIds array with same target type extract target instances
const targetCategorized = {};
for (const k of Object.keys(sourceIdsCategorized)) {
const targetRepo = await getTargetRepoDict[k]();
const targetsFound = await (0, relation_helpers_1.findByForeignKeys)(targetRepo, targetKey, sourceIdsCategorized[k], scope, Object.assign((0, lodash_1.cloneDeep)(options !== null && options !== void 0 ? options : {}), { polymorphicType: k }));
targetCategorized[k] = (0, relation_helpers_1.flattenTargetsOfOneToOneRelation)(sourceIdsCategorized[k], targetsFound, targetKey);
// Remove targetKey if should be excluded but included above
if (needToRemoveTargetKeyFieldLater) {
targetCategorized[k] = targetCategorized[k].map(e => {
if (e) {
delete e[targetKey];
}
return e;
});
}
}
// Merge
// Why the order is correct:
// e.g. target model 1 = a, target model 2 = b
// all entities: [S(a-1), S(a-2), S(b-3), S(a-4), S(b-5)]
// a-result: [a-1, a-2, a-4]
// b-result: [b-3, b-4]
// merged:
// entities[1]->a => targets: [a-1 from a-result.shift()]
// entities[2]->a => targets: [a-1, a-2 from a-result.shift()]
// entities[3]->b => targets: [a-1, a-2, b-3 from b-result.shift()]
// entities[4]->a => targets: [a-1, a-2, b-3, a-4 from a-result.shift()]
// entities[5]->b => targets: [a-1, a-2, b-3, a-4, b-5 from b-result.shift()]
if (targetDiscriminator) {
const allTargets = [];
entities.forEach((value, index, allEntites) => {
allTargets.push(targetCategorized[String(value[targetDiscriminator])].shift());
});
return allTargets;
}
else {
return targetCategorized[relationMeta.target().name];
}
};
}
exports.createHasOneInclusionResolver = createHasOneInclusionResolver;
//# sourceMappingURL=has-one.inclusion-resolver.js.map
;