Skip to content
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

Merged
merged 20 commits into from
Jun 9, 2021

Conversation

matthiasschaub
Copy link
Collaborator

@matthiasschaub matthiasschaub commented May 19, 2021

Corresponding issue

Closes #23
Closes #22

Checklist

@matthiasschaub matthiasschaub added the code quality Related to our standards for 'good' code label May 19, 2021
@matthiasschaub matthiasschaub self-assigned this May 19, 2021
@matthiasschaub matthiasschaub added the bug Something isn't working label May 19, 2021
@matthiasschaub matthiasschaub force-pushed the ohsome-client-exception branch 3 times, most recently from d869a02 to d802dbe Compare May 19, 2021 14:47
@matthiasschaub matthiasschaub changed the title 🚧 Raise exceptions in ohsome client instead of returning None in case of failed ohsome API query Raise exceptions in ohsome client instead of returning None in case of failed ohsome API query May 19, 2021
@joker234 joker234 added this to the Release 0.4.0 milestone May 20, 2021
Copy link
Member

@joker234 joker234 left a 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"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above

Copy link
Collaborator Author

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"),
Copy link
Member

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.

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above

Copy link
Collaborator Author

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/

workers/ohsome_quality_analyst/ohsome/client.py Outdated Show resolved Hide resolved
workers/ohsome_quality_analyst/ohsome/client.py Outdated Show resolved Hide resolved
Comment on lines 38 to 42
try:
resp.raise_for_status() # Raise for response status codes 4xx and 5xx
except httpx.HTTPStatusError:
logging.error("Query ohsome API failed!")
raise
Copy link
Member

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.

Copy link
Collaborator Author

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?

Copy link
Collaborator Author

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?

@matthiasschaub matthiasschaub marked this pull request as draft May 24, 2021 11:27
return data


def check_iso_time(time: str) -> bool:
Copy link
Collaborator Author

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.

Copy link
Member

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.

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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.

@matthiasschaub matthiasschaub marked this pull request as ready for review May 25, 2021 12:11
Copy link
Member

@joker234 joker234 left a 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

workers/ohsome_quality_analyst/ohsome/client.py Outdated Show resolved Hide resolved
workers/ohsome_quality_analyst/ohsome/client.py Outdated Show resolved Hide resolved
workers/ohsome_quality_analyst/ohsome/client.py Outdated Show resolved Hide resolved
@joker234 joker234 assigned joker234 and unassigned joker234 May 27, 2021
# 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("/")
Copy link
Member

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

Copy link
Collaborator Author

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.

workers/ohsome_quality_analyst/ohsome/client.py Outdated Show resolved Hide resolved
workers/ohsome_quality_analyst/ohsome/client.py Outdated Show resolved Hide resolved
return data


def check_iso_time(time: str) -> bool:
Copy link
Member

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.

workers/ohsome_quality_analyst/ohsome/client.py Outdated Show resolved Hide resolved
workers/tests/unittests/test_ohsome_client.py Outdated Show resolved Hide resolved
workers/tests/unittests/test_ohsome_client.py Outdated Show resolved Hide resolved
workers/tests/unittests/test_ohsome_client.py Outdated Show resolved Hide resolved
workers/tests/unittests/test_ohsome_client.py Outdated Show resolved Hide resolved
@matthiasschaub matthiasschaub marked this pull request as draft June 1, 2021 07:47
@matthiasschaub matthiasschaub force-pushed the ohsome-client-exception branch 2 times, most recently from a0724b1 to 8f242a2 Compare June 1, 2021 14:23
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>
@matthiasschaub matthiasschaub marked this pull request as ready for review June 8, 2021 08:29
resp.raise_for_status()
except httpx.HTTPStatusError as error:
raise OhsomeApiError(
"Querying the ohsome API failed!" + error.response.json()["message"]
Copy link
Member

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.:

Suggested change
"Querying the ohsome API failed!" + error.response.json()["message"]
"Querying the ohsome API failed: " + error.response.json()["message"]

@matthiasschaub matthiasschaub merged commit 8bb4f0c into main Jun 9, 2021
@matthiasschaub matthiasschaub deleted the ohsome-client-exception branch June 9, 2021 07:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working code quality Related to our standards for 'good' code
Projects
None yet
Development

Successfully merging this pull request may close these issues.

improve ohsome API client error handling ohsome API definition should be independent from tailing /
2 participants