/** * Support Domain - Salesforce Provider Mapper * * Transform functions to convert raw Salesforce Case records to domain types. */ import { supportCaseSchema, type SupportCase } from "../../schema.js"; import type { SalesforceCaseRecord } from "./raw.types.js"; import { getStatusDisplayLabel, getPriorityDisplayLabel, SALESFORCE_CASE_STATUS, SALESFORCE_CASE_PRIORITY, } from "./raw.types.js"; // ============================================================================ // Helper Functions // ============================================================================ /** * Safely coerce a value to string or return undefined */ function ensureString(value: unknown): string | undefined { if (typeof value === "string" && value.length > 0) { return value; } return undefined; } /** * Get current ISO timestamp */ function nowIsoString(): string { return new Date().toISOString(); } // ============================================================================ // Transform Functions // ============================================================================ /** * Transform a raw Salesforce Case record to a portal SupportCase. * * Converts Salesforce API values (often in Japanese) to portal display labels (English). * * @param record - Raw Salesforce Case record from SOQL query * @returns Validated SupportCase domain object */ export function transformSalesforceCaseToSupportCase(record: SalesforceCaseRecord): SupportCase { // Get raw values const rawStatus = ensureString(record.Status) ?? SALESFORCE_CASE_STATUS.NEW; const rawPriority = ensureString(record.Priority) ?? SALESFORCE_CASE_PRIORITY.MEDIUM; return supportCaseSchema.parse({ id: record.Id, caseNumber: record.CaseNumber, subject: ensureString(record.Subject) ?? "", // Convert Japanese SF values to English display labels status: getStatusDisplayLabel(rawStatus), priority: getPriorityDisplayLabel(rawPriority), category: ensureString(record.Type) ?? null, description: ensureString(record.Description) ?? "", createdAt: ensureString(record.CreatedDate) ?? nowIsoString(), updatedAt: ensureString(record.LastModifiedDate) ?? nowIsoString(), closedAt: ensureString(record.ClosedDate) ?? null, }); } /** * Transform multiple Salesforce Case records to SupportCase array. * * @param records - Array of raw Salesforce Case records * @returns Array of validated SupportCase domain objects */ export function transformSalesforceCasesToSupportCases( records: SalesforceCaseRecord[] ): SupportCase[] { return records.map(transformSalesforceCaseToSupportCase); } /** * Build the SOQL SELECT fields for Case queries. * * Standard Salesforce Case fields based on org configuration. * Note: Type field is not accessible via API in this org. * * @param additionalFields - Optional additional fields to include * @returns Array of field names for SOQL SELECT clause */ export function buildCaseSelectFields(additionalFields: string[] = []): string[] { const baseFields = [ // Core identifiers "Id", "CaseNumber", // Case content "Subject", "Description", // Picklist fields "Status", "Priority", "Origin", // Relationships "AccountId", "ContactId", "OwnerId", // Timestamps "CreatedDate", "LastModifiedDate", "ClosedDate", // Flags "IsEscalated", ]; return [...new Set([...baseFields, ...additionalFields])]; } /** * Build a SOQL query for fetching cases for an account. * * @param accountId - Salesforce Account ID * @param origin - Case origin to filter by (e.g., "Portal Website") * @param additionalFields - Optional additional fields to include * @returns SOQL query string */ export function buildCasesForAccountQuery( accountId: string, origin: string, additionalFields: string[] = [] ): string { const fields = buildCaseSelectFields(additionalFields).join(", "); return ` SELECT ${fields} FROM Case WHERE AccountId = '${accountId}' AND Origin = '${origin}' ORDER BY CreatedDate DESC LIMIT 100 `.trim(); } /** * Build a SOQL query for fetching a single case by ID. * * @param caseId - Salesforce Case ID * @param accountId - Salesforce Account ID (for ownership validation) * @param origin - Case origin to filter by * @param additionalFields - Optional additional fields to include * @returns SOQL query string */ export function buildCaseByIdQuery( caseId: string, accountId: string, origin: string, additionalFields: string[] = [] ): string { const fields = buildCaseSelectFields(additionalFields).join(", "); return ` SELECT ${fields} FROM Case WHERE Id = '${caseId}' AND AccountId = '${accountId}' AND Origin = '${origin}' LIMIT 1 `.trim(); }