""" # Summary Utilities for building API documentation from an OpenAPI specification. """ import json from dataclasses import dataclass from pathlib import Path import click SWAGGER_SPEC_FILENAME = "openapi.json" @dataclass class OpenAPIMetadata: """ Metadata derived from the ``info`` block of an OpenAPI specification. Attributes: site_name: Spec title, used as the MkDocs site name. site_description: Spec description, used as the site description. site_author: Contact name (fallback: contact email), used as the site author. """ site_name: str site_description: str | None site_author: str | None def load_openapi_spec(spec_path: Path) -> dict: """ Load and validate an OpenAPI specification from a JSON file. Args: spec_path (Path): Path to the OpenAPI JSON specification file. Returns: dict: The parsed OpenAPI specification. Raises: click.ClickException: If the file cannot be read or the ``info`` block is invalid. """ if not spec_path.exists(): raise click.ClickException(f"OpenAPI spec not found: {spec_path}") try: data = json.loads(spec_path.read_text(encoding="utf-8")) except (json.JSONDecodeError, OSError) as exc: raise click.ClickException(f"Unable to read OpenAPI spec: {exc}") from exc if not isinstance(data, dict): raise click.ClickException("OpenAPI spec must be a JSON mapping") info = data.get("info") if not isinstance(info, dict) or not isinstance(info.get("title"), str): raise click.ClickException("OpenAPI spec missing 'info.title'") return data def derive_metadata(spec: dict) -> OpenAPIMetadata: """ Derive MkDocs site metadata from an OpenAPI spec ``info`` block. Args: spec (dict): Parsed OpenAPI specification. Returns: OpenAPIMetadata: Site name, description, and author derived from the spec. """ info = spec["info"] contact = info.get("contact") if isinstance(contact, dict): author = contact.get("name") or contact.get("email") else: author = None return OpenAPIMetadata( site_name=info.get("title", ""), site_description=info.get("description"), site_author=author, ) def generate_api_sources(spec: dict, docs_dir: Path) -> None: """ Generate swagger-enabled Markdown sources and the spec copy. The specification is written as ``openapi.json`` inside ``docs_dir`` and an ``index.md`` embedding the swagger UI is generated alongside it. Args: spec (dict): Parsed OpenAPI specification. docs_dir (Path): Directory (for example ``docs/api``) where the swagger sources are written. """ docs_dir.mkdir(parents=True, exist_ok=True) spec_path = docs_dir / SWAGGER_SPEC_FILENAME spec_json = json.dumps(spec, indent=2) if not spec_path.exists() or spec_path.read_text(encoding="utf-8") != spec_json: spec_path.write_text(spec_json, encoding="utf-8") index_path = docs_dir / "index.md" content = "# API Reference\n\n" f'\n' if not index_path.exists() or index_path.read_text(encoding="utf-8") != content: index_path.write_text(content, encoding="utf-8")