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.
156 lines (155 loc) • 6.24 kB
JavaScript
;
/**
* @fileoverview Delete Quiz Command Handler - Clean Architecture Application Layer
* @version 1.0.0
* @since 2025-07-30
* @module DeleteQuizCommandHandler
* @description Handles quiz deletion with business rules and event publication
*
* @architecture
* Layer: Application (Command Handler)
* Pattern: Command Handler Pattern + Repository Pattern
* Dependencies: Domain repositories, event bus, cache
*
* @relationships
* DEPENDS_ON:
* - QuizRepository (Infrastructure)
* - EventBus (Infrastructure)
* - CacheService (Infrastructure)
* USED_BY:
* - MCP delete-quiz tool (Infrastructure)
* - HTTP delete endpoints (Infrastructure)
*
* @contributors Claude Code Agent
* @testCoverage Command handler tests, integration tests
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeleteQuizCommandHandler = void 0;
const QuizDeletedEvent_1 = require("../../domain/events/QuizDeletedEvent");
/**
* Delete Quiz Command Handler
*
* @description Processes quiz deletion commands with proper business logic,
* validation, caching, and event publication.
*
* @example
* ```typescript
* const handler = new DeleteQuizCommandHandler({ quizRepository, eventBus, cacheService });
* const result = await handler.handle(deleteCommand);
* ```
*
* @since 2025-07-30
* @author Claude Code Agent
*/
class DeleteQuizCommandHandler {
constructor(dependencies) {
this.quizRepository = dependencies.quizRepository;
this.eventBus = dependencies.eventBus;
this.cacheService = dependencies.cacheService;
}
/**
* Handle Delete Quiz Command
*
* @description Executes quiz deletion with validation, business rules,
* cache invalidation, and event publication.
*
* @param command Delete quiz command with validation
* @returns Promise<DeleteQuizResult> Result with audit information
*/
async handle(command) {
console.log(`🗑️ Clean Architecture: Processing ${command.toString()}`);
try {
// 1. Validate quiz exists
const existingQuiz = await this.quizRepository.findById(command.quizId);
if (!existingQuiz) {
throw new Error(`Quiz not found: ${command.quizId.value}`);
}
console.log(`📋 Found quiz to delete: "${existingQuiz.title}"`);
// 2. Apply business rules
await this.validateDeletionRules(existingQuiz, command);
// 3. Delete from repository
await this.quizRepository.delete(command.quizId);
const deletedAt = new Date().toISOString();
// 4. Invalidate cache
await this.invalidateCache(command.quizId.value);
// 5. Publish domain event
const event = new QuizDeletedEvent_1.QuizDeletedEvent({
quizId: command.quizId.value,
quizTitle: existingQuiz.title,
deletedAt,
deletedBy: command.requestedBy,
reason: command.reason,
metadata: command.metadata,
});
await this.eventBus.publish(event);
const result = {
success: true,
quizId: command.quizId.value,
deletedAt,
events: ['QuizDeletedEvent'],
audit: command.getAuditInfo(),
};
console.log(`✅ Quiz deleted successfully: ${command.quizId.value}`);
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.error(`❌ Quiz deletion failed: ${errorMessage}`);
// Publish failure event for monitoring (generic event)
try {
await this.eventBus.publish({
eventType: 'QuizDeletionFailed',
eventId: 'temp-failure-id',
occurredOn: new Date(),
version: 1,
data: {
quizId: command.quizId.value,
error: errorMessage,
requestedBy: command.requestedBy,
timestamp: new Date().toISOString(),
},
});
}
catch (eventError) {
console.warn('Failed to publish failure event:', eventError);
}
throw error;
}
}
/**
* Validate business rules for quiz deletion
*/
async validateDeletionRules(quiz, command) {
// Business Rule: Cannot delete quiz with active attempts (if we implement this)
// For now, we allow all deletions with confirmation
// Business Rule: Require confirmation for safety
if (!command.confirmDelete) {
throw new Error('Delete confirmation is required');
}
// Business Rule: Audit trail required for deletions
if (!command.requestedBy || command.requestedBy === 'anonymous') {
console.warn('⚠️ Quiz deletion by anonymous user:', command.quizId.value);
}
console.log(`✅ Deletion validation passed for quiz: ${quiz.title}`);
}
/**
* Invalidate related cache entries
*/
async invalidateCache(quizId) {
try {
// Clear specific quiz cache
await this.cacheService.delete(`quiz:${quizId}`);
// Clear quiz list cache (as it may contain this quiz)
await this.cacheService.delete('quiz:list:all');
// Clear category-specific caches (we don't know which category, so clear pattern)
// Note: This could be optimized by storing category info in the command
console.log(`🗑️ Cache invalidated for quiz: ${quizId}`);
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
console.warn(`⚠️ Cache invalidation warning for quiz ${quizId}:`, errorMessage);
// Don't fail the entire operation for cache issues
}
}
}
exports.DeleteQuizCommandHandler = DeleteQuizCommandHandler;