Switch transport to aiohttp; add session re-auth and order/product endpoints - #4
Conversation
Switch the transport layer from httpx to aiohttp so the package fits the Home Assistant ecosystem, which mandates aiohttp and a shared session per config entry. - HttpClient is now built on aiohttp.ClientSession and accepts an optional injected session; injected sessions are never closed by the client. - RohlikAPI gains a `session=` parameter and a `session` property (replacing the httpx-specific `client` property) to forward an external session. - A small buffered Response wrapper keeps the service layer synchronous (`.json()` / `.raise_for_status()`), decoupling services from aiohttp's streaming semantics. - Query params are coerced to aiohttp-acceptable strings (bool/None/nested). - Connection/timeout failures are caught via a shared HTTP_ERRORS tuple (aiohttp.ClientError plus TimeoutError). - Update dependencies, README, and tests; add coverage for session injection. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmotTwydT558t4JHd5Cnwm
Upstream improvements proven out in the HA-RohlikCZ integration: - Transparent re-authentication: HttpClient now retries a request once after invoking an on_unauthorized callback when it receives HTTP 401, so a long-lived client recovers from an expired session instead of failing every call. RohlikAPI wires this to AuthManager.relogin (login/logout requests are exempt to avoid recursion; concurrent 401s are serialized). - New endpoints: orders.get_detail(id), orders.get_all_delivered() (paginates the delivered-orders endpoint), products.get_detail(id), and products.get_categories(id). - 404 -> None convention for optional-resource fetches (order/product detail, product categories), so a discontinued product reads as "not found" rather than an error. Add tests for the re-auth retry path, relogin, the new endpoints, and the 404 handling. Update README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmotTwydT558t4JHd5Cnwm
|
@claude please review this PR. Focus areas:
Generated by Claude Code |
|
Claude finished @dvejsada's task in 3m 41s —— View job PR Review: Switch transport to aiohttp; add session re-auth and order/product endpointsTasks:
The overall structure is clean and well thought out. A few issues, one of which could cause a real problem in HA. 1.
|
- HttpClient._request now buffers the body inside `async with session.request(...)` so the connection is released even if read() raises mid-response. - Concurrent 401s now re-authenticate once: a generation counter lets coroutines queued behind the in-flight re-auth skip a redundant login. - session property only (re)creates owned sessions; a closed injected session is returned as-is so the next request raises "Session is closed" instead of silently spawning a session that bypasses the owner's connector/SSL. - orders.get_all_delivered distinguishes a failed page (None -> warn, stop, possibly-incomplete result) from an empty page (done), and documents it. Add tests for single re-auth under concurrent 401s and for a closed injected session not being replaced. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmotTwydT558t4JHd5Cnwm
|
Thanks for the review — all four substantive issues are addressed in
On the minor point (Response retaining the full 135 tests pass; ruff, black, and mypy (strict) all green on 3.13. Generated by Claude Code |
|
@claude please re-review the latest changes (commit 7fc93b1), which address the first review:
Also flag anything new the changes may have introduced. Generated by Claude Code |
|
Claude finished @dvejsada's task in 3m 6s —— View job Re-review: commit 7fc93b1Tasks:
All four issues from the first review are correctly addressed. Detailed analysis of each fix follows. 1.
|
Make the API self-describing for callers (and LLMs) reading only the code: - models.py: add an Attributes section to every dataclass documenting each field's meaning and units (energy in kJ/kcal, prices in CZK, formatted vs numeric price fields, IDs and how they feed back into other calls). Fix RecipeDetail.duration type from int to str — the API returns a display string like "Do hodinky", not a number. - client.py: fix the RohlikAPI example to use the typed Cart model (cart.total_price / cart.products) instead of stale dict access. Address the two optional notes from the re-review: - http_client.session: replace the assert with an explicit raise so the "session present" invariant survives `python -O`. - _request: document that if the re-auth callback raises, the error propagates and the request is not retried. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PmotTwydT558t4JHd5Cnwm
|
@claude final review please. Since the last approving review, commit Generated by Claude Code |
|
Claude finished @dvejsada's task in 1m 48s —— View job Final review: commit
|
Replaces the httpx transport with aiohttp so the package fits the Home Assistant ecosystem, and upstreams a few robustness improvements proven out in the
HA-RohlikCZintegration.Transport: httpx → aiohttp
HttpClientis now built onaiohttp.ClientSession. HTTP/2 is dropped (aiohttp doesn't need it; HA mandates aiohttp).RohlikAPI(..., session=...)accepts an externally managedaiohttp.ClientSession(e.g. HA's shared session viaasync_get_clientsession). An injected session is never closed by the client. The httpx-specificclientproperty becomessession.Responsewrapper keeps the service layer synchronous (.json()/.raise_for_status()), decoupling services from aiohttp's streaming semantics."true"/"false",Nonedropped, nested dicts/lists JSON-encoded) — matching what the integration already did inline for product search.HTTP_ERRORStuple (aiohttp.ClientErrorplusTimeoutError, since aiohttp raisesasyncio.TimeoutErroron timeout, which is not aClientError).Transparent re-authentication on HTTP 401
HttpClientinvokes anon_unauthorizedcallback and retries the request once when it receives a 401, so a long-lived client recovers from an expired session instead of failing every subsequent call.RohlikAPIwires this to a newAuthManager.relogin(). Login/logout requests are exempt to avoid recursion; concurrent 401s are serialized so only one re-login fires.New endpoints (from the integration)
orders.get_detail(order_id)— full order including line itemsorders.get_all_delivered(page_size=50)— paginates the entire delivered-orders historyproducts.get_detail(product_id)— raw product detail (brand, attributes)products.get_categories(product_id)— category hierarchy404 → Noneconvention for these optional-resource fetches, so a discontinued product reads as "not found" rather than an error.Tests & docs
relogin, session injection, the new endpoints, and the 404 handling.🤖 Generated with Claude Code
Generated by Claude Code