kleinkram 0.38.1.dev20241119134715__py3-none-any.whl → 0.38.1.dev20241125112529__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.
Potentially problematic release.
This version of kleinkram might be problematic. Click here for more details.
- kleinkram/api/client.py +62 -24
- kleinkram/api/file_transfer.py +333 -204
- kleinkram/api/parsing.py +86 -0
- kleinkram/api/routes.py +78 -309
- kleinkram/app.py +60 -63
- kleinkram/auth.py +0 -2
- kleinkram/commands/download.py +60 -60
- kleinkram/commands/list.py +53 -48
- kleinkram/commands/mission.py +25 -13
- kleinkram/commands/upload.py +79 -53
- kleinkram/commands/verify.py +62 -37
- kleinkram/config.py +2 -3
- kleinkram/errors.py +49 -26
- kleinkram/models.py +2 -2
- kleinkram/resources.py +158 -0
- kleinkram/utils.py +32 -53
- {kleinkram-0.38.1.dev20241119134715.dist-info → kleinkram-0.38.1.dev20241125112529.dist-info}/METADATA +5 -3
- kleinkram-0.38.1.dev20241125112529.dist-info/RECORD +37 -0
- {kleinkram-0.38.1.dev20241119134715.dist-info → kleinkram-0.38.1.dev20241125112529.dist-info}/WHEEL +1 -1
- tests/test_end_to_end.py +105 -0
- tests/test_resources.py +137 -0
- tests/test_utils.py +13 -59
- kleinkram-0.38.1.dev20241119134715.dist-info/RECORD +0 -33
- {kleinkram-0.38.1.dev20241119134715.dist-info → kleinkram-0.38.1.dev20241125112529.dist-info}/LICENSE +0 -0
- {kleinkram-0.38.1.dev20241119134715.dist-info → kleinkram-0.38.1.dev20241125112529.dist-info}/entry_points.txt +0 -0
- {kleinkram-0.38.1.dev20241119134715.dist-info → kleinkram-0.38.1.dev20241125112529.dist-info}/top_level.txt +0 -0
kleinkram/api/client.py
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
4
|
+
from threading import Lock
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
3
7
|
import httpx
|
|
4
8
|
from kleinkram.auth import Config
|
|
5
9
|
from kleinkram.config import Credentials
|
|
6
|
-
from kleinkram.errors import
|
|
7
|
-
|
|
10
|
+
from kleinkram.errors import NotAuthenticated
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
8
13
|
|
|
9
14
|
|
|
10
15
|
COOKIE_AUTH_TOKEN = "authtoken"
|
|
@@ -12,54 +17,87 @@ COOKIE_REFRESH_TOKEN = "refreshtoken"
|
|
|
12
17
|
COOKIE_CLI_KEY = "clikey"
|
|
13
18
|
|
|
14
19
|
|
|
20
|
+
class NotLoggedInException(Exception): ...
|
|
21
|
+
|
|
22
|
+
|
|
15
23
|
class AuthenticatedClient(httpx.Client):
|
|
16
|
-
|
|
24
|
+
_config: Config
|
|
25
|
+
_config_lock: Lock
|
|
26
|
+
|
|
27
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
17
28
|
super().__init__(*args, **kwargs)
|
|
18
|
-
self.config = Config()
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
self._config = Config()
|
|
31
|
+
self._config_lock = Lock()
|
|
32
|
+
|
|
33
|
+
if self._config.has_cli_key:
|
|
34
|
+
assert self._config.cli_key, "unreachable"
|
|
35
|
+
logger.info("using cli key...")
|
|
36
|
+
self.cookies.set(COOKIE_CLI_KEY, self._config.cli_key)
|
|
23
37
|
|
|
24
|
-
elif self.
|
|
25
|
-
|
|
26
|
-
self.
|
|
38
|
+
elif self._config.has_refresh_token:
|
|
39
|
+
logger.info("using refresh token...")
|
|
40
|
+
assert self._config.auth_token is not None, "unreachable"
|
|
41
|
+
self.cookies.set(COOKIE_AUTH_TOKEN, self._config.auth_token)
|
|
27
42
|
else:
|
|
28
|
-
|
|
43
|
+
logger.info("not authenticated...")
|
|
44
|
+
raise NotAuthenticated
|
|
29
45
|
|
|
30
46
|
def _refresh_token(self) -> None:
|
|
31
|
-
if self.
|
|
32
|
-
raise RuntimeError
|
|
33
|
-
|
|
34
|
-
refresh_token = self.config.refresh_token
|
|
35
|
-
if not refresh_token:
|
|
36
|
-
raise RuntimeError
|
|
47
|
+
if self._config.has_cli_key:
|
|
48
|
+
raise RuntimeError("cannot refresh token when using cli key auth")
|
|
37
49
|
|
|
50
|
+
refresh_token = self._config.refresh_token
|
|
51
|
+
if refresh_token is None:
|
|
52
|
+
raise RuntimeError("no refresh token found")
|
|
38
53
|
self.cookies.set(COOKIE_REFRESH_TOKEN, refresh_token)
|
|
39
54
|
|
|
55
|
+
logger.info("refreshing token...")
|
|
40
56
|
response = self.post(
|
|
41
57
|
"/auth/refresh-token",
|
|
42
58
|
)
|
|
43
59
|
response.raise_for_status()
|
|
44
|
-
|
|
45
60
|
new_access_token = response.cookies[COOKIE_AUTH_TOKEN]
|
|
46
61
|
creds = Credentials(auth_token=new_access_token, refresh_token=refresh_token)
|
|
47
62
|
|
|
48
|
-
|
|
63
|
+
logger.info("saving new tokens...")
|
|
64
|
+
|
|
65
|
+
with self._config_lock:
|
|
66
|
+
self._config.save_credentials(creds)
|
|
67
|
+
|
|
49
68
|
self.cookies.set(COOKIE_AUTH_TOKEN, new_access_token)
|
|
50
69
|
|
|
51
|
-
def request(
|
|
52
|
-
|
|
70
|
+
def request(
|
|
71
|
+
self, method: str, url: str | httpx.URL, *args: Any, **kwargs: Any
|
|
72
|
+
) -> httpx.Response:
|
|
73
|
+
if isinstance(url, httpx.URL):
|
|
74
|
+
raise NotImplementedError(f"`httpx.URL` is not supported {url!r}")
|
|
75
|
+
if not url.startswith("/"):
|
|
76
|
+
url = f"/{url}"
|
|
77
|
+
|
|
78
|
+
# try to do a request
|
|
79
|
+
full_url = f"{self._config.endpoint}{url}"
|
|
80
|
+
logger.info(f"requesting {method} {full_url}")
|
|
53
81
|
response = super().request(method, full_url, *args, **kwargs)
|
|
54
82
|
|
|
83
|
+
logger.info(f"got response {response}")
|
|
84
|
+
|
|
85
|
+
# if the requesting a refresh token fails, we are not logged in
|
|
55
86
|
if (url == "/auth/refresh-token") and response.status_code == 401:
|
|
56
|
-
|
|
87
|
+
logger.info("got 401, not logged in...")
|
|
88
|
+
raise NotAuthenticated
|
|
57
89
|
|
|
90
|
+
# otherwise we try to refresh the token
|
|
58
91
|
if response.status_code == 401:
|
|
92
|
+
logger.info("got 401, trying to refresh token...")
|
|
59
93
|
try:
|
|
60
94
|
self._refresh_token()
|
|
61
95
|
except Exception:
|
|
62
|
-
raise
|
|
63
|
-
|
|
96
|
+
raise NotAuthenticated
|
|
97
|
+
|
|
98
|
+
logger.info(f"retrying request {method} {full_url}")
|
|
99
|
+
resp = super().request(method, full_url, *args, **kwargs)
|
|
100
|
+
logger.info(f"got response {resp}")
|
|
101
|
+
return resp
|
|
64
102
|
else:
|
|
65
103
|
return response
|