Coverage for src/lumen/api/root.py: 97%
30 statements
« prev ^ index » next coverage.py v7.16.0, created at 2026-08-31 18:46 +0000
« prev ^ index » next coverage.py v7.16.0, created at 2026-08-31 18:46 +0000
1"""
2This router handles requests to the root URL and the base FHIR endpoint.
3It serves metadata info and acts as the entry point for batch/transaction bundles.
4"""
6from __future__ import annotations
8import json
9from typing import Any, cast
11from fastapi import APIRouter, Request
12from fastapi.responses import JSONResponse, Response
14from ..core.exceptions import FHIR_JSON, OperationOutcomeError
15from ..db.pool import get_pool
16from .bundle import process_bundle
17from .deps import TenantDep, TenantSettingsDep
19router = APIRouter()
22@router.get("/")
23async def root_get(request: Request) -> Response:
24 base = str(request.base_url).rstrip("/")
25 return JSONResponse(
26 {
27 "name": "Lumen FHIR Server",
28 "fhirVersion": "4.3.0",
29 "docsUrl": f"{base}/docs",
30 "metadataUrl": f"{base}/metadata",
31 }
32 )
35@router.post("/")
36async def root_post(
37 request: Request,
38 tenant: TenantDep,
39 tenant_settings: TenantSettingsDep,
40) -> Response:
41 body_bytes = await request.body()
42 try:
43 parsed = json.loads(body_bytes)
44 except json.JSONDecodeError as exc:
45 raise OperationOutcomeError(400, "invalid", f"Invalid JSON: {exc}") from exc
47 if not isinstance(parsed, dict):
48 raise OperationOutcomeError(400, "invalid", "Body must be a JSON object")
50 bundle = cast(dict[str, Any], parsed)
51 if bundle.get("resourceType") != "Bundle":
52 raise OperationOutcomeError(400, "invalid", "resourceType must be 'Bundle' for POST /")
54 pool = get_pool()
55 base_url = str(request.base_url).rstrip("/")
56 result = await process_bundle(bundle, pool, tenant, tenant_settings, base_url)
57 return Response(content=json.dumps(result), media_type=FHIR_JSON)