- add PaginationQueryDto<TOrderBy> generic with orderBy and orderDir fields - add SessionOrderBy, EnvironmentOrderBy, ScenarioOrderBy type aliases - update session, environment, scenario services and controllers to use typed pagination - update MCP list_sessions, list_environments, list_scenarios tools to expose orderBy/orderDir - update tests for all list endpoints to cover page, limit, ordering, and invalid param rejection
138 lines
4.9 KiB
TypeScript
138 lines
4.9 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Delete,
|
|
Get,
|
|
HttpCode,
|
|
Param,
|
|
ParseIntPipe,
|
|
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 { PaginationQueryDto } from './dto/pagination-query.dto';
|
|
import { ScenarioOrderBy } from './scenario.service';
|
|
import { RunsQueryDto } from './dto/runs-query.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);
|
|
}
|
|
|
|
@Get()
|
|
@ApiOperation({ summary: 'List all scenarios (paginated)' })
|
|
@ApiResponse({ status: 200 })
|
|
findAll(@Query() query: PaginationQueryDto<ScenarioOrderBy>) {
|
|
return this.scenarioService.findAll(query);
|
|
}
|
|
|
|
@Get(':id')
|
|
@ApiOperation({ summary: 'Get a scenario with its steps' })
|
|
@ApiResponse({ status: 200 })
|
|
@ApiResponse({ status: 404, description: 'Scenario not found' })
|
|
findOne(@Param('id', ParseIntPipe) id: number) {
|
|
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', ParseIntPipe) id: number, @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', ParseIntPipe) id: number) {
|
|
return this.scenarioService.remove(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', ParseIntPipe) id: number,
|
|
@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', ParseIntPipe) id: number,
|
|
@Param('stepId', ParseIntPipe) stepId: number,
|
|
) {
|
|
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', ParseIntPipe) id: number,
|
|
@Param('stepId', ParseIntPipe) stepId: number,
|
|
@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', ParseIntPipe) id: number,
|
|
@Param('stepId', ParseIntPipe) stepId: number,
|
|
) {
|
|
return this.scenarioService.removeStep(id, stepId);
|
|
}
|
|
|
|
// ── 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', ParseIntPipe) id: number,
|
|
@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', ParseIntPipe) id: number) {
|
|
return this.scenarioService.createRun(id);
|
|
}
|
|
}
|