refactor(api): describe bank operation create body as oneOf by kind (WealthLedger/backend#172)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
ApiProperty,
|
||||
ApiPropertyOptional,
|
||||
getSchemaPath,
|
||||
IntersectionType,
|
||||
OmitType,
|
||||
PartialType,
|
||||
@@ -153,6 +154,49 @@ export class CreateBankOperationDto {
|
||||
@MaxLength(MAX_OPERATION_DESCRIPTION_LENGTH)
|
||||
description?: string;
|
||||
}
|
||||
|
||||
// OpenAPI-only variants. Runtime validation stays on CreateBankOperationDto so
|
||||
// invalid combinations keep the existing BANK_INVALID_INPUT response.
|
||||
class BankOperationCreateBaseDto extends OmitType(CreateBankOperationDto, [
|
||||
'kind', 'targetCardId', 'categoryId', 'originalExpenseId',
|
||||
] as const) {}
|
||||
|
||||
export class CreateExpenseOperationDto extends BankOperationCreateBaseDto {
|
||||
@ApiProperty({ enum: [BankOperationKind.Expense] }) declare kind: 'expense';
|
||||
@ApiPropertyOptional({ format: 'uuid' }) declare categoryId?: string;
|
||||
}
|
||||
|
||||
export class CreateTopupOperationDto extends BankOperationCreateBaseDto {
|
||||
@ApiProperty({ enum: [BankOperationKind.Topup] }) declare kind: 'topup';
|
||||
}
|
||||
|
||||
export class CreateTransferOperationDto extends BankOperationCreateBaseDto {
|
||||
@ApiProperty({ enum: [BankOperationKind.Transfer] }) declare kind: 'transfer';
|
||||
@ApiProperty({ format: 'uuid' }) declare targetCardId: string;
|
||||
}
|
||||
|
||||
export class CreateRefundOperationDto extends BankOperationCreateBaseDto {
|
||||
@ApiProperty({ enum: [BankOperationKind.Refund] }) declare kind: 'refund';
|
||||
@ApiProperty({ format: 'uuid' }) declare originalExpenseId: string;
|
||||
}
|
||||
|
||||
export const createBankOperationSchema = {
|
||||
oneOf: [
|
||||
CreateExpenseOperationDto,
|
||||
CreateTopupOperationDto,
|
||||
CreateTransferOperationDto,
|
||||
CreateRefundOperationDto,
|
||||
].map((model) => ({ $ref: getSchemaPath(model) })),
|
||||
discriminator: {
|
||||
propertyName: 'kind',
|
||||
mapping: {
|
||||
expense: getSchemaPath(CreateExpenseOperationDto),
|
||||
topup: getSchemaPath(CreateTopupOperationDto),
|
||||
transfer: getSchemaPath(CreateTransferOperationDto),
|
||||
refund: getSchemaPath(CreateRefundOperationDto),
|
||||
},
|
||||
},
|
||||
};
|
||||
export class UpdateBankOperationDto extends PartialType(
|
||||
OmitType(CreateBankOperationDto, ['id'] as const),
|
||||
{ skipNullProperties: false },
|
||||
|
||||
@@ -16,7 +16,9 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiBody,
|
||||
ApiCreatedResponse,
|
||||
ApiExtraModels,
|
||||
ApiNoContentResponse,
|
||||
ApiOkResponse,
|
||||
ApiResponse,
|
||||
@@ -46,6 +48,11 @@ import {
|
||||
CategoryNameDto,
|
||||
CreateBankCardDto,
|
||||
CreateBankOperationDto,
|
||||
createBankOperationSchema,
|
||||
CreateExpenseOperationDto,
|
||||
CreateRefundOperationDto,
|
||||
CreateTopupOperationDto,
|
||||
CreateTransferOperationDto,
|
||||
ExpenseCategoryDto,
|
||||
UpdateBankCardDto,
|
||||
UpdateBankOperationDto,
|
||||
@@ -73,6 +80,12 @@ type AuthRequest = Request & { user: AccessPayload };
|
||||
},
|
||||
})
|
||||
@ApiTags('bank-cards')
|
||||
@ApiExtraModels(
|
||||
CreateExpenseOperationDto,
|
||||
CreateTopupOperationDto,
|
||||
CreateTransferOperationDto,
|
||||
CreateRefundOperationDto,
|
||||
)
|
||||
@ApiBearerAuth()
|
||||
@ApiProblemResponses()
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@@ -131,6 +144,7 @@ export class BankCardsController {
|
||||
}
|
||||
|
||||
@Post('operations')
|
||||
@ApiBody({ schema: createBankOperationSchema })
|
||||
@ApiCreatedResponse({ type: BankOperationDto })
|
||||
createOperation(
|
||||
@Req() req: AuthRequest,
|
||||
|
||||
Reference in New Issue
Block a user