|
45 | 45 | rate_limit_default_backoff_handler, |
46 | 46 | user_defined_backoff_handler, |
47 | 47 | ) |
| 48 | +from airbyte_cdk.sources.utils.types import JsonType |
| 49 | +from airbyte_cdk.utils.airbyte_secrets_utils import filter_secrets |
48 | 50 | from airbyte_cdk.utils.constants import ENV_REQUEST_CACHE_PATH |
49 | 51 | from airbyte_cdk.utils.stream_status_utils import ( |
50 | 52 | as_airbyte_message as stream_status_as_airbyte_message, |
@@ -334,6 +336,29 @@ def _send( |
334 | 336 |
|
335 | 337 | return response # type: ignore # will either return a valid response of type requests.Response or raise an exception |
336 | 338 |
|
| 339 | + def _get_response_body(self, response: requests.Response) -> Optional[JsonType]: |
| 340 | + """ |
| 341 | + Extracts and returns the body of an HTTP response. |
| 342 | +
|
| 343 | + This method attempts to parse the response body as JSON. If the response |
| 344 | + body is not valid JSON, it falls back to decoding the response content |
| 345 | + as a UTF-8 string. If both attempts fail, it returns None. |
| 346 | +
|
| 347 | + Args: |
| 348 | + response (requests.Response): The HTTP response object. |
| 349 | +
|
| 350 | + Returns: |
| 351 | + Optional[JsonType]: The parsed JSON object as a string, the decoded |
| 352 | + response content as a string, or None if both parsing attempts fail. |
| 353 | + """ |
| 354 | + try: |
| 355 | + return str(response.json()) |
| 356 | + except requests.exceptions.JSONDecodeError: |
| 357 | + try: |
| 358 | + return response.content.decode("utf-8") |
| 359 | + except Exception: |
| 360 | + return "The Content of the Response couldn't be decoded." |
| 361 | + |
337 | 362 | def _handle_error_resolution( |
338 | 363 | self, |
339 | 364 | response: Optional[requests.Response], |
@@ -362,12 +387,18 @@ def _handle_error_resolution( |
362 | 387 |
|
363 | 388 | if error_resolution.response_action == ResponseAction.FAIL: |
364 | 389 | if response is not None: |
365 | | - error_message = f"'{request.method}' request to '{request.url}' failed with status code '{response.status_code}' and error message '{self._error_message_parser.parse_response_error_message(response)}'" |
| 390 | + filtered_response_message = filter_secrets( |
| 391 | + f"Request (body): '{str(request.body)}'. Response (body): '{self._get_response_body(response)}'. Response (headers): '{response.headers}'." |
| 392 | + ) |
| 393 | + error_message = f"'{request.method}' request to '{request.url}' failed with status code '{response.status_code}' and error message: '{self._error_message_parser.parse_response_error_message(response)}'. {filtered_response_message}" |
366 | 394 | else: |
367 | 395 | error_message = ( |
368 | 396 | f"'{request.method}' request to '{request.url}' failed with exception: '{exc}'" |
369 | 397 | ) |
370 | 398 |
|
| 399 | + # ensure the exception message is emitted before raised |
| 400 | + self._logger.error(error_message) |
| 401 | + |
371 | 402 | raise MessageRepresentationAirbyteTracedErrors( |
372 | 403 | internal_message=error_message, |
373 | 404 | message=error_resolution.error_message or error_message, |
|
0 commit comments