barsa 10c8461661 Implement Zod DTOs for Request Validation and Enhance ESLint Rules
- Introduced Zod DTOs for request validation across multiple controllers, replacing inline validation with structured classes for improved maintainability and clarity.
- Updated ESLint configuration to enforce a rule against importing Zod directly in BFF controllers, promoting the use of shared domain schemas for request validation.
- Removed the SecureErrorMapperService to streamline the security module, as its functionality was deemed unnecessary.
- Enhanced various controllers to utilize the new DTOs, ensuring consistent validation and response handling across the application.
2025-12-26 13:04:15 +09:00

217 lines
7.6 KiB
TypeScript

/**
* Services Domain - Schemas
*
* Zod schemas for runtime validation of services product data.
*/
import { z } from "zod";
import { addressSchema } from "../customer/index.js";
// ============================================================================
// Base Catalog Product Schema
// ============================================================================
export const catalogProductBaseSchema = z.object({
id: z.string(),
sku: z.string(),
name: z.string(),
description: z.string().optional(),
displayOrder: z.number().optional(),
billingCycle: z.string().optional(),
monthlyPrice: z.number().optional(),
oneTimePrice: z.number().optional(),
unitPrice: z.number().optional(),
catalogMetadata: z.record(z.string(), z.unknown()).optional(),
});
// ============================================================================
// PricebookEntry Schema
// ============================================================================
export const catalogPricebookEntrySchema = z.object({
id: z.string().optional(),
name: z.string().optional(),
unitPrice: z.number().optional(),
pricebook2Id: z.string().optional(),
product2Id: z.string().optional(),
isActive: z.boolean().optional(),
});
// ============================================================================
// Internet Product Schemas
// ============================================================================
export const internetCatalogProductSchema = catalogProductBaseSchema.extend({
internetPlanTier: z.string().optional(),
internetOfferingType: z.string().optional(),
features: z.array(z.string()).optional(),
});
export const internetPlanTemplateSchema = z.object({
tierDescription: z.string(),
description: z.string().optional(),
features: z.array(z.string()).optional(),
});
export const internetPlanCatalogItemSchema = internetCatalogProductSchema.extend({
catalogMetadata: z
.object({
tierDescription: z.string().optional(),
features: z.array(z.string()).optional(),
isRecommended: z.boolean().optional(),
})
.optional(),
});
export const internetInstallationCatalogItemSchema = internetCatalogProductSchema.extend({
catalogMetadata: z
.object({
installationTerm: z.enum(["One-time", "12-Month", "24-Month"]),
})
.optional(),
});
export const internetAddonCatalogItemSchema = internetCatalogProductSchema.extend({
isBundledAddon: z.boolean().optional(),
bundledAddonId: z.string().optional(),
});
export const internetCatalogCollectionSchema = z.object({
plans: z.array(internetPlanCatalogItemSchema),
installations: z.array(internetInstallationCatalogItemSchema),
addons: z.array(internetAddonCatalogItemSchema),
});
export const internetCatalogResponseSchema = internetCatalogCollectionSchema;
// ============================================================================
// Internet Eligibility Schemas
// ============================================================================
/**
* Portal-facing internet eligibility status.
*
* NOTE: This is intentionally a small, stable enum used across BFF + Portal.
* The raw Salesforce field value is returned separately as `eligibility`.
*/
export const internetEligibilityStatusSchema = z.enum([
"not_requested",
"pending",
"eligible",
"ineligible",
]);
export const internetEligibilityDetailsSchema = z.object({
status: internetEligibilityStatusSchema,
/** Raw Salesforce value from Account.Internet_Eligibility__c (if present) */
eligibility: z.string().nullable(),
/** Salesforce Case Id (eligibility request) */
requestId: z.string().nullable(),
requestedAt: z.string().datetime().nullable(),
checkedAt: z.string().datetime().nullable(),
notes: z.string().nullable(),
});
/**
* Body for POST /services/internet/eligibility-request (BFF).
* Lives in domain so Portal + BFF stay aligned.
*/
export const internetEligibilityRequestSchema = z.object({
notes: z.string().trim().max(2000).optional(),
address: addressSchema.partial().optional(),
});
// ============================================================================
// SIM Product Schemas
// ============================================================================
export const simCatalogProductSchema = catalogProductBaseSchema.extend({
simDataSize: z.string().optional(),
simPlanType: z.string().optional(),
simHasFamilyDiscount: z.boolean().optional(),
isBundledAddon: z.boolean().optional(),
bundledAddonId: z.string().optional(),
});
export const simActivationFeeCatalogItemSchema = simCatalogProductSchema.extend({
catalogMetadata: z
.object({
isDefault: z.boolean().optional(),
})
.optional(),
});
export const simCatalogCollectionSchema = z.object({
plans: z.array(simCatalogProductSchema),
activationFees: z.array(simActivationFeeCatalogItemSchema),
addons: z.array(simCatalogProductSchema),
});
export const simCatalogResponseSchema = simCatalogCollectionSchema;
// ============================================================================
// VPN Product Schema
// ============================================================================
export const vpnCatalogProductSchema = catalogProductBaseSchema.extend({
vpnRegion: z.string().optional(),
});
export const vpnCatalogCollectionSchema = z.object({
plans: z.array(vpnCatalogProductSchema),
activationFees: z.array(vpnCatalogProductSchema),
});
export const vpnCatalogResponseSchema = vpnCatalogCollectionSchema;
// ============================================================================
// Catalog Filter Schema
// ============================================================================
/**
* Schema for catalog filtering options
*/
export const catalogFilterSchema = z.object({
category: z.string().optional(),
priceMin: z.number().optional(),
priceMax: z.number().optional(),
search: z.string().optional(),
});
// ============================================================================
// Inferred Types from Schemas (Schema-First Approach)
// ============================================================================
export type CatalogProductBase = z.infer<typeof catalogProductBaseSchema>;
export type CatalogPricebookEntry = z.infer<typeof catalogPricebookEntrySchema>;
// Internet products
export type InternetCatalogProduct = z.infer<typeof internetCatalogProductSchema>;
export type InternetPlanTemplate = z.infer<typeof internetPlanTemplateSchema>;
export type InternetPlanCatalogItem = z.infer<typeof internetPlanCatalogItemSchema>;
export type InternetInstallationCatalogItem = z.infer<typeof internetInstallationCatalogItemSchema>;
export type InternetAddonCatalogItem = z.infer<typeof internetAddonCatalogItemSchema>;
export type InternetCatalogCollection = z.infer<typeof internetCatalogCollectionSchema>;
export type InternetEligibilityStatus = z.infer<typeof internetEligibilityStatusSchema>;
export type InternetEligibilityDetails = z.infer<typeof internetEligibilityDetailsSchema>;
export type InternetEligibilityRequest = z.infer<typeof internetEligibilityRequestSchema>;
// SIM products
export type SimCatalogProduct = z.infer<typeof simCatalogProductSchema>;
export type SimActivationFeeCatalogItem = z.infer<typeof simActivationFeeCatalogItemSchema>;
export type SimCatalogCollection = z.infer<typeof simCatalogCollectionSchema>;
// VPN products
export type VpnCatalogProduct = z.infer<typeof vpnCatalogProductSchema>;
export type VpnCatalogCollection = z.infer<typeof vpnCatalogCollectionSchema>;
// Union type for all catalog products
export type CatalogProduct =
| InternetPlanCatalogItem
| InternetInstallationCatalogItem
| InternetAddonCatalogItem
| SimCatalogProduct
| SimActivationFeeCatalogItem
| VpnCatalogProduct
| CatalogProductBase;