import { Body, Controller, Delete, Get, HttpCode, Param, ParseUUIDPipe, Patch, Post, Query, } from "@nestjs/common"; import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; import { ScenarioService } from "./scenario.service"; import { CreateScenarioDto } from "./dto/create-scenario.dto"; import { UpdateScenarioDto } from "./dto/update-scenario.dto"; import { CreateScenarioStepDto } from "./dto/create-scenario-step.dto"; import { UpdateScenarioStepDto } from "./dto/update-scenario-step.dto"; import { AddScenarioCredentialDto } from "./dto/add-scenario-credential.dto"; import { PaginationQueryDto } from "./dto/pagination-query.dto"; import { ScenarioOrderBy } from "./scenario.service"; import { RunsQueryDto } from "./dto/runs-query.dto"; import { ScenarioExportDto } from "./dto/scenario-export.dto"; @ApiTags("scenarios") @Controller("scenarios") export class ScenarioController { constructor(private readonly scenarioService: ScenarioService) {} // ── Scenarios ───────────────────────────────────────────────────────────── @Post() @ApiOperation({ summary: "Create a scenario" }) @ApiResponse({ status: 201, description: "Scenario created" }) create(@Body() dto: CreateScenarioDto) { return this.scenarioService.create(dto); } @Post("import") @ApiOperation({ summary: "Import a scenario from an export payload" }) @ApiResponse({ status: 201, description: "Scenario imported" }) importScenario(@Body() dto: ScenarioExportDto) { return this.scenarioService.importScenario(dto); } @Get() @ApiOperation({ summary: "List all scenarios (paginated)" }) @ApiResponse({ status: 200 }) findAll(@Query() query: PaginationQueryDto) { return this.scenarioService.findAll(query); } @Get("runs") @ApiOperation({ summary: "List runs across all scenarios (paginated, filterable by status)" }) @ApiResponse({ status: 200 }) findAllRuns(@Query() query: RunsQueryDto) { return this.scenarioService.findAllRuns(query); } @Get(":id") @ApiOperation({ summary: "Get a scenario with its steps" }) @ApiResponse({ status: 200 }) @ApiResponse({ status: 404, description: "Scenario not found" }) findOne(@Param("id", ParseUUIDPipe) id: string) { return this.scenarioService.findOne(id); } @Patch(":id") @ApiOperation({ summary: "Update a scenario" }) @ApiResponse({ status: 200 }) @ApiResponse({ status: 404, description: "Scenario not found" }) update( @Param("id", ParseUUIDPipe) id: string, @Body() dto: UpdateScenarioDto, ) { return this.scenarioService.update(id, dto); } @Delete(":id") @HttpCode(204) @ApiOperation({ summary: "Delete a scenario and all its steps" }) @ApiResponse({ status: 204 }) @ApiResponse({ status: 404, description: "Scenario not found" }) remove(@Param("id", ParseUUIDPipe) id: string) { return this.scenarioService.remove(id); } @Get(":id/export") @ApiOperation({ summary: "Export a scenario as a portable JSON payload" }) @ApiResponse({ status: 200 }) @ApiResponse({ status: 404, description: "Scenario not found" }) exportScenario(@Param("id", ParseUUIDPipe) id: string) { return this.scenarioService.exportScenario(id); } // ── Steps ───────────────────────────────────────────────────────────────── @Post(":id/steps") @ApiOperation({ summary: "Add a step to a scenario" }) @ApiResponse({ status: 201, description: "Step created" }) @ApiResponse({ status: 404, description: "Scenario not found" }) createStep( @Param("id", ParseUUIDPipe) id: string, @Body() dto: CreateScenarioStepDto, ) { return this.scenarioService.createStep(id, dto); } @Get(":id/steps/:stepId") @ApiOperation({ summary: "Get a single step" }) @ApiResponse({ status: 200 }) @ApiResponse({ status: 404, description: "Scenario or step not found" }) findStep( @Param("id", ParseUUIDPipe) id: string, @Param("stepId", ParseUUIDPipe) stepId: string, ) { return this.scenarioService.findStep(id, stepId); } @Patch(":id/steps/:stepId") @ApiOperation({ summary: "Update a step" }) @ApiResponse({ status: 200 }) @ApiResponse({ status: 404, description: "Scenario or step not found" }) updateStep( @Param("id", ParseUUIDPipe) id: string, @Param("stepId", ParseUUIDPipe) stepId: string, @Body() dto: UpdateScenarioStepDto, ) { return this.scenarioService.updateStep(id, stepId, dto); } @Delete(":id/steps/:stepId") @HttpCode(204) @ApiOperation({ summary: "Delete a step" }) @ApiResponse({ status: 204 }) @ApiResponse({ status: 404, description: "Scenario or step not found" }) removeStep( @Param("id", ParseUUIDPipe) id: string, @Param("stepId", ParseUUIDPipe) stepId: string, ) { return this.scenarioService.removeStep(id, stepId); } // ── Credentials ─────────────────────────────────────────────────────────── @Get(":id/credentials") @ApiOperation({ summary: "List credentials assigned to a scenario" }) @ApiResponse({ status: 200 }) @ApiResponse({ status: 404, description: "Scenario not found" }) findScenarioCredentials(@Param("id", ParseUUIDPipe) id: string) { return this.scenarioService.findScenarioCredentials(id); } @Post(":id/credentials") @ApiOperation({ summary: "Add a credential to a scenario with an alias" }) @ApiResponse({ status: 201, description: "Credential added" }) @ApiResponse({ status: 404, description: "Scenario or credential not found" }) @ApiResponse({ status: 409, description: "Alias already used in this scenario" }) addScenarioCredential( @Param("id", ParseUUIDPipe) id: string, @Body() dto: AddScenarioCredentialDto, ) { return this.scenarioService.addScenarioCredential(id, dto); } @Delete(":id/credentials/:scCredId") @HttpCode(204) @ApiOperation({ summary: "Remove a credential from a scenario" }) @ApiResponse({ status: 204 }) @ApiResponse({ status: 404, description: "Scenario or credential assignment not found" }) removeScenarioCredential( @Param("id", ParseUUIDPipe) id: string, @Param("scCredId", ParseUUIDPipe) scCredId: string, ) { return this.scenarioService.removeScenarioCredential(id, scCredId); } // ── Runs ────────────────────────────────────────────────────────────────── @Get(":id/runs") @ApiOperation({ summary: "List runs for a scenario (paginated, filterable by status)", }) @ApiResponse({ status: 200 }) @ApiResponse({ status: 404, description: "Scenario not found" }) findRuns( @Param("id", ParseUUIDPipe) id: string, @Query() query: RunsQueryDto, ) { return this.scenarioService.findRuns(id, query); } @Post(":id/run") @ApiOperation({ summary: "Create a new run for a scenario" }) @ApiResponse({ status: 201, description: "Run created with step runs" }) @ApiResponse({ status: 404, description: "Scenario not found" }) createRun(@Param("id", ParseUUIDPipe) id: string) { return this.scenarioService.createRun(id); } @Get(":id/run/:runId") @ApiOperation({ summary: "Get a specific run with step runs and logs" }) @ApiResponse({ status: 200 }) @ApiResponse({ status: 404, description: "Scenario or run not found" }) findRun( @Param("id", ParseUUIDPipe) id: string, @Param("runId", ParseUUIDPipe) runId: string, ) { return this.scenarioService.findRun(id, runId); } @Post(":id/run/:runId/wait") @HttpCode(200) @ApiOperation({ summary: "Block until the run reaches pass or fail (max 5 min), then return run with logs", }) @ApiResponse({ status: 200 }) @ApiResponse({ status: 404, description: "Scenario or run not found" }) waitForRun( @Param("id", ParseUUIDPipe) id: string, @Param("runId", ParseUUIDPipe) runId: string, ) { return this.scenarioService.waitForRun(id, runId); } }