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
This commit is contained in:
164
apps/api/src/brain/dto/brain-query.dto.ts
Normal file
164
apps/api/src/brain/dto/brain-query.dto.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
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;
|
||||
}
|
||||
1
apps/api/src/brain/dto/index.ts
Normal file
1
apps/api/src/brain/dto/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { BrainQueryDto, TaskFilter, EventFilter, ProjectFilter, BrainContextDto } from "./brain-query.dto";
|
||||
Reference in New Issue
Block a user