fleet-platform/app/routers/views.py

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])