UNPKG

mcp-quiz-server

Version:

🧠 AI-Powered Quiz Management via Model Context Protocol (MCP) - Create, manage, and take quizzes directly from VS Code, Claude, and other AI agents.

279 lines (278 loc) • 10.5 kB
"use strict"; /** * @fileoverview Event Bus Factory Implementation - Infrastructure Layer * @version 1.0.0 * @since 2025-07-29 * @lastUpdated 2025-07-29 * @module EventBusFactory Infrastructure Implementation * @description Factory for creating different types of event bus implementations * with proper configuration and dependency injection support. * @contributors Claude Code Agent * @dependencies Event bus implementations, configuration * @requirements REQ-ARCH-001 (Clean Architecture Infrastructure Layer) * @testCoverage Unit tests for factory methods and configuration */ Object.defineProperty(exports, "__esModule", { value: true }); exports.eventBusFactory = exports.ConcreteEventBusFactory = void 0; exports.createEventBus = createEventBus; const InMemoryEventBus_1 = require("./InMemoryEventBus"); /** * Concrete Event Bus Factory * * @description factory for creating event bus instances * with different implementations and configurations. Supports * dependency injection and configuration validation. * * @example * ```typescript * const factory = new ConcreteEventBusFactory(); * * // Create in-memory event bus for development * const devBus = factory.createInMemory({ * maxRetries: 3, * enableDeadLetterQueue: true * }); * * // Create Redis event bus for production * const prodBus = factory.createRedis({ * redisUrl: 'redis://localhost:6379', * maxRetries: 5, * enablePersistence: true * }); * ``` * * @since 2025-07-29 * @author Claude Code Agent * @requirements REQ-ARCH-001 (Clean Architecture Infrastructure Layer) */ class ConcreteEventBusFactory { constructor() { this.defaultConfig = { maxRetries: 3, retryDelayMs: 1000, enableDeadLetterQueue: true, maxConcurrency: 10, enablePersistence: false, eventTimeoutMs: 30000, }; } /** * Get singleton instance */ static getInstance() { if (!ConcreteEventBusFactory.instance) { ConcreteEventBusFactory.instance = new ConcreteEventBusFactory(); } return ConcreteEventBusFactory.instance; } /** * Create an in-memory event bus */ createInMemory(config) { const mergedConfig = this.mergeWithDefaults(config); this.validateConfig(mergedConfig); const eventBus = new InMemoryEventBus_1.InMemoryEventBus(mergedConfig); // Add default middleware for logging and monitoring this.addDefaultMiddleware(eventBus); return eventBus; } /** * Create a Redis-based event bus */ createRedis(config) { const mergedConfig = this.mergeWithDefaults(config); this.validateConfig(mergedConfig); this.validateRedisConfig(config); // For now, return in-memory implementation // In a real implementation, this would create a Redis-backed event bus console.warn('Redis event bus not implemented yet, falling back to in-memory'); return this.createInMemory(mergedConfig); } /** * Create an Azure Service Bus event bus */ createAzureServiceBus(config) { const mergedConfig = this.mergeWithDefaults(config); this.validateConfig(mergedConfig); this.validateAzureConfig(config); // For now, return in-memory implementation // In a real implementation, this would create an Azure Service Bus-backed event bus console.warn('Azure Service Bus event bus not implemented yet, falling back to in-memory'); return this.createInMemory(mergedConfig); } /** * Create a custom event bus implementation */ createCustom(implementation) { if (!implementation) { throw new Error('Custom implementation is required'); } // Validate that the implementation conforms to the interface this.validateEventBusImplementation(implementation); return implementation; } /** * Create event bus based on environment configuration */ createFromEnvironment() { const eventBusType = process.env.EVENT_BUS_TYPE || 'in-memory'; switch (eventBusType.toLowerCase()) { case 'in-memory': return this.createInMemory(this.getConfigFromEnvironment()); case 'redis': const redisUrl = process.env.REDIS_URL; if (!redisUrl) { throw new Error('REDIS_URL environment variable is required for Redis event bus'); } return this.createRedis({ ...this.getConfigFromEnvironment(), redisUrl, }); case 'azure': const connectionString = process.env.AZURE_SERVICE_BUS_CONNECTION_STRING; if (!connectionString) { throw new Error('AZURE_SERVICE_BUS_CONNECTION_STRING environment variable is required'); } return this.createAzureServiceBus({ ...this.getConfigFromEnvironment(), connectionString, }); default: throw new Error(`Unsupported event bus type: ${eventBusType}`); } } /** * Private helper methods */ mergeWithDefaults(config) { return { ...this.defaultConfig, ...config, }; } validateConfig(config) { if (config.maxRetries !== undefined && config.maxRetries < 0) { throw new Error('maxRetries must be non-negative'); } if (config.retryDelayMs !== undefined && config.retryDelayMs < 0) { throw new Error('retryDelayMs must be non-negative'); } if (config.maxConcurrency !== undefined && config.maxConcurrency < 1) { throw new Error('maxConcurrency must be at least 1'); } if (config.eventTimeoutMs !== undefined && config.eventTimeoutMs < 1000) { throw new Error('eventTimeoutMs must be at least 1000ms'); } } validateRedisConfig(config) { if (!config.redisUrl || typeof config.redisUrl !== 'string') { throw new Error('Valid Redis URL is required'); } // Basic URL validation try { new URL(config.redisUrl); } catch (error) { throw new Error('Invalid Redis URL format'); } } validateAzureConfig(config) { if (!config.connectionString || typeof config.connectionString !== 'string') { throw new Error('Valid Azure Service Bus connection string is required'); } // Basic connection string validation if (!config.connectionString.includes('Endpoint=') || !config.connectionString.includes('SharedAccessKeyName=')) { throw new Error('Invalid Azure Service Bus connection string format'); } } validateEventBusImplementation(implementation) { const requiredMethods = [ 'publish', 'publishMany', 'subscribe', 'subscribeToMany', 'unsubscribe', 'unsubscribeAll', 'clear', 'getStats', 'start', 'stop', 'isRunning', 'flush', ]; for (const method of requiredMethods) { if (typeof implementation[method] !== 'function') { throw new Error(`Custom event bus implementation missing required method: ${method}`); } } } getConfigFromEnvironment() { return { maxRetries: parseInt(process.env.EVENT_BUS_MAX_RETRIES || '3'), retryDelayMs: parseInt(process.env.EVENT_BUS_RETRY_DELAY_MS || '1000'), enableDeadLetterQueue: process.env.EVENT_BUS_ENABLE_DLQ !== 'false', maxConcurrency: parseInt(process.env.EVENT_BUS_MAX_CONCURRENCY || '10'), enablePersistence: process.env.EVENT_BUS_ENABLE_PERSISTENCE === 'true', eventTimeoutMs: parseInt(process.env.EVENT_BUS_TIMEOUT_MS || '30000'), }; } addDefaultMiddleware(eventBus) { // Add logging middleware eventBus.addMiddleware({ async beforePublish(event, options) { console.debug(`[EventBus] Publishing event: ${event.constructor.name}`, { eventId: event.eventId, occurredOn: event.occurredOn, }); return event; }, async beforeHandle(event, handler) { console.debug(`[EventBus] Handling event: ${event.constructor.name} with ${handler.constructor.name}`); }, async afterHandle(event, handler, result) { console.debug(`[EventBus] Event handled successfully: ${event.constructor.name}`); }, async onError(event, handler, error) { console.error(`[EventBus] Event handling failed: ${event.constructor.name}`, { handlerName: handler.constructor.name, error: error.message, eventId: event.eventId, }); }, }); // Add performance monitoring middleware eventBus.addMiddleware({ async beforeHandle(event, handler) { event._startTime = Date.now(); }, async afterHandle(event, handler, result) { const duration = Date.now() - (event._startTime || 0); console.debug(`[EventBus] Event processing time: ${duration}ms`, { eventType: event.constructor.name, handlerName: handler.constructor.name, }); }, }); } } exports.ConcreteEventBusFactory = ConcreteEventBusFactory; function createEventBus(type, config) { const factory = ConcreteEventBusFactory.getInstance(); switch (type) { case 'in-memory': return factory.createInMemory(config); case 'redis': return factory.createRedis(config); case 'azure': return factory.createAzureServiceBus(config); case 'environment': return factory.createFromEnvironment(); default: throw new Error(`Unsupported event bus type: ${type}`); } } /** * Export singleton factory instance */ exports.eventBusFactory = ConcreteEventBusFactory.getInstance();