plan(SP22): pre-flight fix — add _extract_items helper for paginated API responses
This commit is contained in:
@@ -751,6 +751,28 @@ BACKOFF_S = (1.0, 2.0, 4.0)
|
|||||||
|
|
||||||
# --- Response models ---------------------------------------------------------
|
# --- Response models ---------------------------------------------------------
|
||||||
|
|
||||||
|
class PaginatedResponse(BaseModel):
|
||||||
|
"""Cyclone's list endpoints return a paginated wrapper:
|
||||||
|
`{"items": [...], "total": N, "returned": N, "has_more": bool}`.
|
||||||
|
Some endpoints (e.g., /api/ta1-acks) omit `returned` and `has_more`."""
|
||||||
|
items: list[dict[str, Any]] = Field(default_factory=list)
|
||||||
|
total: int = 0
|
||||||
|
returned: int | None = None
|
||||||
|
has_more: bool | None = None
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_items(raw: dict[str, Any] | list[Any]) -> list[dict[str, Any]]:
|
||||||
|
"""Unwrap a paginated response into a plain list. Tolerates both
|
||||||
|
the paginated wrapper and a bare list (so tests can use either)."""
|
||||||
|
if isinstance(raw, list):
|
||||||
|
return raw
|
||||||
|
if isinstance(raw, dict) and "items" in raw:
|
||||||
|
return list(raw["items"])
|
||||||
|
raise ValueError(
|
||||||
|
f"Unexpected API response shape; expected list or paginated "
|
||||||
|
f"wrapper with 'items'. Got: {type(raw).__name__}"
|
||||||
|
)
|
||||||
|
|
||||||
class HealthSnapshot(BaseModel):
|
class HealthSnapshot(BaseModel):
|
||||||
status: str
|
status: str
|
||||||
version: str
|
version: str
|
||||||
@@ -958,7 +980,10 @@ Add the methods to the `CycloneClient` class (below `scheduler_status`):
|
|||||||
"/api/claims", params=params
|
"/api/claims", params=params
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return [ClaimSummary.model_validate(c) for c in resp.json()]
|
return [
|
||||||
|
ClaimSummary.model_validate(c)
|
||||||
|
for c in _extract_items(resp.json())
|
||||||
|
]
|
||||||
except (httpx.HTTPStatusError, httpx.RequestError) as e:
|
except (httpx.HTTPStatusError, httpx.RequestError) as e:
|
||||||
last_exc = e
|
last_exc = e
|
||||||
if attempt < MAX_RETRIES - 1:
|
if attempt < MAX_RETRIES - 1:
|
||||||
@@ -1163,7 +1188,10 @@ Add the methods to the `CycloneClient` class:
|
|||||||
params={"since": since},
|
params={"since": since},
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return [ProcessedFile.model_validate(f) for f in resp.json()]
|
return [
|
||||||
|
ProcessedFile.model_validate(f)
|
||||||
|
for f in _extract_items(resp.json())
|
||||||
|
]
|
||||||
except (httpx.HTTPStatusError, httpx.RequestError) as e:
|
except (httpx.HTTPStatusError, httpx.RequestError) as e:
|
||||||
last_exc = e
|
last_exc = e
|
||||||
if attempt < MAX_RETRIES - 1:
|
if attempt < MAX_RETRIES - 1:
|
||||||
@@ -1295,7 +1323,10 @@ Add the methods to the `CycloneClient` class:
|
|||||||
"/api/ta1-acks", params={"since": since}
|
"/api/ta1-acks", params={"since": since}
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return [Ta1Ack.model_validate(a) for a in resp.json()]
|
return [
|
||||||
|
Ta1Ack.model_validate(a)
|
||||||
|
for a in _extract_items(resp.json())
|
||||||
|
]
|
||||||
|
|
||||||
async def list_acks(self, since: str) -> list[Ack999]:
|
async def list_acks(self, since: str) -> list[Ack999]:
|
||||||
assert self._http is not None
|
assert self._http is not None
|
||||||
@@ -1303,7 +1334,10 @@ Add the methods to the `CycloneClient` class:
|
|||||||
"/api/acks", params={"since": since}
|
"/api/acks", params={"since": since}
|
||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return [Ack999.model_validate(a) for a in resp.json()]
|
return [
|
||||||
|
Ack999.model_validate(a)
|
||||||
|
for a in _extract_items(resp.json())
|
||||||
|
]
|
||||||
|
|
||||||
async def list_remittances(
|
async def list_remittances(
|
||||||
self, since: str
|
self, since: str
|
||||||
@@ -1314,7 +1348,8 @@ Add the methods to the `CycloneClient` class:
|
|||||||
)
|
)
|
||||||
resp.raise_for_status()
|
resp.raise_for_status()
|
||||||
return [
|
return [
|
||||||
RemittanceSummary.model_validate(r) for r in resp.json()
|
RemittanceSummary.model_validate(r)
|
||||||
|
for r in _extract_items(resp.json())
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user