/** * 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"; /** * Check if a mapping request has optional Salesforce account ID * This is used for warnings, not validation errors */ export function checkMappingCompleteness(request: CreateMappingRequest | UserIdMapping): string[] { const warnings: string[] = []; if (!request.sfAccountId) { warnings.push("Salesforce account ID not provided - mapping will be incomplete"); } return warnings; } /** * Validate no conflicts exist with existing mappings * Business rule: Each userId, whmcsClientId should be unique * * Note: This assumes the request has already been validated by schema. * Use createMappingRequestSchema.parse() before calling this function. */ export function validateNoConflicts( request: CreateMappingRequest, existingMappings: UserIdMapping[] ): MappingValidationResult { const errors: string[] = []; const warnings: string[] = []; // 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 * * Note: This assumes the mapping has already been validated. * This function adds business warnings about the impact of deletion. */ 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 }; } 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; }