/** * ID Mapping Domain - Validation * * Pure business validation functions for ID mappings. * These functions contain no infrastructure dependencies (no DB, no HTTP, no logging). */ import { z } from "zod"; import { createMappingRequestSchema, updateMappingRequestSchema, userIdMappingSchema, } from "./schema"; import type { CreateMappingRequest, UpdateMappingRequest, UserIdMapping, MappingValidationResult, } from "./contract"; /** * Validate a create mapping request format */ export function validateCreateRequest(request: CreateMappingRequest): MappingValidationResult { const validationResult = createMappingRequestSchema.safeParse(request); if (validationResult.success) { const warnings: string[] = []; if (!request.sfAccountId) { warnings.push("Salesforce account ID not provided - mapping will be incomplete"); } return { isValid: true, errors: [], warnings }; } const errors = validationResult.error.issues.map(issue => issue.message); return { isValid: false, errors, warnings: [] }; } /** * Validate an update mapping request format */ export function validateUpdateRequest( userId: string, request: UpdateMappingRequest ): MappingValidationResult { // First validate userId const userIdValidation = z.string().uuid().safeParse(userId); if (!userIdValidation.success) { return { isValid: false, errors: ["User ID must be a valid UUID"], warnings: [], }; } // Then validate the update request const validationResult = updateMappingRequestSchema.safeParse(request); if (validationResult.success) { return { isValid: true, errors: [], warnings: [] }; } const errors = validationResult.error.issues.map(issue => issue.message); return { isValid: false, errors, warnings: [] }; } /** * Validate an existing mapping */ export function validateExistingMapping(mapping: UserIdMapping): MappingValidationResult { const validationResult = userIdMappingSchema.safeParse(mapping); if (validationResult.success) { const warnings: string[] = []; if (!mapping.sfAccountId) { warnings.push("Mapping is missing Salesforce account ID"); } return { isValid: true, errors: [], warnings }; } const errors = validationResult.error.issues.map(issue => issue.message); return { isValid: false, errors, warnings: [] }; } /** * Validate bulk mappings */ export function validateBulkMappings( mappings: CreateMappingRequest[] ): Array<{ index: number; validation: MappingValidationResult }> { return mappings.map((mapping, index) => ({ index, validation: validateCreateRequest(mapping), })); } /** * Validate no conflicts exist with existing mappings * Business rule: Each userId, whmcsClientId should be unique */ export function validateNoConflicts( request: CreateMappingRequest, existingMappings: UserIdMapping[] ): MappingValidationResult { const errors: string[] = []; const warnings: string[] = []; // First validate the request format const formatValidation = validateCreateRequest(request); if (!formatValidation.isValid) { return formatValidation; } // Check for conflicts const duplicateUser = existingMappings.find(m => m.userId === request.userId); if (duplicateUser) { errors.push(`User ${request.userId} already has a mapping`); } const duplicateWhmcs = existingMappings.find(m => m.whmcsClientId === request.whmcsClientId); if (duplicateWhmcs) { errors.push( `WHMCS client ${request.whmcsClientId} is already mapped to user ${duplicateWhmcs.userId}` ); } if (request.sfAccountId) { const duplicateSf = existingMappings.find(m => m.sfAccountId === request.sfAccountId); if (duplicateSf) { warnings.push( `Salesforce account ${request.sfAccountId} is already mapped to user ${duplicateSf.userId}` ); } } return { isValid: errors.length === 0, errors, warnings }; } /** * Validate deletion constraints * Business rule: Warn about data access impacts */ export function validateDeletion(mapping: UserIdMapping | null | undefined): MappingValidationResult { const errors: string[] = []; const warnings: string[] = []; if (!mapping) { errors.push("Cannot delete non-existent mapping"); return { isValid: false, errors, warnings }; } // Validate the mapping format const formatValidation = validateExistingMapping(mapping); if (!formatValidation.isValid) { return formatValidation; } warnings.push( "Deleting this mapping will prevent access to WHMCS/Salesforce data for this user" ); if (mapping.sfAccountId) { warnings.push( "This mapping includes Salesforce integration - deletion will affect case management" ); } return { isValid: true, errors, warnings }; } /** * Sanitize and normalize a create mapping request * * Note: This performs basic string trimming before validation. * The schema handles validation; this is purely for data cleanup. */ export function sanitizeCreateRequest(request: CreateMappingRequest): CreateMappingRequest { return { userId: request.userId?.trim(), whmcsClientId: request.whmcsClientId, sfAccountId: request.sfAccountId?.trim() || undefined, }; } /** * Sanitize and normalize an update mapping request * * Note: This performs basic string trimming before validation. * The schema handles validation; this is purely for data cleanup. */ export function sanitizeUpdateRequest(request: UpdateMappingRequest): UpdateMappingRequest { const sanitized: Partial = {}; if (request.whmcsClientId !== undefined) { sanitized.whmcsClientId = request.whmcsClientId; } if (request.sfAccountId !== undefined) { sanitized.sfAccountId = request.sfAccountId?.trim() || undefined; } return sanitized; }