|
1 | | -import base64 |
2 | 1 | import json |
3 | | -from typing import Any, Dict, List, Optional |
| 2 | +from typing import Any, Dict |
4 | 3 |
|
5 | 4 | import httpx |
6 | 5 |
|
7 | | -OAUTH_BASE_URL = "https://oauth.openapi.it" |
8 | | -TEST_OAUTH_BASE_URL = "https://test.oauth.openapi.it" |
9 | | - |
10 | | - |
11 | | -class OauthClient: |
12 | | - """ |
13 | | - Synchronous client for handling Openapi authentication and token management. |
14 | | - """ |
15 | | - |
16 | | - def __init__(self, username: str, apikey: str, test: bool = False): |
17 | | - self.client = httpx.Client() |
18 | | - self.url: str = TEST_OAUTH_BASE_URL if test else OAUTH_BASE_URL |
19 | | - self.auth_header: str = ( |
20 | | - "Basic " + base64.b64encode(f"{username}:{apikey}".encode("utf-8")).decode() |
21 | | - ) |
22 | | - self.headers: Dict[str, Any] = { |
23 | | - "Authorization": self.auth_header, |
24 | | - "Content-Type": "application/json", |
25 | | - } |
26 | | - |
27 | | - def __enter__(self): |
28 | | - """Enable use as a synchronous context manager.""" |
29 | | - return self |
30 | | - |
31 | | - def __exit__(self, exc_type, exc_val, exc_tb): |
32 | | - """Ensure the underlying HTTP client is closed on exit.""" |
33 | | - self.client.close() |
34 | | - |
35 | | - def close(self): |
36 | | - """Manually close the underlying HTTP client.""" |
37 | | - self.client.close() |
38 | | - |
39 | | - def get_scopes(self, limit: bool = False) -> Dict[str, Any]: |
40 | | - """Retrieve available scopes for the current user.""" |
41 | | - params = {"limit": int(limit)} |
42 | | - url = f"{self.url}/scopes" |
43 | | - return self.client.get(url=url, headers=self.headers, params=params).json() |
44 | | - |
45 | | - def create_token(self, scopes: List[str] = [], ttl: int = 0) -> Dict[str, Any]: |
46 | | - """Create a new bearer token with specified scopes and TTL.""" |
47 | | - payload = {"scopes": scopes, "ttl": ttl} |
48 | | - url = f"{self.url}/token" |
49 | | - return self.client.post(url=url, headers=self.headers, json=payload).json() |
50 | | - |
51 | | - def get_token(self, scope: str = None) -> Dict[str, Any]: |
52 | | - """Retrieve an existing token, optionally filtered by scope.""" |
53 | | - params = {"scope": scope or ""} |
54 | | - url = f"{self.url}/token" |
55 | | - return self.client.get(url=url, headers=self.headers, params=params).json() |
56 | | - |
57 | | - def delete_token(self, id: str) -> Dict[str, Any]: |
58 | | - """Revoke/Delete a specific token by ID.""" |
59 | | - url = f"{self.url}/token/{id}" |
60 | | - return self.client.delete(url=url, headers=self.headers).json() |
61 | | - |
62 | | - def get_counters(self, period: str, date: str) -> Dict[str, Any]: |
63 | | - """Retrieve usage counters for a specific period and date.""" |
64 | | - url = f"{self.url}/counters/{period}/{date}" |
65 | | - return self.client.get(url=url, headers=self.headers).json() |
66 | | - |
67 | | - |
68 | | -class AsyncOauthClient: |
69 | | - """ |
70 | | - Asynchronous client for handling Openapi authentication and token management. |
71 | | - Suitable for use with FastAPI, aiohttp, etc. |
72 | | - """ |
73 | | - |
74 | | - def __init__(self, username: str, apikey: str, test: bool = False): |
75 | | - self.client = httpx.AsyncClient() |
76 | | - self.url: str = TEST_OAUTH_BASE_URL if test else OAUTH_BASE_URL |
77 | | - self.auth_header: str = ( |
78 | | - "Basic " + base64.b64encode(f"{username}:{apikey}".encode("utf-8")).decode() |
79 | | - ) |
80 | | - self.headers: Dict[str, Any] = { |
81 | | - "Authorization": self.auth_header, |
82 | | - "Content-Type": "application/json", |
83 | | - } |
84 | | - |
85 | | - async def __aenter__(self): |
86 | | - """Enable use as an asynchronous context manager.""" |
87 | | - return self |
88 | | - |
89 | | - async def __aexit__(self, exc_type, exc_val, exc_tb): |
90 | | - """Ensure the underlying HTTP client is closed on exit (async).""" |
91 | | - await self.client.aclose() |
92 | | - |
93 | | - async def aclose(self): |
94 | | - """Manually close the underlying HTTP client (async).""" |
95 | | - await self.client.aclose() |
96 | | - |
97 | | - async def get_scopes(self, limit: bool = False) -> Dict[str, Any]: |
98 | | - """Retrieve available scopes for the current user (async).""" |
99 | | - params = {"limit": int(limit)} |
100 | | - url = f"{self.url}/scopes" |
101 | | - resp = await self.client.get(url=url, headers=self.headers, params=params) |
102 | | - return resp.json() |
103 | | - |
104 | | - async def create_token(self, scopes: List[str] = [], ttl: int = 0) -> Dict[str, Any]: |
105 | | - """Create a new bearer token with specified scopes and TTL (async).""" |
106 | | - payload = {"scopes": scopes, "ttl": ttl} |
107 | | - url = f"{self.url}/token" |
108 | | - resp = await self.client.post(url=url, headers=self.headers, json=payload) |
109 | | - return resp.json() |
110 | | - |
111 | | - async def get_token(self, scope: str = None) -> Dict[str, Any]: |
112 | | - """Retrieve an existing token, optionally filtered by scope (async).""" |
113 | | - params = {"scope": scope or ""} |
114 | | - url = f"{self.url}/token" |
115 | | - resp = await self.client.get(url=url, headers=self.headers, params=params) |
116 | | - return resp.json() |
117 | | - |
118 | | - async def delete_token(self, id: str) -> Dict[str, Any]: |
119 | | - """Revoke/Delete a specific token by ID (async).""" |
120 | | - url = f"{self.url}/token/{id}" |
121 | | - resp = await self.client.delete(url=url, headers=self.headers) |
122 | | - return resp.json() |
123 | | - |
124 | | - async def get_counters(self, period: str, date: str) -> Dict[str, Any]: |
125 | | - """Retrieve usage counters for a specific period and date (async).""" |
126 | | - url = f"{self.url}/counters/{period}/{date}" |
127 | | - resp = await self.client.get(url=url, headers=self.headers) |
128 | | - return resp.json() |
| 6 | +# Backward compatibility imports |
| 7 | +from .async_client import AsyncClient |
| 8 | +from .async_oauth_client import AsyncOauthClient |
| 9 | +from .oauth_client import OauthClient |
129 | 10 |
|
130 | 11 |
|
131 | 12 | class Client: |
@@ -174,64 +55,6 @@ def request( |
174 | 55 | params=params, |
175 | 56 | ).json() |
176 | 57 |
|
177 | | - # Handle cases where the API might return a JSON-encoded string instead of an object |
178 | | - if isinstance(data, str): |
179 | | - try: |
180 | | - data = json.loads(data) |
181 | | - except json.JSONDecodeError: |
182 | | - pass |
183 | | - |
184 | | - return data |
185 | | - |
186 | | - |
187 | | -class AsyncClient: |
188 | | - """ |
189 | | - Asynchronous client for making authenticated requests to Openapi endpoints. |
190 | | - Suitable for use with FastAPI, aiohttp, etc. |
191 | | - """ |
192 | | - |
193 | | - def __init__(self, token: str): |
194 | | - self.client = httpx.AsyncClient() |
195 | | - self.auth_header: str = f"Bearer {token}" |
196 | | - self.headers: Dict[str, str] = { |
197 | | - "Authorization": self.auth_header, |
198 | | - "Content-Type": "application/json", |
199 | | - } |
200 | | - |
201 | | - async def __aenter__(self): |
202 | | - """Enable use as an asynchronous context manager.""" |
203 | | - return self |
204 | | - |
205 | | - async def __aexit__(self, exc_type, exc_val, exc_tb): |
206 | | - """Ensure the underlying HTTP client is closed on exit (async).""" |
207 | | - await self.client.aclose() |
208 | | - |
209 | | - async def aclose(self): |
210 | | - """Manually close the underlying HTTP client (async).""" |
211 | | - await self.client.aclose() |
212 | | - |
213 | | - async def request( |
214 | | - self, |
215 | | - method: str = "GET", |
216 | | - url: str = None, |
217 | | - payload: Dict[str, Any] = None, |
218 | | - params: Dict[str, Any] = None, |
219 | | - ) -> Dict[str, Any]: |
220 | | - """ |
221 | | - Make an asynchronous HTTP request to the specified Openapi endpoint. |
222 | | - """ |
223 | | - payload = payload or {} |
224 | | - params = params or {} |
225 | | - url = url or "" |
226 | | - resp = await self.client.request( |
227 | | - method=method, |
228 | | - url=url, |
229 | | - headers=self.headers, |
230 | | - json=payload, |
231 | | - params=params, |
232 | | - ) |
233 | | - data = resp.json() |
234 | | - |
235 | 58 | # Handle cases where the API might return a JSON-encoded string instead of an object |
236 | 59 | if isinstance(data, str): |
237 | 60 | try: |
|
0 commit comments