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 ---------------------------------------------------------
|
||||
|
||||
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):
|
||||
status: str
|
||||
version: str
|
||||
@@ -958,7 +980,10 @@ Add the methods to the `CycloneClient` class (below `scheduler_status`):
|
||||
"/api/claims", params=params
|
||||
)
|
||||
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:
|
||||
last_exc = e
|
||||
if attempt < MAX_RETRIES - 1:
|
||||
@@ -1163,7 +1188,10 @@ Add the methods to the `CycloneClient` class:
|
||||
params={"since": since},
|
||||
)
|
||||
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:
|
||||
last_exc = e
|
||||
if attempt < MAX_RETRIES - 1:
|
||||
@@ -1295,7 +1323,10 @@ Add the methods to the `CycloneClient` class:
|
||||
"/api/ta1-acks", params={"since": since}
|
||||
)
|
||||
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]:
|
||||
assert self._http is not None
|
||||
@@ -1303,7 +1334,10 @@ Add the methods to the `CycloneClient` class:
|
||||
"/api/acks", params={"since": since}
|
||||
)
|
||||
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(
|
||||
self, since: str
|
||||
@@ -1314,7 +1348,8 @@ Add the methods to the `CycloneClient` class:
|
||||
)
|
||||
resp.raise_for_status()
|
||||
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