import { NotFoundException } from '@nestjs/common'; import { BaseQueryCoreService } from 'src/core/base-query-core/base-query-core.service'; import { BaseQueryCoreDto, CoreIncludesDto, } from 'src/core/base-query-core/dto/base-query-core.dto'; import { safePrismaCall } from 'src/shared/modules/prisma/safe-prisma-call'; type ErrorMessageType = 'NOT_FOUND' | 'DELETED'; export type ServiceErrorMessage = { [key in ErrorMessageType]: string; }; export abstract class PrismaBaseRepository< ModelEntity, ModelPaginate, CreateArgs, UpdateArgs extends { where: Record }, UpdateManyArgs extends { where?: Record }, FindUniqueArgs extends { where: Record }, FindFirstArgs extends { where?: Record }, FindManyArgs extends { where?: Record; skip?: number; take?: number; }, DeleteArgs extends { where: Record }, DeleteManyArgs extends { where?: Record }, CountArgs extends { where?: Record }, UpsertArgs extends { where: Record; create: Record; update: Record; }, > { constructor( private readonly repo: any, public errorMessage: ServiceErrorMessage, public baseQueryCoreService: any = new BaseQueryCoreService(), ) {} async findPaginate( query: BaseQueryCoreDto, baseWhere = {}, ): Promise { const updatedQuery = this.baseQueryCoreService.generatePrismaQuery(query); const { skip = 0, take = 10, where = {}, ...rest } = updatedQuery; let list = await safePrismaCall(() => this.repo.findMany({ skip, take: take + 1, where: { ...where, ...baseWhere, }, ...rest, }), ); const total = await safePrismaCall(() => this.repo.count({ where: { ...where, ...baseWhere, }, }), ); let hasMany = false; if (list.length > take) { hasMany = true; list = list.slice(0, -1); } return { total, list, hasMany, count: list.length, }; } async findPaginateV2( query: BaseQueryCoreDto, baseWhere = {}, ): Promise { const updatedQuery = this.baseQueryCoreService.generatePrismaQuery(query); const { where = {}, ...rest } = updatedQuery; const list = await safePrismaCall(() => this.repo.findMany({ where: { ...where, ...baseWhere, }, ...rest, }), ); return { list, count: list.length, }; } async checkId(params: { where: any }): Promise { return safePrismaCall(() => this.repo.findUnique({ where: params.where }), ).catch(() => { throw new NotFoundException(this.errorMessage.NOT_FOUND); }); } create(params: CreateArgs): Promise { return safePrismaCall(() => this.repo.create(params)); } async getCount(params: CountArgs): Promise { return safePrismaCall(() => this.repo.count(params)); } async update(params: UpdateArgs): Promise { await this.checkId({ where: params.where }); return safePrismaCall(() => this.repo.update(params)); } updateMany(params: UpdateManyArgs): Promise { return safePrismaCall(() => this.repo.updateMany(params), ) as unknown as Promise; } async findUnique(params: FindUniqueArgs): Promise { const returnData = await safePrismaCall(() => this.repo.findUnique(params), ).catch(() => { throw new NotFoundException(this.errorMessage.NOT_FOUND); }); if (returnData?.isDeleted) { throw new NotFoundException(this.errorMessage.DELETED); } return returnData; } findFirst(params: FindFirstArgs): Promise { return safePrismaCall(() => this.repo.findFirst(params)).catch(() => { throw new NotFoundException(this.errorMessage.NOT_FOUND); }); } findMany(params: FindManyArgs): Promise { return safePrismaCall(() => this.repo.findMany(params)); } deleteMany(params: DeleteManyArgs): Promise { return safePrismaCall(() => this.repo.deleteMany(params), ) as unknown as Promise; } async delete(params: DeleteArgs): Promise { await this.checkId({ where: params.where }); return safePrismaCall(() => this.repo.delete(params)); } async findUniqueIncludes( params: FindUniqueArgs, query: CoreIncludesDto, ): Promise { const updatedQuery = this.baseQueryCoreService.generatePrismaQuery(query); // Automatically add isDeleted: false filter to related entities if (updatedQuery.include) { updatedQuery.include = this.addDeletedFilterToIncludes( updatedQuery.include, ); } const returnData = await safePrismaCall(() => this.repo.findUnique({ ...params, ...updatedQuery }), ).catch(() => { throw new NotFoundException(this.errorMessage.NOT_FOUND); }); if (returnData?.isDeleted) { throw new NotFoundException(this.errorMessage.DELETED); } return returnData; } /** * Helper method to automatically add isDeleted: false filter to related entities */ private addDeletedFilterToIncludes(include: any): any { if (!include) return include; const result = { ...include }; // List of relations that have isDeleted field const relationsWithDeletedField = [ 'sessions', 'credentials', 'user', 'superAdmin', ]; for (const key in result) { if (result.hasOwnProperty(key)) { if (relationsWithDeletedField.includes(key)) { // Add isDeleted: false filter for this relation if (result[key] === true) { result[key] = { where: { isDeleted: false, }, }; } else if (typeof result[key] === 'object' && result[key] !== null) { // If it's already an object, merge the where condition if (!result[key].where) { result[key].where = {}; } result[key].where.isDeleted = false; // Recursively apply to nested includes if (result[key].include) { result[key].include = this.addDeletedFilterToIncludes( result[key].include, ); } } } else if (typeof result[key] === 'object' && result[key] !== null) { // Recursively apply to nested includes result[key] = this.addDeletedFilterToIncludes(result[key]); } } } return result; } async findFirstIncludes( params: FindFirstArgs, query: CoreIncludesDto, ): Promise { const updatedQuery = this.baseQueryCoreService.generatePrismaQuery(query); // Automatically add isDeleted: false filter to related entities if (updatedQuery.include) { updatedQuery.include = this.addDeletedFilterToIncludes( updatedQuery.include, ); } const returnData = await safePrismaCall(() => this.repo.findFirst({ ...params, ...updatedQuery }), ).catch(() => { throw new NotFoundException(this.errorMessage.NOT_FOUND); }); if (returnData?.isDeleted) { throw new NotFoundException(this.errorMessage.DELETED); } return returnData; } async findManyIncludes( params: FindManyArgs, query: CoreIncludesDto, ): Promise { const updatedQuery = this.baseQueryCoreService.generatePrismaQuery(query); return safePrismaCall(() => this.repo.findMany({ ...params, ...updatedQuery }), ); } async upsert(params: UpsertArgs): Promise { return safePrismaCall(() => this.repo.upsert(params)); } }