UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

275 lines (218 loc) • 8.94 kB
#!/usr/bin/env node /** * Extract all view definitions from the Optimizely database * This script reverse engineers the complete view system */ const Database = require('better-sqlite3'); const fs = require('fs'); const path = require('path'); function extractViews(dbPath, outputDir) { console.log(`\nšŸ” Extracting views from: ${dbPath}`); try { const db = new Database(dbPath); const views = db.prepare('SELECT name, sql FROM sqlite_master WHERE type=? ORDER BY name').all('view'); console.log(`šŸ“Š Total views found: ${views.length}`); if (views.length === 0) { console.log('āŒ No views found in database'); db.close(); return []; } // Create output directory if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } const viewInventory = []; views.forEach((view, index) => { console.log(`\n${index + 1}. ${view.name}`); console.log(` SQL Length: ${view.sql.length} characters`); // Save individual view file const filename = `${view.name}.sql`; const filepath = path.join(outputDir, filename); const content = `-- Auto-extracted view: ${view.name} -- Extracted from: ${dbPath} -- Extraction date: ${new Date().toISOString()} ${view.sql}; `; fs.writeFileSync(filepath, content); console.log(` āœ… Saved to: ${filepath}`); viewInventory.push({ name: view.name, filename: filename, filepath: filepath, sql: view.sql, sqlLength: view.sql.length }); }); db.close(); return viewInventory; } catch (error) { console.error(`āŒ Error extracting views from ${dbPath}:`, error.message); return []; } } function generateViewCreationScript(viewInventory, outputDir) { console.log('\nšŸ”§ Generating comprehensive view creation script...'); const scriptContent = `#!/usr/bin/env python3 """ COMPREHENSIVE VIEW CREATION SCRIPT Auto-generated from database extraction Generated: ${new Date().toISOString()} This script creates ALL views found in the Optimizely database. Run this script to ensure complete view recreation. """ import sqlite3 import os import glob from pathlib import Path def create_all_views(db_path="data/optimizely-cache.db"): """Create all views in the correct order""" print("šŸ”§ Creating all Optimizely views...") print(f"šŸ“ Database: {db_path}") # Connect to database conn = sqlite3.connect(db_path) cursor = conn.cursor() # View creation order (dependencies first) view_files = [ ${viewInventory.map(view => ` "${view.filename}",`).join('\n')} ] views_dir = Path(__file__).parent / "extracted-views" created_count = 0 for view_file in view_files: view_path = views_dir / view_file view_name = view_file.replace('.sql', '') try: if view_path.exists(): with open(view_path, 'r') as f: sql_content = f.read() # Extract just the CREATE VIEW statement lines = sql_content.split('\\n') sql_lines = [] in_sql = False for line in lines: if line.startswith('CREATE VIEW') or line.startswith('CREATE OR REPLACE VIEW'): in_sql = True if in_sql: sql_lines.append(line) if sql_lines: view_sql = '\\n'.join(sql_lines) cursor.execute(view_sql) conn.commit() print(f"āœ… Created view: {view_name}") created_count += 1 else: print(f"āš ļø No SQL found in {view_file}") else: print(f"āŒ View file not found: {view_path}") except Exception as e: print(f"āŒ Error creating {view_name}: {e}") conn.rollback() # Verify all views were created cursor.execute("SELECT name FROM sqlite_master WHERE type='view' ORDER BY name") created_views = [row[0] for row in cursor.fetchall()] print(f"\\nšŸ“Š View Creation Summary:") print(f" Expected: {len(view_files)} views") print(f" Created: {created_count} views") print(f" In Database: {len(created_views)} views") print(f"\\nšŸ“‹ Views in database:") for view in created_views: print(f" - {view}") conn.close() return created_count == len(view_files) if __name__ == "__main__": import sys db_path = sys.argv[1] if len(sys.argv) > 1 else "data/optimizely-cache.db" success = create_all_views(db_path) sys.exit(0 if success else 1) `; const scriptPath = path.join(outputDir, 'create-all-views.py'); fs.writeFileSync(scriptPath, scriptContent); console.log(`āœ… Created comprehensive script: ${scriptPath}`); // Make it executable try { fs.chmodSync(scriptPath, '755'); } catch (error) { // Ignore on Windows } } function generateViewInventoryReport(viewInventory, outputDir) { console.log('\nšŸ“‹ Generating view inventory report...'); const reportContent = `# OPTIMIZELY DATABASE VIEWS INVENTORY **Generated:** ${new Date().toISOString()} **Total Views:** ${viewInventory.length} ## View Summary | # | View Name | SQL Length | File | |---|-----------|------------|------| ${viewInventory.map((view, index) => `| ${index + 1} | \`${view.name}\` | ${view.sqlLength} chars | ${view.filename} |` ).join('\n')} ## View Details ${viewInventory.map(view => ` ### ${view.name} - **File:** \`${view.filename}\` - **SQL Length:** ${view.sqlLength} characters - **Purpose:** ${inferViewPurpose(view.name)} \`\`\`sql ${view.sql} \`\`\` `).join('\n')} ## Integration Instructions 1. **Add to SQLiteEngine.createSchema():** \`\`\`typescript await this.createViews(); \`\`\` 2. **Run creation script:** \`\`\`bash python scripts/extracted-views/create-all-views.py \`\`\` 3. **Verify views:** \`\`\`bash node scripts/extract-views.js \`\`\` `; const reportPath = path.join(outputDir, 'VIEW-INVENTORY-REPORT.md'); fs.writeFileSync(reportPath, reportContent); console.log(`āœ… Created inventory report: ${reportPath}`); } function inferViewPurpose(viewName) { const purposes = { 'flags_unified_view': 'Unified flag data across environments with denormalized fields', 'experiments_unified_view': 'Cross-platform experiment analytics (Web + Feature)', 'entity_usage_view': 'Orphan detection and entity usage analysis', 'flag_state_history_view': 'Temporal analysis of flag enable/disable events', 'analytics_summary_view': 'Pre-aggregated dashboard metrics', 'pages_standalone_flat': 'Flattened page data for analytics', 'audiences_standalone_flat': 'Flattened audience data for analytics', 'change_history_flat': 'Flattened change history for reporting', 'experiment_audiences_flat': 'Experiment-audience relationship mapping', 'experiment_events_flat': 'Experiment-event relationship mapping', 'experiment_pages_flat': 'Experiment-page relationship mapping', 'flag_variations_flat': 'Flag-variation relationship mapping' }; return purposes[viewName] || 'Analytics view (purpose needs documentation)'; } // Main execution console.log('šŸš€ OPTIMIZELY VIEW EXTRACTION TOOL'); console.log('=================================='); const outputDir = path.join(__dirname, 'extracted-views'); const mainDbPath = 'data/optimizely-cache.db'; const backupDbPath = 'data/optimizely-cache copy.db'; // Extract from main database const mainViews = extractViews(mainDbPath, outputDir); // Try backup database if main fails or has no views if (mainViews.length === 0) { console.log('\nšŸ”„ Trying backup database...'); const backupViews = extractViews(backupDbPath, outputDir); if (backupViews.length > 0) { generateViewCreationScript(backupViews, outputDir); generateViewInventoryReport(backupViews, outputDir); } } else { generateViewCreationScript(mainViews, outputDir); generateViewInventoryReport(mainViews, outputDir); } console.log('\nāœ… View extraction complete!'); console.log(`šŸ“ Output directory: ${outputDir}`); console.log('\nšŸŽÆ Next steps:'); console.log('1. Review extracted views in the output directory'); console.log('2. Run the create-all-views.py script to test recreation'); console.log('3. Integrate view creation into SQLiteEngine.createSchema()');