Skip to content

Commit bd07969

Browse files
fix: Fix circular import and add integration tests for URL handling
Co-Authored-By: AJ Steers <aj@airbyte.io>
1 parent ce069b0 commit bd07969

File tree

2 files changed

+89
-3
lines changed

2 files changed

+89
-3
lines changed

airbyte/_util/api_util.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import requests
2222
from airbyte_api import api, models
2323

24-
from airbyte.cloud.auth import resolve_cloud_config_api_url
2524
from airbyte.constants import CLOUD_API_ROOT, CLOUD_CONFIG_API_ROOT, CLOUD_CONFIG_API_ROOT_ENV_VAR
2625
from airbyte.exceptions import (
2726
AirbyteConnectionSyncError,
@@ -31,6 +30,7 @@
3130
PyAirbyteInputError,
3231
)
3332
from airbyte.secrets.base import SecretString
33+
from airbyte.secrets.util import try_get_secret
3434

3535

3636
if TYPE_CHECKING:
@@ -76,9 +76,9 @@ def get_config_api_root(api_root: str) -> str:
7676
NotImplementedError: If the Config API root cannot be determined.
7777
"""
7878
# First, check if the Config API URL is explicitly set via environment variable
79-
config_api_override = resolve_cloud_config_api_url()
79+
config_api_override = try_get_secret(CLOUD_CONFIG_API_ROOT_ENV_VAR, default=None)
8080
if config_api_override:
81-
return config_api_override
81+
return str(config_api_override)
8282

8383
# Fall back to deriving from the main API root
8484
if api_root == CLOUD_API_ROOT:

tests/integration_tests/cloud/test_cloud_api_util.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,3 +302,89 @@ def test_check_connector(
302302
assert result == expect_success
303303
except AirbyteError as e:
304304
pytest.fail(f"API call failed: {e}")
305+
306+
307+
@pytest.mark.parametrize(
308+
"bogus_api_root",
309+
[
310+
pytest.param(
311+
"https://bogus.invalid.example.com/api/v1",
312+
id="completely_invalid_host",
313+
),
314+
pytest.param(
315+
"https://httpbin.org/status/404",
316+
id="httpbin_404_endpoint",
317+
),
318+
],
319+
)
320+
def test_bogus_api_root_error_includes_url_context(bogus_api_root: str) -> None:
321+
"""Test that API errors include the request URL in context for debugging.
322+
323+
This test validates that when an API call fails due to a bogus base URL,
324+
the error message includes the full request URL that was attempted. This
325+
helps debug URL construction issues like those seen with custom API roots.
326+
327+
Note: This test does not require credentials since it's testing error
328+
behavior with invalid URLs that will fail before authentication.
329+
"""
330+
fake_bearer_token = SecretString("fake-token-for-testing")
331+
fake_workspace_id = "00000000-0000-0000-0000-000000000000"
332+
333+
with pytest.raises(Exception) as exc_info:
334+
api_util.list_sources(
335+
workspace_id=fake_workspace_id,
336+
api_root=bogus_api_root,
337+
client_id=None,
338+
client_secret=None,
339+
bearer_token=fake_bearer_token,
340+
)
341+
342+
error = exc_info.value
343+
error_str = str(error)
344+
345+
print(f"\nBogus API root: {bogus_api_root}")
346+
print(f"Error type: {type(error).__name__}")
347+
print(f"Error message: {error_str}")
348+
349+
if isinstance(error, AirbyteError) and hasattr(error, "context"):
350+
context = error.context or {}
351+
print(f"Error context: {context}")
352+
if "request_url" in context:
353+
request_url = str(context["request_url"])
354+
print(f"Request URL from context: {request_url}")
355+
host_from_bogus = bogus_api_root.split("/")[2]
356+
assert host_from_bogus in request_url, (
357+
f"Expected request_url to contain host '{host_from_bogus}' "
358+
f"from bogus_api_root '{bogus_api_root}', but got '{request_url}'"
359+
)
360+
361+
362+
def test_url_construction_with_path_prefix() -> None:
363+
"""Test that the SDK correctly preserves path prefixes in the base URL.
364+
365+
This test uses httpbin.org/anything which echoes back the request details,
366+
allowing us to verify the actual URL that was constructed and sent.
367+
368+
This test validates that when using a custom API root with a path prefix
369+
(like https://host/api/public/v1), the SDK correctly appends endpoints
370+
to that path rather than replacing it.
371+
"""
372+
base_url_with_path = "https://httpbin.org/anything/api/public/v1"
373+
fake_bearer_token = SecretString("fake-token-for-testing")
374+
fake_workspace_id = "00000000-0000-0000-0000-000000000000"
375+
376+
result = api_util.list_sources(
377+
workspace_id=fake_workspace_id,
378+
api_root=base_url_with_path,
379+
client_id=None,
380+
client_secret=None,
381+
bearer_token=fake_bearer_token,
382+
)
383+
384+
print(f"\nBase URL with path prefix: {base_url_with_path}")
385+
print(f"Result type: {type(result)}")
386+
print(f"Result: {result}")
387+
388+
assert result == [], (
389+
f"Expected empty list from httpbin (no real sources), but got: {result}"
390+
)

0 commit comments

Comments
 (0)