import { Injectable } from '@nestjs/common'; import axios, { AxiosError } from 'axios'; import { mergeOutboundAxiosConfig } from '../../common/helpers/http-client.helper'; import { getCarPolicyProviderError, parseFirstCarPolicy, } from '../../common/helpers/soap-car-policy.helper'; import { InquiryType } from '../../common/enums/inquiry-type.enum'; import { ProviderEnvConfig, InquiryConfig } from '../../config/configuration'; export interface CarPlateSoapFields { plk1: string; plk2: string; plk3: string; plksrl: string; } @Injectable() export class CentInsurCarPolicyClient { async inquireByChassis( config: ProviderEnvConfig, inquiryType: InquiryType, chassisNo: string, ): Promise> { return this.callSoap( config, inquiryType, InquiryType.POLICY_BY_CHASSIS, 'CIIWSPolicyChassis', { ChassisNo: chassisNo }, ); } async inquireByPlate( config: ProviderEnvConfig, inquiryType: InquiryType, plate: CarPlateSoapFields, ): Promise> { return this.callSoap( config, inquiryType, InquiryType.POLICY_BY_PLATE, 'CIIWSPolicyVehicleMeli', { Plk1: plate.plk1, Plk2: plate.plk2, Plk3: plate.plk3, PlkSrl: plate.plksrl, }, ); } async inquireByNationalCode( config: ProviderEnvConfig, inquiryType: InquiryType, nationalCode: string, ): Promise> { return this.callSoap( config, inquiryType, InquiryType.POLICY_BY_NATIONAL_CODE, 'CIIWSPolicyNationalId', { NationalId: nationalCode }, ); } private async callSoap( config: ProviderEnvConfig, inquiryType: InquiryType, fallbackInquiryType: InquiryType, methodName: string, fields: Record, ): Promise> { const inquiryConfig = this.resolveInquiryConfig(config, inquiryType, fallbackInquiryType); if (!this.hasSoapCredentials(inquiryConfig)) { throw new Error(`${inquiryType} is not configured for CentInsur car policy SOAP`); } try { const response = await axios.post( inquiryConfig.url, this.buildCarPolicyEnvelope( methodName, fields, inquiryConfig.username, inquiryConfig.password, ), mergeOutboundAxiosConfig({ headers: { 'Content-Type': 'text/xml; charset=utf-8', SOAPAction: `"http://tempuri.org/ICarAllPlcys/${methodName}"`, }, timeout: config.timeout, responseType: 'text', }), ); const policy = parseFirstCarPolicy(response.data); const providerError = getCarPolicyProviderError(response.data, policy); if (providerError) { const error = new Error(providerError.message); (error as Error & { normalizedError: { code: string; message: string } }).normalizedError = { code: providerError.code, message: providerError.message, }; throw error; } return policy; } catch (error) { if (error instanceof AxiosError) { const wrapped = new Error(error.message); (wrapped as Error & { normalizedError: { code: string; message: string } }).normalizedError = { code: String(error.response?.status ?? 'NETWORK_ERROR'), message: error.message, }; throw wrapped; } throw error; } } resolveInquiryConfig( config: ProviderEnvConfig, inquiryType: InquiryType, fallbackInquiryType: InquiryType, ): InquiryConfig { const candidates = [ inquiryType, fallbackInquiryType, InquiryType.POLICY_BY_PLATE, InquiryType.POLICY_BY_NATIONAL_CODE, InquiryType.POLICY_BY_CHASSIS, InquiryType.CAR_BY_PLATE, InquiryType.THIRD_PARTY_CAR, ]; for (const candidate of candidates) { const inquiryConfig = config.inquiries[candidate]; if (inquiryConfig?.url) { return inquiryConfig; } } return { url: '', username: '', password: '', apiKey: '', authMethod: 'SOAP', }; } hasSoapCredentials(inquiryConfig: InquiryConfig | undefined): boolean { return Boolean(inquiryConfig?.url && inquiryConfig.username && inquiryConfig.password); } private buildCarPolicyEnvelope( methodName: string, fields: Record, username: string, password: string, ): string { const fieldXml = Object.entries(fields) .map(([name, value]) => ` <${name}>${this.escapeXml(value)}`) .join('\n'); return ` <${methodName} xmlns="http://tempuri.org/"> ${fieldXml} ${this.escapeXml(username)} ${this.escapeXml(password)} `; } private escapeXml(value: string): string { return value .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } }