breathe-api
Version:
Model Context Protocol server for Breathe HR APIs with Swagger/OpenAPI support - also works with custom APIs
207 lines • 7.82 kB
JavaScript
import { parseSwagger } from '../tools/swagger-parser.js';
import { generateReactNativeClient } from './react-native.js';
import { generateNextJsClient } from './nextjs.js';
import { generateReactClient } from './react.js';
import { generateRubyClient } from './ruby.js';
import { CodeGenerationError } from '../utils/errors.js';
import fs from 'fs/promises';
import path from 'path';
export async function generateApiClient(config, progressTracker) {
try {
if (progressTracker) {
await progressTracker.update(0.2, 'Parsing Swagger specification...');
}
const parsed = await parseSwagger({
url: config.swaggerUrl,
headers: config.swaggerHeaders,
generateTypes: true,
});
if (progressTracker) {
await progressTracker.update(0.4, 'Creating output directory...');
}
await fs.mkdir(config.outputDir, { recursive: true });
let files;
if (progressTracker) {
await progressTracker.update(0.5, `Generating ${config.platform} client code...`);
}
if (config.platform === 'react-native') {
files = await generateReactNativeClient(parsed, config);
}
else if (config.platform === 'react') {
files = await generateReactClient(parsed, config);
}
else if (config.platform === 'ruby') {
files = await generateRubyClient(parsed, config);
}
else {
files = await generateNextJsClient(parsed, config);
}
if (progressTracker) {
await progressTracker.update(0.8, 'Writing generated files...');
}
const createdFiles = [];
for (const [filename, content] of Object.entries(files)) {
const filePath = path.join(config.outputDir, filename);
const dir = path.dirname(filePath);
await fs.mkdir(dir, { recursive: true });
await fs.writeFile(filePath, content, 'utf-8');
createdFiles.push(filePath);
}
if (progressTracker) {
await progressTracker.update(0.9, 'Generating documentation...');
}
const implDoc = generateImplementationDoc(parsed, config, createdFiles);
const docPath = path.join(config.outputDir, 'IMPLEMENTATION.md');
await fs.writeFile(docPath, implDoc, 'utf-8');
createdFiles.push(docPath);
if (progressTracker) {
await progressTracker.update(1.0, 'Code generation complete!');
}
return `Successfully generated API client for ${config.platform}:\n${createdFiles.join('\n')}`;
}
catch (error) {
throw new CodeGenerationError(`Failed to generate ${config.platform} client: ${error instanceof Error ? error.message : String(error)}`, config.platform, {
swaggerUrl: config.swaggerUrl,
outputDir: config.outputDir,
}, [
{
message: 'Ensure the Swagger URL is accessible and valid',
action: 'Check network connectivity and authentication',
},
{
message: 'Verify the output directory is writable',
action: 'Check file system permissions',
},
]);
}
}
function generateImplementationDoc(parsed, config, files) {
const doc = [];
doc.push(`# API Client Implementation Guide`);
doc.push('');
doc.push(`Generated from: ${config.swaggerUrl}`);
doc.push(`Platform: ${config.platform}`);
doc.push(`Generated on: ${new Date().toISOString()}`);
doc.push('');
doc.push('## Generated Files');
doc.push('');
for (const file of files) {
doc.push(`- ${path.basename(file)}`);
}
doc.push('');
doc.push('## API Overview');
doc.push('');
doc.push(`Base URL: ${parsed.baseUrl || config.baseUrl || 'Not specified'}`);
doc.push(`Total Endpoints: ${parsed.operations.length}`);
doc.push(`Total Schemas: ${parsed.schemas.length}`);
doc.push('');
doc.push('## Available Operations');
doc.push('');
for (const op of parsed.operations) {
doc.push(`### ${op.method} ${op.path}`);
if (op.summary) {
doc.push(`${op.summary}`);
}
if (op.operationId) {
doc.push(`Operation ID: \`${op.operationId}\``);
}
doc.push('');
}
doc.push('## Usage Examples');
doc.push('');
if (config.platform === 'react-native') {
doc.push('### React Native Usage');
doc.push('');
doc.push('```typescript');
doc.push("import { ApiClient } from './api/client';");
doc.push("import { useApi } from './api/hooks';");
doc.push('');
doc.push('// Initialize client');
doc.push('const client = new ApiClient({');
doc.push(' baseURL: process.env.API_BASE_URL,');
if (config.authType === 'bearer') {
doc.push(" token: 'your-auth-token',");
}
doc.push('});');
doc.push('');
doc.push('// Using hooks');
doc.push('function MyComponent() {');
doc.push(" const { data, loading, error } = useApi('operationId');");
doc.push(' // ...');
doc.push('}');
doc.push('```');
}
else if (config.platform === 'ruby') {
doc.push('### Ruby Usage');
doc.push('');
doc.push('```ruby');
doc.push("require_relative './lib/openapi_client'");
doc.push('api = OpenAPIClient::DefaultApi.new');
doc.push('# api.your_method_here');
doc.push('```');
}
else if (config.platform === 'react') {
doc.push('### React Usage');
doc.push('');
doc.push('```typescript');
doc.push("import { apiClient, useApi } from './api';");
doc.push('function Component() {');
doc.push(" const { data, error } = useApi('operationId');");
doc.push(' // ...');
doc.push('}');
doc.push('```');
}
else {
doc.push('### Next.js Usage');
doc.push('');
doc.push('```typescript');
doc.push('// Server Component');
doc.push("import { apiClient } from '@/lib/api/client';");
doc.push('');
doc.push('export default async function Page() {');
doc.push(' const data = await apiClient.operationName();');
doc.push(' return <div>{/* render data */}</div>;');
doc.push('}');
doc.push('');
doc.push('// Client Component with SWR');
doc.push("'use client';");
doc.push("import { useApi } from '@/lib/api/hooks';");
doc.push('');
doc.push('export default function Component() {');
doc.push(" const { data, error, isLoading } = useApi('operationId');");
doc.push(' // ...');
doc.push('}');
doc.push('```');
}
doc.push('');
doc.push('## Configuration');
doc.push('');
doc.push('### Environment Variables');
doc.push('');
doc.push('```env');
doc.push(`API_BASE_URL=${parsed.baseUrl || 'https://api.example.com'}`);
if (config.authType === 'bearer') {
doc.push('API_TOKEN=your-token-here');
}
else if (config.authType === 'api-key') {
doc.push('API_KEY=your-api-key-here');
}
doc.push('```');
doc.push('');
doc.push('## Features');
doc.push('');
if (config.features?.hooks) {
doc.push('- ✅ React Hooks for data fetching');
}
if (config.features?.errorHandling) {
doc.push('- ✅ Comprehensive error handling');
}
if (config.features?.caching) {
doc.push('- ✅ Response caching');
}
if (config.features?.validation) {
doc.push('- ✅ Request/Response validation');
}
return doc.join('\n');
}
//# sourceMappingURL=index.js.map