/** * MSC (Mediterranean Shipping Company) Connector * * Implements CarrierConnectorPort for MSC API integration */ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { CarrierConnectorPort, CarrierRateSearchInput, CarrierAvailabilityInput, } from '@domain/ports/out/carrier-connector.port'; import { RateQuote } from '@domain/entities/rate-quote.entity'; import { BaseCarrierConnector, CarrierConfig } from '../base-carrier.connector'; import { MSCRequestMapper } from './msc.mapper'; @Injectable() export class MSCConnectorAdapter extends BaseCarrierConnector implements CarrierConnectorPort { private readonly apiUrl: string; private readonly apiKey: string; constructor( private readonly configService: ConfigService, private readonly requestMapper: MSCRequestMapper ) { const config: CarrierConfig = { name: 'MSC', code: 'MSCU', baseUrl: configService.get('MSC_API_URL', 'https://api.msc.com/v1'), timeout: 5000, maxRetries: 3, circuitBreakerThreshold: 50, circuitBreakerTimeout: 30000, }; super(config); this.apiUrl = config.baseUrl; this.apiKey = this.configService.get('MSC_API_KEY', ''); } async searchRates(input: CarrierRateSearchInput): Promise { this.logger.log(`Searching MSC rates: ${input.origin} -> ${input.destination}`); try { // Map internal format to MSC API format const mscRequest = this.requestMapper.toMSCRequest(input); // Make API call with circuit breaker const response = await this.makeRequest({ url: `${this.apiUrl}/rates/search`, method: 'POST', data: mscRequest, headers: { 'X-API-Key': this.apiKey, 'Content-Type': 'application/json', }, }); // Map MSC response to domain entities const rateQuotes = this.requestMapper.fromMSCResponse(response.data, input); this.logger.log(`Found ${rateQuotes.length} MSC rates`); return rateQuotes; } catch (error: any) { this.logger.error(`MSC API error: ${error?.message || 'Unknown error'}`); // Handle specific MSC error codes if (error?.response?.status === 404) { this.logger.warn('No MSC rates found for this route'); return []; } if (error?.response?.status === 429) { this.logger.error('MSC rate limit exceeded'); throw new Error('MSC_RATE_LIMIT_EXCEEDED'); } // Return empty array on error (fail gracefully) return []; } } async checkAvailability(input: CarrierAvailabilityInput): Promise { try { const response = await this.makeRequest({ url: `${this.apiUrl}/availability/check`, method: 'POST', data: { origin: input.origin, destination: input.destination, departure_date: input.departureDate, container_type: input.containerType, quantity: input.quantity, }, headers: { 'X-API-Key': this.apiKey, }, }); return (response.data as any).available_slots || 0; } catch (error: any) { this.logger.error(`MSC availability check error: ${error?.message || 'Unknown error'}`); return 0; } } }