mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-10 02:24:35 +08:00
- Implemented main render function for the issue discovery view. - Added data loading functions to fetch discoveries, details, findings, and progress. - Created rendering functions for discovery list and detail sections. - Introduced filtering and searching capabilities for findings. - Implemented actions for exporting and dismissing findings. - Added polling mechanism to track discovery progress. - Included utility functions for HTML escaping and cleanup.
9.0 KiB
9.0 KiB
Swagger/OpenAPI Runner
Guide for generating backend API documentation from OpenAPI/Swagger specifications.
Overview
This script extracts and converts OpenAPI/Swagger specifications to Markdown format for inclusion in the software manual.
Detection Strategy
Check for Existing Specification
async function detectOpenAPISpec() {
// Check for existing spec files
const specPatterns = [
'openapi.json',
'openapi.yaml',
'openapi.yml',
'swagger.json',
'swagger.yaml',
'swagger.yml',
'**/openapi*.json',
'**/swagger*.json'
];
for (const pattern of specPatterns) {
const files = Glob(pattern);
if (files.length > 0) {
return { found: true, type: 'file', path: files[0] };
}
}
// Check for swagger-jsdoc in dependencies
const packageJson = JSON.parse(Read('package.json'));
if (packageJson.dependencies?.['swagger-jsdoc'] ||
packageJson.devDependencies?.['swagger-jsdoc']) {
return { found: true, type: 'jsdoc' };
}
// Check for NestJS Swagger
if (packageJson.dependencies?.['@nestjs/swagger']) {
return { found: true, type: 'nestjs' };
}
// Check for runtime endpoint
return { found: false, suggestion: 'runtime' };
}
Extraction Methods
Method A: From Existing Spec File
async function extractFromFile(specPath, workDir) {
const outputDir = `${workDir}/api-docs/backend`;
Bash({ command: `mkdir -p "${outputDir}"` });
// Copy spec to output
Bash({ command: `cp "${specPath}" "${outputDir}/openapi.json"` });
// Convert to Markdown using widdershins
const result = Bash({
command: `npx widdershins "${specPath}" -o "${outputDir}/api-reference.md" --language_tabs 'javascript:JavaScript' 'python:Python' 'bash:cURL'`,
timeout: 60000
});
return { success: result.exitCode === 0, outputDir };
}
Method B: From swagger-jsdoc
async function extractFromJsDoc(workDir) {
const outputDir = `${workDir}/api-docs/backend`;
// Look for swagger definition file
const defFiles = Glob('**/swagger*.js').concat(Glob('**/openapi*.js'));
if (defFiles.length === 0) {
return { success: false, error: 'No swagger definition found' };
}
// Generate spec
const result = Bash({
command: `npx swagger-jsdoc -d "${defFiles[0]}" -o "${outputDir}/openapi.json"`,
timeout: 60000
});
if (result.exitCode !== 0) {
return { success: false, error: result.stderr };
}
// Convert to Markdown
Bash({
command: `npx widdershins "${outputDir}/openapi.json" -o "${outputDir}/api-reference.md" --language_tabs 'javascript:JavaScript' 'bash:cURL'`
});
return { success: true, outputDir };
}
Method C: From NestJS Swagger
async function extractFromNestJS(workDir) {
const outputDir = `${workDir}/api-docs/backend`;
// NestJS typically exposes /api-docs-json at runtime
// We need to start the server temporarily
// Start server in background
const server = Bash({
command: 'npm run start:dev',
run_in_background: true,
timeout: 30000
});
// Wait for server to be ready
await waitForServer('http://localhost:3000', 30000);
// Fetch OpenAPI spec
const spec = await fetch('http://localhost:3000/api-docs-json');
const specJson = await spec.json();
// Save spec
Write(`${outputDir}/openapi.json`, JSON.stringify(specJson, null, 2));
// Stop server
KillShell({ shell_id: server.task_id });
// Convert to Markdown
Bash({
command: `npx widdershins "${outputDir}/openapi.json" -o "${outputDir}/api-reference.md" --language_tabs 'javascript:JavaScript' 'bash:cURL'`
});
return { success: true, outputDir };
}
Method D: From Runtime Endpoint
async function extractFromRuntime(workDir, serverUrl = 'http://localhost:3000') {
const outputDir = `${workDir}/api-docs/backend`;
// Common OpenAPI endpoint paths
const endpointPaths = [
'/api-docs-json',
'/swagger.json',
'/openapi.json',
'/docs/json',
'/api/v1/docs.json'
];
let specJson = null;
for (const path of endpointPaths) {
try {
const response = await fetch(`${serverUrl}${path}`);
if (response.ok) {
specJson = await response.json();
break;
}
} catch (e) {
continue;
}
}
if (!specJson) {
return { success: false, error: 'Could not fetch OpenAPI spec from server' };
}
// Save and convert
Write(`${outputDir}/openapi.json`, JSON.stringify(specJson, null, 2));
Bash({
command: `npx widdershins "${outputDir}/openapi.json" -o "${outputDir}/api-reference.md"`
});
return { success: true, outputDir };
}
Installation
Required Tools
# For OpenAPI to Markdown conversion
npm install -g widdershins
# Or as dev dependency
npm install --save-dev widdershins
# For generating from JSDoc comments
npm install --save-dev swagger-jsdoc
Configuration
widdershins Options
npx widdershins openapi.json \
-o api-reference.md \
--language_tabs 'javascript:JavaScript' 'python:Python' 'bash:cURL' \
--summary \
--omitHeader \
--resolve \
--expandBody
| Option | Description |
|---|---|
--language_tabs |
Code example languages |
--summary |
Use summary as operation heading |
--omitHeader |
Don't include title header |
--resolve |
Resolve $ref references |
--expandBody |
Show full request body |
swagger-jsdoc Definition
Example swagger-def.js:
module.exports = {
definition: {
openapi: '3.0.0',
info: {
title: 'MyApp API',
version: '1.0.0',
description: 'API documentation for MyApp'
},
servers: [
{ url: 'http://localhost:3000/api/v1' }
]
},
apis: ['./src/routes/*.js', './src/controllers/*.js']
};
Output Format
Generated Markdown Structure
# MyApp API
## Overview
Base URL: `http://localhost:3000/api/v1`
## Authentication
This API uses Bearer token authentication.
---
## Projects
### List Projects
`GET /projects`
Returns a list of all projects.
**Parameters**
| Name | In | Type | Required | Description |
|------|-----|------|----------|-------------|
| status | query | string | false | Filter by status |
| page | query | integer | false | Page number |
**Responses**
| Status | Description |
|--------|-------------|
| 200 | Successful response |
| 401 | Unauthorized |
**Example Request**
```javascript
fetch('/api/v1/projects?status=active')
.then(res => res.json())
.then(data => console.log(data));
Example Response
{
"data": [
{ "id": "1", "name": "Project 1" }
],
"pagination": {
"page": 1,
"total": 10
}
}
## Integration
### Main Runner
```javascript
async function runSwaggerExtraction(workDir) {
const detection = await detectOpenAPISpec();
if (!detection.found) {
console.log('No OpenAPI spec detected. Skipping backend API docs.');
return { success: false, skipped: true };
}
let result;
switch (detection.type) {
case 'file':
result = await extractFromFile(detection.path, workDir);
break;
case 'jsdoc':
result = await extractFromJsDoc(workDir);
break;
case 'nestjs':
result = await extractFromNestJS(workDir);
break;
default:
result = await extractFromRuntime(workDir);
}
if (result.success) {
// Post-process the Markdown
await postProcessApiDocs(result.outputDir);
}
return result;
}
async function postProcessApiDocs(outputDir) {
const mdFile = `${outputDir}/api-reference.md`;
let content = Read(mdFile);
// Remove widdershins header
content = content.replace(/^---[\s\S]*?---\n/, '');
// Add custom styling hints
content = content.replace(/^(#{1,3} .+)$/gm, '$1\n');
Write(mdFile, content);
}
Troubleshooting
Common Issues
"widdershins: command not found"
npm install -g widdershins
# Or use npx
npx widdershins openapi.json -o api.md
"Error parsing OpenAPI spec"
# Validate spec first
npx @redocly/cli lint openapi.json
# Fix common issues
npx @redocly/cli bundle openapi.json -o fixed.json
"Server not responding"
Ensure the development server is running and accessible:
# Check if server is running
curl http://localhost:3000/health
# Check OpenAPI endpoint
curl http://localhost:3000/api-docs-json
Manual Fallback
If automatic extraction fails, document APIs manually:
- List all route files:
Glob('**/routes/*.js') - Extract route definitions using regex
- Build documentation structure manually
async function manualApiExtraction(workDir) {
const routeFiles = Glob('src/routes/*.js').concat(Glob('src/routes/*.ts'));
const endpoints = [];
for (const file of routeFiles) {
const content = Read(file);
const routes = content.matchAll(/router\.(get|post|put|delete|patch)\(['"]([^'"]+)['"]/g);
for (const match of routes) {
endpoints.push({
method: match[1].toUpperCase(),
path: match[2],
file: file
});
}
}
return endpoints;
}