Skip to content

Commit 2e1cd07

Browse files
committed
Add v1.1.0
1 parent 075dd2b commit 2e1cd07

29 files changed

+571
-108
lines changed

.flake8

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
[flake8]
2-
max-line-length = 110
2+
max-line-length = 79
33
exclude = .git,__pycache__,__init__.py,.mypy_cache,.pytest_cache

.github/workflows/codeql-analysis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,11 @@ jobs:
4848
# If you wish to specify custom queries, you can do so here or in a config file.
4949
# By default, queries listed here will override any specified in a config file.
5050
# Prefix the list here with "+" to use these queries and those in the config file.
51-
51+
5252
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
5353
# queries: security-extended,security-and-quality
5454

55-
55+
5656
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
5757
# If this step fails, then you should remove it and run the build manually (see below)
5858
- name: Autobuild
@@ -61,7 +61,7 @@ jobs:
6161
# ℹ️ Command-line programs to run using the OS shell.
6262
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
6363

64-
# If the Autobuild fails above, remove it and uncomment the following three lines.
64+
# If the Autobuild fails above, remove it and uncomment the following three lines.
6565
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
6666

6767
# - run: |

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
*.pyc
22
htmlcov
33
.coverage
4+
coverage.xml

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ repos:
1717
rev: 22.3.0
1818
hooks:
1919
- id: black
20-
args: ["--line-length=110"]
20+
args: ["--line-length=79"]
2121

2222
- repo: https://github.com/pycqa/flake8
2323
rev: 4.0.1
2424
hooks:
2525
- id: flake8
26-
args: ["--max-line-length=110", "--ignore=E203,E501,W503", "--exclude=.git,__pycache__,__init__.py,.mypy_cache,.pytest_cache"]
26+
args: ["--max-line-length=79", "--ignore=E203,E501,W503", "--exclude=.git,__pycache__,__init__.py,.mypy_cache,.pytest_cache"]
2727

2828
- repo: https://github.com/Lucas-C/pre-commit-hooks-bandit
2929
rev: v1.0.5

CHANGELOG.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
Changelog
22
---------
33

4-
1.0.0
4+
## 1.0.0
55

6-
* Initial release.
6+
- Initial release
7+
8+
## 1.1.0
9+
10+
- Add `metrics_get` endpoint
11+
- Add definition `ConfigApiAuthAuth0Tenant`
12+
- Mod extends login tests
13+
- Mod allows v10.12.0 SRT api (sent_unique__bytes > sent_unique_bytes, recv_loss__bytes > recv_loss_bytes)
14+
- Mod allows `auth0_token` (login)
15+
- Mod `about` is now deprecated. Please use `about_get`
16+
- Mod `metrics` is now deprecated. Please use `metrics_post`
17+
- Add `memory_limit_mbytes` to config.debug
18+
- Fix `access_token` and `refresh_token` parameters (login)
19+
- Fix SRT testing (requires core v10.10+)

