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.
Files changed (153) 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 +17 -1
  5. meerschaum/_internal/entry.py +6 -6
  6. meerschaum/_internal/shell/Shell.py +1 -1
  7. meerschaum/_internal/static.py +372 -0
  8. meerschaum/actions/api.py +12 -2
  9. meerschaum/actions/bootstrap.py +7 -7
  10. meerschaum/actions/edit.py +142 -18
  11. meerschaum/actions/register.py +137 -6
  12. meerschaum/actions/show.py +117 -29
  13. meerschaum/actions/stop.py +4 -1
  14. meerschaum/actions/sync.py +1 -1
  15. meerschaum/actions/tag.py +9 -8
  16. meerschaum/api/__init__.py +9 -2
  17. meerschaum/api/_events.py +39 -2
  18. meerschaum/api/_oauth2.py +118 -8
  19. meerschaum/api/_tokens.py +102 -0
  20. meerschaum/api/dash/__init__.py +0 -1
  21. meerschaum/api/dash/callbacks/custom.py +2 -2
  22. meerschaum/api/dash/callbacks/dashboard.py +102 -18
  23. meerschaum/api/dash/callbacks/plugins.py +0 -1
  24. meerschaum/api/dash/callbacks/register.py +1 -1
  25. meerschaum/api/dash/callbacks/settings/__init__.py +1 -0
  26. meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
  27. meerschaum/api/dash/callbacks/settings/tokens.py +388 -0
  28. meerschaum/api/dash/components.py +30 -8
  29. meerschaum/api/dash/keys.py +19 -93
  30. meerschaum/api/dash/pages/dashboard.py +1 -20
  31. meerschaum/api/dash/pages/settings/__init__.py +1 -0
  32. meerschaum/api/dash/pages/settings/password_reset.py +1 -1
  33. meerschaum/api/dash/pages/settings/tokens.py +55 -0
  34. meerschaum/api/dash/pipes.py +94 -59
  35. meerschaum/api/dash/sessions.py +12 -0
  36. meerschaum/api/dash/tokens.py +606 -0
  37. meerschaum/api/dash/websockets.py +1 -1
  38. meerschaum/api/dash/webterm.py +4 -0
  39. meerschaum/api/models/__init__.py +23 -3
  40. meerschaum/api/models/_actions.py +22 -0
  41. meerschaum/api/models/_pipes.py +85 -7
  42. meerschaum/api/models/_tokens.py +81 -0
  43. meerschaum/api/resources/templates/termpage.html +12 -0
  44. meerschaum/api/routes/__init__.py +1 -0
  45. meerschaum/api/routes/_actions.py +3 -4
  46. meerschaum/api/routes/_connectors.py +3 -7
  47. meerschaum/api/routes/_jobs.py +14 -35
  48. meerschaum/api/routes/_login.py +49 -12
  49. meerschaum/api/routes/_misc.py +5 -10
  50. meerschaum/api/routes/_pipes.py +134 -111
  51. meerschaum/api/routes/_plugins.py +38 -28
  52. meerschaum/api/routes/_tokens.py +236 -0
  53. meerschaum/api/routes/_users.py +47 -35
  54. meerschaum/api/routes/_version.py +3 -3
  55. meerschaum/config/__init__.py +43 -20
  56. meerschaum/config/_default.py +32 -5
  57. meerschaum/config/_edit.py +28 -24
  58. meerschaum/config/_environment.py +1 -1
  59. meerschaum/config/_patch.py +6 -6
  60. meerschaum/config/_paths.py +5 -1
  61. meerschaum/config/_read_config.py +65 -34
  62. meerschaum/config/_sync.py +6 -3
  63. meerschaum/config/_version.py +1 -1
  64. meerschaum/config/stack/__init__.py +24 -5
  65. meerschaum/config/static.py +18 -0
  66. meerschaum/connectors/_Connector.py +10 -4
  67. meerschaum/connectors/__init__.py +4 -20
  68. meerschaum/connectors/api/_APIConnector.py +34 -6
  69. meerschaum/connectors/api/_actions.py +2 -2
  70. meerschaum/connectors/api/_jobs.py +1 -1
  71. meerschaum/connectors/api/_login.py +33 -7
  72. meerschaum/connectors/api/_misc.py +2 -2
  73. meerschaum/connectors/api/_pipes.py +15 -14
  74. meerschaum/connectors/api/_plugins.py +2 -2
  75. meerschaum/connectors/api/_request.py +1 -1
  76. meerschaum/connectors/api/_tokens.py +146 -0
  77. meerschaum/connectors/api/_users.py +70 -58
  78. meerschaum/connectors/instance/_InstanceConnector.py +83 -0
  79. meerschaum/connectors/instance/__init__.py +10 -0
  80. meerschaum/connectors/instance/_pipes.py +442 -0
  81. meerschaum/connectors/instance/_plugins.py +151 -0
  82. meerschaum/connectors/instance/_tokens.py +296 -0
  83. meerschaum/connectors/instance/_users.py +181 -0
  84. meerschaum/connectors/parse.py +4 -1
  85. meerschaum/connectors/sql/_SQLConnector.py +8 -5
  86. meerschaum/connectors/sql/_cli.py +12 -11
  87. meerschaum/connectors/sql/_create_engine.py +6 -154
  88. meerschaum/connectors/sql/_fetch.py +2 -18
  89. meerschaum/connectors/sql/_pipes.py +42 -31
  90. meerschaum/connectors/sql/_plugins.py +29 -0
  91. meerschaum/connectors/sql/_sql.py +8 -1
  92. meerschaum/connectors/sql/_users.py +29 -2
  93. meerschaum/connectors/sql/tables/__init__.py +1 -1
  94. meerschaum/connectors/valkey/_ValkeyConnector.py +2 -4
  95. meerschaum/connectors/valkey/_pipes.py +9 -10
  96. meerschaum/connectors/valkey/_plugins.py +2 -26
  97. meerschaum/core/Pipe/__init__.py +31 -14
  98. meerschaum/core/Pipe/_attributes.py +156 -58
  99. meerschaum/core/Pipe/_bootstrap.py +54 -24
  100. meerschaum/core/Pipe/_data.py +41 -1
  101. meerschaum/core/Pipe/_dtypes.py +29 -14
  102. meerschaum/core/Pipe/_edit.py +12 -4
  103. meerschaum/core/Pipe/_show.py +5 -5
  104. meerschaum/core/Pipe/_sync.py +48 -53
  105. meerschaum/core/Pipe/_verify.py +1 -1
  106. meerschaum/{plugins → core/Plugin}/_Plugin.py +9 -11
  107. meerschaum/core/Plugin/__init__.py +1 -1
  108. meerschaum/core/Token/_Token.py +221 -0
  109. meerschaum/core/Token/__init__.py +12 -0
  110. meerschaum/core/User/_User.py +34 -8
  111. meerschaum/core/User/__init__.py +9 -1
  112. meerschaum/core/__init__.py +1 -0
  113. meerschaum/jobs/_Job.py +3 -2
  114. meerschaum/jobs/__init__.py +3 -2
  115. meerschaum/jobs/systemd.py +1 -1
  116. meerschaum/models/__init__.py +35 -0
  117. meerschaum/models/pipes.py +247 -0
  118. meerschaum/models/tokens.py +38 -0
  119. meerschaum/models/users.py +26 -0
  120. meerschaum/plugins/__init__.py +22 -7
  121. meerschaum/plugins/bootstrap.py +2 -1
  122. meerschaum/utils/_get_pipes.py +68 -27
  123. meerschaum/utils/daemon/Daemon.py +2 -1
  124. meerschaum/utils/daemon/__init__.py +30 -2
  125. meerschaum/utils/dataframe.py +95 -14
  126. meerschaum/utils/dtypes/__init__.py +91 -18
  127. meerschaum/utils/dtypes/sql.py +44 -0
  128. meerschaum/utils/formatting/__init__.py +1 -1
  129. meerschaum/utils/formatting/_pipes.py +5 -4
  130. meerschaum/utils/formatting/_shell.py +11 -9
  131. meerschaum/utils/misc.py +237 -80
  132. meerschaum/utils/packages/__init__.py +3 -6
  133. meerschaum/utils/packages/_packages.py +34 -32
  134. meerschaum/utils/pipes.py +181 -0
  135. meerschaum/utils/process.py +1 -1
  136. meerschaum/utils/prompt.py +3 -1
  137. meerschaum/utils/schedule.py +1 -0
  138. meerschaum/utils/sql.py +114 -37
  139. meerschaum/utils/typing.py +1 -4
  140. meerschaum/utils/venv/_Venv.py +2 -2
  141. meerschaum/utils/venv/__init__.py +5 -7
  142. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/METADATA +88 -80
  143. meerschaum-3.0.0rc1.dist-info/RECORD +282 -0
  144. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/WHEEL +1 -1
  145. meerschaum/api/models/_interfaces.py +0 -15
  146. meerschaum/api/models/_locations.py +0 -15
  147. meerschaum/api/models/_metrics.py +0 -15
  148. meerschaum/config/static/__init__.py +0 -186
  149. meerschaum-2.9.5.dist-info/RECORD +0 -263
  150. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/entry_points.txt +0 -0
  151. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/licenses/LICENSE +0 -0
  152. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/top_level.txt +0 -0
  153. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/zip-safe +0 -0
