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