UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

320 lines (271 loc) • 9.63 kB
#!/usr/bin/env node /** * Comprehensive Swagger Default Extractor * Extracts ALL default values from Optimizely's OpenAPI specs for auto-correction system */ import fs from 'fs/promises'; import { OpenAPIParser } from './open-api-parser.js'; class SwaggerDefaultExtractor { constructor() { this.webDefaults = {}; this.featureDefaults = {}; this.allEntities = {}; } /** * Extract all defaults from both Web and Feature Experimentation APIs */ async extractAllDefaults() { console.log('šŸš€ Starting comprehensive default extraction...'); // Download and parse Web Experimentation API console.log('šŸ“„ Fetching Web Experimentation API...'); const webResponse = await fetch('https://api.optimizely.com/v2/swagger.json'); const webSpec = await webResponse.json(); const webParser = new OpenAPIParser(webSpec); const webSchemas = webParser.getAllSchemas(); // Load Feature Experimentation API console.log('šŸ“„ Loading Feature Experimentation API...'); const featureSpec = JSON.parse(await fs.readFile('./docs/api-reference/api-spec/optimizely-swagger-fx.json', 'utf8')); const featureParser = new OpenAPIParser(featureSpec); const featureSchemas = featureParser.getAllSchemas(); console.log('šŸ” Extracting defaults from all schemas...'); // Extract from Web API this.webDefaults = this.extractDefaultsFromSchemas(webSchemas, 'web'); // Extract from Feature API this.featureDefaults = this.extractDefaultsFromSchemas(featureSchemas, 'feature'); // Combine and normalize this.allEntities = this.combineAndNormalize(); console.log('šŸ“Š Extraction Summary:'); console.log(` Web entities: ${Object.keys(this.webDefaults).length}`); console.log(` Feature entities: ${Object.keys(this.featureDefaults).length}`); console.log(` Total unique entities: ${Object.keys(this.allEntities).length}`); return this.allEntities; } /** * Extract defaults from a set of schemas */ extractDefaultsFromSchemas(schemas, apiType) { const defaults = {}; for (const [entityName, schema] of Object.entries(schemas)) { const entityKey = entityName.toLowerCase(); // Skip non-entity schemas if (this.isUtilitySchema(entityName)) continue; const entityDefaults = { apiType, entityName, swaggerDefaults: {}, enumFields: {}, requiredFields: schema.required || [], fieldTypes: {}, fieldDescriptions: {}, examples: {} }; // Extract field-level defaults if (schema.properties) { for (const [fieldName, fieldSchema] of Object.entries(schema.properties)) { // Extract default values if (fieldSchema.default !== undefined) { entityDefaults.swaggerDefaults[fieldName] = fieldSchema.default; } // Extract enum values if (fieldSchema.enum) { entityDefaults.enumFields[fieldName] = fieldSchema.enum; } // Extract field types entityDefaults.fieldTypes[fieldName] = fieldSchema.type; // Extract descriptions if (fieldSchema.description) { entityDefaults.fieldDescriptions[fieldName] = fieldSchema.description; } // Extract examples if (fieldSchema.example !== undefined) { entityDefaults.examples[fieldName] = fieldSchema.example; } } } defaults[entityKey] = entityDefaults; } return defaults; } /** * Check if schema is a utility/helper schema vs entity schema */ isUtilitySchema(name) { const utilityPatterns = [ 'Error', 'Response', 'Request', 'Result', 'Status', 'Meta', 'Page', 'List', 'Collection', 'Wrapper', 'Container' ]; return utilityPatterns.some(pattern => name.includes(pattern) && !['Page'].includes(name) // Page is an actual entity ); } /** * Combine Web and Feature defaults, handling overlaps */ combineAndNormalize() { const combined = {}; // Add all web entities for (const [key, entity] of Object.entries(this.webDefaults)) { combined[key] = entity; } // Add feature entities, merging if overlap for (const [key, featureEntity] of Object.entries(this.featureDefaults)) { if (combined[key]) { // Merge overlapping entities combined[key] = this.mergeEntityDefaults(combined[key], featureEntity); } else { combined[key] = featureEntity; } } return combined; } /** * Merge defaults from web and feature APIs for same entity */ mergeEntityDefaults(webEntity, featureEntity) { return { ...webEntity, apiType: 'both', featureDefaults: featureEntity.swaggerDefaults, featureEnums: featureEntity.enumFields, featureFieldTypes: featureEntity.fieldTypes, swaggerDefaults: { ...webEntity.swaggerDefaults, ...featureEntity.swaggerDefaults }, enumFields: { ...webEntity.enumFields, ...featureEntity.enumFields }, fieldTypes: { ...webEntity.fieldTypes, ...featureEntity.fieldTypes } }; } /** * Generate the comprehensive defaults file */ async generateDefaultsFile() { const output = { generatedAt: new Date().toISOString(), totalEntities: Object.keys(this.allEntities).length, entities: this.allEntities, // Quick lookup maps lookups: { entitiesWithDefaults: this.getEntitiesWithDefaults(), entitiesWithEnums: this.getEntitiesWithEnums(), strictModeEntities: this.getStrictModeEntities(), urlFields: this.getUrlFields(), enumFieldsByEntity: this.getEnumFieldsByEntity() } }; await fs.writeFile('./src/generated/comprehensive-defaults.json', JSON.stringify(output, null, 2)); console.log('āœ… Generated comprehensive-defaults.json'); return output; } /** * Get entities that have swagger default values */ getEntitiesWithDefaults() { const withDefaults = {}; for (const [key, entity] of Object.entries(this.allEntities)) { if (Object.keys(entity.swaggerDefaults).length > 0) { withDefaults[key] = Object.keys(entity.swaggerDefaults); } } return withDefaults; } /** * Get entities that have enum fields */ getEntitiesWithEnums() { const withEnums = {}; for (const [key, entity] of Object.entries(this.allEntities)) { if (Object.keys(entity.enumFields).length > 0) { withEnums[key] = Object.keys(entity.enumFields); } } return withEnums; } /** * Identify entities that should be in strict mode (no auto-correction) */ getStrictModeEntities() { // Events are strict - wrong event types break analytics // These entities should fail fast, not auto-correct return ['event', 'metric', 'revenue']; } /** * Find all URL fields across entities */ getUrlFields() { const urlFields = {}; for (const [entityKey, entity] of Object.entries(this.allEntities)) { const urls = []; for (const [fieldName, fieldType] of Object.entries(entity.fieldTypes)) { if (fieldName.includes('url') || fieldName.includes('webhook') || fieldName === 'edit_url') { urls.push(fieldName); } } if (urls.length > 0) { urlFields[entityKey] = urls; } } return urlFields; } /** * Get enum fields organized by entity */ getEnumFieldsByEntity() { const organized = {}; for (const [entityKey, entity] of Object.entries(this.allEntities)) { if (Object.keys(entity.enumFields).length > 0) { organized[entityKey] = entity.enumFields; } } return organized; } /** * Display comprehensive summary */ displaySummary() { console.log('\nšŸ“‹ COMPREHENSIVE DEFAULTS SUMMARY'); console.log('====================================='); for (const [entityKey, entity] of Object.entries(this.allEntities)) { console.log(`\nšŸ” ${entityKey.toUpperCase()} (${entity.apiType})`); if (Object.keys(entity.swaggerDefaults).length > 0) { console.log(' āœ… Swagger Defaults:'); for (const [field, value] of Object.entries(entity.swaggerDefaults)) { console.log(` ${field}: ${JSON.stringify(value)}`); } } if (Object.keys(entity.enumFields).length > 0) { console.log(' šŸŽÆ Enum Fields:'); for (const [field, values] of Object.entries(entity.enumFields)) { console.log(` ${field}: [${values.slice(0, 3).join(', ')}${values.length > 3 ? '...' : ''}]`); } } if (entity.requiredFields.length > 0) { console.log(` āš ļø Required: ${entity.requiredFields.join(', ')}`); } } } } // CLI execution async function main() { try { const extractor = new SwaggerDefaultExtractor(); await extractor.extractAllDefaults(); await extractor.generateDefaultsFile(); extractor.displaySummary(); console.log('\nāœ… Comprehensive default extraction complete!'); console.log('šŸ“ Output: ./src/generated/comprehensive-defaults.json'); } catch (error) { console.error('āŒ Error:', error.message); process.exit(1); } } if (import.meta.url === `file://${process.argv[1]}`) { main(); } export { SwaggerDefaultExtractor };