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
JavaScript
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