sdkrouter 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sdkrouter/__init__.py +110 -0
- sdkrouter/_api/__init__.py +28 -0
- sdkrouter/_api/client.py +204 -0
- sdkrouter/_api/generated/__init__.py +21 -0
- sdkrouter/_api/generated/cdn/__init__.py +209 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/__init__.py +7 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/client.py +133 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/models.py +163 -0
- sdkrouter/_api/generated/cdn/cdn__api__cdn/sync_client.py +132 -0
- sdkrouter/_api/generated/cdn/client.py +75 -0
- sdkrouter/_api/generated/cdn/logger.py +256 -0
- sdkrouter/_api/generated/cdn/pyproject.toml +55 -0
- sdkrouter/_api/generated/cdn/retry.py +272 -0
- sdkrouter/_api/generated/cdn/sync_client.py +58 -0
- sdkrouter/_api/generated/cleaner/__init__.py +212 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/__init__.py +7 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/client.py +83 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/models.py +117 -0
- sdkrouter/_api/generated/cleaner/cleaner__api__cleaner/sync_client.py +82 -0
- sdkrouter/_api/generated/cleaner/client.py +75 -0
- sdkrouter/_api/generated/cleaner/enums.py +55 -0
- sdkrouter/_api/generated/cleaner/logger.py +256 -0
- sdkrouter/_api/generated/cleaner/pyproject.toml +55 -0
- sdkrouter/_api/generated/cleaner/retry.py +272 -0
- sdkrouter/_api/generated/cleaner/sync_client.py +58 -0
- sdkrouter/_api/generated/keys/__init__.py +212 -0
- sdkrouter/_api/generated/keys/client.py +75 -0
- sdkrouter/_api/generated/keys/enums.py +64 -0
- sdkrouter/_api/generated/keys/keys__api__keys/__init__.py +7 -0
- sdkrouter/_api/generated/keys/keys__api__keys/client.py +150 -0
- sdkrouter/_api/generated/keys/keys__api__keys/models.py +152 -0
- sdkrouter/_api/generated/keys/keys__api__keys/sync_client.py +149 -0
- sdkrouter/_api/generated/keys/logger.py +256 -0
- sdkrouter/_api/generated/keys/pyproject.toml +55 -0
- sdkrouter/_api/generated/keys/retry.py +272 -0
- sdkrouter/_api/generated/keys/sync_client.py +58 -0
- sdkrouter/_api/generated/models/__init__.py +209 -0
- sdkrouter/_api/generated/models/client.py +75 -0
- sdkrouter/_api/generated/models/logger.py +256 -0
- sdkrouter/_api/generated/models/models__api__llm_models/__init__.py +7 -0
- sdkrouter/_api/generated/models/models__api__llm_models/client.py +99 -0
- sdkrouter/_api/generated/models/models__api__llm_models/models.py +206 -0
- sdkrouter/_api/generated/models/models__api__llm_models/sync_client.py +99 -0
- sdkrouter/_api/generated/models/pyproject.toml +55 -0
- sdkrouter/_api/generated/models/retry.py +272 -0
- sdkrouter/_api/generated/models/sync_client.py +58 -0
- sdkrouter/_api/generated/shortlinks/__init__.py +209 -0
- sdkrouter/_api/generated/shortlinks/client.py +75 -0
- sdkrouter/_api/generated/shortlinks/logger.py +256 -0
- sdkrouter/_api/generated/shortlinks/pyproject.toml +55 -0
- sdkrouter/_api/generated/shortlinks/retry.py +272 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/__init__.py +7 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/client.py +137 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/models.py +153 -0
- sdkrouter/_api/generated/shortlinks/shortlinks__api__shortlinks/sync_client.py +136 -0
- sdkrouter/_api/generated/shortlinks/sync_client.py +58 -0
- sdkrouter/_api/generated/vision/__init__.py +212 -0
- sdkrouter/_api/generated/vision/client.py +75 -0
- sdkrouter/_api/generated/vision/enums.py +40 -0
- sdkrouter/_api/generated/vision/logger.py +256 -0
- sdkrouter/_api/generated/vision/pyproject.toml +55 -0
- sdkrouter/_api/generated/vision/retry.py +272 -0
- sdkrouter/_api/generated/vision/sync_client.py +58 -0
- sdkrouter/_api/generated/vision/vision__api__vision/__init__.py +7 -0
- sdkrouter/_api/generated/vision/vision__api__vision/client.py +65 -0
- sdkrouter/_api/generated/vision/vision__api__vision/models.py +138 -0
- sdkrouter/_api/generated/vision/vision__api__vision/sync_client.py +65 -0
- sdkrouter/_client.py +432 -0
- sdkrouter/_config.py +74 -0
- sdkrouter/_constants.py +21 -0
- sdkrouter/_internal/__init__.py +1 -0
- sdkrouter/_types/__init__.py +30 -0
- sdkrouter/_types/cdn.py +27 -0
- sdkrouter/_types/models.py +26 -0
- sdkrouter/_types/ocr.py +24 -0
- sdkrouter/_types/parsed.py +101 -0
- sdkrouter/_types/shortlinks.py +27 -0
- sdkrouter/_types/vision.py +29 -0
- sdkrouter/_version.py +3 -0
- sdkrouter/helpers/__init__.py +13 -0
- sdkrouter/helpers/formatting.py +15 -0
- sdkrouter/helpers/html.py +100 -0
- sdkrouter/helpers/json_cleaner.py +53 -0
- sdkrouter/tools/__init__.py +129 -0
- sdkrouter/tools/cdn.py +285 -0
- sdkrouter/tools/cleaner.py +186 -0
- sdkrouter/tools/keys.py +215 -0
- sdkrouter/tools/models.py +196 -0
- sdkrouter/tools/shortlinks.py +165 -0
- sdkrouter/tools/vision.py +173 -0
- sdkrouter/utils/__init__.py +27 -0
- sdkrouter/utils/parsing.py +109 -0
- sdkrouter/utils/tokens.py +375 -0
- sdkrouter-0.1.1.dist-info/METADATA +411 -0
- sdkrouter-0.1.1.dist-info/RECORD +96 -0
- sdkrouter-0.1.1.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# Auto-generated by DjangoCFG - see CLAUDE.md
|
|
2
|
+
"""
|
|
3
|
+
SDKRouter API - API Client with JWT Management
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
>>> from api import API
|
|
7
|
+
>>>
|
|
8
|
+
>>> api = API('https://api.example.com')
|
|
9
|
+
>>>
|
|
10
|
+
>>> # Set JWT token
|
|
11
|
+
>>> api.set_token('your-jwt-token', 'refresh-token')
|
|
12
|
+
>>>
|
|
13
|
+
>>> # Use API
|
|
14
|
+
>>> async with api:
|
|
15
|
+
... posts = await api.posts.list()
|
|
16
|
+
... user = await api.users.retrieve(1)
|
|
17
|
+
>>>
|
|
18
|
+
>>> # Check authentication
|
|
19
|
+
>>> if api.is_authenticated():
|
|
20
|
+
... # ...
|
|
21
|
+
>>>
|
|
22
|
+
>>> # Get OpenAPI schema path
|
|
23
|
+
>>> schema_path = api.get_schema_path()
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import threading
|
|
29
|
+
from typing import Any
|
|
30
|
+
|
|
31
|
+
import httpx
|
|
32
|
+
|
|
33
|
+
from .client import APIClient
|
|
34
|
+
from .logger import LoggerConfig
|
|
35
|
+
from .retry import RetryConfig
|
|
36
|
+
from .keys__api__keys import KeysKeysAPI
|
|
37
|
+
from . import enums
|
|
38
|
+
# NOTE: Individual enum imports commented out due to invalid Python syntax with dotted names
|
|
39
|
+
# from .enums import APIKeyCreate.permission, APIKeyCreateRequest.permission, APIKeyDetail.permission, APIKeyList.permission
|
|
40
|
+
|
|
41
|
+
TOKEN_KEY = "auth_token"
|
|
42
|
+
REFRESH_TOKEN_KEY = "refresh_token"
|
|
43
|
+
|
|
44
|
+
# Auto-generated by DjangoCFG - see CLAUDE.md
|
|
45
|
+
class API:
|
|
46
|
+
"""
|
|
47
|
+
API Client wrapper with JWT token management.
|
|
48
|
+
|
|
49
|
+
This class provides:
|
|
50
|
+
- Thread-safe JWT token storage
|
|
51
|
+
- Automatic Authorization header injection
|
|
52
|
+
- Context manager support for async operations
|
|
53
|
+
- Optional retry and logging configuration
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
>>> api = API('https://api.example.com')
|
|
57
|
+
>>> api.set_token('jwt-token')
|
|
58
|
+
>>> async with api:
|
|
59
|
+
... users = await api.users.list()
|
|
60
|
+
>>>
|
|
61
|
+
>>> # With retry and logging
|
|
62
|
+
>>> api = API(
|
|
63
|
+
... 'https://api.example.com',
|
|
64
|
+
... retry_config=RetryConfig(max_attempts=5),
|
|
65
|
+
... logger_config=LoggerConfig(enabled=True)
|
|
66
|
+
... )
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
def __init__(
|
|
70
|
+
self,
|
|
71
|
+
base_url: str,
|
|
72
|
+
logger_config: LoggerConfig | None = None,
|
|
73
|
+
retry_config: RetryConfig | None = None,
|
|
74
|
+
**kwargs: Any
|
|
75
|
+
):
|
|
76
|
+
"""
|
|
77
|
+
Initialize API client.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
base_url: Base API URL (e.g., 'https://api.example.com')
|
|
81
|
+
logger_config: Logger configuration (None to disable logging)
|
|
82
|
+
retry_config: Retry configuration (None to disable retry)
|
|
83
|
+
**kwargs: Additional httpx.AsyncClient kwargs
|
|
84
|
+
"""
|
|
85
|
+
self.base_url = base_url.rstrip('/')
|
|
86
|
+
self._kwargs = kwargs
|
|
87
|
+
self._logger_config = logger_config
|
|
88
|
+
self._retry_config = retry_config
|
|
89
|
+
self._token: str | None = None
|
|
90
|
+
self._refresh_token: str | None = None
|
|
91
|
+
self._lock = threading.Lock()
|
|
92
|
+
self._client: APIClient | None = None
|
|
93
|
+
self._init_clients()
|
|
94
|
+
|
|
95
|
+
def _init_clients(self) -> None:
|
|
96
|
+
"""Initialize API client with current token."""
|
|
97
|
+
# Create httpx client with auth header if token exists
|
|
98
|
+
headers = {}
|
|
99
|
+
if self._token:
|
|
100
|
+
headers['Authorization'] = f'Bearer {self._token}'
|
|
101
|
+
|
|
102
|
+
kwargs = {**self._kwargs}
|
|
103
|
+
if headers:
|
|
104
|
+
kwargs['headers'] = headers
|
|
105
|
+
|
|
106
|
+
# Create new APIClient
|
|
107
|
+
self._client = APIClient(
|
|
108
|
+
self.base_url,
|
|
109
|
+
logger_config=self._logger_config,
|
|
110
|
+
retry_config=self._retry_config,
|
|
111
|
+
**kwargs
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def keys_keys(self) -> KeysKeysAPI:
|
|
116
|
+
"""Access keys endpoints."""
|
|
117
|
+
return self._client.keys_keys
|
|
118
|
+
|
|
119
|
+
def get_token(self) -> str | None:
|
|
120
|
+
"""Get current JWT token."""
|
|
121
|
+
with self._lock:
|
|
122
|
+
return self._token
|
|
123
|
+
|
|
124
|
+
def get_refresh_token(self) -> str | None:
|
|
125
|
+
"""Get current refresh token."""
|
|
126
|
+
with self._lock:
|
|
127
|
+
return self._refresh_token
|
|
128
|
+
|
|
129
|
+
def set_token(self, token: str, refresh_token: str | None = None) -> None:
|
|
130
|
+
"""
|
|
131
|
+
Set JWT token and refresh token.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
token: JWT access token
|
|
135
|
+
refresh_token: JWT refresh token (optional)
|
|
136
|
+
"""
|
|
137
|
+
with self._lock:
|
|
138
|
+
self._token = token
|
|
139
|
+
if refresh_token:
|
|
140
|
+
self._refresh_token = refresh_token
|
|
141
|
+
|
|
142
|
+
# Reinitialize clients with new token
|
|
143
|
+
self._init_clients()
|
|
144
|
+
|
|
145
|
+
def clear_tokens(self) -> None:
|
|
146
|
+
"""Clear all tokens."""
|
|
147
|
+
with self._lock:
|
|
148
|
+
self._token = None
|
|
149
|
+
self._refresh_token = None
|
|
150
|
+
|
|
151
|
+
# Reinitialize clients without token
|
|
152
|
+
self._init_clients()
|
|
153
|
+
|
|
154
|
+
def is_authenticated(self) -> bool:
|
|
155
|
+
"""Check if user is authenticated."""
|
|
156
|
+
return self.get_token() is not None
|
|
157
|
+
|
|
158
|
+
def set_base_url(self, url: str) -> None:
|
|
159
|
+
"""
|
|
160
|
+
Update base URL and reinitialize clients.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
url: New base URL
|
|
164
|
+
"""
|
|
165
|
+
self.base_url = url.rstrip('/')
|
|
166
|
+
self._init_clients()
|
|
167
|
+
|
|
168
|
+
def get_base_url(self) -> str:
|
|
169
|
+
"""Get current base URL."""
|
|
170
|
+
return self.base_url
|
|
171
|
+
|
|
172
|
+
def get_schema_path(self) -> str:
|
|
173
|
+
"""
|
|
174
|
+
Get OpenAPI schema path.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Path to the OpenAPI schema JSON file
|
|
178
|
+
|
|
179
|
+
Note:
|
|
180
|
+
The OpenAPI schema is available in the schema.json file.
|
|
181
|
+
You can load it dynamically using:
|
|
182
|
+
```python
|
|
183
|
+
import json
|
|
184
|
+
from pathlib import Path
|
|
185
|
+
|
|
186
|
+
schema_path = Path(__file__).parent / 'schema.json'
|
|
187
|
+
with open(schema_path) as f:
|
|
188
|
+
schema = json.load(f)
|
|
189
|
+
```
|
|
190
|
+
"""
|
|
191
|
+
return './schema.json'
|
|
192
|
+
|
|
193
|
+
async def __aenter__(self) -> 'API':
|
|
194
|
+
"""Async context manager entry."""
|
|
195
|
+
if self._client:
|
|
196
|
+
await self._client.__aenter__()
|
|
197
|
+
return self
|
|
198
|
+
|
|
199
|
+
async def __aexit__(self, *args: Any) -> None:
|
|
200
|
+
"""Async context manager exit."""
|
|
201
|
+
if self._client:
|
|
202
|
+
await self._client.__aexit__(*args)
|
|
203
|
+
|
|
204
|
+
async def close(self) -> None:
|
|
205
|
+
"""Close HTTP client."""
|
|
206
|
+
if self._client:
|
|
207
|
+
await self._client.close()
|
|
208
|
+
|
|
209
|
+
__all__ = [
|
|
210
|
+
"API",
|
|
211
|
+
"APIClient",
|
|
212
|
+
]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from .keys__api__keys import KeysKeysAPI
|
|
8
|
+
from .logger import APILogger, LoggerConfig
|
|
9
|
+
from .retry import RetryConfig, RetryAsyncClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class APIClient:
|
|
13
|
+
"""
|
|
14
|
+
Async API client for SDKRouter API.
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
>>> async with APIClient(base_url='https://api.example.com') as client:
|
|
18
|
+
... users = await client.users.list()
|
|
19
|
+
... post = await client.posts.create(data=new_post)
|
|
20
|
+
>>>
|
|
21
|
+
>>> # With retry configuration
|
|
22
|
+
>>> retry_config = RetryConfig(max_attempts=5, min_wait=2.0)
|
|
23
|
+
>>> async with APIClient(base_url='https://api.example.com', retry_config=retry_config) as client:
|
|
24
|
+
... users = await client.users.list()
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
base_url: str,
|
|
30
|
+
logger_config: Optional[LoggerConfig] = None,
|
|
31
|
+
retry_config: Optional[RetryConfig] = None,
|
|
32
|
+
**kwargs: Any,
|
|
33
|
+
):
|
|
34
|
+
"""
|
|
35
|
+
Initialize API client.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
base_url: Base API URL (e.g., 'https://api.example.com')
|
|
39
|
+
logger_config: Logger configuration (None to disable logging)
|
|
40
|
+
retry_config: Retry configuration (None to disable retry)
|
|
41
|
+
**kwargs: Additional httpx.AsyncClient kwargs
|
|
42
|
+
"""
|
|
43
|
+
self.base_url = base_url.rstrip('/')
|
|
44
|
+
|
|
45
|
+
# Create HTTP client with or without retry
|
|
46
|
+
if retry_config is not None:
|
|
47
|
+
self._client = RetryAsyncClient(
|
|
48
|
+
base_url=self.base_url,
|
|
49
|
+
retry_config=retry_config,
|
|
50
|
+
**kwargs,
|
|
51
|
+
)
|
|
52
|
+
else:
|
|
53
|
+
self._client = httpx.AsyncClient(
|
|
54
|
+
base_url=self.base_url,
|
|
55
|
+
**kwargs,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Initialize logger
|
|
59
|
+
self.logger: Optional[APILogger] = None
|
|
60
|
+
if logger_config is not None:
|
|
61
|
+
self.logger = APILogger(logger_config)
|
|
62
|
+
|
|
63
|
+
# Initialize sub-clients
|
|
64
|
+
self.keys_keys = KeysKeysAPI(self._client)
|
|
65
|
+
|
|
66
|
+
async def __aenter__(self) -> 'APIClient':
|
|
67
|
+
await self._client.__aenter__()
|
|
68
|
+
return self
|
|
69
|
+
|
|
70
|
+
async def __aexit__(self, *args: Any) -> None:
|
|
71
|
+
await self._client.__aexit__(*args)
|
|
72
|
+
|
|
73
|
+
async def close(self) -> None:
|
|
74
|
+
"""Close HTTP client."""
|
|
75
|
+
await self._client.aclose()
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Auto-generated by DjangoCFG - see CLAUDE.md
|
|
2
|
+
from enum import IntEnum, Enum
|
|
3
|
+
|
|
4
|
+
# Python 3.10 compatibility: StrEnum was added in Python 3.11
|
|
5
|
+
# Use str + Enum instead for backward compatibility
|
|
6
|
+
class StrEnum(str, Enum):
|
|
7
|
+
"""String Enum for Python 3.10+ compatibility"""
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class APIKeyCreatePermission(StrEnum):
|
|
12
|
+
"""
|
|
13
|
+
* `read` - Read Only
|
|
14
|
+
* `write` - Read & Write
|
|
15
|
+
* `admin` - Admin
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
READ = "read"
|
|
19
|
+
WRITE = "write"
|
|
20
|
+
ADMIN = "admin"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class APIKeyCreateRequestPermission(StrEnum):
|
|
25
|
+
"""
|
|
26
|
+
* `read` - Read Only
|
|
27
|
+
* `write` - Read & Write
|
|
28
|
+
* `admin` - Admin
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
READ = "read"
|
|
32
|
+
WRITE = "write"
|
|
33
|
+
ADMIN = "admin"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class APIKeyDetailPermission(StrEnum):
|
|
38
|
+
"""
|
|
39
|
+
Permission level for this key
|
|
40
|
+
* `read` - Read Only
|
|
41
|
+
* `write` - Read & Write
|
|
42
|
+
* `admin` - Admin
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
READ = "read"
|
|
46
|
+
WRITE = "write"
|
|
47
|
+
ADMIN = "admin"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class APIKeyListPermission(StrEnum):
|
|
52
|
+
"""
|
|
53
|
+
Permission level for this key
|
|
54
|
+
* `read` - Read Only
|
|
55
|
+
* `write` - Read & Write
|
|
56
|
+
* `admin` - Admin
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
READ = "read"
|
|
60
|
+
WRITE = "write"
|
|
61
|
+
ADMIN = "admin"
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from .models import *
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class KeysKeysAPI:
|
|
9
|
+
"""API endpoints for Keys."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, client: httpx.AsyncClient):
|
|
12
|
+
"""Initialize sub-client with shared httpx client."""
|
|
13
|
+
self._client = client
|
|
14
|
+
|
|
15
|
+
async def list(self, page: int | None = None, page_size: int | None = None) -> list[PaginatedAPIKeyListList]:
|
|
16
|
+
"""
|
|
17
|
+
ViewSet for API key management. Endpoints: - GET /api/keys/ - List
|
|
18
|
+
user's API keys - POST /api/keys/ - Create a new API key - GET
|
|
19
|
+
/api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
|
|
20
|
+
Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
|
|
21
|
+
/api/keys/{id}/reactivate/ - Reactivate API key
|
|
22
|
+
"""
|
|
23
|
+
url = "/api/keys/"
|
|
24
|
+
response = await self._client.get(url, params={"page": page if page is not None else None, "page_size": page_size if page_size is not None else None})
|
|
25
|
+
if not response.is_success:
|
|
26
|
+
try:
|
|
27
|
+
error_body = response.json()
|
|
28
|
+
except Exception:
|
|
29
|
+
error_body = response.text
|
|
30
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
31
|
+
return PaginatedAPIKeyListList.model_validate(response.json())
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
async def create(self, data: APIKeyCreateRequest) -> APIKeyCreate:
|
|
35
|
+
"""
|
|
36
|
+
Create a new API key.
|
|
37
|
+
"""
|
|
38
|
+
url = "/api/keys/"
|
|
39
|
+
response = await self._client.post(url, json=data.model_dump(exclude_unset=True))
|
|
40
|
+
if not response.is_success:
|
|
41
|
+
try:
|
|
42
|
+
error_body = response.json()
|
|
43
|
+
except Exception:
|
|
44
|
+
error_body = response.text
|
|
45
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
46
|
+
return APIKeyCreate.model_validate(response.json())
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def retrieve(self, id: str) -> APIKeyDetail:
|
|
50
|
+
"""
|
|
51
|
+
ViewSet for API key management. Endpoints: - GET /api/keys/ - List
|
|
52
|
+
user's API keys - POST /api/keys/ - Create a new API key - GET
|
|
53
|
+
/api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
|
|
54
|
+
Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
|
|
55
|
+
/api/keys/{id}/reactivate/ - Reactivate API key
|
|
56
|
+
"""
|
|
57
|
+
url = f"/api/keys/{id}/"
|
|
58
|
+
response = await self._client.get(url)
|
|
59
|
+
if not response.is_success:
|
|
60
|
+
try:
|
|
61
|
+
error_body = response.json()
|
|
62
|
+
except Exception:
|
|
63
|
+
error_body = response.text
|
|
64
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
65
|
+
return APIKeyDetail.model_validate(response.json())
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async def update(self, id: str) -> APIKeyList:
|
|
69
|
+
"""
|
|
70
|
+
ViewSet for API key management. Endpoints: - GET /api/keys/ - List
|
|
71
|
+
user's API keys - POST /api/keys/ - Create a new API key - GET
|
|
72
|
+
/api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
|
|
73
|
+
Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
|
|
74
|
+
/api/keys/{id}/reactivate/ - Reactivate API key
|
|
75
|
+
"""
|
|
76
|
+
url = f"/api/keys/{id}/"
|
|
77
|
+
response = await self._client.put(url)
|
|
78
|
+
if not response.is_success:
|
|
79
|
+
try:
|
|
80
|
+
error_body = response.json()
|
|
81
|
+
except Exception:
|
|
82
|
+
error_body = response.text
|
|
83
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
84
|
+
return APIKeyList.model_validate(response.json())
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
async def partial_update(self, id: str) -> APIKeyList:
|
|
88
|
+
"""
|
|
89
|
+
ViewSet for API key management. Endpoints: - GET /api/keys/ - List
|
|
90
|
+
user's API keys - POST /api/keys/ - Create a new API key - GET
|
|
91
|
+
/api/keys/{id}/ - Get API key details - DELETE /api/keys/{id}/ -
|
|
92
|
+
Deactivate API key - POST /api/keys/{id}/rotate/ - Rotate API key - POST
|
|
93
|
+
/api/keys/{id}/reactivate/ - Reactivate API key
|
|
94
|
+
"""
|
|
95
|
+
url = f"/api/keys/{id}/"
|
|
96
|
+
response = await self._client.patch(url)
|
|
97
|
+
if not response.is_success:
|
|
98
|
+
try:
|
|
99
|
+
error_body = response.json()
|
|
100
|
+
except Exception:
|
|
101
|
+
error_body = response.text
|
|
102
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
103
|
+
return APIKeyList.model_validate(response.json())
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
async def destroy(self, id: str) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Deactivate an API key (soft delete).
|
|
109
|
+
"""
|
|
110
|
+
url = f"/api/keys/{id}/"
|
|
111
|
+
response = await self._client.delete(url)
|
|
112
|
+
if not response.is_success:
|
|
113
|
+
try:
|
|
114
|
+
error_body = response.json()
|
|
115
|
+
except Exception:
|
|
116
|
+
error_body = response.text
|
|
117
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
118
|
+
return None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
async def reactivate_create(self, id: str) -> APIKeyList:
|
|
122
|
+
"""
|
|
123
|
+
Reactivate a deactivated API key.
|
|
124
|
+
"""
|
|
125
|
+
url = f"/api/keys/{id}/reactivate/"
|
|
126
|
+
response = await self._client.post(url)
|
|
127
|
+
if not response.is_success:
|
|
128
|
+
try:
|
|
129
|
+
error_body = response.json()
|
|
130
|
+
except Exception:
|
|
131
|
+
error_body = response.text
|
|
132
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
133
|
+
return APIKeyList.model_validate(response.json())
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
async def rotate_create(self, id: str) -> APIKeyList:
|
|
137
|
+
"""
|
|
138
|
+
Rotate an API key (generate new key).
|
|
139
|
+
"""
|
|
140
|
+
url = f"/api/keys/{id}/rotate/"
|
|
141
|
+
response = await self._client.post(url)
|
|
142
|
+
if not response.is_success:
|
|
143
|
+
try:
|
|
144
|
+
error_body = response.json()
|
|
145
|
+
except Exception:
|
|
146
|
+
error_body = response.text
|
|
147
|
+
raise httpx.HTTPStatusError(f"{response.status_code}: {error_body}", request=response.request, response=response)
|
|
148
|
+
return APIKeyList.model_validate(response.json())
|
|
149
|
+
|
|
150
|
+
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Auto-generated by DjangoCFG - see CLAUDE.md
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
|
+
|
|
9
|
+
from ..enums import APIKeyCreatePermission, APIKeyCreateRequestPermission, APIKeyDetailPermission, APIKeyListPermission
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class APIKeyList(BaseModel):
|
|
13
|
+
"""
|
|
14
|
+
Serializer for listing API keys (no sensitive data).
|
|
15
|
+
|
|
16
|
+
Response model (includes read-only fields).
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
model_config = ConfigDict(
|
|
20
|
+
validate_assignment=True,
|
|
21
|
+
extra="allow",
|
|
22
|
+
frozen=False,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
id: Any = ...
|
|
26
|
+
name: Any = Field(description='Human-readable name for the key')
|
|
27
|
+
key_prefix: Any = Field(description="Visible prefix for identification (e.g., 'sk_live_abc...')")
|
|
28
|
+
permission: APIKeyListPermission = Field(description='Permission level for this key\n\n* `read` - Read Only\n* `write` - Read & Write\n* `admin` - Admin')
|
|
29
|
+
is_active: bool = Field(description='Whether this key can be used')
|
|
30
|
+
is_expired: bool = ...
|
|
31
|
+
is_valid: bool = ...
|
|
32
|
+
is_quota_exceeded: bool = ...
|
|
33
|
+
rate_limit_rpm: int | None = Field(None, description='Max requests per minute (null = unlimited)')
|
|
34
|
+
rate_limit_rpd: int | None = Field(None, description='Max requests per day (null = unlimited)')
|
|
35
|
+
quota_monthly_usd: Any | None = Field(None, description='Monthly spending limit in USD (null = unlimited)', pattern='^-?\\d{0,8}(?:\\.\\d{0,2})?$')
|
|
36
|
+
quota_used_usd: Any = Field(description='Amount spent this month in USD', pattern='^-?\\d{0,4}(?:\\.\\d{0,6})?$')
|
|
37
|
+
last_used_at: Any | None = Field(None, description='Last time this key was used')
|
|
38
|
+
total_requests: int = Field(description='Total number of requests made with this key')
|
|
39
|
+
total_cost_usd: Any = Field(description='Total cost of all requests in USD', pattern='^-?\\d{0,6}(?:\\.\\d{0,6})?$')
|
|
40
|
+
expires_at: Any | None = Field(None, description='When this key expires (null = never)')
|
|
41
|
+
created_at: Any = ...
|
|
42
|
+
updated_at: Any = ...
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class PaginatedAPIKeyListList(BaseModel):
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
Response model (includes read-only fields).
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
model_config = ConfigDict(
|
|
53
|
+
validate_assignment=True,
|
|
54
|
+
extra="allow",
|
|
55
|
+
frozen=False,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
count: int = Field(description='Total number of items across all pages')
|
|
59
|
+
page: int = Field(description='Current page number (1-based)')
|
|
60
|
+
pages: int = Field(description='Total number of pages')
|
|
61
|
+
page_size: int = Field(description='Number of items per page')
|
|
62
|
+
has_next: bool = Field(description='Whether there is a next page')
|
|
63
|
+
has_previous: bool = Field(description='Whether there is a previous page')
|
|
64
|
+
next_page: int | None = Field(None, description='Next page number (null if no next page)')
|
|
65
|
+
previous_page: int | None = Field(None, description='Previous page number (null if no previous page)')
|
|
66
|
+
results: list[APIKeyList] = Field(description='Array of items for current page')
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class APIKeyCreateRequest(BaseModel):
|
|
71
|
+
"""
|
|
72
|
+
Serializer for creating new API keys.
|
|
73
|
+
|
|
74
|
+
Request model (no read-only fields).
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
model_config = ConfigDict(
|
|
78
|
+
validate_assignment=True,
|
|
79
|
+
extra="allow",
|
|
80
|
+
frozen=False,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
name: str = Field(min_length=1, max_length=100)
|
|
84
|
+
permission: APIKeyCreateRequestPermission | None = Field(None, description='* `read` - Read Only\n* `write` - Read & Write\n* `admin` - Admin')
|
|
85
|
+
rate_limit_rpm: int | None = Field(None, ge=1)
|
|
86
|
+
rate_limit_rpd: int | None = Field(None, ge=1)
|
|
87
|
+
quota_monthly_usd: str | None = Field(None, pattern='^-?\\d{0,8}(?:\\.\\d{0,2})?$')
|
|
88
|
+
expires_at: str | None = None
|
|
89
|
+
metadata: dict[str, Any] | None = None
|
|
90
|
+
is_test: bool | None = None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class APIKeyCreate(BaseModel):
|
|
95
|
+
"""
|
|
96
|
+
Serializer for creating new API keys.
|
|
97
|
+
|
|
98
|
+
Response model (includes read-only fields).
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
model_config = ConfigDict(
|
|
102
|
+
validate_assignment=True,
|
|
103
|
+
extra="allow",
|
|
104
|
+
frozen=False,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
name: str = Field(max_length=100)
|
|
108
|
+
permission: APIKeyCreatePermission | None = Field(None, description='* `read` - Read Only\n* `write` - Read & Write\n* `admin` - Admin')
|
|
109
|
+
rate_limit_rpm: int | None = Field(None, ge=1)
|
|
110
|
+
rate_limit_rpd: int | None = Field(None, ge=1)
|
|
111
|
+
quota_monthly_usd: str | None = Field(None, pattern='^-?\\d{0,8}(?:\\.\\d{0,2})?$')
|
|
112
|
+
expires_at: str | None = None
|
|
113
|
+
metadata: dict[str, Any] | None = None
|
|
114
|
+
is_test: bool | None = None
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class APIKeyDetail(BaseModel):
|
|
119
|
+
"""
|
|
120
|
+
Serializer for API key details.
|
|
121
|
+
|
|
122
|
+
Response model (includes read-only fields).
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
model_config = ConfigDict(
|
|
126
|
+
validate_assignment=True,
|
|
127
|
+
extra="allow",
|
|
128
|
+
frozen=False,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
id: Any = ...
|
|
132
|
+
name: Any = Field(description='Human-readable name for the key')
|
|
133
|
+
key_prefix: Any = Field(description="Visible prefix for identification (e.g., 'sk_live_abc...')")
|
|
134
|
+
permission: APIKeyDetailPermission = Field(description='Permission level for this key\n\n* `read` - Read Only\n* `write` - Read & Write\n* `admin` - Admin')
|
|
135
|
+
is_active: bool = Field(description='Whether this key can be used')
|
|
136
|
+
is_expired: bool = ...
|
|
137
|
+
is_valid: bool = ...
|
|
138
|
+
is_quota_exceeded: bool = ...
|
|
139
|
+
rate_limit_rpm: int | None = Field(None, description='Max requests per minute (null = unlimited)')
|
|
140
|
+
rate_limit_rpd: int | None = Field(None, description='Max requests per day (null = unlimited)')
|
|
141
|
+
quota_monthly_usd: Any | None = Field(None, description='Monthly spending limit in USD (null = unlimited)', pattern='^-?\\d{0,8}(?:\\.\\d{0,2})?$')
|
|
142
|
+
quota_used_usd: Any = Field(description='Amount spent this month in USD', pattern='^-?\\d{0,4}(?:\\.\\d{0,6})?$')
|
|
143
|
+
last_used_at: Any | None = Field(None, description='Last time this key was used')
|
|
144
|
+
total_requests: int = Field(description='Total number of requests made with this key')
|
|
145
|
+
total_cost_usd: Any = Field(description='Total cost of all requests in USD', pattern='^-?\\d{0,6}(?:\\.\\d{0,6})?$')
|
|
146
|
+
expires_at: Any | None = Field(None, description='When this key expires (null = never)')
|
|
147
|
+
created_at: Any = ...
|
|
148
|
+
updated_at: Any = ...
|
|
149
|
+
metadata: dict[str, Any] | None = Field(None, description='Additional metadata (e.g., allowed IPs, allowed models)')
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|