UNPKG

@simonecoelhosfo/optimizely-mcp-server

Version:

Optimizely MCP Server for AI assistants with integrated CLI tools

883 lines 43.7 kB
/** * OpenAPI Reference Handler - Core business logic for OpenAPI schema queries * @description Handles all OpenAPI schema-related queries including schemas, operations, * field details, dependencies, examples, and validation rules. */ import { readFile } from 'fs/promises'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import { getLogger } from '../logging/Logger.js'; import { FIELDS } from '../generated/fields.generated.js'; import { getModelFriendlyTemplate } from '../templates/ModelFriendlyTemplates.js'; import { SUPPORTED_ENTITIES, SUPPORTED_OPERATIONS } from './OpenAPITypes.js'; import { ENTITY_RELATIONSHIPS, getDependenciesForEntity, getRelationshipsForEntity, getCreationOrder, getPlatformDifferences } from './EntityDependencyKnowledge.js'; // Get current file directory for OpenAPI spec paths const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); export class OpenAPIReferenceHandler { webSpec = null; featureSpec = null; webParser = null; featureParser = null; constructor() { this.initializeParsers(); } /** * Initialize OpenAPI parsers for both platforms */ async initializeParsers() { try { // Import the OpenAPIParser class with graceful fallback let OpenAPIParser = null; try { const swaggerParserModule = await import('../../scripts/swagger-parser.js'); OpenAPIParser = swaggerParserModule.OpenAPIParser; } catch (error) { getLogger().warn('OpenAPIReferenceHandler: swagger-parser not available, using fallback mode'); } // Load Web Experimentation spec from src folder const webSpecPath = join(__dirname, '../api-reference/swagger-web.json'); try { const webSpecContent = await readFile(webSpecPath, 'utf-8'); this.webSpec = JSON.parse(webSpecContent); if (OpenAPIParser) { this.webParser = new OpenAPIParser(this.webSpec); } getLogger().info(`OpenAPIReferenceHandler: Web Experimentation spec loaded from ${webSpecPath}`); } catch (error) { getLogger().warn({ error: error.message }, `OpenAPIReferenceHandler: Could not load Web Experimentation spec from ${webSpecPath}`); } // Load Feature Experimentation spec from src folder const featureSpecPath = join(__dirname, '../api-reference/swagger-feature.json'); try { const featureSpecContent = await readFile(featureSpecPath, 'utf-8'); this.featureSpec = JSON.parse(featureSpecContent); if (OpenAPIParser) { this.featureParser = new OpenAPIParser(this.featureSpec); } getLogger().info(`OpenAPIReferenceHandler: Feature Experimentation spec loaded from ${featureSpecPath}`); } catch (error) { getLogger().warn({ error: error.message }, `OpenAPIReferenceHandler: Could not load Feature Experimentation spec from ${featureSpecPath}`); } } catch (error) { getLogger().error({ error: error.message }, 'OpenAPIReferenceHandler: Failed to initialize parsers'); } } /** * Get the appropriate parser based on project ID or default to web */ getParserForProject(projectId) { // For now, default to web parser // TODO: Implement project-based platform detection return this.webParser || this.featureParser; } /** * Handle schema information queries */ async handleSchemaQuery(entityType, projectId) { try { getLogger().info({ entityType, projectId }, 'OpenAPIReferenceHandler.handleSchemaQuery'); if (!SUPPORTED_ENTITIES.includes(entityType)) { throw new Error(`Unsupported entity type: ${entityType}`); } // Get processed data from fields.generated.ts const fieldData = FIELDS[entityType]; if (!fieldData) { throw new Error(`No field data found for entity type: ${entityType}`); } // Get platform-specific parser const parser = this.getParserForProject(projectId); let liveSchema = null; if (parser) { try { const allSchemas = parser.getAllSchemas(); liveSchema = allSchemas[entityType]; } catch (error) { getLogger().warn({ entityType, error: error.message }, 'OpenAPIReferenceHandler: Could not get live schema'); } } // Get platform-specific differences const platformDifferences = getPlatformDifferences(entityType); const response = { entity_type: entityType, openapi_schema: { required_fields: Array.isArray(fieldData.required) ? [...fieldData.required] : [], optional_fields: Array.isArray(fieldData.optional) ? [...fieldData.optional] : [], defaults: fieldData.defaults ? { ...fieldData.defaults } : {}, enums: fieldData.enums ? this.convertReadonlyEnums(fieldData.enums) : {}, field_types: fieldData.fieldTypes ? { ...fieldData.fieldTypes } : {}, field_descriptions: fieldData.fieldDescriptions ? { ...fieldData.fieldDescriptions } : {} }, platform_specific: { feature_experimentation: platformDifferences.feature_experimentation, web_experimentation: platformDifferences.web_experimentation }, validation_rules: fieldData.validation ? { ...fieldData.validation } : {}, live_schema: liveSchema }; return response; } catch (error) { getLogger().error({ entityType, projectId, error: error.message }, 'OpenAPIReferenceHandler.handleSchemaQuery failed'); throw error; } } /** * Handle operation information queries */ async handleOperationQuery(entityType, operation) { try { getLogger().info({ entityType, operation }, 'OpenAPIReferenceHandler.handleOperationQuery'); if (!SUPPORTED_ENTITIES.includes(entityType)) { throw new Error(`Unsupported entity type: ${entityType}`); } if (!SUPPORTED_OPERATIONS.includes(operation)) { throw new Error(`Unsupported operation: ${operation}`); } // Get endpoint mapping const endpointInfo = this.getEndpointForOperation(entityType, operation); if (!endpointInfo) { throw new Error(`Operation ${operation} not supported for ${entityType}`); } // Get endpoint details from OpenAPI const parser = this.getParserForProject(); let endpointDetails = null; if (parser) { try { endpointDetails = parser.getEndpointDetails(endpointInfo.method, endpointInfo.path); } catch (error) { getLogger().warn({ entityType, operation, error: error.message }, 'OpenAPIReferenceHandler: Could not get endpoint details'); } } // Get base URL and generate full URLs const platform = endpointInfo.platform || 'both'; const baseUrl = this.getBaseUrlForPlatform(platform); const fullUrl = baseUrl + endpointInfo.path; const exampleUrl = this.generateExampleUrl(baseUrl, endpointInfo.path, entityType); // Extract parameters const requiredParams = this.extractRequiredParameters(endpointDetails); const optionalParams = this.extractOptionalParameters(endpointDetails); // Generate example payload const examplePayload = this.generateExamplePayload(entityType, operation, endpointDetails); // Generate example request const exampleRequest = { url: exampleUrl, method: endpointInfo.method, headers: { 'Authorization': 'Bearer YOUR_API_TOKEN', 'Content-Type': 'application/json' }, ...(examplePayload && (endpointInfo.method === 'POST' || endpointInfo.method === 'PATCH') && { body: examplePayload }) }; // Generate curl example const curlExample = this.generateCurlExample(endpointInfo.method, exampleUrl, (endpointInfo.method === 'POST' || endpointInfo.method === 'PATCH') ? examplePayload : undefined); const response = { entity_type: entityType, operation: operation, endpoint: `${endpointInfo.method} ${endpointInfo.path}`, method: endpointInfo.method, full_url: fullUrl, base_url: baseUrl, required_parameters: requiredParams, optional_parameters: optionalParams, request_body_schema: endpointDetails?.requestBody?.content?.['application/json']?.schema, response_schema: endpointDetails?.responses?.['200']?.content?.['application/json']?.schema, example_payload: examplePayload, example_request: exampleRequest, curl_example: curlExample, platform_specific: endpointInfo.platform }; return response; } catch (error) { getLogger().error({ entityType, operation, error: error.message }, 'OpenAPIReferenceHandler.handleOperationQuery failed'); throw error; } } /** * Handle field detail queries */ async handleFieldQuery(entityType, fieldName) { try { getLogger().info({ entityType, fieldName }, 'OpenAPIReferenceHandler.handleFieldQuery'); if (!SUPPORTED_ENTITIES.includes(entityType)) { throw new Error(`Unsupported entity type: ${entityType}`); } // Get field data from fields.generated.ts const fieldData = FIELDS[entityType]; if (!fieldData) { throw new Error(`No field data found for entity type: ${entityType}`); } // Check if field exists const requiredFields = Array.isArray(fieldData.required) ? [...fieldData.required] : []; const optionalFields = Array.isArray(fieldData.optional) ? [...fieldData.optional] : []; const isRequired = requiredFields.includes(fieldName); const isOptional = optionalFields.includes(fieldName); if (!isRequired && !isOptional) { throw new Error(`Field '${fieldName}' not found for entity type '${entityType}'`); } // Safe field access with type assertions const fieldTypes = fieldData.fieldTypes; const fieldDescriptions = fieldData.fieldDescriptions; const defaults = fieldData.defaults; const enums = fieldData.enums; const validation = fieldData.validation; const fieldExamples = fieldData.fieldExamples; // Get enhanced template data const template = getModelFriendlyTemplate(entityType); const templateField = template?.fields?.[fieldName]; const response = { entity_type: entityType, field_name: fieldName, field_details: { type: fieldTypes?.[fieldName] || templateField?.type || 'unknown', required: isRequired || templateField?.required || false, description: fieldDescriptions?.[fieldName] || templateField?.description || 'No description available', default_value: defaults?.[fieldName] || templateField?.default, enum_values: enums?.[fieldName] || templateField?.enum, validation_rules: validation?.[fieldName], example_value: fieldExamples?.[fieldName], // Enhanced data from templates validation_hints: templateField?.validation_hints, traffic_allocation_helper: templateField?.traffic_allocation_helper, weight_allocation_helper: templateField?.weight_allocation_helper, change_type_details: templateField?.change_type_details, variable_type_details: templateField?.variable_type_details, examples_by_use_case: templateField?.examples_by_use_case, common_patterns: templateField?.common_patterns, descriptions: templateField?.descriptions, activation_code_examples: templateField?.activation_code_examples, common_errors: templateField?.common_errors } }; return response; } catch (error) { getLogger().error({ entityType, fieldName, error: error.message }, 'OpenAPIReferenceHandler.handleFieldQuery failed'); throw error; } } /** * Handle dependency queries */ async handleDependencyQuery(entityType, operation) { try { getLogger().info({ entityType, operation }, 'OpenAPIReferenceHandler.handleDependencyQuery'); if (!SUPPORTED_ENTITIES.includes(entityType)) { throw new Error(`Unsupported entity type: ${entityType}`); } // Get dependencies from domain knowledge const dependencies = getDependenciesForEntity(entityType, operation); const creationOrder = getCreationOrder(entityType); const platformDifferences = getPlatformDifferences(entityType); // Separate required and optional dependencies const requiredDeps = dependencies.filter(dep => dep.relationship === 'required'); const optionalDeps = dependencies.filter(dep => dep.relationship === 'optional'); const response = { entity_type: entityType, operation: operation, dependencies: { required_entities: requiredDeps, optional_entities: optionalDeps, creation_order: [...creationOrder] }, platform_specific: { feature_experimentation: { required: Array.isArray(platformDifferences.feature_experimentation?.required) ? [...platformDifferences.feature_experimentation.required] : [], optional: Array.isArray(platformDifferences.feature_experimentation?.optional) ? [...platformDifferences.feature_experimentation.optional] : [] }, web_experimentation: { required: Array.isArray(platformDifferences.web_experimentation?.required) ? [...platformDifferences.web_experimentation.required] : [], optional: Array.isArray(platformDifferences.web_experimentation?.optional) ? [...platformDifferences.web_experimentation.optional] : [] } } }; return response; } catch (error) { getLogger().error({ entityType, operation, error: error.message }, 'OpenAPIReferenceHandler.handleDependencyQuery failed'); throw error; } } /** * Handle example queries */ async handleExamplesQuery(entityType, operation) { try { getLogger().info({ entityType, operation }, 'OpenAPIReferenceHandler.handleExamplesQuery'); if (!SUPPORTED_ENTITIES.includes(entityType)) { throw new Error(`Unsupported entity type: ${entityType}`); } // Get field data for examples const fieldData = FIELDS[entityType]; if (!fieldData) { throw new Error(`No field data found for entity type: ${entityType}`); } // Get enhanced template data const template = getModelFriendlyTemplate(entityType); // Use template example if available, otherwise generate basic const templateExample = template?.example_filled; const basicExample = templateExample || this.generateBasicExample(entityType, fieldData); // Generate advanced example if operation specified let advancedExample = null; if (operation) { advancedExample = this.generateAdvancedExample(entityType, operation, fieldData); } const response = { entity_type: entityType, operation: operation, examples: { basic_example: basicExample, advanced_example: advancedExample, template_example: templateExample, platform_specific_examples: { feature_experimentation: this.generatePlatformExample(entityType, 'feature'), web_experimentation: this.generatePlatformExample(entityType, 'web') }, // Enhanced examples from templates field_examples: this.extractFieldExamples(template), use_case_examples: this.extractUseCaseExamples(template) } }; return response; } catch (error) { getLogger().error({ entityType, operation, error: error.message }, 'OpenAPIReferenceHandler.handleExamplesQuery failed'); throw error; } } /** * Handle validation queries */ async handleValidationQuery(entityType) { try { getLogger().info({ entityType }, 'OpenAPIReferenceHandler.handleValidationQuery'); if (!SUPPORTED_ENTITIES.includes(entityType)) { throw new Error(`Unsupported entity type: ${entityType}`); } // Get field data for validation rules const fieldData = FIELDS[entityType]; if (!fieldData) { throw new Error(`No field data found for entity type: ${entityType}`); } // Get enhanced template data const template = getModelFriendlyTemplate(entityType); const templateValidationHints = this.extractValidationHints(template); const response = { entity_type: entityType, validation_rules: { field_validations: fieldData.validation || {}, business_rules: this.getBusinessRules(entityType), platform_specific_rules: { feature_experimentation: this.getPlatformValidationRules(entityType, 'feature'), web_experimentation: this.getPlatformValidationRules(entityType, 'web') }, // Enhanced validation from templates validation_hints: templateValidationHints, traffic_allocation_rules: this.extractTrafficAllocationRules(template), type_specific_validation: this.extractTypeValidation(template) }, common_validation_errors: this.getCommonValidationErrors(entityType) }; return response; } catch (error) { getLogger().error({ entityType, error: error.message }, 'OpenAPIReferenceHandler.handleValidationQuery failed'); throw error; } } /** * Handle relationships queries */ async handleRelationshipsQuery(entityType) { try { getLogger().info({ entityType }, 'OpenAPIReferenceHandler.handleRelationshipsQuery'); if (!SUPPORTED_ENTITIES.includes(entityType)) { throw new Error(`Unsupported entity type: ${entityType}`); } // Get relationships from domain knowledge const relationships = getRelationshipsForEntity(entityType); const entityRelationships = ENTITY_RELATIONSHIPS[entityType]; if (!entityRelationships) { throw new Error(`No relationship data found for entity type: ${entityType}`); } const response = { entity_type: entityType, relationships: { parent_entities: relationships.filter(r => r.relationship_type === 'many-to-one'), child_entities: relationships.filter(r => r.relationship_type === 'one-to-many'), referenced_by: relationships.filter(r => r.field_name.endsWith('_id')), references: relationships.filter(r => !r.field_name.endsWith('_id')) }, entity_hierarchy: { level: this.calculateEntityLevel(entityType), position: this.getEntityPosition(entityType), depends_on: [...entityRelationships.references], dependents: [...entityRelationships.referenced_by] } }; return response; } catch (error) { getLogger().error({ entityType, error: error.message }, 'OpenAPIReferenceHandler.handleRelationshipsQuery failed'); throw error; } } // Helper methods convertReadonlyEnums(enums) { const result = {}; for (const [key, value] of Object.entries(enums)) { if (Array.isArray(value)) { result[key] = [...value]; } } return result; } getEndpointForOperation(entityType, operation) { const endpointMappings = { project: { create: { method: 'POST', path: '/v2/projects', platform: 'both' }, update: { method: 'PATCH', path: '/v2/projects/{project_id}', platform: 'both' }, delete: { method: 'DELETE', path: '/v2/projects/{project_id}', platform: 'both' }, list: { method: 'GET', path: '/v2/projects', platform: 'both' }, get: { method: 'GET', path: '/v2/projects/{project_id}', platform: 'both' } }, experiment: { create: { method: 'POST', path: '/v2/projects/{project_id}/experiments', platform: 'web' }, update: { method: 'PATCH', path: '/v2/projects/{project_id}/experiments/{experiment_id}', platform: 'web' }, delete: { method: 'DELETE', path: '/v2/projects/{project_id}/experiments/{experiment_id}', platform: 'web' }, list: { method: 'GET', path: '/v2/projects/{project_id}/experiments', platform: 'web' }, get: { method: 'GET', path: '/v2/projects/{project_id}/experiments/{experiment_id}', platform: 'web' } }, flag: { create: { method: 'POST', path: '/flags/v1/projects/{project_id}/flags', platform: 'feature' }, update: { method: 'PATCH', path: '/flags/v1/projects/{project_id}/flags/{flag_key}', platform: 'feature' }, delete: { method: 'DELETE', path: '/flags/v1/projects/{project_id}/flags/{flag_key}', platform: 'feature' }, list: { method: 'GET', path: '/flags/v1/projects/{project_id}/flags', platform: 'feature' }, get: { method: 'GET', path: '/flags/v1/projects/{project_id}/flags/{flag_key}', platform: 'feature' } }, audience: { create: { method: 'POST', path: '/v2/projects/{project_id}/audiences', platform: 'both' }, update: { method: 'PATCH', path: '/v2/audiences/{audience_id}', platform: 'both' }, delete: { method: 'DELETE', path: '/v2/audiences/{audience_id}', platform: 'both' }, list: { method: 'GET', path: '/v2/projects/{project_id}/audiences', platform: 'both' }, get: { method: 'GET', path: '/v2/audiences/{audience_id}', platform: 'both' } }, page: { create: { method: 'POST', path: '/v2/projects/{project_id}/pages', platform: 'web' }, update: { method: 'PATCH', path: '/v2/pages/{page_id}', platform: 'web' }, delete: { method: 'DELETE', path: '/v2/pages/{page_id}', platform: 'web' }, list: { method: 'GET', path: '/v2/projects/{project_id}/pages', platform: 'web' }, get: { method: 'GET', path: '/v2/pages/{page_id}', platform: 'web' } }, event: { create: { method: 'POST', path: '/v2/projects/{project_id}/custom_events', platform: 'both' }, update: { method: 'PATCH', path: '/v2/projects/{project_id}/custom_events/{event_id}', platform: 'both' }, delete: { method: 'DELETE', path: '/v2/projects/{project_id}/custom_events/{event_id}', platform: 'both' }, list: { method: 'GET', path: '/v2/projects/{project_id}/events', platform: 'both' }, get: { method: 'GET', path: '/v2/events/{event_id}', platform: 'both' } }, attribute: { create: { method: 'POST', path: '/v2/projects/{project_id}/attributes', platform: 'both' }, update: { method: 'PATCH', path: '/v2/attributes/{attribute_id}', platform: 'both' }, delete: { method: 'DELETE', path: '/v2/attributes/{attribute_id}', platform: 'both' }, list: { method: 'GET', path: '/v2/projects/{project_id}/attributes', platform: 'both' }, get: { method: 'GET', path: '/v2/attributes/{attribute_id}', platform: 'both' } }, ruleset: { create: { method: 'POST', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/rulesets', platform: 'feature' }, update: { method: 'PATCH', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/rulesets/{ruleset_id}', platform: 'feature' }, delete: { method: 'DELETE', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/rulesets/{ruleset_id}', platform: 'feature' }, list: { method: 'GET', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/rulesets', platform: 'feature' }, get: { method: 'GET', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/rulesets/{ruleset_id}', platform: 'feature' } }, rule: { create: { method: 'POST', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/rulesets/{ruleset_id}/rules', platform: 'feature' }, update: { method: 'PATCH', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/rulesets/{ruleset_id}/rules/{rule_id}', platform: 'feature' }, delete: { method: 'DELETE', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/rulesets/{ruleset_id}/rules/{rule_id}', platform: 'feature' }, list: { method: 'GET', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/rulesets/{ruleset_id}/rules', platform: 'feature' }, get: { method: 'GET', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/rulesets/{ruleset_id}/rules/{rule_id}', platform: 'feature' } }, variation: { create: { method: 'POST', path: '/v2/projects/{project_id}/experiments/{experiment_id}/variations', platform: 'web' }, update: { method: 'PATCH', path: '/v2/projects/{project_id}/experiments/{experiment_id}/variations/{variation_id}', platform: 'web' }, delete: { method: 'DELETE', path: '/v2/projects/{project_id}/experiments/{experiment_id}/variations/{variation_id}', platform: 'web' }, list: { method: 'GET', path: '/v2/projects/{project_id}/experiments/{experiment_id}/variations', platform: 'web' }, get: { method: 'GET', path: '/v2/projects/{project_id}/experiments/{experiment_id}/variations/{variation_id}', platform: 'web' } }, variable_definition: { create: { method: 'POST', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/variables', platform: 'feature' }, update: { method: 'PATCH', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/variables/{variable_key}', platform: 'feature' }, delete: { method: 'DELETE', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/variables/{variable_key}', platform: 'feature' }, list: { method: 'GET', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/variables', platform: 'feature' }, get: { method: 'GET', path: '/flags/v1/projects/{project_id}/flags/{flag_key}/variables/{variable_key}', platform: 'feature' } }, campaign: { create: { method: 'POST', path: '/v2/projects/{project_id}/campaigns', platform: 'web' }, update: { method: 'PATCH', path: '/v2/projects/{project_id}/campaigns/{campaign_id}', platform: 'web' }, delete: { method: 'DELETE', path: '/v2/projects/{project_id}/campaigns/{campaign_id}', platform: 'web' }, list: { method: 'GET', path: '/v2/projects/{project_id}/campaigns', platform: 'web' }, get: { method: 'GET', path: '/v2/projects/{project_id}/campaigns/{campaign_id}', platform: 'web' } } }; return endpointMappings[entityType]?.[operation] || null; } getBaseUrlForPlatform(platform) { switch (platform) { case 'feature': return 'https://api.optimizely.com'; case 'web': return 'https://api.optimizely.com'; case 'both': default: return 'https://api.optimizely.com'; } } generateExampleUrl(baseUrl, path, entityType) { // Replace path parameters with realistic example values let exampleUrl = baseUrl + path; // Common parameter replacements const parameterExamples = { '{project_id}': '12345', '{experiment_id}': '67890', '{flag_key}': 'new_checkout_flow', '{audience_id}': '54321', '{page_id}': '98765', '{event_id}': '11111', '{attribute_id}': '22222', '{variation_id}': '33333', '{campaign_id}': '44444', '{ruleset_id}': '55555', '{rule_id}': '66666', '{environment_key}': 'production' }; // Replace all parameter placeholders for (const [placeholder, example] of Object.entries(parameterExamples)) { exampleUrl = exampleUrl.replace(placeholder, example); } return exampleUrl; } generateCurlExample(method, url, body) { let curl = `curl -X ${method} "${url}" \\ -H "Authorization: Bearer YOUR_API_TOKEN" \\ -H "Content-Type: application/json"`; if (body && (method === 'POST' || method === 'PATCH')) { curl += ` \\ -d '${JSON.stringify(body, null, 2)}'`; } return curl; } extractRequiredParameters(endpointDetails) { if (!endpointDetails) { return { path: [], query: [], body: [] }; } const pathParams = endpointDetails.parameters?.filter((p) => p.in === 'path' && p.required).map((p) => p.name) || []; const queryParams = endpointDetails.parameters?.filter((p) => p.in === 'query' && p.required).map((p) => p.name) || []; // Extract required body fields from schema const bodyParams = []; const requestBodySchema = endpointDetails.requestBody?.content?.['application/json']?.schema; if (requestBodySchema?.required) { bodyParams.push(...requestBodySchema.required); } return { path: pathParams, query: queryParams, body: bodyParams }; } extractOptionalParameters(endpointDetails) { if (!endpointDetails) { return { path: [], query: [], body: [] }; } const pathParams = endpointDetails.parameters?.filter((p) => p.in === 'path' && !p.required).map((p) => p.name) || []; const queryParams = endpointDetails.parameters?.filter((p) => p.in === 'query' && !p.required).map((p) => p.name) || []; // Extract optional body fields from schema const bodyParams = []; const requestBodySchema = endpointDetails.requestBody?.content?.['application/json']?.schema; if (requestBodySchema?.properties) { const required = requestBodySchema.required || []; const allProps = Object.keys(requestBodySchema.properties); const optional = allProps.filter(prop => !required.includes(prop)); bodyParams.push(...optional); } return { path: pathParams, query: queryParams, body: bodyParams }; } generateExamplePayload(entityType, operation, endpointDetails) { const fieldData = FIELDS[entityType]; if (!fieldData) return {}; const example = {}; // Add required fields with examples or defaults if (Array.isArray(fieldData.required)) { fieldData.required.forEach((field) => { const fieldExamples = fieldData.fieldExamples; const defaults = fieldData.defaults; const fieldTypes = fieldData.fieldTypes; if (fieldExamples && fieldExamples[field] !== undefined) { example[field] = fieldExamples[field]; } else if (defaults && defaults[field] !== undefined) { example[field] = defaults[field]; } else { // Generate type-appropriate example const fieldType = fieldTypes && fieldTypes[field]; example[field] = this.generateExampleForType(fieldType); } }); } return example; } generateBasicExample(entityType, fieldData) { const example = {}; // Include required fields only if (Array.isArray(fieldData.required)) { fieldData.required.forEach((field) => { const fieldExamples = fieldData.fieldExamples; const defaults = fieldData.defaults; const fieldTypes = fieldData.fieldTypes; if (fieldExamples && fieldExamples[field] !== undefined) { example[field] = fieldExamples[field]; } else if (defaults && defaults[field] !== undefined) { example[field] = defaults[field]; } else { const fieldType = fieldTypes && fieldTypes[field]; example[field] = this.generateExampleForType(fieldType); } }); } return example; } generateAdvancedExample(entityType, operation, fieldData) { const example = {}; // Include both required and some optional fields const requiredFields = Array.isArray(fieldData.required) ? [...fieldData.required] : []; const optionalFields = Array.isArray(fieldData.optional) ? fieldData.optional.slice(0, 3) : []; const allFields = [...requiredFields, ...optionalFields]; allFields.forEach((field) => { const fieldExamples = fieldData.fieldExamples; const defaults = fieldData.defaults; const fieldTypes = fieldData.fieldTypes; if (fieldExamples && fieldExamples[field] !== undefined) { example[field] = fieldExamples[field]; } else if (defaults && defaults[field] !== undefined) { example[field] = defaults[field]; } else { const fieldType = fieldTypes && fieldTypes[field]; example[field] = this.generateExampleForType(fieldType); } }); return example; } generatePlatformExample(entityType, platform) { // Return platform-specific examples if available const fieldData = FIELDS[entityType]; if (!fieldData) return null; // For now, return basic example - can be enhanced with platform-specific logic return this.generateBasicExample(entityType, fieldData); } generateExampleForType(fieldType) { switch (fieldType) { case 'string': return 'example_string'; case 'integer': return 123; case 'number': return 123.45; case 'boolean': return true; case 'array': return []; case 'object': return {}; default: return 'example_value'; } } getBusinessRules(entityType) { // Return entity-specific business rules const commonRules = { experiment: [ { rule_name: 'minimum_variations', description: 'Experiments must have at least 2 variations', validation_logic: 'variations.length >= 2' } ], flag: [ { rule_name: 'unique_flag_key', description: 'Flag keys must be unique within a project', validation_logic: 'key must be unique per project' } ] }; return commonRules[entityType] || []; } getPlatformValidationRules(entityType, platform) { // Return platform-specific validation rules return {}; } getCommonValidationErrors(entityType) { return [ { error_pattern: 'required field missing', description: 'One or more required fields are missing from the request', solution: 'Ensure all required fields are included in the request body' } ]; } calculateEntityLevel(entityType) { // Calculate hierarchical level (0 = top level) const entityRelationships = ENTITY_RELATIONSHIPS[entityType]; return entityRelationships?.parent_entities?.length || 0; } getEntityPosition(entityType) { const level = this.calculateEntityLevel(entityType); if (level === 0) return 'root'; if (level === 1) return 'child'; return 'nested'; } /** * Extract field-specific examples from template */ extractFieldExamples(template) { if (!template?.fields) return {}; const fieldExamples = {}; Object.entries(template.fields).forEach(([fieldName, fieldSchema]) => { if (fieldSchema.validation_hints?.valid_examples) { fieldExamples[fieldName] = fieldSchema.validation_hints.valid_examples; } else if (fieldSchema.examples_by_use_case) { fieldExamples[fieldName] = fieldSchema.examples_by_use_case; } else if (fieldSchema.variable_type_details) { // Extract examples from variable type details const typeExamples = {}; Object.entries(fieldSchema.variable_type_details).forEach(([typeName, typeData]) => { if (typeData.examples) { typeExamples[typeName] = typeData.examples; } }); if (Object.keys(typeExamples).length > 0) { fieldExamples[fieldName] = typeExamples; } } }); return fieldExamples; } /** * Extract use case examples from template */ extractUseCaseExamples(template) { if (!template?.fields) return {}; const useCaseExamples = {}; Object.entries(template.fields).forEach(([fieldName, fieldSchema]) => { if (fieldSchema.common_patterns) { useCaseExamples[`${fieldName}_patterns`] = fieldSchema.common_patterns; } if (fieldSchema.change_type_details) { useCaseExamples[`${fieldName}_change_types`] = fieldSchema.change_type_details; } if (fieldSchema.traffic_allocation_helper?.common_splits) { useCaseExamples[`${fieldName}_traffic_splits`] = fieldSchema.traffic_allocation_helper.common_splits; } if (fieldSchema.weight_allocation_helper?.common_patterns) { useCaseExamples[`${fieldName}_weight_patterns`] = fieldSchema.weight_allocation_helper.common_patterns; } }); return useCaseExamples; } /** * Extract validation hints from template */ extractValidationHints(template) { if (!template?.fields) return {}; const validationHints = {}; Object.entries(template.fields).forEach(([fieldName, fieldSchema]) => { if (fieldSchema.validation_hints) { validationHints[fieldName] = fieldSchema.validation_hints; } }); return validationHints; } /** * Extract traffic allocation rules from template */ extractTrafficAllocationRules(template) { if (!template?.fields) return {}; const rules = {}; Object.entries(template.fields).forEach(([fieldName, fieldSchema]) => { if (fieldSchema.traffic_allocation_helper) { rules[fieldName] = fieldSchema.traffic_allocation_helper; } if (fieldSchema.weight_allocation_helper) { rules[fieldName] = fieldSchema.weight_allocation_helper; } }); return rules; } /** * Extract type-specific validation from template */ extractTypeValidation(template) { if (!template?.fields) return {}; const typeValidation = {}; Object.entries(template.fields).forEach(([fieldName, fieldSchema]) => { if (fieldSchema.variable_type_details) { const typeRules = {}; Object.entries(fieldSchema.variable_type_details).forEach(([typeName, typeData]) => { if (typeData.validation || typeData.best_practices || typeData.common_mistakes) { typeRules[typeName] = { validation: typeData.validation, best_practices: typeData.best_practices, common_mistakes: typeData.common_mistakes }; } }); if (Object.keys(typeRules).length > 0) { typeValidation[fieldName] = typeRules; } } }); return typeValidation; } } //# sourceMappingURL=OpenAPIReferenceHandler.js.map