import { Injectable } from '@nestjs/common'; import { Prisma } from '@prisma/client'; import { DatabaseException } from '../../exceptions/database.exception'; import { ICollection } from '../../interfaces/collection.interface'; import { IRepository } from '../../interfaces/repository.interface'; import { PrismaService } from './prisma-service'; /** * Child classes MUST redefined _model property with appropriate model name */ @Injectable() export abstract class PrismaRepository implements IRepository { protected model: string; constructor(protected readonly _prisma: PrismaService) {} async findAll( page = 1, perPage = 10, where?: any, include?: any, ): Promise> { const [data, total] = await this._prisma.$transaction([ this._prisma[this.model].findMany({ where, include, skip: (page - 1) * perPage, take: perPage, }), this._prisma[this.model].count({ where, }), ]); return Promise.resolve({ data, total, }); } async findOneByUuid(uuid: string): Promise { try { const entity = await this._prisma[this.model].findUnique({ where: { uuid }, }); return entity; } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseException( Prisma.PrismaClientKnownRequestError.name, e.code, e.message, ); } else { throw new DatabaseException(); } } } async findOne(where: any, include?: any): Promise { try { const entity = await this._prisma[this.model].findFirst({ where: where, include: include, }); return entity; } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseException( Prisma.PrismaClientKnownRequestError.name, e.code, ); } else { throw new DatabaseException(); } } } // TODO : using any is not good, but needed for nested entities // TODO : Refactor for good clean architecture ? async create(entity: Partial | any, include?: any): Promise { try { const res = await this._prisma[this.model].create({ data: entity, include: include, }); return res; } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseException( Prisma.PrismaClientKnownRequestError.name, e.code, e.message, ); } else { throw new DatabaseException(); } } } async update(uuid: string, entity: Partial): Promise { try { const updatedEntity = await this._prisma[this.model].update({ where: { uuid }, data: entity, }); return updatedEntity; } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseException( Prisma.PrismaClientKnownRequestError.name, e.code, e.message, ); } else { throw new DatabaseException(); } } } async updateWhere( where: any, entity: Partial | any, include?: any, ): Promise { try { const updatedEntity = await this._prisma[this.model].update({ where: where, data: entity, include: include, }); return updatedEntity; } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseException( Prisma.PrismaClientKnownRequestError.name, e.code, e.message, ); } else { throw new DatabaseException(); } } } async delete(uuid: string): Promise { try { const entity = await this._prisma[this.model].delete({ where: { uuid }, }); return entity; } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseException( Prisma.PrismaClientKnownRequestError.name, e.code, e.message, ); } else { throw new DatabaseException(); } } } async deleteMany(where: any): Promise { try { const entity = await this._prisma[this.model].deleteMany({ where: where, }); return entity; } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseException( Prisma.PrismaClientKnownRequestError.name, e.code, e.message, ); } else { throw new DatabaseException(); } } } async findAllByQuery( include: string[], where: string[], ): Promise> { const query = `SELECT ${include.join(',')} FROM ${ this.model } WHERE ${where.join(' AND ')}`; const data: T[] = await this._prisma.$queryRawUnsafe(query); return Promise.resolve({ data, total: data.length, }); } async createWithFields(fields: object): Promise { try { const command = `INSERT INTO ${this.model} ("${Object.keys(fields).join( '","', )}") VALUES (${Object.values(fields).join(',')})`; return await this._prisma.$executeRawUnsafe(command); } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseException( Prisma.PrismaClientKnownRequestError.name, e.code, e.message, ); } else { throw new DatabaseException(); } } } async updateWithFields(uuid: string, entity: object): Promise { entity['"updatedAt"'] = `to_timestamp(${Date.now()} / 1000.0)`; const values = Object.keys(entity).map((key) => `${key} = ${entity[key]}`); try { const command = `UPDATE ${this.model} SET ${values.join( ', ', )} WHERE uuid = '${uuid}'`; return await this._prisma.$executeRawUnsafe(command); } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseException( Prisma.PrismaClientKnownRequestError.name, e.code, e.message, ); } else { throw new DatabaseException(); } } } async healthCheck(): Promise { try { await this._prisma.$queryRaw`SELECT 1`; return true; } catch (e) { if (e instanceof Prisma.PrismaClientKnownRequestError) { throw new DatabaseException( Prisma.PrismaClientKnownRequestError.name, e.code, e.message, ); } else { throw new DatabaseException(); } } } }