From cab8ff6656632abd074a96a324a4c3d74bb0125b Mon Sep 17 00:00:00 2001 From: Tyler Date: Fri, 19 Jun 2026 19:26:17 -0600 Subject: [PATCH] feat(backend): add GET /api/claims with filter/sort/pagination --- backend/src/cyclone/api.py | 33 +++++++++++++++++++++++++ backend/tests/test_api_gets.py | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/backend/src/cyclone/api.py b/backend/src/cyclone/api.py index 9bcd874..eeb5ccc 100644 --- a/backend/src/cyclone/api.py +++ b/backend/src/cyclone/api.py @@ -417,4 +417,37 @@ def get_batch(batch_id: str) -> Any: return json.loads(rec.result.model_dump_json()) +@app.get("/api/claims") +def list_claims( + batch_id: str | None = Query(None), + status: str | None = Query(None), + provider_npi: str | None = Query(None), + payer: str | None = Query(None), + date_from: str | None = Query(None), + date_to: str | None = Query(None), + sort: str | None = Query(None), + order: str = Query("desc"), + limit: int = Query(100, ge=1, le=1000), + offset: int = Query(0, ge=0), +) -> dict[str, Any]: + common = dict( + batch_id=batch_id, + status=status, + provider_npi=provider_npi, + payer=payer, + date_from=date_from, + date_to=date_to, + ) + items = list(store.iter_claims( + sort=sort, order=order, limit=limit, offset=offset, **common, + )) + total = len(list(store.iter_claims(**common))) + return { + "items": items, + "total": total, + "returned": len(items), + "has_more": total > offset + len(items), + } + + __all__ = ["app"] diff --git a/backend/tests/test_api_gets.py b/backend/tests/test_api_gets.py index 32a2a09..c18253c 100644 --- a/backend/tests/test_api_gets.py +++ b/backend/tests/test_api_gets.py @@ -81,3 +81,47 @@ def test_get_batch_by_id_returns_full_result(seeded_store): def test_get_batch_404_when_missing(client: TestClient): resp = client.get("/api/batches/does-not-exist") assert resp.status_code == 404 + + +# --------------------------------------------------------------------------- # +# /api/claims +# --------------------------------------------------------------------------- # + + +def test_claims_returns_empty_list_when_no_parses(client: TestClient): + resp = client.get("/api/claims") + assert resp.status_code == 200 + body = resp.json() + assert body["items"] == [] + assert body["total"] == 0 + + +def test_claims_filters_by_status_submitted(seeded_store): + resp = seeded_store.get("/api/claims?status=submitted") + assert resp.status_code == 200 + body = resp.json() + assert all(c["status"] == "submitted" for c in body["items"]) + assert body["total"] >= 1 + + +def test_claims_filters_by_batch_id(seeded_store): + bid = seeded_store.get("/api/batches").json()["items"][0]["id"] + resp = seeded_store.get(f"/api/claims?batch_id={bid}") + assert resp.status_code == 200 + body = resp.json() + assert all(c["batchId"] == bid for c in body["items"]) + + +def test_claims_sorts_by_billed_amount_desc(seeded_store): + resp = seeded_store.get("/api/claims?sort=billedAmount&order=desc") + assert resp.status_code == 200 + body = resp.json() + amounts = [c["billedAmount"] for c in body["items"]] + assert amounts == sorted(amounts, reverse=True) + + +def test_claims_pagination_limit_offset(seeded_store): + resp1 = seeded_store.get("/api/claims?limit=1&offset=0") + body1 = resp1.json() + assert body1["returned"] == 1 + assert body1["has_more"] is True