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