|
20 | 20 | import airbyte_api |
21 | 21 | import requests |
22 | 22 | from airbyte_api import api, models |
| 23 | +from airbyte_api.errors import SDKError |
23 | 24 |
|
24 | 25 | from airbyte.constants import CLOUD_API_ROOT, CLOUD_CONFIG_API_ROOT, CLOUD_CONFIG_API_ROOT_ENV_VAR |
25 | 26 | from airbyte.exceptions import ( |
@@ -58,6 +59,42 @@ def status_ok(status_code: int) -> bool: |
58 | 59 | return status_code >= 200 and status_code < 300 # noqa: PLR2004 # allow inline magic numbers |
59 | 60 |
|
60 | 61 |
|
| 62 | +def _get_sdk_error_context(error: SDKError) -> dict[str, Any]: |
| 63 | + """Extract context information from an SDKError for debugging. |
| 64 | +
|
| 65 | + This helper extracts the actual request URL and other useful debugging |
| 66 | + information from the Speakeasy SDK's SDKError exception. The SDK stores |
| 67 | + the raw response object which contains the request details. |
| 68 | + """ |
| 69 | + context: dict[str, Any] = { |
| 70 | + "status_code": error.status_code, |
| 71 | + "error_message": error.message, |
| 72 | + } |
| 73 | + |
| 74 | + if error.raw_response is not None: |
| 75 | + request = error.raw_response.request |
| 76 | + if request is not None: |
| 77 | + context["request_url"] = str(request.url) |
| 78 | + context["request_method"] = request.method |
| 79 | + context["response_content_type"] = error.raw_response.headers.get("content-type") |
| 80 | + |
| 81 | + return context |
| 82 | + |
| 83 | + |
| 84 | +def _wrap_sdk_error(error: SDKError, base_context: dict[str, Any] | None = None) -> AirbyteError: |
| 85 | + """Wrap an SDKError with additional context for debugging. |
| 86 | +
|
| 87 | + This function converts a Speakeasy SDK error into an AirbyteError with |
| 88 | + full URL context, making it easier to debug API issues like 404 errors. |
| 89 | + """ |
| 90 | + sdk_context = _get_sdk_error_context(error) |
| 91 | + merged_context = {**(base_context or {}), **sdk_context} |
| 92 | + return AirbyteError( |
| 93 | + message=f"API error occurred: {error.message}", |
| 94 | + context=merged_context, |
| 95 | + ) |
| 96 | + |
| 97 | + |
61 | 98 | def get_config_api_root(api_root: str) -> str: |
62 | 99 | """Get the configuration API root from the main API root. |
63 | 100 |
|
@@ -185,11 +222,16 @@ def get_workspace( |
185 | 222 | client_secret=client_secret, |
186 | 223 | bearer_token=bearer_token, |
187 | 224 | ) |
188 | | - response = airbyte_instance.workspaces.get_workspace( |
189 | | - api.GetWorkspaceRequest( |
190 | | - workspace_id=workspace_id, |
191 | | - ), |
192 | | - ) |
| 225 | + base_context = {"workspace_id": workspace_id, "api_root": api_root} |
| 226 | + try: |
| 227 | + response = airbyte_instance.workspaces.get_workspace( |
| 228 | + api.GetWorkspaceRequest( |
| 229 | + workspace_id=workspace_id, |
| 230 | + ), |
| 231 | + ) |
| 232 | + except SDKError as e: |
| 233 | + raise _wrap_sdk_error(e, base_context) from e |
| 234 | + |
193 | 235 | if status_ok(response.status_code) and response.workspace_response: |
194 | 236 | return response.workspace_response |
195 | 237 |
|
@@ -232,14 +274,19 @@ def list_connections( |
232 | 274 | result: list[models.ConnectionResponse] = [] |
233 | 275 | has_more = True |
234 | 276 | offset, page_size = 0, 100 |
| 277 | + base_context = {"workspace_id": workspace_id, "api_root": api_root} |
235 | 278 | while has_more: |
236 | | - response = airbyte_instance.connections.list_connections( |
237 | | - api.ListConnectionsRequest( |
238 | | - workspace_ids=[workspace_id], |
239 | | - offset=offset, |
240 | | - limit=page_size, |
241 | | - ), |
242 | | - ) |
| 279 | + try: |
| 280 | + response = airbyte_instance.connections.list_connections( |
| 281 | + api.ListConnectionsRequest( |
| 282 | + workspace_ids=[workspace_id], |
| 283 | + offset=offset, |
| 284 | + limit=page_size, |
| 285 | + ), |
| 286 | + ) |
| 287 | + except SDKError as e: |
| 288 | + raise _wrap_sdk_error(e, base_context) from e |
| 289 | + |
243 | 290 | has_more = bool(response.connections_response and response.connections_response.next) |
244 | 291 | offset += page_size |
245 | 292 |
|
@@ -286,10 +333,17 @@ def list_workspaces( |
286 | 333 | result: list[models.WorkspaceResponse] = [] |
287 | 334 | has_more = True |
288 | 335 | offset, page_size = 0, 100 |
| 336 | + base_context = {"workspace_id": workspace_id, "api_root": api_root} |
289 | 337 | while has_more: |
290 | | - response: api.ListWorkspacesResponse = airbyte_instance.workspaces.list_workspaces( |
291 | | - api.ListWorkspacesRequest(workspace_ids=[workspace_id], offset=offset, limit=page_size), |
292 | | - ) |
| 338 | + try: |
| 339 | + response: api.ListWorkspacesResponse = airbyte_instance.workspaces.list_workspaces( |
| 340 | + api.ListWorkspacesRequest( |
| 341 | + workspace_ids=[workspace_id], offset=offset, limit=page_size |
| 342 | + ), |
| 343 | + ) |
| 344 | + except SDKError as e: |
| 345 | + raise _wrap_sdk_error(e, base_context) from e |
| 346 | + |
293 | 347 | has_more = bool(response.workspaces_response and response.workspaces_response.next) |
294 | 348 | offset += page_size |
295 | 349 |
|
@@ -338,14 +392,19 @@ def list_sources( |
338 | 392 | result: list[models.SourceResponse] = [] |
339 | 393 | has_more = True |
340 | 394 | offset, page_size = 0, 100 |
| 395 | + base_context = {"workspace_id": workspace_id, "api_root": api_root} |
341 | 396 | while has_more: |
342 | | - response: api.ListSourcesResponse = airbyte_instance.sources.list_sources( |
343 | | - api.ListSourcesRequest( |
344 | | - workspace_ids=[workspace_id], |
345 | | - offset=offset, |
346 | | - limit=page_size, |
347 | | - ), |
348 | | - ) |
| 397 | + try: |
| 398 | + response: api.ListSourcesResponse = airbyte_instance.sources.list_sources( |
| 399 | + api.ListSourcesRequest( |
| 400 | + workspace_ids=[workspace_id], |
| 401 | + offset=offset, |
| 402 | + limit=page_size, |
| 403 | + ), |
| 404 | + ) |
| 405 | + except SDKError as e: |
| 406 | + raise _wrap_sdk_error(e, base_context) from e |
| 407 | + |
349 | 408 | has_more = bool(response.sources_response and response.sources_response.next) |
350 | 409 | offset += page_size |
351 | 410 |
|
@@ -389,14 +448,19 @@ def list_destinations( |
389 | 448 | result: list[models.DestinationResponse] = [] |
390 | 449 | has_more = True |
391 | 450 | offset, page_size = 0, 100 |
| 451 | + base_context = {"workspace_id": workspace_id, "api_root": api_root} |
392 | 452 | while has_more: |
393 | | - response = airbyte_instance.destinations.list_destinations( |
394 | | - api.ListDestinationsRequest( |
395 | | - workspace_ids=[workspace_id], |
396 | | - offset=offset, |
397 | | - limit=page_size, |
398 | | - ), |
399 | | - ) |
| 453 | + try: |
| 454 | + response = airbyte_instance.destinations.list_destinations( |
| 455 | + api.ListDestinationsRequest( |
| 456 | + workspace_ids=[workspace_id], |
| 457 | + offset=offset, |
| 458 | + limit=page_size, |
| 459 | + ), |
| 460 | + ) |
| 461 | + except SDKError as e: |
| 462 | + raise _wrap_sdk_error(e, base_context) from e |
| 463 | + |
400 | 464 | has_more = bool(response.destinations_response and response.destinations_response.next) |
401 | 465 | offset += page_size |
402 | 466 |
|
@@ -438,11 +502,20 @@ def get_connection( |
438 | 502 | bearer_token=bearer_token, |
439 | 503 | api_root=api_root, |
440 | 504 | ) |
441 | | - response = airbyte_instance.connections.get_connection( |
442 | | - api.GetConnectionRequest( |
443 | | - connection_id=connection_id, |
444 | | - ), |
445 | | - ) |
| 505 | + base_context = { |
| 506 | + "workspace_id": workspace_id, |
| 507 | + "connection_id": connection_id, |
| 508 | + "api_root": api_root, |
| 509 | + } |
| 510 | + try: |
| 511 | + response = airbyte_instance.connections.get_connection( |
| 512 | + api.GetConnectionRequest( |
| 513 | + connection_id=connection_id, |
| 514 | + ), |
| 515 | + ) |
| 516 | + except SDKError as e: |
| 517 | + raise _wrap_sdk_error(e, base_context) from e |
| 518 | + |
446 | 519 | if status_ok(response.status_code) and response.connection_response: |
447 | 520 | return response.connection_response |
448 | 521 |
|
|
0 commit comments