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.

320 lines (319 loc) 13 kB
"use strict"; /** * @fileoverview Quiz HTTP Controller - Clean Architecture Integration * @version 1.0.0 * @since 2025-07-30 * @module QuizController * @description HTTP controller integrating Clean Architecture handlers with Express routes * * @architecture * Layer: Infrastructure (HTTP Adapter) * Pattern: Controller Pattern + Adapter Pattern * Dependencies: CreateQuizCommandHandler, GetQuizQueryHandler, DIContainer * * @relationships * DEPENDS_ON: * - CreateQuizCommandHandler (Application Layer) * - GetQuizQueryHandler (Application Layer) * - DIContainer (Infrastructure) * USED_BY: * - Express HTTP server * - HTTP routing middleware * * @backwards_compatibility * - Preserves exact same HTTP API interface * - Maintains identical request/response formats * - Supports all existing validation patterns * - Adds Clean Architecture benefits (caching, validation, events) * * @contributors Claude Code Agent * @testCoverage HTTP integration tests, Clean Architecture integration tests */ Object.defineProperty(exports, "__esModule", { value: true }); exports.QuizController = void 0; const CreateQuizCommand_1 = require("../../../application/commands/CreateQuizCommand"); const GetQuizQuery_1 = require("../../../application/queries/GetQuizQuery"); const ServiceRegistry_1 = require("../../di/ServiceRegistry"); const PerformanceDecorator_1 = require("../../../application/decorators/PerformanceDecorator"); /** * Quiz HTTP Controller with Clean Architecture Integration * * @description Provides HTTP endpoints for quiz operations while leveraging * Clean Architecture handlers for business logic processing. * Maintains backward compatibility with existing HTTP API. * * @example * ```typescript * const controller = new QuizController({ container }); * app.post('/quiz/create', controller.createQuiz.bind(controller)); * app.get('/quiz/:id', controller.getQuiz.bind(controller)); * ``` * * @since 2025-07-30 * @author Claude Code Agent */ class QuizController { constructor(dependencies) { this.container = dependencies.container; } /** * Create Quiz HTTP Endpoint * * @description Creates a new quiz using Clean Architecture command handler. * Falls back to direct service if Clean Architecture unavailable. * * @route POST /quiz/create * @param req Express request with quiz data in body * @param res Express response * @param next Express next function for error handling */ async createQuiz(req, res, next) { await (0, PerformanceDecorator_1.withPerformanceTracking)('HTTP.QuizController.createQuiz', async (tracker) => { try { console.log(`🚀 HTTP Controller: Creating quiz "${req.body.title}"`); // Try Clean Architecture approach first if (this.hasCleanArchitecture()) { tracker.recordEvent(); // Clean Architecture path await this.createQuizWithCleanArchitecture(req, res, tracker); } else { // Fallback to direct service access await this.createQuizWithDirectService(req, res, tracker); } } catch (error) { console.error('❌ Quiz creation failed in controller:', error); next(error); // Let Express error handler deal with it throw error; // Re-throw for performance tracking } }); } /** * Get Quiz HTTP Endpoint * * @description Retrieves a quiz using Clean Architecture query handler. * Falls back to direct service if Clean Architecture unavailable. * * @route GET /quiz/:id * @param req Express request with quiz ID in params * @param res Express response * @param next Express next function for error handling */ async getQuiz(req, res, next) { try { const quizId = req.params.id; const includeQuestions = req.query.includeQuestions === 'true'; const includeMetadata = req.query.includeMetadata === 'true'; console.log(`🔍 HTTP Controller: Retrieving quiz ${quizId}`); // Try Clean Architecture approach first if (this.hasCleanArchitecture()) { await this.getQuizWithCleanArchitecture(req, res, { quizId, includeQuestions, includeMetadata, }); } else { // Fallback to direct service access await this.getQuizWithDirectService(req, res, quizId); } } catch (error) { console.error('❌ Quiz retrieval failed in controller:', error); next(error); } } /** * List Quizzes HTTP Endpoint * * @description Lists quizzes with optional filtering and pagination. * Uses Clean Architecture when available. * * @route GET /quiz/all * @param req Express request with optional query parameters * @param res Express response * @param next Express next function for error handling */ async listQuizzes(req, res, next) { try { console.log('📋 HTTP Controller: Listing quizzes'); // For now, fall back to direct service until we implement ListQuizzesQueryHandler await this.listQuizzesWithDirectService(req, res); } catch (error) { console.error('❌ Quiz listing failed in controller:', error); next(error); } } /** * Submit Quiz HTTP Endpoint * * @description Handles quiz submission with answer processing. * Will use Clean Architecture when submit command handler is implemented. * * @route POST /quiz/submit * @param req Express request with quiz answers * @param res Express response * @param next Express next function for error handling */ async submitQuiz(req, res, next) { try { const { quizId, answers } = req.body; console.log(`📝 HTTP Controller: Submitting quiz ${quizId}`); // For now, fall back to direct service until we implement SubmitQuizCommandHandler await this.submitQuizWithDirectService(req, res); } catch (error) { console.error('❌ Quiz submission failed in controller:', error); next(error); } } /** * Create quiz using Clean Architecture */ async createQuizWithCleanArchitecture(req, res, tracker) { const commandHandler = this.container.resolve(ServiceRegistry_1.ServiceTokens.CreateQuizCommandHandler); // Create Clean Architecture command const command = new CreateQuizCommand_1.CreateQuizCommand({ title: req.body.title, description: req.body.description, category: req.body.category || 'General', difficulty: req.body.difficulty || 'medium', timeLimit: req.body.timeLimit, questions: req.body.questions.map((q) => ({ question: q.question, options: q.options, correctAnswer: q.answer, explanation: q.explanation, points: q.points || 10, })), metadata: { tags: req.body.tags || [], createdBy: req.body.createdBy || 'anonymous', source: 'http-api', ipAddress: req.ip || req.connection.remoteAddress, userAgent: req.get('User-Agent'), }, }); // Execute through Clean Architecture const result = await commandHandler.handle(command); // Return HTTP response res.status(201).json({ success: true, quiz: { id: result.quizId, title: req.body.title, description: req.body.description, category: req.body.category || 'General', difficulty: req.body.difficulty || 'medium', timeLimit: req.body.timeLimit, questionCount: req.body.questions.length, createdAt: result.createdAt, questions: req.body.questions.map((q, index) => ({ question: q.question, options: q.options, correctAnswer: q.answer, explanation: q.explanation, order: index + 1, })), }, enhanced: true, // Indicate Clean Architecture was used events: result.events || [], timestamp: new Date().toISOString(), }); console.log('✅ Quiz created via Clean Architecture:', result.quizId); } /** * Create quiz using direct service access (fallback) */ async createQuizWithDirectService(req, res, tracker) { // Import the existing enhanced quiz service // Enhanced quiz service removed - functionality moved to clean architecture handlers // Fallback disabled for now // Service fallback disabled - return not implemented error res.status(501).json({ error: 'Enhanced quiz service removed - please use clean architecture handlers' }); } /** * Get quiz using Clean Architecture */ async getQuizWithCleanArchitecture(req, res, options) { const queryHandler = this.container.resolve(ServiceRegistry_1.ServiceTokens.GetQuizQueryHandler); // Create Clean Architecture query const query = new GetQuizQuery_1.GetQuizQuery({ quizId: options.quizId, includeQuestions: options.includeQuestions, includeStatistics: false, includeResults: false, includeMetadata: options.includeMetadata, userId: req.body.userId || req.query.userId, }); // Execute through Clean Architecture const quizView = await queryHandler.handle(query); // Return HTTP response res.json({ success: true, quiz: { id: quizView.id, title: quizView.title, description: quizView.description, category: quizView.category, difficulty: quizView.difficulty, questionCount: quizView.questionCount, createdAt: quizView.createdAt, updatedAt: quizView.updatedAt, isActive: quizView.isActive, ...(quizView.questions && { questions: quizView.questions.map((q) => ({ id: q.id, question: q.questionText, options: q.options, correctAnswer: q.correctAnswer, explanation: q.explanation, order: q.order, points: q.points, })), }), ...(quizView.metadata && { metadata: quizView.metadata }), }, cached: true, // Indicate caching was used enhanced: true, timestamp: new Date().toISOString(), }); console.log('✅ Quiz retrieved via Clean Architecture:', options.quizId); } /** * Get quiz using direct service access (fallback) */ async getQuizWithDirectService(req, res, quizId) { // Enhanced quiz service removed - functionality moved to clean architecture handlers res.status(501).json({ error: 'Enhanced quiz service removed - please use clean architecture handlers' }); } /** * List quizzes using direct service (fallback for now) */ async listQuizzesWithDirectService(req, res) { // Enhanced quiz service removed - functionality moved to clean architecture handlers res.status(501).json({ error: 'Enhanced quiz service removed - please use clean architecture handlers' }); } /** * Submit quiz using direct service (fallback for now) */ async submitQuizWithDirectService(req, res) { // Enhanced quiz service removed - functionality moved to clean architecture handlers res.status(501).json({ error: 'Enhanced quiz service removed - please use clean architecture handlers' }); } /** * Check if Clean Architecture components are available */ hasCleanArchitecture() { try { this.container.resolve(ServiceRegistry_1.ServiceTokens.CreateQuizCommandHandler); this.container.resolve(ServiceRegistry_1.ServiceTokens.GetQuizQueryHandler); return true; } catch (_a) { return false; } } } exports.QuizController = QuizController;