This commit is contained in:
92
app/localapi-docs/src/cli.py
Normal file
92
app/localapi-docs/src/cli.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import click
|
||||
from pathlib import Path
|
||||
import json
|
||||
from src.core.parser import parse_openapi_spec, _basic_validate
|
||||
from src.utils.templates import generate_html, generate_markdown, generate_json, serve_docs
|
||||
from src.utils.search import create_search_index, search_index
|
||||
|
||||
|
||||
@click.group()
|
||||
def main():
|
||||
"""LocalAPI Docs - Privacy-First OpenAPI Documentation CLI"""
|
||||
pass
|
||||
|
||||
|
||||
@main.command("serve")
|
||||
@click.argument("spec_path", type=click.Path(exists=True))
|
||||
@click.option("--host", default="127.0.0.1", help="Host to bind the server to")
|
||||
@click.option("--port", default=8080, type=int, help="Port to bind the server to")
|
||||
def serve(spec_path: str, host: str, port: int):
|
||||
"""Serve interactive API documentation locally"""
|
||||
serve_docs(spec_path, host=host, port=port)
|
||||
|
||||
|
||||
@main.command("generate")
|
||||
@click.argument("spec_path", type=click.Path(exists=True))
|
||||
@click.option("--output", "-o", type=click.Path(), help="Output file path")
|
||||
@click.option("--format", "fmt", type=click.Choice(["html", "markdown", "json"]), default="html", help="Output format")
|
||||
@click.option("--template", type=click.Path(exists=True), help="Custom template file path")
|
||||
def generate(spec_path: str, output: str | None, fmt: str, template: str | None):
|
||||
"""Generate documentation in various formats"""
|
||||
if output is None:
|
||||
if fmt == "html":
|
||||
output = "docs.html"
|
||||
elif fmt == "markdown":
|
||||
output = "docs.md"
|
||||
else:
|
||||
output = "docs.json"
|
||||
try:
|
||||
if fmt == "html":
|
||||
generate_html(spec_path, output, template_path=template)
|
||||
elif fmt == "markdown":
|
||||
generate_markdown(spec_path, output, template_path=template)
|
||||
else:
|
||||
generate_json(spec_path, output, template_path=template)
|
||||
click.echo(f"Documentation generated: {output}")
|
||||
except Exception as e:
|
||||
click.echo(f"Error generating documentation: {e}", err=True)
|
||||
|
||||
|
||||
@main.command("validate")
|
||||
@click.argument("spec_path", type=click.Path(exists=True))
|
||||
def validate(spec_path: str):
|
||||
"""Validate an OpenAPI specification file"""
|
||||
try:
|
||||
spec = parse_openapi_spec(spec_path)
|
||||
click.echo(f"Valid OpenAPI spec: {spec.info['title']} v{spec.info['version']}")
|
||||
return True
|
||||
except ValueError as e:
|
||||
click.echo(f"Validation failed: {e}", err=True)
|
||||
return False
|
||||
|
||||
|
||||
@main.command("search")
|
||||
@click.argument("spec_path", type=click.Path(exists=True))
|
||||
@click.argument("query", nargs=-1)
|
||||
def search(spec_path: str, query: tuple):
|
||||
"""Search for endpoints in an OpenAPI specification"""
|
||||
query_str = " ".join(query)
|
||||
if not query_str:
|
||||
click.echo("Please provide a search query")
|
||||
return
|
||||
try:
|
||||
try:
|
||||
spec = parse_openapi_spec(spec_path)
|
||||
spec_dict = spec.model_dump()
|
||||
except Exception:
|
||||
content = Path(spec_path).read_text()
|
||||
if spec_path.endswith(('.yaml', '.yml')):
|
||||
import yaml
|
||||
spec_dict = yaml.safe_load(content)
|
||||
else:
|
||||
spec_dict = json.loads(content)
|
||||
index = create_search_index(spec_dict)
|
||||
results = search_index(index, query_str)
|
||||
if results:
|
||||
click.echo(f"Found {len(results)} results for '{query_str}':")
|
||||
for r in results:
|
||||
click.echo(f" [{r.method}] {r.path} - {r.summary or ''}")
|
||||
else:
|
||||
click.echo(f"No results found for '{query_str}'")
|
||||
except Exception as e:
|
||||
click.echo(f"Search failed: {e}", err=True)
|
||||
Reference in New Issue
Block a user