/** * @file rr-error.js * @description Centralized error class and assertion logic for Reynolds & Reynolds API calls. * Provides consistent handling across all RR modules (customer, repair order, lookups, etc.) */ /** * Custom Error type for RR API responses */ class RrApiError extends Error { constructor(message, { reqId, url, apiName, errorData, status, statusText } = {}) { super(message); this.name = "RrApiError"; this.reqId = reqId || null; this.url = url || null; this.apiName = apiName || null; this.errorData = errorData || null; this.status = status || null; this.statusText = statusText || null; } } /** * Assert that a Reynolds & Reynolds response is successful. * * Expected success structure (based on Rome RR specs): * { * "SuccessFlag": true, * "ErrorCode": "0", * "ErrorMessage": "", * "Data": { ... } * } * * Or if SOAP/XML-based: * { * "Envelope": { * "Body": { * "Response": { * "SuccessFlag": true, * ... * } * } * } * } * * This helper unwraps and normalizes the response to detect any error cases. */ function assertRrOk(data, { apiName = "RR API Call", allowEmpty = false } = {}) { if (!data && !allowEmpty) { throw new RrApiError(`${apiName} returned no data`, { apiName }); } // Normalize envelope const response = data?.Envelope?.Body?.Response || data?.Envelope?.Body?.[Object.keys(data.Envelope?.Body || {})[0]] || data?.Response || data; // Handle array of errors or error objects const errorBlock = response?.Errors || response?.Error || response?.Fault || null; // Basic success conditions per RR documentation const success = response?.SuccessFlag === true || response?.ErrorCode === "0" || response?.ResultCode === "0" || (Array.isArray(errorBlock) && errorBlock.length === 0); // If success, return normalized response if (success || allowEmpty) { return response?.Data || response; } // Construct contextual error info const errorMessage = response?.ErrorMessage || response?.FaultString || response?.Message || "Unknown RR API error"; throw new RrApiError(`${apiName} failed: ${errorMessage}`, { apiName, errorData: response, status: response?.ErrorCode || response?.ResultCode }); } /** * Safely unwrap nested RR API responses for consistency across handlers. */ function extractRrResponseData(data) { if (!data) return null; return ( data?.Envelope?.Body?.Response?.Data || data?.Envelope?.Body?.[Object.keys(data.Envelope?.Body || {})[0]]?.Data || data?.Data || data ); } module.exports = { RrApiError, assertRrOk, extractRrResponseData };