import { HttpError } from "@lib/error-handling/http-error"; import { AnyKeys, Document, FilterQuery, Model } from "mongoose"; export const CrudService = ( model: Model, crudOptions?: { defaultFilter?: FilterQuery; } ) => { return class CrudServiceClass { public model: Model = model; async create(data: AnyKeys): Promise { return this.model.create(data); } async updateOne( filter: FilterQuery, data: AnyKeys ): Promise { filter = { ...crudOptions?.defaultFilter, ...filter }; await this.existsOrThrow(filter); await this.model.updateOne(filter, data); return this.findOneOrFail(filter); } async updateMany( filter: FilterQuery, data: AnyKeys, checkExists: boolean = true ): Promise { filter = { ...crudOptions?.defaultFilter, ...filter }; if (checkExists) await this.existsOrThrow(filter); await this.model.updateMany(filter, data); return this.model.find(filter); } async deleteOne(filter: FilterQuery): Promise { filter = { ...crudOptions?.defaultFilter, ...filter }; await this.existsOrThrow(filter); return this.model.findOneAndDelete(filter); } async softDelete( filter: FilterQuery ): Promise { filter = { ...crudOptions?.defaultFilter, ...filter }; await this.existsOrThrow(filter); await this.model.updateOne(filter, { isDeleted: true }); return this.findOneOrFail(filter); } async list( filter: FilterQuery, paginationOptions: { limit?: number; skip?: number; } = { limit: 10, skip: 1, }, options?: { populateArray?: any, filterOptions?: any }, ): Promise<{ docs: ModelDoc[]; paginationData: { total: number; page: number; perPage: number; }; }> { if (options?.filterOptions) filter = { ...filter, ...options.filterOptions }; filter = { ...crudOptions?.defaultFilter, ...filter }; const queryInstruction = this.model .find(filter) .limit(paginationOptions.limit) .skip(paginationOptions.skip); if (options?.populateArray) queryInstruction.populate(options.populateArray); const docs = await queryInstruction; const total = await this.model.countDocuments(filter); const paginationData = { total: total, page: paginationOptions.skip, perPage: paginationOptions.limit, }; return { docs, paginationData }; } async listAll( filter: FilterQuery, options?: { populateArray: any }, ): Promise { filter = { ...crudOptions?.defaultFilter, ...filter }; const queryInstruction = this.model.find(filter); if (options?.populateArray) queryInstruction.populate(options.populateArray); return queryInstruction; } async search( filter: FilterQuery, paginationOptions: { limit?: number; skip?: number; } = { limit: 10, skip: 1, }, options?: { populateArray: any }, ): Promise<{ docs: ModelDoc[]; paginationData: { total: number; page: number; perPage: number; }; }> { filter = { ...crudOptions?.defaultFilter, ...filter }; const queryInstruction = this.model .find(filter) .limit(paginationOptions.limit) .skip(paginationOptions.skip); if (options?.populateArray) queryInstruction.populate(options.populateArray); const docs = await queryInstruction; const total = await this.model.countDocuments(filter); const paginationData = { total: total, page: paginationOptions.skip, perPage: paginationOptions.limit, }; return { docs, paginationData }; } async findOne( filter: FilterQuery, options?: { populateArray: any }): Promise { const queryInstruction = this.model.findOne(filter); if (options?.populateArray) queryInstruction.populate(options.populateArray); const document = await queryInstruction return document; } async findOneOrFail( filter: FilterQuery, options?: { populateArray?: any, selectArray?: any } ): Promise { await this.existsOrThrow(filter); const queryInstruction = this.model.findOne(filter); if (options?.populateArray) queryInstruction.populate(options.populateArray); if (options?.selectArray) queryInstruction.select(options.selectArray); const document = await queryInstruction; return document; } private async existsOrThrow(filter: FilterQuery) { if (!(await this.model.exists(filter))) { throw new HttpError(404, "No Matching Result Found."); } } }; };