import axios from "axios"; import { Buffer } from "buffer"; import { BaseService } from "../base/BaseService.js"; import { AircallDataManager } from "./AircallDataManager.js"; export class AircallService extends BaseService { constructor(config) { super(config); this.baseUrl = "https://api.aircall.io/v1"; console.log('Initializing Aircall service with credentials:', { apiId: config.apiId ? 'present' : 'missing', apiToken: config.apiToken ? 'present' : 'missing' }); this.auth = Buffer.from(`${config.apiId}:${config.apiToken}`).toString( "base64" ); this.dataManager = new AircallDataManager( this.mongodb, this.redis, this.timeManager ); if (!config.apiId || !config.apiToken) { throw new Error("Aircall API credentials are required"); } } async getMetrics(timeRange) { const dateRange = await this.timeManager.getDateRange(timeRange); console.log('Fetching metrics for date range:', { start: dateRange.start.toISOString(), end: dateRange.end.toISOString() }); return this.dataManager.getData(dateRange, async (range) => { const calls = await this.fetchAllCalls(range.start, range.end); console.log('Fetched calls:', { count: calls.length, sample: calls.length > 0 ? calls[0] : null }); return calls; }); } async fetchAllCalls(start, end) { try { let allCalls = []; let currentPage = 1; let hasMore = true; let totalPages = null; while (hasMore) { const response = await this.makeRequest("/calls", { from: Math.floor(start.getTime() / 1000), to: Math.floor(end.getTime() / 1000), order: "asc", page: currentPage, per_page: 50, }); console.log('API Response:', { page: currentPage, totalPages: response.meta.total_pages, callsCount: response.calls?.length, params: { from: Math.floor(start.getTime() / 1000), to: Math.floor(end.getTime() / 1000) } }); if (!response.calls) { throw new Error("Invalid API response format"); } allCalls = [...allCalls, ...response.calls]; hasMore = response.meta.next_page_link !== null; totalPages = response.meta.total_pages; currentPage++; if (hasMore) { // Rate limiting pause await new Promise((resolve) => setTimeout(resolve, 1)); } } return allCalls; } catch (error) { console.error("Error fetching all calls:", error); throw error; } } async makeRequest(endpoint, params = {}) { try { console.log('Making API request:', { endpoint, params }); const response = await axios.get(`${this.baseUrl}${endpoint}`, { headers: { Authorization: `Basic ${this.auth}`, "Content-Type": "application/json", }, params, }); return response.data; } catch (error) { if (error.response?.status === 429) { console.log("Rate limit reached, waiting before retry..."); await new Promise((resolve) => setTimeout(resolve, 5000)); return this.makeRequest(endpoint, params); } this.handleApiError(error, `Error making request to ${endpoint}`); } } validateApiResponse(response, context = "") { if (!response || typeof response !== "object") { throw new Error(`${context}: Invalid API response format`); } if (response.error) { throw new Error(`${context}: ${response.error}`); } return true; } getPaginationInfo(meta) { return { currentPage: meta.current_page, totalPages: meta.total_pages, hasNextPage: meta.next_page_link !== null, totalRecords: meta.total, }; } }