-
Notifications
You must be signed in to change notification settings - Fork 45
fix: Add DNSResolutionError for handling DNS resolution failures #360
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
Changes from 17 commits
eb212b3
283802d
b3c0734
984f4eb
aef78e8
69620ac
42579ca
7a0b67b
9bac7fc
492dc08
5ade37c
e8541be
24d48c1
888d5a3
8a88e1d
8506c83
57e3578
9148cd5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,6 +36,7 @@ | |
| ) | ||
| from airbyte_cdk.sources.streams.http.exceptions import ( | ||
| DefaultBackoffException, | ||
| DNSResolutionError, | ||
| RateLimitBackoffException, | ||
| RequestBodyException, | ||
| UserDefinedBackoffException, | ||
|
|
@@ -300,6 +301,16 @@ def _send( | |
|
|
||
| try: | ||
| response = self._session.send(request, **request_kwargs) | ||
| except requests.ConnectionError as e: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we only catch |
||
| if "Name or service not known" in str(e) or "nodename nor servname provided" in str(e): | ||
| assert ( | ||
| request.url is not None | ||
| ), "Request URL cannot be None for DNS resolution error" | ||
| exc = DNSResolutionError( | ||
| url=request.url, request=request, response=e, error_message=str(e) | ||
| ) | ||
| else: | ||
| exc = e | ||
| except requests.RequestException as e: | ||
| exc = e | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
|
|
||
| import pytest | ||
| import requests | ||
| from requests.exceptions import InvalidURL | ||
|
|
||
| from airbyte_cdk.models import AirbyteLogMessage, AirbyteMessage, Level, SyncMode, Type | ||
| from airbyte_cdk.sources.streams import CheckpointMixin | ||
|
|
@@ -20,9 +21,13 @@ | |
| from airbyte_cdk.sources.streams.core import StreamData | ||
| from airbyte_cdk.sources.streams.http import HttpStream, HttpSubStream | ||
| from airbyte_cdk.sources.streams.http.error_handlers import ErrorHandler, HttpStatusErrorHandler | ||
| from airbyte_cdk.sources.streams.http.error_handlers.response_models import ResponseAction | ||
| from airbyte_cdk.sources.streams.http.error_handlers.response_models import ( | ||
| FailureType, | ||
| ResponseAction, | ||
| ) | ||
| from airbyte_cdk.sources.streams.http.exceptions import ( | ||
| DefaultBackoffException, | ||
| DNSResolutionError, | ||
| RequestBodyException, | ||
| UserDefinedBackoffException, | ||
| ) | ||
|
|
@@ -331,6 +336,34 @@ def test_raise_on_http_errors(mocker, error): | |
| assert send_mock.call_count == stream.max_retries + 1 | ||
|
|
||
|
|
||
| class StubHttpStreamWithErrorHandler(StubBasicReadHttpStream): | ||
| def get_error_handler(self) -> Optional[ErrorHandler]: | ||
| return HttpStatusErrorHandler(logging.getLogger()) | ||
|
|
||
|
|
||
| def test_dns_resolution_error_retry(): | ||
| """Test that DNS resolution errors are retried""" | ||
| stream = StubHttpStreamWithErrorHandler() | ||
| error_handler = stream.get_error_handler() | ||
| request = requests.PreparedRequest() | ||
| request.url = "https://example.com" | ||
| dns_error = DNSResolutionError( | ||
|
Comment on lines
+344
to
+350
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a test that simulates/mocks the error we expect the http api to raise? Specifically, I think we're expecting something like an invalid URL code, except with the noted error text about DNS. |
||
| url="https://example.com", request=request, response=Exception("DNS lookup failed") | ||
| ) | ||
| resolution = error_handler.interpret_response(dns_error) | ||
| assert resolution.response_action == ResponseAction.RETRY | ||
| assert resolution.failure_type == FailureType.transient_error | ||
|
|
||
|
|
||
| def test_invalid_url_fails(): | ||
| """Test that invalid URLs fail immediately""" | ||
| stream = StubHttpStreamWithErrorHandler() | ||
| error_handler = stream.get_error_handler() | ||
| resolution = error_handler.interpret_response(InvalidURL()) | ||
| assert resolution.response_action == ResponseAction.FAIL | ||
| assert resolution.failure_type == FailureType.config_error | ||
|
|
||
|
|
||
| class PostHttpStream(StubBasicReadHttpStream): | ||
| http_method = "POST" | ||
|
|
||
|
|
||
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 don't see where this exception class is raised. Can you help me understand this?
Does our code need to raise this exception in order for its handling to trigger? Or is it going to be picked up automatically somehow?