/** * Drizzle Phase Repository Adapter * * Implements PhaseRepository interface using Drizzle ORM. */ import { eq, asc, and } from 'drizzle-orm'; import { nanoid } from 'nanoid'; import type { DrizzleDatabase } from '../../index.js'; import { phases, phaseDependencies, type Phase } from '../../schema.js'; import type { PhaseRepository, CreatePhaseData, UpdatePhaseData, } from '../phase-repository.js'; /** * Drizzle adapter for PhaseRepository. * * Uses dependency injection for database instance, * enabling isolated test databases. */ export class DrizzlePhaseRepository implements PhaseRepository { constructor(private db: DrizzleDatabase) {} async create(data: CreatePhaseData): Promise { const { id: providedId, ...rest } = data; const id = providedId ?? nanoid(); const now = new Date(); const [created] = await this.db.insert(phases).values({ id, ...rest, status: data.status ?? 'pending', createdAt: now, updatedAt: now, }).returning(); return created; } async findById(id: string): Promise { const result = await this.db .select() .from(phases) .where(eq(phases.id, id)) .limit(1); return result[0] ?? null; } async findByInitiativeId(initiativeId: string): Promise { return this.db .select() .from(phases) .where(eq(phases.initiativeId, initiativeId)) .orderBy(asc(phases.createdAt)); } async findDependenciesByInitiativeId(initiativeId: string): Promise> { return this.db .select({ phaseId: phaseDependencies.phaseId, dependsOnPhaseId: phaseDependencies.dependsOnPhaseId }) .from(phaseDependencies) .innerJoin(phases, eq(phaseDependencies.phaseId, phases.id)) .where(eq(phases.initiativeId, initiativeId)); } async update(id: string, data: UpdatePhaseData): Promise { const [updated] = await this.db .update(phases) .set({ ...data, updatedAt: new Date() }) .where(eq(phases.id, id)) .returning(); if (!updated) { throw new Error(`Phase not found: ${id}`); } return updated; } async delete(id: string): Promise { const [deleted] = await this.db.delete(phases).where(eq(phases.id, id)).returning(); if (!deleted) { throw new Error(`Phase not found: ${id}`); } } async createDependency(phaseId: string, dependsOnPhaseId: string): Promise { const id = nanoid(); const now = new Date(); await this.db.insert(phaseDependencies).values({ id, phaseId, dependsOnPhaseId, createdAt: now, }); } async getDependencies(phaseId: string): Promise { const result = await this.db .select({ dependsOnPhaseId: phaseDependencies.dependsOnPhaseId }) .from(phaseDependencies) .where(eq(phaseDependencies.phaseId, phaseId)); return result.map((row) => row.dependsOnPhaseId); } async getDependents(phaseId: string): Promise { const result = await this.db .select({ phaseId: phaseDependencies.phaseId }) .from(phaseDependencies) .where(eq(phaseDependencies.dependsOnPhaseId, phaseId)); return result.map((row) => row.phaseId); } async removeDependency(phaseId: string, dependsOnPhaseId: string): Promise { await this.db .delete(phaseDependencies) .where( and( eq(phaseDependencies.phaseId, phaseId), eq(phaseDependencies.dependsOnPhaseId, dependsOnPhaseId), ), ); } }