mongodb-dynamic-api
Version:
Auto generated CRUD API for MongoDB using NestJS
182 lines • 8.56 kB
JavaScript
"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