import { applyDecorators } from '@nestjs/common'; import { ApiBadRequestResponse, ApiConflictResponse, ApiForbiddenResponse, ApiNotFoundResponse, ApiProperty, ApiPropertyOptional, ApiUnauthorizedResponse, ApiUnprocessableEntityResponse, } from '@nestjs/swagger'; /** Error body of the REST API (`DomainErrorFilter`, guards and validation). */ export class ApiErrorDto { @ApiProperty({ example: 404 }) statusCode!: number; @ApiPropertyOptional({ description: 'Domain error code', enum: ['NOT_FOUND', 'FORBIDDEN', 'VALIDATION', 'CONFLICT', 'BUSINESS_RULE'], }) code?: string; @ApiProperty({ description: 'Human message (a list of messages for invalid input)', oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' } }], example: 'Issue not found', }) message!: string | string[]; } /** 400 / 401 / 403 / 404: what any authenticated read can answer. */ export const ApiReadErrors = () => applyDecorators( ApiBadRequestResponse({ type: ApiErrorDto, description: 'Invalid input' }), ApiUnauthorizedResponse({ type: ApiErrorDto, description: 'Missing or rejected Pireon token', }), ApiForbiddenResponse({ type: ApiErrorDto, description: 'Not a member of the project (or not allowed)', }), ApiNotFoundResponse({ type: ApiErrorDto, description: 'Not found' }), ); /** Reads + 409 (concurrent change) and 422 (business rule) for commands. */ export const ApiCommandErrors = () => applyDecorators( ApiReadErrors(), ApiConflictResponse({ type: ApiErrorDto, description: 'Someone changed it at the same time: retry', }), ApiUnprocessableEntityResponse({ type: ApiErrorDto, description: 'Business rule (e.g. a transition the workflow forbids)', }), );