README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# datarhei Core PyClient
2-
For rapid development of Python applications around the [datarhei Core](https://github.com/datarhei/core).
3-
Requires Python 3.7+ and datarhei Core v16.9+.
2+
For rapid development of Python applications around the [datarhei Core](https://github.com/datarhei/core).
3+
Requires Python 3.7+ and datarhei Core v16.10+.
44

55
---
66

@@ -107,7 +107,7 @@ asyncio.run(main())
107107

108108
### Cluster
109109

110-
***Do not use in production!***
110+
***Do not use in production!***
111111
*This is an upcoming feature. More here: [Core v16.10#cluster](https://github.com/datarhei/core/tree/cluster))*
112112

113113

@@ -216,10 +216,16 @@ asyncio.run(main())
216216

217217
### Metrics
218218

219+
- `GET` /api/v3/metrics
220+
221+
```python
222+
v3_metrics_get()
223+
```
224+
219225
- `POST` /api/v3/metrics
220226

221227
```python
222-
v3_metrics(config: Metrics)
228+
v3_metrics_post(config: Metrics)
223229
```
224230
*Model: [Metrics](https://github.com/datarhei/core-client-python/blob/main/core_client/base/models/v3/metrics.py)*
225231

@@ -604,7 +610,7 @@ $ pre-commit run --all-files
604610

605611
## Contributing
606612

607-
Found a mistake or misconduct? Create a [issue](https://github.com/datarhei/core-client-python/issues) or send a pull-request.
613+
Found a mistake or misconduct? Create a [issue](https://github.com/datarhei/core-client-python/issues) or send a pull-request.
608614
Suggestions for improvement are welcome.
609615

610616
## Licence

core_client/__init__.py

Lines changed: 117 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@
66
import json
77
from datetime import datetime
88
from httpx import InvalidURL as HttpInvalidURL, HTTPError
9-
from pydantic import HttpUrl, ValidationError as PydanticValidationError, validate_arguments
9+
from pydantic import (
10+
HttpUrl,
11+
ValidationError as PydanticValidationError,
12+
validate_arguments,
13+
)
1014

1115
from . import base
1216
from .models import Client as ClientModel
@@ -22,6 +26,7 @@ def __init__(
2226
password: str = None,
2327
access_token: str = None,
2428
refresh_token: str = None,
29+
auth0_token: str = None,
2530
retries: int = 3,
2631
timeout: float = 10.0,
2732
):
@@ -37,72 +42,143 @@ def __init__(
3742
self.password = password
3843
self.access_token = access_token
3944
self.refresh_token = refresh_token
45+
self.access_token_expires_at = None
46+
self.auth0_token = auth0_token
4047
self.retries = retries
4148
self.timeout = timeout
4249

43-
def _access_token(self, response: Token = None):
50+
def _basic_login(self):
51+
r_login = httpx.post(
52+
url=f"{self.base_url}/api/login",
53+
json={
54+
"username": f"{self.username}",
55+
"password": f"{self.password}",
56+
},
57+
# failed login delay: 5s
58+
timeout=10.0,
59+
)
60+
if r_login.status_code == 200:
61+
try:
62+
response = Token(**r_login.json())
63+
self._set_access_token_expires_at(response)
64+
self.refresh_token = response.refresh_token
65+
return response
66+
except PydanticValidationError:
67+
return Token()
68+
else:
69+
raise HTTPError("Authorization failed")
70+
71+
def _auth0_login(self):
72+
_headers = self.headers
73+
_headers["authorization"] = f"Bearer {self.auth0_token}"
74+
r_login = httpx.post(
75+
url=f"{self.base_url}/api/login",
76+
headers=_headers,
77+
# failed login delay: 5s
78+
timeout=10.0,
79+
)
80+
if r_login.status_code == 200:
81+
try:
82+
response = Token(**r_login.json())
83+
self._set_access_token_expires_at(response)
84+
self.refresh_token = response.refresh_token
85+
return response
86+
except PydanticValidationError:
87+
return Token()
88+
else:
89+
raise HTTPError("Authorization failed")
90+
91+
def _set_access_token_expires_at(self, response: Token = None):
4492
if response:
45-
self.token_expire = json.loads(
46-
(base64.b64decode(response.access_token.split(".")[1])).decode("utf-8")
47-
)["exp"]
93+
try:
94+
self.access_token_expires_at = json.loads(
95+
(
96+
base64.b64decode(response.access_token.split(".")[1])
97+
).decode("utf-8")
98+
)["exp"]
99+
except (AttributeError, IndexError):
100+
raise HTTPError("Authorization failed")
48101
self.access_token = response.access_token
102+
return self.access_token_expires_at
49103

50-
def _refresh_token(self):
104+
def _access_token_is_expired(self):
105+
if not self.access_token_expires_at:
106+
self._set_access_token_expires_at(
107+
Token(access_token=self.access_token)
108+
)
109+
if (
110+
datetime.fromtimestamp(self.access_token_expires_at)
111+
> datetime.now()
112+
):
113+
return False
114+
return True
115+
116+
def _refresh_access_token(self):
51117
if self.refresh_token:
52118
_headers = self.headers
53119
_headers["authorization"] = f"Bearer {self.refresh_token}"
54-
r_refresh_token = httpx.get(
120+
r_refresh_access_token = httpx.get(
55121
url=f"{self.base_url}/api/login/refresh",
56122
headers=_headers,
57123
# failed login delay: 5s
58124
timeout=10.0,
59125
)
60-
if r_refresh_token.status_code == 200:
61-
response = AccessToken(**r_refresh_token.json())
62-
self._access_token(response)
63-
return True
64-
return False
65-
66-
def _token_expired(self):
67-
if datetime.fromtimestamp(self.token_expire) > datetime.now():
68-
return False
69-
return True
126+
if r_refresh_access_token.status_code == 200:
127+
response = AccessToken(**r_refresh_access_token.json())
128+
if response.access_token is not None:
129+
self._set_access_token_expires_at(response)
130+
else:
131+
self._basic_login()
132+
else:
133+
self._basic_login()
70134

71135
def _get_headers(self):
72136
_headers = self.headers
73-
if self.refresh_token:
74-
if self._token_expired():
75-
self._refresh_token()
76-
_headers["authorization"] = f"Bearer {self.access_token}"
77-
return _headers
137+
if self.refresh_token and self._access_token_is_expired():
138+
self._refresh_access_token()
139+
_headers["authorization"] = f"Bearer {self.access_token}"
78140
return _headers
79141

142+
def token(self):
143+
return Token(
144+
access_token=self.access_token,
145+
refresh_token=self.refresh_token,
146+
expires_at=int(self.access_token_expires_at),
147+
)
148+
80149
def login(self):
81150
r_about = httpx.get(url=f"{self.base_url}/api")
82151
if r_about.status_code == 200:
83152
try:
84153
about = About(**r_about.json())
85154
except PydanticValidationError:
86155
raise HttpInvalidURL(f'"{self.base_url}/api"')
87-
if about.auths and "localjwt" in about.auths and self.username and self.password:
88-
r_login = httpx.post(
89-
url=f"{self.base_url}/api/login",
90-
json={
91-
"username": f"{self.username}",
92-
"password": f"{self.password}",
93-
},
94-
# failed login delay: 5s
95-
timeout=10.0,
96-
)
97-
if r_login.status_code == 200:
156+
if about.auths and "localjwt" in about.auths:
157+
if self.refresh_token:
98158
try:
99-
response = Token(**r_login.json())
100-
self._access_token(response)
101-
self.refresh_token = response.refresh_token
159+
self._refresh_access_token()
160+
return self.token()
161+
except (PydanticValidationError, TypeError):
162+
raise HTTPError("Authorization failed")
163+
elif self.auth0_token:
164+
self._auth0_login()
165+
return self.token()
166+
elif self.username and self.password:
167+
self._basic_login()
168+
return self.token()
169+
elif (
170+
self.access_token
171+
and not self.refresh_token
172+
and not (self.username and self.password)
173+
):
174+
try:
175+
response = Token(access_token=self.access_token)
176+
self._set_access_token_expires_at(response)
102177
return response
103-
except PydanticValidationError:
178+
except (PydanticValidationError, TypeError):
104179
return Token()
105-
raise HTTPError(f'"Authorization failed", {r_login.status_code}')
180+
else:
181+
raise HTTPError("Authorization failed")
106182
return Token()
107183
raise HTTPError(f'"{self.base_url}/api", {r_about.status_code}')
108184

@@ -150,6 +226,8 @@ async def proxy_method(self, *args, **kwargs):
150226
submodule_name = f"{module_name}.{submodule_info.name}"
151227
submodule = importlib.import_module(submodule_name)
152228
if hasattr(submodule, "asyncio"):
153-
AsyncClient._add_proxy_method(submodule_info.name, submodule.asyncio)
229+
AsyncClient._add_proxy_method(
230+
submodule_info.name, submodule.asyncio
231+
)
154232
if hasattr(submodule, "sync"):
155233
Client._add_proxy_method(submodule_info.name, submodule.sync)

core_client/base/api/about.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
"""
2+
Is deprecated. Please use `about_get`.
3+
"""
4+
15
import httpx
26
from pydantic import parse_obj_as, validate_arguments
37

core_client/base/api/about_get.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import httpx
2+
from pydantic import parse_obj_as, validate_arguments
3+
4+
from ...models import Client
5+
from ..models import Error, About
6+
7+
8+
@validate_arguments()
9+
def _build_request(
10+
client: Client,
11+
retries: int = None,
12+
timeout: float = None,
13+
):
14+
if not retries:
15+
retries = client.retries
16+
if not timeout:
17+
timeout = client.timeout
18+
return {
19+
"method": "get",
20+
"url": f"{client.base_url}/api",
21+
"headers": client.headers,
22+
"timeout": timeout,
23+
"data": None,
24+
"json": None,
25+
}, retries
26+
27+
28+
def _build_response(response: httpx.Response):
29+
if response.status_code == 200:
30+
response_200 = parse_obj_as(About, response.json())
31+
return response_200
32+
else:
33+
response_error = parse_obj_as(Error, response.json())
34+
return response_error
35+
36+
37+
def sync(client: Client, **kwargs):
38+
request, retries = _build_request(client, **kwargs)
39+
transport = httpx.HTTPTransport(retries=retries)
40+
httpx_client = httpx.Client(transport=transport, http2=True)
41+
response = httpx_client.request(**request)
42+
return _build_response(response=response)
43+
44+
45+
async def asyncio(client: Client, **kwargs):
46+
request, retries = _build_request(client, **kwargs)
47+
transport = httpx.AsyncHTTPTransport(retries=retries)
48+
async with httpx.AsyncClient(transport=transport) as httpx_client:
49+
response = await httpx_client.request(**request)
50+
return _build_response(response=response)

core_client/base/api/v3_cluster_get_node_proxy.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77

88
@validate_arguments()
9-
def _build_request(client: Client, id: str, retries: int = None, timeout: float = None):
9+
def _build_request(
10+
client: Client, id: str, retries: int = None, timeout: float = None
11+
):
1012
if not retries:
1113
retries = client.retries
1214
if not timeout:

0 commit comments

Comments
 (0)