You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix DeviceIn API auth/header handling and expand device models
🐞 Bug fix✨ Enhancement🕐 20-40 Minutes
AI Description
• Switch DeviceIn requests to use access token and optional reCAPTCHA header.
• Extend device/device-type models to match newly returned API fields.
• Update example usage to consume a device list from the client.
Diagram
graph TD
ex["example.py"] --> cli["IECClient.get_device_in()"] --> dat["data.get_device_in()"] --> http["commons.send_get_request()"] --> api{{"IEC DeviceIn API"}}
dat --> dev(("Device models"))
dat --> dtype(("DeviceType model"))
subgraph Legend
direction LR
_m["Module/Method"] ~~~ _ext{{"External API"}} ~~~ _model(("Model"))
end
Loading
High-Level Assessment
The following are alternative approaches to this PR:
1. Centralize reCAPTCHA/header overrides in request helper
➕ Avoids per-endpoint header branching in each data function
➕ Makes it easier to add future endpoint-specific headers consistently
➖ May require broader refactor of commons helpers and call sites
➖ Risk of unintentionally changing behavior for other endpoints
2. Endpoint-specific auth strategy (token selection per endpoint)
➕ Makes explicit which endpoints require access_token vs id_token
➕ Reduces chance of future regressions when endpoints differ
➖ Adds more configuration/branching in the client/data layer
➖ Slightly more maintenance overhead than a simple fix
Recommendation: The PR’s approach (switch to access_token for DeviceIn and add an optional RecaptchaToken header) is a pragmatic fix with minimal surface-area change. If more endpoints begin requiring special headers or different token types, consider centralizing header overrides and/or codifying per-endpoint auth requirements to prevent drift.
Files changed (5) +27 / -10
Enhancement (4) +21 / -8
example.pyUpdate example to fetch devices via get_devices()+3/-3
Update example to fetch devices via get_devices()
• Replaces DeviceIn response handling with a direct list returned by get_devices(). Adjusts selection logic to use the first device from the list when present.
iec_client.pyExpose recaptcha_token parameter on IECClient.get_device_in()+5/-2
Expose recaptcha_token parameter on IECClient.get_device_in()
• Extends the client method signature to accept an optional recaptcha_token and forwards it to the data layer. Updates docstring to document the new argument.
device.pyExpand Device and CounterDevice fields to match API payload+6/-2
Expand Device and CounterDevice fields to match API payload
• Adds new device-level fields (disconnect/electronic/disconnected status and report status). Extends CounterDevice with last_mr_type_code and device_digit_length fields for more complete meter metadata.
device_type.pyAdd electronic/disconnection fields to DeviceType model+7/-1
Add electronic/disconnection fields to DeviceType model
• Adds is_electronic, is_disconnected, and disconnect_reason fields and updates the example payload comment accordingly. Improves alignment with the API’s returned schema.
data.pyFix DeviceIn auth token and add optional RecaptchaToken header+6/-2
Fix DeviceIn auth token and add optional RecaptchaToken header
• Updates DeviceIn request authorization to use token.access_token instead of token.id_token. Adds an optional recaptcha_token parameter and conditionally injects a RecaptchaToken header for the endpoint.
1. CounterDevice decode may fail✓ Resolved🐞 Bug≡ Correctness
Description
CounterDevice now requires lastMrTypeCode and deviceDigitLength with no defaults, so
deserialization will raise if the API (or any older/stored payload) omits these keys. This breaks
get_device_by_device_id() because it decodes the response into Devices -> CounterDevice.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
The module’s documented example payload for the CounterDevice endpoint omits the newly-required
keys, and the decoding path in data.get_device_by_device_id() goes through
Devices/CounterDevice, so missing keys will cause decode-time exceptions.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
`iec_api.models.device.CounterDevice` added two new fields (`lastMrTypeCode`, `deviceDigitLength`) as required (no default). If these keys are missing in the response, mashumaro deserialization will fail and callers like `get_device_by_device_id()` will raise instead of returning data.
## Issue Context
The same module includes an example response for the endpoint that does not include these two keys, indicating the library previously handled payloads without them.
## Fix Focus Areas
- iec_api/models/device.py[72-85]
- iec_api/models/device.py[22-47]
- iec_api/data.py[333-342]
## Suggested fix
Change the new fields to be backward-compatible:
- Make them `Optional[int]` with `default=None` (preferred), OR
- Provide safe defaults if the API semantics require a numeric value.
Example:
- `last_mr_type_code: Optional[int] = field(default=None, metadata=field_options(alias="lastMrTypeCode"))`
- `device_digit_length: Optional[int] = field(default=None, metadata=field_options(alias="deviceDigitLength"))`
This keeps decoding robust when the API omits these fields.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
data.get_device_in() adds RecaptchaToken into the shared HEADERS_WITH_AUTH dict, so once set
it can persist into later requests even when recaptcha_token is not provided. This contaminates
subsequent calls with a stale/incorrect token and can cause intermittent request failures.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
HEADERS_WITH_AUTH is a shared module-level dict; both add_auth_bearer_to_headers() and the new
recaptcha logic mutate the passed dict, so RecaptchaToken can remain set for subsequent calls.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
`iec_api.data.get_device_in()` currently mutates the module-level `HEADERS_WITH_AUTH` by adding a per-request `RecaptchaToken` header. Because the headers dict is shared, the token can persist across subsequent calls (even when `recaptcha_token` is omitted), causing request contamination.
## Issue Context
- `HEADERS_WITH_AUTH` is a module-level dict constant.
- `commons.add_auth_bearer_to_headers(...)` mutates the passed dict in-place.
- `get_device_in()` additionally mutates the same dict by setting `headers["RecaptchaToken"]`.
## Fix Focus Areas
- iec_api/data.py[428-438]
- iec_api/commons.py[24-34]
- iec_api/const.py[22-28]
## Suggested fix
In `get_device_in()`, start from a copy of `HEADERS_WITH_AUTH` (or build a fresh dict) before adding Authorization and `RecaptchaToken`, e.g.:
- `headers = commons.add_auth_bearer_to_headers(HEADERS_WITH_AUTH.copy(), token.access_token)`
- then conditionally add `RecaptchaToken` to that copied dict.
This prevents a previously-set recaptcha token from leaking into later calls.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
3. Optional status defaults to 0✓ Resolved🐞 Bug⚙ Maintainability
Description
Device.report_result_status is Optional[int] but defaults to 0, so missing
reportResultStatus is indistinguishable from an explicit zero returned by the API. This can lead
to incorrect downstream logic that treats 0 as a real status code rather than “not provided”.
ⓘ Recommendations generated based on similar findings in past PRs
Evidence
Device now forces a missing reportResultStatus to 0, while another model (MeterReadingData)
treats the same alias as optional with a None default, indicating the intended semantic
distinction.
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution
## Issue description
`Device.report_result_status` is typed as `Optional[int]` but uses `default=0`, which collapses two different states (missing vs explicitly 0). This makes it harder for callers to correctly interpret the API response.
## Issue Context
A similar field in `MeterReadingData` already defaults to `None`, suggesting the project’s convention is to preserve missing-as-None.
## Fix Focus Areas
- iec_api/models/device.py[50-61]
- iec_api/models/remote_reading.py[150-156]
## Suggested fix
Change `report_result_status` default from `0` to `None`:
- `report_result_status: Optional[int] = field(default=None, metadata=field_options(alias="reportResultStatus"))`
This preserves the ability to distinguish “field absent” from “status code 0”.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.