Skip to content

Commit b73eb3c

Browse files
committed
add failure_type to backoff exceptions, update tests to reflect
1 parent 5ef44e2 commit b73eb3c

File tree

2 files changed

+87
-5
lines changed

2 files changed

+87
-5
lines changed

airbyte_cdk/sources/streams/http/exceptions.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
from typing import Optional, Union
77

8-
from airbyte_cdk.models import FailureType
98
import requests
109

10+
from airbyte_cdk.models import FailureType
1111

12-
class BaseBackoffException(requests.exceptions.HTTPError):
1312

13+
class BaseBackoffException(requests.exceptions.HTTPError):
1414
def __init__(
1515
self,
1616
request: requests.PreparedRequest,
@@ -55,12 +55,41 @@ def __init__(
5555
:param response: the response that triggered the backoff exception
5656
"""
5757
self.backoff = backoff
58-
super().__init__(request=request, response=response, error_message=error_message)
58+
super().__init__(
59+
request=request,
60+
response=response,
61+
error_message=error_message,
62+
failure_type=failure_type,
63+
)
5964

6065

6166
class DefaultBackoffException(BaseBackoffException):
62-
pass
67+
def __init__(
68+
self,
69+
request: requests.PreparedRequest,
70+
response: Optional[Union[requests.Response, Exception]],
71+
error_message: str = "",
72+
failure_type: Optional[FailureType] = None,
73+
):
74+
super().__init__(
75+
request=request,
76+
response=response,
77+
error_message=error_message,
78+
failure_type=failure_type,
79+
)
6380

6481

6582
class RateLimitBackoffException(BaseBackoffException):
66-
pass
83+
def __init__(
84+
self,
85+
request: requests.PreparedRequest,
86+
response: Optional[Union[requests.Response, Exception]],
87+
error_message: str = "",
88+
failure_type: Optional[FailureType] = None,
89+
):
90+
super().__init__(
91+
request=request,
92+
response=response,
93+
error_message=error_message,
94+
failure_type=failure_type,
95+
)

unit_tests/sources/streams/http/test_http_client.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,3 +744,56 @@ def test_given_different_headers_then_response_is_not_cached(requests_mock):
744744
)
745745

746746
assert second_response.json()["test"] == "second response"
747+
748+
749+
@pytest.mark.usefixtures("mock_sleep")
750+
@pytest.mark.parametrize(
751+
"response_code, expected_failure_type, error_message, exception_class",
752+
[
753+
(400, FailureType.system_error, "test error message", UserDefinedBackoffException),
754+
(401, FailureType.config_error, "test error message", UserDefinedBackoffException),
755+
(403, FailureType.transient_error, "test error message", UserDefinedBackoffException),
756+
(400, FailureType.system_error, "test error message", DefaultBackoffException),
757+
(401, FailureType.config_error, "test error message", DefaultBackoffException),
758+
(403, FailureType.transient_error, "test error message", DefaultBackoffException),
759+
(400, FailureType.system_error, "test error message", RateLimitBackoffException),
760+
(401, FailureType.config_error, "test error message", RateLimitBackoffException),
761+
(403, FailureType.transient_error, "test error message", RateLimitBackoffException),
762+
],
763+
)
764+
def test_send_with_retry_raises_airbyte_traced_exception_with_failure_type(
765+
response_code, expected_failure_type, error_message, exception_class
766+
):
767+
error_mapping = {
768+
response_code: ErrorResolution(ResponseAction.RETRY, expected_failure_type, error_message),
769+
}
770+
http_client = HttpClient(
771+
name="test",
772+
logger=MagicMock(spec=logging.Logger),
773+
error_handler=HttpStatusErrorHandler(
774+
logger=MagicMock(), error_mapping=error_mapping, max_retries=1
775+
),
776+
)
777+
http_client._send = MagicMock(spec=http_client._send)
778+
http_client._send.__name__ = "_send"
779+
prepared_request = MagicMock(spec=requests.PreparedRequest)
780+
mocked_response = MagicMock(spec=requests.Response)
781+
mocked_response.status_code = response_code
782+
if exception_class == UserDefinedBackoffException:
783+
http_client._send.side_effect = exception_class(
784+
backoff=0,
785+
request=prepared_request,
786+
response=mocked_response,
787+
error_message=error_message,
788+
failure_type=expected_failure_type,
789+
)
790+
else:
791+
http_client._send.side_effect = exception_class(
792+
request=prepared_request,
793+
response=mocked_response,
794+
error_message=error_message,
795+
failure_type=expected_failure_type,
796+
)
797+
with pytest.raises(AirbyteTracedException) as e:
798+
http_client._send_with_retry(prepared_request, {})
799+
assert e.value.failure_type == expected_failure_type

0 commit comments

Comments
 (0)