UNPKG

digital-samba-mcp-server

Version:

Digital Samba MCP Server - Model Context Protocol server for Digital Samba's video conferencing API

342 lines 13.8 kB
/** * Enhanced Digital Samba API Client Module - Integration File * * This file extends the base digital-samba-api.ts to integrate the new * token-manager, connection-manager, and resource-optimizer features. * It enhances the existing API client with improved connection handling, * automatic token refresh, and optimized resource usage for high-traffic * scenarios. * * @module digital-samba-api-enhanced * @author Digital Samba Team * @version 0.1.0 */ // Local modules import { DigitalSambaApiClient } from './digital-samba-api.js'; import { ConnectionManager } from './connection-manager.js'; import { TokenManager } from './token-manager.js'; import { ResourceOptimizer } from './resource-optimizer.js'; import logger from './logger.js'; /** * Enhanced Digital Samba API Client * * This class extends the base DigitalSambaApiClient with improved connection handling, * automatic token refresh, and optimized resource usage for high-traffic scenarios. */ export class EnhancedDigitalSambaApiClient extends DigitalSambaApiClient { /** * Creates an instance of the Enhanced Digital Samba API Client * * @constructor * @param {string} [apiKey] - Optional API key for direct authentication * @param {string} [apiBaseUrl='https://api.digitalsamba.com/api/v1'] - Base URL for the Digital Samba API * @param {MemoryCache} [cache] - Optional cache instance * @param {EnhancedApiClientOptions} [options] - Additional options */ constructor(apiKey, apiBaseUrl = 'https://api.digitalsamba.com/api/v1', cache, options = {}) { super(apiKey, apiBaseUrl, cache); this.tokenManagers = new Map(); // Set enabled features this.enabledFeatures = { connectionManagement: options.enableConnectionManagement !== false, tokenManagement: options.enableTokenManagement !== false, resourceOptimization: options.enableResourceOptimization !== false }; // Initialize connection manager if enabled if (this.enabledFeatures.connectionManagement) { this.connectionManager = new ConnectionManager({ apiUrl: apiBaseUrl, poolSize: options.connectionPoolSize || 5 }); // Set up connection event handlers this.setupConnectionEventHandlers(); logger.info('Enhanced API client initialized with connection management', { poolSize: options.connectionPoolSize || 5 }); } // Initialize resource optimizer if enabled if (this.enabledFeatures.resourceOptimization) { this.resourceOptimizer = new ResourceOptimizer(); logger.info('Enhanced API client initialized with resource optimization'); } logger.info('Enhanced Digital Samba API Client initialized', { enabledFeatures: this.enabledFeatures }); } /** * Set up connection event handlers */ setupConnectionEventHandlers() { if (!this.connectionManager) return; // Connection error handler this.connectionManager.on('connection:error', ({ connectionId, error }) => { logger.warn(`Connection error on ${connectionId}: ${error}`); }); // Reconnect events this.connectionManager.on('reconnect:scheduled', ({ attempt, delayMs }) => { logger.info(`Reconnect scheduled (attempt ${attempt}), delay: ${delayMs}ms`); }); this.connectionManager.on('reconnect:success', ({ successCount, totalConnections }) => { logger.info(`Reconnect successful, ${successCount}/${totalConnections} connections restored`); }); this.connectionManager.on('reconnect:failed', ({ attempts, max }) => { logger.error(`Reconnect failed after ${attempts}/${max} attempts`); }); } /** * Override the request method to use the connection manager * @param endpoint API endpoint * @param options Request options * @returns Promise resolving to the response data */ async request(endpoint, options = {}) { // Use connection manager if enabled if (this.enabledFeatures.connectionManagement && this.connectionManager) { try { const url = endpoint.startsWith('http') ? endpoint : `${this.apiBaseUrl}${endpoint}`; // Get API key const apiKey = this.getApiKey(); // Set authorization header const headers = { 'Authorization': `Bearer ${apiKey}`, 'Content-Type': 'application/json', ...options.headers }; // Execute request through connection manager // Type assertion to handle compatibility between node-fetch and standard fetch types const fetchOptions = { ...options, headers: headers }; // Execute request through connection manager const response = await this.connectionManager.fetch(url, fetchOptions); // Handle response if (!response.ok) { const errorText = await response.text(); logger.error(`API Error Response: ${errorText}`, { status: response.status, statusText: response.statusText }); // Parse error text as JSON if possible let errorData; try { errorData = JSON.parse(errorText); } catch { // Not JSON, use as plain text errorData = { message: errorText }; } // Handle specific error types (reusing the parent class logic) throw this.handleErrorResponse(response.status, errorData, errorText, endpoint); } // Return empty object for 204 No Content responses if (response.status === 204) { return {}; } // Parse response const responseData = await response.json(); // Apply resource optimization if enabled if (this.enabledFeatures.resourceOptimization && this.resourceOptimizer) { return this.resourceOptimizer.compressResponse(responseData); } return responseData; } catch (error) { // Let parent class handle error types throw error; } } else { // Fall back to parent implementation return super.request(endpoint, options); } } /** * Handle error response - helper method to match parent class behavior */ handleErrorResponse(status, errorData, errorText, endpoint) { // This method simulates the error handling in the parent class // In a real implementation, consider refactoring to avoid duplication if (status === 400) { // Validation error return new Error(`Validation error: ${errorData.message || errorText}`); } else if (status === 401 || status === 403) { // Authentication error return new Error(`Authentication error: ${errorData.message || errorText}`); } else if (status === 404) { // Not Found error const matches = endpoint.match(/\/([^\/]+)\/([^\/]+)/); const resourceType = matches ? matches[1] : 'resource'; const resourceId = matches ? matches[2] : 'unknown'; return new Error(`Resource not found: ${resourceType} ${resourceId}`); } else { // Generic API error return new Error(`Digital Samba API error (${status}): ${errorData.message || errorText}`); } } /** * Create a token manager for a room * @param roomId Room ID * @param sessionId Session ID * @param tokenOptions Token options * @returns Token manager for the room */ createTokenManager(roomId, sessionId, tokenOptions = {}) { // Skip if token management is disabled if (!this.enabledFeatures.tokenManagement) { throw new Error('Token management is disabled for this client'); } // Check if token manager already exists const existingManager = this.tokenManagers.get(roomId); if (existingManager) { return existingManager; } // Create new token manager const tokenManager = new TokenManager({ roomId, tokenOptions: { ...tokenOptions, exp: tokenOptions.exp || '60' // Default to 1 hour }, apiUrl: this.apiBaseUrl }); // Store token manager this.tokenManagers.set(roomId, tokenManager); logger.info('Created token manager', { roomId, sessionId }); return tokenManager; } /** * Get a token manager for a room * @param roomId Room ID * @returns Token manager for the room or undefined if not found */ getTokenManager(roomId) { return this.tokenManagers.get(roomId); } /** * Generate a room token with automatic refresh * @param roomId Room ID * @param options Token options * @param sessionId Session ID for token context * @returns Promise resolving to token response */ async generateRoomTokenWithRefresh(roomId, options = {}, sessionId) { // Skip if token management is disabled if (!this.enabledFeatures.tokenManagement) { // Fall back to standard token generation return this.generateRoomToken(roomId, options); } // Get or create token manager let tokenManager = this.getTokenManager(roomId); if (!tokenManager) { tokenManager = this.createTokenManager(roomId, sessionId, options); } // Generate token const token = await tokenManager.generateToken(sessionId, this.getApiKey()); return { token: token.token, link: token.link, expiresAt: token.expiresAt.toISOString() }; } /** * Batch API requests of the same type * @param batchId Batch identifier * @param key Request key * @param executor Function to execute the batch * @returns Promise resolving to the result */ batchRequest(batchId, key, executor) { // Skip if resource optimization is disabled if (!this.enabledFeatures.resourceOptimization || !this.resourceOptimizer) { throw new Error('Resource optimization is disabled for this client'); } return this.resourceOptimizer.batchRequest(batchId, key, executor); } /** * Load data incrementally * @param dataLoader Function to load data * @param pageSize Page size * @param maxPages Maximum number of pages to load * @returns Promise resolving to the loaded data */ loadIncrementally(dataLoader, pageSize = 20, maxPages = 10) { // Skip if resource optimization is disabled if (!this.enabledFeatures.resourceOptimization || !this.resourceOptimizer) { throw new Error('Resource optimization is disabled for this client'); } return this.resourceOptimizer.loadIncrementally(dataLoader, pageSize, maxPages); } /** * Get client health status * @returns Whether the client is healthy */ isHealthy() { if (this.enabledFeatures.connectionManagement && this.connectionManager) { return this.connectionManager.isHealthy(); } // Default to true if connection management is disabled return true; } /** * Get client statistics * @returns Client statistics */ getStats() { const stats = {}; if (this.enabledFeatures.connectionManagement && this.connectionManager) { stats.connectionManager = this.connectionManager.getStats(); } if (this.enabledFeatures.tokenManagement) { stats.tokenManagers = { count: this.tokenManagers.size, rooms: Array.from(this.tokenManagers.keys()) }; } if (this.enabledFeatures.resourceOptimization && this.resourceOptimizer) { stats.resourceOptimizer = this.resourceOptimizer.getStats(); } return stats; } /** * Clean up resources */ destroy() { // Clean up connection manager if (this.connectionManager) { this.connectionManager.destroy(); } // Clean up token managers for (const tokenManager of this.tokenManagers.values()) { tokenManager.destroy(); } this.tokenManagers.clear(); // Clean up resource optimizer if (this.resourceOptimizer) { this.resourceOptimizer.destroy(); } logger.info('Enhanced Digital Samba API Client destroyed'); } } /** * Create an enhanced API client * @param apiKey API key * @param apiBaseUrl API base URL * @param options Additional options * @returns A new enhanced API client instance */ export function createEnhancedApiClient(apiKey, apiBaseUrl, options = {}) { return new EnhancedDigitalSambaApiClient(apiKey, apiBaseUrl, options.cache, options); } /** * Export default enhanced API client utilities */ export default { EnhancedDigitalSambaApiClient, createEnhancedApiClient }; //# sourceMappingURL=digital-samba-api-enhanced.js.map