refactor(api): describe bank operation create body as oneOf by kind (WealthLedger/backend#172)
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
ApiProperty,
|
ApiProperty,
|
||||||
ApiPropertyOptional,
|
ApiPropertyOptional,
|
||||||
|
getSchemaPath,
|
||||||
IntersectionType,
|
IntersectionType,
|
||||||
OmitType,
|
OmitType,
|
||||||
PartialType,
|
PartialType,
|
||||||
@@ -153,6 +154,49 @@ export class CreateBankOperationDto {
|
|||||||
@MaxLength(MAX_OPERATION_DESCRIPTION_LENGTH)
|
@MaxLength(MAX_OPERATION_DESCRIPTION_LENGTH)
|
||||||
description?: string;
|
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(
|
export class UpdateBankOperationDto extends PartialType(
|
||||||
OmitType(CreateBankOperationDto, ['id'] as const),
|
OmitType(CreateBankOperationDto, ['id'] as const),
|
||||||
{ skipNullProperties: false },
|
{ skipNullProperties: false },
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
ApiBearerAuth,
|
ApiBearerAuth,
|
||||||
|
ApiBody,
|
||||||
ApiCreatedResponse,
|
ApiCreatedResponse,
|
||||||
|
ApiExtraModels,
|
||||||
ApiNoContentResponse,
|
ApiNoContentResponse,
|
||||||
ApiOkResponse,
|
ApiOkResponse,
|
||||||
ApiResponse,
|
ApiResponse,
|
||||||
@@ -46,6 +48,11 @@ import {
|
|||||||
CategoryNameDto,
|
CategoryNameDto,
|
||||||
CreateBankCardDto,
|
CreateBankCardDto,
|
||||||
CreateBankOperationDto,
|
CreateBankOperationDto,
|
||||||
|
createBankOperationSchema,
|
||||||
|
CreateExpenseOperationDto,
|
||||||
|
CreateRefundOperationDto,
|
||||||
|
CreateTopupOperationDto,
|
||||||
|
CreateTransferOperationDto,
|
||||||
ExpenseCategoryDto,
|
ExpenseCategoryDto,
|
||||||
UpdateBankCardDto,
|
UpdateBankCardDto,
|
||||||
UpdateBankOperationDto,
|
UpdateBankOperationDto,
|
||||||
@@ -73,6 +80,12 @@ type AuthRequest = Request & { user: AccessPayload };
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ApiTags('bank-cards')
|
@ApiTags('bank-cards')
|
||||||
|
@ApiExtraModels(
|
||||||
|
CreateExpenseOperationDto,
|
||||||
|
CreateTopupOperationDto,
|
||||||
|
CreateTransferOperationDto,
|
||||||
|
CreateRefundOperationDto,
|
||||||
|
)
|
||||||
@ApiBearerAuth()
|
@ApiBearerAuth()
|
||||||
@ApiProblemResponses()
|
@ApiProblemResponses()
|
||||||
@UseGuards(JwtAuthGuard)
|
@UseGuards(JwtAuthGuard)
|
||||||
@@ -131,6 +144,7 @@ export class BankCardsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Post('operations')
|
@Post('operations')
|
||||||
|
@ApiBody({ schema: createBankOperationSchema })
|
||||||
@ApiCreatedResponse({ type: BankOperationDto })
|
@ApiCreatedResponse({ type: BankOperationDto })
|
||||||
createOperation(
|
createOperation(
|
||||||
@Req() req: AuthRequest,
|
@Req() req: AuthRequest,
|
||||||
|
|||||||
Reference in New Issue
Block a user