Skip to content

Commit f2296b9

Browse files
committed
fixup! feat(platform): support external token providers and simplify caching
1 parent 7e3fac5 commit f2296b9

File tree

4 files changed

+45
-4
lines changed

4 files changed

+45
-4
lines changed

src/aignostics/platform/_client.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class Client:
6767
6868
- Provides access to platform resources like applications, versions, and runs.
6969
- Handles authentication and API client configuration.
70+
- Supports external token providers for machine-to-machine or custom auth flows.
7071
- Retries on network and server errors for specific operations.
7172
- Caches operation results for specific operations.
7273
"""
@@ -88,8 +89,9 @@ def __init__(self, cache_token: bool = True, token_provider: Callable[[], str] |
8889
Ignored when ``token_provider`` is supplied.
8990
token_provider: Optional external token provider callable. When provided,
9091
bypasses internal OAuth authentication entirely. The callable must
91-
return a valid bearer token string. When set, ``cache_token`` has no
92-
effect because the external provider manages its own token lifecycle.
92+
return a raw access token string (without the ``Bearer `` prefix).
93+
When set, ``cache_token`` has no effect because the external provider
94+
manages its own token lifecycle.
9395
9496
Sets up resource accessors for applications, versions, and runs.
9597
"""
@@ -292,7 +294,10 @@ def get_api_client(cache_token: bool = True, token_provider: Callable[[], str] |
292294
client.user_agent = user_agent()
293295
api_client = _AuthenticatedApi(client, effective_provider)
294296

295-
# Store in the appropriate singleton cache
297+
# Store in the appropriate singleton cache.
298+
# For external providers we use a simple bounded dict rather than LRU:
299+
# switching providers is rare in practice, and a full clear is simpler
300+
# than tracking access order while still bounding memory.
296301
if token_provider is not None:
297302
if len(Client._api_client_external) >= _MAX_EXTERNAL_CLIENTS:
298303
logger.warning(

src/aignostics/platform/_operation_cache.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from __future__ import annotations
1515

16+
import functools
1617
import hashlib
1718
import time
1819
import typing as t
@@ -121,6 +122,7 @@ def cached_operation(
121122
"""
122123

123124
def decorator(func: Callable[P, T]) -> Callable[P, T]:
125+
@functools.wraps(func)
124126
def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
125127
# Check if nocache is requested and remove it from kwargs before passing to func
126128
nocache = kwargs.pop("nocache", False)

src/aignostics/platform/resources/runs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ class Run(_AuthenticatedResource):
112112
"""
113113

114114
def __init__(self, api: _AuthenticatedApi, run_id: str) -> None:
115-
"""Initializes an Run instance.
115+
"""Initializes a Run instance.
116116
117117
Args:
118118
api (_AuthenticatedApi): The configured API client.

tests/aignostics/platform/client_token_provider_test.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,3 +228,37 @@ def test_external_provider_cache_bounded() -> None:
228228
mock_logger.warning.assert_called()
229229
warning_msg = mock_logger.warning.call_args[0][0]
230230
assert "resource leak" in warning_msg
231+
232+
233+
# --- Integration tests ---
234+
235+
236+
@pytest.mark.integration
237+
def test_external_provider_wires_through_to_resources() -> None:
238+
"""Integration: Client(token_provider=...) wires through real constructors.
239+
240+
Verifies that an external token provider flows through Client → _AuthenticatedApi →
241+
resource classes (Applications, Runs) without any AttributeError. Only the
242+
ApiClient constructor is mocked to avoid real HTTP calls.
243+
"""
244+
my_provider = _make_provider("integration-test-token")
245+
246+
with patch("aignostics.platform._client.ApiClient") as mock_api_client_cls:
247+
# Create client with external provider — real _AuthenticatedApi and
248+
# resource constructors (_AuthenticatedResource.__init__) run.
249+
client = Client(token_provider=my_provider)
250+
251+
# Verify the provider is wired through the real _AuthenticatedApi
252+
assert isinstance(client._api, _AuthenticatedApi)
253+
assert client._api.token_provider is my_provider
254+
255+
# Verify resources received the same _AuthenticatedApi instance
256+
assert client.applications._api is client._api
257+
assert client.runs._api is client._api
258+
assert client.versions._api is client._api
259+
260+
# Verify the Configuration passed to ApiClient produces the correct auth header
261+
config = mock_api_client_cls.call_args[0][0]
262+
assert isinstance(config, _OAuth2TokenProviderConfiguration)
263+
auth = config.auth_settings()
264+
assert auth["OAuth2AuthorizationCodeBearer"]["value"] == "Bearer integration-test-token"

0 commit comments

Comments
 (0)