Files
ad/molodetz/docs_export.py
T
retoor e512556703 Auto-generate docs downloads from the API group registry
Shared endpoint enricher builds curl, request, and response samples for
every registry endpoint so download.md/html stay complete without a
hand-maintained duplicate. Group pages reuse the same curl builder.
2026-10-05 16:00:14 +02:00

223 lines
8.2 KiB
Python

# retoor <retoor@molodetz.nl>
import json
from html import escape
from molodetz.docs_api import ordered_groups, substitute
from molodetz.docs_prose import page_source, render_page, visible_pages
BASE_PLACEHOLDER = "https://example.com"
USERNAME_PLACEHOLDER = "retoor"
API_KEY_PLACEHOLDER = "YOUR_API_KEY"
def _filled(value):
if value is None:
return None
if isinstance(value, str):
return substitute(value, BASE_PLACEHOLDER, USERNAME_PLACEHOLDER, API_KEY_PLACEHOLDER)
if isinstance(value, dict):
return {key: _filled(item) for key, item in value.items()}
if isinstance(value, list):
return [_filled(item) for item in value]
return value
def request_sample(item):
if item.get("probe_body") is not None:
return _filled(item["probe_body"])
body = {}
query = {}
path = {}
for field in item.get("fields") or []:
example = field.get("example")
if example is None and field.get("enum"):
example = field["enum"][0]
if example is None:
continue
example = _filled(example)
location = field.get("location") or "body"
if location == "query":
query[field["name"]] = example
elif location == "path":
path[field["name"]] = example
else:
body[field["name"]] = example
sample = {}
if path:
sample["path"] = path
if query:
sample["query"] = query
if body:
sample["body"] = body
return sample or None
def response_sample(item):
return _filled(item.get("sample"))
def curl_example(item, base=BASE_PLACEHOLDER, username=USERNAME_PLACEHOLDER, api_key=API_KEY_PLACEHOLDER):
path = item["path"]
for field in item.get("fields") or []:
if field.get("location") == "path" and field.get("example") is not None:
path = path.replace("{" + field["name"] + "}", str(_filled(field["example"])))
headers = ["-H 'Accept: application/json'"]
if item.get("auth") != "public":
headers.append("-H 'X-API-KEY: {{api_key}}'")
parts = ["curl", "-X", item["method"], *headers]
body = request_sample(item)
payload = None
if isinstance(body, dict) and "body" in body:
payload = body["body"]
elif isinstance(body, dict) and body and "path" not in body and "query" not in body:
payload = body
elif item.get("probe_body") is not None:
payload = _filled(item["probe_body"])
if payload is not None and item["method"] not in ("GET", "HEAD"):
parts.append("-H 'Content-Type: application/json'")
parts.append(f"-d '{json.dumps(payload, ensure_ascii=False)}'")
query = body.get("query") if isinstance(body, dict) else None
url = f"{{{{base}}}}{path}"
if query:
from urllib.parse import urlencode
url = f"{url}?{urlencode(query, doseq=True)}"
parts.append(f"'{url}'")
return substitute(" ".join(parts), base, username, api_key)
def enrich_endpoint(item, base=BASE_PLACEHOLDER, username=USERNAME_PLACEHOLDER, api_key=API_KEY_PLACEHOLDER):
return {
**item,
"curl": curl_example(item, base, username, api_key),
"request_sample": request_sample(item),
"response_sample": response_sample(item),
}
def visible_api_groups(viewer_is_admin):
return [group for group in ordered_groups() if viewer_is_admin or group["slug"] != "admin"]
def _json_block(value):
if value is None:
return "null"
return json.dumps(value, indent=2, ensure_ascii=False, sort_keys=True)
def format_endpoint_markdown(item):
enriched = enrich_endpoint(item)
lines = [
f"### `{enriched['method']} {enriched['path']}`",
"",
f"- **Title:** {enriched['title']}",
f"- **Auth:** {enriched['auth']} ({enriched['min_role']})",
f"- **Negotiation:** {enriched['negotiation']}",
]
if enriched.get("description"):
lines += ["", enriched["description"]]
if enriched.get("fields"):
lines += ["", "#### Fields", ""]
for field in enriched["fields"]:
required = "required" if field.get("required") else "optional"
enum = f" allowed={field['enum']}" if field.get("enum") else ""
example = f" example={field['example']!r}" if field.get("example") is not None else ""
lines.append(
f"- `{field['name']}` ({field.get('type', 'string')}, {field.get('location', 'body')}, {required}){enum}{example}"
+ (f" - {field['description']}" if field.get("description") else "")
)
lines += ["", "#### Request sample", "", "```bash", enriched["curl"], "```"]
if enriched["request_sample"] is not None:
lines += ["", "```json", _json_block(enriched["request_sample"]), "```"]
lines += ["", "#### Response sample", "", "```json", _json_block(enriched["response_sample"]), "```", ""]
return "\n".join(lines)
def format_endpoint_html(item):
enriched = enrich_endpoint(item)
fields_rows = ""
if enriched.get("fields"):
rows = []
for field in enriched["fields"]:
rows.append(
"<tr>"
f"<td><code>{escape(field['name'])}</code></td>"
f"<td>{escape(str(field.get('type', 'string')))}</td>"
f"<td>{escape(str(field.get('location', 'body')))}</td>"
f"<td>{'yes' if field.get('required') else 'no'}</td>"
f"<td>{escape(str(field.get('example', '')))}</td>"
"</tr>"
)
fields_rows = (
"<h4>Fields</h4><table><thead><tr><th>Name</th><th>Type</th><th>Location</th>"
"<th>Required</th><th>Example</th></tr></thead><tbody>"
+ "".join(rows)
+ "</tbody></table>"
)
request_json = (
f"<h4>Request JSON</h4><pre>{escape(_json_block(enriched['request_sample']))}</pre>"
if enriched["request_sample"] is not None
else ""
)
return (
f"<section>"
f"<h3><code>{escape(enriched['method'])} {escape(enriched['path'])}</code></h3>"
f"<p><strong>{escape(enriched['title'])}</strong> · auth {escape(enriched['auth'])} "
f"({escape(enriched['min_role'])}) · {escape(enriched['negotiation'])}</p>"
+ (f"<p>{escape(enriched['description'])}</p>" if enriched.get("description") else "")
+ fields_rows
+ f"<h4>Request sample</h4><pre>{escape(enriched['curl'])}</pre>"
+ request_json
+ f"<h4>Response sample</h4><pre>{escape(_json_block(enriched['response_sample']))}</pre>"
+ "</section>"
)
def format_group_markdown(group):
parts = [f"## API: {group['title']}", "", group.get("description") or "", ""]
for item in group["endpoints"]:
parts.append(format_endpoint_markdown(item))
return "\n".join(parts).strip() + "\n"
def format_group_html(group):
body = "".join(format_endpoint_html(item) for item in group["endpoints"])
return (
f"<section><h2>API: {escape(group['title'])}</h2>"
f"<p>{escape(group.get('description') or '')}</p>{body}</section>"
)
def export_markdown(viewer_is_admin):
parts = [
"# Molodetz documentation",
"",
"Auto-generated from the live docs registry. Give this file to an agent to learn the API.",
"",
]
for page in visible_pages(viewer_is_admin):
parts += [page_source(page["slug"]).strip(), ""]
parts.append("# API reference")
parts.append("")
for group in visible_api_groups(viewer_is_admin):
parts.append(format_group_markdown(group))
parts.append("")
return "\n".join(parts).rstrip() + "\n"
def export_html(viewer_is_admin):
body = [
"<h1>Molodetz documentation</h1>",
"<p>Auto-generated from the live docs registry. Give this file to an agent to learn the API.</p>",
]
for page in visible_pages(viewer_is_admin):
body.append(f"<section>{render_page(page['slug'])}</section>")
body.append("<h1>API reference</h1>")
for group in visible_api_groups(viewer_is_admin):
body.append(format_group_html(group))
return (
"<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\">"
"<title>Molodetz documentation</title></head>"
f"<body>{''.join(body)}</body></html>"
)