Files
stack/apps/api/src/brain/dto/brain-query.dto.ts
Jason Woltje 1bd21b33d7 feat(#22): implement brain query API
- Create brain module with service, controller, and DTOs
- POST /api/brain/query - Structured queries for tasks, events, projects
- GET /api/brain/context - Get current workspace context for agents
- GET /api/brain/search - Search across all entities
- Support filters: status, priority, date ranges, assignee, etc.
- 41 tests covering service (27) and controller (14)
- Integrated with AuthGuard, WorkspaceGuard, PermissionGuard
2026-01-29 19:40:30 -06:00

165 lines
3.6 KiB
TypeScript

import { TaskStatus, TaskPriority, ProjectStatus, EntityType } from "@prisma/client";
import {
IsUUID,
IsEnum,
IsOptional,
IsString,
IsInt,
Min,
Max,
IsDateString,
IsArray,
ValidateNested,
IsBoolean,
} from "class-validator";
import { Type } from "class-transformer";
export class TaskFilter {
@IsOptional()
@IsEnum(TaskStatus, { message: "status must be a valid TaskStatus" })
status?: TaskStatus;
@IsOptional()
@IsArray()
@IsEnum(TaskStatus, { each: true, message: "statuses must be valid TaskStatus values" })
statuses?: TaskStatus[];
@IsOptional()
@IsEnum(TaskPriority, { message: "priority must be a valid TaskPriority" })
priority?: TaskPriority;
@IsOptional()
@IsArray()
@IsEnum(TaskPriority, { each: true, message: "priorities must be valid TaskPriority values" })
priorities?: TaskPriority[];
@IsOptional()
@IsUUID("4", { message: "assigneeId must be a valid UUID" })
assigneeId?: string;
@IsOptional()
@IsUUID("4", { message: "projectId must be a valid UUID" })
projectId?: string;
@IsOptional()
@IsDateString({}, { message: "dueDateFrom must be a valid ISO 8601 date string" })
dueDateFrom?: Date;
@IsOptional()
@IsDateString({}, { message: "dueDateTo must be a valid ISO 8601 date string" })
dueDateTo?: Date;
@IsOptional()
@IsBoolean()
overdue?: boolean;
@IsOptional()
@IsBoolean()
unassigned?: boolean;
}
export class EventFilter {
@IsOptional()
@IsUUID("4", { message: "projectId must be a valid UUID" })
projectId?: string;
@IsOptional()
@IsDateString({}, { message: "startFrom must be a valid ISO 8601 date string" })
startFrom?: Date;
@IsOptional()
@IsDateString({}, { message: "startTo must be a valid ISO 8601 date string" })
startTo?: Date;
@IsOptional()
@IsBoolean()
allDay?: boolean;
@IsOptional()
@IsBoolean()
upcoming?: boolean;
}
export class ProjectFilter {
@IsOptional()
@IsEnum(ProjectStatus, { message: "status must be a valid ProjectStatus" })
status?: ProjectStatus;
@IsOptional()
@IsArray()
@IsEnum(ProjectStatus, { each: true, message: "statuses must be valid ProjectStatus values" })
statuses?: ProjectStatus[];
@IsOptional()
@IsDateString({}, { message: "startDateFrom must be a valid ISO 8601 date string" })
startDateFrom?: Date;
@IsOptional()
@IsDateString({}, { message: "startDateTo must be a valid ISO 8601 date string" })
startDateTo?: Date;
}
export class BrainQueryDto {
@IsUUID("4", { message: "workspaceId must be a valid UUID" })
workspaceId!: string;
@IsOptional()
@IsString()
query?: string;
@IsOptional()
@IsArray()
@IsEnum(EntityType, { each: true, message: "entities must be valid EntityType values" })
entities?: EntityType[];
@IsOptional()
@ValidateNested()
@Type(() => TaskFilter)
tasks?: TaskFilter;
@IsOptional()
@ValidateNested()
@Type(() => EventFilter)
events?: EventFilter;
@IsOptional()
@ValidateNested()
@Type(() => ProjectFilter)
projects?: ProjectFilter;
@IsOptional()
@IsString()
search?: string;
@IsOptional()
@Type(() => Number)
@IsInt({ message: "limit must be an integer" })
@Min(1, { message: "limit must be at least 1" })
@Max(100, { message: "limit must not exceed 100" })
limit?: number;
}
export class BrainContextDto {
@IsUUID("4", { message: "workspaceId must be a valid UUID" })
workspaceId!: string;
@IsOptional()
@IsBoolean()
includeEvents?: boolean;
@IsOptional()
@IsBoolean()
includeTasks?: boolean;
@IsOptional()
@IsBoolean()
includeProjects?: boolean;
@IsOptional()
@Type(() => Number)
@IsInt()
@Min(1)
@Max(30)
eventDays?: number;
}