@joktec/mongo
Version:
JokTec - Mongo Service
155 lines • 7.2 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.StrictReferencePlugin = void 0;
const core_1 = require("@joktec/core");
const utils_1 = require("@joktec/utils");
const typegoose_1 = require("@typegoose/typegoose");
const lodash_1 = require("lodash");
const mongoose_1 = __importStar(require("mongoose"));
function getRefPath(schema) {
const refPath = [];
Object.values(schema.paths).map(schemaType => {
const isRefObject = schemaType instanceof mongoose_1.default.Schema.Types.ObjectId &&
schemaType.options.ref &&
schemaType.options.strictRef === true;
if (isRefObject) {
refPath.push({ path: schemaType.path, ref: schemaType.options.ref });
}
const isArrayRef = schemaType instanceof mongoose_1.default.Schema.Types.Array &&
schemaType.caster &&
schemaType.caster?.options?.ref &&
schemaType.options.strictRef === true;
if (isArrayRef) {
refPath.push({ path: schemaType.path, ref: schemaType.caster?.options?.ref });
}
});
return refPath;
}
function getVirtualPath(schema) {
return (0, lodash_1.pickBy)(schema.virtuals, (virtual) => {
const strictRef = (0, lodash_1.get)(virtual, 'options.strictRef', false);
const isForeign = (0, lodash_1.get)(virtual, 'options.foreignField', '_id') !== '_id';
return strictRef && isForeign;
});
}
const StrictReferencePlugin = (schema, opts) => {
schema.pre('save', async function (next) {
const refPaths = getRefPath(schema);
if (!refPaths.length)
return next();
const validation = new mongoose_1.Error.ValidationError();
for (const refPath of refPaths) {
const insertIds = (0, utils_1.toArray)(this.get(refPath.path));
if (!insertIds.length)
continue;
const items = await (0, typegoose_1.getModelWithString)(refPath.ref).find().in('_id', insertIds).exec();
const storeIds = items.map(item => String(item._id));
const notExistIds = (0, lodash_1.difference)(insertIds, storeIds);
if (notExistIds.length) {
const validator = new mongoose_1.Error.ValidatorError({
type: 'strictRef',
message: `Not found dependent documents in '${refPath.ref}' with path '${refPath.path}'.`,
path: refPath.path,
value: notExistIds,
reason: new core_1.BadRequestException('MONGO_REF_EXCEPTION'),
});
validation.addError(refPath.path, validator);
}
}
if (!(0, lodash_1.isEmpty)(validation.errors)) {
return next(validation);
}
next();
});
schema.pre(['findOneAndUpdate', 'findOneAndReplace', 'updateOne', 'updateMany'], async function (next) {
const paranoidKey = opts?.paranoidKey;
if (paranoidKey && this.get(paranoidKey)) {
return next();
}
const refPaths = getRefPath(schema);
if (!refPaths.length)
return next();
const validation = new mongoose_1.Error.ValidationError();
for (const refPath of refPaths) {
const insertIds = (0, utils_1.toArray)(this.get(refPath.path));
if (!insertIds.length)
continue;
const items = await (0, typegoose_1.getModelWithString)(refPath.ref).find().in('_id', insertIds).exec();
const storeIds = items.map(item => String(item._id));
const notExistIds = (0, lodash_1.difference)(insertIds, storeIds);
if (notExistIds.length) {
const validator = new mongoose_1.Error.ValidatorError({
type: 'strictRef',
message: `Not found dependent documents in '${refPath.ref}' with path '${refPath.path}'.`,
path: refPath.path,
value: notExistIds,
reason: new core_1.BadRequestException('MONGO_REF_EXCEPTION'),
});
validation.addError(refPath.path, validator);
}
}
if (!(0, lodash_1.isEmpty)(validation.errors)) {
return next(validation);
}
next();
});
schema.pre(['findOneAndUpdate', 'updateMany', 'updateOne', 'findOneAndDelete', 'deleteMany', 'deleteOne'], async function (next) {
const paranoidKey = opts?.paranoidKey;
if (paranoidKey && this.get(paranoidKey)) {
return next();
}
const virtualPath = getVirtualPath(schema);
if ((0, lodash_1.isEmpty)(virtualPath))
return next();
const items = await (0, typegoose_1.getModelWithString)(this.model.modelName)
.find(this.getFilter(), '_id', this.getOptions())
.exec();
if (!items.length)
return next();
const validation = new mongoose_1.Error.ValidationError();
for (const [path, virtual] of Object.entries(virtualPath)) {
const { ref, foreignField } = (0, lodash_1.get)(virtual, 'options', {});
const query = { [foreignField]: { $in: items.map(item => item._id) } };
const dependentDocuments = await (0, typegoose_1.getModelWithString)(ref).find(query, '_id').exec();
if (dependentDocuments.length > 0) {
const validator = new mongoose_1.Error.ValidatorError({
type: 'strictRef',
message: `Cannot delete: ${dependentDocuments.length} dependent documents in '${ref}' with path '${path}'.`,
path: path,
value: dependentDocuments.map(doc => doc._id),
reason: new core_1.BadRequestException('MONGO_REF_EXCEPTION'),
});
validation.addError(path, validator);
}
}
if (!(0, lodash_1.isEmpty)(validation.errors)) {
return next(validation);
}
next();
});
};
exports.StrictReferencePlugin = StrictReferencePlugin;
//# sourceMappingURL=strict-reference.plugin.js.map