Refactor Controllers to Utilize Zod DTOs for Parameter Validation
- Updated InvoicesController, NotificationsController, OrdersController, and SubscriptionsController to replace inline parameter validation with Zod DTOs, enhancing code maintainability and clarity. - Introduced new DTOs for invoice and notification ID parameters, ensuring consistent validation across endpoints. - Refactored service method calls to utilize the new DTOs, improving type safety and reducing potential errors. - Cleaned up unused imports and optimized code structure for better readability.
This commit is contained in:
parent
a1be0ea527
commit
fcc9bc247e
@ -1,14 +1,4 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Param,
|
||||
Query,
|
||||
Request,
|
||||
ParseIntPipe,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
} from "@nestjs/common";
|
||||
import { Controller, Get, Post, Param, Query, Request, HttpCode, HttpStatus } from "@nestjs/common";
|
||||
import { InvoicesOrchestratorService } from "./services/invoices-orchestrator.service.js";
|
||||
import { WhmcsService } from "@bff/integrations/whmcs/whmcs.service.js";
|
||||
import { MappingsService } from "@bff/modules/id-mappings/mappings.service.js";
|
||||
@ -17,6 +7,7 @@ import type { RequestWithUser } from "@bff/modules/auth/auth.types.js";
|
||||
|
||||
import type { Invoice, InvoiceList, InvoiceSsoLink } from "@customer-portal/domain/billing";
|
||||
import {
|
||||
invoiceIdParamSchema,
|
||||
invoiceListQuerySchema,
|
||||
invoiceListSchema,
|
||||
invoiceSchema,
|
||||
@ -37,6 +28,7 @@ import {
|
||||
} from "@customer-portal/domain/payments";
|
||||
|
||||
class InvoiceListQueryDto extends createZodDto(invoiceListQuerySchema) {}
|
||||
class InvoiceIdParamDto extends createZodDto(invoiceIdParamSchema) {}
|
||||
class InvoiceListDto extends createZodDto(invoiceListSchema) {}
|
||||
class InvoiceDto extends createZodDto(invoiceSchema) {}
|
||||
class InvoiceSsoLinkDto extends createZodDto(invoiceSsoLinkSchema) {}
|
||||
@ -104,15 +96,15 @@ export class InvoicesController {
|
||||
@ZodResponse({ description: "Get invoice by id", type: InvoiceDto })
|
||||
async getInvoiceById(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) invoiceId: number
|
||||
@Param() params: InvoiceIdParamDto
|
||||
): Promise<Invoice> {
|
||||
return this.invoicesService.getInvoiceById(req.user.id, invoiceId);
|
||||
return this.invoicesService.getInvoiceById(req.user.id, params.id);
|
||||
}
|
||||
|
||||
@Get(":id/subscriptions")
|
||||
getInvoiceSubscriptions(
|
||||
@Request() _req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) _invoiceId: number
|
||||
@Param() _params: InvoiceIdParamDto
|
||||
): Subscription[] {
|
||||
// This functionality has been moved to WHMCS directly
|
||||
// For now, return empty array as subscriptions are managed in WHMCS
|
||||
@ -124,7 +116,7 @@ export class InvoicesController {
|
||||
@ZodResponse({ description: "Create invoice SSO link", type: InvoiceSsoLinkDto })
|
||||
async createSsoLink(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) invoiceId: number,
|
||||
@Param() params: InvoiceIdParamDto,
|
||||
@Query() query: InvoiceSsoQueryDto
|
||||
): Promise<InvoiceSsoLink> {
|
||||
const mapping = await this.mappingsService.findByUserId(req.user.id);
|
||||
@ -136,7 +128,7 @@ export class InvoicesController {
|
||||
|
||||
const ssoUrl = await this.whmcsService.whmcsSsoForInvoice(
|
||||
mapping.whmcsClientId,
|
||||
invoiceId,
|
||||
params.id,
|
||||
parsedQuery.target
|
||||
);
|
||||
|
||||
@ -151,7 +143,7 @@ export class InvoicesController {
|
||||
@ZodResponse({ description: "Create invoice payment link", type: InvoicePaymentLinkDto })
|
||||
async createPaymentLink(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) invoiceId: number,
|
||||
@Param() params: InvoiceIdParamDto,
|
||||
@Query() query: InvoicePaymentLinkQueryDto
|
||||
): Promise<InvoicePaymentLink> {
|
||||
const mapping = await this.mappingsService.findByUserId(req.user.id);
|
||||
@ -163,7 +155,7 @@ export class InvoicesController {
|
||||
|
||||
const ssoResult = await this.whmcsService.createPaymentSsoToken(
|
||||
mapping.whmcsClientId,
|
||||
invoiceId,
|
||||
params.id,
|
||||
parsedQuery.paymentMethodId,
|
||||
parsedQuery.gatewayName
|
||||
);
|
||||
|
||||
@ -11,6 +11,7 @@ import { NotificationService } from "./notifications.service.js";
|
||||
import {
|
||||
notificationListResponseSchema,
|
||||
notificationUnreadCountResponseSchema,
|
||||
notificationIdParamSchema,
|
||||
type NotificationListResponse,
|
||||
} from "@customer-portal/domain/notifications";
|
||||
import { notificationQuerySchema } from "@customer-portal/domain/notifications";
|
||||
@ -21,6 +22,7 @@ import {
|
||||
import { createZodDto, ZodResponse } from "nestjs-zod";
|
||||
|
||||
class NotificationQueryDto extends createZodDto(notificationQuerySchema) {}
|
||||
class NotificationIdParamDto extends createZodDto(notificationIdParamSchema) {}
|
||||
class NotificationListResponseDto extends createZodDto(notificationListResponseSchema) {}
|
||||
class NotificationUnreadCountResponseDto extends createZodDto(
|
||||
notificationUnreadCountResponseSchema
|
||||
@ -70,9 +72,9 @@ export class NotificationsController {
|
||||
@ZodResponse({ description: "Mark as read", type: ApiSuccessAckResponseDto })
|
||||
async markAsRead(
|
||||
@Req() req: RequestWithUser,
|
||||
@Param("id") notificationId: string
|
||||
@Param() params: NotificationIdParamDto
|
||||
): Promise<ApiSuccessAckResponse> {
|
||||
await this.notificationService.markAsRead(notificationId, req.user.id);
|
||||
await this.notificationService.markAsRead(params.id, req.user.id);
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@ -95,9 +97,9 @@ export class NotificationsController {
|
||||
@ZodResponse({ description: "Dismiss notification", type: ApiSuccessAckResponseDto })
|
||||
async dismiss(
|
||||
@Req() req: RequestWithUser,
|
||||
@Param("id") notificationId: string
|
||||
@Param() params: NotificationIdParamDto
|
||||
): Promise<ApiSuccessAckResponse> {
|
||||
await this.notificationService.dismiss(notificationId, req.user.id);
|
||||
await this.notificationService.dismiss(params.id, req.user.id);
|
||||
return { success: true };
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,10 +7,9 @@ import { CheckoutSessionService } from "../services/checkout-session.service.js"
|
||||
import {
|
||||
checkoutCartSchema,
|
||||
checkoutBuildCartRequestSchema,
|
||||
checkoutBuildCartResponseSchema,
|
||||
checkoutSessionIdParamSchema,
|
||||
checkoutSessionResponseSchema,
|
||||
checkoutValidateCartResponseSchema,
|
||||
checkoutSessionDataSchema,
|
||||
checkoutValidateCartDataSchema,
|
||||
} from "@customer-portal/domain/orders";
|
||||
import type { RequestWithUser } from "@bff/modules/auth/auth.types.js";
|
||||
import { SalesforceReadThrottleGuard } from "@bff/integrations/salesforce/guards/salesforce-read-throttle.guard.js";
|
||||
@ -18,9 +17,9 @@ import { SalesforceReadThrottleGuard } from "@bff/integrations/salesforce/guards
|
||||
class CheckoutBuildCartRequestDto extends createZodDto(checkoutBuildCartRequestSchema) {}
|
||||
class CheckoutSessionIdParamDto extends createZodDto(checkoutSessionIdParamSchema) {}
|
||||
class CheckoutCartDto extends createZodDto(checkoutCartSchema) {}
|
||||
class CheckoutBuildCartResponseDto extends createZodDto(checkoutBuildCartResponseSchema) {}
|
||||
class CheckoutSessionResponseDto extends createZodDto(checkoutSessionResponseSchema) {}
|
||||
class ValidateCartResponseDto extends createZodDto(checkoutValidateCartResponseSchema) {}
|
||||
class CheckoutBuildCartResponseDto extends createZodDto(checkoutCartSchema) {}
|
||||
class CheckoutSessionResponseDto extends createZodDto(checkoutSessionDataSchema) {}
|
||||
class ValidateCartResponseDto extends createZodDto(checkoutValidateCartDataSchema) {}
|
||||
|
||||
@Controller("checkout")
|
||||
@Public() // Cart building and validation can be done without authentication
|
||||
@ -52,7 +51,7 @@ export class CheckoutController {
|
||||
req.user?.id
|
||||
);
|
||||
|
||||
return { success: true as const, data: cart };
|
||||
return cart;
|
||||
} catch (error) {
|
||||
this.logger.error("Failed to build checkout cart", {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
@ -90,15 +89,12 @@ export class CheckoutController {
|
||||
const session = await this.checkoutSessions.createSession(body, cart);
|
||||
|
||||
return {
|
||||
success: true as const,
|
||||
data: {
|
||||
sessionId: session.sessionId,
|
||||
expiresAt: session.expiresAt,
|
||||
orderType: body.orderType,
|
||||
cart: {
|
||||
items: cart.items,
|
||||
totals: cart.totals,
|
||||
},
|
||||
sessionId: session.sessionId,
|
||||
expiresAt: session.expiresAt,
|
||||
orderType: body.orderType,
|
||||
cart: {
|
||||
items: cart.items,
|
||||
totals: cart.totals,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -113,15 +109,12 @@ export class CheckoutController {
|
||||
async getSession(@Param() params: CheckoutSessionIdParamDto) {
|
||||
const session = await this.checkoutSessions.getSession(params.sessionId);
|
||||
return {
|
||||
success: true as const,
|
||||
data: {
|
||||
sessionId: params.sessionId,
|
||||
expiresAt: session.expiresAt,
|
||||
orderType: session.request.orderType,
|
||||
cart: {
|
||||
items: session.cart.items,
|
||||
totals: session.cart.totals,
|
||||
},
|
||||
sessionId: params.sessionId,
|
||||
expiresAt: session.expiresAt,
|
||||
orderType: session.request.orderType,
|
||||
cart: {
|
||||
items: session.cart.items,
|
||||
totals: session.cart.totals,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -140,7 +133,7 @@ export class CheckoutController {
|
||||
try {
|
||||
this.checkoutService.validateCart(cart);
|
||||
|
||||
return { success: true as const, data: { valid: true } };
|
||||
return { valid: true };
|
||||
} catch (error) {
|
||||
this.logger.error("Checkout cart validation failed", {
|
||||
error: error instanceof Error ? error.message : String(error),
|
||||
|
||||
@ -25,7 +25,6 @@ import {
|
||||
orderListResponseSchema,
|
||||
type CreateOrderRequest,
|
||||
} from "@customer-portal/domain/orders";
|
||||
import { apiSuccessResponseSchema } from "@customer-portal/domain/common";
|
||||
import { Observable } from "rxjs";
|
||||
import { OrderEventsService } from "./services/order-events.service.js";
|
||||
import { SalesforceReadThrottleGuard } from "@bff/integrations/salesforce/guards/salesforce-read-throttle.guard.js";
|
||||
@ -37,9 +36,7 @@ import { SkipSuccessEnvelope } from "@bff/core/http/transform.interceptor.js";
|
||||
class CreateOrderRequestDto extends createZodDto(createOrderRequestSchema) {}
|
||||
class CheckoutSessionCreateOrderDto extends createZodDto(checkoutSessionCreateOrderRequestSchema) {}
|
||||
class SfOrderIdParamDto extends createZodDto(sfOrderIdParamSchema) {}
|
||||
class CreateOrderResponseDto extends createZodDto(
|
||||
apiSuccessResponseSchema(orderCreateResponseSchema)
|
||||
) {}
|
||||
class CreateOrderResponseDto extends createZodDto(orderCreateResponseSchema) {}
|
||||
class OrderDetailsDto extends createZodDto(orderDetailsSchema) {}
|
||||
class OrderListResponseDto extends createZodDto(orderListResponseSchema) {}
|
||||
|
||||
@ -70,7 +67,7 @@ export class OrdersController {
|
||||
|
||||
try {
|
||||
const result = await this.orderOrchestrator.createOrder(req.user.id, body);
|
||||
return { success: true as const, data: result };
|
||||
return result;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
{
|
||||
@ -137,7 +134,7 @@ export class OrdersController {
|
||||
|
||||
await this.checkoutSessions.deleteSession(body.checkoutSessionId);
|
||||
|
||||
return { success: true as const, data: result };
|
||||
return result;
|
||||
}
|
||||
|
||||
@Get("user")
|
||||
|
||||
@ -1,12 +1,22 @@
|
||||
import { Body, Controller, Get, Header, Post, Req, UseGuards } from "@nestjs/common";
|
||||
import { createZodDto } from "nestjs-zod";
|
||||
import { createZodDto, ZodResponse } from "nestjs-zod";
|
||||
import type { RequestWithUser } from "@bff/modules/auth/auth.types.js";
|
||||
import { RateLimit, RateLimitGuard } from "@bff/core/rate-limiting/index.js";
|
||||
import { InternetServicesService } from "./services/internet-services.service.js";
|
||||
import type { InternetEligibilityDetails } from "@customer-portal/domain/services";
|
||||
import { internetEligibilityRequestSchema } from "@customer-portal/domain/services";
|
||||
import {
|
||||
internetEligibilityDetailsSchema,
|
||||
internetEligibilityRequestSchema,
|
||||
internetEligibilityRequestResponseSchema,
|
||||
} from "@customer-portal/domain/services";
|
||||
|
||||
class EligibilityRequestDto extends createZodDto(internetEligibilityRequestSchema) {}
|
||||
class InternetEligibilityDetailsResponseDto extends createZodDto(
|
||||
internetEligibilityDetailsSchema
|
||||
) {}
|
||||
class InternetEligibilityRequestResponseDto extends createZodDto(
|
||||
internetEligibilityRequestResponseSchema
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Internet Eligibility Controller
|
||||
@ -26,6 +36,10 @@ export class InternetEligibilityController {
|
||||
@Get("eligibility")
|
||||
@RateLimit({ limit: 60, ttl: 60 }) // 60/min per IP (cheap)
|
||||
@Header("Cache-Control", "private, no-store")
|
||||
@ZodResponse({
|
||||
description: "Get internet eligibility",
|
||||
type: InternetEligibilityDetailsResponseDto,
|
||||
})
|
||||
async getEligibility(@Req() req: RequestWithUser): Promise<InternetEligibilityDetails> {
|
||||
return this.internetCatalog.getEligibilityDetailsForUser(req.user.id);
|
||||
}
|
||||
@ -33,6 +47,10 @@ export class InternetEligibilityController {
|
||||
@Post("eligibility-request")
|
||||
@RateLimit({ limit: 5, ttl: 300 }) // 5 per 5 minutes per IP
|
||||
@Header("Cache-Control", "private, no-store")
|
||||
@ZodResponse({
|
||||
description: "Request internet eligibility check",
|
||||
type: InternetEligibilityRequestResponseDto,
|
||||
})
|
||||
async requestEligibility(
|
||||
@Req() req: RequestWithUser,
|
||||
@Body() body: EligibilityRequestDto
|
||||
|
||||
@ -6,7 +6,6 @@ import {
|
||||
Query,
|
||||
Body,
|
||||
Request,
|
||||
ParseIntPipe,
|
||||
Header,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
@ -18,6 +17,8 @@ import { SimTopUpPricingService } from "./sim-management/services/sim-topup-pric
|
||||
import {
|
||||
subscriptionQuerySchema,
|
||||
subscriptionListSchema,
|
||||
subscriptionArraySchema,
|
||||
subscriptionIdParamSchema,
|
||||
subscriptionSchema,
|
||||
subscriptionStatsSchema,
|
||||
simActionResponseSchema,
|
||||
@ -32,8 +33,6 @@ import type {
|
||||
SimPlanChangeResult,
|
||||
} from "@customer-portal/domain/subscriptions";
|
||||
import type { InvoiceList } from "@customer-portal/domain/billing";
|
||||
import type { ApiSuccessResponse } from "@customer-portal/domain/common";
|
||||
import { apiSuccessResponseSchema } from "@customer-portal/domain/common";
|
||||
import { createPaginationSchema } from "@customer-portal/domain/toolkit/validation/helpers";
|
||||
import {
|
||||
simTopupRequestSchema,
|
||||
@ -47,12 +46,22 @@ import {
|
||||
simHistoryQuerySchema,
|
||||
simSftpListQuerySchema,
|
||||
simCallHistoryImportQuerySchema,
|
||||
simHistoryAvailableMonthsSchema,
|
||||
simSftpListResultSchema,
|
||||
simCallHistoryImportResultSchema,
|
||||
simTopUpPricingSchema,
|
||||
simTopUpPricingPreviewRequestSchema,
|
||||
simTopUpPricingPreviewResponseSchema,
|
||||
simReissueEsimRequestSchema,
|
||||
simInfoSchema,
|
||||
simDetailsSchema,
|
||||
simUsageSchema,
|
||||
simTopUpHistorySchema,
|
||||
simAvailablePlanArraySchema,
|
||||
simCancellationPreviewSchema,
|
||||
simDomesticCallHistoryResponseSchema,
|
||||
simInternationalCallHistoryResponseSchema,
|
||||
simSmsHistoryResponseSchema,
|
||||
type SimAvailablePlan,
|
||||
type SimCancellationPreview,
|
||||
type SimDomesticCallHistoryResponse,
|
||||
@ -81,6 +90,7 @@ const subscriptionInvoiceQuerySchema = createPaginationSchema({
|
||||
|
||||
class SubscriptionQueryDto extends createZodDto(subscriptionQuerySchema) {}
|
||||
class SubscriptionInvoiceQueryDto extends createZodDto(subscriptionInvoiceQuerySchema) {}
|
||||
class SubscriptionIdParamDto extends createZodDto(subscriptionIdParamSchema) {}
|
||||
class SimTopupRequestDto extends createZodDto(simTopupRequestSchema) {}
|
||||
class SimChangePlanRequestDto extends createZodDto(simChangePlanRequestSchema) {}
|
||||
class SimCancelRequestDto extends createZodDto(simCancelRequestSchema) {}
|
||||
@ -103,15 +113,35 @@ class SimUsageDto extends createZodDto(simUsageSchema) {}
|
||||
class SimTopUpHistoryDto extends createZodDto(simTopUpHistorySchema) {}
|
||||
|
||||
class SubscriptionListDto extends createZodDto(subscriptionListSchema) {}
|
||||
class ActiveSubscriptionsDto extends createZodDto(subscriptionArraySchema) {}
|
||||
class SubscriptionDto extends createZodDto(subscriptionSchema) {}
|
||||
class SubscriptionStatsDto extends createZodDto(subscriptionStatsSchema) {}
|
||||
class SimActionResponseDto extends createZodDto(simActionResponseSchema) {}
|
||||
class SimPlanChangeResultDto extends createZodDto(simPlanChangeResultSchema) {}
|
||||
class InvoiceListDto extends createZodDto(invoiceListSchema) {}
|
||||
class InternetCancellationPreviewResponseDto extends createZodDto(
|
||||
apiSuccessResponseSchema(internetCancellationPreviewSchema)
|
||||
internetCancellationPreviewSchema
|
||||
) {}
|
||||
|
||||
class SimHistoryAvailableMonthsResponseDto extends createZodDto(simHistoryAvailableMonthsSchema) {}
|
||||
class SimSftpListResultResponseDto extends createZodDto(simSftpListResultSchema) {}
|
||||
class SimCallHistoryImportResultResponseDto extends createZodDto(
|
||||
simCallHistoryImportResultSchema
|
||||
) {}
|
||||
class SimTopUpPricingResponseDto extends createZodDto(simTopUpPricingSchema) {}
|
||||
class SimTopUpPricingPreviewResponseDto extends createZodDto(
|
||||
simTopUpPricingPreviewResponseSchema
|
||||
) {}
|
||||
class SimAvailablePlansResponseDto extends createZodDto(simAvailablePlanArraySchema) {}
|
||||
class SimCancellationPreviewResponseDto extends createZodDto(simCancellationPreviewSchema) {}
|
||||
class SimDomesticCallHistoryResponseDto extends createZodDto(
|
||||
simDomesticCallHistoryResponseSchema
|
||||
) {}
|
||||
class SimInternationalCallHistoryResponseDto extends createZodDto(
|
||||
simInternationalCallHistoryResponseSchema
|
||||
) {}
|
||||
class SimSmsHistoryResponseDto extends createZodDto(simSmsHistoryResponseSchema) {}
|
||||
|
||||
@Controller("subscriptions")
|
||||
export class SubscriptionsController {
|
||||
constructor(
|
||||
@ -138,7 +168,7 @@ export class SubscriptionsController {
|
||||
|
||||
@Get("active")
|
||||
@Header("Cache-Control", "private, max-age=300") // 5 minutes, user-specific
|
||||
@ZodResponse({ description: "List active subscriptions", type: [SubscriptionDto] })
|
||||
@ZodResponse({ description: "List active subscriptions", type: ActiveSubscriptionsDto })
|
||||
async getActiveSubscriptions(@Request() req: RequestWithUser): Promise<Subscription[]> {
|
||||
return this.subscriptionsService.getActiveSubscriptions(req.user.id);
|
||||
}
|
||||
@ -158,9 +188,13 @@ export class SubscriptionsController {
|
||||
@Public()
|
||||
@Get("sim/call-history/available-months")
|
||||
@Header("Cache-Control", "public, max-age=3600")
|
||||
@ZodResponse({
|
||||
description: "Get available call/SMS history months",
|
||||
type: SimHistoryAvailableMonthsResponseDto,
|
||||
})
|
||||
async getAvailableMonths() {
|
||||
const months = await this.simCallHistoryService.getAvailableMonths();
|
||||
return { success: true, data: months };
|
||||
return months;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,10 +202,11 @@ export class SubscriptionsController {
|
||||
*/
|
||||
@UseGuards(AdminGuard)
|
||||
@Get("sim/call-history/sftp-files")
|
||||
@ZodResponse({ description: "List available SFTP files", type: SimSftpListResultResponseDto })
|
||||
async listSftpFiles(@Query() query: SimSftpListQueryDto) {
|
||||
const parsedQuery = simSftpListQuerySchema.parse(query as unknown);
|
||||
const files = await this.simCallHistoryService.listSftpFiles(parsedQuery.path);
|
||||
return { success: true, data: files, path: parsedQuery.path };
|
||||
return { files, path: parsedQuery.path };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,28 +214,33 @@ export class SubscriptionsController {
|
||||
*/
|
||||
@UseGuards(AdminGuard)
|
||||
@Post("sim/call-history/import")
|
||||
@ZodResponse({
|
||||
description: "Import call history (admin)",
|
||||
type: SimCallHistoryImportResultResponseDto,
|
||||
})
|
||||
async importCallHistory(@Query() query: SimCallHistoryImportQueryDto) {
|
||||
const parsedQuery = simCallHistoryImportQuerySchema.parse(query as unknown);
|
||||
const result = await this.simCallHistoryService.importCallHistory(parsedQuery.month);
|
||||
return {
|
||||
success: true,
|
||||
message: `Imported ${result.domestic} domestic calls, ${result.international} international calls, ${result.sms} SMS`,
|
||||
data: result,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
@Get("sim/top-up/pricing")
|
||||
@Header("Cache-Control", "public, max-age=3600") // 1 hour, pricing is relatively static
|
||||
@ZodResponse({ description: "Get SIM top-up pricing", type: SimTopUpPricingResponseDto })
|
||||
async getSimTopUpPricing() {
|
||||
const pricing = await this.simTopUpPricingService.getTopUpPricing();
|
||||
return { success: true, data: pricing };
|
||||
return pricing;
|
||||
}
|
||||
|
||||
@Get("sim/top-up/pricing/preview")
|
||||
@Header("Cache-Control", "public, max-age=3600") // 1 hour, pricing calculation is deterministic
|
||||
@ZodResponse({
|
||||
description: "Preview SIM top-up pricing",
|
||||
type: SimTopUpPricingPreviewResponseDto,
|
||||
})
|
||||
async previewSimTopUpPricing(@Query() query: SimTopUpPricingPreviewRequestDto) {
|
||||
const preview = await this.simTopUpPricingService.calculatePricingPreview(query.quotaMb);
|
||||
return { success: true, data: preview };
|
||||
return preview;
|
||||
}
|
||||
|
||||
@Get("debug/sim-details/:account")
|
||||
@ -216,9 +256,9 @@ export class SubscriptionsController {
|
||||
@ZodResponse({ description: "Get subscription", type: SubscriptionDto })
|
||||
async getSubscriptionById(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number
|
||||
@Param() params: SubscriptionIdParamDto
|
||||
): Promise<Subscription> {
|
||||
return this.subscriptionsService.getSubscriptionById(req.user.id, subscriptionId);
|
||||
return this.subscriptionsService.getSubscriptionById(req.user.id, params.id);
|
||||
}
|
||||
|
||||
@Get(":id/invoices")
|
||||
@ -226,10 +266,10 @@ export class SubscriptionsController {
|
||||
@ZodResponse({ description: "Get subscription invoices", type: InvoiceListDto })
|
||||
async getSubscriptionInvoices(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Query() query: SubscriptionInvoiceQueryDto
|
||||
): Promise<InvoiceList> {
|
||||
return this.subscriptionsService.getSubscriptionInvoices(req.user.id, subscriptionId, query);
|
||||
return this.subscriptionsService.getSubscriptionInvoices(req.user.id, params.id, query);
|
||||
}
|
||||
|
||||
// ==================== SIM Management Endpoints (subscription-specific) ====================
|
||||
@ -238,56 +278,47 @@ export class SubscriptionsController {
|
||||
@UseGuards(AdminGuard)
|
||||
async debugSimSubscription(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number
|
||||
@Param() params: SubscriptionIdParamDto
|
||||
): Promise<Record<string, unknown>> {
|
||||
return this.simManagementService.debugSimSubscription(req.user.id, subscriptionId);
|
||||
return this.simManagementService.debugSimSubscription(req.user.id, params.id);
|
||||
}
|
||||
|
||||
@Get(":id/sim")
|
||||
@ZodResponse({ description: "Get SIM info", type: SimInfoDto })
|
||||
async getSimInfo(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number
|
||||
) {
|
||||
return this.simManagementService.getSimInfo(req.user.id, subscriptionId);
|
||||
async getSimInfo(@Request() req: RequestWithUser, @Param() params: SubscriptionIdParamDto) {
|
||||
return this.simManagementService.getSimInfo(req.user.id, params.id);
|
||||
}
|
||||
|
||||
@Get(":id/sim/details")
|
||||
@ZodResponse({ description: "Get SIM details", type: SimDetailsDto })
|
||||
async getSimDetails(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number
|
||||
) {
|
||||
return this.simManagementService.getSimDetails(req.user.id, subscriptionId);
|
||||
async getSimDetails(@Request() req: RequestWithUser, @Param() params: SubscriptionIdParamDto) {
|
||||
return this.simManagementService.getSimDetails(req.user.id, params.id);
|
||||
}
|
||||
|
||||
@Get(":id/sim/usage")
|
||||
@ZodResponse({ description: "Get SIM usage", type: SimUsageDto })
|
||||
async getSimUsage(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number
|
||||
) {
|
||||
return this.simManagementService.getSimUsage(req.user.id, subscriptionId);
|
||||
async getSimUsage(@Request() req: RequestWithUser, @Param() params: SubscriptionIdParamDto) {
|
||||
return this.simManagementService.getSimUsage(req.user.id, params.id);
|
||||
}
|
||||
|
||||
@Get(":id/sim/top-up-history")
|
||||
@ZodResponse({ description: "Get SIM top-up history", type: SimTopUpHistoryDto })
|
||||
async getSimTopUpHistory(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Query() query: SimTopUpHistoryRequestDto
|
||||
) {
|
||||
return this.simManagementService.getSimTopUpHistory(req.user.id, subscriptionId, query);
|
||||
return this.simManagementService.getSimTopUpHistory(req.user.id, params.id, query);
|
||||
}
|
||||
|
||||
@Post(":id/sim/top-up")
|
||||
@ZodResponse({ description: "Top up SIM", type: SimActionResponseDto })
|
||||
async topUpSim(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Body() body: SimTopupRequestDto
|
||||
): Promise<SimActionResponse> {
|
||||
await this.simManagementService.topUpSim(req.user.id, subscriptionId, body);
|
||||
await this.simManagementService.topUpSim(req.user.id, params.id, body);
|
||||
return { success: true, message: "SIM top-up completed successfully" };
|
||||
}
|
||||
|
||||
@ -295,10 +326,10 @@ export class SubscriptionsController {
|
||||
@ZodResponse({ description: "Change SIM plan", type: SimPlanChangeResultDto })
|
||||
async changeSimPlan(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Body() body: SimChangePlanRequestDto
|
||||
): Promise<SimPlanChangeResult> {
|
||||
const result = await this.simManagementService.changeSimPlan(req.user.id, subscriptionId, body);
|
||||
const result = await this.simManagementService.changeSimPlan(req.user.id, params.id, body);
|
||||
return {
|
||||
success: true,
|
||||
message: "SIM plan change completed successfully",
|
||||
@ -310,25 +341,22 @@ export class SubscriptionsController {
|
||||
@ZodResponse({ description: "Cancel SIM", type: SimActionResponseDto })
|
||||
async cancelSim(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Body() body: SimCancelRequestDto
|
||||
): Promise<SimActionResponse> {
|
||||
await this.simManagementService.cancelSim(req.user.id, subscriptionId, body);
|
||||
await this.simManagementService.cancelSim(req.user.id, params.id, body);
|
||||
return { success: true, message: "SIM cancellation completed successfully" };
|
||||
}
|
||||
|
||||
@Post(":id/sim/reissue-esim")
|
||||
@ZodResponse({ description: "Reissue eSIM profile", type: SimActionResponseDto })
|
||||
async reissueEsimProfile(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Body() body: SimReissueEsimRequestDto
|
||||
): Promise<SimActionResponse> {
|
||||
const parsedBody = simReissueEsimRequestSchema.parse(body as unknown);
|
||||
await this.simManagementService.reissueEsimProfile(
|
||||
req.user.id,
|
||||
subscriptionId,
|
||||
parsedBody.newEid
|
||||
);
|
||||
await this.simManagementService.reissueEsimProfile(req.user.id, params.id, parsedBody.newEid);
|
||||
return { success: true, message: "eSIM profile reissue completed successfully" };
|
||||
}
|
||||
|
||||
@ -336,10 +364,10 @@ export class SubscriptionsController {
|
||||
@ZodResponse({ description: "Update SIM features", type: SimActionResponseDto })
|
||||
async updateSimFeatures(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Body() body: SimFeaturesRequestDto
|
||||
): Promise<SimActionResponse> {
|
||||
await this.simManagementService.updateSimFeatures(req.user.id, subscriptionId, body);
|
||||
await this.simManagementService.updateSimFeatures(req.user.id, params.id, body);
|
||||
return { success: true, message: "SIM features updated successfully" };
|
||||
}
|
||||
|
||||
@ -350,12 +378,13 @@ export class SubscriptionsController {
|
||||
*/
|
||||
@Get(":id/sim/available-plans")
|
||||
@Header("Cache-Control", "private, max-age=300")
|
||||
@ZodResponse({ description: "Get available SIM plans", type: SimAvailablePlansResponseDto })
|
||||
async getAvailablePlans(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number
|
||||
): Promise<ApiSuccessResponse<SimAvailablePlan[]>> {
|
||||
const plans = await this.simPlanService.getAvailablePlans(req.user.id, subscriptionId);
|
||||
return { success: true, data: plans };
|
||||
@Param() params: SubscriptionIdParamDto
|
||||
): Promise<SimAvailablePlan[]> {
|
||||
const plans = await this.simPlanService.getAvailablePlans(req.user.id, params.id);
|
||||
return plans;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -365,10 +394,10 @@ export class SubscriptionsController {
|
||||
@ZodResponse({ description: "Change SIM plan (full)", type: SimPlanChangeResultDto })
|
||||
async changeSimPlanFull(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Body() body: SimChangePlanFullRequestDto
|
||||
): Promise<SimPlanChangeResult> {
|
||||
const result = await this.simPlanService.changeSimPlanFull(req.user.id, subscriptionId, body);
|
||||
const result = await this.simPlanService.changeSimPlanFull(req.user.id, params.id, body);
|
||||
return {
|
||||
success: true,
|
||||
message: `SIM plan change scheduled for ${result.scheduledAt}`,
|
||||
@ -381,15 +410,19 @@ export class SubscriptionsController {
|
||||
*/
|
||||
@Get(":id/sim/cancellation-preview")
|
||||
@Header("Cache-Control", "private, max-age=60")
|
||||
@ZodResponse({
|
||||
description: "Get SIM cancellation preview",
|
||||
type: SimCancellationPreviewResponseDto,
|
||||
})
|
||||
async getCancellationPreview(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number
|
||||
): Promise<ApiSuccessResponse<SimCancellationPreview>> {
|
||||
@Param() params: SubscriptionIdParamDto
|
||||
): Promise<SimCancellationPreview> {
|
||||
const preview = await this.simCancellationService.getCancellationPreview(
|
||||
req.user.id,
|
||||
subscriptionId
|
||||
params.id
|
||||
);
|
||||
return { success: true, data: preview };
|
||||
return preview;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -399,10 +432,10 @@ export class SubscriptionsController {
|
||||
@ZodResponse({ description: "Cancel SIM (full)", type: SimActionResponseDto })
|
||||
async cancelSimFull(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Body() body: SimCancelFullRequestDto
|
||||
): Promise<SimActionResponse> {
|
||||
await this.simCancellationService.cancelSimFull(req.user.id, subscriptionId, body);
|
||||
await this.simCancellationService.cancelSimFull(req.user.id, params.id, body);
|
||||
return {
|
||||
success: true,
|
||||
message: `SIM cancellation scheduled for end of ${body.cancellationMonth}`,
|
||||
@ -416,10 +449,10 @@ export class SubscriptionsController {
|
||||
@ZodResponse({ description: "Reissue SIM", type: SimActionResponseDto })
|
||||
async reissueSim(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Body() body: SimReissueFullRequestDto
|
||||
): Promise<SimActionResponse> {
|
||||
await this.esimManagementService.reissueSim(req.user.id, subscriptionId, body);
|
||||
await this.esimManagementService.reissueSim(req.user.id, params.id, body);
|
||||
|
||||
if (body.simType === "esim") {
|
||||
return { success: true, message: "eSIM profile reissue request submitted" };
|
||||
@ -444,13 +477,13 @@ export class SubscriptionsController {
|
||||
})
|
||||
async getInternetCancellationPreview(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number
|
||||
@Param() params: SubscriptionIdParamDto
|
||||
) {
|
||||
const preview = await this.internetCancellationService.getCancellationPreview(
|
||||
req.user.id,
|
||||
subscriptionId
|
||||
params.id
|
||||
);
|
||||
return { success: true as const, data: preview };
|
||||
return preview;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -460,10 +493,10 @@ export class SubscriptionsController {
|
||||
@ZodResponse({ description: "Cancel internet", type: SimActionResponseDto })
|
||||
async cancelInternet(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Body() body: InternetCancelRequestDto
|
||||
): Promise<SubscriptionActionResponse> {
|
||||
await this.internetCancellationService.submitCancellation(req.user.id, subscriptionId, body);
|
||||
await this.internetCancellationService.submitCancellation(req.user.id, params.id, body);
|
||||
return {
|
||||
success: true,
|
||||
message: `Internet cancellation scheduled for end of ${body.cancellationMonth}`,
|
||||
@ -477,20 +510,24 @@ export class SubscriptionsController {
|
||||
*/
|
||||
@Get(":id/sim/call-history/domestic")
|
||||
@Header("Cache-Control", "private, max-age=300")
|
||||
@ZodResponse({
|
||||
description: "Get domestic call history",
|
||||
type: SimDomesticCallHistoryResponseDto,
|
||||
})
|
||||
async getDomesticCallHistory(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Query() query: SimHistoryQueryDto
|
||||
): Promise<ApiSuccessResponse<SimDomesticCallHistoryResponse>> {
|
||||
): Promise<SimDomesticCallHistoryResponse> {
|
||||
const parsedQuery = simHistoryQuerySchema.parse(query as unknown);
|
||||
const result = await this.simCallHistoryService.getDomesticCallHistory(
|
||||
req.user.id,
|
||||
subscriptionId,
|
||||
params.id,
|
||||
parsedQuery.month,
|
||||
parsedQuery.page,
|
||||
parsedQuery.limit
|
||||
);
|
||||
return { success: true, data: result };
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -498,20 +535,24 @@ export class SubscriptionsController {
|
||||
*/
|
||||
@Get(":id/sim/call-history/international")
|
||||
@Header("Cache-Control", "private, max-age=300")
|
||||
@ZodResponse({
|
||||
description: "Get international call history",
|
||||
type: SimInternationalCallHistoryResponseDto,
|
||||
})
|
||||
async getInternationalCallHistory(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Query() query: SimHistoryQueryDto
|
||||
): Promise<ApiSuccessResponse<SimInternationalCallHistoryResponse>> {
|
||||
): Promise<SimInternationalCallHistoryResponse> {
|
||||
const parsedQuery = simHistoryQuerySchema.parse(query as unknown);
|
||||
const result = await this.simCallHistoryService.getInternationalCallHistory(
|
||||
req.user.id,
|
||||
subscriptionId,
|
||||
params.id,
|
||||
parsedQuery.month,
|
||||
parsedQuery.page,
|
||||
parsedQuery.limit
|
||||
);
|
||||
return { success: true, data: result };
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -519,19 +560,20 @@ export class SubscriptionsController {
|
||||
*/
|
||||
@Get(":id/sim/sms-history")
|
||||
@Header("Cache-Control", "private, max-age=300")
|
||||
@ZodResponse({ description: "Get SMS history", type: SimSmsHistoryResponseDto })
|
||||
async getSmsHistory(
|
||||
@Request() req: RequestWithUser,
|
||||
@Param("id", ParseIntPipe) subscriptionId: number,
|
||||
@Param() params: SubscriptionIdParamDto,
|
||||
@Query() query: SimHistoryQueryDto
|
||||
): Promise<ApiSuccessResponse<SimSmsHistoryResponse>> {
|
||||
): Promise<SimSmsHistoryResponse> {
|
||||
const parsedQuery = simHistoryQuerySchema.parse(query as unknown);
|
||||
const result = await this.simCallHistoryService.getSmsHistory(
|
||||
req.user.id,
|
||||
subscriptionId,
|
||||
params.id,
|
||||
parsedQuery.month,
|
||||
parsedQuery.page,
|
||||
parsedQuery.limit
|
||||
);
|
||||
return { success: true, data: result };
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ export type {
|
||||
InvoiceStatus,
|
||||
InvoiceItem,
|
||||
Invoice,
|
||||
InvoiceIdParam,
|
||||
InvoicePagination,
|
||||
InvoiceList,
|
||||
InvoiceSsoLink,
|
||||
|
||||
@ -49,6 +49,15 @@ export const invoiceSchema = z.object({
|
||||
daysOverdue: z.number().int().nonnegative().optional(),
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Route Param Schemas (BFF)
|
||||
// ============================================================================
|
||||
|
||||
export const invoiceIdParamSchema = z.object({
|
||||
id: z.coerce.number().int().positive("Invoice id must be positive"),
|
||||
});
|
||||
export type InvoiceIdParam = z.infer<typeof invoiceIdParamSchema>;
|
||||
|
||||
// Invoice Pagination Schema
|
||||
export const invoicePaginationSchema = z.object({
|
||||
page: z.number().int().nonnegative(),
|
||||
|
||||
@ -20,6 +20,7 @@ export {
|
||||
notificationListResponseSchema,
|
||||
notificationUnreadCountResponseSchema,
|
||||
notificationQuerySchema,
|
||||
notificationIdParamSchema,
|
||||
// Types
|
||||
type Notification,
|
||||
type CreateNotificationRequest,
|
||||
@ -27,4 +28,5 @@ export {
|
||||
type NotificationListResponse,
|
||||
type NotificationUnreadCountResponse,
|
||||
type NotificationQuery,
|
||||
type NotificationIdParam,
|
||||
} from "./schema.js";
|
||||
|
||||
@ -224,3 +224,12 @@ export const notificationQuerySchema = z.object({
|
||||
});
|
||||
|
||||
export type NotificationQuery = z.infer<typeof notificationQuerySchema>;
|
||||
|
||||
// =============================================================================
|
||||
// Route Param Schemas (BFF)
|
||||
// =============================================================================
|
||||
|
||||
export const notificationIdParamSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
});
|
||||
export type NotificationIdParam = z.infer<typeof notificationIdParamSchema>;
|
||||
|
||||
@ -345,17 +345,18 @@ export const checkoutCartSummarySchema = z.object({
|
||||
totals: checkoutTotalsSchema,
|
||||
});
|
||||
|
||||
export const checkoutSessionResponseSchema = apiSuccessResponseSchema(
|
||||
z.object({
|
||||
sessionId: z.string().uuid(),
|
||||
expiresAt: z.string(),
|
||||
orderType: z.enum(["Internet", "SIM", "VPN", "Other"]),
|
||||
cart: checkoutCartSummarySchema,
|
||||
})
|
||||
);
|
||||
export const checkoutSessionDataSchema = z.object({
|
||||
sessionId: z.string().uuid(),
|
||||
expiresAt: z.string(),
|
||||
orderType: z.enum(["Internet", "SIM", "VPN", "Other"]),
|
||||
cart: checkoutCartSummarySchema,
|
||||
});
|
||||
|
||||
export const checkoutSessionResponseSchema = apiSuccessResponseSchema(checkoutSessionDataSchema);
|
||||
|
||||
export const checkoutValidateCartDataSchema = z.object({ valid: z.boolean() });
|
||||
export const checkoutValidateCartResponseSchema = apiSuccessResponseSchema(
|
||||
z.object({ valid: z.boolean() })
|
||||
checkoutValidateCartDataSchema
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@ -29,6 +29,8 @@ export type {
|
||||
InternetAddonCatalogItem,
|
||||
InternetEligibilityStatus,
|
||||
InternetEligibilityDetails,
|
||||
InternetEligibilityRequest,
|
||||
InternetEligibilityRequestResponse,
|
||||
// SIM products
|
||||
SimCatalogProduct,
|
||||
SimActivationFeeCatalogItem,
|
||||
|
||||
@ -121,6 +121,10 @@ export const internetEligibilityRequestSchema = z.object({
|
||||
address: addressSchema.partial().optional(),
|
||||
});
|
||||
|
||||
export const internetEligibilityRequestResponseSchema = z.object({
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// SIM Product Schemas
|
||||
// ============================================================================
|
||||
@ -195,6 +199,9 @@ export type InternetCatalogCollection = z.infer<typeof internetCatalogCollection
|
||||
export type InternetEligibilityStatus = z.infer<typeof internetEligibilityStatusSchema>;
|
||||
export type InternetEligibilityDetails = z.infer<typeof internetEligibilityDetailsSchema>;
|
||||
export type InternetEligibilityRequest = z.infer<typeof internetEligibilityRequestSchema>;
|
||||
export type InternetEligibilityRequestResponse = z.infer<
|
||||
typeof internetEligibilityRequestResponseSchema
|
||||
>;
|
||||
|
||||
// SIM products
|
||||
export type SimCatalogProduct = z.infer<typeof simCatalogProductSchema>;
|
||||
|
||||
@ -38,6 +38,7 @@ export type {
|
||||
SimInfo,
|
||||
// Portal-facing DTOs
|
||||
SimAvailablePlan,
|
||||
SimAvailablePlanArray,
|
||||
SimCancellationMonth,
|
||||
SimCancellationPreview,
|
||||
SimReissueFullRequest,
|
||||
@ -52,6 +53,7 @@ export type {
|
||||
SimHistoryAvailableMonths,
|
||||
SimCallHistoryImportResult,
|
||||
SimSftpFiles,
|
||||
SimSftpListResult,
|
||||
// Request types
|
||||
SimTopUpRequest,
|
||||
SimPlanChangeRequest,
|
||||
|
||||
@ -176,6 +176,9 @@ export const simAvailablePlanSchema = simCatalogProductSchema.extend({
|
||||
|
||||
export type SimAvailablePlan = z.infer<typeof simAvailablePlanSchema>;
|
||||
|
||||
export const simAvailablePlanArraySchema = z.array(simAvailablePlanSchema);
|
||||
export type SimAvailablePlanArray = z.infer<typeof simAvailablePlanArraySchema>;
|
||||
|
||||
/**
|
||||
* Cancellation month option for SIM cancellation preview
|
||||
*/
|
||||
@ -252,6 +255,12 @@ export type SimCallHistoryImportResult = z.infer<typeof simCallHistoryImportResu
|
||||
export const simSftpFilesSchema = z.array(z.string());
|
||||
export type SimSftpFiles = z.infer<typeof simSftpFilesSchema>;
|
||||
|
||||
export const simSftpListResultSchema = z.object({
|
||||
path: z.string(),
|
||||
files: simSftpFilesSchema,
|
||||
});
|
||||
export type SimSftpListResult = z.infer<typeof simSftpListResultSchema>;
|
||||
|
||||
const isoDateSchema = z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Date must be in YYYY-MM-DD format");
|
||||
const timeHmsSchema = z.string().regex(/^\d{2}:\d{2}:\d{2}$/, "Time must be in HH:MM:SS format");
|
||||
|
||||
|
||||
@ -17,7 +17,9 @@ export type {
|
||||
SubscriptionStatus,
|
||||
SubscriptionCycle,
|
||||
Subscription,
|
||||
SubscriptionArray,
|
||||
SubscriptionList,
|
||||
SubscriptionIdParam,
|
||||
SubscriptionQueryParams,
|
||||
SubscriptionQuery,
|
||||
SubscriptionStats,
|
||||
|
||||
@ -51,12 +51,23 @@ export const subscriptionSchema = z.object({
|
||||
serverName: z.string().optional(),
|
||||
});
|
||||
|
||||
export const subscriptionArraySchema = z.array(subscriptionSchema);
|
||||
|
||||
// Subscription List Schema
|
||||
export const subscriptionListSchema = z.object({
|
||||
subscriptions: z.array(subscriptionSchema),
|
||||
totalCount: z.number().int().nonnegative(),
|
||||
});
|
||||
|
||||
// ============================================================================
|
||||
// Route Param Schemas (BFF)
|
||||
// ============================================================================
|
||||
|
||||
export const subscriptionIdParamSchema = z.object({
|
||||
id: z.coerce.number().int().positive("Subscription id must be positive"),
|
||||
});
|
||||
export type SubscriptionIdParam = z.infer<typeof subscriptionIdParamSchema>;
|
||||
|
||||
// ============================================================================
|
||||
// Query Parameter Schemas
|
||||
// ============================================================================
|
||||
@ -116,6 +127,7 @@ export const simPlanChangeResultSchema = apiSuccessMessageResponseSchema.extend(
|
||||
export type SubscriptionStatus = z.infer<typeof subscriptionStatusSchema>;
|
||||
export type SubscriptionCycle = z.infer<typeof subscriptionCycleSchema>;
|
||||
export type Subscription = z.infer<typeof subscriptionSchema>;
|
||||
export type SubscriptionArray = z.infer<typeof subscriptionArraySchema>;
|
||||
export type SubscriptionList = z.infer<typeof subscriptionListSchema>;
|
||||
export type SubscriptionStats = z.infer<typeof subscriptionStatsSchema>;
|
||||
export type SimActionResponse = z.infer<typeof simActionResponseSchema>;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user