meerschaum 2.9.5__py3-none-any.whl → 3.0.0rc1__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.
- meerschaum/__init__.py +5 -2
- meerschaum/_internal/__init__.py +1 -0
- meerschaum/_internal/arguments/_parse_arguments.py +4 -4
- meerschaum/_internal/arguments/_parser.py +17 -1
- meerschaum/_internal/entry.py +6 -6
- meerschaum/_internal/shell/Shell.py +1 -1
- meerschaum/_internal/static.py +372 -0
- meerschaum/actions/api.py +12 -2
- meerschaum/actions/bootstrap.py +7 -7
- meerschaum/actions/edit.py +142 -18
- meerschaum/actions/register.py +137 -6
- meerschaum/actions/show.py +117 -29
- meerschaum/actions/stop.py +4 -1
- meerschaum/actions/sync.py +1 -1
- meerschaum/actions/tag.py +9 -8
- meerschaum/api/__init__.py +9 -2
- meerschaum/api/_events.py +39 -2
- meerschaum/api/_oauth2.py +118 -8
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -1
- meerschaum/api/dash/callbacks/custom.py +2 -2
- meerschaum/api/dash/callbacks/dashboard.py +102 -18
- meerschaum/api/dash/callbacks/plugins.py +0 -1
- meerschaum/api/dash/callbacks/register.py +1 -1
- meerschaum/api/dash/callbacks/settings/__init__.py +1 -0
- meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
- meerschaum/api/dash/callbacks/settings/tokens.py +388 -0
- meerschaum/api/dash/components.py +30 -8
- meerschaum/api/dash/keys.py +19 -93
- meerschaum/api/dash/pages/dashboard.py +1 -20
- meerschaum/api/dash/pages/settings/__init__.py +1 -0
- meerschaum/api/dash/pages/settings/password_reset.py +1 -1
- meerschaum/api/dash/pages/settings/tokens.py +55 -0
- meerschaum/api/dash/pipes.py +94 -59
- meerschaum/api/dash/sessions.py +12 -0
- meerschaum/api/dash/tokens.py +606 -0
- meerschaum/api/dash/websockets.py +1 -1
- meerschaum/api/dash/webterm.py +4 -0
- meerschaum/api/models/__init__.py +23 -3
- meerschaum/api/models/_actions.py +22 -0
- meerschaum/api/models/_pipes.py +85 -7
- meerschaum/api/models/_tokens.py +81 -0
- meerschaum/api/resources/templates/termpage.html +12 -0
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +3 -4
- meerschaum/api/routes/_connectors.py +3 -7
- meerschaum/api/routes/_jobs.py +14 -35
- meerschaum/api/routes/_login.py +49 -12
- meerschaum/api/routes/_misc.py +5 -10
- meerschaum/api/routes/_pipes.py +134 -111
- meerschaum/api/routes/_plugins.py +38 -28
- meerschaum/api/routes/_tokens.py +236 -0
- meerschaum/api/routes/_users.py +47 -35
- meerschaum/api/routes/_version.py +3 -3
- meerschaum/config/__init__.py +43 -20
- meerschaum/config/_default.py +32 -5
- meerschaum/config/_edit.py +28 -24
- meerschaum/config/_environment.py +1 -1
- meerschaum/config/_patch.py +6 -6
- meerschaum/config/_paths.py +5 -1
- meerschaum/config/_read_config.py +65 -34
- meerschaum/config/_sync.py +6 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +24 -5
- meerschaum/config/static.py +18 -0
- meerschaum/connectors/_Connector.py +10 -4
- meerschaum/connectors/__init__.py +4 -20
- meerschaum/connectors/api/_APIConnector.py +34 -6
- meerschaum/connectors/api/_actions.py +2 -2
- meerschaum/connectors/api/_jobs.py +1 -1
- meerschaum/connectors/api/_login.py +33 -7
- meerschaum/connectors/api/_misc.py +2 -2
- meerschaum/connectors/api/_pipes.py +15 -14
- meerschaum/connectors/api/_plugins.py +2 -2
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/api/_tokens.py +146 -0
- meerschaum/connectors/api/_users.py +70 -58
- meerschaum/connectors/instance/_InstanceConnector.py +83 -0
- meerschaum/connectors/instance/__init__.py +10 -0
- meerschaum/connectors/instance/_pipes.py +442 -0
- meerschaum/connectors/instance/_plugins.py +151 -0
- meerschaum/connectors/instance/_tokens.py +296 -0
- meerschaum/connectors/instance/_users.py +181 -0
- meerschaum/connectors/parse.py +4 -1
- meerschaum/connectors/sql/_SQLConnector.py +8 -5
- meerschaum/connectors/sql/_cli.py +12 -11
- meerschaum/connectors/sql/_create_engine.py +6 -154
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +42 -31
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +8 -1
- meerschaum/connectors/sql/_users.py +29 -2
- meerschaum/connectors/sql/tables/__init__.py +1 -1
- meerschaum/connectors/valkey/_ValkeyConnector.py +2 -4
- meerschaum/connectors/valkey/_pipes.py +9 -10
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +31 -14
- meerschaum/core/Pipe/_attributes.py +156 -58
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_data.py +41 -1
- meerschaum/core/Pipe/_dtypes.py +29 -14
- meerschaum/core/Pipe/_edit.py +12 -4
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +48 -53
- meerschaum/core/Pipe/_verify.py +1 -1
- meerschaum/{plugins → core/Plugin}/_Plugin.py +9 -11
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +221 -0
- meerschaum/core/Token/__init__.py +12 -0
- meerschaum/core/User/_User.py +34 -8
- meerschaum/core/User/__init__.py +9 -1
- meerschaum/core/__init__.py +1 -0
- meerschaum/jobs/_Job.py +3 -2
- meerschaum/jobs/__init__.py +3 -2
- meerschaum/jobs/systemd.py +1 -1
- meerschaum/models/__init__.py +35 -0
- meerschaum/models/pipes.py +247 -0
- meerschaum/models/tokens.py +38 -0
- meerschaum/models/users.py +26 -0
- meerschaum/plugins/__init__.py +22 -7
- meerschaum/plugins/bootstrap.py +2 -1
- meerschaum/utils/_get_pipes.py +68 -27
- meerschaum/utils/daemon/Daemon.py +2 -1
- meerschaum/utils/daemon/__init__.py +30 -2
- meerschaum/utils/dataframe.py +95 -14
- meerschaum/utils/dtypes/__init__.py +91 -18
- meerschaum/utils/dtypes/sql.py +44 -0
- meerschaum/utils/formatting/__init__.py +1 -1
- meerschaum/utils/formatting/_pipes.py +5 -4
- meerschaum/utils/formatting/_shell.py +11 -9
- meerschaum/utils/misc.py +237 -80
- meerschaum/utils/packages/__init__.py +3 -6
- meerschaum/utils/packages/_packages.py +34 -32
- meerschaum/utils/pipes.py +181 -0
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +3 -1
- meerschaum/utils/schedule.py +1 -0
- meerschaum/utils/sql.py +114 -37
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +5 -7
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/METADATA +88 -80
- meerschaum-3.0.0rc1.dist-info/RECORD +282 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/WHEEL +1 -1
- meerschaum/api/models/_interfaces.py +0 -15
- meerschaum/api/models/_locations.py +0 -15
- meerschaum/api/models/_metrics.py +0 -15
- meerschaum/config/static/__init__.py +0 -186
- meerschaum-2.9.5.dist-info/RECORD +0 -263
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/zip-safe +0 -0
meerschaum/actions/edit.py
CHANGED
@@ -27,6 +27,7 @@ def edit(
|
|
27
27
|
'users' : _edit_users,
|
28
28
|
'plugins' : _edit_plugins,
|
29
29
|
'jobs' : _edit_jobs,
|
30
|
+
'tokens' : _edit_tokens,
|
30
31
|
}
|
31
32
|
return choose_subaction(action, options, **kw)
|
32
33
|
|
@@ -66,7 +67,7 @@ def _complete_edit(
|
|
66
67
|
return default_action_completer(action=(['edit'] + action), **kw)
|
67
68
|
|
68
69
|
|
69
|
-
def _edit_config(action: Optional[List[str]] = None, **kw
|
70
|
+
def _edit_config(action: Optional[List[str]] = None, **kw: Any) -> SuccessTuple:
|
70
71
|
"""
|
71
72
|
Edit Meerschaum configuration files.
|
72
73
|
|
@@ -104,14 +105,14 @@ def _complete_edit_config(action: Optional[List[str]] = None, **kw: Any) -> List
|
|
104
105
|
return possibilities
|
105
106
|
|
106
107
|
def _edit_pipes(
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
108
|
+
action: Optional[List[str]] = None,
|
109
|
+
params: Optional[Dict[str, Any]] = None,
|
110
|
+
yes: bool = False,
|
111
|
+
force: bool = False,
|
112
|
+
noask: bool = False,
|
113
|
+
debug: bool = False,
|
114
|
+
**kw: Any
|
115
|
+
) -> SuccessTuple:
|
115
116
|
"""
|
116
117
|
Open and edit pipes' configuration files.
|
117
118
|
|
@@ -218,7 +219,7 @@ def _edit_users(
|
|
218
219
|
from meerschaum.utils.prompt import prompt, yes_no, get_password, get_email
|
219
220
|
from meerschaum.utils.misc import edit_file
|
220
221
|
from meerschaum.config._paths import USERS_CACHE_RESOURCES_PATH
|
221
|
-
from meerschaum.
|
222
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
222
223
|
from meerschaum.utils.yaml import yaml
|
223
224
|
import os, pathlib
|
224
225
|
instance_connector = parse_instance_keys(mrsm_instance)
|
@@ -235,7 +236,7 @@ def _edit_users(
|
|
235
236
|
minimum_length = STATIC_CONFIG['users']['min_password_length'],
|
236
237
|
)
|
237
238
|
|
238
|
-
|
239
|
+
### Make an admin
|
239
240
|
_type = ''
|
240
241
|
if yes_no(f"Change the user type for user '{username}'?", default='n', yes=yes, noask=noask):
|
241
242
|
is_admin = yes_no(
|
@@ -248,15 +249,34 @@ def _edit_users(
|
|
248
249
|
if yes_no(f"Change the email for user '{username}'?", default='n', yes=yes, noask=noask):
|
249
250
|
email = get_email(username)
|
250
251
|
|
252
|
+
attributes = {}
|
253
|
+
### Change the scopes.
|
254
|
+
attributes['scopes'] = prompt(
|
255
|
+
f"Scopes for user '{username}' (`*` to grant all scopes):",
|
256
|
+
default_editable=' '.join(User(
|
257
|
+
username,
|
258
|
+
instance=instance_connector,
|
259
|
+
).get_scopes(refresh=True, debug=debug)),
|
260
|
+
).split()
|
261
|
+
if attributes['scopes'] == ['*']:
|
262
|
+
attributes['scopes'] = list(STATIC_CONFIG['tokens']['scopes'])
|
263
|
+
|
251
264
|
### Change the attributes
|
252
|
-
attributes = None
|
253
265
|
if yes_no(
|
254
|
-
|
255
|
-
|
266
|
+
f"Edit the attributes YAML file for user '{username}'?",
|
267
|
+
default='n',
|
268
|
+
yes=yes,
|
269
|
+
noask=noask,
|
256
270
|
):
|
257
271
|
attr_path = pathlib.Path(os.path.join(USERS_CACHE_RESOURCES_PATH, f'{username}.yaml'))
|
258
272
|
try:
|
259
|
-
existing_attrs = instance_connector.get_user_attributes(
|
273
|
+
existing_attrs = instance_connector.get_user_attributes(
|
274
|
+
User(
|
275
|
+
username,
|
276
|
+
instance=instance_connector,
|
277
|
+
),
|
278
|
+
debug=debug,
|
279
|
+
)
|
260
280
|
with open(attr_path, 'w+') as f:
|
261
281
|
yaml.dump(existing_attrs, stream=f, sort_keys=False)
|
262
282
|
edit_file(attr_path)
|
@@ -271,7 +291,14 @@ def _edit_users(
|
|
271
291
|
attributes = None
|
272
292
|
|
273
293
|
### Submit changes
|
274
|
-
return User(
|
294
|
+
return User(
|
295
|
+
username,
|
296
|
+
password,
|
297
|
+
email=email,
|
298
|
+
type=_type,
|
299
|
+
attributes=attributes,
|
300
|
+
instance=instance_connector,
|
301
|
+
)
|
275
302
|
|
276
303
|
if not action:
|
277
304
|
return False, "No users to edit."
|
@@ -301,14 +328,14 @@ def _edit_users(
|
|
301
328
|
f" ({succeeded} succeeded, {failed} failed)."
|
302
329
|
)
|
303
330
|
info(msg)
|
304
|
-
return
|
331
|
+
return succeeded > 0, msg
|
305
332
|
|
306
333
|
|
307
334
|
def _edit_plugins(
|
308
335
|
action: Optional[List[str]] = None,
|
309
336
|
debug: bool = False,
|
310
337
|
**kwargs: Any
|
311
|
-
):
|
338
|
+
) -> mrsm.SuccessTuple:
|
312
339
|
"""
|
313
340
|
Edit a plugin's source code.
|
314
341
|
"""
|
@@ -507,6 +534,103 @@ def _edit_jobs(
|
|
507
534
|
return True, msg
|
508
535
|
|
509
536
|
|
537
|
+
def _edit_tokens(
|
538
|
+
action: Optional[List[str]] = None,
|
539
|
+
mrsm_instance: Optional[str] = None,
|
540
|
+
debug: bool = False,
|
541
|
+
) -> mrsm.SuccessTuple:
|
542
|
+
"""
|
543
|
+
Edit tokens registered to an instance.
|
544
|
+
"""
|
545
|
+
import uuid
|
546
|
+
from meerschaum.utils.warnings import warn
|
547
|
+
from meerschaum.utils.misc import is_uuid
|
548
|
+
from meerschaum.core import Token
|
549
|
+
from meerschaum.connectors.parse import parse_instance_keys
|
550
|
+
from meerschaum.utils.prompt import prompt, yes_no
|
551
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
552
|
+
dateutil_parser = mrsm.attempt_import('dateutil.parser')
|
553
|
+
|
554
|
+
if not action:
|
555
|
+
return False, "Provide token labels or IDs for the tokens to edit."
|
556
|
+
|
557
|
+
conn = parse_instance_keys(mrsm_instance)
|
558
|
+
|
559
|
+
labels = [
|
560
|
+
label
|
561
|
+
for label in (action or [])
|
562
|
+
if not is_uuid(label)
|
563
|
+
]
|
564
|
+
potential_token_ids = [
|
565
|
+
uuid.UUID(potential_id)
|
566
|
+
for potential_id in (action or [])
|
567
|
+
if is_uuid(potential_id)
|
568
|
+
]
|
569
|
+
|
570
|
+
tokens = conn.get_tokens(
|
571
|
+
labels=(labels or None),
|
572
|
+
ids=(potential_token_ids or None),
|
573
|
+
debug=debug,
|
574
|
+
)
|
575
|
+
|
576
|
+
num_edited = 0
|
577
|
+
for token in tokens:
|
578
|
+
token_model = token.to_model(refresh=True)
|
579
|
+
if token_model is None:
|
580
|
+
warn(f"Token '{token.id}' does not exist.", stack=False)
|
581
|
+
continue
|
582
|
+
|
583
|
+
new_attrs = {}
|
584
|
+
|
585
|
+
new_attrs['label'] = prompt("Label:", default_editable=token_model.label)
|
586
|
+
new_expiration_str = prompt(
|
587
|
+
"Expiration (empty for no expiration):",
|
588
|
+
default_editable=('' if token.expiration is None else str(token_model.expiration)),
|
589
|
+
)
|
590
|
+
new_attrs['expiration'] = (
|
591
|
+
dateutil_parser.parse(new_expiration_str)
|
592
|
+
if new_expiration_str
|
593
|
+
else None
|
594
|
+
)
|
595
|
+
new_scopes_str = prompt(
|
596
|
+
"Scope (`*` to grant all permissions):",
|
597
|
+
default_editable=' '.join(token_model.scopes),
|
598
|
+
)
|
599
|
+
new_attrs['scopes'] = (
|
600
|
+
new_scopes_str.split(' ')
|
601
|
+
if new_scopes_str != '*'
|
602
|
+
else list(STATIC_CONFIG['tokens']['scopes'])
|
603
|
+
)
|
604
|
+
invalidate = (
|
605
|
+
yes_no("Do you want to invalidate this token?", default='n')
|
606
|
+
if token_model.is_valid
|
607
|
+
else True
|
608
|
+
)
|
609
|
+
new_attrs['is_valid'] = token_model.is_valid and not invalidate
|
610
|
+
|
611
|
+
new_token = Token(**{**dict(token_model), **new_attrs})
|
612
|
+
edit_success, edit_msg = new_token.edit(debug=debug)
|
613
|
+
if not edit_success:
|
614
|
+
return False, edit_msg
|
615
|
+
|
616
|
+
if invalidate:
|
617
|
+
invalidate_success, invalidate_msg = new_token.invalidate(debug=debug)
|
618
|
+
if not invalidate_success:
|
619
|
+
return False, invalidate_msg
|
620
|
+
|
621
|
+
num_edited += 1
|
622
|
+
|
623
|
+
msg = (
|
624
|
+
f"Successfully edited {num_edited} token"
|
625
|
+
+ ('s' if num_edited != 1 else '')
|
626
|
+
+ '.'
|
627
|
+
)
|
628
|
+
|
629
|
+
return True, msg
|
630
|
+
|
631
|
+
|
632
|
+
|
633
|
+
|
510
634
|
### NOTE: This must be the final statement of the module.
|
511
635
|
### Any subactions added below these lines will not
|
512
636
|
### be added to the `help` docstring.
|
meerschaum/actions/register.py
CHANGED
@@ -7,6 +7,9 @@ Register new Pipes. Requires the API to be running.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
+
|
11
|
+
from datetime import datetime, timedelta, timezone
|
12
|
+
|
10
13
|
import meerschaum as mrsm
|
11
14
|
from meerschaum.utils.typing import SuccessTuple, Any, List, Optional, Dict
|
12
15
|
|
@@ -26,6 +29,7 @@ def register(
|
|
26
29
|
'plugins' : _register_plugins,
|
27
30
|
'users' : _register_users,
|
28
31
|
'connectors': _register_connectors,
|
32
|
+
'tokens' : _register_tokens,
|
29
33
|
}
|
30
34
|
return choose_subaction(action, options, **kw)
|
31
35
|
|
@@ -175,11 +179,11 @@ def _register_plugins(
|
|
175
179
|
reload_plugins(debug=debug)
|
176
180
|
|
177
181
|
repo_connector = parse_repo_keys(repository)
|
178
|
-
if repo_connector.
|
182
|
+
if repo_connector.type != 'api':
|
179
183
|
return False, (
|
180
184
|
"Can only upload plugins to the Meerschaum API." +
|
181
|
-
f"Connector '{repo_connector}' is
|
182
|
-
f"'{
|
185
|
+
f"Connector '{repo_connector}' is " +
|
186
|
+
f"'{type(repo_connector)}'."
|
183
187
|
)
|
184
188
|
|
185
189
|
if len(action) == 0 or action == ['']:
|
@@ -272,6 +276,7 @@ def _complete_register_plugins(*args, **kw):
|
|
272
276
|
def _register_users(
|
273
277
|
action: Optional[List[str]] = None,
|
274
278
|
mrsm_instance: Optional[str] = None,
|
279
|
+
scopes: Optional[List[str]] = None,
|
275
280
|
shell: bool = False,
|
276
281
|
debug: bool = False,
|
277
282
|
**kw: Any
|
@@ -280,7 +285,7 @@ def _register_users(
|
|
280
285
|
Register a new user to a Meerschaum instance.
|
281
286
|
"""
|
282
287
|
from meerschaum.config import get_config
|
283
|
-
from meerschaum.
|
288
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
284
289
|
from meerschaum.connectors.parse import parse_instance_keys
|
285
290
|
from meerschaum.utils.warnings import warn, info
|
286
291
|
from meerschaum.core import User
|
@@ -333,9 +338,17 @@ def _register_users(
|
|
333
338
|
)
|
334
339
|
if len(email) == 0:
|
335
340
|
email = None
|
336
|
-
user = User(
|
341
|
+
user = User(
|
342
|
+
username,
|
343
|
+
password,
|
344
|
+
email=email,
|
345
|
+
attributes={
|
346
|
+
'scopes': scopes or list(STATIC_CONFIG['tokens']['scopes']),
|
347
|
+
},
|
348
|
+
instance=instance_connector,
|
349
|
+
)
|
337
350
|
info(f"Registering user '{user}' to Meerschaum instance '{instance_connector}'...")
|
338
|
-
result_tuple =
|
351
|
+
result_tuple = user.register(debug=debug)
|
339
352
|
print_tuple(result_tuple)
|
340
353
|
success[username] = result_tuple[0]
|
341
354
|
if success[username]:
|
@@ -443,6 +456,124 @@ def _complete_register_connectors(
|
|
443
456
|
return _complete_show_connectors(action)
|
444
457
|
|
445
458
|
|
459
|
+
def _register_tokens(
|
460
|
+
mrsm_instance: Optional[str] = None,
|
461
|
+
name: Optional[str] = None,
|
462
|
+
ttl_days: Optional[int] = None,
|
463
|
+
scopes: Optional[List[str]] = None,
|
464
|
+
end: Optional[datetime] = None,
|
465
|
+
force: bool = False,
|
466
|
+
yes: bool = False,
|
467
|
+
noask: bool = False,
|
468
|
+
nopretty: bool = False,
|
469
|
+
debug: bool = False,
|
470
|
+
**kwargs: Any
|
471
|
+
) -> mrsm.SuccessTuple:
|
472
|
+
"""
|
473
|
+
Register a new long-lived access token for API access.
|
474
|
+
Note that omitting and end time or TTL will generate token which does not expire.
|
475
|
+
|
476
|
+
Examples:
|
477
|
+
|
478
|
+
mrsm register token --end 2032-01-01
|
479
|
+
|
480
|
+
mrsm register token --ttl-days 1000
|
481
|
+
|
482
|
+
mrsm register token --name weather-sensor
|
483
|
+
|
484
|
+
"""
|
485
|
+
import json
|
486
|
+
from meerschaum.utils.schedule import parse_start_time
|
487
|
+
from meerschaum.core import User
|
488
|
+
from meerschaum.core.Token._Token import Token, _PLACEHOLDER_EXPIRATION
|
489
|
+
from meerschaum.connectors.parse import parse_instance_keys
|
490
|
+
from meerschaum.utils.prompt import yes_no, choose
|
491
|
+
from meerschaum.utils.formatting import make_header, print_tuple
|
492
|
+
from meerschaum.utils.dtypes import value_is_null
|
493
|
+
|
494
|
+
expiration = (
|
495
|
+
end
|
496
|
+
if end is not None
|
497
|
+
else (
|
498
|
+
parse_start_time(f"starting in {ttl_days} days")
|
499
|
+
if ttl_days
|
500
|
+
else _PLACEHOLDER_EXPIRATION
|
501
|
+
)
|
502
|
+
)
|
503
|
+
|
504
|
+
instance_connector = parse_instance_keys(mrsm_instance)
|
505
|
+
user = None
|
506
|
+
if instance_connector.type != 'api':
|
507
|
+
usernames = instance_connector.get_users(debug=debug)
|
508
|
+
if not usernames:
|
509
|
+
return False, f"No users are registered to '{instance_connector}'."
|
510
|
+
|
511
|
+
username = choose(
|
512
|
+
"To which user should this token be registered? Enter the number.",
|
513
|
+
usernames,
|
514
|
+
)
|
515
|
+
user_id = instance_connector.get_user_id(
|
516
|
+
User(username, instance=mrsm_instance),
|
517
|
+
debug=debug,
|
518
|
+
)
|
519
|
+
if user_id is None:
|
520
|
+
return False, f"Cannot load ID for user '{username}'."
|
521
|
+
|
522
|
+
user = User(username, user_id=user_id, instance=mrsm_instance)
|
523
|
+
|
524
|
+
token = Token(
|
525
|
+
label=name,
|
526
|
+
expiration=expiration,
|
527
|
+
scopes=scopes,
|
528
|
+
user=user,
|
529
|
+
instance=mrsm_instance,
|
530
|
+
)
|
531
|
+
|
532
|
+
register_success, register_msg = token.register(debug=debug)
|
533
|
+
token_id = token.id
|
534
|
+
token_secret = token.secret
|
535
|
+
if not register_success:
|
536
|
+
return False, f"Failed to register token '{token.label}':\n{register_msg}"
|
537
|
+
|
538
|
+
token_model = token.to_model(refresh=True)
|
539
|
+
token_kwargs = dict(token_model)
|
540
|
+
token_kwargs['id'] = token_id
|
541
|
+
token_kwargs['secret'] = token_secret
|
542
|
+
token = Token(**token_kwargs)
|
543
|
+
|
544
|
+
if not nopretty:
|
545
|
+
print_tuple(
|
546
|
+
(
|
547
|
+
True,
|
548
|
+
(
|
549
|
+
f"Registered token '{token}'.\n "
|
550
|
+
"Write down the client secret, because it won't be shown again."
|
551
|
+
)
|
552
|
+
),
|
553
|
+
calm=True
|
554
|
+
)
|
555
|
+
|
556
|
+
msg_to_print = (
|
557
|
+
(
|
558
|
+
make_header(f"Attributes for token '{token}':") + "\n"
|
559
|
+
+ f" - Client ID: {token_model.id}\n"
|
560
|
+
+ f" - Client Secret: {token_secret}\n"
|
561
|
+
+ " - Expiration: "
|
562
|
+
+ (
|
563
|
+
token.expiration.isoformat()
|
564
|
+
if not value_is_null(token.expiration)
|
565
|
+
else 'Does not expire.'
|
566
|
+
)
|
567
|
+
+ "\n"
|
568
|
+
+ f" - API Key: {token.get_api_key()}\n"
|
569
|
+
)
|
570
|
+
if not nopretty
|
571
|
+
else json.dumps({'secret': token_secret, **dict(token_model)})
|
572
|
+
)
|
573
|
+
print(msg_to_print)
|
574
|
+
return True, f"Successfully registered token '{token.label}'."
|
575
|
+
|
576
|
+
|
446
577
|
### NOTE: This must be the final statement of the module.
|
447
578
|
### Any subactions added below these lines will not
|
448
579
|
### be added to the `help` docstring.
|
meerschaum/actions/show.py
CHANGED
@@ -45,6 +45,7 @@ def show(
|
|
45
45
|
'tags' : _show_tags,
|
46
46
|
'schedules' : _show_schedules,
|
47
47
|
'venvs' : _show_venvs,
|
48
|
+
'tokens' : _show_tokens,
|
48
49
|
}
|
49
50
|
return choose_subaction(action, show_options, **kw)
|
50
51
|
|
@@ -113,11 +114,11 @@ def _show_help(**kw: Any) -> SuccessTuple:
|
|
113
114
|
|
114
115
|
|
115
116
|
def _show_config(
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
117
|
+
action: Optional[List[str]] = None,
|
118
|
+
debug: bool = False,
|
119
|
+
nopretty: bool = False,
|
120
|
+
**kw: Any
|
121
|
+
) -> SuccessTuple:
|
121
122
|
"""
|
122
123
|
Show the configuration dictionary.
|
123
124
|
Sub-actions defined in the action list are recursive indices in the config dictionary.
|
@@ -789,34 +790,22 @@ def _show_tags(
|
|
789
790
|
from meerschaum.utils.formatting import pipe_repr, UNICODE, ANSI
|
790
791
|
from meerschaum.utils.pool import get_pool
|
791
792
|
from meerschaum.config import get_config
|
793
|
+
from meerschaum.connectors.parse import parse_instance_keys
|
792
794
|
rich_tree, rich_panel, rich_text, rich_console, rich_columns = (
|
793
795
|
mrsm.attempt_import('rich.tree', 'rich.panel', 'rich.text', 'rich.console', 'rich.columns')
|
794
796
|
)
|
795
|
-
panel = rich_panel.Panel.fit('Tags')
|
796
|
-
tree = rich_tree.Tree(panel)
|
797
797
|
action = action or []
|
798
798
|
tags = action + (tags or [])
|
799
|
-
pipes = mrsm.get_pipes(as_list=True, tags=tags, **kwargs)
|
800
|
-
if not pipes:
|
801
|
-
return False, f"No pipes were found with the given tags."
|
802
799
|
|
803
|
-
pool = get_pool(workers=workers)
|
804
800
|
tag_prefix = get_config('formatting', 'pipes', 'unicode', 'icons', 'tag') if UNICODE else ''
|
805
801
|
tag_style = get_config('formatting', 'pipes', 'ansi', 'styles', 'tags') if ANSI else None
|
806
802
|
|
807
|
-
tags_pipes =
|
808
|
-
|
809
|
-
|
810
|
-
pipes_tags = dict(pool.map(gather_pipe_tags, pipes))
|
811
|
-
|
812
|
-
for pipe, tags in pipes_tags.items():
|
813
|
-
for tag in tags:
|
814
|
-
if action and tag not in action:
|
815
|
-
continue
|
816
|
-
tags_pipes[tag].append(pipe)
|
803
|
+
tags_pipes = mrsm.get_pipes(as_tags_dict=True, tags=tags, **kwargs)
|
804
|
+
if action:
|
805
|
+
tags_pipes = {tag: pipes for tag, pipes in tags_pipes.items() if tag in action}
|
817
806
|
|
818
807
|
columns = []
|
819
|
-
sorted_tags = sorted(
|
808
|
+
sorted_tags = sorted(list(tags_pipes))
|
820
809
|
for tag in sorted_tags:
|
821
810
|
_pipes = tags_pipes[tag]
|
822
811
|
tag_text = (
|
@@ -904,9 +893,7 @@ def _show_schedules(
|
|
904
893
|
return True, "Success"
|
905
894
|
|
906
895
|
|
907
|
-
def _show_venvs(
|
908
|
-
**kwargs: Any
|
909
|
-
):
|
896
|
+
def _show_venvs(**kwargs: Any) -> SuccessTuple:
|
910
897
|
"""
|
911
898
|
Print the available virtual environments in the current MRSM_ROOT_DIR.
|
912
899
|
"""
|
@@ -921,12 +908,113 @@ def _show_venvs(
|
|
921
908
|
for _venv in os.listdir(VIRTENV_RESOURCES_PATH)
|
922
909
|
if venv_exists(_venv)
|
923
910
|
]
|
924
|
-
print_options(
|
925
|
-
|
926
|
-
|
927
|
-
|
911
|
+
print_options(venvs, name='Virtual Environments:', **kwargs)
|
912
|
+
return True, "Success"
|
913
|
+
|
914
|
+
|
915
|
+
def _show_tokens(
|
916
|
+
action: Optional[List[str]] = None,
|
917
|
+
mrsm_instance: Optional[str] = None,
|
918
|
+
nopretty: bool = False,
|
919
|
+
debug: bool = False,
|
920
|
+
**kwargs: Any
|
921
|
+
) -> SuccessTuple:
|
922
|
+
"""
|
923
|
+
Print a table of the registered tokens on the instance.
|
924
|
+
"""
|
925
|
+
import json
|
926
|
+
import uuid
|
927
|
+
from meerschaum.connectors.parse import parse_instance_keys
|
928
|
+
from meerschaum.utils.dtypes import value_is_null, json_serialize_value
|
929
|
+
from meerschaum.utils.misc import is_uuid
|
930
|
+
from meerschaum.utils.packages import import_rich
|
931
|
+
from meerschaum.utils.formatting import UNICODE, get_console
|
932
|
+
rich = import_rich()
|
933
|
+
rich_table, rich_json = mrsm.attempt_import('rich.table', 'rich.json')
|
934
|
+
|
935
|
+
conn = parse_instance_keys(mrsm_instance)
|
936
|
+
|
937
|
+
labels = [
|
938
|
+
label
|
939
|
+
for label in (action or [])
|
940
|
+
if not is_uuid(label)
|
941
|
+
]
|
942
|
+
potential_token_ids = [
|
943
|
+
uuid.UUID(potential_id)
|
944
|
+
for potential_id in (action or [])
|
945
|
+
if is_uuid(potential_id)
|
946
|
+
]
|
947
|
+
|
948
|
+
tokens = conn.get_tokens(
|
949
|
+
labels=(labels or None),
|
950
|
+
ids=(potential_token_ids or None),
|
951
|
+
debug=debug,
|
952
|
+
)
|
953
|
+
|
954
|
+
if nopretty:
|
955
|
+
for token in tokens:
|
956
|
+
print(json.dumps({
|
957
|
+
'id': str(token.id),
|
958
|
+
'label': token.label,
|
959
|
+
'scopes': token.scopes,
|
960
|
+
'expiration': (
|
961
|
+
token.expiration.isoformat()
|
962
|
+
if not value_is_null(token.expiration)
|
963
|
+
else None
|
964
|
+
),
|
965
|
+
'creation': (token.creation.isoformat() if not value_is_null(token.creation) else None),
|
966
|
+
'user': (token.user.username if token.user is not None else None),
|
967
|
+
'is_valid': token.is_valid,
|
968
|
+
}))
|
969
|
+
return True, "Success"
|
970
|
+
|
971
|
+
if len(tokens) == 1:
|
972
|
+
token = tokens[0]
|
973
|
+
tokens_json = json.dumps({
|
974
|
+
'id': str(token.id),
|
975
|
+
'label': token.label,
|
976
|
+
'scopes': token.scopes,
|
977
|
+
'creation': (token.creation.isoformat() if not value_is_null(token.creation) else None),
|
978
|
+
'expiration': (token.expiration.isoformat() if not value_is_null(token.expiration) else None),
|
979
|
+
'user': (token.user.username if token.user is not None else None),
|
980
|
+
'is_valid': token.is_valid,
|
981
|
+
}, default=json_serialize_value, indent=4)
|
982
|
+
get_console().print(rich_json.JSON(tokens_json))
|
983
|
+
|
984
|
+
return True, "Success"
|
985
|
+
|
986
|
+
is_valid_true = (
|
987
|
+
"🟢"
|
988
|
+
if UNICODE
|
989
|
+
else "[+]"
|
928
990
|
)
|
991
|
+
is_valid_false = (
|
992
|
+
"🔴"
|
993
|
+
if UNICODE
|
994
|
+
else "[-]"
|
995
|
+
)
|
996
|
+
|
997
|
+
table = rich_table.Table(title=f"Registered Tokens on instance '{conn}'")
|
998
|
+
table.add_column("ID")
|
999
|
+
table.add_column("Label")
|
1000
|
+
table.add_column("User")
|
1001
|
+
table.add_column("Expiration")
|
1002
|
+
table.add_column("Valid")
|
1003
|
+
|
1004
|
+
for token in tokens:
|
1005
|
+
table.add_row(
|
1006
|
+
str(token.id),
|
1007
|
+
token.label,
|
1008
|
+
(token.user.username if token.user is not None else ""),
|
1009
|
+
(
|
1010
|
+
token.expiration.isoformat()
|
1011
|
+
if not value_is_null(token.expiration)
|
1012
|
+
else 'Does not expire'
|
1013
|
+
),
|
1014
|
+
(is_valid_true if token.is_valid else is_valid_false),
|
1015
|
+
)
|
929
1016
|
|
1017
|
+
mrsm.pprint(table)
|
930
1018
|
return True, "Success"
|
931
1019
|
|
932
1020
|
|
meerschaum/actions/stop.py
CHANGED
@@ -7,7 +7,8 @@ Stop running jobs that were started with `-d` or `start job`.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
-
from meerschaum.utils.typing import Optional, List,
|
10
|
+
from meerschaum.utils.typing import Optional, List, SuccessTuple, Any
|
11
|
+
|
11
12
|
|
12
13
|
def stop(action: Optional[List[str]] = None, **kw) -> SuccessTuple:
|
13
14
|
"""
|
@@ -111,6 +112,8 @@ def _stop_jobs(
|
|
111
112
|
)
|
112
113
|
|
113
114
|
if not jobs_to_stop:
|
115
|
+
if jobs:
|
116
|
+
return True, "The selected jobs are currently running."
|
114
117
|
return False, "No running, paused or restarting jobs to stop."
|
115
118
|
|
116
119
|
if not action:
|
meerschaum/actions/sync.py
CHANGED
@@ -287,7 +287,7 @@ def _sync_pipes(
|
|
287
287
|
from meerschaum.utils.formatting._shell import progress
|
288
288
|
from meerschaum.utils.formatting._shell import clear_screen
|
289
289
|
from meerschaum.utils.formatting import print_pipes_results
|
290
|
-
from meerschaum.
|
290
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
291
291
|
from meerschaum.utils.misc import interval_str
|
292
292
|
|
293
293
|
noninteractive_val = os.environ.get(STATIC_CONFIG['environment']['noninteractive'], None)
|
meerschaum/actions/tag.py
CHANGED
@@ -8,12 +8,12 @@ Functions for editing elements belong here.
|
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
10
|
import meerschaum as mrsm
|
11
|
-
from meerschaum.utils.typing import List, Any, SuccessTuple, Optional
|
11
|
+
from meerschaum.utils.typing import List, Any, SuccessTuple, Optional
|
12
12
|
|
13
13
|
def tag(
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
action: Optional[List[str]] = None,
|
15
|
+
**kwargs: Any
|
16
|
+
) -> SuccessTuple:
|
17
17
|
"""
|
18
18
|
Edit an existing element.
|
19
19
|
"""
|
@@ -25,10 +25,10 @@ def tag(
|
|
25
25
|
|
26
26
|
|
27
27
|
def _tag_pipes(
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
action: Optional[List[str]] = None,
|
29
|
+
debug: bool = False,
|
30
|
+
**kwargs: Any
|
31
|
+
) -> SuccessTuple:
|
32
32
|
"""
|
33
33
|
Add or remove tags to registered pipes.
|
34
34
|
Prefix a tag with a leading underscore to remove it.
|
@@ -68,6 +68,7 @@ def _tag_pipes(
|
|
68
68
|
pipe_was_edited = True
|
69
69
|
|
70
70
|
if pipe_was_edited:
|
71
|
+
pipe.tags = pipe_tags
|
71
72
|
edited_pipes.append(pipe)
|
72
73
|
|
73
74
|
if not edited_pipes:
|