-
Notifications
You must be signed in to change notification settings - Fork 7
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
Raise exceptions in ohsome client instead of returning None in case of failed ohsome API query #29
Conversation
c82664e
to
759f46c
Compare
d869a02
to
d802dbe
Compare
workers/tests/integrationtests/fixtures/vcr_cassettes/test_indicator_tags_ratio.json
Outdated
Show resolved
Hide resolved
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.
Thanks for dealing with this issue. I left some suggestions, all in all a good change 👍
200, content=self.invalid_geojson | ||
200, | ||
content=self.invalid_geojson, | ||
request=httpx.Request("POST", "mock.org"), |
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.
see above
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.
Changed URL to http://www.example.org/
mock_request.return_value = httpx.Response( | ||
200, | ||
content=self.valid_geojson, | ||
request=httpx.Request("POST", "mock.org"), |
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.
What is the page "mock.org"
doing in here? I assume you want to query another page here.
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.
Actually this will make a request to a website. It just create the object which is needed by the httpx client to make a request: https://www.python-httpx.org/api/#request
I substitude mock.org with "url" to make the generic naming more clear.
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.
Changed URL to http://www.example.org/
mock_request.return_value = httpx.Response( | ||
400, | ||
content=self.invalid_geojson, | ||
request=httpx.Request("POST", "mock.org"), |
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.
see above
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.
Changed URL to http://www.example.org/
try: | ||
resp.raise_for_status() # Raise for response status codes 4xx and 5xx | ||
except httpx.HTTPStatusError: | ||
logging.error("Query ohsome API failed!") | ||
raise |
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.
Currently, these errors are returned to the user just as “Internal Server Error 500”. It might be useful to give the user a better error message.
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.
Can we provide more context then stating that quering the ohsome API failed since response status code could be any of 4xx and 5xx? Should we state more clearly that the reponse code has bee returend from the ohsome 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.
Does commit b390b84 adresses the problem you raised here?
workers/tests/integrationtests/fixtures/vcr_cassettes/test_indicator_tags_ratio.json
Outdated
Show resolved
Hide resolved
0c26995
to
b165c08
Compare
return data | ||
|
||
|
||
def check_iso_time(time: str) -> bool: |
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 started to implement this function to validate the time parameter before it is used as query parameter with the ohsome API. But since the ISO-8601 conform timestring(s) can be quite versatile it can only be tested rudimentary with the built-in stdlib. An alternative is to use a library such as dateutils to validate the timestring. Another one is to forgo it and pass it to the ohsome API in the hope to catch invalid timestring through the response of the ohsome API.
@joker234 I would be happy to hear your thoughts on that.
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'll review the implemented suggestion, but in general I would say you shouldn't deal with that. The ohsome API only covers part of the ISO 8601 implementation. I would suggest to catch the exception/error of the ohsome API and deal with that properly. In general I would never try to deal with time parsing manually, but use existing, well-maintained libraries like dateutil.
Maybe @tyrasd has an additional opinion on the general matter if it's reasonable to check the time input in your own code or if the ohsome API should deal with that.
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 like the idea to catch invalid timestrings through the response of ohsome API.
While playing around with the ohsome API I found out two different response related to wrong use of the time parameter:
{
"timestamp": "2021-05-31T07:33:31.206528",
"status": 400,
"message": "You need to give at least two timestamps or a time interval for this resource.",
"requestUrl": "https://api.ohsome.org/v1/contributions/count?bboxes=8.67%2C49.39%2C8.71%2C49.42&filter=type%3Away%20and%20natural%3D*&format=json&time=20140101"
}
{
"timestamp": "2021-05-31T07:36:40.661092",
"status": 400,
"message": "The provided time parameter is not ISO-8601 conform.",
"requestUrl": "https://api.ohsome.org/v1/contributions/count?bboxes=8.67%2C49.39%2C8.71%2C49.42&filter=type%3Away%20and%20natural%3D*&format=json&time=20140101%2C2015010"
}
@tyrasd are there more cases that should be covered? I would be happy to here your thoughts on that.
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 removed functions for testing the time parameter.
The code in line 43 will raise an exception if the ohsome response will return status code 400 when the time parameter is invalid.
try:
resp.raise_for_status() # Raise for response status codes 4xx and 5xx
except httpx.HTTPStatusError as error:
raise OhsomeApiError(
"Querying the ohsome API failed!" + error.response.json()["message"]
) from error
Which will lead to following Error:
Traceback (most recent call last):
File "/home/matthias/work/oqt/test.py", line 7, in <module>
resp.raise_for_status() # Raise for response status codes 4xx and 5xx
File "/home/matthias/.cache/pypoetry/virtualenvs/ohsome-quality-analyst-JhxvPu0L-py3.9/lib/python3.9/site-packages/httpx/_models.py", line 1105, in raise_for_status
raise HTTPStatusError(message, request=request, response=self)
httpx.HTTPStatusError: 400 Client Error: for url: https://api.ohsome.org/v1/contributions/count?bboxes=8.67%2C49.39%2C8.71%2C49.42&filter=type%3Away%20and%20natural%3D*&format=json&time=20140101%2C2015010
For more information check: https://httpstatuses.com/400
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/home/matthias/work/oqt/test.py", line 10, in <module>
raise OhsomeApiError("Querying the ohsome API failed! " + _) from error
ohsome_quality_analyst.utils.exceptions.OhsomeApiError: Querying the ohsome API failed! The provided time parameter is not ISO-8601 conform.
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.
Made some suggestions.
Additional ToDos (added to checklist):
- Review check_iso_time
- Review test_ohsome_client.py
- fix failing test
# self.assertTrue(ohsome_client.check_iso_time("2014-01-01/2018-01-01/P1Y")) | ||
|
||
def test_build_url(self) -> None: | ||
ohsome_api = OHSOME_API.rstrip("/") |
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'm not a huge fan of using variables from outside of the test classes. You can keep it like that, but I would have defined the OHSOME_API
variable separately in the test class.
Disadvantage: if you change the default value, you have to change it in the tests as well, which you will recognize, because the tests will run ;)
Advantage: no one can accidentally change the default value of this variable. E.g. because the person runs the tests with the OHSOME_API
environment variable anyway
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.
Yes, I see your point. I removed the dependecy on the env and rely on hardcoded URL for now.
return data | ||
|
||
|
||
def check_iso_time(time: str) -> bool: |
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'll review the implemented suggestion, but in general I would say you shouldn't deal with that. The ohsome API only covers part of the ISO 8601 implementation. I would suggest to catch the exception/error of the ohsome API and deal with that properly. In general I would never try to deal with time parsing manually, but use existing, well-maintained libraries like dateutil.
Maybe @tyrasd has an additional opinion on the general matter if it's reasonable to check the time input in your own code or if the ohsome API should deal with that.
635b08f
to
9127f4a
Compare
a0724b1
to
8f242a2
Compare
Raise exceptions in ohsome client instead of returning None in case of failed ohsome API query.
Co-authored-by: Johannes Visintini <johannes.visintini@heigit.org>
8f242a2
to
65ac79f
Compare
Co-authored-by: Johannes Visintini <johannes.visintini@heigit.org>
Co-authored-by: Johannes Visintini <johannes.visintini@heigit.org>
Co-authored-by: Johannes Visintini <johannes.visintini@heigit.org>
Improve error message by adding a custom Exception for the ohsome client module and additional context in text form.
Co-authored-by: Johannes Visintini <johannes.visintini@heigit.org>
1028de4
to
b56d65f
Compare
resp.raise_for_status() | ||
except httpx.HTTPStatusError as error: | ||
raise OhsomeApiError( | ||
"Querying the ohsome API failed!" + error.response.json()["message"] |
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.
Didn't try this out, but I would add spacing between the two strings, e.g.:
"Querying the ohsome API failed!" + error.response.json()["message"] | |
"Querying the ohsome API failed: " + error.response.json()["message"] |
Corresponding issue
Closes #23
Closes #22
Checklist
main
(e.g. throughgit rebase main
)check_iso_time
test_ohsome_client.py