ai-voicebot/client/demo-api-evolution.js
2025-09-03 13:54:29 -07:00

178 lines
5.6 KiB
JavaScript

#!/usr/bin/env node
/**
* Demo script to show API evolution detection in action
* This simulates having missing endpoints and shows the automated update process
*/
const fs = require('fs');
const path = require('path');
class DemoEvolutionChecker {
constructor() {
this.schemaPath = path.join(__dirname, 'openapi-schema.json');
// Simulate missing the 'clear_password' endpoint to show detection
this.implementedEndpoints = new Set([
'GET:/ai-voicebot/api/admin/names',
'POST:/ai-voicebot/api/admin/set_password',
// 'POST:/ai-voicebot/api/admin/clear_password', // Commented out to simulate missing
'GET:/ai-voicebot/api/health',
'GET:/ai-voicebot/api/session',
'GET:/ai-voicebot/api/lobby',
'POST:/ai-voicebot/api/lobby/{session_id}'
]);
}
loadSchema() {
try {
if (!fs.existsSync(this.schemaPath)) {
throw new Error(`Schema file not found at: ${this.schemaPath}`);
}
const schemaContent = fs.readFileSync(this.schemaPath, 'utf8');
return JSON.parse(schemaContent);
} catch (error) {
throw new Error(`Failed to load OpenAPI schema: ${error.message}`);
}
}
extractEndpoints(schema) {
const endpoints = [];
if (!schema.paths) {
throw new Error('No paths found in OpenAPI schema');
}
Object.keys(schema.paths).forEach(path => {
// Skip the generic proxy endpoint
if (path === '/ai-voicebot/{path}') {
return;
}
const pathObj = schema.paths[path];
Object.keys(pathObj).forEach(method => {
const operation = pathObj[method];
const endpointKey = `${method.toUpperCase()}:${path}`;
endpoints.push({
path,
method: method.toUpperCase(),
operationId: operation.operationId,
summary: operation.summary,
implemented: this.implementedEndpoints.has(endpointKey),
endpointKey
});
});
});
return endpoints;
}
generateMethodName(endpoint) {
if (endpoint.operationId) {
// Convert snake_case operation ID to camelCase
return endpoint.operationId.replace(/_([a-z])/g, (match, letter) => letter.toUpperCase());
}
// Fallback: generate from path and method
const pathParts = endpoint.path.split('/').filter(part => part && !part.startsWith('{'));
const lastPart = pathParts[pathParts.length - 1] || 'endpoint';
const method = endpoint.method.toLowerCase();
if (method === 'get') {
return `get${lastPart.charAt(0).toUpperCase() + lastPart.slice(1)}`;
} else {
return `${method}${lastPart.charAt(0).toUpperCase() + lastPart.slice(1)}`;
}
}
generateImplementationStub(endpoint) {
const methodName = this.generateMethodName(endpoint);
const hasPathParams = endpoint.path.includes('{');
const needsBody = ['POST', 'PUT', 'PATCH'].includes(endpoint.method);
let params = [];
let requestOptions = `{ method: '${endpoint.method}'`;
// Add path parameters
if (hasPathParams) {
const pathParams = endpoint.path.match(/\{([^}]+)\}/g) || [];
pathParams.forEach(param => {
const paramName = param.replace(/[{}]/g, '');
params.push(`${paramName}: string`);
});
}
// Add body parameter for methods that typically need one
if (needsBody) {
params.push('data?: any');
requestOptions += ', body: data';
}
requestOptions += ' }';
const paramString = params.length > 0 ? params.join(', ') : '';
return ` async ${methodName}(${paramString}): Promise<any> {
return this.request<any>('${endpoint.path}', ${requestOptions});
}`;
}
async runDemo() {
console.log('🔍 API Evolution Check (Demo Mode)');
console.log('='.repeat(50));
console.log('Note: This demo simulates a missing endpoint to show detection in action');
console.log('');
try {
const schema = this.loadSchema();
const endpoints = this.extractEndpoints(schema);
const implemented = endpoints.filter(ep => ep.implemented);
const unimplemented = endpoints.filter(ep => !ep.implemented);
console.log(`📊 Summary:`);
console.log(` Total endpoints: ${endpoints.length}`);
console.log(` Implemented: ${implemented.length}`);
console.log(` Unimplemented: ${unimplemented.length}`);
console.log('');
if (unimplemented.length === 0) {
console.log('✅ All API endpoints are implemented in ApiClient!');
return;
}
console.log('⚠️ Unimplemented API endpoints:');
unimplemented.forEach(endpoint => {
console.log(`${endpoint.method} ${endpoint.path}`);
if (endpoint.summary) {
console.log(` ${endpoint.summary}`);
}
});
console.log('');
console.log('💡 Implementation suggestions:');
console.log('Add these methods to the ApiClient class:');
console.log('');
unimplemented.forEach(endpoint => {
console.log(this.generateImplementationStub(endpoint));
console.log('');
});
console.log('📝 Next steps:');
console.log('1. Copy the suggested methods above into client/src/api-client.ts');
console.log('2. Update the implementedEndpoints Set in the evolution checker');
console.log('3. Add convenience methods to the appropriate API namespaces');
console.log('4. Update type imports if needed');
} catch (error) {
console.error('❌ Error during API evolution check:', error.message);
process.exit(1);
}
}
}
// Run the demo
const checker = new DemoEvolutionChecker();
checker.runDemo();