diff --git a/src/assumptions/assumptions.controller.ts b/src/assumptions/assumptions.controller.ts new file mode 100644 index 0000000..8ea4228 --- /dev/null +++ b/src/assumptions/assumptions.controller.ts @@ -0,0 +1,82 @@ +import { Body, Controller, Delete, Get, Param, ParseUUIDPipe, Patch, Post, Query } from '@nestjs/common'; +import { IsIn, IsOptional, IsString, IsUUID } from 'class-validator'; +import { and, desc, eq } from 'drizzle-orm'; +import { CurrentSession } from '../auth/session.decorator'; +import type { AuthenticatedSession } from '../auth/tenant.guard'; +import { db } from '../db/client'; +import { assumptions } from '../db/schema'; + +class CreateAssumptionDto { + @IsString() statement!: string; + @IsOptional() @IsUUID() decisionId?: string; + @IsOptional() @IsString() source?: string; + @IsOptional() @IsIn(['low','medium','high']) confidence?: string; + @IsOptional() @IsString() reviewDate?: string; + @IsOptional() @IsString() notes?: string; +} + +class UpdateAssumptionDto { + @IsOptional() @IsString() statement?: string; + @IsOptional() @IsIn(['low','medium','high']) confidence?: string; + @IsOptional() @IsIn(['unverified','confirmed','refuted','pending_review']) status?: string; + @IsOptional() @IsString() evidenceUrl?: string; + @IsOptional() @IsString() notes?: string; + @IsOptional() @IsString() reviewDate?: string; +} + +@Controller('assumptions') +export class AssumptionsController { + @Get() + async list( + @CurrentSession() session: AuthenticatedSession, + @Query('decisionId') decisionId?: string, + @Query('status') status?: string, + ) { + const tid = session.tenantId; + let where = eq(assumptions.tenantId, tid) as ReturnType; + if (decisionId) where = and(where, eq(assumptions.decisionId, decisionId)) as typeof where; + if (status) where = and(where, eq(assumptions.status, status)) as typeof where; + return db.query.assumptions.findMany({ where, orderBy: [desc(assumptions.createdAt)], limit: 300 }); + } + + @Post() + async create(@CurrentSession() session: AuthenticatedSession, @Body() dto: CreateAssumptionDto) { + const [row] = await db.insert(assumptions).values({ + tenantId: session.tenantId, + statement: dto.statement, + decisionId: dto.decisionId ?? null, + source: dto.source, + confidence: dto.confidence ?? 'medium', + reviewDate: dto.reviewDate ?? null, + notes: dto.notes, + }).returning(); + return row; + } + + @Patch(':id') + async update( + @CurrentSession() session: AuthenticatedSession, + @Param('id', ParseUUIDPipe) id: string, + @Body() dto: UpdateAssumptionDto, + ) { + const upd: Record = { updatedAt: new Date() }; + if (dto.statement !== undefined) upd.statement = dto.statement; + if (dto.confidence !== undefined) upd.confidence = dto.confidence; + if (dto.status !== undefined) upd.status = dto.status; + if (dto.evidenceUrl !== undefined) upd.evidenceUrl = dto.evidenceUrl; + if (dto.notes !== undefined) upd.notes = dto.notes; + if (dto.reviewDate !== undefined) upd.reviewDate = dto.reviewDate; + const [row] = await db.update(assumptions) + .set(upd) + .where(and(eq(assumptions.id, id), eq(assumptions.tenantId, session.tenantId))) + .returning(); + return row; + } + + @Delete(':id') + async remove(@CurrentSession() session: AuthenticatedSession, @Param('id', ParseUUIDPipe) id: string) { + await db.delete(assumptions) + .where(and(eq(assumptions.id, id), eq(assumptions.tenantId, session.tenantId))); + return { deleted: true }; + } +}