meerschaum 2.9.5__py3-none-any.whl → 3.0.0rc2__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.
Files changed (158) hide show
  1. meerschaum/__init__.py +5 -2
  2. meerschaum/_internal/__init__.py +1 -0
  3. meerschaum/_internal/arguments/_parse_arguments.py +4 -4
  4. meerschaum/_internal/arguments/_parser.py +19 -2
  5. meerschaum/_internal/docs/index.py +49 -2
  6. meerschaum/_internal/entry.py +6 -6
  7. meerschaum/_internal/shell/Shell.py +1 -1
  8. meerschaum/_internal/static.py +356 -0
  9. meerschaum/actions/api.py +12 -2
  10. meerschaum/actions/bootstrap.py +7 -7
  11. meerschaum/actions/edit.py +142 -18
  12. meerschaum/actions/register.py +137 -6
  13. meerschaum/actions/show.py +117 -29
  14. meerschaum/actions/stop.py +4 -1
  15. meerschaum/actions/sync.py +1 -1
  16. meerschaum/actions/tag.py +9 -8
  17. meerschaum/actions/verify.py +5 -8
  18. meerschaum/api/__init__.py +11 -3
  19. meerschaum/api/_events.py +39 -2
  20. meerschaum/api/_oauth2.py +118 -8
  21. meerschaum/api/_tokens.py +102 -0
  22. meerschaum/api/dash/__init__.py +0 -3
  23. meerschaum/api/dash/callbacks/custom.py +2 -2
  24. meerschaum/api/dash/callbacks/dashboard.py +103 -19
  25. meerschaum/api/dash/callbacks/plugins.py +0 -1
  26. meerschaum/api/dash/callbacks/register.py +1 -1
  27. meerschaum/api/dash/callbacks/settings/__init__.py +1 -0
  28. meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
  29. meerschaum/api/dash/callbacks/settings/tokens.py +388 -0
  30. meerschaum/api/dash/components.py +30 -8
  31. meerschaum/api/dash/keys.py +19 -93
  32. meerschaum/api/dash/pages/dashboard.py +1 -20
  33. meerschaum/api/dash/pages/settings/__init__.py +1 -0
  34. meerschaum/api/dash/pages/settings/password_reset.py +1 -1
  35. meerschaum/api/dash/pages/settings/tokens.py +55 -0
  36. meerschaum/api/dash/pipes.py +94 -59
  37. meerschaum/api/dash/sessions.py +12 -0
  38. meerschaum/api/dash/tokens.py +606 -0
  39. meerschaum/api/dash/websockets.py +1 -1
  40. meerschaum/api/dash/webterm.py +4 -0
  41. meerschaum/api/models/__init__.py +23 -3
  42. meerschaum/api/models/_actions.py +22 -0
  43. meerschaum/api/models/_pipes.py +85 -7
  44. meerschaum/api/models/_tokens.py +81 -0
  45. meerschaum/api/resources/templates/termpage.html +12 -0
  46. meerschaum/api/routes/__init__.py +1 -0
  47. meerschaum/api/routes/_actions.py +3 -4
  48. meerschaum/api/routes/_connectors.py +3 -7
  49. meerschaum/api/routes/_jobs.py +14 -35
  50. meerschaum/api/routes/_login.py +49 -12
  51. meerschaum/api/routes/_misc.py +5 -10
  52. meerschaum/api/routes/_pipes.py +173 -140
  53. meerschaum/api/routes/_plugins.py +38 -28
  54. meerschaum/api/routes/_tokens.py +236 -0
  55. meerschaum/api/routes/_users.py +47 -35
  56. meerschaum/api/routes/_version.py +3 -3
  57. meerschaum/config/__init__.py +43 -20
  58. meerschaum/config/_default.py +43 -6
  59. meerschaum/config/_edit.py +28 -24
  60. meerschaum/config/_environment.py +1 -1
  61. meerschaum/config/_patch.py +6 -6
  62. meerschaum/config/_paths.py +5 -1
  63. meerschaum/config/_read_config.py +65 -34
  64. meerschaum/config/_sync.py +6 -3
  65. meerschaum/config/_version.py +1 -1
  66. meerschaum/config/stack/__init__.py +31 -11
  67. meerschaum/config/static.py +18 -0
  68. meerschaum/connectors/_Connector.py +10 -4
  69. meerschaum/connectors/__init__.py +4 -20
  70. meerschaum/connectors/api/_APIConnector.py +34 -6
  71. meerschaum/connectors/api/_actions.py +2 -2
  72. meerschaum/connectors/api/_jobs.py +1 -1
  73. meerschaum/connectors/api/_login.py +33 -7
  74. meerschaum/connectors/api/_misc.py +2 -2
  75. meerschaum/connectors/api/_pipes.py +16 -31
  76. meerschaum/connectors/api/_plugins.py +2 -2
  77. meerschaum/connectors/api/_request.py +1 -1
  78. meerschaum/connectors/api/_tokens.py +146 -0
  79. meerschaum/connectors/api/_users.py +70 -58
  80. meerschaum/connectors/instance/_InstanceConnector.py +83 -0
  81. meerschaum/connectors/instance/__init__.py +10 -0
  82. meerschaum/connectors/instance/_pipes.py +442 -0
  83. meerschaum/connectors/instance/_plugins.py +151 -0
  84. meerschaum/connectors/instance/_tokens.py +296 -0
  85. meerschaum/connectors/instance/_users.py +181 -0
  86. meerschaum/connectors/parse.py +4 -1
  87. meerschaum/connectors/sql/_SQLConnector.py +8 -5
  88. meerschaum/connectors/sql/_cli.py +12 -11
  89. meerschaum/connectors/sql/_create_engine.py +9 -168
  90. meerschaum/connectors/sql/_fetch.py +2 -18
  91. meerschaum/connectors/sql/_pipes.py +156 -190
  92. meerschaum/connectors/sql/_plugins.py +29 -0
  93. meerschaum/connectors/sql/_sql.py +46 -21
  94. meerschaum/connectors/sql/_users.py +29 -2
  95. meerschaum/connectors/sql/tables/__init__.py +1 -1
  96. meerschaum/connectors/valkey/_ValkeyConnector.py +2 -4
  97. meerschaum/connectors/valkey/_pipes.py +53 -26
  98. meerschaum/connectors/valkey/_plugins.py +2 -26
  99. meerschaum/core/Pipe/__init__.py +59 -19
  100. meerschaum/core/Pipe/_attributes.py +412 -90
  101. meerschaum/core/Pipe/_bootstrap.py +54 -24
  102. meerschaum/core/Pipe/_data.py +96 -18
  103. meerschaum/core/Pipe/_dtypes.py +48 -18
  104. meerschaum/core/Pipe/_edit.py +14 -4
  105. meerschaum/core/Pipe/_fetch.py +1 -1
  106. meerschaum/core/Pipe/_show.py +5 -5
  107. meerschaum/core/Pipe/_sync.py +118 -193
  108. meerschaum/core/Pipe/_verify.py +4 -4
  109. meerschaum/{plugins → core/Plugin}/_Plugin.py +9 -11
  110. meerschaum/core/Plugin/__init__.py +1 -1
  111. meerschaum/core/Token/_Token.py +220 -0
  112. meerschaum/core/Token/__init__.py +12 -0
  113. meerschaum/core/User/_User.py +34 -8
  114. meerschaum/core/User/__init__.py +9 -1
  115. meerschaum/core/__init__.py +1 -0
  116. meerschaum/jobs/_Job.py +3 -2
  117. meerschaum/jobs/__init__.py +3 -2
  118. meerschaum/jobs/systemd.py +1 -1
  119. meerschaum/models/__init__.py +35 -0
  120. meerschaum/models/pipes.py +247 -0
  121. meerschaum/models/tokens.py +38 -0
  122. meerschaum/models/users.py +26 -0
  123. meerschaum/plugins/__init__.py +22 -7
  124. meerschaum/plugins/bootstrap.py +2 -1
  125. meerschaum/utils/_get_pipes.py +68 -27
  126. meerschaum/utils/daemon/Daemon.py +2 -1
  127. meerschaum/utils/daemon/__init__.py +30 -2
  128. meerschaum/utils/dataframe.py +473 -81
  129. meerschaum/utils/debug.py +15 -15
  130. meerschaum/utils/dtypes/__init__.py +473 -34
  131. meerschaum/utils/dtypes/sql.py +368 -28
  132. meerschaum/utils/formatting/__init__.py +1 -1
  133. meerschaum/utils/formatting/_pipes.py +5 -4
  134. meerschaum/utils/formatting/_shell.py +11 -9
  135. meerschaum/utils/misc.py +246 -148
  136. meerschaum/utils/packages/__init__.py +10 -27
  137. meerschaum/utils/packages/_packages.py +41 -34
  138. meerschaum/utils/pipes.py +181 -0
  139. meerschaum/utils/process.py +1 -1
  140. meerschaum/utils/prompt.py +3 -1
  141. meerschaum/utils/schedule.py +2 -1
  142. meerschaum/utils/sql.py +121 -44
  143. meerschaum/utils/typing.py +1 -4
  144. meerschaum/utils/venv/_Venv.py +2 -2
  145. meerschaum/utils/venv/__init__.py +5 -7
  146. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/METADATA +92 -96
  147. meerschaum-3.0.0rc2.dist-info/RECORD +283 -0
  148. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/WHEEL +1 -1
  149. meerschaum-3.0.0rc2.dist-info/licenses/NOTICE +2 -0
  150. meerschaum/api/models/_interfaces.py +0 -15
  151. meerschaum/api/models/_locations.py +0 -15
  152. meerschaum/api/models/_metrics.py +0 -15
  153. meerschaum/config/static/__init__.py +0 -186
  154. meerschaum-2.9.5.dist-info/RECORD +0 -263
  155. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/entry_points.txt +0 -0
  156. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/licenses/LICENSE +0 -0
  157. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/top_level.txt +0 -0
  158. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc2.dist-info}/zip-safe +0 -0
