Skip to main content
Glama
ValidateSchemaUseCase.js7.73 kB
"use strict"; /** * ValidateSchemaUseCase.ts * * @semantic-intent Use case for validating database schema integrity * Coordinates domain services to identify potential schema issues * * @observable-anchoring * - Environment drives database selection (observable routing) * - Validation results based on observable schema state * - Cache key based on environment for performance * * @intent-preservation * - Environment semantic maintained throughout validation * - Schema integrity rules preserved from domain layer * - Validation errors maintain semantic context * * @semantic-over-structural * - Focuses on semantic integrity, not just syntax * - Validates relationships make semantic sense * - Checks for semantic anti-patterns */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ValidateSchemaUseCase = exports.ValidationSeverity = void 0; /** * Validation issue severity */ var ValidationSeverity; (function (ValidationSeverity) { ValidationSeverity["ERROR"] = "ERROR"; ValidationSeverity["WARNING"] = "WARNING"; ValidationSeverity["INFO"] = "INFO"; })(ValidationSeverity || (exports.ValidationSeverity = ValidationSeverity = {})); /** * Use case: Validate database schema * * Semantic Intent: Orchestrates domain services to identify schema integrity issues * Observable Anchoring: Cache based on environment, validation based on current schema state */ class ValidateSchemaUseCase { repository; schemaAnalyzer; databaseConfig; cache; static CACHE_TTL_SECONDS = 600; // 10 minutes constructor(repository, schemaAnalyzer, databaseConfig, cache) { this.repository = repository; this.schemaAnalyzer = schemaAnalyzer; this.databaseConfig = databaseConfig; this.cache = cache; Object.freeze(this); } /** * Execute schema validation * * Semantic: Environment drives database selection and validation scope */ async execute(request) { const environment = request.environment; // Observable: Cache key based on environment const cacheKey = `schema:${environment}`; // Check cache first (avoid repeated API calls) let schema = await this.cache.get(cacheKey); if (!schema) { // Fetch schema from repository const databaseId = this.databaseConfig.getDatabaseId(environment); schema = await this.repository.fetchDatabaseSchema(databaseId); // Cache for future requests (10-minute TTL) await this.cache.set(cacheKey, schema, ValidateSchemaUseCase.CACHE_TTL_SECONDS); } // Validate schema and collect issues const issues = this.validateSchema(schema); // Count issues by severity const errorCount = issues.filter((i) => i.severity === ValidationSeverity.ERROR).length; const warningCount = issues.filter((i) => i.severity === ValidationSeverity.WARNING).length; const infoCount = issues.filter((i) => i.severity === ValidationSeverity.INFO).length; return { databaseName: schema.name, environment: schema.environment, isValid: errorCount === 0, errorCount, warningCount, infoCount, issues, validatedAt: new Date(), }; } /** * Validate database schema * * Semantic: Apply domain validation rules to identify issues */ validateSchema(schema) { const issues = []; // Validate each table for (const table of schema.tables) { // Check for tables without primary key const hasPrimaryKey = table.columns.some((col) => col.isPrimaryKey); if (!hasPrimaryKey) { issues.push({ severity: ValidationSeverity.WARNING, category: 'Missing Primary Key', message: `Table '${table.name}' has no primary key`, table: table.name, details: { recommendation: 'Add a primary key column for better query performance and data integrity', }, }); } // Check for orphaned foreign keys (references non-existent tables) for (const fk of table.foreignKeys) { const referencedTableExists = schema.tables.some((t) => t.name === fk.referencesTable); if (!referencedTableExists) { issues.push({ severity: ValidationSeverity.ERROR, category: 'Orphaned Foreign Key', message: `Foreign key references non-existent table '${fk.referencesTable}'`, table: table.name, column: fk.column, details: { referencedTable: fk.referencesTable, referencedColumn: fk.referencesColumn, }, }); } else { // Check if referenced column exists const referencedTable = schema.tables.find((t) => t.name === fk.referencesTable); const referencedColumnExists = referencedTable?.columns.some((col) => col.name === fk.referencesColumn); if (!referencedColumnExists) { issues.push({ severity: ValidationSeverity.ERROR, category: 'Invalid Foreign Key', message: `Foreign key references non-existent column '${fk.referencesColumn}' in table '${fk.referencesTable}'`, table: table.name, column: fk.column, details: { referencedTable: fk.referencesTable, referencedColumn: fk.referencesColumn, }, }); } } } // Check for tables with no indexes (potential performance issue) if (table.indexes.length === 0 && table.type === 'table') { issues.push({ severity: ValidationSeverity.INFO, category: 'No Indexes', message: `Table '${table.name}' has no indexes`, table: table.name, details: { recommendation: 'Consider adding indexes on frequently queried columns', }, }); } // Check for nullable foreign key columns (potential data integrity issue) for (const fk of table.foreignKeys) { const fkColumn = table.columns.find((col) => col.name === fk.column); if (fkColumn?.isNullable && fk.onDelete !== 'SET NULL') { issues.push({ severity: ValidationSeverity.WARNING, category: 'Nullable Foreign Key', message: `Nullable foreign key column '${fk.column}' should have ON DELETE SET NULL`, table: table.name, column: fk.column, details: { currentOnDelete: fk.onDelete, recommendation: 'SET NULL', }, }); } } } return issues; } } exports.ValidateSchemaUseCase = ValidateSchemaUseCase; //# sourceMappingURL=ValidateSchemaUseCase.js.map

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/semanticintent/semantic-d1-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server