-
Notifications
You must be signed in to change notification settings - Fork 0
Deserialize API responses for non 2XX status codes if defined #778
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #778 +/- ##
==========================================
- Coverage 94.40% 94.28% -0.12%
==========================================
Files 7 7
Lines 804 805 +1
==========================================
Hits 759 759
- Misses 45 46 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly minor changes, naming and re-ordering. Overall, this looks good, and should help consumers of packages that depend on this handle specific exceptions more flexibly.
Provides the exception to raise when the remote server returns an unsuccessful response. | ||
For more information about the failure, inspect ``.status_code`` and ``.reason_phrase``. | ||
For more information about the failure, inspect ``.status_code``, ``.reason_phrase``, and if the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For more information about the failure, inspect ``.status_code``, ``.reason_phrase``, and if the | |
For more information about the failure, inspect ``.status_code`` and ``.reason_phrase``. If the |
For more information about the failure, inspect ``.status_code`` and ``.reason_phrase``. | ||
For more information about the failure, inspect ``.status_code``, ``.reason_phrase``, and if the | ||
server defines a custom exception model, ``.exception_model``. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
server defines a custom exception model, ``.exception_model``. | |
server defines a custom exception model, ``.exception_model`` contains the deserialized response. |
src/ansys/openapi/common/_util.py
Outdated
def handle_response(response: requests.Response) -> requests.Response: | ||
def handle_response( | ||
response: requests.Response, response_model: DeserializedType = None | ||
) -> requests.Response: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is kind of weird. It no longer needs to return anything, so really all it's doing is encapsulating the "if not 2xx then raise" logic.
Maybe we just delete the function and include the if-check in __call_api
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought about that, but it's not a private function, so there's a low but non-zero chance someone is relying on it.
If we think that risk is low enough then yes it can be removed and replaced with a check in the one place it's used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so. As you say, it's not a private function, but it's also not documented. For me this falls into a grey area, and I'm happy to take the chance that either no one's using it, or if they are, they can easily work around this change.
_response_type = response_type_map.get(response_data.status_code, None) | ||
|
||
return_data = self.deserialize(response_data, _response_type) | ||
deserialized_response = return_data |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is all a bit weird. We define a variable called return_data
, which is a union of both the request response type and our deserialized type, and initially contains the raw request response.
We then maybe overwrite that with the deserialized response, and then write that to a different variable deserialized_response
.
I realize that return_data
needs to be the union of the types eventually, but it might be cleaner if that union happens closer to the end of the function.
tests/test_api_client.py
Outdated
assert headers["Content-Type"] == "text/plain" | ||
|
||
def test_get_model_returns_defined_exception(self): | ||
"""This test getting an object from a server which returns a defined exception object when the requested id does |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"""This test getting an object from a server which returns a defined exception object when the requested id does | |
"""This test gets an object from a server which returns a defined exception object when the requested id does |
tests/test_api_client.py
Outdated
assert exception_model.stack_trace == stack_trace | ||
|
||
def test_get_model_throws_if_no_defined_exception_type(self): | ||
"""This test getting an object from a server which returns a defined exception object when the requested id does |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"""This test getting an object from a server which returns a defined exception object when the requested id does | |
"""This test gets an object from a server which returns a defined exception object when the requested id does |
tests/test_api_client.py
Outdated
assert "Content-Type" in headers | ||
assert headers["Content-Type"] == "text/plain" | ||
|
||
def test_get_model_returns_defined_exception(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def test_get_model_returns_defined_exception(self): | |
def test_get_model_raises_exception_with_deserialized_response(self): |
tests/test_api_client.py
Outdated
assert exception_model.exception_code == exception_code | ||
assert exception_model.stack_trace == stack_trace | ||
|
||
def test_get_model_throws_if_no_defined_exception_type(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def test_get_model_throws_if_no_defined_exception_type(self): | |
def test_get_model_raises_exception_with_no_deserialized_response(self): |
132db2c
to
da24ed9
Compare
Closes #757
This PR adds deserialization for exceptions if models are defined in the type map. A new property
exception_model
will be present on anApiException
object which will be populated with the return type if it's defined.I considered just returning the model without throwing, but that might be a surprising outcome for users, and would break the pattern of "everything was OK because I got a response and not an exception". This is an additive change to
handle_response
and to the exception type, existing users of that code should continue to see the same behaviour as before.