sumo-wrapper-python 1.0.7__py3-none-any.whl → 1.0.9__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 sumo-wrapper-python might be problematic. Click here for more details.
- sumo/wrapper/_auth_provider.py +150 -20
- sumo/wrapper/_decorators.py +6 -0
- sumo/wrapper/_logging.py +3 -0
- sumo/wrapper/_retry_strategy.py +4 -1
- sumo/wrapper/_version.py +2 -2
- sumo/wrapper/login.py +5 -0
- sumo/wrapper/sumo_client.py +21 -0
- {sumo_wrapper_python-1.0.7.dist-info → sumo_wrapper_python-1.0.9.dist-info}/METADATA +9 -154
- sumo_wrapper_python-1.0.9.dist-info/RECORD +17 -0
- {sumo_wrapper_python-1.0.7.dist-info → sumo_wrapper_python-1.0.9.dist-info}/WHEEL +1 -1
- sumo_wrapper_python-1.0.7.dist-info/RECORD +0 -17
- {sumo_wrapper_python-1.0.7.dist-info → sumo_wrapper_python-1.0.9.dist-info}/LICENSE +0 -0
- {sumo_wrapper_python-1.0.7.dist-info → sumo_wrapper_python-1.0.9.dist-info}/entry_points.txt +0 -0
- {sumo_wrapper_python-1.0.7.dist-info → sumo_wrapper_python-1.0.9.dist-info}/top_level.txt +0 -0
sumo/wrapper/_auth_provider.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import platform
|
|
2
|
+
from pathlib import Path
|
|
1
3
|
import msal
|
|
2
4
|
import os
|
|
3
5
|
from datetime import datetime, timedelta
|
|
@@ -7,9 +9,13 @@ import json
|
|
|
7
9
|
import jwt
|
|
8
10
|
import time
|
|
9
11
|
from azure.identity import ManagedIdentityCredential
|
|
12
|
+
import tenacity as tn
|
|
13
|
+
from ._retry_strategy import _log_retry_info, _return_last_value
|
|
10
14
|
|
|
11
15
|
from msal_extensions.persistence import FilePersistence
|
|
12
16
|
from msal_extensions.token_cache import PersistedTokenCache
|
|
17
|
+
import errno
|
|
18
|
+
|
|
13
19
|
|
|
14
20
|
if not sys.platform.startswith("linux"):
|
|
15
21
|
from msal_extensions import build_encrypted_persistence
|
|
@@ -19,13 +25,32 @@ def scope_for_resource(resource_id):
|
|
|
19
25
|
return f"{resource_id}/.default"
|
|
20
26
|
|
|
21
27
|
|
|
28
|
+
def _maybe_nfs_exception(exception):
|
|
29
|
+
return isinstance(exception, OSError) and (
|
|
30
|
+
exception.errno in (errno.EAGAIN, errno.ESTALE)
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
22
34
|
class AuthProvider:
|
|
23
35
|
def __init__(self, resource_id):
|
|
24
36
|
self._resource_id = resource_id
|
|
25
37
|
self._scope = scope_for_resource(resource_id)
|
|
26
38
|
self._app = None
|
|
39
|
+
self._login_timeout_minutes = 5
|
|
40
|
+
os.system("") # Ensure color init on all platforms (win10)
|
|
41
|
+
|
|
27
42
|
return
|
|
28
43
|
|
|
44
|
+
@tn.retry(
|
|
45
|
+
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
46
|
+
stop=tn.stop_after_attempt(6),
|
|
47
|
+
wait=(
|
|
48
|
+
tn.wait_exponential(multiplier=0.5, exp_base=2)
|
|
49
|
+
+ tn.wait_random_exponential(multiplier=0.5, exp_base=2)
|
|
50
|
+
),
|
|
51
|
+
retry_error_callback=_return_last_value,
|
|
52
|
+
before_sleep=_log_retry_info,
|
|
53
|
+
)
|
|
29
54
|
def get_token(self):
|
|
30
55
|
accounts = self._app.get_accounts()
|
|
31
56
|
if len(accounts) == 0:
|
|
@@ -37,7 +62,11 @@ class AuthProvider:
|
|
|
37
62
|
return result["access_token"]
|
|
38
63
|
|
|
39
64
|
def get_authorization(self):
|
|
40
|
-
|
|
65
|
+
token = self.get_token()
|
|
66
|
+
if token is None:
|
|
67
|
+
return ""
|
|
68
|
+
|
|
69
|
+
return {"Authorization": "Bearer " + token}
|
|
41
70
|
|
|
42
71
|
pass
|
|
43
72
|
|
|
@@ -77,6 +106,16 @@ def get_token_path(resource_id, suffix):
|
|
|
77
106
|
)
|
|
78
107
|
|
|
79
108
|
|
|
109
|
+
@tn.retry(
|
|
110
|
+
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
111
|
+
stop=tn.stop_after_attempt(6),
|
|
112
|
+
wait=(
|
|
113
|
+
tn.wait_exponential(multiplier=0.5, exp_base=2)
|
|
114
|
+
+ tn.wait_random_exponential(multiplier=0.5, exp_base=2)
|
|
115
|
+
),
|
|
116
|
+
retry_error_callback=_return_last_value,
|
|
117
|
+
before_sleep=_log_retry_info,
|
|
118
|
+
)
|
|
80
119
|
def get_token_cache(resource_id, suffix):
|
|
81
120
|
# https://github.com/AzureAD/microsoft-authentication-extensions-\
|
|
82
121
|
# for-python
|
|
@@ -110,6 +149,16 @@ def get_token_cache(resource_id, suffix):
|
|
|
110
149
|
return cache
|
|
111
150
|
|
|
112
151
|
|
|
152
|
+
@tn.retry(
|
|
153
|
+
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
154
|
+
stop=tn.stop_after_attempt(6),
|
|
155
|
+
wait=(
|
|
156
|
+
tn.wait_exponential(multiplier=0.5, exp_base=2)
|
|
157
|
+
+ tn.wait_random_exponential(multiplier=0.5, exp_base=2)
|
|
158
|
+
),
|
|
159
|
+
retry_error_callback=_return_last_value,
|
|
160
|
+
before_sleep=_log_retry_info,
|
|
161
|
+
)
|
|
113
162
|
def protect_token_cache(resource_id, suffix):
|
|
114
163
|
token_path = get_token_path(resource_id, suffix)
|
|
115
164
|
|
|
@@ -143,27 +192,36 @@ class AuthProviderInteractive(AuthProvider):
|
|
|
143
192
|
pass
|
|
144
193
|
return
|
|
145
194
|
|
|
195
|
+
@tn.retry(
|
|
196
|
+
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
197
|
+
stop=tn.stop_after_attempt(6),
|
|
198
|
+
wait=(
|
|
199
|
+
tn.wait_exponential(multiplier=0.5, exp_base=2)
|
|
200
|
+
+ tn.wait_random_exponential(multiplier=0.5, exp_base=2)
|
|
201
|
+
),
|
|
202
|
+
retry_error_callback=_return_last_value,
|
|
203
|
+
before_sleep=_log_retry_info,
|
|
204
|
+
)
|
|
146
205
|
def login(self):
|
|
147
206
|
scopes = [self._scope + " offline_access"]
|
|
148
|
-
login_timeout_minutes = 7
|
|
149
|
-
os.system("") # Ensure color init on all platforms (win10)
|
|
150
207
|
print(
|
|
151
208
|
"\n\n \033[31m NOTE! \033[0m"
|
|
152
209
|
+ " Please login to Equinor Azure to enable Sumo access: "
|
|
153
|
-
+ "we
|
|
210
|
+
+ "we are opening a login web-page for you in your browser."
|
|
154
211
|
+ "\nYou should complete your login within "
|
|
155
|
-
+ str(
|
|
212
|
+
+ str(self._login_timeout_minutes)
|
|
156
213
|
+ " minutes, "
|
|
157
214
|
+ "that is before "
|
|
158
215
|
+ str(
|
|
159
216
|
(
|
|
160
|
-
datetime.now()
|
|
217
|
+
datetime.now()
|
|
218
|
+
+ timedelta(minutes=self._login_timeout_minutes)
|
|
161
219
|
).strftime("%H:%M:%S")
|
|
162
220
|
)
|
|
163
221
|
)
|
|
164
222
|
try:
|
|
165
223
|
result = self._app.acquire_token_interactive(
|
|
166
|
-
scopes, timeout=(
|
|
224
|
+
scopes, timeout=(self._login_timeout_minutes * 60)
|
|
167
225
|
)
|
|
168
226
|
if "error" in result:
|
|
169
227
|
print(
|
|
@@ -180,7 +238,9 @@ class AuthProviderInteractive(AuthProvider):
|
|
|
180
238
|
return
|
|
181
239
|
|
|
182
240
|
protect_token_cache(self._resource_id, ".token")
|
|
183
|
-
print(
|
|
241
|
+
print(
|
|
242
|
+
"Equinor Azure login for Sumo access was successful (interactive)"
|
|
243
|
+
)
|
|
184
244
|
return
|
|
185
245
|
|
|
186
246
|
pass
|
|
@@ -200,25 +260,58 @@ class AuthProviderDeviceCode(AuthProvider):
|
|
|
200
260
|
pass
|
|
201
261
|
return
|
|
202
262
|
|
|
263
|
+
@tn.retry(
|
|
264
|
+
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
265
|
+
stop=tn.stop_after_attempt(6),
|
|
266
|
+
wait=(
|
|
267
|
+
tn.wait_exponential(multiplier=0.5, exp_base=2)
|
|
268
|
+
+ tn.wait_random_exponential(multiplier=0.5, exp_base=2)
|
|
269
|
+
),
|
|
270
|
+
retry_error_callback=_return_last_value,
|
|
271
|
+
before_sleep=_log_retry_info,
|
|
272
|
+
)
|
|
203
273
|
def login(self):
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
274
|
+
try:
|
|
275
|
+
scopes = [self._scope + " offline_access"]
|
|
276
|
+
flow = self._app.initiate_device_flow(scopes)
|
|
277
|
+
if "error" in flow:
|
|
278
|
+
print(
|
|
279
|
+
"\n\n \033[31m"
|
|
280
|
+
+ "Failed to initiate device-code login. Err: %s"
|
|
281
|
+
+ "\033[0m" % json.dumps(flow, indent=4)
|
|
282
|
+
)
|
|
283
|
+
return
|
|
284
|
+
flow["expires_at"] = (
|
|
285
|
+
int(time.time()) + self._login_timeout_minutes * 60
|
|
210
286
|
)
|
|
211
287
|
|
|
212
|
-
|
|
213
|
-
|
|
288
|
+
print(
|
|
289
|
+
"\033[31m"
|
|
290
|
+
+ " NOTE! Please login to Equinor Azure to enable Sumo access:"
|
|
291
|
+
+ flow["message"]
|
|
292
|
+
+ " \033[0m"
|
|
293
|
+
+ "\nYou should complete your login within a few minutes"
|
|
294
|
+
)
|
|
295
|
+
result = self._app.acquire_token_by_device_flow(flow)
|
|
214
296
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
297
|
+
if "error" in result:
|
|
298
|
+
print(
|
|
299
|
+
"\n\n \033[31m Error during Equinor Azure login "
|
|
300
|
+
"for Sumo access: \033[0m"
|
|
301
|
+
)
|
|
302
|
+
print("Err: ", json.dumps(result, indent=4))
|
|
303
|
+
return
|
|
304
|
+
except Exception:
|
|
305
|
+
print(
|
|
306
|
+
"\n\n \033[31m Failed Equinor Azure login for Sumo access, "
|
|
307
|
+
"one possible reason is timeout \033[0m"
|
|
219
308
|
)
|
|
309
|
+
return
|
|
220
310
|
|
|
221
311
|
protect_token_cache(self._resource_id, ".token")
|
|
312
|
+
print(
|
|
313
|
+
"Equinor Azure login for Sumo access was successful (device-code)"
|
|
314
|
+
)
|
|
222
315
|
|
|
223
316
|
return
|
|
224
317
|
|
|
@@ -232,6 +325,16 @@ class AuthProviderManaged(AuthProvider):
|
|
|
232
325
|
self._scope = scope_for_resource(resource_id)
|
|
233
326
|
return
|
|
234
327
|
|
|
328
|
+
@tn.retry(
|
|
329
|
+
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
330
|
+
stop=tn.stop_after_attempt(6),
|
|
331
|
+
wait=(
|
|
332
|
+
tn.wait_exponential(multiplier=0.5, exp_base=2)
|
|
333
|
+
+ tn.wait_random_exponential(multiplier=0.5, exp_base=2)
|
|
334
|
+
),
|
|
335
|
+
retry_error_callback=_return_last_value,
|
|
336
|
+
before_sleep=_log_retry_info,
|
|
337
|
+
)
|
|
235
338
|
def get_token(self):
|
|
236
339
|
return self._app.get_token(self._scope).token
|
|
237
340
|
|
|
@@ -239,6 +342,16 @@ class AuthProviderManaged(AuthProvider):
|
|
|
239
342
|
|
|
240
343
|
|
|
241
344
|
class AuthProviderSumoToken(AuthProvider):
|
|
345
|
+
@tn.retry(
|
|
346
|
+
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
347
|
+
stop=tn.stop_after_attempt(6),
|
|
348
|
+
wait=(
|
|
349
|
+
tn.wait_exponential(multiplier=0.5, exp_base=2)
|
|
350
|
+
+ tn.wait_random_exponential(multiplier=0.5, exp_base=2)
|
|
351
|
+
),
|
|
352
|
+
retry_error_callback=_return_last_value,
|
|
353
|
+
before_sleep=_log_retry_info,
|
|
354
|
+
)
|
|
242
355
|
def __init__(self, resource_id):
|
|
243
356
|
protect_token_cache(resource_id, ".sharedkey")
|
|
244
357
|
token_path = get_token_path(resource_id, ".sharedkey")
|
|
@@ -253,6 +366,16 @@ class AuthProviderSumoToken(AuthProvider):
|
|
|
253
366
|
return {"X-SUMO-Token": self._token}
|
|
254
367
|
|
|
255
368
|
|
|
369
|
+
@tn.retry(
|
|
370
|
+
retry=tn.retry_if_exception(_maybe_nfs_exception),
|
|
371
|
+
stop=tn.stop_after_attempt(6),
|
|
372
|
+
wait=(
|
|
373
|
+
tn.wait_exponential(multiplier=0.5, exp_base=2)
|
|
374
|
+
+ tn.wait_random_exponential(multiplier=0.5, exp_base=2)
|
|
375
|
+
),
|
|
376
|
+
retry_error_callback=_return_last_value,
|
|
377
|
+
before_sleep=_log_retry_info,
|
|
378
|
+
)
|
|
256
379
|
def get_auth_provider(
|
|
257
380
|
client_id,
|
|
258
381
|
authority,
|
|
@@ -294,4 +417,11 @@ def get_auth_provider(
|
|
|
294
417
|
):
|
|
295
418
|
return AuthProviderManaged(resource_id)
|
|
296
419
|
# ELSE
|
|
420
|
+
lockfile_path = Path.home() / ".config/chromium/SingletonLock"
|
|
421
|
+
if Path(lockfile_path).is_symlink() and not str(
|
|
422
|
+
Path(lockfile_path).resolve()
|
|
423
|
+
).__contains__(platform.node()):
|
|
424
|
+
# https://github.com/equinor/sumo-wrapper-python/issues/193
|
|
425
|
+
return AuthProviderDeviceCode(client_id, authority, resource_id)
|
|
426
|
+
# ELSE
|
|
297
427
|
return AuthProviderInteractive(client_id, authority, resource_id)
|
sumo/wrapper/_decorators.py
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
+
# For sphinx:
|
|
2
|
+
from functools import wraps
|
|
3
|
+
|
|
4
|
+
|
|
1
5
|
def raise_for_status(func):
|
|
6
|
+
@wraps(func)
|
|
2
7
|
def wrapper(*args, **kwargs):
|
|
3
8
|
# FIXME: in newer versions of httpx, raise_for_status() is chainable,
|
|
4
9
|
# so we could simply write
|
|
@@ -11,6 +16,7 @@ def raise_for_status(func):
|
|
|
11
16
|
|
|
12
17
|
|
|
13
18
|
def raise_for_status_async(func):
|
|
19
|
+
@wraps(func)
|
|
14
20
|
async def wrapper(*args, **kwargs):
|
|
15
21
|
# FIXME: in newer versions of httpx, raise_for_status() is chainable,
|
|
16
22
|
# so we could simply write
|
sumo/wrapper/_logging.py
CHANGED
|
@@ -20,6 +20,9 @@ class LogHandlerSumo(logging.Handler):
|
|
|
20
20
|
"funcname": record.funcName,
|
|
21
21
|
"linenumber": record.lineno,
|
|
22
22
|
}
|
|
23
|
+
if "objectUuid" in record.__dict__.keys():
|
|
24
|
+
json["objectUuid"] = record.__dict__.get("objectUuid")
|
|
25
|
+
|
|
23
26
|
self._sumoClient.post("/message-log/new", json=json)
|
|
24
27
|
except Exception:
|
|
25
28
|
# Never fail on logging
|
sumo/wrapper/_retry_strategy.py
CHANGED
|
@@ -17,7 +17,10 @@ def _log_retry_info(retry_state):
|
|
|
17
17
|
|
|
18
18
|
# Define the conditions for retrying based on exception types
|
|
19
19
|
def _is_retryable_exception(exception):
|
|
20
|
-
return isinstance(
|
|
20
|
+
return isinstance(
|
|
21
|
+
exception,
|
|
22
|
+
(httpx.TimeoutException, httpx.NetworkError, httpx.ProxyError),
|
|
23
|
+
)
|
|
21
24
|
|
|
22
25
|
|
|
23
26
|
# Define the conditions for retrying based on HTTP status codes
|
sumo/wrapper/_version.py
CHANGED
sumo/wrapper/login.py
CHANGED
|
@@ -74,6 +74,11 @@ def main():
|
|
|
74
74
|
if args.print_token:
|
|
75
75
|
print(f"TOKEN: {token}")
|
|
76
76
|
|
|
77
|
+
if token is not None:
|
|
78
|
+
print("Successfully logged in to Sumo environment: " + env)
|
|
79
|
+
else:
|
|
80
|
+
print("Failed login to Sumo environment: " + env)
|
|
81
|
+
|
|
77
82
|
|
|
78
83
|
if __name__ == "__main__":
|
|
79
84
|
main()
|
sumo/wrapper/sumo_client.py
CHANGED
|
@@ -84,6 +84,27 @@ class SumoClient:
|
|
|
84
84
|
access_token=access_token,
|
|
85
85
|
devicecode=devicecode,
|
|
86
86
|
)
|
|
87
|
+
if (
|
|
88
|
+
self.auth.get_token() is None
|
|
89
|
+
and refresh_token is None
|
|
90
|
+
and access_token is None
|
|
91
|
+
):
|
|
92
|
+
print("\n \033[31m !!! Falling back to device-code login:\033[0m")
|
|
93
|
+
self.auth = get_auth_provider(
|
|
94
|
+
client_id=APP_REGISTRATION[env]["CLIENT_ID"],
|
|
95
|
+
authority=f"{AUTHORITY_HOST_URI}/{TENANT_ID}",
|
|
96
|
+
resource_id=APP_REGISTRATION[env]["RESOURCE_ID"],
|
|
97
|
+
interactive=False,
|
|
98
|
+
refresh_token=None,
|
|
99
|
+
access_token=None,
|
|
100
|
+
devicecode=True,
|
|
101
|
+
)
|
|
102
|
+
if self.auth.get_token() is None:
|
|
103
|
+
print(
|
|
104
|
+
"\n\n \033[31m "
|
|
105
|
+
+ "NOTE! Login failed/timed out. Giving up."
|
|
106
|
+
+ "\033[0m"
|
|
107
|
+
)
|
|
87
108
|
|
|
88
109
|
if env == "localhost":
|
|
89
110
|
self.base_url = "http://localhost:8084/api/v1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: sumo-wrapper-python
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Summary: Python wrapper for the Sumo API
|
|
5
5
|
Author: Equinor
|
|
6
6
|
License: Apache License
|
|
@@ -220,165 +220,20 @@ Requires-Dist: azure-identity >=1.13.0
|
|
|
220
220
|
Provides-Extra: docs
|
|
221
221
|
Requires-Dist: sphinx ; extra == 'docs'
|
|
222
222
|
Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
|
|
223
|
+
Requires-Dist: autoapi ; extra == 'docs'
|
|
224
|
+
Requires-Dist: sphinx-autodoc-typehints ; extra == 'docs'
|
|
225
|
+
Requires-Dist: sphinxcontrib-apidoc ; extra == 'docs'
|
|
223
226
|
Provides-Extra: test
|
|
224
227
|
Requires-Dist: pytest ; extra == 'test'
|
|
225
228
|
Requires-Dist: PyYAML ; extra == 'test'
|
|
226
229
|
|
|
227
230
|
# sumo-wrapper-python
|
|
228
231
|
|
|
229
|
-
|
|
232
|
+
[](https://sumo-wrapper-python.readthedocs.io/en/latest/?badge=latest)
|
|
230
233
|
|
|
231
|
-
|
|
234
|
+
## Documentation and guidelines
|
|
235
|
+
[sumo-wrapper-python documentation](https://sumo-wrapper-python.readthedocs.io/en/latest/)
|
|
232
236
|
|
|
233
|
-
##
|
|
237
|
+
## Contribute
|
|
238
|
+
[Contribution guidelines](./CONTRIBUTING.md)
|
|
234
239
|
|
|
235
|
-
pip install sumo-wrapper-python
|
|
236
|
-
|
|
237
|
-
For internal Equinor users, this package is available through the Komodo
|
|
238
|
-
distribution.
|
|
239
|
-
|
|
240
|
-
# Table of contents
|
|
241
|
-
|
|
242
|
-
- [sumo-wrapper-python](#sumo-wrapper-python)
|
|
243
|
-
- [Install:](#install)
|
|
244
|
-
- [Table of contents](#table-of-contents)
|
|
245
|
-
- [SumoClient](#sumoclient)
|
|
246
|
-
- [Initialization](#initialization)
|
|
247
|
-
- [Parameters](#parameters)
|
|
248
|
-
- [`token` logic](#token-logic)
|
|
249
|
-
- [Methods](#methods)
|
|
250
|
-
- [get(path, \*\*params)](#getpath-params)
|
|
251
|
-
- [post(path, json, blob, params)](#postpath-json-blob-params)
|
|
252
|
-
- [put(path, json, blob)](#putpath-json-blob)
|
|
253
|
-
- [delete(path)](#deletepath)
|
|
254
|
-
- [Async methods](#async-methods)
|
|
255
|
-
|
|
256
|
-
# SumoClient
|
|
257
|
-
|
|
258
|
-
A thin wrapper class for the Sumo API.
|
|
259
|
-
|
|
260
|
-
### Initialization
|
|
261
|
-
|
|
262
|
-
```python
|
|
263
|
-
from sumo.wrapper import SumoClient
|
|
264
|
-
|
|
265
|
-
sumo = SumoClient(env="dev")
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### Parameters
|
|
269
|
-
|
|
270
|
-
```python
|
|
271
|
-
class SumoClient:
|
|
272
|
-
def __init__(
|
|
273
|
-
self,
|
|
274
|
-
env:str,
|
|
275
|
-
token:str=None,
|
|
276
|
-
interactive:bool=False,
|
|
277
|
-
verbosity:str="CRITICAL"
|
|
278
|
-
):
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
- `env`: sumo environment
|
|
282
|
-
- `token`: bearer token or refresh token
|
|
283
|
-
- `interactive`: use interactive flow when authenticating
|
|
284
|
-
- `verbosity`: "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
|
|
285
|
-
|
|
286
|
-
###### `token` logic
|
|
287
|
-
|
|
288
|
-
If an access token is provided in the `token` parameter, it will be used as long
|
|
289
|
-
as it's valid. An error will be raised when it expires.
|
|
290
|
-
|
|
291
|
-
If we are unable to decode the provided `token` as a JWT, we treat it as a
|
|
292
|
-
refresh token and attempt to use it to retrieve an access token.
|
|
293
|
-
|
|
294
|
-
If no `token` is provided, an authentication code flow/interactive flow is
|
|
295
|
-
triggered to retrieve a token.
|
|
296
|
-
|
|
297
|
-
## Methods
|
|
298
|
-
|
|
299
|
-
`SumoClient` has one method for each HTTP-method that is used in the sumo-core
|
|
300
|
-
API. See examples of how to use these methods below.
|
|
301
|
-
|
|
302
|
-
All methods accepts a path argument. Path parameters can be interpolated into
|
|
303
|
-
the path string. Example:
|
|
304
|
-
|
|
305
|
-
```python
|
|
306
|
-
object_id = "1234"
|
|
307
|
-
|
|
308
|
-
# GET/objects('{obejctid}')
|
|
309
|
-
sumo.get(f"/objects('{object_id}')")
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
### get(path, \*\*params)
|
|
313
|
-
|
|
314
|
-
Performs a GET-request to sumo-core. Accepts query parameters as keyword
|
|
315
|
-
arguments.
|
|
316
|
-
|
|
317
|
-
```python
|
|
318
|
-
# Retrieve userdata
|
|
319
|
-
user_data = sumo.get("/userdata")
|
|
320
|
-
|
|
321
|
-
# Search for objects
|
|
322
|
-
results = sumo.get("/search",
|
|
323
|
-
query="class:surface",
|
|
324
|
-
size:3,
|
|
325
|
-
select=["_id"]
|
|
326
|
-
)
|
|
327
|
-
|
|
328
|
-
# Get object by id
|
|
329
|
-
object_id = "159405ba-0046-b321-55ce-542f383ba5c7"
|
|
330
|
-
|
|
331
|
-
obj = sumo.get(f"/objects('{object_id}')")
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### post(path, json, blob, params)
|
|
335
|
-
|
|
336
|
-
Performs a POST-request to sumo-core. Accepts json and blob, but not both at the
|
|
337
|
-
same time.
|
|
338
|
-
|
|
339
|
-
```python
|
|
340
|
-
# Upload new parent object
|
|
341
|
-
parent_object = sumo.post("/objects", json=parent_meta_data)
|
|
342
|
-
|
|
343
|
-
# Upload child object
|
|
344
|
-
parent_id = parent_object["_id"]
|
|
345
|
-
|
|
346
|
-
child_object = sumo.post(f"/objects('{parent_id}')", json=child_meta_data)
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### put(path, json, blob)
|
|
350
|
-
|
|
351
|
-
Performs a PUT-request to sumo-core. Accepts json and blob, but not both at the
|
|
352
|
-
same time.
|
|
353
|
-
|
|
354
|
-
```python
|
|
355
|
-
# Upload blob to child object
|
|
356
|
-
child_id = child_object["_id"]
|
|
357
|
-
|
|
358
|
-
sumo.put(f"/objects('{child_id}')/blob", blob=blob)
|
|
359
|
-
```
|
|
360
|
-
|
|
361
|
-
### delete(path)
|
|
362
|
-
|
|
363
|
-
Performs a DELETE-request to sumo-core.
|
|
364
|
-
|
|
365
|
-
```python
|
|
366
|
-
# Delete blob
|
|
367
|
-
sumo.delete(f"/objects('{child_id}')/blob")
|
|
368
|
-
|
|
369
|
-
# Delete child object
|
|
370
|
-
sumo.delete(f"/objects('{child_id}')")
|
|
371
|
-
|
|
372
|
-
# Delete parent object
|
|
373
|
-
sumo.delete(f"/objects('{parent_id}')")
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
## Async methods
|
|
377
|
-
|
|
378
|
-
`SumoClient` also has *async* alternatives `get_async`, `post_async`, `put_async` and `delete_async`.
|
|
379
|
-
These accept the same parameters as their synchronous counterparts, but have to be *awaited*.
|
|
380
|
-
|
|
381
|
-
```python
|
|
382
|
-
# Retrieve userdata
|
|
383
|
-
user_data = await sumo.get_async("/userdata")
|
|
384
|
-
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
sumo/__init__.py,sha256=ftS-xRPSH-vU7fIHlnZQaCTWbNvs4owJivNW65kzsIM,85
|
|
2
|
+
sumo/wrapper/__init__.py,sha256=wW7Bjl0btCgK9_exGVrfJsnFmTOybIdyeCudz0LueQM,235
|
|
3
|
+
sumo/wrapper/_auth_provider.py,sha256=HieseMcqT9lMBqgyzwL5Pr6CCyk-eFjKyYhJQx8UKRw,13494
|
|
4
|
+
sumo/wrapper/_blob_client.py,sha256=y15_TThJ4ENFtEAF37mgju6Htc8D3ZyzcYCqsYQZo7Y,1495
|
|
5
|
+
sumo/wrapper/_decorators.py,sha256=3IEi6GXVkkDACHoo8dOeDoBtZh5TlJ6Tw0qlpOVHqLQ,806
|
|
6
|
+
sumo/wrapper/_logging.py,sha256=lnhjn6oQna33jZpzeZ7IeBya2uKNfrzXr_C3nw7txo0,965
|
|
7
|
+
sumo/wrapper/_retry_strategy.py,sha256=9PjOT0hOhLbCHrJu02thUkGdY39NpB2rqa_XUODoguw,2415
|
|
8
|
+
sumo/wrapper/_version.py,sha256=29gfaFnVGHlO4YwJu6vgvQjCQwPqDmuCvkEOYMZyflo,411
|
|
9
|
+
sumo/wrapper/config.py,sha256=6t7qqjrrmd11m4VMlRryiMYw2JDU_R51305woAP1TAs,865
|
|
10
|
+
sumo/wrapper/login.py,sha256=HY3CLu3u0RZtgSbitpdyk2T0P-M9ttZ6LRDsGO4BwRM,1773
|
|
11
|
+
sumo/wrapper/sumo_client.py,sha256=Mjco-nFF28GgmjXgDmGmXmFXr7LJI50M54zL0zt3CJs,15545
|
|
12
|
+
sumo_wrapper_python-1.0.9.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
13
|
+
sumo_wrapper_python-1.0.9.dist-info/METADATA,sha256=pmewZzDIe8sALypH6d9EVNxBr5qiuoCFlW5MfBjytSg,14267
|
|
14
|
+
sumo_wrapper_python-1.0.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
15
|
+
sumo_wrapper_python-1.0.9.dist-info/entry_points.txt,sha256=V_vGky2C3He5vohJAxnBdvpt_fqfUDFj5irUm9HtoFc,55
|
|
16
|
+
sumo_wrapper_python-1.0.9.dist-info/top_level.txt,sha256=rLbKyH9rWgCj3PoLeR7fvC5X8vCaUc5LF8-Y_GBWZL0,5
|
|
17
|
+
sumo_wrapper_python-1.0.9.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
sumo/__init__.py,sha256=ftS-xRPSH-vU7fIHlnZQaCTWbNvs4owJivNW65kzsIM,85
|
|
2
|
-
sumo/wrapper/__init__.py,sha256=wW7Bjl0btCgK9_exGVrfJsnFmTOybIdyeCudz0LueQM,235
|
|
3
|
-
sumo/wrapper/_auth_provider.py,sha256=rfTgOwmfOyg7DJwUoejCINfrzhi1lWIx-UH0Pir-8XM,9005
|
|
4
|
-
sumo/wrapper/_blob_client.py,sha256=y15_TThJ4ENFtEAF37mgju6Htc8D3ZyzcYCqsYQZo7Y,1495
|
|
5
|
-
sumo/wrapper/_decorators.py,sha256=FSorPaTEBpq2N0qrqfdTt0jMToe_0PpOYlsL6vZSbIg,728
|
|
6
|
-
sumo/wrapper/_logging.py,sha256=XgXcOAEoVw0aqij7V7EPTiP3ALqa8xNDgJgKyWOxeok,838
|
|
7
|
-
sumo/wrapper/_retry_strategy.py,sha256=sata8h2tXWQIm9GQYWJCmzNBLm9Jy_odGYkaOH27j3o,2374
|
|
8
|
-
sumo/wrapper/_version.py,sha256=B_2jTRPLk-a7dhEz8Rto7VRGwxfpXrqO4mYrudjn7bw,411
|
|
9
|
-
sumo/wrapper/config.py,sha256=6t7qqjrrmd11m4VMlRryiMYw2JDU_R51305woAP1TAs,865
|
|
10
|
-
sumo/wrapper/login.py,sha256=HB_USKw6pR4j5EyNevaFVJGQjQ3t_8F__ZBaR29XLf4,1610
|
|
11
|
-
sumo/wrapper/sumo_client.py,sha256=q5k6qP_oSgzmfFf1Bc2zw8L2KK85GFXg99Ro8GT3GZA,14741
|
|
12
|
-
sumo_wrapper_python-1.0.7.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
13
|
-
sumo_wrapper_python-1.0.7.dist-info/METADATA,sha256=7qpWbfF_muUNT0kIfYzzK_YUeqZOraqlpWWS1eAQVKQ,17407
|
|
14
|
-
sumo_wrapper_python-1.0.7.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
15
|
-
sumo_wrapper_python-1.0.7.dist-info/entry_points.txt,sha256=V_vGky2C3He5vohJAxnBdvpt_fqfUDFj5irUm9HtoFc,55
|
|
16
|
-
sumo_wrapper_python-1.0.7.dist-info/top_level.txt,sha256=rLbKyH9rWgCj3PoLeR7fvC5X8vCaUc5LF8-Y_GBWZL0,5
|
|
17
|
-
sumo_wrapper_python-1.0.7.dist-info/RECORD,,
|
|
File without changes
|
{sumo_wrapper_python-1.0.7.dist-info → sumo_wrapper_python-1.0.9.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|