import { notFound } from "@cv/core"; import { type EntityService, PrismaService } from "@cv/core"; import { Injectable } from "@nestjs/common"; import { Prisma } from "@prisma/client"; import { Application } from "./application.entity"; import { DuplicateApplicationError } from "./application.error"; import { ApplicationMapper } from "./application.mapper"; type ApplicationFilters = { userId?: string; id?: string | string[]; }; type ApplicationWhereInput = { userId?: string; id?: { in: string[] } | string; }; @Injectable() export class ApplicationService implements EntityService { readonly applicationInclude = { user: true, vacancy: { include: { company: true, role: true, level: true, jobType: true, skills: true, }, }, status: true, cv: { include: { template: true, }, }, } satisfies Prisma.ApplicationInclude; constructor( private readonly prisma: PrismaService, private readonly applicationMapper: ApplicationMapper, ) {} async findById(id: string): Promise { const application = await this.prisma.application.findUnique({ where: { id }, include: this.applicationInclude, }); return application ? this.applicationMapper.toDomain(application) : null; } async findByIdOrFail(id: string): Promise { const application = await this.findById(id); return application ?? notFound("Application", "id", id); } private buildWhere(filters: ApplicationFilters = {}): ApplicationWhereInput { const where: ApplicationWhereInput = {}; if (filters.userId) { where.userId = filters.userId; } if (filters.id !== undefined) { where.id = Array.isArray(filters.id) ? { in: filters.id } : filters.id; } return where; } async findMany(filters: ApplicationFilters = {}): Promise { const applications = await this.prisma.application.findMany({ where: this.buildWhere(filters), include: this.applicationInclude, orderBy: { appliedAt: "desc" }, }); return this.applicationMapper.mapToDomainWithRelations(applications); } async count(filters: ApplicationFilters = {}): Promise { return this.prisma.application.count({ where: this.buildWhere(filters) }); } private buildPrismaData(entity: Application): { statusId: string; cvId: string | null; coverLetter: string | null; } { return { statusId: entity.statusId, cvId: entity.cv?.id ?? null, coverLetter: entity.coverLetter ?? null, }; } async save(entity: Application): Promise { const data = this.buildPrismaData(entity); try { const application = await this.prisma.application.upsert({ where: { id: entity.id }, create: { id: entity.id, userId: entity.userId, profileId: entity.profileId, vacancyId: entity.vacancy.id, ...data, }, update: data, include: this.applicationInclude, }); const domain = this.applicationMapper.toDomain(application); if (!domain) { throw new Error("Failed to map application to domain"); } return domain; } catch (error: unknown) { if ( error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002" && error.meta && typeof error.meta === "object" && "target" in error.meta && Array.isArray(error.meta["target"]) && error.meta["target"].includes("userId") && error.meta["target"].includes("vacancyId") ) { throw new DuplicateApplicationError(); } throw error; } } async destroy(entity: Application): Promise { await this.prisma.application.delete({ where: { id: entity.id }, }); } }