@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
275 lines (218 loc) ⢠8.94 kB
JavaScript
#!/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()');