294 lines
10 KiB
JavaScript
294 lines
10 KiB
JavaScript
/**
|
|
* @fileoverview Extract complete provider endpoint details from api-docs.json
|
|
* Extracts all 147 provider endpoints with exact parameter names, types, and descriptions
|
|
*/
|
|
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
/**
|
|
* Extract complete provider endpoint details
|
|
*/
|
|
function extractCompleteProviderDetails() {
|
|
try {
|
|
console.log('=== EXTRACTING COMPLETE PROVIDER ENDPOINT DETAILS ===');
|
|
console.log('');
|
|
|
|
// Read the categorized endpoints
|
|
const categorizedPath = path.join(process.cwd(), 'categorized-endpoints.json');
|
|
const categorizedContent = fs.readFileSync(categorizedPath, 'utf8');
|
|
const categorized = JSON.parse(categorizedContent);
|
|
|
|
// Read the original api-docs.json for complete parameter details
|
|
const apiDocsPath = path.join(process.cwd(), '..', 'api-docs.json');
|
|
const apiDocsContent = fs.readFileSync(apiDocsPath, 'utf8');
|
|
const apiDocs = JSON.parse(apiDocsContent);
|
|
|
|
console.log(`Found ${categorized.provider.length} provider endpoints to process`);
|
|
console.log('');
|
|
|
|
const completeProviderEndpoints = [];
|
|
|
|
// Process each provider endpoint
|
|
categorized.provider.forEach((endpoint, index) => {
|
|
console.log(`Processing ${index + 1}/${categorized.provider.length}: ${endpoint.method} ${endpoint.path}`);
|
|
|
|
// Find the complete endpoint details in api-docs.json
|
|
const completeDetails = findCompleteEndpointDetails(apiDocs, endpoint.path, endpoint.method);
|
|
|
|
if (completeDetails) {
|
|
const enhancedEndpoint = {
|
|
...endpoint,
|
|
completeParameters: extractCompleteParameters(completeDetails),
|
|
requestBodySchema: extractRequestBodySchema(completeDetails),
|
|
responseSchema: extractResponseSchema(completeDetails),
|
|
exactToolName: generateExactToolName(endpoint, completeDetails),
|
|
detailedDescription: completeDetails.summary || completeDetails.description || endpoint.summary,
|
|
operationId: completeDetails.operationId,
|
|
tags: completeDetails.tags || []
|
|
};
|
|
|
|
completeProviderEndpoints.push(enhancedEndpoint);
|
|
} else {
|
|
console.warn(`⚠️ Could not find complete details for: ${endpoint.method} ${endpoint.path}`);
|
|
// Still add the endpoint with available information
|
|
completeProviderEndpoints.push({
|
|
...endpoint,
|
|
completeParameters: {},
|
|
requestBodySchema: null,
|
|
responseSchema: null,
|
|
exactToolName: generateExactToolName(endpoint, null),
|
|
detailedDescription: endpoint.summary || endpoint.description,
|
|
operationId: null,
|
|
tags: []
|
|
});
|
|
}
|
|
});
|
|
|
|
console.log('');
|
|
console.log(`=== EXTRACTION COMPLETE ===`);
|
|
console.log(`Processed ${completeProviderEndpoints.length} provider endpoints`);
|
|
console.log('');
|
|
|
|
// Save the complete provider details
|
|
const outputPath = path.join(process.cwd(), 'complete-provider-endpoints.json');
|
|
fs.writeFileSync(outputPath, JSON.stringify(completeProviderEndpoints, null, 2));
|
|
|
|
console.log(`Complete provider endpoint details saved to: ${outputPath}`);
|
|
|
|
// Display summary by category
|
|
const categoryCount = {};
|
|
completeProviderEndpoints.forEach(endpoint => {
|
|
const category = endpoint.category || 'unknown';
|
|
categoryCount[category] = (categoryCount[category] || 0) + 1;
|
|
});
|
|
|
|
console.log('');
|
|
console.log('=== PROVIDER ENDPOINTS BY CATEGORY ===');
|
|
Object.keys(categoryCount).sort().forEach(category => {
|
|
console.log(`${category}: ${categoryCount[category]} endpoints`);
|
|
});
|
|
|
|
return completeProviderEndpoints;
|
|
|
|
} catch (error) {
|
|
console.error('Error extracting complete provider details:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find complete endpoint details in api-docs.json
|
|
*/
|
|
function findCompleteEndpointDetails(apiDocs, path, method) {
|
|
const paths = apiDocs.paths || {};
|
|
const pathData = paths[path];
|
|
|
|
if (!pathData) {
|
|
return null;
|
|
}
|
|
|
|
const methodData = pathData[method.toLowerCase()];
|
|
return methodData || null;
|
|
}
|
|
|
|
/**
|
|
* Extract complete parameters from endpoint definition
|
|
*/
|
|
function extractCompleteParameters(endpointDetails) {
|
|
const parameters = {};
|
|
|
|
// Extract path parameters, query parameters, header parameters
|
|
if (endpointDetails.parameters) {
|
|
endpointDetails.parameters.forEach(param => {
|
|
parameters[param.name] = {
|
|
name: param.name,
|
|
in: param.in, // path, query, header, etc.
|
|
type: param.schema?.type || param.type || 'string',
|
|
format: param.schema?.format || param.format,
|
|
required: param.required || false,
|
|
description: param.description || `${param.name} parameter`,
|
|
example: param.schema?.example || param.example,
|
|
enum: param.schema?.enum || param.enum,
|
|
minimum: param.schema?.minimum || param.minimum,
|
|
maximum: param.schema?.maximum || param.maximum
|
|
};
|
|
});
|
|
}
|
|
|
|
return parameters;
|
|
}
|
|
|
|
/**
|
|
* Extract request body schema
|
|
*/
|
|
function extractRequestBodySchema(endpointDetails) {
|
|
if (!endpointDetails.requestBody) {
|
|
return null;
|
|
}
|
|
|
|
const requestBody = {
|
|
required: endpointDetails.requestBody.required || false,
|
|
description: endpointDetails.requestBody.description || '',
|
|
content: {}
|
|
};
|
|
|
|
// Extract content types and their schemas
|
|
if (endpointDetails.requestBody.content) {
|
|
Object.keys(endpointDetails.requestBody.content).forEach(contentType => {
|
|
const contentData = endpointDetails.requestBody.content[contentType];
|
|
|
|
if (contentData.schema) {
|
|
requestBody.content[contentType] = {
|
|
schema: contentData.schema,
|
|
properties: extractSchemaProperties(contentData.schema),
|
|
required: contentData.schema.required || []
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
return requestBody;
|
|
}
|
|
|
|
/**
|
|
* Extract schema properties recursively
|
|
*/
|
|
function extractSchemaProperties(schema) {
|
|
if (!schema || !schema.properties) {
|
|
return {};
|
|
}
|
|
|
|
const properties = {};
|
|
|
|
Object.keys(schema.properties).forEach(propName => {
|
|
const prop = schema.properties[propName];
|
|
properties[propName] = {
|
|
type: prop.type || 'string',
|
|
format: prop.format,
|
|
description: prop.description || `${propName} property`,
|
|
example: prop.example,
|
|
enum: prop.enum,
|
|
items: prop.items,
|
|
properties: prop.properties ? extractSchemaProperties(prop) : undefined,
|
|
required: schema.required ? schema.required.includes(propName) : false
|
|
};
|
|
});
|
|
|
|
return properties;
|
|
}
|
|
|
|
/**
|
|
* Extract response schema
|
|
*/
|
|
function extractResponseSchema(endpointDetails) {
|
|
if (!endpointDetails.responses) {
|
|
return null;
|
|
}
|
|
|
|
const responses = {};
|
|
|
|
Object.keys(endpointDetails.responses).forEach(statusCode => {
|
|
const response = endpointDetails.responses[statusCode];
|
|
responses[statusCode] = {
|
|
description: response.description || '',
|
|
content: response.content || {}
|
|
};
|
|
});
|
|
|
|
return responses;
|
|
}
|
|
|
|
/**
|
|
* Generate exact tool name following the established convention
|
|
*/
|
|
function generateExactToolName(endpoint, completeDetails) {
|
|
const method = endpoint.method.toLowerCase();
|
|
const path = endpoint.path.toLowerCase();
|
|
|
|
// Extract meaningful parts from the path
|
|
let pathParts = path.split('/').filter(part => part && !part.startsWith('{') && !part.endsWith('}'));
|
|
|
|
// Remove common prefixes
|
|
pathParts = pathParts.filter(part => !['api', 'emr', 'emr-api'].includes(part));
|
|
|
|
// Determine action based on method and path context
|
|
let action = method;
|
|
if (method === 'get') {
|
|
action = 'get';
|
|
} else if (method === 'post') {
|
|
if (path.includes('/login') || path.includes('/register') || path.includes('/create')) {
|
|
action = 'create';
|
|
} else if (path.includes('/search') || path.includes('/find')) {
|
|
action = 'search';
|
|
} else {
|
|
action = 'create';
|
|
}
|
|
} else if (method === 'put' || method === 'patch') {
|
|
action = 'update';
|
|
} else if (method === 'delete') {
|
|
action = 'delete';
|
|
}
|
|
|
|
// Create resource name from path parts
|
|
let resource = pathParts.join('_').replace(/-/g, '_');
|
|
|
|
// Clean up resource name
|
|
resource = resource.replace(/[^a-z0-9_]/g, '');
|
|
|
|
// Handle special cases for better naming
|
|
if (path.includes('/appointment')) {
|
|
resource = resource.replace(/appointment/, 'appointment');
|
|
}
|
|
if (path.includes('/patient')) {
|
|
resource = resource.replace(/patient/, 'patient');
|
|
}
|
|
if (path.includes('/meeting')) {
|
|
resource = resource.replace(/meeting/, 'meeting');
|
|
}
|
|
if (path.includes('/form')) {
|
|
resource = resource.replace(/form/, 'form');
|
|
}
|
|
if (path.includes('/document')) {
|
|
resource = resource.replace(/document/, 'document');
|
|
}
|
|
|
|
// Ensure we have a resource name
|
|
if (!resource) {
|
|
if (completeDetails && completeDetails.operationId) {
|
|
resource = completeDetails.operationId.toLowerCase().replace(/[^a-z0-9_]/g, '_');
|
|
} else {
|
|
resource = 'unknown';
|
|
}
|
|
}
|
|
|
|
return `provider_${action}_${resource}`;
|
|
}
|
|
|
|
// Run the extraction
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
extractCompleteProviderDetails();
|
|
}
|
|
|
|
export { extractCompleteProviderDetails };
|