Commit 5b47e04
fix(ei): return the named collection EI methods promise (#107)
Every Energy Intelligence method typed `List[Dict[str, Any]]` returned
`response["data"]`, but the EI controllers put their records under a *named*
key inside `data`. `client.ei.rig_counts.by_basin()` returned
`{"report_date": ..., "basins": [...]}` where the signature and the docstring
example promised the basin list, so the documented
`for basin in basins: basin["count"]` iterated dict keys.
Verified live against https://api.oilpriceapi.com on 2026-09-13 with a
Scale-tier key: 27 methods across seven EI resources are wrong the same way,
in both the sync resources and `async_resources.py`.
- `unwrap_ei_collection` / `unwrap_ei_object` / `ei_data` in
`oilpriceapi/resources/ei/_envelopes.py` are now the single place that knows
the envelope shape. The per-method
`if "data" in response: return response["data"]` is gone, and
`unwrap_well_permit_search_response` keeps its name and its error message
but delegates to the shared helper instead of carrying a second copy.
- A success body missing the named collection raises
`OilPriceAPIError(code="MALFORMED_RESPONSE")` instead of handing back the
envelope. An empty collection stays an empty list.
- `ei.well_permits.get()` / `ei.frac_focus.get()` return the record rather
than its `{"well_permit": ...}` wrapper.
- `ei.forecasts.prices()` / `.production()` are typed `Dict[str, Any]`: both
return a mapping keyed by commodity / series code, never a list.
- `ei.well_permits.latest()` / `ei.frac_focus.latest()` deliberately keep
returning the envelope so their pagination and freshness counters stay
reachable; their docstrings now say so.
- Docstring examples use the field names the API actually returns.
Tests drive the real client transport (respx over httpx), not a stubbed
resource, because the defect lives between the HTTP body and the return value.
Valid, empty, missing-collection, malformed-row, 401/403/429 and async parity
are covered, plus a source-level guard that every async EI method's return
expression matches its sync twin character for character.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ao5paex73xXvuM424Libo1 parent 7982b0b commit 5b47e04
13 files changed
Lines changed: 1587 additions & 543 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
5 | 52 | | |
6 | 53 | | |
7 | 54 | | |
| |||
112 | 159 | | |
113 | 160 | | |
114 | 161 | | |
115 | | - | |
116 | | - | |
117 | 162 | | |
118 | 163 | | |
119 | 164 | | |
| |||
0 commit comments