- Implement tests for `isOrgAdminProvider` to validate admin roles based on organization membership and role. - Create tests for `saved_views_provider` to ensure correct API interactions for saving, renaming, and deleting saved views. - Enhance ticket tree tests to accurately reflect inactive and merged ticket handling. - Update CSV import tests to verify error handling and file saving functionality. - Introduce tests for deal cards to ensure correct display of next steps and task statuses.
96 lines
3.2 KiB
Python
96 lines
3.2 KiB
Python
"""Render every route in the OpenAPI schema to docs/api/endpoint-index.md.
|
|
|
|
The curated API pages are written by hand. This index is not: a hand-maintained
|
|
list of 350 operations would be wrong within a week. Re-run this script when
|
|
routes change:
|
|
|
|
cd backend && uv run python scripts/generate_endpoint_index.py
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import django
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "crm.settings")
|
|
os.environ.setdefault("SECRET_KEY", "generate-endpoint-index-only")
|
|
os.environ.setdefault("ADMIN_EMAIL", "admin@localhost")
|
|
django.setup()
|
|
|
|
from drf_spectacular.generators import SchemaGenerator # noqa: E402
|
|
|
|
OUTPUT = (
|
|
Path(__file__).resolve().parent.parent.parent / "docs" / "api" / "endpoint-index.md"
|
|
)
|
|
METHODS = ("get", "post", "put", "patch", "delete")
|
|
|
|
HEADER = """# Endpoint index
|
|
|
|
Every route the API exposes, generated from the OpenAPI schema. The curated
|
|
pages in this section explain the endpoints you are most likely to need; this
|
|
table exists so nothing is invisible.
|
|
|
|
Interactive versions of the same schema are served by a running backend at
|
|
`/swagger-ui/` and `/api/schema/redoc/`, with the raw document at `/schema/`.
|
|
|
|
!!! note
|
|
This page is generated by `backend/scripts/generate_endpoint_index.py`.
|
|
Edit that script, not this file.
|
|
|
|
| Path | Methods |
|
|
| --- | --- |
|
|
"""
|
|
|
|
|
|
# What to use instead of a deprecated operation. Whether an operation is
|
|
# deprecated is read from the schema (`deprecated=True` in the view's
|
|
# `extend_schema`), so a note here only ever decorates what the code already
|
|
# says; `render` refuses a note on an operation the code no longer deprecates,
|
|
# rather than printing advice about a route that has moved on.
|
|
REPLACEMENTS = {
|
|
("/api/leads/upload/", "post"): "use `import/preview/` + `import/commit/`",
|
|
(
|
|
"/api/leads/create-from-site/",
|
|
"post",
|
|
): "use `/api/public/forms/{org_id}/{form_id}/submit/`",
|
|
}
|
|
|
|
|
|
def render(schema) -> list[str]:
|
|
"""One table row per path, each method marked when it is deprecated."""
|
|
deprecated = {
|
|
(path, method)
|
|
for path, operations in schema["paths"].items()
|
|
for method, operation in operations.items()
|
|
if method in METHODS and operation.get("deprecated")
|
|
}
|
|
stale = sorted(set(REPLACEMENTS) - deprecated)
|
|
if stale:
|
|
raise SystemExit(
|
|
"REPLACEMENTS names operations the schema does not mark deprecated: "
|
|
+ ", ".join(f"{m.upper()} {p}" for p, m in stale)
|
|
)
|
|
rows = []
|
|
for path in sorted(schema["paths"]):
|
|
labels = []
|
|
for method in sorted(m for m in schema["paths"][path] if m in METHODS):
|
|
label = method.upper()
|
|
if (path, method) in deprecated:
|
|
note = REPLACEMENTS.get((path, method))
|
|
label += f" (deprecated; {note})" if note else " (deprecated)"
|
|
labels.append(label)
|
|
if labels:
|
|
rows.append(f"| `{path}` | {', '.join(labels)} |")
|
|
return rows
|
|
|
|
|
|
def main() -> None:
|
|
rows = render(SchemaGenerator().get_schema(request=None, public=True))
|
|
OUTPUT.write_text(HEADER + "\n".join(rows) + "\n")
|
|
print(f"wrote {OUTPUT}, {len(rows)} paths")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|