refactor: domain package cleanup
- Remove validation wrapper functions from common/validation.ts (use Zod schemas directly) - Delete duplicate CheckoutItem/CheckoutTotals/CheckoutCart/OrderCreateResponse from orders/contract.ts - Delete empty orders/checkout.ts - Remove unused MIGRATION_STEPS/MIGRATION_TRANSFER_ITEMS UI constants from auth/forms.ts - Standardize checkout/contract.ts to not re-export schema types - Fix customer/providers/index.ts to not re-export contract types through providers barrel
This commit is contained in:
parent
6e51012d21
commit
0c63bc5c33
@ -1,11 +1,10 @@
|
||||
import { BadRequestException } from "@nestjs/common";
|
||||
import { validateUuidV4OrThrow } from "@customer-portal/domain/common";
|
||||
import { uuidSchema } from "@customer-portal/domain/common";
|
||||
|
||||
export function parseUuidOrThrow(id: string, message = "Invalid ID format"): string {
|
||||
try {
|
||||
// Domain validator throws its own Error; we translate to a BadRequestException with our message.
|
||||
return validateUuidV4OrThrow(id);
|
||||
} catch {
|
||||
const result = uuidSchema.safeParse(id);
|
||||
if (!result.success) {
|
||||
throw new BadRequestException(message);
|
||||
}
|
||||
return result.data;
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import { Logger } from "nestjs-pino";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
|
||||
import { SalesforceConnection } from "./salesforce-connection.service.js";
|
||||
import type { SalesforceAccountRecord } from "@customer-portal/domain/customer/providers";
|
||||
import type { SalesforceAccountRecord } from "@customer-portal/domain/customer";
|
||||
import type { SalesforceResponse } from "@customer-portal/domain/common/providers";
|
||||
import { customerNumberSchema, salesforceIdSchema } from "@customer-portal/domain/common";
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Injectable, BadRequestException } from "@nestjs/common";
|
||||
import type { User as PrismaUser } from "@prisma/client";
|
||||
import { PrismaService } from "@bff/infra/database/prisma.service.js";
|
||||
import { normalizeAndValidateEmail } from "@customer-portal/domain/common";
|
||||
import { emailSchema } from "@customer-portal/domain/common";
|
||||
import { extractErrorMessage } from "@bff/core/utils/error.util.js";
|
||||
import { parseUuidOrThrow } from "@bff/core/utils/validation.util.js";
|
||||
|
||||
@ -17,7 +17,7 @@ export class UserAuthRepository {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async findByEmail(email: string): Promise<PrismaUser | null> {
|
||||
const normalized = normalizeAndValidateEmail(email);
|
||||
const normalized = emailSchema.parse(email);
|
||||
try {
|
||||
return await this.prisma.user.findUnique({ where: { email: normalized } });
|
||||
} catch (error) {
|
||||
@ -41,7 +41,7 @@ export class UserAuthRepository {
|
||||
throw new BadRequestException("Email is required to create a user");
|
||||
}
|
||||
|
||||
const normalizedEmail = normalizeAndValidateEmail(data.email);
|
||||
const normalizedEmail = emailSchema.parse(data.email);
|
||||
|
||||
try {
|
||||
return await this.prisma.user.create({
|
||||
@ -72,7 +72,7 @@ export class UserAuthRepository {
|
||||
|
||||
async updateEmail(id: string, email: string): Promise<void> {
|
||||
const validId = parseUuidOrThrow(id, INVALID_USER_ID_FORMAT);
|
||||
const normalized = normalizeAndValidateEmail(email);
|
||||
const normalized = emailSchema.parse(email);
|
||||
try {
|
||||
await this.prisma.user.update({
|
||||
where: { id: validId },
|
||||
|
||||
@ -56,21 +56,3 @@ export function getPasswordStrengthDisplay(strength: number): {
|
||||
if (strength >= 60) return { label: "Fair", colorClass: "bg-yellow-500" };
|
||||
return { label: "Weak", colorClass: "bg-red-500" };
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Migration Info (Business Constants)
|
||||
// ============================================================================
|
||||
|
||||
export const MIGRATION_TRANSFER_ITEMS = [
|
||||
"All active services",
|
||||
"Billing history",
|
||||
"Support cases",
|
||||
"Account details",
|
||||
] as const;
|
||||
|
||||
export const MIGRATION_STEPS = [
|
||||
"Enter your legacy portal email and password",
|
||||
"We verify your account and migrate your data",
|
||||
"Create a new secure password for the upgraded portal",
|
||||
"Access your dashboard with all your services ready",
|
||||
] as const;
|
||||
|
||||
@ -107,8 +107,6 @@ export {
|
||||
PASSWORD_REQUIREMENTS,
|
||||
checkPasswordStrength,
|
||||
getPasswordStrengthDisplay,
|
||||
MIGRATION_TRANSFER_ITEMS,
|
||||
MIGRATION_STEPS,
|
||||
type PasswordRequirementKey,
|
||||
} from "./forms.js";
|
||||
|
||||
|
||||
@ -19,5 +19,5 @@ export const CHECKOUT_ORDER_TYPE = {
|
||||
|
||||
export type CheckoutOrderTypeValue = (typeof CHECKOUT_ORDER_TYPE)[keyof typeof CHECKOUT_ORDER_TYPE];
|
||||
|
||||
// Re-export types from schema
|
||||
export type { OrderType, PriceBreakdownItem, CartItem } from "./schema.js";
|
||||
// Schema-derived types (OrderType, PriceBreakdownItem, CartItem)
|
||||
// are exported from index.ts, not contract.ts.
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
/**
|
||||
* Common Domain - Validation Utilities
|
||||
*
|
||||
* Generic validation functions used across all domains.
|
||||
* These are pure functions with no infrastructure dependencies.
|
||||
* Generic validation schemas used across all domains.
|
||||
* These are pure schemas with no infrastructure dependencies.
|
||||
*/
|
||||
|
||||
import { z } from "zod";
|
||||
@ -23,49 +23,3 @@ export const requiredStringSchema = z.string().min(1, "This field is required").
|
||||
* Generic schema for customer/account identifiers
|
||||
*/
|
||||
export const customerNumberSchema = z.string().min(1, "Customer number is required").trim();
|
||||
|
||||
/**
|
||||
* Normalize and validate an email address
|
||||
*
|
||||
* This is a convenience wrapper that throws on invalid input.
|
||||
* For validation without throwing, use the emailSchema directly with .safeParse()
|
||||
*
|
||||
* @throws Error if email format is invalid
|
||||
*/
|
||||
export function normalizeAndValidateEmail(email: string): string {
|
||||
const emailValidationSchema = z
|
||||
.string()
|
||||
.email()
|
||||
.transform(e => e.toLowerCase().trim());
|
||||
return emailValidationSchema.parse(email);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a UUID (v4)
|
||||
*
|
||||
* This is a convenience wrapper that throws on invalid input.
|
||||
* For validation without throwing, use the uuidSchema directly with .safeParse()
|
||||
*
|
||||
* @throws Error if UUID format is invalid
|
||||
*/
|
||||
export function validateUuidV4OrThrow(id: string, message = "Invalid UUID format"): string {
|
||||
const parsed = uuidSchema.safeParse(id);
|
||||
if (!parsed.success) {
|
||||
throw new Error(message);
|
||||
}
|
||||
return parsed.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a valid email (non-throwing)
|
||||
*/
|
||||
export function isValidEmail(email: string): boolean {
|
||||
return z.string().email().safeParse(email).success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a string is a valid UUID (non-throwing)
|
||||
*/
|
||||
export function isValidUuid(id: string): boolean {
|
||||
return uuidSchema.safeParse(id).success;
|
||||
}
|
||||
|
||||
@ -15,7 +15,13 @@
|
||||
// Constants
|
||||
// ============================================================================
|
||||
|
||||
export { USER_ROLE, type UserRoleValue } from "./contract.js";
|
||||
export {
|
||||
USER_ROLE,
|
||||
type UserRoleValue,
|
||||
type SalesforceAccountFieldMap,
|
||||
type SalesforceAccountRecord,
|
||||
type SalesforceContactRecord,
|
||||
} from "./contract.js";
|
||||
|
||||
// ============================================================================
|
||||
// Domain Types (Clean Names - Public API)
|
||||
|
||||
@ -9,12 +9,7 @@
|
||||
export * from "./portal/index.js";
|
||||
export * from "./whmcs/index.js";
|
||||
|
||||
// Provider-specific integration types (BFF-only convenience re-exports)
|
||||
export type {
|
||||
SalesforceAccountFieldMap,
|
||||
SalesforceAccountRecord,
|
||||
SalesforceContactRecord,
|
||||
} from "../contract.js";
|
||||
// Provider-specific WHMCS integration types (BFF-only)
|
||||
export type {
|
||||
WhmcsAddClientParams,
|
||||
WhmcsValidateLoginParams,
|
||||
|
||||
@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Orders Domain - Checkout Types
|
||||
*
|
||||
* Minimal type definitions for checkout flow.
|
||||
* Frontend handles its own URL param serialization.
|
||||
*/
|
||||
|
||||
// This file is intentionally minimal after cleanup.
|
||||
// The build/derive/normalize functions were removed as they were
|
||||
// unnecessary abstractions that should be handled by the frontend.
|
||||
//
|
||||
// See CLEANUP_PROPOSAL_NORMALIZERS.md for details.
|
||||
@ -5,8 +5,6 @@
|
||||
* Validated types are derived from schemas (see schema.ts).
|
||||
*/
|
||||
|
||||
import type { OrderConfigurations } from "./schema.js";
|
||||
|
||||
// ============================================================================
|
||||
// Order Type Constants
|
||||
// ============================================================================
|
||||
@ -132,74 +130,9 @@ export interface UserMapping {
|
||||
sfAccountId?: string | null | undefined;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Checkout Types
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Individual item in checkout cart
|
||||
*/
|
||||
export interface CheckoutItem {
|
||||
id: string;
|
||||
sku: string;
|
||||
name: string;
|
||||
description?: string | undefined;
|
||||
monthlyPrice?: number | undefined;
|
||||
oneTimePrice?: number | undefined;
|
||||
quantity: number;
|
||||
itemType: "plan" | "installation" | "addon" | "activation" | "vpn";
|
||||
autoAdded?: boolean | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkout totals calculation
|
||||
*/
|
||||
export interface CheckoutTotals {
|
||||
monthlyTotal: number;
|
||||
oneTimeTotal: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete checkout cart with items, totals, and configuration
|
||||
*/
|
||||
export interface CheckoutCart {
|
||||
items: CheckoutItem[];
|
||||
totals: CheckoutTotals;
|
||||
configuration: OrderConfigurations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Order creation response from BFF
|
||||
*/
|
||||
export interface OrderCreateResponse {
|
||||
sfOrderId: string;
|
||||
status: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Order fulfillment validation result
|
||||
*/
|
||||
// NOTE: Provider-specific fulfillment types are intentionally not part of the public orders contract.
|
||||
// BFF integration code should import them from:
|
||||
// @customer-portal/domain/orders/providers
|
||||
|
||||
// ============================================================================
|
||||
// Re-export Types from Schema (Schema-First Approach)
|
||||
// ============================================================================
|
||||
|
||||
export type {
|
||||
// Order item types
|
||||
OrderItemSummary,
|
||||
OrderItemDetails,
|
||||
// Order types
|
||||
OrderSummary,
|
||||
OrderDetails,
|
||||
// Query and creation types
|
||||
OrderQueryParams,
|
||||
OrderConfigurationsAddress,
|
||||
OrderConfigurations,
|
||||
CreateOrderRequest,
|
||||
OrderBusinessValidation,
|
||||
SfOrderIdParam,
|
||||
} from "./schema.js";
|
||||
//
|
||||
// Checkout types (CheckoutItem, CheckoutTotals, CheckoutCart, OrderCreateResponse)
|
||||
// and other schema-derived types are exported from schema.ts directly.
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import {
|
||||
orderConfigurationsSchema,
|
||||
orderSelectionsSchema,
|
||||
type CheckoutTotals,
|
||||
type OrderConfigurations,
|
||||
type OrderSelections,
|
||||
type OrderItemSummary,
|
||||
@ -10,7 +11,6 @@ import {
|
||||
type OrderDisplayItemChargeKind,
|
||||
} from "./schema.js";
|
||||
import { ORDER_TYPE } from "./contract.js";
|
||||
import type { CheckoutTotals } from "./contract.js";
|
||||
import type { SimConfigureFormData } from "../sim/index.js";
|
||||
import type { WhmcsOrderItem } from "./providers/whmcs/raw.types.js";
|
||||
|
||||
|
||||
@ -15,11 +15,6 @@ export {
|
||||
type OrderType,
|
||||
type OrderTypeValue,
|
||||
type UserMapping,
|
||||
// Checkout types
|
||||
type CheckoutItem,
|
||||
type CheckoutTotals,
|
||||
type CheckoutCart,
|
||||
type OrderCreateResponse,
|
||||
// Constants
|
||||
ORDER_TYPE,
|
||||
ORDER_STATUS,
|
||||
|
||||
@ -9,7 +9,7 @@ import type {
|
||||
OrderItemDetails,
|
||||
OrderItemSummary,
|
||||
OrderSummary,
|
||||
} from "../../contract.js";
|
||||
} from "../../schema.js";
|
||||
import { normalizeBillingCycle } from "../../helpers.js";
|
||||
import { orderDetailsSchema, orderSummarySchema, orderItemDetailsSchema } from "../../schema.js";
|
||||
import type {
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
* Transforms normalized order data to WHMCS API format.
|
||||
*/
|
||||
|
||||
import type { OrderDetails, OrderItemDetails } from "../../contract.js";
|
||||
import type { OrderDetails, OrderItemDetails } from "../../schema.js";
|
||||
import { normalizeBillingCycle } from "../../helpers.js";
|
||||
import { serializeWhmcsKeyValueMap } from "../../../common/providers/whmcs-utils/index.js";
|
||||
import {
|
||||
|
||||
@ -387,6 +387,10 @@ export type OrderConfigurationsAddress = z.infer<typeof orderConfigurationsAddre
|
||||
export type OrderConfigurations = z.infer<typeof orderConfigurationsSchema>;
|
||||
export type CreateOrderRequest = z.infer<typeof createOrderRequestSchema>;
|
||||
export type OrderBusinessValidation = z.infer<typeof orderBusinessValidationSchema>;
|
||||
export type CheckoutItem = z.infer<typeof checkoutItemSchema>;
|
||||
export type CheckoutTotals = z.infer<typeof checkoutTotalsSchema>;
|
||||
export type CheckoutCart = z.infer<typeof checkoutCartSchema>;
|
||||
export type OrderCreateResponse = z.infer<typeof orderCreateResponseSchema>;
|
||||
export type CheckoutBuildCartRequest = z.infer<typeof checkoutBuildCartRequestSchema>;
|
||||
export type CheckoutBuildCartResponse = z.infer<typeof checkoutBuildCartResponseSchema>;
|
||||
export type CheckoutSessionCreateOrderRequest = z.infer<
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import {
|
||||
orderConfigurationsSchema,
|
||||
orderSelectionsSchema,
|
||||
type CheckoutCart,
|
||||
type OrderConfigurations,
|
||||
type CreateOrderRequest,
|
||||
type OrderSelections,
|
||||
} from "./schema.js";
|
||||
import { ORDER_TYPE, type CheckoutCart, type OrderTypeValue } from "./contract.js";
|
||||
import { ORDER_TYPE, type OrderTypeValue } from "./contract.js";
|
||||
|
||||
export type CheckoutOrderTypeValue = Extract<OrderTypeValue, "Internet" | "SIM" | "VPN">;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user