44 lines
1.6 KiB
Python
44 lines
1.6 KiB
Python
import json
|
|
from typing import Annotated, Any
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
|
from psycopg.types.json import Jsonb
|
|
|
|
from app.auth import AuthAccount, require_scope
|
|
from app.db import get_pool
|
|
from app.models.views import LiveViewResponse
|
|
from app.rate_limit import limiter
|
|
|
|
router = APIRouter(prefix="/api/views", tags=["views"])
|
|
|
|
_FILTERS_DESC = "JSON object: cost_centre, assigned_city, vehicle_numbers[]"
|
|
|
|
|
|
def _parse_filters(filters_q: str | None) -> dict[str, Any]:
|
|
if not filters_q:
|
|
return {}
|
|
try:
|
|
parsed = json.loads(filters_q)
|
|
except json.JSONDecodeError as exc:
|
|
raise HTTPException(status_code=400, detail=f"invalid filters json: {exc}") from exc
|
|
if not isinstance(parsed, dict):
|
|
raise HTTPException(status_code=400, detail="filters must be a JSON object")
|
|
return parsed
|
|
|
|
|
|
@router.get("/live", response_model=LiveViewResponse)
|
|
@limiter.limit("60/minute")
|
|
async def live_view(
|
|
request: Request,
|
|
_account: Annotated[AuthAccount, Depends(require_scope("read:fleet"))],
|
|
filters: Annotated[str | None, Query(description=_FILTERS_DESC)] = None,
|
|
) -> LiveViewResponse:
|
|
_ = request
|
|
filters_dict = _parse_filters(filters)
|
|
pool = await get_pool()
|
|
async with pool.connection() as conn, conn.cursor() as cur:
|
|
await cur.execute("SELECT serve.fn_live_view(%s)", (Jsonb(filters_dict),))
|
|
row = await cur.fetchone()
|
|
if row is None or row[0] is None:
|
|
raise HTTPException(status_code=500, detail="serve.fn_live_view returned NULL")
|
|
return LiveViewResponse.model_validate(row[0])
|