meerschaum 2.9.5__py3-none-any.whl → 3.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerschaum/__init__.py +5 -2
- meerschaum/_internal/__init__.py +1 -0
- meerschaum/_internal/arguments/_parse_arguments.py +4 -4
- meerschaum/_internal/arguments/_parser.py +17 -1
- meerschaum/_internal/entry.py +6 -6
- meerschaum/_internal/shell/Shell.py +1 -1
- meerschaum/_internal/static.py +372 -0
- meerschaum/actions/api.py +12 -2
- meerschaum/actions/bootstrap.py +7 -7
- meerschaum/actions/edit.py +142 -18
- meerschaum/actions/register.py +137 -6
- meerschaum/actions/show.py +117 -29
- meerschaum/actions/stop.py +4 -1
- meerschaum/actions/sync.py +1 -1
- meerschaum/actions/tag.py +9 -8
- meerschaum/api/__init__.py +9 -2
- meerschaum/api/_events.py +39 -2
- meerschaum/api/_oauth2.py +118 -8
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -1
- meerschaum/api/dash/callbacks/custom.py +2 -2
- meerschaum/api/dash/callbacks/dashboard.py +102 -18
- meerschaum/api/dash/callbacks/plugins.py +0 -1
- meerschaum/api/dash/callbacks/register.py +1 -1
- meerschaum/api/dash/callbacks/settings/__init__.py +1 -0
- meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
- meerschaum/api/dash/callbacks/settings/tokens.py +388 -0
- meerschaum/api/dash/components.py +30 -8
- meerschaum/api/dash/keys.py +19 -93
- meerschaum/api/dash/pages/dashboard.py +1 -20
- meerschaum/api/dash/pages/settings/__init__.py +1 -0
- meerschaum/api/dash/pages/settings/password_reset.py +1 -1
- meerschaum/api/dash/pages/settings/tokens.py +55 -0
- meerschaum/api/dash/pipes.py +94 -59
- meerschaum/api/dash/sessions.py +12 -0
- meerschaum/api/dash/tokens.py +606 -0
- meerschaum/api/dash/websockets.py +1 -1
- meerschaum/api/dash/webterm.py +4 -0
- meerschaum/api/models/__init__.py +23 -3
- meerschaum/api/models/_actions.py +22 -0
- meerschaum/api/models/_pipes.py +85 -7
- meerschaum/api/models/_tokens.py +81 -0
- meerschaum/api/resources/templates/termpage.html +12 -0
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +3 -4
- meerschaum/api/routes/_connectors.py +3 -7
- meerschaum/api/routes/_jobs.py +14 -35
- meerschaum/api/routes/_login.py +49 -12
- meerschaum/api/routes/_misc.py +5 -10
- meerschaum/api/routes/_pipes.py +134 -111
- meerschaum/api/routes/_plugins.py +38 -28
- meerschaum/api/routes/_tokens.py +236 -0
- meerschaum/api/routes/_users.py +47 -35
- meerschaum/api/routes/_version.py +3 -3
- meerschaum/config/__init__.py +43 -20
- meerschaum/config/_default.py +32 -5
- meerschaum/config/_edit.py +28 -24
- meerschaum/config/_environment.py +1 -1
- meerschaum/config/_patch.py +6 -6
- meerschaum/config/_paths.py +5 -1
- meerschaum/config/_read_config.py +65 -34
- meerschaum/config/_sync.py +6 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/stack/__init__.py +24 -5
- meerschaum/config/static.py +18 -0
- meerschaum/connectors/_Connector.py +10 -4
- meerschaum/connectors/__init__.py +4 -20
- meerschaum/connectors/api/_APIConnector.py +34 -6
- meerschaum/connectors/api/_actions.py +2 -2
- meerschaum/connectors/api/_jobs.py +1 -1
- meerschaum/connectors/api/_login.py +33 -7
- meerschaum/connectors/api/_misc.py +2 -2
- meerschaum/connectors/api/_pipes.py +15 -14
- meerschaum/connectors/api/_plugins.py +2 -2
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/api/_tokens.py +146 -0
- meerschaum/connectors/api/_users.py +70 -58
- meerschaum/connectors/instance/_InstanceConnector.py +83 -0
- meerschaum/connectors/instance/__init__.py +10 -0
- meerschaum/connectors/instance/_pipes.py +442 -0
- meerschaum/connectors/instance/_plugins.py +151 -0
- meerschaum/connectors/instance/_tokens.py +296 -0
- meerschaum/connectors/instance/_users.py +181 -0
- meerschaum/connectors/parse.py +4 -1
- meerschaum/connectors/sql/_SQLConnector.py +8 -5
- meerschaum/connectors/sql/_cli.py +12 -11
- meerschaum/connectors/sql/_create_engine.py +6 -154
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +42 -31
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +8 -1
- meerschaum/connectors/sql/_users.py +29 -2
- meerschaum/connectors/sql/tables/__init__.py +1 -1
- meerschaum/connectors/valkey/_ValkeyConnector.py +2 -4
- meerschaum/connectors/valkey/_pipes.py +9 -10
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +31 -14
- meerschaum/core/Pipe/_attributes.py +156 -58
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_data.py +41 -1
- meerschaum/core/Pipe/_dtypes.py +29 -14
- meerschaum/core/Pipe/_edit.py +12 -4
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +48 -53
- meerschaum/core/Pipe/_verify.py +1 -1
- meerschaum/{plugins → core/Plugin}/_Plugin.py +9 -11
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +221 -0
- meerschaum/core/Token/__init__.py +12 -0
- meerschaum/core/User/_User.py +34 -8
- meerschaum/core/User/__init__.py +9 -1
- meerschaum/core/__init__.py +1 -0
- meerschaum/jobs/_Job.py +3 -2
- meerschaum/jobs/__init__.py +3 -2
- meerschaum/jobs/systemd.py +1 -1
- meerschaum/models/__init__.py +35 -0
- meerschaum/models/pipes.py +247 -0
- meerschaum/models/tokens.py +38 -0
- meerschaum/models/users.py +26 -0
- meerschaum/plugins/__init__.py +22 -7
- meerschaum/plugins/bootstrap.py +2 -1
- meerschaum/utils/_get_pipes.py +68 -27
- meerschaum/utils/daemon/Daemon.py +2 -1
- meerschaum/utils/daemon/__init__.py +30 -2
- meerschaum/utils/dataframe.py +95 -14
- meerschaum/utils/dtypes/__init__.py +91 -18
- meerschaum/utils/dtypes/sql.py +44 -0
- meerschaum/utils/formatting/__init__.py +1 -1
- meerschaum/utils/formatting/_pipes.py +5 -4
- meerschaum/utils/formatting/_shell.py +11 -9
- meerschaum/utils/misc.py +237 -80
- meerschaum/utils/packages/__init__.py +3 -6
- meerschaum/utils/packages/_packages.py +34 -32
- meerschaum/utils/pipes.py +181 -0
- meerschaum/utils/process.py +1 -1
- meerschaum/utils/prompt.py +3 -1
- meerschaum/utils/schedule.py +1 -0
- meerschaum/utils/sql.py +114 -37
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +5 -7
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/METADATA +88 -80
- meerschaum-3.0.0rc1.dist-info/RECORD +282 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/WHEEL +1 -1
- meerschaum/api/models/_interfaces.py +0 -15
- meerschaum/api/models/_locations.py +0 -15
- meerschaum/api/models/_metrics.py +0 -15
- meerschaum/config/static/__init__.py +0 -186
- meerschaum-2.9.5.dist-info/RECORD +0 -263
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0rc1.dist-info}/zip-safe +0 -0
meerschaum/api/routes/_pipes.py
CHANGED
@@ -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
|
-
|
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
|
-
|
97
|
+
success, msg = get_api_connector(instance_keys).register_pipe(pipe, debug=debug)
|
83
98
|
pipes(instance_keys, refresh=True)
|
84
|
-
return
|
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
|
-
|
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
|
-
|
134
|
+
success, msg = get_api_connector(instance_keys).edit_pipe(pipe, patch=patch, debug=debug)
|
121
135
|
pipes(instance_keys, refresh=True)
|
122
|
-
return
|
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
|
-
|
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(
|
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
|
-
|
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:
|
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
|
-
) ->
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
) ->
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|