Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | 2x 2x 2x 2x 2x 2x 6x 2x 1x 2x 1x 2x 1x 2x 1x 2x 1x | import {
Body,
Controller,
Get,
Param,
ParseUUIDPipe,
Patch,
Post,
Query,
} from '@nestjs/common';
import {
ApiBearerAuth,
ApiBody,
ApiCreatedResponse,
ApiForbiddenResponse,
ApiOkResponse,
ApiOperation,
ApiQuery,
ApiTags,
ApiUnauthorizedResponse,
} from '@nestjs/swagger';
import { Roles } from '../../auth/auth.decorators';
import { WorkOrdersClient } from '../clients/work-orders.client';
import {
AssignWorkOrderRequest,
CreateWorkOrderRequest,
Paginated,
UpdateWorkOrderStatusRequest,
WorkOrderDto,
WorkOrderPage,
} from '../http/api-types';
/**
* Pass-through routes. The gateway validates only what it owns (path shape,
* and now authorization — roles map to the actors in docs/flows.md); payload
* validation lives with the service that owns the data.
*/
@ApiTags('work-orders')
@ApiBearerAuth()
@ApiUnauthorizedResponse({ description: 'Missing or invalid token' })
@ApiForbiddenResponse({ description: 'Role not allowed on this route' })
@Controller('work-orders')
export class WorkOrdersController {
constructor(private readonly workOrders: WorkOrdersClient) {}
@ApiOperation({
summary: 'Open a maintenance request',
description:
'Roles: tenant, manager. Triage fields come back null — AI classification runs asynchronously (flow 4).',
})
@ApiBody({ type: CreateWorkOrderRequest })
@ApiCreatedResponse({ type: WorkOrderDto })
@Roles('tenant', 'manager')
@Post()
create(@Body() body: unknown): Promise<WorkOrderDto> {
return this.workOrders.create(body);
}
@ApiOperation({
summary: 'List work orders',
description: 'Roles: any authenticated user.',
})
@ApiQuery({ name: 'page', required: false, example: 1 })
@ApiQuery({ name: 'limit', required: false, example: 20 })
@ApiQuery({
name: 'status',
required: false,
enum: ['open', 'assigned', 'in_progress', 'completed', 'cancelled'],
})
@ApiQuery({
name: 'priority',
required: false,
enum: ['low', 'medium', 'high', 'urgent'],
})
@ApiQuery({ name: 'propertyId', required: false })
@ApiOkResponse({ type: WorkOrderPage })
@Get()
list(
@Query() query: Record<string, string>,
): Promise<Paginated<WorkOrderDto>> {
return this.workOrders.list(query);
}
@ApiOperation({
summary: 'Get one work order',
description: 'Roles: any authenticated user.',
})
@ApiOkResponse({ type: WorkOrderDto })
@Get(':id')
getById(@Param('id', ParseUUIDPipe) id: string): Promise<WorkOrderDto> {
return this.workOrders.getById(id);
}
@ApiOperation({
summary: 'Assign a technician',
description:
"Roles: manager. The only way into the 'assigned' state — guarantees an assignee exists.",
})
@ApiBody({ type: AssignWorkOrderRequest })
@ApiOkResponse({ type: WorkOrderDto })
@Roles('manager')
@Patch(':id/assign')
assign(
@Param('id', ParseUUIDPipe) id: string,
@Body() body: unknown,
): Promise<WorkOrderDto> {
return this.workOrders.assign(id, body);
}
@ApiOperation({
summary: 'Move a work order through its lifecycle',
description:
'Roles: technician, manager. Transitions are guarded by the state machine (flow 8); invalid ones return 409.',
})
@ApiBody({ type: UpdateWorkOrderStatusRequest })
@ApiOkResponse({ type: WorkOrderDto })
@Roles('technician', 'manager')
@Patch(':id/status')
updateStatus(
@Param('id', ParseUUIDPipe) id: string,
@Body() body: unknown,
): Promise<WorkOrderDto> {
return this.workOrders.updateStatus(id, body);
}
}
|