@@ -0,0 +1,606 @@
1
+ #! /usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # vim:fenc=utf-8
4
+
5
+ """
6
+ Dash utility functions for constructing tokens components.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from datetime import datetime, timezone, timedelta
12
+ from typing import Any, List
13
+
14
+ import meerschaum as mrsm
15
+ from meerschaum.api import debug, CHECK_UPDATE, get_api_connector
16
+ from meerschaum.api.dash.connectors import get_web_connector
17
+ from meerschaum.api.dash.components import alert_from_success_tuple
18
+ from meerschaum.api.dash.sessions import get_user_from_session
19
+ from meerschaum.utils.typing import WebState, SuccessTuple, List, Tuple
20
+ from meerschaum.utils.packages import attempt_import, import_html, import_dcc
21
+ from meerschaum.utils.misc import interval_str
22
+ from meerschaum.utils.dtypes import value_is_null, round_time
23
+ from meerschaum._internal.static import STATIC_CONFIG
24
+ from meerschaum.core import Token
25
+ from meerschaum.utils.daemon import get_new_daemon_name
26
+ dcc, html = import_dcc(check_update=CHECK_UPDATE), import_html(check_update=CHECK_UPDATE)
27
+ dbc = attempt_import('dash_bootstrap_components', lazy=False, check_update=CHECK_UPDATE)
28
+
29
+
30
+ def get_tokens_table(session_id: Optional[str] = None) -> Tuple[dbc.Table, List[dbc.Alert]]:
31
+ """
32
+ Return the main tokens table.
33
+ """
34
+ conn = get_api_connector()
35
+ user = get_user_from_session(session_id) if session_id is not None else None
36
+ alerts = []
37
+ try:
38
+ tokens = conn.get_tokens(user=user, debug=debug)
39
+ except Exception as e:
40
+ tokens = []
41
+ alerts = [alert_from_success_tuple((False, f"Failed to fetch tokens from '{conn}':\n{e}"))]
42
+
43
+ if not tokens:
44
+ return tokens, alerts
45
+
46
+ table_header = [
47
+ html.Thead(
48
+ html.Tr([
49
+ html.Th("Label"),
50
+ html.Th("Client ID"),
51
+ html.Th("Created"),
52
+ html.Th("Expires in"),
53
+ html.Th("Is Valid"),
54
+ html.Th(""),
55
+ ]),
56
+ ),
57
+ ]
58
+
59
+ rows = [
60
+ html.Tr([
61
+ html.Td(str(token.label)),
62
+ html.Td(html.Pre(str(token.id))),
63
+ html.Td(get_creation_string(token).replace('Created ', '')),
64
+ html.Td(get_expiration_string(token).replace('Expires in ', '')),
65
+ html.Td("✅" if token.is_valid else "❌"),
66
+ html.Td([
67
+ dbc.Button(
68
+ html.B("⠇"),
69
+ color='link',
70
+ size='sm',
71
+ id={'type': 'tokens-context-button', 'index': str(token.id)},
72
+ style={'text-decoration': 'none'},
73
+ ),
74
+ build_manage_token_popover(token),
75
+ build_edit_token_modal(token),
76
+ build_invalidate_token_modal(token),
77
+ build_delete_token_modal(token),
78
+ ]),
79
+ ])
80
+ for token in tokens
81
+ ]
82
+
83
+ table_body = [html.Tbody(rows)]
84
+ table = dbc.Table(table_header + table_body)
85
+ return table, alerts
86
+
87
+
88
+ def build_manage_token_popover(token: Token) -> dbc.Popover:
89
+ """
90
+ Return the "Manage token" popover.
91
+ """
92
+ return dbc.Popover(
93
+ [
94
+ dbc.PopoverHeader(["Manage token"]),
95
+ dbc.PopoverBody([
96
+ dbc.ButtonGroup(
97
+ ([
98
+ dbc.Button(
99
+ "Edit",
100
+ outline=True,
101
+ color='light',
102
+ id={
103
+ 'type': 'tokens-edit-button',
104
+ 'index': str(token.id),
105
+ },
106
+ ),
107
+ dbc.Button(
108
+ "Invalidate",
109
+ outline=True,
110
+ color='warning',
111
+ id={
112
+ 'type': 'tokens-invalidate-button',
113
+ 'index': str(token.id),
114
+ },
115
+ ),
116
+ ] if token.is_valid else []) + [
117
+ dbc.Button(
118
+ "Delete",
119
+ color='danger',
120
+ outline=True,
121
+ id={
122
+ 'type': 'tokens-delete-button',
123
+ 'index': str(token.id),
124
+ },
125
+ ),
126
+ ]),
127
+ ]),
128
+ ],
129
+ body=True,
130
+ trigger='legacy',
131
+ autohide=True,
132
+ id={'type': 'tokens-context-popover', 'index': str(token.id)},
133
+ target={'type': 'tokens-context-button', 'index': str(token.id)},
134
+ )
135
+
136
+
137
+ def build_edit_token_modal(token: Token) -> dbc.Modal:
138
+ """
139
+ Return the Modal for editing the token.
140
+ """
141
+ return dbc.Modal(
142
+ [
143
+ dbc.ModalHeader([
144
+ html.H4([
145
+ "Edit token ",
146
+ html.B(str(token.label))
147
+ ]),
148
+ ]),
149
+ dbc.ModalBody([
150
+ html.Div(id={'type': 'tokens-edit-alerts-div', 'index': str(token.id)}),
151
+
152
+ dbc.Form([
153
+ dbc.Row(
154
+ ([
155
+ dbc.Label("Name", width='auto'),
156
+ dbc.Col(
157
+ [
158
+ dbc.Input(
159
+ placeholder="Enter token's label",
160
+ value=str(token.label),
161
+ id={
162
+ 'type': 'tokens-name-input',
163
+ 'index': str(token.id),
164
+ },
165
+ ),
166
+ ],
167
+ className="me-3",
168
+ ),
169
+ dbc.Label("Expiration", width='auto'),
170
+ dbc.Col(
171
+ dcc.DatePickerSingle(
172
+ date=(
173
+ token.expiration.to_pydatetime()
174
+ if hasattr(token.expiration, 'to_pydatetime')
175
+ else token.expiration
176
+ ),
177
+ clearable=True,
178
+ min_date_allowed=datetime.today().date(),
179
+ display_format="YYYY-MM-DD",
180
+ id={
181
+ 'type': 'tokens-expiration-datepickersingle',
182
+ 'index': str(token.id),
183
+ },
184
+ )
185
+ ),
186
+ ] if token.is_valid else []) + [
187
+ dbc.Col([
188
+ ]),
189
+ ],
190
+ className='g-2',
191
+ ),
192
+ html.Br(),
193
+ dbc.Row([
194
+ ]),
195
+ html.Div([
196
+ dbc.Button(
197
+ "Deselect all",
198
+ size='sm',
199
+ color='link',
200
+ id={
201
+ 'type': "tokens-deselect-scopes-button",
202
+ 'index': str(token.id),
203
+ },
204
+ style={'text-decoration': 'none'},
205
+ ),
206
+ html.Br(),
207
+ dbc.Row([
208
+ dbc.Label("Scopes", width='auto'),
209
+ dbc.Col([
210
+ dbc.Checklist(
211
+ options=[
212
+ {"label": scope, "value": scope}
213
+ for scope in STATIC_CONFIG['tokens']['scopes']
214
+ ],
215
+ value=token.scopes,
216
+ id={
217
+ 'type': "tokens-scopes-checklist",
218
+ 'index': str(token.id),
219
+ },
220
+ style={'columnCount': 3},
221
+ ),
222
+ ]),
223
+ ]),
224
+ ] if token.is_valid else [], id={
225
+ 'type': 'tokens-scopes-checklist-div',
226
+ 'index': str(token.id),
227
+ }),
228
+ ]),
229
+
230
+ ]),
231
+ dbc.ModalFooter(
232
+ [
233
+ html.Small(str(token.id)),
234
+ dbc.Button(
235
+ "Submit",
236
+ id={'type': 'tokens-edit-submit-button', 'index': str(token.id)},
237
+ ),
238
+ ],
239
+ className='d-flex justify-content-between',
240
+ ),
241
+ ],
242
+ size='lg',
243
+ is_open=False,
244
+ id={'type': 'tokens-edit-modal', 'index': str(token.id)},
245
+ )
246
+
247
+
248
+ def build_invalidate_token_modal(token: Token) -> dbc.Modal:
249
+ """
250
+ Return the Invalidate token modal.
251
+ """
252
+ return dbc.Modal(
253
+ [
254
+ dbc.ModalHeader([
255
+ html.H4([
256
+ "Invalidate token ",
257
+ html.B(token.label),
258
+ "?"
259
+ ]),
260
+ ]),
261
+ dbc.ModalBody([
262
+ html.Div(
263
+ id={
264
+ 'type': 'tokens-invalidate-alerts-div',
265
+ 'index': str(token.id)
266
+ },
267
+ ),
268
+ html.P(
269
+ [
270
+ "Are you sure you want to invalidate token ",
271
+ html.B(token.label),
272
+ " (",
273
+ html.I(str(token.id)),
274
+ ")?",
275
+ ],
276
+ ),
277
+ html.P([html.B("This action cannot be undone!")]),
278
+ ]),
279
+ dbc.ModalFooter([
280
+ dbc.Button(
281
+ "Invalidate",
282
+ color='danger',
283
+ id={
284
+ 'type': 'tokens-invalidate-confirm-button',
285
+ 'index': str(token.id),
286
+ },
287
+ ),
288
+ ]),
289
+ ],
290
+ id={
291
+ 'type': 'tokens-invalidate-modal',
292
+ 'index': str(token.id),
293
+ },
294
+ )
295
+
296
+
297
+ def build_delete_token_modal(token: Token) -> dbc.Modal:
298
+ """
299
+ Return the delete token modal.
300
+ """
301
+ return dbc.Modal(
302
+ [
303
+ dbc.ModalHeader([
304
+ html.H4([
305
+ "Delete token ",
306
+ html.B(token.label),
307
+ "?"
308
+ ]),
309
+ ]),
310
+ dbc.ModalBody([
311
+ html.Div(
312
+ id={
313
+ 'type': 'tokens-delete-alerts-div',
314
+ 'index': str(token.id)
315
+ },
316
+ ),
317
+ html.P(
318
+ [
319
+ "Are you sure you want to delete token ",
320
+ html.B(token.label),
321
+ " (",
322
+ html.I(str(token.id)),
323
+ ")?",
324
+ ],
325
+ ),
326
+ html.P([html.B("This action cannot be undone!")]),
327
+ ]),
328
+ dbc.ModalFooter([
329
+ dbc.Button(
330
+ "Delete",
331
+ color='danger',
332
+ id={
333
+ 'type': 'tokens-delete-confirm-button',
334
+ 'index': str(token.id),
335
+ },
336
+ ),
337
+ ]),
338
+ ],
339
+ id={
340
+ 'type': 'tokens-delete-modal',
341
+ 'index': str(token.id),
342
+ },
343
+ )
344
+
345
+
346
+ def get_tokens_cards(session_id: Optional[str] = None) -> Tuple[List[dbc.Card], List[dbc.Alert]]:
347
+ """
348
+ Return the cards and alerts for tokens.
349
+ """
350
+ cards, alerts = [], []
351
+ conn = get_api_connector()
352
+ user = get_user_from_session(session_id) if session_id is not None else None
353
+ try:
354
+ tokens = conn.get_tokens(user=user, debug=debug)
355
+ except Exception as e:
356
+ tokens = []
357
+ alerts = [alert_from_success_tuple((False, f"Failed to fetch tokens from '{conn}':\n{e}"))]
358
+
359
+ for token in tokens:
360
+ try:
361
+ cards.append(
362
+ dbc.Card([
363
+ dbc.CardHeader(
364
+ [
365
+ html.H5(token.label),
366
+ ]
367
+ ),
368
+ dbc.CardBody(
369
+ [
370
+ html.Code(str(token.id), style={'color': '#999999'}),
371
+ ]
372
+ ),
373
+ dbc.CardFooter(
374
+ [
375
+ html.P(
376
+ get_creation_string(token),
377
+ style={'color': '#999999'},
378
+ ),
379
+ html.P(
380
+ get_expiration_string(token),
381
+ style={'color': '#999999'},
382
+ ),
383
+ ]
384
+ ),
385
+ ])
386
+ )
387
+ except Exception as e:
388
+ alerts.append(
389
+ alert_from_success_tuple((False, f"Failed to load metadata for token:\n{e}"))
390
+ )
391
+
392
+ return cards, alerts
393
+
394
+
395
+ def get_creation_string(token: mrsm.core.Token) -> str:
396
+ """
397
+ Return the formatted string to represent the token's creation timestamp.
398
+ """
399
+ creation = token.creation
400
+ if value_is_null(str(creation)):
401
+ return ''
402
+ now = datetime.now(timezone.utc)
403
+ return 'Created ' + interval_str(creation - now, round_unit=True)
404
+
405
+
406
+ def get_expiration_string(token: mrsm.core.Token) -> str:
407
+ """
408
+ Return the formatted string to represent the token's expiration timestamp.
409
+ """
410
+ expiration = token.expiration
411
+ if value_is_null(str(expiration)):
412
+ return 'Does not expire'
413
+ now = datetime.now(timezone.utc)
414
+ return 'Expires in ' + interval_str(expiration - now, round_unit=True)
415
+
416
+
417
+ def build_tokens_register_input_modal() -> dbc.Modal:
418
+ """
419
+ Return the layout for the tokens register input modal.
420
+ """
421
+ now = datetime.now(timezone.utc)
422
+ default_expiration_days = mrsm.get_config(
423
+ 'system', 'api', 'tokens', 'default_expiration_days',
424
+ ) or 366
425
+ default_expiration = round_time(
426
+ now + timedelta(days=default_expiration_days),
427
+ timedelta(days=1),
428
+ )
429
+ min_date_allowed = round_time(now + timedelta(days=1), timedelta(days=1))
430
+
431
+ return [
432
+ dbc.ModalHeader(html.H4("Register Token")),
433
+ dbc.ModalBody([
434
+ dbc.Form([
435
+ dbc.Row(
436
+ [
437
+ dbc.Label("Name", width='auto'),
438
+ dbc.Col(
439
+ [
440
+ dbc.Input(
441
+ placeholder="Enter token's label",
442
+ value=get_new_daemon_name(),
443
+ id='tokens-name-input'
444
+ ),
445
+ ],
446
+ className="me-3",
447
+ ),
448
+ dbc.Label("Expiration", width='auto'),
449
+ dbc.Col(
450
+ dcc.DatePickerSingle(
451
+ date=default_expiration,
452
+ clearable=True,
453
+ min_date_allowed=min_date_allowed,
454
+ display_format="YYYY-MM-DD",
455
+ id='tokens-expiration-datepickersingle',
456
+ )
457
+ ),
458
+ dbc.Col(
459
+ dbc.Switch(
460
+ id="tokens-toggle-scopes-switch",
461
+ label="Grant all scopes",
462
+ value=True,
463
+ ),
464
+ className="me-3",
465
+ ),
466
+ ],
467
+ className='g-2',
468
+ ),
469
+ html.Br(),
470
+ dbc.Row([
471
+ ]),
472
+ html.Div([
473
+ dbc.Button(
474
+ "Deselect all",
475
+ size='sm',
476
+ color='link',
477
+ id="tokens-deselect-scopes-button",
478
+ style={'text-decoration': 'none'},
479
+ ),
480
+ html.Br(),
481
+ dbc.Row([
482
+ dbc.Label("Scopes", width='auto'),
483
+ dbc.Col([
484
+ dbc.Checklist(
485
+ options=[
486
+ {"label": scope, "value": scope}
487
+ for scope in STATIC_CONFIG['tokens']['scopes']
488
+ ],
489
+ value=list(STATIC_CONFIG['tokens']['scopes']),
490
+ id="tokens-scopes-checklist",
491
+ style={'columnCount': 3},
492
+ ),
493
+ ]),
494
+ ]),
495
+ ], id='tokens-scopes-checklist-div', style={'display': 'none'}),
496
+ ]),
497
+ ]),
498
+ dbc.ModalFooter([
499
+ dbc.Button('Register', id='tokens-register-button'),
500
+ ]),
501
+ ]
502
+
503
+
504
+ def build_register_table_from_token(token: Token) -> dbc.Table:
505
+ """
506
+ Return a table with the token's metadata.
507
+ """
508
+ table_header = [html.Thead(html.Tr([html.Th("Attribute"), html.Th("Value")]))]
509
+ table_header = []
510
+ pre_style = {'white-space': 'pre-wrap', 'word-break': 'break-all'}
511
+ rows = [
512
+ html.Tr([
513
+ html.Td(html.B("Client ID")),
514
+ html.Td(html.Pre(
515
+ str(token.id),
516
+ style=pre_style,
517
+ id='token-id-pre',
518
+ )),
519
+ ]),
520
+ html.Tr([
521
+ html.Td(html.B("Client Secret")),
522
+ html.Td(html.Pre(
523
+ str(token.secret),
524
+ style=pre_style,
525
+ id='token-secret-pre',
526
+ )),
527
+ ]),
528
+ html.Tr([
529
+ html.Td(html.B("API Key")),
530
+ html.Td(html.Pre(token.get_api_key(), style=pre_style)),
531
+ ]),
532
+ html.Tr([
533
+ html.Td(html.B("Expiration")),
534
+ html.Td(
535
+ html.Pre(token.expiration.isoformat(), style=pre_style)
536
+ if token.expiration is not None
537
+ else "Does not expire"
538
+ ),
539
+ ]),
540
+ html.Tr([
541
+ html.Td(html.B("Scopes")),
542
+ html.Td(html.P(' '.join(token.scopes), style={'word-break': 'normal'})),
543
+ ]),
544
+ html.Tr([
545
+ html.Td(html.B("User")),
546
+ html.Td(token.user.username if token.user is not None else ""),
547
+ ]),
548
+ ]
549
+ table_body = [html.Tbody(rows)]
550
+ table = dbc.Table(
551
+ table_header + table_body,
552
+ id='tokens-register-table',
553
+ )
554
+ return table
555
+
556
+
557
+ def build_tokens_register_output_modal(token: Token) -> List[Any]:
558
+ """
559
+ Return the layout for the tokens register output modal.
560
+ """
561
+ success, msg = token.register(debug=debug)
562
+ header_text = (
563
+ "Registered token "
564
+ if success
565
+ else "Failed to register token "
566
+ )
567
+ body_children = (
568
+ [
569
+ dbc.Stack(
570
+ [
571
+ html.Div([
572
+ html.P(html.B("Copy the token's details to dismiss.")),
573
+ html.P(html.I("The secret and API key will not be shown again.")),
574
+ ]),
575
+ html.Div([
576
+ dbc.Button(
577
+ "Copy token details",
578
+ id='tokens-register-copy-button',
579
+ ),
580
+ ], className="ms-auto"),
581
+ html.Div([
582
+ dcc.Clipboard(id="tokens-register-clipboard"),
583
+ ]),
584
+ ],
585
+ direction='horizontal',
586
+ gap=2,
587
+ ),
588
+ build_register_table_from_token(token),
589
+ ]
590
+ if success
591
+ else alert_from_success_tuple((False, msg))
592
+ )
593
+ return [
594
+ dbc.ModalHeader(
595
+ html.H4([header_text, html.B(token.label)]),
596
+ close_button=False,
597
+ ),
598
+ dbc.ModalBody(body_children),
599
+ dbc.ModalFooter([
600
+ dbc.Button(
601
+ "Close",
602
+ id='tokens-close-register-output-modal-button',
603
+ disabled=True,
604
+ ),
605
+ ]),
606
+ ]
@@ -8,7 +8,7 @@ Functions for interacting via WebSockets.
8
8
 
9
9
  import asyncio, sys
10
10
  from meerschaum.api._websockets import websockets
11
- from meerschaum.config.static import STATIC_CONFIG
11
+ from meerschaum._internal.static import STATIC_CONFIG
12
12
 
13
13
  def ws_url_from_href(href: str) -> str:
14
14
  """
