# retoor from __future__ import annotations from fastapi import HTTPException, Request from fastapi.responses import JSONResponse from devplacepy.services.dbapi import policy from devplacepy.services.dbapi.policy import Caller, DbApiBadTable, DbApiDenied OPERATORS = {"gte": ">=", "lte": "<=", "gt": ">", "lt": "<"} RESERVED = {"limit", "before", "search", "include_deleted", "key", "hard", "execute"} def require_dbapi_caller(request: Request) -> Caller: try: return policy.require_caller(request) except DbApiDenied as exc: from devplacepy.services.audit import record as audit audit.record( request, "database.access.denied", result="denied", summary=f"denied database API access to {request.url.path}", ) raise HTTPException(status_code=403, detail=str(exc)) def assert_table(name: str): try: return policy.assert_table(name) except DbApiBadTable as exc: raise HTTPException(status_code=404, detail=str(exc)) def error(status: int, message: str, **extra) -> JSONResponse: return JSONResponse( {"error": {"status": status, "message": message, **extra}}, status_code=status ) async def read_body(request: Request) -> dict: content_type = request.headers.get("content-type", "") if content_type.startswith("application/json"): try: data = await request.json() except Exception: return {} return data if isinstance(data, dict) else {} form = await request.form() return {key: value for key, value in form.items()} def row_payload(body: dict) -> dict: if "values_json" in body: import json try: parsed = json.loads(body["values_json"]) except Exception: return {} return parsed if isinstance(parsed, dict) else {} return {key: value for key, value in body.items() if key not in ("confirm", "values_json")} def parse_filters(request: Request) -> tuple[dict, dict]: filters: dict = {} comparisons: dict = {} for key, value in request.query_params.multi_items(): if key in RESERVED: continue if "." in key: prefix, column = key.split(".", 1) if prefix in ("filter", "eq"): filters[column] = value elif prefix in OPERATORS: comparisons[column] = {OPERATORS[prefix]: value} return filters, comparisons