| """Standard API response envelope. |
| |
| All new endpoints should return via these helpers to produce a consistent |
| ``{success, data, meta}`` shape that the frontend can depend on. |
| |
| Existing endpoints are NOT required to migrate — this is additive only. |
| |
| Usage:: |
| |
| from app.core.response import ok, created, paginated |
| |
| @router.get("/sync/progress") |
| def get_progress(...): |
| return ok({"events": []}) |
| |
| @router.post("/sync/progress") |
| def post_progress(...): |
| return created({"synced": True}, message="Progress recorded.") |
| """ |
| from __future__ import annotations |
|
|
| from datetime import datetime, timezone |
| from typing import Any |
|
|
|
|
| def _now_iso() -> str: |
| return datetime.now(timezone.utc).isoformat() |
|
|
|
|
| def ok( |
| data: Any, |
| *, |
| meta: dict[str, Any] | None = None, |
| message: str | None = None, |
| ) -> dict[str, Any]: |
| """Return a 200 OK envelope. |
| |
| Shape:: |
| |
| {"success": true, "data": <data>, "meta": {"timestamp": "...", ...}} |
| """ |
| m: dict[str, Any] = {"timestamp": _now_iso()} |
| if meta: |
| m.update(meta) |
| if message: |
| m["message"] = message |
| return {"success": True, "data": data, "meta": m} |
|
|
|
|
| def created( |
| data: Any, |
| *, |
| meta: dict[str, Any] | None = None, |
| message: str | None = None, |
| ) -> dict[str, Any]: |
| """Return a 201 Created envelope (same shape as ``ok``).""" |
| return ok(data, meta=meta, message=message) |
|
|
|
|
| def paginated( |
| data: list[Any], |
| *, |
| total: int, |
| page: int = 1, |
| per_page: int = 20, |
| extra_meta: dict[str, Any] | None = None, |
| ) -> dict[str, Any]: |
| """Return a paginated 200 OK envelope. |
| |
| Shape:: |
| |
| { |
| "success": true, |
| "data": [...], |
| "meta": { |
| "timestamp": "...", |
| "total": 42, |
| "page": 1, |
| "per_page": 20, |
| "pages": 3 |
| } |
| } |
| """ |
| pages = max(1, (total + per_page - 1) // per_page) |
| meta: dict[str, Any] = { |
| "timestamp": _now_iso(), |
| "total": total, |
| "page": page, |
| "per_page": per_page, |
| "pages": pages, |
| } |
| if extra_meta: |
| meta.update(extra_meta) |
| return {"success": True, "data": data, "meta": meta} |
|
|