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) {} findAll = async ( 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, }); }; findOneByUuid = async (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(); } } }; findOne = async (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(); } } } update = async (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(); } } }; updateWhere = async ( 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(); } } }; delete = async (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(); } } }; deleteMany = async (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(); } } }; findAllByQuery = async ( 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, }); }; createWithFields = async (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(); } } }; updateWithFields = async (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(); } } }; healthCheck = async (): 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(); } } }; }