feat(CC-067): add AssumptionsController (CRUD with confidence + status)

This commit is contained in:
admin-valentin 2026-08-02 12:12:58 +00:00
parent cbeb3b35a9
commit f80f8e6c48

View file

@ -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<typeof eq>;
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<string, unknown> = { 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 };
}
}