import { PrismaService } from "@cv/system"; import { Injectable } from "@nestjs/common"; /** * Draft entity representation - ID is null if no match found */ export interface DraftEntity { id: string | null; name: string; } /** * Resolved job experience with draft entities */ export interface ResolvedJobExperience { company: DraftEntity; role: DraftEntity; level: DraftEntity; skills: DraftEntity[]; startDate: Date; endDate: Date | null; description: string | null; } /** * Resolved education with draft entities */ export interface ResolvedEducation { institution: DraftEntity; degree: string; fieldOfStudy: string | null; skills: DraftEntity[]; startDate: Date; endDate: Date | null; description: string | null; } /** * Service for resolving entity names to existing database entities * Returns stubs with null IDs for unmatched names */ @Injectable() export class EntityResolverService { constructor(private readonly prisma: PrismaService) {} /** * Resolve a company name to an existing entity */ async resolveCompany(name: string): Promise { const match = await this.prisma.company.findFirst({ where: { name: { equals: name, mode: "insensitive" } }, }); return match ? { id: match.id, name: match.name } : { id: null, name }; } /** * Resolve a role name to an existing entity */ async resolveRole(name: string): Promise { const match = await this.prisma.role.findFirst({ where: { name: { equals: name, mode: "insensitive" } }, }); return match ? { id: match.id, name: match.name } : { id: null, name }; } /** * Resolve a level name to an existing entity * Falls back to "Mid-level" if not provided */ async resolveLevel(name?: string): Promise { const searchName = name || "Mid-level"; const match = await this.prisma.level.findFirst({ where: { name: { equals: searchName, mode: "insensitive" } }, }); return match ? { id: match.id, name: match.name } : { id: null, name: searchName }; } /** * Resolve a skill name to an existing entity */ async resolveSkill(name: string): Promise { const trimmed = name.trim(); if (!trimmed) { return { id: null, name: "" }; } const match = await this.prisma.skill.findFirst({ where: { name: { equals: trimmed, mode: "insensitive" } }, }); return match ? { id: match.id, name: match.name } : { id: null, name: trimmed }; } /** * Resolve an institution name to an existing entity */ async resolveInstitution(name: string): Promise { const match = await this.prisma.institution.findFirst({ where: { name: { equals: name, mode: "insensitive" } }, }); return match ? { id: match.id, name: match.name } : { id: null, name }; } /** * Resolve multiple skills in parallel */ async resolveSkills(names: string[]): Promise { const uniqueNames = [...new Set(names.filter((s) => s?.trim()))]; return Promise.all(uniqueNames.map((name) => this.resolveSkill(name))); } }