@simonecoelhosfo/optimizely-mcp-server
Version:
Optimizely MCP Server for AI assistants with integrated CLI tools
883 lines • 43.7 kB
JavaScript
/**
* 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