/** * Drizzle Task Repository Adapter * * Implements TaskRepository interface using Drizzle ORM. */ import { eq, asc, and } from 'drizzle-orm'; import { nanoid } from 'nanoid'; import type { DrizzleDatabase } from '../../index.js'; import { tasks, taskDependencies, type Task } from '../../schema.js'; import type { TaskRepository, CreateTaskData, UpdateTaskData, PendingApprovalFilters, } from '../task-repository.js'; /** * Drizzle adapter for TaskRepository. * * Uses dependency injection for database instance, * enabling isolated test databases. */ export class DrizzleTaskRepository implements TaskRepository { constructor(private db: DrizzleDatabase) {} async create(data: CreateTaskData): Promise { const id = nanoid(); const now = new Date(); const [created] = await this.db.insert(tasks).values({ id, ...data, type: data.type ?? 'auto', category: data.category ?? 'execute', priority: data.priority ?? 'medium', status: data.status ?? 'pending', order: data.order ?? 0, createdAt: now, updatedAt: now, }).returning(); return created; } async findById(id: string): Promise { const result = await this.db .select() .from(tasks) .where(eq(tasks.id, id)) .limit(1); return result[0] ?? null; } async findByParentTaskId(parentTaskId: string): Promise { return this.db .select() .from(tasks) .where(eq(tasks.parentTaskId, parentTaskId)) .orderBy(asc(tasks.order)); } async findByInitiativeId(initiativeId: string): Promise { return this.db .select() .from(tasks) .where(eq(tasks.initiativeId, initiativeId)) .orderBy(asc(tasks.order)); } async findByPhaseId(phaseId: string): Promise { return this.db .select() .from(tasks) .where(eq(tasks.phaseId, phaseId)) .orderBy(asc(tasks.order)); } async findPendingApproval(filters?: PendingApprovalFilters): Promise { const conditions = [eq(tasks.status, 'pending_approval')]; if (filters?.initiativeId) { conditions.push(eq(tasks.initiativeId, filters.initiativeId)); } if (filters?.phaseId) { conditions.push(eq(tasks.phaseId, filters.phaseId)); } if (filters?.category) { conditions.push(eq(tasks.category, filters.category)); } return this.db .select() .from(tasks) .where(and(...conditions)) .orderBy(asc(tasks.createdAt)); } async update(id: string, data: UpdateTaskData): Promise { const [updated] = await this.db .update(tasks) .set({ ...data, updatedAt: new Date() }) .where(eq(tasks.id, id)) .returning(); if (!updated) { throw new Error(`Task not found: ${id}`); } return updated; } async delete(id: string): Promise { const [deleted] = await this.db.delete(tasks).where(eq(tasks.id, id)).returning(); if (!deleted) { throw new Error(`Task not found: ${id}`); } } async createDependency(taskId: string, dependsOnTaskId: string): Promise { const id = nanoid(); const now = new Date(); await this.db.insert(taskDependencies).values({ id, taskId, dependsOnTaskId, createdAt: now, }); } async getDependencies(taskId: string): Promise { const deps = await this.db .select({ dependsOnTaskId: taskDependencies.dependsOnTaskId }) .from(taskDependencies) .where(eq(taskDependencies.taskId, taskId)); return deps.map((d) => d.dependsOnTaskId); } }