/** * Datadog API Client * Handles authentication, rate limiting, and API requests */ import axios, { AxiosInstance, AxiosError } from 'axios'; import Bottleneck from 'bottleneck'; import type { DatadogConfig } from '../types/index.js'; export class DatadogClient { private client: AxiosInstance; private limiter: Bottleneck; private apiKey: string; private appKey: string; constructor(config: DatadogConfig) { this.apiKey = config.apiKey; this.appKey = config.appKey; // Initialize rate limiter this.limiter = new Bottleneck({ maxConcurrent: config.rateLimit?.maxConcurrent || 10, minTime: config.rateLimit?.minTime || 100, }); // Initialize axios client const site = config.site || 'datadoghq.com'; this.client = axios.create({ baseURL: `https://api.${site}/api`, headers: { 'Content-Type': 'application/json', 'DD-API-KEY': this.apiKey, 'DD-APPLICATION-KEY': this.appKey, }, }); // Add response interceptor for error handling this.client.interceptors.response.use( (response) => response, (error: AxiosError) => { return Promise.reject(this.handleError(error)); } ); } private handleError(error: AxiosError): Error { if (error.response) { const status = error.response.status; const data = error.response.data as any; switch (status) { case 400: return new Error(`Datadog API bad request: ${JSON.stringify(data)}`); case 401: return new Error('Datadog API authentication failed. Check your API key.'); case 403: return new Error('Datadog API access forbidden. Check your application key permissions.'); case 404: return new Error('Datadog API resource not found.'); case 429: return new Error('Datadog API rate limit exceeded. Please retry later.'); case 500: case 502: case 503: return new Error('Datadog API server error. Please retry later.'); default: return new Error(`Datadog API error (${status}): ${JSON.stringify(data)}`); } } else if (error.request) { return new Error('Datadog API request failed. No response received.'); } else { return new Error(`Datadog API error: ${error.message}`); } } async get(path: string, params?: Record): Promise { return this.limiter.schedule(async () => { const response = await this.client.get(path, { params }); return response.data; }); } async post(path: string, data?: any): Promise { return this.limiter.schedule(async () => { const response = await this.client.post(path, data); return response.data; }); } async put(path: string, data?: any): Promise { return this.limiter.schedule(async () => { const response = await this.client.put(path, data); return response.data; }); } async patch(path: string, data?: any): Promise { return this.limiter.schedule(async () => { const response = await this.client.patch(path, data); return response.data; }); } async delete(path: string): Promise { return this.limiter.schedule(async () => { const response = await this.client.delete(path); return response.data; }); } }