@@ -26,6 +26,21 @@ from meerschaum.api import (
26
26
  manager,
27
27
  debug,
28
28
  no_auth,
29
+ ScopedAuth,
30
+ )
31
+ from meerschaum.models import (
32
+ ConnectorKeysModel,
33
+ MetricKeyModel,
34
+ LocationKeyModel,
35
+ InstanceKeysModel,
36
+ PipeModel,
37
+ PipeWithParametersModel,
38
+ PipesWithParametersDictModel,
39
+ )
40
+ from meerschaum.api.models import (
41
+ SuccessTupleResponseModel,
42
+ FetchPipesKeysResponseModel,
43
+ SyncPipeRequestModel,
29
44
  )
30
45
  from meerschaum.api._chunks import generate_chunks_cursor_token
31
46
  from meerschaum.utils.packages import attempt_import
@@ -35,6 +50,7 @@ from meerschaum.utils.misc import (
35
50
  is_pipe_registered,
36
51
  is_int,
37
52
  replace_pipes_in_dict,
53
+ string_to_dict,
38
54
  )
39
55
  from meerschaum.connectors.sql.tables import get_tables
40
56
 
@@ -49,6 +65,7 @@ MAX_RESPONSE_ROW_LIMIT: int = mrsm.get_config('system', 'api', 'data', 'max_resp
49
65
  @app.post(
50
66
  pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/register',
51
67
  tags=['Pipes: Attributes'],
68
+ response_model=SuccessTupleResponseModel,
52
69
  )
53
70
  def register_pipe(
54
71
  connector_keys: str,
@@ -56,10 +73,8 @@ def register_pipe(
56
73
  location_key: str,
57
74
  instance_keys: Optional[str] = None,
58
75
  parameters: Optional[Dict[str, Any]] = None,
59
- curr_user = (
60
- fastapi.Depends(manager) if not no_auth else None
61
- ),
62
- ):
76
+ curr_user = fastapi.Security(ScopedAuth(['pipes:write']), scopes=['pipes:write']),
77
+ ) -> SuccessTupleResponseModel:
63
78
  """
64
79
  Register a new pipe.
65
80
  """
@@ -69,7 +84,7 @@ def register_pipe(
69
84
  "The administrator for this server has not allowed pipe registration.\n\n"
70
85
  "Please contact the system administrator, or if you are running this server, "
71
86
  "open the configuration file with `edit config system` and search for 'permissions'."
72
- " Under the keys `api:permissions:registration`, " +
87
+ " Under the keys `api:permissions:registration`, " +
73
88
  "you can toggle various registration types."
74
89
  )
75
90
  pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
@@ -79,14 +94,15 @@ def register_pipe(
79
94
  )
80
95
  if parameters:
81
96
  pipe.parameters = parameters
82
- results = get_api_connector(instance_keys).register_pipe(pipe, debug=debug)
97
+ success, msg = get_api_connector(instance_keys).register_pipe(pipe, debug=debug)
83
98
  pipes(instance_keys, refresh=True)
84
- return results
99
+ return success, msg
85
100
 
86
101
 
87
102
  @app.patch(
88
103
  pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/edit',
89
104
  tags=['Pipes: Attributes'],
105
+ response_model=SuccessTupleResponseModel,
90
106
  )
91
107
  def edit_pipe(
92
108
  connector_keys: str,
@@ -95,10 +111,8 @@ def edit_pipe(
95
111
  parameters: dict,
96
112
  instance_keys: Optional[str] = None,
97
113
  patch: bool = False,
98
- curr_user = (
99
- fastapi.Depends(manager) if not no_auth else None
100
- ),
101
- ):
114
+ curr_user = fastapi.Security(ScopedAuth(['pipes:write'])),
115
+ ) -> SuccessTupleResponseModel:
102
116
  """
103
117
  Edit an existing pipe's parameters.
104
118
  """
@@ -117,36 +131,26 @@ def edit_pipe(
117
131
  status_code=409, detail=f"{pipe} is not registered."
118
132
  )
119
133
  pipe.parameters = parameters
120
- results = get_api_connector(instance_keys).edit_pipe(pipe, patch=patch, debug=debug)
134
+ success, msg = get_api_connector(instance_keys).edit_pipe(pipe, patch=patch, debug=debug)
121
135
  pipes(instance_keys, refresh=True)
122
- return results
136
+ return success, msg
123
137
 
124
138
 
125
139
  @app.delete(
126
140
  pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/delete',
127
141
  tags=['Pipes: Attributes'],
142
+ response_model=SuccessTupleResponseModel,
128
143
  )
129
144
  def delete_pipe(
130
145
  connector_keys: str,
131
146
  metric_key: str,
132
147
  location_key: str,
133
148
  instance_keys: Optional[str] = None,
134
- curr_user = (
135
- fastapi.Depends(manager) if not no_auth else None
136
- ),
137
- ):
149
+ curr_user = fastapi.Security(ScopedAuth(['pipes:delete'])),
150
+ ) -> SuccessTupleResponseModel:
138
151
  """
139
152
  Delete a Pipe (without dropping its table).
140
153
  """
