feat(#167): Implement Runner jobs CRUD and queue submission

Implements runner-jobs module for job lifecycle management and queue submission.

Changes:
- Created RunnerJobsModule with service, controller, and DTOs
- Implemented job creation with BullMQ queue submission
- Implemented job listing with filters (status, type, agentTaskId)
- Implemented job detail retrieval with steps and events
- Implemented cancel operation for pending/queued jobs
- Implemented retry operation for failed jobs
- Added comprehensive unit tests (24 tests, 100% coverage)
- Integrated with BullMQ for async job processing
- Integrated with Prisma for database operations
- Followed existing CRUD patterns from tasks/events modules

API Endpoints:
- POST /runner-jobs - Create and queue a new job
- GET /runner-jobs - List jobs (with filters)
- GET /runner-jobs/:id - Get job details
- POST /runner-jobs/:id/cancel - Cancel a running job
- POST /runner-jobs/:id/retry - Retry a failed job

Quality Gates:
- Typecheck:  PASSED
- Lint:  PASSED
- Build:  PASSED
- Tests:  PASSED (24/24 tests)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-01 21:09:03 -06:00
parent a2cd614e87
commit 7102b4a1d2
73 changed files with 2498 additions and 45 deletions

View File

@@ -0,0 +1,90 @@
import { Controller, Get, Post, Body, Param, Query, UseGuards } from "@nestjs/common";
import { RunnerJobsService } from "./runner-jobs.service";
import { CreateJobDto, QueryJobsDto } from "./dto";
import { AuthGuard } from "../auth/guards/auth.guard";
import { WorkspaceGuard, PermissionGuard } from "../common/guards";
import { Workspace, Permission, RequirePermission } from "../common/decorators";
import { CurrentUser } from "../auth/decorators/current-user.decorator";
import type { AuthenticatedUser } from "../common/types/user.types";
/**
* Controller for runner job endpoints
* All endpoints require authentication and workspace context
*
* Guards are applied in order:
* 1. AuthGuard - Verifies user authentication
* 2. WorkspaceGuard - Validates workspace access and sets RLS context
* 3. PermissionGuard - Checks role-based permissions
*/
@Controller("runner-jobs")
@UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard)
export class RunnerJobsController {
constructor(private readonly runnerJobsService: RunnerJobsService) {}
/**
* POST /api/runner-jobs
* Create a new runner job and queue it
* Requires: MEMBER role or higher
*/
@Post()
@RequirePermission(Permission.WORKSPACE_MEMBER)
async create(
@Body() createJobDto: CreateJobDto,
@Workspace() workspaceId: string,
@CurrentUser() _user: AuthenticatedUser
) {
return this.runnerJobsService.create(workspaceId, createJobDto);
}
/**
* GET /api/runner-jobs
* Get paginated jobs with optional filters
* Requires: Any workspace member (including GUEST)
*/
@Get()
@RequirePermission(Permission.WORKSPACE_ANY)
async findAll(@Query() query: QueryJobsDto, @Workspace() workspaceId: string) {
return this.runnerJobsService.findAll(Object.assign({}, query, { workspaceId }));
}
/**
* GET /api/runner-jobs/:id
* Get a single job by ID
* Requires: Any workspace member
*/
@Get(":id")
@RequirePermission(Permission.WORKSPACE_ANY)
async findOne(@Param("id") id: string, @Workspace() workspaceId: string) {
return this.runnerJobsService.findOne(id, workspaceId);
}
/**
* POST /api/runner-jobs/:id/cancel
* Cancel a running or queued job
* Requires: MEMBER role or higher
*/
@Post(":id/cancel")
@RequirePermission(Permission.WORKSPACE_MEMBER)
async cancel(
@Param("id") id: string,
@Workspace() workspaceId: string,
@CurrentUser() _user: AuthenticatedUser
) {
return this.runnerJobsService.cancel(id, workspaceId);
}
/**
* POST /api/runner-jobs/:id/retry
* Retry a failed job
* Requires: MEMBER role or higher
*/
@Post(":id/retry")
@RequirePermission(Permission.WORKSPACE_MEMBER)
async retry(
@Param("id") id: string,
@Workspace() workspaceId: string,
@CurrentUser() _user: AuthenticatedUser
) {
return this.runnerJobsService.retry(id, workspaceId);
}
}