Make _get_error_code resilient to malformed API responses#392
Conversation
There was a problem hiding this comment.
As you are refactoring this code, it may be nice to also support the extraction of API specific error codes when available instead of just getting the generic one.
For example:
{
"success":false,
"error":{
"code":1100,
"errors":[{
"code":408,
"path":"/test/:"
}]
}
}
https://kb.synology.com/en-global/DG/DSM_Login_Web_API_Guide/2
Synology APIs (File Station, Active Directory, etc.) can return a list of per-item errors inside response['error']['errors'], each with its own code plus path or msg. Expose them through a defensive accessor that returns [] on missing or malformed input, matching the style of the _get_error_code fix. Addresses review feedback on N4S4#392.
|
Thanks for the pointer. Added Kept as a separate accessor so existing callers and the per-API dispatch in |
Pre-commit's autopep8 hook flagged several long lines (>79 chars) in the assertEqual calls. Reformat using local `response`/`expected` variables (matching the existing style used in test_normal_error_returns_code_with_extras and test_file_station_shape) instead of accepting autopep8's mid-call line wraps, which are harder to read. No behavior change; same 22 tests still pass.
Summary
Authentication._get_error_codeextracts the error code from a Synology API response by chaining.get()calls:If the server returns
{"success": false}without anerrorfield — or witherrorset toNone, a non-dict value, or a dict missingcode— the second.get()raisesAttributeError.Because this method is called on every API response, a malformed reply turns a recoverable error into an exception with no useful context.
Reproduction
The last case is also incorrect because the docstring says the method returns an
int.Fix
Validate the response structure before dereferencing it, and fall back to the existing
CODE_UNKNOWNvalue when the response does not match the documented schema.CODE_UNKNOWNis already mapped to"Unknown Error"inerror_codes.Behavior
{"success": True}00{"success": False, "error": {"code": 100}}100100{"success": False}AttributeError9999(CODE_UNKNOWN){"success": False, "error": None}AttributeError9999{"success": False, "error": {}}None9999{"success": False, "error": "oops"}AttributeError9999{"success": False, "error": {"code": "NaN"}}"NaN"9999{}AttributeError9999Callers using
if not error_codecontinue to take the error path._get_error_message(9999, ...)already returns"Error 9999 - Unknown Error".Tests
Adds
tests/test_auth.pywith 11 cases covering:error;errorset toNone;errorset to{};errorset to a non-dict value;error.codeset to a non-int value;All malformed-response cases now return
CODE_UNKNOWNinstead of raising an exception or returning a non-intvalue.