meerschaum 2.9.4__py3-none-any.whl → 3.0.0__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 +33 -4
- meerschaum/_internal/cli/__init__.py +6 -0
- meerschaum/_internal/cli/daemons.py +103 -0
- meerschaum/_internal/cli/entry.py +220 -0
- meerschaum/_internal/cli/workers.py +435 -0
- meerschaum/_internal/docs/index.py +48 -2
- meerschaum/_internal/entry.py +50 -14
- meerschaum/_internal/shell/Shell.py +121 -29
- meerschaum/_internal/shell/__init__.py +4 -1
- meerschaum/_internal/static.py +359 -0
- meerschaum/_internal/term/TermPageHandler.py +1 -2
- meerschaum/_internal/term/__init__.py +40 -6
- meerschaum/_internal/term/tools.py +33 -8
- meerschaum/actions/__init__.py +6 -4
- meerschaum/actions/api.py +53 -13
- meerschaum/actions/attach.py +1 -0
- meerschaum/actions/bootstrap.py +8 -8
- meerschaum/actions/delete.py +4 -2
- meerschaum/actions/edit.py +171 -25
- meerschaum/actions/login.py +8 -8
- meerschaum/actions/register.py +143 -6
- meerschaum/actions/reload.py +22 -5
- meerschaum/actions/restart.py +14 -0
- meerschaum/actions/show.py +184 -31
- meerschaum/actions/start.py +166 -17
- meerschaum/actions/stop.py +38 -2
- meerschaum/actions/sync.py +7 -2
- meerschaum/actions/tag.py +9 -8
- meerschaum/actions/verify.py +5 -8
- meerschaum/api/__init__.py +45 -15
- meerschaum/api/_events.py +46 -4
- meerschaum/api/_oauth2.py +162 -9
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -3
- meerschaum/api/dash/callbacks/__init__.py +1 -0
- meerschaum/api/dash/callbacks/custom.py +4 -3
- meerschaum/api/dash/callbacks/dashboard.py +228 -117
- meerschaum/api/dash/callbacks/jobs.py +14 -7
- meerschaum/api/dash/callbacks/login.py +10 -1
- meerschaum/api/dash/callbacks/pipes.py +194 -14
- meerschaum/api/dash/callbacks/plugins.py +0 -1
- meerschaum/api/dash/callbacks/register.py +10 -3
- meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
- meerschaum/api/dash/callbacks/tokens.py +389 -0
- meerschaum/api/dash/components.py +36 -15
- meerschaum/api/dash/jobs.py +1 -1
- meerschaum/api/dash/keys.py +35 -93
- meerschaum/api/dash/pages/__init__.py +2 -1
- meerschaum/api/dash/pages/dashboard.py +1 -20
- meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
- meerschaum/api/dash/pages/login.py +2 -2
- meerschaum/api/dash/pages/pipes.py +16 -5
- meerschaum/api/dash/pages/settings/password_reset.py +1 -1
- meerschaum/api/dash/pages/tokens.py +53 -0
- meerschaum/api/dash/pipes.py +438 -88
- meerschaum/api/dash/sessions.py +12 -0
- meerschaum/api/dash/tokens.py +603 -0
- meerschaum/api/dash/websockets.py +1 -1
- meerschaum/api/dash/webterm.py +18 -6
- meerschaum/api/models/__init__.py +23 -3
- meerschaum/api/models/_actions.py +22 -0
- meerschaum/api/models/_pipes.py +91 -7
- meerschaum/api/models/_tokens.py +81 -0
- meerschaum/api/resources/static/css/dash.css +16 -0
- meerschaum/api/resources/static/js/terminado.js +3 -0
- meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
- meerschaum/api/resources/templates/termpage.html +13 -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 +26 -35
- meerschaum/api/routes/_login.py +120 -15
- meerschaum/api/routes/_misc.py +5 -10
- meerschaum/api/routes/_pipes.py +178 -143
- 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/api/routes/_webterm.py +3 -3
- meerschaum/config/__init__.py +100 -30
- meerschaum/config/_default.py +132 -64
- meerschaum/config/_edit.py +38 -32
- meerschaum/config/_formatting.py +2 -0
- meerschaum/config/_patch.py +10 -8
- meerschaum/config/_paths.py +133 -13
- meerschaum/config/_read_config.py +87 -36
- meerschaum/config/_sync.py +6 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/environment.py +262 -0
- meerschaum/config/stack/__init__.py +37 -15
- meerschaum/config/static.py +18 -0
- meerschaum/connectors/_Connector.py +11 -6
- meerschaum/connectors/__init__.py +41 -22
- meerschaum/connectors/api/_APIConnector.py +34 -6
- meerschaum/connectors/api/_actions.py +2 -2
- meerschaum/connectors/api/_jobs.py +12 -1
- meerschaum/connectors/api/_login.py +33 -7
- meerschaum/connectors/api/_misc.py +2 -2
- meerschaum/connectors/api/_pipes.py +23 -32
- 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 +159 -0
- meerschaum/connectors/instance/_tokens.py +317 -0
- meerschaum/connectors/instance/_users.py +188 -0
- meerschaum/connectors/parse.py +5 -2
- meerschaum/connectors/sql/_SQLConnector.py +22 -5
- meerschaum/connectors/sql/_cli.py +12 -11
- meerschaum/connectors/sql/_create_engine.py +12 -168
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +295 -278
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +47 -22
- meerschaum/connectors/sql/_users.py +36 -2
- meerschaum/connectors/sql/tables/__init__.py +254 -122
- meerschaum/connectors/valkey/_ValkeyConnector.py +5 -7
- meerschaum/connectors/valkey/_pipes.py +60 -31
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +115 -85
- meerschaum/core/Pipe/_attributes.py +425 -124
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_cache.py +555 -0
- meerschaum/core/Pipe/_clear.py +0 -11
- meerschaum/core/Pipe/_data.py +96 -68
- meerschaum/core/Pipe/_deduplicate.py +0 -13
- meerschaum/core/Pipe/_delete.py +12 -21
- meerschaum/core/Pipe/_drop.py +11 -23
- meerschaum/core/Pipe/_dtypes.py +49 -19
- meerschaum/core/Pipe/_edit.py +14 -4
- meerschaum/core/Pipe/_fetch.py +1 -1
- meerschaum/core/Pipe/_index.py +8 -14
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +123 -204
- meerschaum/core/Pipe/_verify.py +4 -4
- meerschaum/{plugins → core/Plugin}/_Plugin.py +16 -12
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +220 -0
- meerschaum/core/Token/__init__.py +12 -0
- meerschaum/core/User/_User.py +35 -10
- meerschaum/core/User/__init__.py +9 -1
- meerschaum/core/__init__.py +1 -0
- meerschaum/jobs/_Executor.py +88 -4
- meerschaum/jobs/_Job.py +149 -38
- meerschaum/jobs/__init__.py +3 -2
- meerschaum/jobs/systemd.py +8 -3
- 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 +301 -88
- meerschaum/plugins/bootstrap.py +510 -4
- meerschaum/utils/_get_pipes.py +97 -30
- meerschaum/utils/daemon/Daemon.py +199 -43
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
- meerschaum/utils/daemon/RotatingFile.py +63 -36
- meerschaum/utils/daemon/StdinFile.py +53 -13
- meerschaum/utils/daemon/__init__.py +47 -6
- meerschaum/utils/daemon/_names.py +6 -3
- meerschaum/utils/dataframe.py +480 -82
- meerschaum/utils/debug.py +49 -19
- meerschaum/utils/dtypes/__init__.py +478 -37
- meerschaum/utils/dtypes/sql.py +369 -29
- meerschaum/utils/formatting/__init__.py +5 -2
- meerschaum/utils/formatting/_jobs.py +1 -1
- meerschaum/utils/formatting/_pipes.py +52 -50
- meerschaum/utils/formatting/_pprint.py +1 -0
- meerschaum/utils/formatting/_shell.py +44 -18
- meerschaum/utils/misc.py +268 -186
- meerschaum/utils/packages/__init__.py +25 -40
- meerschaum/utils/packages/_packages.py +42 -34
- meerschaum/utils/pipes.py +213 -0
- meerschaum/utils/process.py +2 -2
- meerschaum/utils/prompt.py +175 -144
- meerschaum/utils/schedule.py +2 -1
- meerschaum/utils/sql.py +135 -49
- meerschaum/utils/threading.py +42 -0
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +7 -7
- meerschaum/utils/warnings.py +19 -13
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/METADATA +94 -96
- meerschaum-3.0.0.dist-info/RECORD +289 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/WHEEL +1 -1
- meerschaum-3.0.0.dist-info/licenses/NOTICE +2 -0
- meerschaum/api/models/_interfaces.py +0 -15
- meerschaum/api/models/_locations.py +0 -15
- meerschaum/api/models/_metrics.py +0 -15
- meerschaum/config/_environment.py +0 -145
- meerschaum/config/static/__init__.py +0 -186
- meerschaum-2.9.4.dist-info/RECORD +0 -263
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.4.dist-info → meerschaum-3.0.0.dist-info}/zip-safe +0 -0
@@ -0,0 +1,389 @@
|
|
1
|
+
#! /usr/bin/env python3
|
2
|
+
# vim:fenc=utf-8
|
3
|
+
|
4
|
+
"""
|
5
|
+
Define the callbacks for the tokens page.
|
6
|
+
"""
|
7
|
+
|
8
|
+
import json
|
9
|
+
from typing import Optional, List, Dict, Any, Tuple
|
10
|
+
from datetime import datetime, timezone
|
11
|
+
|
12
|
+
import dash
|
13
|
+
from dash.dependencies import Input, Output, State, MATCH, ALL
|
14
|
+
from dash.exceptions import PreventUpdate
|
15
|
+
import dash_bootstrap_components as dbc
|
16
|
+
import dash.html as html
|
17
|
+
import dash.dcc as dcc
|
18
|
+
|
19
|
+
import meerschaum as mrsm
|
20
|
+
from meerschaum.api import get_api_connector, debug
|
21
|
+
from meerschaum.api.dash import dash_app
|
22
|
+
from meerschaum.api.dash.sessions import get_user_from_session
|
23
|
+
from meerschaum.api.dash.components import alert_from_success_tuple, build_cards_grid
|
24
|
+
from meerschaum.api.dash.tokens import (
|
25
|
+
get_tokens_cards,
|
26
|
+
get_tokens_table,
|
27
|
+
build_tokens_register_input_modal,
|
28
|
+
build_tokens_register_output_modal,
|
29
|
+
)
|
30
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
31
|
+
from meerschaum.utils.daemon import get_new_daemon_name
|
32
|
+
from meerschaum.core import Token
|
33
|
+
|
34
|
+
|
35
|
+
@dash_app.callback(
|
36
|
+
Output('tokens-output-div', 'children'),
|
37
|
+
Output('tokens-register-input-modal', 'children'),
|
38
|
+
Output('tokens-alert-div', 'children'),
|
39
|
+
Input('tokens-refresh-button', 'n_clicks'),
|
40
|
+
State('session-store', 'data'),
|
41
|
+
)
|
42
|
+
def refresh_tokens_button_click(
|
43
|
+
n_clicks: Optional[int],
|
44
|
+
session_data: Optional[Dict[str, Any]] = None,
|
45
|
+
):
|
46
|
+
"""
|
47
|
+
Build the tokens cards on load or refresh.
|
48
|
+
"""
|
49
|
+
session_id = (session_data or {}).get('session-id', None)
|
50
|
+
tokens_table, alerts = get_tokens_table(session_id)
|
51
|
+
if not tokens_table:
|
52
|
+
return (
|
53
|
+
[
|
54
|
+
html.H4('No tokens registered.'),
|
55
|
+
html.P('Click the `+` button to register a new token.'),
|
56
|
+
],
|
57
|
+
build_tokens_register_input_modal(),
|
58
|
+
alerts,
|
59
|
+
)
|
60
|
+
|
61
|
+
return tokens_table, build_tokens_register_input_modal(), alerts
|
62
|
+
|
63
|
+
|
64
|
+
@dash_app.callback(
|
65
|
+
Output('tokens-register-input-modal', 'is_open'),
|
66
|
+
Input('tokens-create-button', 'n_clicks'),
|
67
|
+
prevent_initial_call=True,
|
68
|
+
)
|
69
|
+
def create_tokens_button_click(n_clicks: Optional[int]):
|
70
|
+
"""
|
71
|
+
Open the tokens registration modal when the plus button is clicked.
|
72
|
+
"""
|
73
|
+
if not n_clicks:
|
74
|
+
raise PreventUpdate
|
75
|
+
|
76
|
+
return True
|
77
|
+
|
78
|
+
|
79
|
+
@dash_app.callback(
|
80
|
+
Output("tokens-scopes-checklist-div", 'style'),
|
81
|
+
Input("tokens-toggle-scopes-switch", 'value'),
|
82
|
+
prevent_initial_call=True,
|
83
|
+
)
|
84
|
+
def toggle_token_scopes_checklist(value: bool):
|
85
|
+
"""
|
86
|
+
Toggle the scopes checklist.
|
87
|
+
"""
|
88
|
+
return {'display': 'none'} if value else {}
|
89
|
+
|
90
|
+
|
91
|
+
@dash_app.callback(
|
92
|
+
Output('tokens-scopes-checklist', 'value'),
|
93
|
+
Output('tokens-deselect-scopes-button', 'children'),
|
94
|
+
Input('tokens-deselect-scopes-button', 'n_clicks'),
|
95
|
+
State('tokens-deselect-scopes-button', 'children'),
|
96
|
+
prevent_initial_call=True,
|
97
|
+
)
|
98
|
+
def deselect_scopes_click(n_clicks: Optional[int], name: str):
|
99
|
+
"""
|
100
|
+
Set the value of the scopes checklist to an empty list.
|
101
|
+
"""
|
102
|
+
if not n_clicks:
|
103
|
+
raise PreventUpdate
|
104
|
+
|
105
|
+
new_name = 'Select all' if name == 'Deselect all' else 'Deselect all'
|
106
|
+
value = (
|
107
|
+
[]
|
108
|
+
if name == 'Deselect all'
|
109
|
+
else list(STATIC_CONFIG['tokens']['scopes'])
|
110
|
+
)
|
111
|
+
|
112
|
+
return value, new_name
|
113
|
+
|
114
|
+
|
115
|
+
@dash_app.callback(
|
116
|
+
Output({'type': 'tokens-scopes-checklist', 'index': MATCH}, 'value'),
|
117
|
+
Output({'type': 'tokens-deselect-scopes-button', 'index': MATCH}, 'children'),
|
118
|
+
Input({'type': 'tokens-deselect-scopes-button', 'index': MATCH}, 'n_clicks'),
|
119
|
+
State({'type': 'tokens-deselect-scopes-button', 'index': MATCH}, 'children'),
|
120
|
+
prevent_initial_call=True,
|
121
|
+
)
|
122
|
+
def edit_token_deselect_scopes_click(n_clicks: Optional[int], name: str):
|
123
|
+
"""
|
124
|
+
Set the value of the scopes checklist to an empty list.
|
125
|
+
"""
|
126
|
+
if not n_clicks:
|
127
|
+
raise PreventUpdate
|
128
|
+
|
129
|
+
new_name = 'Select all' if name == 'Deselect all' else 'Deselect all'
|
130
|
+
value = (
|
131
|
+
[]
|
132
|
+
if name == 'Deselect all'
|
133
|
+
else list(STATIC_CONFIG['tokens']['scopes'])
|
134
|
+
)
|
135
|
+
|
136
|
+
return value, new_name
|
137
|
+
|
138
|
+
|
139
|
+
@dash_app.callback(
|
140
|
+
Output('tokens-register-input-modal', 'is_open'),
|
141
|
+
Output('tokens-register-output-modal', 'is_open'),
|
142
|
+
Output('tokens-register-output-modal', 'children'),
|
143
|
+
Input('tokens-register-button', 'n_clicks'),
|
144
|
+
State('tokens-name-input', 'value'),
|
145
|
+
State('tokens-scopes-checklist', 'value'),
|
146
|
+
State('tokens-expiration-datepickersingle', 'date'),
|
147
|
+
State('session-store', 'data'),
|
148
|
+
prevent_initial_call=True,
|
149
|
+
)
|
150
|
+
def register_token_click(
|
151
|
+
n_clicks: Optional[int],
|
152
|
+
name: str,
|
153
|
+
scopes: List[str],
|
154
|
+
expiration: Optional[datetime] = None,
|
155
|
+
session_data: Optional[Dict[str, Any]] = None,
|
156
|
+
):
|
157
|
+
"""
|
158
|
+
Register the token.
|
159
|
+
"""
|
160
|
+
if not n_clicks:
|
161
|
+
raise PreventUpdate
|
162
|
+
|
163
|
+
session_id = (session_data or {}).get('session-id', None)
|
164
|
+
token = Token(
|
165
|
+
label=(name or None),
|
166
|
+
user=get_user_from_session(session_id),
|
167
|
+
expiration=(datetime.fromisoformat(f"{expiration}") if expiration is not None else None),
|
168
|
+
)
|
169
|
+
return False, True, build_tokens_register_output_modal(token)
|
170
|
+
|
171
|
+
|
172
|
+
@dash_app.callback(
|
173
|
+
Output("tokens-refresh-button", "n_clicks"),
|
174
|
+
Input("tokens-register-output-modal", "is_open"),
|
175
|
+
Input({'type': 'tokens-edit-modal', 'index': ALL}, 'is_open'),
|
176
|
+
Input({'type': 'tokens-invalidate-modal', 'index': ALL}, 'is_open'),
|
177
|
+
Input({'type': 'tokens-delete-modal', 'index': ALL}, 'is_open'),
|
178
|
+
State("tokens-refresh-button", "n_clicks"),
|
179
|
+
prevent_initial_call=True,
|
180
|
+
)
|
181
|
+
def register_token_modal_close_refresh(
|
182
|
+
register_is_open: bool,
|
183
|
+
edit_is_open_list,
|
184
|
+
invalidate_is_open_list,
|
185
|
+
delete_is_open_list,
|
186
|
+
n_clicks: int,
|
187
|
+
):
|
188
|
+
"""
|
189
|
+
Refresh the cards when the registration, edit, invalidate, or delete modals changes visibility.
|
190
|
+
"""
|
191
|
+
if any(
|
192
|
+
edit_is_open_list
|
193
|
+
+ invalidate_is_open_list
|
194
|
+
+ delete_is_open_list
|
195
|
+
):
|
196
|
+
raise PreventUpdate
|
197
|
+
|
198
|
+
return (n_clicks or 0) + 1
|
199
|
+
|
200
|
+
|
201
|
+
@dash_app.callback(
|
202
|
+
Output('tokens-register-clipboard', 'content'),
|
203
|
+
Output('tokens-register-clipboard', 'n_clicks'),
|
204
|
+
Output('tokens-register-copy-button', 'children'),
|
205
|
+
Input('tokens-register-copy-button', 'n_clicks'),
|
206
|
+
State('tokens-register-clipboard', 'n_clicks'),
|
207
|
+
State('token-id-pre', 'children'),
|
208
|
+
State('token-secret-pre', 'children'),
|
209
|
+
prevent_initial_call=True,
|
210
|
+
)
|
211
|
+
def copy_token_button_click(
|
212
|
+
n_clicks: int,
|
213
|
+
clipboard_n_clicks: Optional[int],
|
214
|
+
token_id: str,
|
215
|
+
token_secret: str,
|
216
|
+
) -> Tuple[str, int, str]:
|
217
|
+
"""
|
218
|
+
Copy the token's ID and secret to the clipboard.
|
219
|
+
"""
|
220
|
+
if not n_clicks:
|
221
|
+
raise PreventUpdate
|
222
|
+
return (
|
223
|
+
f"Client ID: {token_id}\nClient Secret: {token_secret}",
|
224
|
+
(clipboard_n_clicks or 0) + 1,
|
225
|
+
"Copied!",
|
226
|
+
)
|
227
|
+
|
228
|
+
|
229
|
+
@dash_app.callback(
|
230
|
+
Output('tokens-close-register-output-modal-button', 'disabled'),
|
231
|
+
Output('tokens-register-output-modal', 'backdrop'),
|
232
|
+
Input('tokens-register-clipboard', 'n_clicks'),
|
233
|
+
prevent_initial_call=True,
|
234
|
+
)
|
235
|
+
def enable_close_button(n_clicks):
|
236
|
+
"""
|
237
|
+
Enable the close button once the token has been copied.
|
238
|
+
"""
|
239
|
+
if not n_clicks:
|
240
|
+
raise PreventUpdate
|
241
|
+
return False, True
|
242
|
+
|
243
|
+
|
244
|
+
@dash_app.callback(
|
245
|
+
Output('tokens-register-output-modal', 'is_open'),
|
246
|
+
Input('tokens-close-register-output-modal-button', 'n_clicks'),
|
247
|
+
prevent_initial_call=True,
|
248
|
+
)
|
249
|
+
def close_register_output_modal(n_clicks: int) -> bool:
|
250
|
+
"""
|
251
|
+
Close the register output modal when the Close button is clicked.
|
252
|
+
"""
|
253
|
+
if not n_clicks:
|
254
|
+
raise PreventUpdate
|
255
|
+
return False
|
256
|
+
|
257
|
+
|
258
|
+
@dash_app.callback(
|
259
|
+
Output({'type': 'tokens-edit-modal', 'index': MATCH}, 'is_open'),
|
260
|
+
Input({'type': 'tokens-edit-button', 'index': MATCH}, 'n_clicks'),
|
261
|
+
prevent_initial_call=True,
|
262
|
+
)
|
263
|
+
def edit_token_button_click(n_clicks: int):
|
264
|
+
if not n_clicks:
|
265
|
+
raise PreventUpdate
|
266
|
+
return True
|
267
|
+
|
268
|
+
|
269
|
+
@dash_app.callback(
|
270
|
+
Output({'type': 'tokens-edit-modal', 'index': MATCH}, 'is_open'),
|
271
|
+
Output({'type': 'tokens-edit-alerts-div', 'index': MATCH}, 'children'),
|
272
|
+
Input({'type': 'tokens-edit-submit-button', 'index': MATCH}, 'n_clicks'),
|
273
|
+
State({'type': 'tokens-expiration-datepickersingle', 'index': MATCH}, 'date'),
|
274
|
+
State({'type': 'tokens-scopes-checklist', 'index': MATCH}, 'value'),
|
275
|
+
State({'type': 'tokens-name-input', 'index': MATCH}, 'value'),
|
276
|
+
prevent_initial_call=True,
|
277
|
+
)
|
278
|
+
def edit_token_submit_button_click(
|
279
|
+
n_clicks: int,
|
280
|
+
expiration: Optional[datetime],
|
281
|
+
scopes: List[str],
|
282
|
+
label: str,
|
283
|
+
):
|
284
|
+
if not n_clicks:
|
285
|
+
raise PreventUpdate
|
286
|
+
|
287
|
+
ctx = dash.callback_context.triggered
|
288
|
+
if ctx[0]['value'] is None:
|
289
|
+
raise PreventUpdate
|
290
|
+
|
291
|
+
component_dict = json.loads(ctx[0]['prop_id'].split('.' + 'n_clicks')[0])
|
292
|
+
token_id = component_dict['index']
|
293
|
+
|
294
|
+
token = Token(
|
295
|
+
id=token_id,
|
296
|
+
label=label,
|
297
|
+
expiration=(datetime.fromisoformat(f"{expiration}T00:00:00Z") if expiration is not None else None),
|
298
|
+
scopes=scopes,
|
299
|
+
instance=get_api_connector(),
|
300
|
+
)
|
301
|
+
|
302
|
+
success, msg = token.edit(debug=debug)
|
303
|
+
if not success:
|
304
|
+
return dash.no_update, alert_from_success_tuple((success, msg))
|
305
|
+
|
306
|
+
return False, dash.no_update
|
307
|
+
|
308
|
+
|
309
|
+
@dash_app.callback(
|
310
|
+
Output({'type': 'tokens-invalidate-modal', 'index': MATCH}, 'is_open'),
|
311
|
+
Input({'type': 'tokens-invalidate-button', 'index': MATCH}, 'n_clicks'),
|
312
|
+
prevent_initial_call=True,
|
313
|
+
)
|
314
|
+
def invalidate_token_click(n_clicks: int):
|
315
|
+
if not n_clicks:
|
316
|
+
raise PreventUpdate
|
317
|
+
return True
|
318
|
+
|
319
|
+
|
320
|
+
@dash_app.callback(
|
321
|
+
Output({'type': 'tokens-delete-modal', 'index': MATCH}, 'is_open'),
|
322
|
+
Input({'type': 'tokens-delete-button', 'index': MATCH}, 'n_clicks'),
|
323
|
+
prevent_initial_call=True,
|
324
|
+
)
|
325
|
+
def invalidate_token_click(n_clicks: int):
|
326
|
+
if not n_clicks:
|
327
|
+
raise PreventUpdate
|
328
|
+
return True
|
329
|
+
|
330
|
+
|
331
|
+
|
332
|
+
@dash_app.callback(
|
333
|
+
Output({'type': 'tokens-edit-modal', 'index': MATCH}, 'is_open'),
|
334
|
+
Output({'type': 'tokens-invalidate-modal', 'index': MATCH}, 'is_open'),
|
335
|
+
Output({'type': 'tokens-invalidate-alerts-div', 'index': MATCH}, 'children'),
|
336
|
+
Input({'type': 'tokens-invalidate-confirm-button', 'index': MATCH}, 'n_clicks'),
|
337
|
+
prevent_initial_call=True,
|
338
|
+
)
|
339
|
+
def invalidate_token_confirm_click(n_clicks: int):
|
340
|
+
if not n_clicks:
|
341
|
+
raise PreventUpdate
|
342
|
+
|
343
|
+
ctx = dash.callback_context.triggered
|
344
|
+
if ctx[0]['value'] is None:
|
345
|
+
raise PreventUpdate
|
346
|
+
|
347
|
+
component_dict = json.loads(ctx[0]['prop_id'].split('.' + 'n_clicks')[0])
|
348
|
+
token_id = component_dict['index']
|
349
|
+
|
350
|
+
token = Token(
|
351
|
+
id=token_id,
|
352
|
+
instance=get_api_connector(),
|
353
|
+
)
|
354
|
+
|
355
|
+
success, msg = token.invalidate(debug=debug)
|
356
|
+
if not success:
|
357
|
+
return dash.no_update, dash.no_update, alert_from_success_tuple((success, msg))
|
358
|
+
|
359
|
+
return False, False, dash.no_update
|
360
|
+
|
361
|
+
|
362
|
+
@dash_app.callback(
|
363
|
+
Output({'type': 'tokens-edit-modal', 'index': MATCH}, 'is_open'),
|
364
|
+
Output({'type': 'tokens-delete-modal', 'index': MATCH}, 'is_open'),
|
365
|
+
Output({'type': 'tokens-delete-alerts-div', 'index': MATCH}, 'children'),
|
366
|
+
Input({'type': 'tokens-delete-confirm-button', 'index': MATCH}, 'n_clicks'),
|
367
|
+
prevent_initial_call=True,
|
368
|
+
)
|
369
|
+
def delete_token_confirm_click(n_clicks: int):
|
370
|
+
if not n_clicks:
|
371
|
+
raise PreventUpdate
|
372
|
+
|
373
|
+
ctx = dash.callback_context.triggered
|
374
|
+
if ctx[0]['value'] is None:
|
375
|
+
raise PreventUpdate
|
376
|
+
|
377
|
+
component_dict = json.loads(ctx[0]['prop_id'].split('.' + 'n_clicks')[0])
|
378
|
+
token_id = component_dict['index']
|
379
|
+
|
380
|
+
token = Token(
|
381
|
+
id=token_id,
|
382
|
+
instance=get_api_connector(),
|
383
|
+
)
|
384
|
+
|
385
|
+
success, msg = token.delete(debug=debug)
|
386
|
+
if not success:
|
387
|
+
return dash.no_update, dash.no_update, alert_from_success_tuple((success, msg))
|
388
|
+
|
389
|
+
return False, False, []
|
@@ -10,10 +10,10 @@ from __future__ import annotations
|
|
10
10
|
|
11
11
|
from meerschaum.utils.packages import attempt_import, import_dcc, import_html
|
12
12
|
from meerschaum.utils.typing import SuccessTuple, List
|
13
|
-
from meerschaum.
|
13
|
+
from meerschaum._internal.static import STATIC_CONFIG
|
14
14
|
from meerschaum.utils.misc import remove_ansi
|
15
15
|
from meerschaum._internal.shell.Shell import get_shell_intro
|
16
|
-
from meerschaum.api import endpoints, CHECK_UPDATE, docs_enabled
|
16
|
+
from meerschaum.api import endpoints, CHECK_UPDATE, docs_enabled, get_api_connector
|
17
17
|
from meerschaum.connectors import instance_types, _load_builtin_custom_connectors
|
18
18
|
from meerschaum.utils.misc import get_connector_labels
|
19
19
|
from meerschaum.config import __doc__ as doc
|
@@ -104,7 +104,7 @@ instance_select = dbc.Select(
|
|
104
104
|
id='instance-select',
|
105
105
|
size='sm',
|
106
106
|
options=[
|
107
|
-
{'label': i, 'value': i}
|
107
|
+
{'label': (i[:32] + '…') if len(i) > 32 else i, 'value': i}
|
108
108
|
for i in get_connector_labels(*instance_types)
|
109
109
|
],
|
110
110
|
class_name='dbc_dark custom-select custom-select-sm',
|
@@ -163,6 +163,7 @@ pages_navbar = html.Div(
|
|
163
163
|
id='pages-navbar-div',
|
164
164
|
)
|
165
165
|
|
166
|
+
|
166
167
|
navbar = dbc.Navbar(
|
167
168
|
dbc.Container(
|
168
169
|
[
|
@@ -171,13 +172,11 @@ navbar = dbc.Navbar(
|
|
171
172
|
dbc.Collapse(
|
172
173
|
dbc.Row(
|
173
174
|
[
|
174
|
-
dbc.Col(instance_select),
|
175
|
-
dbc.Col(
|
176
|
-
sign_out_button,
|
177
|
-
className="ms-auto",
|
178
|
-
),
|
175
|
+
dbc.Col(instance_select, width="auto"),
|
176
|
+
dbc.Col(sign_out_button, width="auto"),
|
179
177
|
],
|
180
178
|
className="g-0 ms-auto flex-nowrap mt-3 mt-md-0",
|
179
|
+
align="center",
|
181
180
|
),
|
182
181
|
id='navbar-collapse',
|
183
182
|
is_open=False,
|
@@ -267,6 +266,26 @@ def build_pages_offcanvas_children():
|
|
267
266
|
|
268
267
|
plugins_accordion_items = []
|
269
268
|
for page_group, pages_dicts in _plugin_endpoints_to_pages.items():
|
269
|
+
if len(pages_dicts) == 1:
|
270
|
+
page_href, page_dict = list(pages_dicts.items())[0]
|
271
|
+
if page_dict['page_key'].lower() == page_group.lower():
|
272
|
+
pages_listgroup_items.append(
|
273
|
+
dbc.ListGroupItem(
|
274
|
+
dbc.Button(
|
275
|
+
' '.join([word.capitalize() for word in page_dict['page_key'].split(' ')]),
|
276
|
+
style={
|
277
|
+
'width': '100%',
|
278
|
+
'text-align': 'left',
|
279
|
+
'text-decoration': 'none',
|
280
|
+
'padding-left': '0',
|
281
|
+
},
|
282
|
+
href=page_href,
|
283
|
+
color='dark',
|
284
|
+
)
|
285
|
+
)
|
286
|
+
)
|
287
|
+
continue
|
288
|
+
|
270
289
|
plugin_listgroup_items = [
|
271
290
|
dbc.ListGroupItem(
|
272
291
|
dbc.Button(
|
@@ -294,13 +313,15 @@ def build_pages_offcanvas_children():
|
|
294
313
|
class_name='pages-offcanvas-accordion',
|
295
314
|
)
|
296
315
|
plugins_accordion_items.append(plugin_accordion_item)
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
316
|
+
|
317
|
+
if plugins_accordion_items:
|
318
|
+
plugins_accordion = dbc.Accordion(
|
319
|
+
plugins_accordion_items,
|
320
|
+
start_collapsed=True,
|
321
|
+
flush=True,
|
322
|
+
always_open=True,
|
323
|
+
)
|
324
|
+
pages_listgroup_items.append(plugins_accordion)
|
304
325
|
|
305
326
|
pages_children = dbc.Card(
|
306
327
|
dbc.ListGroup(
|
meerschaum/api/dash/jobs.py
CHANGED
meerschaum/api/dash/keys.py
CHANGED
@@ -16,13 +16,10 @@ dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK
|
|
16
16
|
html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
|
17
17
|
|
18
18
|
placeholders = {
|
19
|
-
'ck'
|
20
|
-
'mk'
|
21
|
-
'lk'
|
22
|
-
'
|
23
|
-
'Additional search parameters. ' +
|
24
|
-
'Simple dictionary format or JSON accepted.'
|
25
|
-
),
|
19
|
+
'ck': 'Connectors',
|
20
|
+
'mk': 'Metrics',
|
21
|
+
'lk': 'Locations',
|
22
|
+
'tags': 'Tags',
|
26
23
|
}
|
27
24
|
widths = {
|
28
25
|
'flags' : {'size' : 12},
|
@@ -212,11 +209,41 @@ dropdown_keys_row = dbc.Row(
|
|
212
209
|
),
|
213
210
|
] ### end of filters row children
|
214
211
|
)
|
212
|
+
tags_dropdown = html.Div(
|
213
|
+
dcc.Dropdown(
|
214
|
+
id='tags-dropdown',
|
215
|
+
options=[],
|
216
|
+
placeholder=placeholders['tags'],
|
217
|
+
multi=True,
|
218
|
+
searchable=True,
|
219
|
+
),
|
220
|
+
className="dbc_dark",
|
221
|
+
id="tags-dropdown-div",
|
222
|
+
)
|
223
|
+
|
215
224
|
dropdown_tab_content = html.Div([
|
216
225
|
dbc.Card(
|
217
226
|
dbc.CardBody(
|
218
227
|
[
|
219
228
|
dropdown_keys_row,
|
229
|
+
html.Br(),
|
230
|
+
dbc.Row(
|
231
|
+
[
|
232
|
+
dbc.Col(tags_dropdown, width=True),
|
233
|
+
dbc.Col(
|
234
|
+
dbc.Button(
|
235
|
+
"Clear all",
|
236
|
+
id='clear-all-keys-button',
|
237
|
+
color='link',
|
238
|
+
size='sm',
|
239
|
+
style={'text-decoration': 'none'},
|
240
|
+
),
|
241
|
+
width='auto',
|
242
|
+
),
|
243
|
+
],
|
244
|
+
className='g-0',
|
245
|
+
align='center',
|
246
|
+
),
|
220
247
|
], ### end of card children
|
221
248
|
className='card-text',
|
222
249
|
)
|
@@ -232,94 +259,9 @@ dropdown_tab_content = html.Div([
|
|
232
259
|
),
|
233
260
|
])
|
234
261
|
|
235
|
-
text_tab_content = dbc.Card(
|
236
|
-
dbc.CardBody(
|
237
|
-
[
|
238
|
-
dbc.Row(
|
239
|
-
[
|
240
|
-
dbc.Col(html.Div(className='dbc_dark', children=[
|
241
|
-
dbc.InputGroup(
|
242
|
-
[
|
243
|
-
dbc.Button(
|
244
|
-
'Clear',
|
245
|
-
id='clear-connector-keys-input-button',
|
246
|
-
color='link',
|
247
|
-
size='sm',
|
248
|
-
),
|
249
|
-
dbc.Input(
|
250
|
-
id='connector-keys-input',
|
251
|
-
placeholder=placeholders['ck'],
|
252
|
-
type='text',
|
253
|
-
value='',
|
254
|
-
list='connector-keys-list',
|
255
|
-
className='dbc_dark'
|
256
|
-
),
|
257
|
-
],
|
258
|
-
size=input_group_sizes['ck'],
|
259
|
-
)]),
|
260
|
-
width=4,
|
261
|
-
),
|
262
|
-
dbc.Col(
|
263
|
-
dbc.InputGroup(
|
264
|
-
[
|
265
|
-
dbc.Button(
|
266
|
-
'Clear',
|
267
|
-
id='clear-metric-keys-input-button',
|
268
|
-
color='link',
|
269
|
-
size='sm',
|
270
|
-
),
|
271
|
-
dbc.Input(
|
272
|
-
id='metric-keys-input',
|
273
|
-
placeholder=placeholders['mk'],
|
274
|
-
type='text',
|
275
|
-
value='',
|
276
|
-
list='metric-keys-list',
|
277
|
-
),
|
278
|
-
],
|
279
|
-
size=input_group_sizes['mk'],
|
280
|
-
),
|
281
|
-
width=4,
|
282
|
-
),
|
283
|
-
dbc.Col(
|
284
|
-
dbc.InputGroup(
|
285
|
-
[
|
286
|
-
dbc.Button(
|
287
|
-
'Clear',
|
288
|
-
id='clear-location-keys-input-button',
|
289
|
-
color='link',
|
290
|
-
size='sm',
|
291
|
-
),
|
292
|
-
dbc.Input(
|
293
|
-
id='location-keys-input',
|
294
|
-
placeholder=placeholders['lk'],
|
295
|
-
type='text',
|
296
|
-
value='',
|
297
|
-
list='location-keys-list',
|
298
|
-
),
|
299
|
-
],
|
300
|
-
size=input_group_sizes['lk'],
|
301
|
-
),
|
302
|
-
width=4,
|
303
|
-
),
|
304
|
-
]
|
305
|
-
),
|
306
|
-
html.Br(),
|
307
|
-
dbc.Row(
|
308
|
-
dbc.Col(
|
309
|
-
dbc.InputGroup(
|
310
|
-
[
|
311
|
-
search_parameters_editor,
|
312
|
-
],
|
313
|
-
size=input_group_sizes['params'],
|
314
|
-
)
|
315
|
-
)
|
316
|
-
),
|
317
|
-
]
|
318
|
-
)
|
319
|
-
)
|
320
|
-
|
321
262
|
keys_lists_content = html.Div([
|
322
263
|
html.Datalist(id='connector-keys-list'),
|
323
264
|
html.Datalist(id='metric-keys-list'),
|
324
265
|
html.Datalist(id='location-keys-list'),
|
266
|
+
html.Datalist(id='tags-list'),
|
325
267
|
], hidden=True)
|
@@ -9,7 +9,8 @@ import meerschaum.api.dash.pages.error
|
|
9
9
|
import meerschaum.api.dash.pages.login
|
10
10
|
import meerschaum.api.dash.pages.dashboard
|
11
11
|
import meerschaum.api.dash.pages.plugins
|
12
|
+
import meerschaum.api.dash.pages.tokens
|
12
13
|
import meerschaum.api.dash.pages.register
|
13
14
|
import meerschaum.api.dash.pages.pipes
|
14
|
-
import meerschaum.api.dash.pages.
|
15
|
+
import meerschaum.api.dash.pages.jobs
|
15
16
|
import meerschaum.api.dash.pages.settings
|