UNPKG

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
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