@loopback/repository
Version:
Define and implement a common set of interfaces for interacting with databases
282 lines • 15.9 kB
JavaScript
"use strict";
// 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.DefaultHasManyThroughRepository = void 0;
const lodash_1 = require("lodash");
const __1 = require("../..");
/**
* a class for CRUD operations for hasManyThrough relation.
*
* Warning: The hasManyThrough interface is experimental and is subject to change.
* If backwards-incompatible changes are made, a new major version may not be
* released.
*/
class DefaultHasManyThroughRepository {
constructor(getTargetRepository, getThroughRepository, getTargetConstraintFromThroughModels, getTargetKeys, getThroughConstraintFromSource, getTargetIds, getThroughConstraintFromTarget, targetResolver, throughResolver) {
this.getTargetRepository = getTargetRepository;
this.getThroughRepository = getThroughRepository;
this.getTargetConstraintFromThroughModels = getTargetConstraintFromThroughModels;
this.getTargetKeys = getTargetKeys;
this.getThroughConstraintFromSource = getThroughConstraintFromSource;
this.getTargetIds = getTargetIds;
this.getThroughConstraintFromTarget = getThroughConstraintFromTarget;
this.targetResolver = targetResolver;
this.throughResolver = throughResolver;
if (typeof getTargetRepository === 'function') {
this.getTargetRepositoryDict = {
[targetResolver().name]: getTargetRepository,
};
}
else {
this.getTargetRepositoryDict = getTargetRepository;
}
}
async create(targetModelData, options) {
let targetPolymorphicTypeName = options === null || options === void 0 ? void 0 : options.polymorphicType;
if (targetPolymorphicTypeName) {
if (!this.getTargetRepositoryDict[targetPolymorphicTypeName]) {
throw new __1.InvalidPolymorphismError(targetPolymorphicTypeName);
}
}
else {
if (Object.keys(this.getTargetRepositoryDict).length > 1) {
console.warn('It is highly recommended to specify the polymorphicTypes param when using polymorphic types.');
}
targetPolymorphicTypeName = this.targetResolver().name;
if (!this.getTargetRepositoryDict[targetPolymorphicTypeName]) {
throw new __1.InvalidPolymorphismError(targetPolymorphicTypeName);
}
}
const targetRepository = await this.getTargetRepositoryDict[targetPolymorphicTypeName]();
const targetInstance = await targetRepository.create(targetModelData, options);
await this.link(targetInstance.getId(), options);
return targetInstance;
}
async find(filter, options) {
var _a;
const targetDiscriminatorOnThrough = (_a = options === null || options === void 0 ? void 0 : options.throughOptions) === null || _a === void 0 ? void 0 : _a.discriminator;
let targetPolymorphicTypes = options === null || options === void 0 ? void 0 : options.polymorphicType;
let allKeys;
if (Object.keys(this.getTargetRepositoryDict).length <= 1) {
allKeys = Object.keys(this.getTargetRepositoryDict);
}
else {
if (!targetDiscriminatorOnThrough) {
console.warn('It is highly recommended to specify the targetDiscriminatorOnThrough param when using polymorphic types.');
}
if (!targetPolymorphicTypes || targetPolymorphicTypes.length === 0) {
console.warn('It is highly recommended to specify the polymorphicTypes param when using polymorphic types.');
allKeys = Object.keys(this.getTargetRepositoryDict);
}
else {
if (typeof targetPolymorphicTypes === 'string') {
targetPolymorphicTypes = [targetPolymorphicTypes];
}
allKeys = [];
new Set(targetPolymorphicTypes).forEach(element => {
if (Object.keys(this.getTargetRepositoryDict).includes(element)) {
allKeys.push(element);
}
});
}
}
const sourceConstraint = this.getThroughConstraintFromSource();
const throughCategorized = {};
const throughRepository = await this.getThroughRepository();
(await throughRepository.find((0, __1.constrainFilter)(undefined, sourceConstraint), options === null || options === void 0 ? void 0 : options.throughOptions)).forEach(element => {
let concreteTargetType;
if (!targetDiscriminatorOnThrough) {
concreteTargetType = this.targetResolver().name;
}
else {
concreteTargetType = String(element[targetDiscriminatorOnThrough]);
}
if (!allKeys.includes(concreteTargetType)) {
return;
}
if (!this.getTargetRepositoryDict[concreteTargetType]) {
throw new __1.InvalidPolymorphismError(concreteTargetType, targetDiscriminatorOnThrough);
}
if (!throughCategorized[concreteTargetType]) {
throughCategorized[concreteTargetType] = [];
}
throughCategorized[concreteTargetType].push(element);
});
let allTargets = [];
for (const key of Object.keys(throughCategorized)) {
const targetRepository = await this.getTargetRepositoryDict[key]();
const targetConstraint = this.getTargetConstraintFromThroughModels(throughCategorized[key]);
allTargets = allTargets.concat(await targetRepository.find((0, __1.constrainFilter)(filter, targetConstraint), Object.assign((0, lodash_1.cloneDeep)(options !== null && options !== void 0 ? options : {}), { polymorphicType: key })));
}
return allTargets;
}
async delete(where, options) {
var _a, _b, _c;
const targetDiscriminatorOnThrough = (_a = options === null || options === void 0 ? void 0 : options.throughOptions) === null || _a === void 0 ? void 0 : _a.discriminator;
let targetPolymorphicTypes = options === null || options === void 0 ? void 0 : options.polymorphicType;
let allKeys;
if (Object.keys(this.getTargetRepositoryDict).length <= 1) {
allKeys = Object.keys(this.getTargetRepositoryDict);
}
else {
if (!targetDiscriminatorOnThrough) {
console.warn('It is highly recommended to specify the targetDiscriminatorOnThrough param when using polymorphic types.');
}
if (!targetPolymorphicTypes || targetPolymorphicTypes.length === 0) {
console.warn('It is highly recommended to specify the polymorphicTypes param when using polymorphic types.');
allKeys = Object.keys(this.getTargetRepositoryDict);
}
else {
if (typeof targetPolymorphicTypes === 'string') {
targetPolymorphicTypes = [targetPolymorphicTypes];
}
allKeys = [];
new Set(targetPolymorphicTypes).forEach(element => {
if (Object.keys(this.getTargetRepositoryDict).includes(element)) {
allKeys.push(element);
}
});
}
}
const sourceConstraint = this.getThroughConstraintFromSource();
let totalCount = 0;
const throughCategorized = {};
const throughRepository = await this.getThroughRepository();
(await throughRepository.find((0, __1.constrainFilter)(undefined, sourceConstraint), options === null || options === void 0 ? void 0 : options.throughOptions)).forEach(element => {
let concreteTargetType;
if (!targetDiscriminatorOnThrough) {
concreteTargetType = this.targetResolver().name;
}
else {
concreteTargetType = String(element[targetDiscriminatorOnThrough]);
}
if (!allKeys.includes(concreteTargetType)) {
return;
}
if (!this.getTargetRepositoryDict[concreteTargetType]) {
throw new __1.InvalidPolymorphismError(concreteTargetType, targetDiscriminatorOnThrough);
}
if (!throughCategorized[concreteTargetType]) {
throughCategorized[concreteTargetType] = [];
}
throughCategorized[concreteTargetType].push(element);
});
for (const targetKey of Object.keys(throughCategorized)) {
const targetRepository = await this.getTargetRepositoryDict[targetKey]();
if (where) {
// only delete related through models
// TODO(Agnes): this performance can be improved by only fetching related data
// TODO: add target ids to the `where` constraint
const targets = await targetRepository.find({ where });
const targetIds = this.getTargetIds(targets);
if (targetIds.length > 0) {
const targetConstraint = this.getThroughConstraintFromTarget(targetIds);
const constraints = { ...targetConstraint, ...sourceConstraint };
await throughRepository.deleteAll((0, __1.constrainDataObject)({}, constraints), options === null || options === void 0 ? void 0 : options.throughOptions);
}
}
else {
// otherwise, delete through models that relate to the sourceId
const targetFkValues = this.getTargetKeys(throughCategorized[targetKey]);
// delete through instances that have the targets that are going to be deleted
const throughFkConstraint = this.getThroughConstraintFromTarget(targetFkValues);
await throughRepository.deleteAll((0, __1.constrainWhereOr)({}, [
sourceConstraint,
throughFkConstraint,
]));
}
// delete target(s)
const targetConstraint = this.getTargetConstraintFromThroughModels(throughCategorized[targetKey]);
totalCount +=
(_c = (_b = (await targetRepository.deleteAll((0, __1.constrainWhere)(where, targetConstraint), options))) === null || _b === void 0 ? void 0 : _b.count) !== null && _c !== void 0 ? _c : 0;
}
return { count: totalCount };
}
// only allows patch target instances for now
async patch(dataObject, where, options) {
var _a, _b, _c;
const targetDiscriminatorOnThrough = (_a = options === null || options === void 0 ? void 0 : options.throughOptions) === null || _a === void 0 ? void 0 : _a.discriminator;
const isMultipleTypes = options === null || options === void 0 ? void 0 : options.isPolymorphic;
let allKeys;
if (!targetDiscriminatorOnThrough) {
if (Object.keys(this.getTargetRepositoryDict).length > 1) {
console.warn('It is highly recommended to specify the targetDiscriminatorOnThrough param when using polymorphic types.');
}
}
if (!isMultipleTypes) {
if (Object.keys(this.getTargetRepositoryDict).length > 1) {
console.warn('It is highly recommended to specify the isMultipleTypes param and pass in a dictionary of dataobjects when using polymorphic types.');
}
allKeys = Object.keys(this.getTargetRepositoryDict);
}
else {
allKeys = [];
new Set(Object.keys(dataObject)).forEach(element => {
if (Object.keys(this.getTargetRepositoryDict).includes(element)) {
allKeys.push(element);
}
});
}
const sourceConstraint = this.getThroughConstraintFromSource();
const throughCategorized = {};
const throughRepository = await this.getThroughRepository();
(await throughRepository.find((0, __1.constrainFilter)(undefined, sourceConstraint), options === null || options === void 0 ? void 0 : options.throughOptions)).forEach(element => {
let concreteTargetType;
if (!targetDiscriminatorOnThrough) {
concreteTargetType = this.targetResolver().name;
}
else {
concreteTargetType = String(element[targetDiscriminatorOnThrough]);
}
if (!allKeys.includes(concreteTargetType)) {
return;
}
if (!this.getTargetRepositoryDict[concreteTargetType]) {
throw new __1.InvalidPolymorphismError(concreteTargetType, targetDiscriminatorOnThrough);
}
if (!throughCategorized[concreteTargetType]) {
throughCategorized[concreteTargetType] = [];
}
throughCategorized[concreteTargetType].push(element);
});
let updatedCount = 0;
for (const key of Object.keys(throughCategorized)) {
const targetRepository = await this.getTargetRepositoryDict[key]();
const targetConstraint = this.getTargetConstraintFromThroughModels(throughCategorized[key]);
updatedCount +=
(_c = (_b = (await targetRepository.updateAll((0, __1.constrainDataObject)(isMultipleTypes
? dataObject[key]
: dataObject, targetConstraint), (0, __1.constrainWhere)(where, targetConstraint), options))) === null || _b === void 0 ? void 0 : _b.count) !== null && _c !== void 0 ? _c : 0;
}
return { count: updatedCount };
}
async link(targetId, options) {
var _a;
const throughRepository = await this.getThroughRepository();
const sourceConstraint = this.getThroughConstraintFromSource();
const targetConstraint = this.getThroughConstraintFromTarget([targetId]);
const constraints = { ...targetConstraint, ...sourceConstraint };
await throughRepository.create((0, __1.constrainDataObject)((_a = options === null || options === void 0 ? void 0 : options.throughData) !== null && _a !== void 0 ? _a : {}, constraints), options === null || options === void 0 ? void 0 : options.throughOptions);
}
async unlink(targetId, options) {
const throughRepository = await this.getThroughRepository();
const sourceConstraint = this.getThroughConstraintFromSource();
const targetConstraint = this.getThroughConstraintFromTarget([targetId]);
const constraints = { ...targetConstraint, ...sourceConstraint };
await throughRepository.deleteAll((0, __1.constrainDataObject)({}, constraints), options === null || options === void 0 ? void 0 : options.throughOptions);
}
async unlinkAll(options) {
const throughRepository = await this.getThroughRepository();
const sourceConstraint = this.getThroughConstraintFromSource();
const throughInstances = await throughRepository.find((0, __1.constrainFilter)(undefined, sourceConstraint), options === null || options === void 0 ? void 0 : options.throughOptions);
const targetFkValues = this.getTargetKeys(throughInstances);
const targetConstraint = this.getThroughConstraintFromTarget(targetFkValues);
const constraints = { ...targetConstraint, ...sourceConstraint };
await throughRepository.deleteAll((0, __1.constrainDataObject)({}, constraints), options === null || options === void 0 ? void 0 : options.throughOptions);
}
}
exports.DefaultHasManyThroughRepository = DefaultHasManyThroughRepository;
//# sourceMappingURL=has-many-through.repository.js.map