UNPKG

mongodb-dynamic-api

Version:

Auto generated CRUD API for MongoDB using NestJS

182 lines 8.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseService = void 0; const common_1 = require("@nestjs/common"); const class_transformer_1 = require("class-transformer"); const logger_1 = require("../../logger"); const dynamic_api_global_state_service_1 = require("../dynamic-api-global-state/dynamic-api-global-state.service"); class BaseService { constructor(model) { this.model = model; this.baseServiceLogger = new logger_1.MongoDBDynamicApiLogger(BaseService.name); this.callbackMethods = { findManyDocuments: this.findManyDocuments.bind(this), findOneDocument: this.findOneDocument.bind(this), createManyDocuments: this.createManyDocuments.bind(this), createOneDocument: this.createOneDocument.bind(this), updateManyDocuments: this.updateManyDocuments.bind(this), updateOneDocument: this.updateOneDocument.bind(this), deleteManyDocuments: this.deleteManyDocuments.bind(this), deleteOneDocument: this.deleteOneDocument.bind(this), aggregateDocuments: this.aggregateDocuments.bind(this), }; } get isSoftDeletable() { const paths = Object.getOwnPropertyNames(this.model.schema.paths); return paths.includes('deletedAt') && paths.includes('isDeleted'); } verifyArguments(...args) { if (args.some((arg) => arg === undefined)) { throw new common_1.BadRequestException('Invalid or missing argument'); } } async aggregateDocumentsWithAbilityPredicate(pipeline) { this.baseServiceLogger.debug('aggregateDocumentsWithAbilityPredicate', { pipeline: JSON.stringify(pipeline), entityName: this.entity.name, }); const documents = await this.aggregateDocuments(this.entity, pipeline); if (this.abilityPredicate) { documents.forEach((d) => this.handleAbilityPredicate(d)); } return documents; } async findManyDocumentsWithAbilityPredicate(conditions = {}) { this.baseServiceLogger.debug('findManyDocumentsWithAbilityPredicate', { conditions: JSON.stringify(conditions), entityName: this.entity.name, }); const documents = await this.findManyDocuments(this.entity, conditions); if (this.abilityPredicate) { documents.forEach((d) => this.handleAbilityPredicate(d)); } return documents; } async findOneDocumentWithAbilityPredicate(_id, conditions = {}, authAbilityPredicate) { this.baseServiceLogger.debug('findOneDocumentWithAbilityPredicate', { _id, conditions: JSON.stringify(conditions), entityName: this.entity.name, authAbilityPredicate: !!authAbilityPredicate, }); const document = await this.findOneDocument(this.entity, { ...(_id ? { _id } : {}), ...conditions, }); if (!document) { throw new common_1.BadRequestException('Document not found'); } if (authAbilityPredicate || this.abilityPredicate) { this.handleAbilityPredicate(document, authAbilityPredicate); } return document; } async aggregateDocuments(entity, pipeline) { const model = await dynamic_api_global_state_service_1.DynamicApiGlobalStateService.getEntityModel(entity); return model.aggregate(pipeline).exec(); } async findManyDocuments(entity, query) { const model = await dynamic_api_global_state_service_1.DynamicApiGlobalStateService.getEntityModel(entity); return model.find(query).lean().exec(); } async findOneDocument(entity, query) { const model = await dynamic_api_global_state_service_1.DynamicApiGlobalStateService.getEntityModel(entity); return model.findOne(query).lean().exec(); } async createManyDocuments(entity, data) { const model = await dynamic_api_global_state_service_1.DynamicApiGlobalStateService.getEntityModel(entity); return model.create(data); } async createOneDocument(entity, data) { const model = await dynamic_api_global_state_service_1.DynamicApiGlobalStateService.getEntityModel(entity); return model.create(data); } async updateManyDocuments(entity, query, update) { const model = await dynamic_api_global_state_service_1.DynamicApiGlobalStateService.getEntityModel(entity); return model.updateMany(query, update).exec(); } async updateOneDocument(entity, query, update) { const model = await dynamic_api_global_state_service_1.DynamicApiGlobalStateService.getEntityModel(entity); return model.updateOne(query, update).exec(); } async deleteManyDocuments(entity, ids) { const model = await dynamic_api_global_state_service_1.DynamicApiGlobalStateService.getEntityModel(entity); const paths = Object.getOwnPropertyNames(model.schema.paths); const isSoftDeletable = paths.includes('deletedAt') && paths.includes('isDeleted'); if (isSoftDeletable) { const result = await model.updateMany({ _id: { $in: ids } }, { isDeleted: true, deletedAt: new Date() }).exec(); return { deletedCount: result.modifiedCount }; } return model.deleteMany({ _id: { $in: ids } }).exec(); } async deleteOneDocument(entity, id) { const model = await dynamic_api_global_state_service_1.DynamicApiGlobalStateService.getEntityModel(entity); const paths = Object.getOwnPropertyNames(model.schema.paths); const isSoftDeletable = paths.includes('deletedAt') && paths.includes('isDeleted'); if (isSoftDeletable) { const result = await model.updateOne({ _id: id }, { isDeleted: true, deletedAt: new Date() }).exec(); return { deletedCount: result.modifiedCount }; } return model.deleteOne({ _id: id }).exec(); } buildInstance(document) { const { _id, id, __v, isDeleted, deletedAt, ...rest } = document; return (0, class_transformer_1.plainToInstance)(this.entity, { ...rest, ...(_id || id ? { id: _id?.toString() ?? id } : {}), ...(isDeleted ? { deletedAt } : {}), }); } handleAbilityPredicate(document, authAbilityPredicate) { this.baseServiceLogger.debug('handleAbilityPredicate', { documentId: document?._id?.toString() || document?.id, entityName: this.entity.name, abilityPredicate: !!this.abilityPredicate, authAbilityPredicate: !!authAbilityPredicate, }); const isAllowed = authAbilityPredicate ? authAbilityPredicate(this.buildInstance(document)) : this.abilityPredicate(document, this.user); if (!isAllowed) { throw new common_1.ForbiddenException('Forbidden resource'); } } handleDuplicateKeyError(error, reThrow = true) { if (error.code === 11000) { const properties = Object.entries(error.keyValue) .filter(([key]) => key !== 'deletedAt') .map(([key, value]) => `${key} '${value}'`); throw new common_1.ConflictException(properties.length === 1 ? `${properties[0]} is already used` : `The combination of ${properties.join(', ')} already exists`); } if (!reThrow) { return; } if (error instanceof common_1.HttpException) { throw error; } throw new common_1.ServiceUnavailableException(error.message); } handleMongoErrors(error, reThrow = true) { if (error.name === 'CastError') { throw new common_1.NotFoundException(`${this.entity?.name ?? 'Document'} not found`); } if (error.name === 'ValidationError') { const errorDetails = Object.values(error.errors)?.map(({ properties }) => properties.message); throw new common_1.BadRequestException(errorDetails?.length ? errorDetails : ['Invalid payload']); } if (!reThrow) { return; } if (error instanceof common_1.HttpException) { throw error; } throw new common_1.ServiceUnavailableException(error.message); } handleDocumentNotFound() { throw new common_1.NotFoundException('Document not found'); } } exports.BaseService = BaseService; //# sourceMappingURL=base.service.js.map