141
- allow_actions = mrsm.get_config('system', 'api', 'permissions', 'actions', 'non_admin')
142
- if not allow_actions:
143
- return False, (
144
- "The administrator for this server has not allowed actions.\n\n"
145
- "Please contact the system administrator, or if you are running this server, "
146
- "open the configuration file with `edit config system` and search for 'permissions'."
147
- " Under the keys `api:permissions:actions`, "
148
- "you can toggle non-admin actions."
149
- )
150
154
  pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
151
155
  if not is_pipe_registered(pipe, pipes(instance_keys, refresh=True)):
152
156
  raise fastapi.HTTPException(
@@ -157,7 +161,11 @@ def delete_pipe(
157
161
  return results
158
162
 
159
163
 
160
- @app.get(pipes_endpoint + '/keys', tags=['Pipes: Attributes'])
164
+ @app.get(
165
+ pipes_endpoint + '/keys',
166
+ tags=['Pipes: Attributes'],
167
+ response_model=FetchPipesKeysResponseModel,
168
+ )
161
169
  async def fetch_pipes_keys(
162
170
  connector_keys: str = "[]",
163
171
  metric_keys: str = "[]",
@@ -165,10 +173,8 @@ async def fetch_pipes_keys(
165
173
  instance_keys: Optional[str] = None,
166
174
  tags: str = "[]",
167
175
  params: str = "{}",
168
- curr_user = (
169
- fastapi.Depends(manager) if not no_auth else None
170
- ),
171
- ):
176
+ curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
177
+ ) -> FetchPipesKeysResponseModel:
172
178
  """
173
179
  Get a list of tuples of all registered pipes' keys.
174
180
  """
@@ -187,32 +193,33 @@ async def get_pipes(
187
193
  connector_keys: str = "",
188
194
  metric_keys: str = "",
189
195
  location_keys: str = "",
190
- instance_keys: Optional[str] = None,
191
- curr_user=(
192
- fastapi.Depends(manager) if not no_auth else None
193
- ),
196
+ instance_keys: str = "",
197
+ curr_user=fastapi.Security(ScopedAuth(['pipes:read'])),
194
198
  debug: bool = False,
195
- ) -> Dict[str, Any]:
199
+ ) -> PipesWithParametersDictModel:
196
200
  """
197
201
  Get all registered Pipes with metadata, excluding parameters.
198
202
  """
199
- kw = {'debug': debug, 'mrsm_instance': get_api_connector(instance_keys)}
203
+ kw = {'debug': debug, 'mrsm_instance': get_api_connector(instance_keys or None)}
200
204
  if connector_keys != "":
201
205
  kw['connector_keys'] = connector_keys
202
206
  if metric_keys != "":
203
207
  kw['metric_keys'] = metric_keys
204
208
  if location_keys != "":
205
209
  kw['location_keys'] = location_keys
206
- return replace_pipes_in_dict(_get_pipes(**kw), lambda p: p.attributes)
210
+ pipes_dict = replace_pipes_in_dict(_get_pipes(**kw), lambda p: p.attributes)
211
+ for metrics in pipes_dict.values():
212
+ for locations in metrics.values():
213
+ if None in locations:
214
+ locations['None'] = locations.pop(None)
215
+ return pipes_dict
207
216
 
208
217
 
209
218
  @app.get(pipes_endpoint + '/{connector_keys}', tags=['Pipes: Attributes'])
210
219
  async def get_pipes_by_connector(
211
220
  connector_keys: str,
212
221
  instance_keys: Optional[str] = None,
213
- curr_user = (
214
- fastapi.Depends(manager) if not no_auth else None
215
- ),
222
+ curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
216
223
  ) -> Dict[str, Any]:
217
224
  """
218
225
  Get all registered Pipes by connector_keys with metadata, excluding parameters.
@@ -221,7 +228,11 @@ async def get_pipes_by_connector(
221
228
  raise fastapi.HTTPException(
222
229
  status_code=404, detail=f"Connector '{connector_keys}' not found."
223
230
  )
224
- return replace_pipes_in_dict(pipes(instance_keys)[connector_keys], lambda p: p.attributes)
231
+ metrics = replace_pipes_in_dict(pipes(instance_keys)[connector_keys], lambda p: p.attributes)
232
+ for locations in metrics.values():
233
+ if None in locations:
234
+ locations['None'] = locations.pop(None)
235
+ return metrics
225
236
 
226
237
 
227
238
  @app.get(pipes_endpoint + '/{connector_keys}/{metric_key}', tags=['Pipes: Attributes'])
@@ -229,12 +240,10 @@ async def get_pipes_by_connector_and_metric(
229
240
  connector_keys: str,
230
241
  metric_key: str,
231
242
  instance_keys: Optional[str] = None,
232
- curr_user = (
233
- fastapi.Depends(manager) if not no_auth else None
234
- ),
243
+ curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
235
244
  ):
236
245
  """
237
- Get all registered Pipes by connector_keys and metric_key with metadata, excluding parameters.
246
+ Get all registered Pipes by `connector_keys` and `metric_key` with metadata, excluding parameters.
238
247
  """
239
248
  if connector_keys not in pipes(instance_keys):
240
249
  raise fastapi.HTTPException(
@@ -246,10 +255,13 @@ async def get_pipes_by_connector_and_metric(
246
255
  status_code=404,
247
256
  detail=f"Metric '{metric_key}' not found.",
248
257
  )
249
- return replace_pipes_in_dict(
258
+ locations = replace_pipes_in_dict(
250
259
  pipes(instance_keys)[connector_keys][metric_key],
251
260
  lambda p: p.attributes
252
261
  )
262
+ if None in locations:
263
+ locations['None'] = locations.pop(None)
264
+ return locations
253
265
 
254
266
 
255
267
  @app.get(
@@ -261,9 +273,7 @@ async def get_pipe_by_connector_and_metric_and_location(
261
273
  metric_key: str,
262
274
  location_key: str,
263
275
  instance_keys: Optional[str] = None,
264
- curr_user = (
265
- fastapi.Depends(manager) if not no_auth else None
266
- ),
276
+ curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
267
277
  ):
268
278
  """
269
279
  Get a specific Pipe with metadata, excluding parameters.
@@ -299,9 +309,7 @@ def get_sync_time(
299
309
  remote: bool = False,
300
310
  round_down: bool = True,
301
311
  instance_keys: Optional[str] = None,
302
- curr_user = (
303
- fastapi.Depends(manager) if not no_auth else None
304
- ),
312
+ curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
305
313
  ) -> Union[str, int, None]:
306
314
  """
307
315
  Get a Pipe's latest datetime value.
@@ -323,31 +331,74 @@ def get_sync_time(
323
331
  @app.post(
324
332
  pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/data',
325
333
  tags=['Pipes: Data'],
334
+ response_model=SuccessTupleResponseModel,
335
+ openapi_extra={
336
+ 'requestBody': {
337
+ 'content': {
338
+ 'application/json': {
339
+ 'example': [
340
+ {
341
+ 'timestamp': '2026-01-01',
342
+ 'id': 1,
343
+ 'value': 100.1,
344
+ },
345
+ {
346
+ 'timestamp': '2026-01-02',
347
+ 'id': 1,
348
+ 'value': 200.2,
349
+ },
350
+ ],
351
+ },
352
+ 'text/plain': {
353
+ 'example': 'a:1,b:2',
354
+ },
355
+ },
356
+ 'required': True,
357
+ },
358
+ },
326
359
  )
327
- def sync_pipe(
360
+ async def sync_pipe(
328
361
  connector_keys: str,
329
362
  metric_key: str,
330
363
  location_key: str,
331
- data: Union[List[Dict[Any, Any]], Dict[Any, Any]],
364
+ request: fastapi.Request,
332
365
  instance_keys: Optional[str] = None,
333
366
  check_existing: bool = True,
334
367
  blocking: bool = True,
335
368
  force: bool = False,
336
369
  workers: Optional[int] = None,
337
370
  columns: Optional[str] = None,
338
- curr_user = (
339
- fastapi.Depends(manager) if not no_auth else None
340
- ),
371
+ curr_user = fastapi.Security(ScopedAuth(['pipes:write'])),
341
372
  debug: bool = False,
342
- ) -> List[Union[bool, str]]:
373
+ ) -> mrsm.SuccessTuple:
343
374
  """
344
375
  Add data to an existing Pipe.
345
376
  See [`meerschaum.Pipe.sync`](https://docs.meerschaum.io/meerschaum.html#Pipe.sync).
346
377
  """
378
+ body = await request.body()
379
+ try:
380
+ data = json.loads(body)
381
+ except (json.JSONDecodeError, UnicodeDecodeError):
382
+ data = body.decode('utf-8', errors='replace')
383
+
347
384
  if not data:
348
- return [True, "No data to sync."]
385
+ return True, "No data to sync."
386
+
387
+ if isinstance(data, str) and data.strip() and not data.lstrip()[0] not in ('{', '['):
388
+ try:
389
+ lines = data.splitlines()
390
+ data = [string_to_dict(line) for line in lines]
391
+ except Exception:
392
+ data = None
393
+
394
+ if not data:
395
+ raise fastapi.HTTPException(
396
+ status=400,
397
+ detail="Cannot sync given data.",
398
+ )
399
+
349
400
  pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
350
- if pipe.target in ('mrsm_users', 'mrsm_plugins', 'mrsm_pipes'):
401
+ if pipe.target in ('mrsm_users', 'mrsm_plugins', 'mrsm_pipes', 'mrsm_tokens'):
351
402
  raise fastapi.HTTPException(
352
403
  status_code=409,
353
404
  detail=f"Cannot sync data to protected table '{pipe.target}'.",
@@ -364,7 +415,7 @@ def sync_pipe(
364
415
  force=force,
365
416
  workers=workers,
366
417
  )
367
- return list((success, msg))
418
+ return success, msg
368
419
 
369
420
 
370
421
  @app.get(
@@ -387,9 +438,7 @@ def get_pipe_data(
387
438
  date_unit: str = 'us',
388
439
  double_precision: int = 15,
389
440
  geometry_format: str = 'wkb_hex',
390
- curr_user = (
391
- fastapi.Depends(manager) if not no_auth else None
392
- ),
441
+ curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
393
442
  ) -> str:
394
443
  """
395
444
  Get a pipe's data, applying any filtering.
@@ -525,6 +574,7 @@ def get_pipe_chunk_bounds(
525
574
  end: Union[str, int, None] = None,
526
575
  bounded: bool = True,
527
576
  chunk_interval_minutes: Union[int, None] = None,
577
+ curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
528
578
  ) -> List[List[Union[str, int, None]]]:
529
579
  """
530
580
  Return a list of request boundaries between `begin` and `end` (or the pipe's sync times).
@@ -558,16 +608,15 @@ def get_pipe_chunk_bounds(
558
608
  @app.delete(
559
609
  pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/drop',
560
610
  tags=['Pipes: Data'],
611
+ response_model=SuccessTupleResponseModel,
561
612
  )
562
613
  def drop_pipe(
563
614
  connector_keys: str,
564
615
  metric_key: str,
565
616
  location_key: str,
566
617
  instance_keys: Optional[str] = None,
567
- curr_user = (
568
- fastapi.Depends(manager) if not no_auth else None
569
- ),
570
- ):
618
+ curr_user = fastapi.Security(ScopedAuth(['pipes:drop'])),
619
+ ) -> SuccessTupleResponseModel:
571
620
  """
572
621
  Drop a pipe's target table.
573
622
  """
@@ -577,7 +626,7 @@ def drop_pipe(
577
626
  "The administrator for this server has not allowed actions.\n\n"
578
627
  "Please contact the system administrator, or if you are running this server, "
579
628
  "open the configuration file with `edit config system` and search for 'permissions'."
580
- " Under the keys `api:permissions:actions`, " +
629
+ " Under the keys `api:permissions:actions`, " +
581
630
  "you can toggle non-admin actions."
582
631
  )
583
632
  pipe_object = get_pipe(connector_keys, metric_key, location_key, instance_keys)
@@ -589,6 +638,7 @@ def drop_pipe(
589
638
  @app.delete(
590
639
  pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/clear',
591
640
  tags=['Pipes: Data'],
641
+ response_model=SuccessTupleResponseModel,
592
642
  )
593
643
  def clear_pipe(
594
644
  connector_keys: str,
@@ -598,10 +648,8 @@ def clear_pipe(
598
648
  begin: Union[str, int, None] = None,
599
649
  end: Union[str, int, None] = None,
600
650
  params: Optional[str] = None,
601
- curr_user = (
602
- fastapi.Depends(manager) if not no_auth else None
603
- ),
604
- ):
651
+ curr_user = fastapi.Security(ScopedAuth(['pipes:delete'])),
652
+ ) -> SuccessTupleResponseModel:
605
653
  """
606
654
  Delete rows from a pipe's target table.
607
655
  """
@@ -619,15 +667,6 @@ def clear_pipe(
619
667
  detail="Params must be a valid JSON-encoded dictionary.",
620
668
  )
621
669
 
622
- allow_actions = mrsm.get_config('system', 'api', 'permissions', 'actions', 'non_admin')
623
- if not allow_actions:
624
- return False, (
625
- "The administrator for this server has not allowed actions.\n\n"
626
- "Please contact the system administrator, or if you are running this server, "
627
- "open the configuration file with `edit config system` and search for 'permissions'."
628
- " Under the keys `api:permissions:actions`, " +
629
- "you can toggle non-admin actions."
630
- )
631
670
  pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
632
671
  begin, end = pipe.parse_date_bounds(begin, end)
633
672
  results = get_api_connector(instance_keys=instance_keys).clear_pipe(
@@ -653,9 +692,7 @@ def get_pipe_csv(
653
692
  begin: Union[str, int, None] = None,
654
693
  end: Union[str, int, None] = None,
655
694
  params: Optional[str] = None,
656
- curr_user = (
657
- fastapi.Depends(manager) if not no_auth else None
658
- ),
695
+ curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
659
696
  ) -> str:
660
697
  """
661
698
  Get a pipe's data as a CSV file. Optionally set query boundaries.
@@ -720,9 +757,7 @@ def get_pipe_id(
720
757
  metric_key: str,
721
758
  location_key: str,
722
759
  instance_keys: Optional[str] = None,
723
- curr_user = (
724
- fastapi.Depends(manager) if not no_auth else None
725
- ),
760
+ curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
726
761
  ) -> Union[int, str]:
727
762
  """
728
763
  Get a pipe's ID.
@@ -742,9 +777,7 @@ def get_pipe_attributes(
742
777
  metric_key: str,
743
778
  location_key: str,
744
779
  instance_keys: Optional[str] = None,
745
- curr_user=(
746
- fastapi.Depends(manager) if not no_auth else None
747
- ),
780
+ curr_user=fastapi.Security(ScopedAuth(['pipes:read'])),
748
781
  ) -> Dict[str, Any]:
749
782
  """Get a pipe's attributes."""
750
783
  return get_pipe(
@@ -765,9 +798,7 @@ def get_pipe_exists(
765
798
  metric_key: str,
766
799
  location_key: str,
767
800
  instance_keys: Optional[str] = None,
768
- curr_user = (
769
- fastapi.Depends(manager) if not no_auth else None
770
- ),
801
+ curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
771
802
  ) -> bool:
772
803
  """Determine whether a pipe's target table exists."""
773
804
  return get_pipe(connector_keys, metric_key, location_key, instance_keys).exists(debug=debug)
@@ -776,9 +807,7 @@ def get_pipe_exists(
776
807
  @app.post(endpoints['metadata'], tags=['Misc'])
777
808
  def create_metadata(
778
809
  instance_keys: Optional[str] = None,
779
- curr_user = (
780
- fastapi.Depends(manager) if not no_auth else None
781
- ),
810
+ curr_user = fastapi.Security(ScopedAuth(['actions:execute'])),
782
811
  ) -> bool:
783
812
  """Create pipe instance metadata tables."""
784
813
  conn = get_api_connector(instance_keys)
@@ -804,9 +833,7 @@ def get_pipe_rowcount(
804
833
  end: Union[str, int, None] = None,
805
834
  params: Optional[Dict[str, Any]] = None,
806
835
  remote: bool = False,
807
- curr_user = (
808
- fastapi.Depends(manager) if not no_auth else None
809
- ),
836
+ curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
810
837
  ) -> int:
811
838
  """
812
839
  Return a pipe's rowcount.
@@ -852,9 +879,7 @@ def get_pipe_columns_types(
852
879
  metric_key: str,
853
880
  location_key: str,
854
881
  instance_keys: Optional[str] = None,
855
- curr_user=(
856
- fastapi.Depends(manager) if not no_auth else None
857
- ),
882
+ curr_user=fastapi.Security(ScopedAuth(['pipes:read'])),
858
883
  ) -> Dict[str, str]:
859
884
  """
860
885
  Return a dictionary of column names and types.
@@ -868,7 +893,9 @@ def get_pipe_columns_types(
868
893
  }
869
894
  ```
870
895
  """
871
- return get_pipe(connector_keys, metric_key, location_key, instance_keys).dtypes
896
+ pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
897
+ columns_types = pipe.get_columns_types(debug=debug)
898
+ return pipe.dtypes
872
899
 
873
900
 
874
901
  @app.get(
@@ -880,9 +907,7 @@ def get_pipe_columns_indices(
880
907
  metric_key: str,
881
908
  location_key: str,
882
909
  instance_keys: Optional[str] = None,
883
- curr_user=(
884
- fastapi.Depends(manager) if not no_auth else None
885
- ),
910
+ curr_user=fastapi.Security(ScopedAuth(['pipes:read'])),
886
911
  ) -> Dict[str, List[Dict[str, str]]]:
887
912
  """
888
913
  Return a dictionary of column names and related indices.
@@ -934,9 +959,7 @@ def get_pipe_index_names(
934
959
  metric_key: str,
935
960
  location_key: str,
936
961
  instance_keys: Optional[str] = None,
937
- curr_user=(
938
- fastapi.Depends(manager) if not no_auth else None
939
- ),
962
+ curr_user=fastapi.Security(ScopedAuth(['pipes:read'])),
940
963
  ) -> Dict[str, Union[str, Dict[str, str], List[Dict[str, str]]]]:
941
964
  """
942
965
  Return a dictionary of index keys and index names.