teecup/app/errors.py

48 lines
2.1 KiB
Python

"""
Felles feilhåndtering: en maskinlesbar `code` på ALLE HTTP-feil, pluss
oversettelse av Postgres-constraint-brudd -> rene HTTP-feil.
Responsform på tvers av HELE API-et: {"detail": {"code": "...", "message": "..."}}.
Ingen egen exception-klasse, ingen global handler -- FastAPI støtter allerede
at `detail` er et JSON-serialiserbart objekt, ikke bare en streng, så
`app_error()` er bare en liten factory brukt konsekvent på alle ~39 steder
som kaster HTTPException i dette API-et.
Kodene er gjenbrukt der samme forretningsbetydning gjentar seg (f.eks.
NOT_FOUND på tvers av mange ressurstyper) -- IKKE én unik kode per kastested,
det ville vært for finkornet til at en frontend kan bygge stabil logikk på
det. Se plan-loggen for hele taksonomien.
Postgres-constraint-mønsteret (unik nøkkel, fremmednøkkel, CHECK) gjentar seg
på tvers av routerne (lag-navn, roster, sesjons-rekkefølge, lås). Meldingene
er generiske og lekker aldri rå Postgres-tekst (constraint-/tabellnavn) til
klienten.
"""
from contextlib import asynccontextmanager
import asyncpg
from fastapi import HTTPException
def app_error(status_code: int, code: str, message: str) -> HTTPException:
return HTTPException(status_code=status_code, detail={"code": code, "message": message})
# async (ikke bare @contextmanager) slik at den kan stå i samme
# "async with org_connection(...) as conn, translate_db_errors():"-setning som
# org_connection — Python krever at ALLE ledd i én async with er async
# context managers. Kroppen selv gjør ingenting async, den bare venter på at
# koden inni with-blokken (som awaiter) kaster.
@asynccontextmanager
async def translate_db_errors():
try:
yield
except asyncpg.UniqueViolationError as e:
raise app_error(
409, "DUPLICATE", "Raden finnes allerede (dupliserer noe som skal være unikt)."
) from e
except asyncpg.ForeignKeyViolationError as e:
raise app_error(400, "INVALID_REFERENCE", "Refererer til noe som ikke finnes.") from e
except asyncpg.CheckViolationError as e:
raise app_error(400, "VALIDATION_FAILED", "Verdien bryter en regel for feltet.") from e