meerschaum 2.3.6__py3-none-any.whl → 2.4.0.dev1__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/actions/bootstrap.py +36 -10
- meerschaum/actions/copy.py +3 -3
- meerschaum/actions/start.py +13 -14
- meerschaum/api/dash/__init__.py +7 -6
- meerschaum/api/dash/callbacks/__init__.py +1 -0
- meerschaum/api/dash/callbacks/dashboard.py +7 -5
- meerschaum/api/dash/callbacks/pipes.py +42 -0
- meerschaum/api/dash/pages/__init__.py +1 -0
- meerschaum/api/dash/pages/pipes.py +16 -0
- meerschaum/api/dash/pipes.py +79 -47
- meerschaum/api/dash/users.py +19 -6
- meerschaum/api/routes/_login.py +4 -4
- meerschaum/api/routes/_pipes.py +3 -3
- meerschaum/config/_default.py +9 -1
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +59 -16
- meerschaum/connectors/Connector.py +19 -13
- meerschaum/connectors/__init__.py +9 -5
- meerschaum/connectors/poll.py +30 -24
- meerschaum/connectors/sql/_pipes.py +126 -154
- meerschaum/connectors/sql/_plugins.py +45 -43
- meerschaum/connectors/sql/_users.py +46 -38
- meerschaum/connectors/valkey/ValkeyConnector.py +535 -0
- meerschaum/connectors/valkey/__init__.py +8 -0
- meerschaum/connectors/valkey/_fetch.py +75 -0
- meerschaum/connectors/valkey/_pipes.py +839 -0
- meerschaum/connectors/valkey/_plugins.py +265 -0
- meerschaum/connectors/valkey/_users.py +305 -0
- meerschaum/core/Pipe/__init__.py +3 -0
- meerschaum/core/Pipe/_attributes.py +1 -2
- meerschaum/core/Pipe/_clear.py +16 -13
- meerschaum/core/Pipe/_copy.py +106 -0
- meerschaum/core/Pipe/_drop.py +4 -4
- meerschaum/core/Pipe/_dtypes.py +14 -14
- meerschaum/core/Pipe/_edit.py +15 -14
- meerschaum/core/Pipe/_sync.py +134 -51
- meerschaum/core/Pipe/_verify.py +11 -11
- meerschaum/core/User/_User.py +14 -12
- meerschaum/plugins/_Plugin.py +17 -13
- meerschaum/utils/_get_pipes.py +14 -20
- meerschaum/utils/dataframe.py +288 -101
- meerschaum/utils/dtypes/__init__.py +31 -6
- meerschaum/utils/dtypes/sql.py +4 -4
- meerschaum/utils/misc.py +3 -3
- meerschaum/utils/packages/_packages.py +1 -0
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/METADATA +3 -1
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/RECORD +53 -44
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/WHEEL +1 -1
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/LICENSE +0 -0
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/NOTICE +0 -0
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/top_level.txt +0 -0
- {meerschaum-2.3.6.dist-info → meerschaum-2.4.0.dev1.dist-info}/zip-safe +0 -0
meerschaum/actions/bootstrap.py
CHANGED
@@ -234,7 +234,13 @@ def _bootstrap_connectors(
|
|
234
234
|
Prompt the user for the details necessary to create a Connector.
|
235
235
|
"""
|
236
236
|
from meerschaum.connectors.parse import is_valid_connector_keys
|
237
|
-
from meerschaum.connectors import
|
237
|
+
from meerschaum.connectors import (
|
238
|
+
connectors,
|
239
|
+
get_connector,
|
240
|
+
types,
|
241
|
+
custom_types,
|
242
|
+
_load_builtin_custom_connectors,
|
243
|
+
)
|
238
244
|
from meerschaum.utils.prompt import prompt, yes_no, choose
|
239
245
|
from meerschaum.config import get_config
|
240
246
|
from meerschaum.config._edit import write_config
|
@@ -246,6 +252,7 @@ def _bootstrap_connectors(
|
|
246
252
|
|
247
253
|
abort_tuple = False, "No connectors bootstrapped."
|
248
254
|
_clear = get_config('shell', 'clear_screen', patch=True)
|
255
|
+
_load_builtin_custom_connectors()
|
249
256
|
|
250
257
|
if action is None:
|
251
258
|
action = []
|
@@ -262,8 +269,8 @@ def _bootstrap_connectors(
|
|
262
269
|
+ ' See https://meerschaum.io/reference/connectors '
|
263
270
|
+ 'for documentation on connectors.\n'
|
264
271
|
),
|
265
|
-
sorted(
|
266
|
-
default
|
272
|
+
sorted([k for k in connectors if k != 'plugin']),
|
273
|
+
default='sql',
|
267
274
|
)
|
268
275
|
except KeyboardInterrupt:
|
269
276
|
return abort_tuple
|
@@ -288,21 +295,30 @@ def _bootstrap_connectors(
|
|
288
295
|
warn(f"Connector '{_type}:{_label}' already exists.", stack=False)
|
289
296
|
overwrite = yes_no(
|
290
297
|
f"Do you want to overwrite connector '{_type}:{_label}'?",
|
291
|
-
default
|
292
|
-
yes
|
293
|
-
noask
|
298
|
+
default='n',
|
299
|
+
yes=yes,
|
300
|
+
noask=noask,
|
294
301
|
)
|
295
302
|
if not overwrite and not force:
|
296
|
-
return False,
|
303
|
+
return False, "No changes made to connector configuration."
|
297
304
|
break
|
298
305
|
elif _label == "":
|
299
|
-
warn(
|
306
|
+
warn("Please enter a label.", stack=False)
|
300
307
|
else:
|
301
308
|
break
|
302
309
|
|
303
310
|
cls = types.get(_type)
|
304
311
|
cls_required_attrs = getattr(cls, 'REQUIRED_ATTRIBUTES', [])
|
305
|
-
|
312
|
+
cls_optional_attrs = getattr(cls, 'OPTIONAL_ATTRIBUTES', [])
|
313
|
+
cls_default_attrs = getattr(cls, 'DEFAULT_ATTRIBUTES', {})
|
314
|
+
type_attributes = connector_attributes.get(
|
315
|
+
_type,
|
316
|
+
{
|
317
|
+
'required': cls_required_attrs,
|
318
|
+
'optional': cls_optional_attrs,
|
319
|
+
'default': cls_default_attrs,
|
320
|
+
}
|
321
|
+
)
|
306
322
|
|
307
323
|
new_attributes = {}
|
308
324
|
if 'flavors' in type_attributes:
|
@@ -323,6 +339,7 @@ def _bootstrap_connectors(
|
|
323
339
|
default = type_attributes['flavors'][flavor].get('defaults', {})
|
324
340
|
else:
|
325
341
|
required = sorted(list(type_attributes.get('required', {})))
|
342
|
+
optional = sorted(list(type_attributes.get('optional', {})))
|
326
343
|
default = type_attributes.get('default', {})
|
327
344
|
info(
|
328
345
|
f"Please answer the following questions to configure the new connector '{_type}:{_label}'."
|
@@ -330,13 +347,22 @@ def _bootstrap_connectors(
|
|
330
347
|
)
|
331
348
|
for r in required:
|
332
349
|
try:
|
333
|
-
|
350
|
+
default_val = str(default.get(r)) if r in default else None
|
351
|
+
val = prompt(f"Value for {r}:", default=default_val)
|
334
352
|
except KeyboardInterrupt:
|
335
353
|
continue
|
336
354
|
if is_int(val):
|
337
355
|
val = int(val)
|
338
356
|
new_attributes[r] = val
|
339
357
|
|
358
|
+
for o in optional:
|
359
|
+
try:
|
360
|
+
val = prompt(f"Value for {o} (optional; empty to omit):")
|
361
|
+
except KeyboardInterrupt:
|
362
|
+
continue
|
363
|
+
if val:
|
364
|
+
new_attributes[o] = val
|
365
|
+
|
340
366
|
for k, v in default.items():
|
341
367
|
### skip already configured attributes, (e.g. flavor or from required)
|
342
368
|
if k in new_attributes:
|
meerschaum/actions/copy.py
CHANGED
meerschaum/actions/start.py
CHANGED
@@ -455,12 +455,12 @@ def _start_webterm(
|
|
455
455
|
|
456
456
|
|
457
457
|
def _start_connectors(
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
458
|
+
action: Optional[List[str]] = None,
|
459
|
+
connector_keys: Optional[List[str]] = None,
|
460
|
+
min_seconds: int = 3,
|
461
|
+
debug: bool = False,
|
462
|
+
**kw
|
463
|
+
) -> SuccessTuple:
|
464
464
|
"""
|
465
465
|
Start polling connectors to verify a connection can be made.
|
466
466
|
"""
|
@@ -480,7 +480,7 @@ def _start_connectors(
|
|
480
480
|
for keys in unique_keys:
|
481
481
|
try:
|
482
482
|
conn = parse_instance_keys(keys)
|
483
|
-
except Exception
|
483
|
+
except Exception:
|
484
484
|
warn(f"Invalid connector keys: '{keys}'. Skipping...", stack=False)
|
485
485
|
continue
|
486
486
|
valid_conns.append(conn)
|
@@ -488,20 +488,19 @@ def _start_connectors(
|
|
488
488
|
if not valid_conns:
|
489
489
|
return False, "No valid connector keys were provided."
|
490
490
|
|
491
|
-
|
492
491
|
connected = {}
|
493
492
|
def connect(conn):
|
494
493
|
success = retry_connect(
|
495
494
|
conn,
|
496
|
-
retry_wait
|
497
|
-
enforce_chaining
|
498
|
-
enforce_login
|
499
|
-
print_on_connect
|
500
|
-
debug
|
495
|
+
retry_wait=min_seconds,
|
496
|
+
enforce_chaining=False,
|
497
|
+
enforce_login=False,
|
498
|
+
print_on_connect=True,
|
499
|
+
debug=debug,
|
501
500
|
)
|
502
501
|
connected[conn] = success
|
503
502
|
return success
|
504
|
-
|
503
|
+
|
505
504
|
pool = get_pool()
|
506
505
|
try:
|
507
506
|
pool.map(connect, valid_conns)
|
meerschaum/api/dash/__init__.py
CHANGED
@@ -50,16 +50,17 @@ stylesheets = [
|
|
50
50
|
'/static/css/bootstrap.min.css',
|
51
51
|
'/static/css/dbc_dark.css',
|
52
52
|
'/static/css/dash.css',
|
53
|
+
'/static/css/bootstrap-icons/font/bootstrap-icons.min.css',
|
53
54
|
]
|
54
55
|
scripts = ['/static/js/node_modules/xterm/lib/xterm.js']
|
55
56
|
dash_app = enrich.DashProxy(
|
56
57
|
__name__,
|
57
|
-
title
|
58
|
-
requests_pathname_prefix
|
59
|
-
external_stylesheets
|
60
|
-
update_title
|
61
|
-
suppress_callback_exceptions
|
62
|
-
transforms
|
58
|
+
title='Meerschaum Web',
|
59
|
+
requests_pathname_prefix=endpoints['dash'] + '/',
|
60
|
+
external_stylesheets=stylesheets,
|
61
|
+
update_title=None,
|
62
|
+
suppress_callback_exceptions=True,
|
63
|
+
transforms=[
|
63
64
|
enrich.TriggerTransform(),
|
64
65
|
enrich.MultiplexerTransform(),
|
65
66
|
],
|
@@ -11,6 +11,7 @@ import meerschaum.api.dash.callbacks.login
|
|
11
11
|
import meerschaum.api.dash.callbacks.plugins
|
12
12
|
import meerschaum.api.dash.callbacks.jobs
|
13
13
|
import meerschaum.api.dash.callbacks.register
|
14
|
+
import meerschaum.api.dash.callbacks.pipes
|
14
15
|
from meerschaum.api.dash.callbacks.custom import init_dash_plugins, add_plugin_pages
|
15
16
|
|
16
17
|
init_dash_plugins()
|
@@ -101,6 +101,7 @@ _paths = {
|
|
101
101
|
'' : pages.dashboard.layout,
|
102
102
|
'plugins' : pages.plugins.layout,
|
103
103
|
'register': pages.register.layout,
|
104
|
+
'pipes' : pages.pipes.layout,
|
104
105
|
}
|
105
106
|
_required_login = {''}
|
106
107
|
|
@@ -113,10 +114,10 @@ _required_login = {''}
|
|
113
114
|
State('location', 'href'),
|
114
115
|
)
|
115
116
|
def update_page_layout_div(
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
117
|
+
pathname: str,
|
118
|
+
session_store_data: Dict[str, Any],
|
119
|
+
location_href: str,
|
120
|
+
) -> Tuple[List[Any], Dict[str, Any]]:
|
120
121
|
"""
|
121
122
|
Route the user to the correct page.
|
122
123
|
|
@@ -135,7 +136,7 @@ def update_page_layout_div(
|
|
135
136
|
ctx = dash.callback_context
|
136
137
|
dash_endpoint = endpoints['dash']
|
137
138
|
try:
|
138
|
-
session_id = session_store_data.get('session-id', None)
|
139
|
+
session_id = session_store_data.get('session-id', None)
|
139
140
|
except AttributeError:
|
140
141
|
session_id = None
|
141
142
|
|
@@ -760,6 +761,7 @@ def update_pipe_accordion(item, session_store_data):
|
|
760
761
|
raise PreventUpdate
|
761
762
|
|
762
763
|
session_id = session_store_data.get('session-id', None)
|
764
|
+
print(f"{session_id=}")
|
763
765
|
authenticated = is_session_authenticated(str(session_id))
|
764
766
|
return accordion_items_from_pipe(pipe, active_items=[item], authenticated=authenticated)
|
765
767
|
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#! /usr/bin/env python3
|
2
|
+
# vim:fenc=utf-8
|
3
|
+
|
4
|
+
"""
|
5
|
+
Define callbacks for the `/dash/pipes/` page.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from dash.exceptions import PreventUpdate
|
9
|
+
from dash.dependencies import Input, Output, State
|
10
|
+
from dash import no_update
|
11
|
+
|
12
|
+
import meerschaum as mrsm
|
13
|
+
from meerschaum.api.dash import dash_app, debug, active_sessions
|
14
|
+
from meerschaum.api.dash.pipes import build_pipe_card
|
15
|
+
from meerschaum.api import get_api_connector, CHECK_UPDATE
|
16
|
+
from meerschaum.utils.packages import import_html, import_dcc
|
17
|
+
html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
|
18
|
+
|
19
|
+
|
20
|
+
@dash_app.callback(
|
21
|
+
Output('pipe-output-div', 'children'),
|
22
|
+
Input('pipes-location', 'pathname'),
|
23
|
+
)
|
24
|
+
def render_page_from_url(pathname):
|
25
|
+
if not str(pathname).startswith('/dash/pipes'):
|
26
|
+
return no_update
|
27
|
+
|
28
|
+
keys = pathname.replace('/dash/pipes', '').lstrip('/').rstrip('/').split('/')
|
29
|
+
if len(keys) not in (2, 3):
|
30
|
+
return no_update
|
31
|
+
|
32
|
+
ck = keys[0]
|
33
|
+
mk = keys[1]
|
34
|
+
lk = keys[2] if len(keys) == 3 else None
|
35
|
+
|
36
|
+
pipe = mrsm.Pipe(ck, mk, lk)
|
37
|
+
### TODO Check if logged in
|
38
|
+
return [
|
39
|
+
html.Br(),
|
40
|
+
build_pipe_card(pipe, authenticated=False),
|
41
|
+
html.Br(),
|
42
|
+
]
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#! /usr/bin/env python3
|
2
|
+
# vim:fenc=utf-8
|
3
|
+
|
4
|
+
"""
|
5
|
+
Display pipes via a shareable URL.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from meerschaum.api import get_api_connector, CHECK_UPDATE
|
9
|
+
from meerschaum.utils.packages import import_html, import_dcc
|
10
|
+
html, dcc = import_html(check_update=CHECK_UPDATE), import_dcc(check_update=CHECK_UPDATE)
|
11
|
+
import dash_bootstrap_components as dbc
|
12
|
+
|
13
|
+
layout = dbc.Container([
|
14
|
+
dcc.Location('pipes-location'),
|
15
|
+
html.Div(id='pipe-output-div'),
|
16
|
+
])
|
meerschaum/api/dash/pipes.py
CHANGED
@@ -48,13 +48,14 @@ def pipe_from_ctx(ctx, trigger_property: str = 'n_clicks') -> Union[mrsm.Pipe, N
|
|
48
48
|
return None
|
49
49
|
return mrsm.Pipe(**meta)
|
50
50
|
|
51
|
+
|
51
52
|
def keys_from_state(
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
53
|
+
state: Dict[str, Any],
|
54
|
+
with_params: bool = False
|
55
|
+
) -> Union[
|
56
|
+
Tuple[List[str], List[str], List[str]],
|
57
|
+
Tuple[List[str], List[str], List[str], str],
|
58
|
+
]:
|
58
59
|
"""
|
59
60
|
Read the current state and return the selected keys lists.
|
60
61
|
"""
|
@@ -85,10 +86,11 @@ def keys_from_state(
|
|
85
86
|
keys.append(params)
|
86
87
|
return tuple(keys)
|
87
88
|
|
89
|
+
|
88
90
|
def pipes_from_state(
|
89
|
-
|
90
|
-
|
91
|
-
|
91
|
+
state: Dict[str, Any],
|
92
|
+
**kw
|
93
|
+
):
|
92
94
|
_ck, _mk, _lk, _params = keys_from_state(state, with_params=True)
|
93
95
|
try:
|
94
96
|
_pipes = _get_pipes(
|
@@ -103,10 +105,10 @@ def pipes_from_state(
|
|
103
105
|
|
104
106
|
|
105
107
|
def build_pipe_card(
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
108
|
+
pipe: mrsm.Pipe,
|
109
|
+
authenticated: bool = False,
|
110
|
+
_build_children_num: int = 10,
|
111
|
+
) -> 'dbc.Card':
|
110
112
|
"""
|
111
113
|
Return a card for the given pipe.
|
112
114
|
|
@@ -128,11 +130,11 @@ def build_pipe_card(
|
|
128
130
|
dbc.Col(
|
129
131
|
(
|
130
132
|
dbc.DropdownMenu(
|
131
|
-
label
|
132
|
-
children
|
133
|
+
label="Manage",
|
134
|
+
children=[
|
133
135
|
dbc.DropdownMenuItem(
|
134
136
|
'Open in Python',
|
135
|
-
id
|
137
|
+
id={
|
136
138
|
'type': 'manage-pipe-button',
|
137
139
|
'index': meta_str,
|
138
140
|
'action': 'python',
|
@@ -140,7 +142,7 @@ def build_pipe_card(
|
|
140
142
|
),
|
141
143
|
dbc.DropdownMenuItem(
|
142
144
|
'Delete',
|
143
|
-
id
|
145
|
+
id={
|
144
146
|
'type': 'manage-pipe-button',
|
145
147
|
'index': meta_str,
|
146
148
|
'action': 'delete',
|
@@ -148,7 +150,7 @@ def build_pipe_card(
|
|
148
150
|
),
|
149
151
|
dbc.DropdownMenuItem(
|
150
152
|
'Drop',
|
151
|
-
id
|
153
|
+
id={
|
152
154
|
'type': 'manage-pipe-button',
|
153
155
|
'index': meta_str,
|
154
156
|
'action': 'drop',
|
@@ -156,7 +158,7 @@ def build_pipe_card(
|
|
156
158
|
),
|
157
159
|
dbc.DropdownMenuItem(
|
158
160
|
'Clear',
|
159
|
-
id
|
161
|
+
id={
|
160
162
|
'type': 'manage-pipe-button',
|
161
163
|
'index': meta_str,
|
162
164
|
'action': 'clear',
|
@@ -164,7 +166,7 @@ def build_pipe_card(
|
|
164
166
|
),
|
165
167
|
dbc.DropdownMenuItem(
|
166
168
|
'Verify',
|
167
|
-
id
|
169
|
+
id={
|
168
170
|
'type': 'manage-pipe-button',
|
169
171
|
'index': meta_str,
|
170
172
|
'action': 'verify',
|
@@ -172,56 +174,86 @@ def build_pipe_card(
|
|
172
174
|
),
|
173
175
|
dbc.DropdownMenuItem(
|
174
176
|
'Sync',
|
175
|
-
id
|
177
|
+
id={
|
176
178
|
'type': 'manage-pipe-button',
|
177
179
|
'index': meta_str,
|
178
180
|
'action': 'sync',
|
179
181
|
},
|
180
182
|
),
|
181
183
|
],
|
182
|
-
direction
|
183
|
-
menu_variant
|
184
|
-
size
|
185
|
-
color
|
184
|
+
direction="up",
|
185
|
+
menu_variant="dark",
|
186
|
+
size='sm',
|
187
|
+
color='secondary',
|
186
188
|
)
|
187
189
|
) if authenticated else [],
|
188
|
-
width
|
190
|
+
width=2,
|
189
191
|
),
|
190
192
|
dbc.Col(width=6),
|
191
193
|
dbc.Col(
|
192
194
|
dbc.Button(
|
193
195
|
'Download CSV',
|
194
|
-
size
|
195
|
-
color
|
196
|
-
style
|
197
|
-
id
|
196
|
+
size='sm',
|
197
|
+
color='link',
|
198
|
+
style={'float': 'right'},
|
199
|
+
id={'type': 'pipe-download-csv-button', 'index': meta_str},
|
198
200
|
),
|
199
|
-
width
|
201
|
+
width=4,
|
200
202
|
),
|
201
203
|
],
|
202
|
-
justify
|
204
|
+
justify='start',
|
203
205
|
)
|
204
206
|
card_body_children = [
|
205
|
-
html.H5(
|
206
|
-
html.B(str(pipe)),
|
207
|
-
className = 'card-title',
|
208
|
-
style = {'font-family': ['monospace']}
|
209
|
-
),
|
210
207
|
html.Div(
|
211
208
|
dbc.Accordion(
|
212
209
|
accordion_items_from_pipe(
|
213
210
|
pipe,
|
214
|
-
authenticated
|
215
|
-
_build_children_num
|
211
|
+
authenticated=authenticated,
|
212
|
+
_build_children_num=_build_children_num,
|
216
213
|
),
|
217
|
-
flush
|
218
|
-
start_collapsed
|
219
|
-
id
|
214
|
+
flush=True,
|
215
|
+
start_collapsed=True,
|
216
|
+
id={'type': 'pipe-accordion', 'index': meta_str},
|
220
217
|
)
|
221
218
|
)
|
222
219
|
|
223
220
|
]
|
221
|
+
|
222
|
+
pipe_url = (
|
223
|
+
f"/dash/pipes/{pipe.connector_keys}/{pipe.metric_key}/{pipe.location_key}"
|
224
|
+
)
|
225
|
+
|
226
|
+
card_header_children = dbc.Row(
|
227
|
+
[
|
228
|
+
dbc.Col(
|
229
|
+
html.H5(
|
230
|
+
html.B(str(pipe)),
|
231
|
+
className='card-title',
|
232
|
+
style={'font-family': ['monospace']}
|
233
|
+
),
|
234
|
+
width=11,
|
235
|
+
),
|
236
|
+
dbc.Col(
|
237
|
+
dbc.Button(
|
238
|
+
[
|
239
|
+
html.I(
|
240
|
+
className="bi bi-share",
|
241
|
+
),
|
242
|
+
],
|
243
|
+
# href=pipe_url,
|
244
|
+
style={'float': 'right'},
|
245
|
+
outline=True,
|
246
|
+
color='link',
|
247
|
+
id={'type': 'share-pipe-button', 'index': meta_str},
|
248
|
+
),
|
249
|
+
width=1,
|
250
|
+
),
|
251
|
+
],
|
252
|
+
justify='start',
|
253
|
+
)
|
254
|
+
|
224
255
|
return dbc.Card([
|
256
|
+
dbc.CardHeader(children=card_header_children),
|
225
257
|
dbc.CardBody(children=card_body_children),
|
226
258
|
dbc.CardFooter(children=footer_children),
|
227
259
|
])
|
@@ -270,11 +302,11 @@ def get_pipes_cards(*keys, session_data: Optional[Dict[str, Any]] = None):
|
|
270
302
|
|
271
303
|
|
272
304
|
def accordion_items_from_pipe(
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
305
|
+
pipe: mrsm.Pipe,
|
306
|
+
active_items: Optional[List[str]] = None,
|
307
|
+
authenticated: bool = False,
|
308
|
+
_build_children_num: int = 10,
|
309
|
+
) -> 'List[dbc.AccordionItem]':
|
278
310
|
"""
|
279
311
|
Build the accordion items for a given pipe.
|
280
312
|
"""
|
meerschaum/api/dash/users.py
CHANGED
@@ -18,6 +18,7 @@ from meerschaum.core import User
|
|
18
18
|
dcc, html = import_dcc(check_update=CHECK_UPDATE), import_html(check_update=CHECK_UPDATE)
|
19
19
|
dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
|
20
20
|
|
21
|
+
|
21
22
|
def get_users_cards(state: WebState) -> Tuple[List[dbc.Card], List[SuccessTuple]]:
|
22
23
|
"""
|
23
24
|
Return the cards and alerts for users.
|
@@ -52,21 +53,33 @@ def is_session_authenticated(session_id: str) -> bool:
|
|
52
53
|
"""
|
53
54
|
if no_auth:
|
54
55
|
return True
|
56
|
+
if session_id not in active_sessions:
|
57
|
+
return False
|
55
58
|
if session_id in unauthenticated_sessions:
|
56
59
|
return False
|
57
60
|
if session_id in authenticated_sessions:
|
58
61
|
return True
|
62
|
+
|
59
63
|
permissions = get_config('system', 'api', 'permissions')
|
60
64
|
allow_non_admin = permissions.get('actions', {}).get('non_admin', False)
|
61
|
-
|
62
|
-
|
63
|
-
conn = get_api_connector()
|
65
|
+
|
66
|
+
is_auth = True if allow_non_admin else session_is_admin(session_id)
|
64
67
|
username = active_sessions.get(session_id, {}).get('username', None)
|
65
|
-
|
66
|
-
user_type = conn.get_user_type(user, debug=debug)
|
67
|
-
is_auth = user_type == 'admin'
|
68
|
+
|
68
69
|
if is_auth:
|
69
70
|
authenticated_sessions[session_id] = username
|
70
71
|
else:
|
71
72
|
unauthenticated_sessions[session_id] = username
|
73
|
+
|
72
74
|
return is_auth
|
75
|
+
|
76
|
+
|
77
|
+
def session_is_admin(session_id: str) -> bool:
|
78
|
+
"""
|
79
|
+
Check whether a session ID corresponds to an admin user.
|
80
|
+
"""
|
81
|
+
conn = get_api_connector()
|
82
|
+
username = active_sessions.get(session_id, {}).get('username', None)
|
83
|
+
user = User(username, instance=conn)
|
84
|
+
user_type = conn.get_user_type(user, debug=debug)
|
85
|
+
return user_type == 'admin'
|
meerschaum/api/routes/_login.py
CHANGED
@@ -23,8 +23,8 @@ from meerschaum.api._oauth2 import CustomOAuth2PasswordRequestForm
|
|
23
23
|
|
24
24
|
@manager.user_loader()
|
25
25
|
def load_user(
|
26
|
-
|
27
|
-
|
26
|
+
username: str
|
27
|
+
) -> User:
|
28
28
|
"""
|
29
29
|
Create the `meerschaum.core.User` object from the username.
|
30
30
|
"""
|
@@ -33,8 +33,8 @@ def load_user(
|
|
33
33
|
|
34
34
|
@app.post(endpoints['login'], tags=['Users'])
|
35
35
|
def login(
|
36
|
-
|
37
|
-
|
36
|
+
data: CustomOAuth2PasswordRequestForm = fastapi.Depends()
|
37
|
+
) -> Dict[str, Any]:
|
38
38
|
"""
|
39
39
|
Login and set the session token.
|
40
40
|
"""
|
meerschaum/api/routes/_pipes.py
CHANGED
@@ -474,9 +474,9 @@ def get_pipe_data(
|
|
474
474
|
df[col] = df[col].apply(lambda x: f'{x:f}' if isinstance(x, Decimal) else x)
|
475
475
|
|
476
476
|
json_content = df.to_json(
|
477
|
-
date_format
|
478
|
-
orient
|
479
|
-
date_unit
|
477
|
+
date_format='iso',
|
478
|
+
orient='records',
|
479
|
+
date_unit='us',
|
480
480
|
)
|
481
481
|
|
482
482
|
return fastapi.Response(
|
meerschaum/config/_default.py
CHANGED
@@ -52,6 +52,14 @@ default_meerschaum_config = {
|
|
52
52
|
'protocol': 'https',
|
53
53
|
},
|
54
54
|
},
|
55
|
+
'valkey': {
|
56
|
+
'main': {
|
57
|
+
'host': 'localhost',
|
58
|
+
'username': 'default',
|
59
|
+
'password': 'mrsm',
|
60
|
+
'port': 6379,
|
61
|
+
},
|
62
|
+
},
|
55
63
|
},
|
56
64
|
}
|
57
65
|
default_system_config = {
|
@@ -74,7 +82,7 @@ default_system_config = {
|
|
74
82
|
},
|
75
83
|
},
|
76
84
|
|
77
|
-
'api'
|
85
|
+
'api': {
|
78
86
|
},
|
79
87
|
},
|
80
88
|
### not to be confused with system_config['connectors']['api'], this is the configuration
|
meerschaum/config/_version.py
CHANGED