UNPKG

defect-inspection-tools-mcp-server

Version:

Defect inspection tools server handling defect detection, analysis, and reporting with AI/ML capabilities for quality control

278 lines 10.1 kB
import { BaseService, ServiceHealthStatus } from './base-service.js'; import { SanitizationMiddleware } from '../middleware/sanitization-middleware.js'; import { ErrorHandler } from '../middleware/error-handler.js'; import { logger } from '../utils/logger.js'; import axios from 'axios'; export class ExternalApiService extends BaseService { constructor(serviceName, config) { super(serviceName, config); this.requestQueue = []; this.isProcessingQueue = false; const baseConfig = super.getConfig(); this.config = { ...baseConfig, baseURL: config.baseURL, timeout: config.timeout || 30000, maxRetries: config.maxRetries || 3, retryDelay: config.retryDelay || 1000, rateLimitPerSecond: config.rateLimitPerSecond || 10, defaultHeaders: config.defaultHeaders }; this.client = axios.create({ baseURL: this.config.baseURL, timeout: this.config.timeout, headers: this.config.defaultHeaders || {} }); this.setupInterceptors(); } // Setup axios interceptors setupInterceptors() { // Request interceptor this.client.interceptors.request.use((config) => { logger.debug(`External API request: ${config.method?.toUpperCase()} ${config.url}`, { serviceName: this.serviceName, url: config.url, method: config.method }); return config; }, (error) => { logger.error('External API request error', { serviceName: this.serviceName, error: error.message }); return Promise.reject(error); }); // Response interceptor this.client.interceptors.response.use((response) => { logger.debug(`External API response: ${response.status} ${response.statusText}`, { serviceName: this.serviceName, status: response.status, statusText: response.statusText, url: response.config.url }); return response; }, (error) => { logger.error('External API response error', { serviceName: this.serviceName, error: error.message, status: error.response?.status, statusText: error.response?.statusText, url: error.config?.url }); return Promise.reject(error); }); } // Health check implementation async healthCheck() { try { if (!this.config.baseURL) { return ServiceHealthStatus.HEALTHY; // API service without base URL is ok } // Try to make a simple request to check API health const response = await this.client.get('/health', { timeout: 5000 }); if (response.status >= 200 && response.status < 300) { return ServiceHealthStatus.HEALTHY; } else if (response.status >= 400 && response.status < 500) { return ServiceHealthStatus.DEGRADED; } else { return ServiceHealthStatus.UNHEALTHY; } } catch (error) { logger.warn(`External API health check failed for ${this.serviceName}`, { serviceName: this.serviceName, error: error.message }); return ServiceHealthStatus.DEGRADED; // Degraded rather than unhealthy for external services } } // Make HTTP request with resilience async makeRequest(options) { const context = ErrorHandler.createErrorContext(`${this.serviceName}.makeRequest`, { method: options.method || 'GET', url: options.url, hasData: !!options.data }); return this.executeWithResilience(async () => { // Rate limiting await this.enforceRateLimit(); // Sanitize request data if requested const sanitizedData = options.sanitizeRequest !== false && options.data ? SanitizationMiddleware.sanitizeToolArguments(options.data) : options.data; // Sanitize request params const sanitizedParams = options.params ? SanitizationMiddleware.sanitizeToolArguments(options.params) : options.params; const requestConfig = { method: options.method || 'GET', url: options.url, data: sanitizedData, params: sanitizedParams, headers: { ...this.config.defaultHeaders, ...options.headers }, timeout: options.timeout || this.config.timeout }; const response = await this.client.request(requestConfig); // Sanitize response data if requested const sanitizedResponseData = options.sanitizeResponse !== false ? SanitizationMiddleware.sanitizeToolResponse(response.data) : response.data; const apiResponse = { data: sanitizedResponseData, status: response.status, statusText: response.statusText, headers: response.headers, config: response.config }; logger.debug(`External API request completed successfully`, { serviceName: this.serviceName, method: options.method || 'GET', url: options.url, status: response.status, dataSize: JSON.stringify(response.data).length }); return apiResponse; }, context); } // Convenience methods for common HTTP methods async get(url, params, options = {}) { return this.makeRequest({ method: 'GET', url, params, ...options }); } async post(url, data, options = {}) { return this.makeRequest({ method: 'POST', url, data, ...options }); } async put(url, data, options = {}) { return this.makeRequest({ method: 'PUT', url, data, ...options }); } async delete(url, options = {}) { return this.makeRequest({ method: 'DELETE', url, ...options }); } async patch(url, data, options = {}) { return this.makeRequest({ method: 'PATCH', url, data, ...options }); } // Rate limiting implementation async enforceRateLimit() { return new Promise((resolve) => { this.requestQueue.push(() => Promise.resolve().then(resolve)); this.processQueue(); }); } async processQueue() { if (this.isProcessingQueue) { return; } this.isProcessingQueue = true; while (this.requestQueue.length > 0) { const resolve = this.requestQueue.shift(); if (resolve) { resolve(); // Wait for rate limit interval const waitTime = 1000 / this.config.rateLimitPerSecond; await new Promise(r => setTimeout(r, waitTime)); } } this.isProcessingQueue = false; } // Update configuration updateConfig(newConfig) { this.config = { ...this.config, ...newConfig }; // Update axios instance config if (newConfig.baseURL) { this.client.defaults.baseURL = newConfig.baseURL; } if (newConfig.timeout) { this.client.defaults.timeout = newConfig.timeout; } if (newConfig.defaultHeaders) { this.client.defaults.headers = { ...this.client.defaults.headers, ...newConfig.defaultHeaders }; } logger.info(`External API service configuration updated`, { serviceName: this.serviceName, newConfig: SanitizationMiddleware.sanitizeForLogging(newConfig) }); } // Set authentication header setAuthHeader(token, type = 'Bearer') { let authHeader; switch (type) { case 'Bearer': authHeader = `Bearer ${token}`; break; case 'Basic': authHeader = `Basic ${token}`; break; case 'ApiKey': authHeader = token; break; default: authHeader = `Bearer ${token}`; } this.client.defaults.headers.common['Authorization'] = authHeader; logger.info(`Authentication header set for ${this.serviceName}`, { serviceName: this.serviceName, type }); } // Remove authentication header removeAuthHeader() { delete this.client.defaults.headers.common['Authorization']; logger.info(`Authentication header removed for ${this.serviceName}`, { serviceName: this.serviceName }); } // Get current configuration getConfig() { return { timeout: this.config.timeout, retries: this.config.retries, retryDelay: this.config.retryDelay, circuitBreakerThreshold: this.config.circuitBreakerThreshold, circuitBreakerTimeout: this.config.circuitBreakerTimeout }; } // Get queue status getQueueStatus() { return { queueLength: this.requestQueue.length, isProcessing: this.isProcessingQueue }; } // Clear request queue clearQueue() { this.requestQueue = []; this.isProcessingQueue = false; logger.info(`Request queue cleared for ${this.serviceName}`, { serviceName: this.serviceName }); } } //# sourceMappingURL=external-api-service.js.map