@@ -31,6 +31,10 @@ def get_webterm(state: WebState) -> Tuple[Any, Any]:
31
31
  """
32
32
  Start the webterm and return its iframe.
33
33
  """
34
+ from meerschaum.api import _include_webterm
35
+ if not _include_webterm:
36
+ return console_div, []
37
+
34
38
  session_id = state['session-store.data'].get('session-id', None)
35
39
  username = get_username_from_session(session_id)
36
40
  if not is_session_authenticated(session_id):
@@ -6,6 +6,26 @@
6
6
  Create and manipulate SQL tables with ORM
7
7
  """
8
8
 
9
- from meerschaum.api.models._pipes import MetaPipe
10
- # from meerschaum.api.models._metrics import Metric
11
- # from meerschaum.api.models._locations import Location
9
+ import meerschaum as mrsm
10
+ import meerschaum.models
11
+
12
+ from meerschaum.api.models._pipes import (
13
+ FetchPipesKeysResponseModel,
14
+ SyncPipeRequestModel,
15
+ )
16
+ from meerschaum.api.models._actions import SuccessTupleResponseModel
17
+ from meerschaum.api.models._tokens import (
18
+ GetTokensResponseModel,
19
+ RegisterTokenResponseModel,
20
+ RegisterTokenRequestModel,
21
+ GetTokenResponseModel,
22
+ )
23
+
24
+ __all__ = (
25
+ 'FetchPipesKeysResponseModel',
26
+ 'SyncPipeRequestModel',
27
+ 'SuccessTupleResponseModel',
28
+ 'RegisterTokenResponseModel',
29
+ 'RegisterTokenRequestModel',
30
+ 'GetTokenResponseModel',
31
+ )
@@ -0,0 +1,22 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Define actions response models.
6
+ """
7
+
8
+ from typing import Tuple
9
+ from pydantic import RootModel, ConfigDict
10
+ from meerschaum.utils.typing import SuccessTuple
11
+
12
+
13
+ class SuccessTupleResponseModel(RootModel[SuccessTuple]):
14
+ """
15
+ A response model for a tuple of a boolean and a string.
16
+ E.g. `[true, "Success"]`
17
+ """
18
+ model_config = ConfigDict(
19
+ json_schema_extra={
20
+ 'example': [True, "Success"],
21
+ },
22
+ )