Skip to content

Commit 0f5af27

Browse files
committed
fixup! feat(platform): support external token providers and simplify caching
1 parent dd946fe commit 0f5af27

3 files changed

Lines changed: 104 additions & 85 deletions

File tree

specifications/SPEC_PLATFORM_SERVICE.md

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ The Platform Module shall:
3232
- **[FR-11]** Download and verify file integrity using CRC32C checksums for run artifacts
3333
- **[FR-12]** Generate signed URLs for secure Google Cloud Storage access
3434
- **[FR-13]** Provide user and organization information retrieval with sensitive data masking options
35+
- **[FR-14]** Support external token providers to bypass internal OAuth 2.0 flows for machine-to-machine, service account, or custom token lifecycle scenarios.
3536

3637
### 1.3 Non-Functional Requirements
3738

@@ -44,7 +45,6 @@ The Platform Module shall:
4445
### 1.4 Constraints and Limitations
4546

4647
- OAuth 2.0 dependency: Requires external Auth0 service for authentication, creating external dependency
47-
- Browser dependency: Interactive flow requires web browser availability, limiting headless deployment options
4848
- Network dependency: Requires internet connectivity for initial authentication and token validation
4949
- Platform-specific: Designed specifically for Aignostics Platform API integration
5050

@@ -58,6 +58,7 @@ The Platform Module shall:
5858
platform/
5959
├── _service.py # Core service implementation with health monitoring
6060
├── _client.py # API client factory and configuration management
61+
├── _api.py # Authenticated API wrapper (_AuthenticatedApi, _AuthenticatedResource)
6162
├── _authentication.py # OAuth flows and token management
6263
├── _cli.py # Command-line interface for user operations
6364
├── _settings.py # Environment-specific configuration management
@@ -88,7 +89,7 @@ platform/
8889

8990
- **Factory Pattern**: `Client.get_api_client()` creates configured API clients based on environment settings
9091
- **Service Layer Pattern**: Business logic encapsulated in service classes with clean separation from API details
91-
- **Strategy Pattern**: Multiple authentication flows (Authorization Code vs Device Flow) selected based on environment capabilities
92+
- **Strategy Pattern**: Multiple authentication flows (Authorization Code vs Device Flow) selected based on environment capabilities; external token provider as a fully independent alternative strategy
9293
- **Template Method Pattern**: Base authentication flow with specific implementations for different OAuth grant types
9394

9495
---
@@ -107,10 +108,10 @@ platform/
107108

108109
### 3.2 Outputs
109110

110-
| Output Type | Destination | Format/Type | Success Criteria | Code Location |
111-
| ---------------- | ------------------- | ---------------------- | --------------------------------------------------- | ---------------------------------------------------- |
112-
| JWT Access Token | Token cache/memory | String | Valid JWT with required claims and unexpired | `_authentication.py::get_token()` return value |
113-
| API Client | Client applications | PublicApi object | Authenticated and configured for target environment | `_client.py::Client.get_api_client()` factory method |
111+
| Output Type | Destination | Format/Type | Success Criteria | Code Location |
112+
| ---------------- | ------------------- | ---------------------------- | --------------------------------------------------- | ---------------------------------------------------- |
113+
| JWT Access Token | Token cache/memory | String | Valid JWT with required claims and unexpired | `_authentication.py::get_token()` return value |
114+
| API Client | Client applications | `_AuthenticatedApi` object | Authenticated and configured for target environment | `_client.py::Client.get_api_client()` factory method |
114115
| User Information | CLI/Application | UserInfo/Me objects | Complete user and organization data | `_service.py::Service.get_user_info()` method |
115116
| Health Status | Monitoring systems | Health object | Accurate service and dependency status | `_service.py::Service.health()` method |
116117
| Downloaded Files | Local filesystem | Binary/structured data | Verified checksums and complete downloads | `_utils.py` download functions and `ApplicationRun` |
@@ -171,7 +172,9 @@ UserInfo:
171172
172173
```mermaid
173174
graph TD
174-
A[User Request] --> B{Token Cached?}
175+
A[User Request] --> X{External Token Provider?}
176+
X -->|Yes| I[Create API Client with External Provider]
177+
X -->|No| B{Token Cached?}
175178
B -->|Yes| C[Use Cached Token]
176179
B -->|No| D[OAuth Authentication]
177180

@@ -202,7 +205,11 @@ graph TD
202205
class Client:
203206
"""Main client for interacting with the Aignostics Platform API."""
204207

205-
def __init__(self, cache_token: bool = True) -> None:
208+
def __init__(
209+
self,
210+
cache_token: bool = True,
211+
token_provider: Callable[[], str] | None = None,
212+
) -> None:
206213
"""Initializes authenticated API client with resource accessors."""
207214

208215
def me(self) -> Me:
@@ -215,7 +222,10 @@ class Client:
215222
"""Creates ApplicationRun instance for existing run."""
216223

217224
@staticmethod
218-
def get_api_client(cache_token: bool = True) -> PublicApi:
225+
def get_api_client(
226+
cache_token: bool = True,
227+
token_provider: Callable[[], str] | None = None,
228+
) -> _AuthenticatedApi: # internal subclass of PublicApi; exposes token_provider attribute
219229
"""Creates authenticated API client with proper configuration."""
220230
```
221231

@@ -242,10 +252,10 @@ class Service(BaseService):
242252
```
243253

244254
```python
245-
class Applications:
255+
class Applications(_AuthenticatedResource):
246256
"""Resource class for managing applications."""
247257

248-
def __init__(self, api: PublicApi) -> None:
258+
def __init__(self, api: _AuthenticatedApi) -> None:
249259
"""Initializes the Applications resource with the API client."""
250260

251261
def list(self) -> Iterator[Application]:
@@ -257,11 +267,11 @@ class Applications:
257267
```
258268

259269
```python
260-
class Versions:
270+
class Versions(_AuthenticatedResource):
261271
"""Resource class for managing application versions."""
262272

263-
def __init__(self, api: PublicApi) -> None:
264-
"""Initializes the Versions resource with the API client."""
273+
# Constructor inherited from _AuthenticatedResource
274+
# def __init__(self, api: _AuthenticatedApi) -> None:
265275

266276
def list(self, application: Application | str) -> Iterator[ApplicationVersion]:
267277
"""Find all versions for a specific application."""
@@ -277,11 +287,11 @@ class Versions:
277287
```
278288

279289
```python
280-
class Runs:
290+
class Runs(_AuthenticatedResource):
281291
"""Resource class for managing application runs."""
282292

283-
def __init__(self, api: PublicApi) -> None:
284-
"""Initializes the Runs resource with the API client."""
293+
# Constructor inherited from _AuthenticatedResource
294+
# def __init__(self, api: _AuthenticatedApi) -> None:
285295

286296
def create(self, application_version: str, items: list[ItemCreationRequest]) -> ApplicationRun:
287297
"""Creates a new application run."""
@@ -297,10 +307,10 @@ class Runs:
297307
```
298308

299309
```python
300-
class ApplicationRun:
310+
class ApplicationRun(_AuthenticatedResource):
301311
"""Represents a single application run."""
302312

303-
def __init__(self, api: PublicApi, application_run_id: str) -> None:
313+
def __init__(self, api: _AuthenticatedApi, application_run_id: str) -> None:
304314
"""Initializes an ApplicationRun instance."""
305315

306316
@classmethod

0 commit comments

Comments
 (0)