meerschaum 2.9.5__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meerschaum/__init__.py +5 -2
- meerschaum/_internal/__init__.py +1 -0
- meerschaum/_internal/arguments/_parse_arguments.py +4 -4
- meerschaum/_internal/arguments/_parser.py +33 -4
- meerschaum/_internal/cli/__init__.py +6 -0
- meerschaum/_internal/cli/daemons.py +103 -0
- meerschaum/_internal/cli/entry.py +220 -0
- meerschaum/_internal/cli/workers.py +435 -0
- meerschaum/_internal/docs/index.py +48 -2
- meerschaum/_internal/entry.py +50 -14
- meerschaum/_internal/shell/Shell.py +121 -29
- meerschaum/_internal/shell/__init__.py +4 -1
- meerschaum/_internal/static.py +359 -0
- meerschaum/_internal/term/TermPageHandler.py +1 -2
- meerschaum/_internal/term/__init__.py +40 -6
- meerschaum/_internal/term/tools.py +33 -8
- meerschaum/actions/__init__.py +6 -4
- meerschaum/actions/api.py +53 -13
- meerschaum/actions/attach.py +1 -0
- meerschaum/actions/bootstrap.py +8 -8
- meerschaum/actions/delete.py +4 -2
- meerschaum/actions/edit.py +171 -25
- meerschaum/actions/login.py +8 -8
- meerschaum/actions/register.py +143 -6
- meerschaum/actions/reload.py +22 -5
- meerschaum/actions/restart.py +14 -0
- meerschaum/actions/show.py +184 -31
- meerschaum/actions/start.py +166 -17
- meerschaum/actions/stop.py +38 -2
- meerschaum/actions/sync.py +7 -2
- meerschaum/actions/tag.py +9 -8
- meerschaum/actions/verify.py +5 -8
- meerschaum/api/__init__.py +45 -15
- meerschaum/api/_events.py +46 -4
- meerschaum/api/_oauth2.py +162 -9
- meerschaum/api/_tokens.py +102 -0
- meerschaum/api/dash/__init__.py +0 -3
- meerschaum/api/dash/callbacks/__init__.py +1 -0
- meerschaum/api/dash/callbacks/custom.py +4 -3
- meerschaum/api/dash/callbacks/dashboard.py +198 -118
- meerschaum/api/dash/callbacks/jobs.py +14 -7
- meerschaum/api/dash/callbacks/login.py +10 -1
- meerschaum/api/dash/callbacks/pipes.py +194 -14
- meerschaum/api/dash/callbacks/plugins.py +0 -1
- meerschaum/api/dash/callbacks/register.py +10 -3
- meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
- meerschaum/api/dash/callbacks/tokens.py +389 -0
- meerschaum/api/dash/components.py +36 -15
- meerschaum/api/dash/jobs.py +1 -1
- meerschaum/api/dash/keys.py +35 -93
- meerschaum/api/dash/pages/__init__.py +2 -1
- meerschaum/api/dash/pages/dashboard.py +1 -20
- meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
- meerschaum/api/dash/pages/login.py +2 -2
- meerschaum/api/dash/pages/pipes.py +16 -5
- meerschaum/api/dash/pages/settings/password_reset.py +1 -1
- meerschaum/api/dash/pages/tokens.py +53 -0
- meerschaum/api/dash/pipes.py +382 -95
- meerschaum/api/dash/sessions.py +12 -0
- meerschaum/api/dash/tokens.py +603 -0
- meerschaum/api/dash/websockets.py +1 -1
- meerschaum/api/dash/webterm.py +18 -6
- meerschaum/api/models/__init__.py +23 -3
- meerschaum/api/models/_actions.py +22 -0
- meerschaum/api/models/_pipes.py +91 -7
- meerschaum/api/models/_tokens.py +81 -0
- meerschaum/api/resources/static/js/terminado.js +3 -0
- meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
- meerschaum/api/resources/templates/termpage.html +13 -0
- meerschaum/api/routes/__init__.py +1 -0
- meerschaum/api/routes/_actions.py +3 -4
- meerschaum/api/routes/_connectors.py +3 -7
- meerschaum/api/routes/_jobs.py +26 -35
- meerschaum/api/routes/_login.py +120 -15
- meerschaum/api/routes/_misc.py +5 -10
- meerschaum/api/routes/_pipes.py +178 -143
- meerschaum/api/routes/_plugins.py +38 -28
- meerschaum/api/routes/_tokens.py +236 -0
- meerschaum/api/routes/_users.py +47 -35
- meerschaum/api/routes/_version.py +3 -3
- meerschaum/api/routes/_webterm.py +3 -3
- meerschaum/config/__init__.py +100 -30
- meerschaum/config/_default.py +132 -64
- meerschaum/config/_edit.py +38 -32
- meerschaum/config/_formatting.py +2 -0
- meerschaum/config/_patch.py +10 -8
- meerschaum/config/_paths.py +133 -13
- meerschaum/config/_read_config.py +87 -36
- meerschaum/config/_sync.py +6 -3
- meerschaum/config/_version.py +1 -1
- meerschaum/config/environment.py +262 -0
- meerschaum/config/stack/__init__.py +37 -15
- meerschaum/config/static.py +18 -0
- meerschaum/connectors/_Connector.py +11 -6
- meerschaum/connectors/__init__.py +41 -22
- meerschaum/connectors/api/_APIConnector.py +34 -6
- meerschaum/connectors/api/_actions.py +2 -2
- meerschaum/connectors/api/_jobs.py +12 -1
- meerschaum/connectors/api/_login.py +33 -7
- meerschaum/connectors/api/_misc.py +2 -2
- meerschaum/connectors/api/_pipes.py +23 -32
- meerschaum/connectors/api/_plugins.py +2 -2
- meerschaum/connectors/api/_request.py +1 -1
- meerschaum/connectors/api/_tokens.py +146 -0
- meerschaum/connectors/api/_users.py +70 -58
- meerschaum/connectors/instance/_InstanceConnector.py +83 -0
- meerschaum/connectors/instance/__init__.py +10 -0
- meerschaum/connectors/instance/_pipes.py +442 -0
- meerschaum/connectors/instance/_plugins.py +159 -0
- meerschaum/connectors/instance/_tokens.py +317 -0
- meerschaum/connectors/instance/_users.py +188 -0
- meerschaum/connectors/parse.py +5 -2
- meerschaum/connectors/sql/_SQLConnector.py +22 -5
- meerschaum/connectors/sql/_cli.py +12 -11
- meerschaum/connectors/sql/_create_engine.py +12 -168
- meerschaum/connectors/sql/_fetch.py +2 -18
- meerschaum/connectors/sql/_pipes.py +295 -278
- meerschaum/connectors/sql/_plugins.py +29 -0
- meerschaum/connectors/sql/_sql.py +46 -21
- meerschaum/connectors/sql/_users.py +36 -2
- meerschaum/connectors/sql/tables/__init__.py +254 -122
- meerschaum/connectors/valkey/_ValkeyConnector.py +5 -7
- meerschaum/connectors/valkey/_pipes.py +60 -31
- meerschaum/connectors/valkey/_plugins.py +2 -26
- meerschaum/core/Pipe/__init__.py +115 -85
- meerschaum/core/Pipe/_attributes.py +425 -124
- meerschaum/core/Pipe/_bootstrap.py +54 -24
- meerschaum/core/Pipe/_cache.py +555 -0
- meerschaum/core/Pipe/_clear.py +0 -11
- meerschaum/core/Pipe/_data.py +96 -68
- meerschaum/core/Pipe/_deduplicate.py +0 -13
- meerschaum/core/Pipe/_delete.py +12 -21
- meerschaum/core/Pipe/_drop.py +11 -23
- meerschaum/core/Pipe/_dtypes.py +49 -19
- meerschaum/core/Pipe/_edit.py +14 -4
- meerschaum/core/Pipe/_fetch.py +1 -1
- meerschaum/core/Pipe/_index.py +8 -14
- meerschaum/core/Pipe/_show.py +5 -5
- meerschaum/core/Pipe/_sync.py +123 -204
- meerschaum/core/Pipe/_verify.py +4 -4
- meerschaum/{plugins → core/Plugin}/_Plugin.py +16 -12
- meerschaum/core/Plugin/__init__.py +1 -1
- meerschaum/core/Token/_Token.py +220 -0
- meerschaum/core/Token/__init__.py +12 -0
- meerschaum/core/User/_User.py +35 -10
- meerschaum/core/User/__init__.py +9 -1
- meerschaum/core/__init__.py +1 -0
- meerschaum/jobs/_Executor.py +88 -4
- meerschaum/jobs/_Job.py +149 -38
- meerschaum/jobs/__init__.py +3 -2
- meerschaum/jobs/systemd.py +8 -3
- meerschaum/models/__init__.py +35 -0
- meerschaum/models/pipes.py +247 -0
- meerschaum/models/tokens.py +38 -0
- meerschaum/models/users.py +26 -0
- meerschaum/plugins/__init__.py +301 -88
- meerschaum/plugins/bootstrap.py +510 -4
- meerschaum/utils/_get_pipes.py +97 -30
- meerschaum/utils/daemon/Daemon.py +199 -43
- meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
- meerschaum/utils/daemon/RotatingFile.py +63 -36
- meerschaum/utils/daemon/StdinFile.py +53 -13
- meerschaum/utils/daemon/__init__.py +47 -6
- meerschaum/utils/daemon/_names.py +6 -3
- meerschaum/utils/dataframe.py +479 -81
- meerschaum/utils/debug.py +49 -19
- meerschaum/utils/dtypes/__init__.py +476 -34
- meerschaum/utils/dtypes/sql.py +369 -29
- meerschaum/utils/formatting/__init__.py +5 -2
- meerschaum/utils/formatting/_jobs.py +1 -1
- meerschaum/utils/formatting/_pipes.py +52 -50
- meerschaum/utils/formatting/_pprint.py +1 -0
- meerschaum/utils/formatting/_shell.py +44 -18
- meerschaum/utils/misc.py +268 -186
- meerschaum/utils/packages/__init__.py +25 -40
- meerschaum/utils/packages/_packages.py +42 -34
- meerschaum/utils/pipes.py +213 -0
- meerschaum/utils/process.py +2 -2
- meerschaum/utils/prompt.py +175 -144
- meerschaum/utils/schedule.py +2 -1
- meerschaum/utils/sql.py +134 -47
- meerschaum/utils/threading.py +42 -0
- meerschaum/utils/typing.py +1 -4
- meerschaum/utils/venv/_Venv.py +2 -2
- meerschaum/utils/venv/__init__.py +7 -7
- meerschaum/utils/warnings.py +19 -13
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/METADATA +94 -96
- meerschaum-3.0.0.dist-info/RECORD +289 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/WHEEL +1 -1
- meerschaum-3.0.0.dist-info/licenses/NOTICE +2 -0
- meerschaum/api/models/_interfaces.py +0 -15
- meerschaum/api/models/_locations.py +0 -15
- meerschaum/api/models/_metrics.py +0 -15
- meerschaum/config/_environment.py +0 -145
- meerschaum/config/static/__init__.py +0 -186
- meerschaum-2.9.5.dist-info/RECORD +0 -263
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/zip-safe +0 -0
meerschaum/api/routes/_pipes.py
CHANGED
@@ -23,9 +23,23 @@ from meerschaum.api import (
|
|
23
23
|
pipes,
|
24
24
|
get_pipe,
|
25
25
|
_get_pipes,
|
26
|
-
manager,
|
27
26
|
debug,
|
28
|
-
|
27
|
+
ScopedAuth,
|
28
|
+
manager,
|
29
|
+
)
|
30
|
+
from meerschaum.models import (
|
31
|
+
ConnectorKeysModel,
|
32
|
+
MetricKeyModel,
|
33
|
+
LocationKeyModel,
|
34
|
+
InstanceKeysModel,
|
35
|
+
PipeModel,
|
36
|
+
PipeWithParametersModel,
|
37
|
+
PipesWithParametersDictModel,
|
38
|
+
)
|
39
|
+
from meerschaum.api.models import (
|
40
|
+
SuccessTupleResponseModel,
|
41
|
+
FetchPipesKeysResponseModel,
|
42
|
+
SyncPipeRequestModel,
|
29
43
|
)
|
30
44
|
from meerschaum.api._chunks import generate_chunks_cursor_token
|
31
45
|
from meerschaum.utils.packages import attempt_import
|
@@ -35,6 +49,7 @@ from meerschaum.utils.misc import (
|
|
35
49
|
is_pipe_registered,
|
36
50
|
is_int,
|
37
51
|
replace_pipes_in_dict,
|
52
|
+
string_to_dict,
|
38
53
|
)
|
39
54
|
from meerschaum.connectors.sql.tables import get_tables
|
40
55
|
|
@@ -49,6 +64,7 @@ MAX_RESPONSE_ROW_LIMIT: int = mrsm.get_config('system', 'api', 'data', 'max_resp
|
|
49
64
|
@app.post(
|
50
65
|
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/register',
|
51
66
|
tags=['Pipes: Attributes'],
|
67
|
+
response_model=SuccessTupleResponseModel,
|
52
68
|
)
|
53
69
|
def register_pipe(
|
54
70
|
connector_keys: str,
|
@@ -56,10 +72,8 @@ def register_pipe(
|
|
56
72
|
location_key: str,
|
57
73
|
instance_keys: Optional[str] = None,
|
58
74
|
parameters: Optional[Dict[str, Any]] = None,
|
59
|
-
curr_user = (
|
60
|
-
|
61
|
-
),
|
62
|
-
):
|
75
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:write']), scopes=['pipes:write']),
|
76
|
+
) -> mrsm.SuccessTuple:
|
63
77
|
"""
|
64
78
|
Register a new pipe.
|
65
79
|
"""
|
@@ -69,7 +83,7 @@ def register_pipe(
|
|
69
83
|
"The administrator for this server has not allowed pipe registration.\n\n"
|
70
84
|
"Please contact the system administrator, or if you are running this server, "
|
71
85
|
"open the configuration file with `edit config system` and search for 'permissions'."
|
72
|
-
" Under the keys `api:permissions:registration`, " +
|
86
|
+
" Under the keys `api:permissions:registration`, " +
|
73
87
|
"you can toggle various registration types."
|
74
88
|
)
|
75
89
|
pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
@@ -79,14 +93,16 @@ def register_pipe(
|
|
79
93
|
)
|
80
94
|
if parameters:
|
81
95
|
pipe.parameters = parameters
|
82
|
-
|
83
|
-
|
84
|
-
|
96
|
+
|
97
|
+
success, msg = get_api_connector(instance_keys).register_pipe(pipe, debug=debug)
|
98
|
+
_ = pipes(instance_keys, refresh=True)
|
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
|
+
) -> mrsm.SuccessTuple:
|
102
116
|
"""
|
103
117
|
Edit an existing pipe's parameters.
|
104
118
|
"""
|
@@ -111,53 +125,50 @@ def edit_pipe(
|
|
111
125
|
" Under the keys `api:permissions:actions`, "
|
112
126
|
"you can toggle non-admin actions."
|
113
127
|
)
|
128
|
+
|
114
129
|
pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
115
130
|
if not is_pipe_registered(pipe, pipes(instance_keys, refresh=True)):
|
116
131
|
raise fastapi.HTTPException(
|
117
132
|
status_code=409, detail=f"{pipe} is not registered."
|
118
133
|
)
|
134
|
+
|
119
135
|
pipe.parameters = parameters
|
120
|
-
|
121
|
-
pipes(instance_keys, refresh=True)
|
122
|
-
return
|
136
|
+
success, msg = get_api_connector(instance_keys).edit_pipe(pipe, patch=patch, debug=debug)
|
137
|
+
_ = pipes(instance_keys, refresh=True)
|
138
|
+
return success, msg
|
123
139
|
|
124
140
|
|
125
141
|
@app.delete(
|
126
142
|
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/delete',
|
127
143
|
tags=['Pipes: Attributes'],
|
144
|
+
response_model=SuccessTupleResponseModel,
|
128
145
|
)
|
129
146
|
def delete_pipe(
|
130
147
|
connector_keys: str,
|
131
148
|
metric_key: str,
|
132
149
|
location_key: str,
|
133
150
|
instance_keys: Optional[str] = None,
|
134
|
-
curr_user = (
|
135
|
-
|
136
|
-
),
|
137
|
-
):
|
151
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:delete'])),
|
152
|
+
) -> mrsm.SuccessTuple:
|
138
153
|
"""
|
139
154
|
Delete a Pipe (without dropping its table).
|
140
155
|
"""
|
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
156
|
pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
151
157
|
if not is_pipe_registered(pipe, pipes(instance_keys, refresh=True)):
|
152
158
|
raise fastapi.HTTPException(
|
153
159
|
status_code=409, detail=f"{pipe} is not registered."
|
154
160
|
)
|
155
|
-
|
156
|
-
|
157
|
-
|
161
|
+
|
162
|
+
success, msg = get_api_connector(instance_keys).delete_pipe(pipe, debug=debug)
|
163
|
+
_ = pipes(instance_keys, refresh=True)
|
164
|
+
return success, msg
|
158
165
|
|
159
166
|
|
160
|
-
@app.get(
|
167
|
+
@app.get(
|
168
|
+
pipes_endpoint + '/keys',
|
169
|
+
tags=['Pipes: Attributes'],
|
170
|
+
response_model=FetchPipesKeysResponseModel,
|
171
|
+
)
|
161
172
|
async def fetch_pipes_keys(
|
162
173
|
connector_keys: str = "[]",
|
163
174
|
metric_keys: str = "[]",
|
@@ -165,10 +176,8 @@ async def fetch_pipes_keys(
|
|
165
176
|
instance_keys: Optional[str] = None,
|
166
177
|
tags: str = "[]",
|
167
178
|
params: str = "{}",
|
168
|
-
curr_user = (
|
169
|
-
|
170
|
-
),
|
171
|
-
):
|
179
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
|
180
|
+
) -> FetchPipesKeysResponseModel:
|
172
181
|
"""
|
173
182
|
Get a list of tuples of all registered pipes' keys.
|
174
183
|
"""
|
@@ -179,7 +188,10 @@ async def fetch_pipes_keys(
|
|
179
188
|
tags=json.loads(tags),
|
180
189
|
params=json.loads(params),
|
181
190
|
)
|
182
|
-
return
|
191
|
+
return [
|
192
|
+
(keys_tuple[0], keys_tuple[1], keys_tuple[2])
|
193
|
+
for keys_tuple in keys
|
194
|
+
]
|
183
195
|
|
184
196
|
|
185
197
|
@app.get(pipes_endpoint, tags=['Pipes: Attributes'])
|
@@ -187,41 +199,51 @@ async def get_pipes(
|
|
187
199
|
connector_keys: str = "",
|
188
200
|
metric_keys: str = "",
|
189
201
|
location_keys: str = "",
|
190
|
-
instance_keys:
|
191
|
-
curr_user=(
|
192
|
-
|
193
|
-
),
|
194
|
-
debug: bool = False,
|
195
|
-
) -> Dict[str, Any]:
|
202
|
+
instance_keys: str = "",
|
203
|
+
curr_user=fastapi.Security(ScopedAuth(['pipes:read'])),
|
204
|
+
) -> PipesWithParametersDictModel:
|
196
205
|
"""
|
197
206
|
Get all registered Pipes with metadata, excluding parameters.
|
198
207
|
"""
|
199
|
-
kw = {'debug': debug, 'mrsm_instance': get_api_connector(instance_keys)}
|
208
|
+
kw = {'debug': debug, 'mrsm_instance': get_api_connector(instance_keys or None)}
|
200
209
|
if connector_keys != "":
|
201
210
|
kw['connector_keys'] = connector_keys
|
202
211
|
if metric_keys != "":
|
203
212
|
kw['metric_keys'] = metric_keys
|
204
213
|
if location_keys != "":
|
205
214
|
kw['location_keys'] = location_keys
|
206
|
-
|
215
|
+
|
216
|
+
pipes_dict = replace_pipes_in_dict(_get_pipes(**kw), lambda p: p.attributes)
|
217
|
+
for metrics in pipes_dict.values():
|
218
|
+
for locations in metrics.values():
|
219
|
+
if None in locations:
|
220
|
+
locations['None'] = locations.pop(None)
|
221
|
+
return pipes_dict
|
207
222
|
|
208
223
|
|
209
224
|
@app.get(pipes_endpoint + '/{connector_keys}', tags=['Pipes: Attributes'])
|
210
225
|
async def get_pipes_by_connector(
|
211
226
|
connector_keys: str,
|
212
227
|
instance_keys: Optional[str] = None,
|
213
|
-
curr_user = (
|
214
|
-
fastapi.Depends(manager) if not no_auth else None
|
215
|
-
),
|
228
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
|
216
229
|
) -> Dict[str, Any]:
|
217
230
|
"""
|
218
231
|
Get all registered Pipes by connector_keys with metadata, excluding parameters.
|
219
232
|
"""
|
220
|
-
if connector_keys not in pipes(instance_keys):
|
233
|
+
if connector_keys not in pipes(instance_keys, refresh=True):
|
221
234
|
raise fastapi.HTTPException(
|
222
235
|
status_code=404, detail=f"Connector '{connector_keys}' not found."
|
223
236
|
)
|
224
|
-
|
237
|
+
|
238
|
+
metrics = replace_pipes_in_dict(
|
239
|
+
pipes(instance_keys, refresh=False)[connector_keys],
|
240
|
+
lambda p: p.attributes
|
241
|
+
)
|
242
|
+
for locations in metrics.values():
|
243
|
+
if None in locations:
|
244
|
+
locations['None'] = locations.pop(None)
|
245
|
+
|
246
|
+
return metrics
|
225
247
|
|
226
248
|
|
227
249
|
@app.get(pipes_endpoint + '/{connector_keys}/{metric_key}', tags=['Pipes: Attributes'])
|
@@ -229,28 +251,33 @@ async def get_pipes_by_connector_and_metric(
|
|
229
251
|
connector_keys: str,
|
230
252
|
metric_key: str,
|
231
253
|
instance_keys: Optional[str] = None,
|
232
|
-
curr_user = (
|
233
|
-
fastapi.Depends(manager) if not no_auth else None
|
234
|
-
),
|
254
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
|
235
255
|
):
|
236
256
|
"""
|
237
|
-
Get all registered Pipes by connector_keys and metric_key with metadata, excluding parameters.
|
257
|
+
Get all registered Pipes by `connector_keys` and `metric_key` with metadata, excluding parameters.
|
238
258
|
"""
|
239
|
-
if connector_keys not in pipes(instance_keys):
|
259
|
+
if connector_keys not in pipes(instance_keys, refresh=True):
|
240
260
|
raise fastapi.HTTPException(
|
241
261
|
status_code=404,
|
242
262
|
detail=f"Connector '{connector_keys}' not found.",
|
243
263
|
)
|
244
|
-
|
264
|
+
|
265
|
+
if metric_key not in pipes(instance_keys, refresh=False)[connector_keys]:
|
245
266
|
raise fastapi.HTTPException(
|
246
267
|
status_code=404,
|
247
268
|
detail=f"Metric '{metric_key}' not found.",
|
248
269
|
)
|
249
|
-
|
250
|
-
|
270
|
+
|
271
|
+
locations = replace_pipes_in_dict(
|
272
|
+
pipes(instance_keys, refresh=False)[connector_keys][metric_key],
|
251
273
|
lambda p: p.attributes
|
252
274
|
)
|
253
275
|
|
276
|
+
if None in locations:
|
277
|
+
locations['None'] = locations.pop(None)
|
278
|
+
|
279
|
+
return locations
|
280
|
+
|
254
281
|
|
255
282
|
@app.get(
|
256
283
|
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}',
|
@@ -261,29 +288,30 @@ async def get_pipe_by_connector_and_metric_and_location(
|
|
261
288
|
metric_key: str,
|
262
289
|
location_key: str,
|
263
290
|
instance_keys: Optional[str] = None,
|
264
|
-
curr_user = (
|
265
|
-
fastapi.Depends(manager) if not no_auth else None
|
266
|
-
),
|
291
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
|
267
292
|
):
|
268
293
|
"""
|
269
294
|
Get a specific Pipe with metadata, excluding parameters.
|
270
295
|
"""
|
271
|
-
if connector_keys not in pipes(instance_keys):
|
296
|
+
if connector_keys not in pipes(instance_keys, refresh=True):
|
272
297
|
raise fastapi.HTTPException(
|
273
298
|
status_code=404,
|
274
299
|
detail=f"Connector '{connector_keys}' not found.",
|
275
300
|
)
|
276
|
-
|
301
|
+
|
302
|
+
if metric_key not in pipes(instance_keys, refresh=False)[connector_keys]:
|
277
303
|
raise fastapi.HTTPException(status_code=404, detail=f"Metric '{metric_key}' not found.")
|
304
|
+
|
278
305
|
if location_key in ('[None]', 'None', 'null'):
|
279
306
|
location_key = None
|
280
|
-
|
307
|
+
|
308
|
+
if location_key not in pipes(instance_keys, refresh=False)[connector_keys][metric_key]:
|
281
309
|
raise fastapi.HTTPException(
|
282
310
|
status_code=404,
|
283
311
|
detail=f"location_key '{location_key}' not found."
|
284
312
|
)
|
285
313
|
|
286
|
-
return pipes(instance_keys)[connector_keys][metric_key][location_key].attributes
|
314
|
+
return pipes(instance_keys, refresh=False)[connector_keys][metric_key][location_key].attributes
|
287
315
|
|
288
316
|
|
289
317
|
@app.get(
|
@@ -299,9 +327,7 @@ def get_sync_time(
|
|
299
327
|
remote: bool = False,
|
300
328
|
round_down: bool = True,
|
301
329
|
instance_keys: Optional[str] = None,
|
302
|
-
curr_user = (
|
303
|
-
fastapi.Depends(manager) if not no_auth else None
|
304
|
-
),
|
330
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
|
305
331
|
) -> Union[str, int, None]:
|
306
332
|
"""
|
307
333
|
Get a Pipe's latest datetime value.
|
@@ -323,31 +349,73 @@ def get_sync_time(
|
|
323
349
|
@app.post(
|
324
350
|
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/data',
|
325
351
|
tags=['Pipes: Data'],
|
352
|
+
response_model=SuccessTupleResponseModel,
|
353
|
+
openapi_extra={
|
354
|
+
'requestBody': {
|
355
|
+
'content': {
|
356
|
+
'application/json': {
|
357
|
+
'example': [
|
358
|
+
{
|
359
|
+
'timestamp': '2026-01-01',
|
360
|
+
'id': 1,
|
361
|
+
'value': 100.1,
|
362
|
+
},
|
363
|
+
{
|
364
|
+
'timestamp': '2026-01-02',
|
365
|
+
'id': 1,
|
366
|
+
'value': 200.2,
|
367
|
+
},
|
368
|
+
],
|
369
|
+
},
|
370
|
+
'text/plain': {
|
371
|
+
'example': 'a:1,b:2',
|
372
|
+
},
|
373
|
+
},
|
374
|
+
'required': True,
|
375
|
+
},
|
376
|
+
},
|
326
377
|
)
|
327
|
-
def sync_pipe(
|
378
|
+
async def sync_pipe(
|
328
379
|
connector_keys: str,
|
329
380
|
metric_key: str,
|
330
381
|
location_key: str,
|
331
|
-
|
382
|
+
request: fastapi.Request,
|
332
383
|
instance_keys: Optional[str] = None,
|
333
384
|
check_existing: bool = True,
|
334
385
|
blocking: bool = True,
|
335
386
|
force: bool = False,
|
336
387
|
workers: Optional[int] = None,
|
337
388
|
columns: Optional[str] = None,
|
338
|
-
curr_user = (
|
339
|
-
|
340
|
-
),
|
341
|
-
debug: bool = False,
|
342
|
-
) -> List[Union[bool, str]]:
|
389
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:write'])),
|
390
|
+
) -> mrsm.SuccessTuple:
|
343
391
|
"""
|
344
392
|
Add data to an existing Pipe.
|
345
393
|
See [`meerschaum.Pipe.sync`](https://docs.meerschaum.io/meerschaum.html#Pipe.sync).
|
346
394
|
"""
|
395
|
+
body = await request.body()
|
396
|
+
try:
|
397
|
+
data = json.loads(body)
|
398
|
+
except (json.JSONDecodeError, UnicodeDecodeError):
|
399
|
+
data = body.decode('utf-8', errors='replace')
|
400
|
+
|
347
401
|
if not data:
|
348
|
-
return
|
349
|
-
|
350
|
-
if
|
402
|
+
return True, "No data to sync."
|
403
|
+
|
404
|
+
if isinstance(data, str) and data.strip() and not data.lstrip()[0] not in ('{', '['):
|
405
|
+
try:
|
406
|
+
lines = data.splitlines()
|
407
|
+
data = [string_to_dict(line) for line in lines]
|
408
|
+
except Exception:
|
409
|
+
data = None
|
410
|
+
|
411
|
+
if not data:
|
412
|
+
raise fastapi.HTTPException(
|
413
|
+
status=400,
|
414
|
+
detail="Cannot sync given data.",
|
415
|
+
)
|
416
|
+
|
417
|
+
pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys, refresh=True)
|
418
|
+
if pipe.target in ('mrsm_users', 'mrsm_plugins', 'mrsm_pipes', 'mrsm_tokens'):
|
351
419
|
raise fastapi.HTTPException(
|
352
420
|
status_code=409,
|
353
421
|
detail=f"Cannot sync data to protected table '{pipe.target}'.",
|
@@ -364,7 +432,7 @@ def sync_pipe(
|
|
364
432
|
force=force,
|
365
433
|
workers=workers,
|
366
434
|
)
|
367
|
-
return
|
435
|
+
return success, msg
|
368
436
|
|
369
437
|
|
370
438
|
@app.get(
|
@@ -387,9 +455,7 @@ def get_pipe_data(
|
|
387
455
|
date_unit: str = 'us',
|
388
456
|
double_precision: int = 15,
|
389
457
|
geometry_format: str = 'wkb_hex',
|
390
|
-
curr_user = (
|
391
|
-
fastapi.Depends(manager) if not no_auth else None
|
392
|
-
),
|
458
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
|
393
459
|
) -> str:
|
394
460
|
"""
|
395
461
|
Get a pipe's data, applying any filtering.
|
@@ -525,6 +591,7 @@ def get_pipe_chunk_bounds(
|
|
525
591
|
end: Union[str, int, None] = None,
|
526
592
|
bounded: bool = True,
|
527
593
|
chunk_interval_minutes: Union[int, None] = None,
|
594
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
|
528
595
|
) -> List[List[Union[str, int, None]]]:
|
529
596
|
"""
|
530
597
|
Return a list of request boundaries between `begin` and `end` (or the pipe's sync times).
|
@@ -558,37 +625,28 @@ def get_pipe_chunk_bounds(
|
|
558
625
|
@app.delete(
|
559
626
|
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/drop',
|
560
627
|
tags=['Pipes: Data'],
|
628
|
+
response_model=SuccessTupleResponseModel,
|
561
629
|
)
|
562
630
|
def drop_pipe(
|
563
631
|
connector_keys: str,
|
564
632
|
metric_key: str,
|
565
633
|
location_key: str,
|
566
634
|
instance_keys: Optional[str] = None,
|
567
|
-
curr_user = (
|
568
|
-
|
569
|
-
),
|
570
|
-
):
|
635
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:drop'])),
|
636
|
+
) -> mrsm.SuccessTuple:
|
571
637
|
"""
|
572
638
|
Drop a pipe's target table.
|
573
639
|
"""
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
"Please contact the system administrator, or if you are running this server, "
|
579
|
-
"open the configuration file with `edit config system` and search for 'permissions'."
|
580
|
-
" Under the keys `api:permissions:actions`, " +
|
581
|
-
"you can toggle non-admin actions."
|
582
|
-
)
|
583
|
-
pipe_object = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
584
|
-
results = get_api_connector(instance_keys=instance_keys).drop_pipe(pipe_object, debug=debug)
|
585
|
-
pipes(instance_keys, refresh=True)
|
586
|
-
return results
|
640
|
+
pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
641
|
+
success, msg = pipe.drop(debug=debug)
|
642
|
+
_ = pipes(instance_keys, refresh=True)
|
643
|
+
return success, msg
|
587
644
|
|
588
645
|
|
589
646
|
@app.delete(
|
590
647
|
pipes_endpoint + '/{connector_keys}/{metric_key}/{location_key}/clear',
|
591
648
|
tags=['Pipes: Data'],
|
649
|
+
response_model=SuccessTupleResponseModel,
|
592
650
|
)
|
593
651
|
def clear_pipe(
|
594
652
|
connector_keys: str,
|
@@ -598,10 +656,8 @@ def clear_pipe(
|
|
598
656
|
begin: Union[str, int, None] = None,
|
599
657
|
end: Union[str, int, None] = None,
|
600
658
|
params: Optional[str] = None,
|
601
|
-
curr_user = (
|
602
|
-
|
603
|
-
),
|
604
|
-
):
|
659
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:delete'])),
|
660
|
+
) -> SuccessTupleResponseModel:
|
605
661
|
"""
|
606
662
|
Delete rows from a pipe's target table.
|
607
663
|
"""
|
@@ -613,21 +669,13 @@ def clear_pipe(
|
|
613
669
|
_params = json.loads(params)
|
614
670
|
except Exception:
|
615
671
|
_params = None
|
672
|
+
|
616
673
|
if not isinstance(_params, dict):
|
617
674
|
raise fastapi.HTTPException(
|
618
675
|
status_code=409,
|
619
676
|
detail="Params must be a valid JSON-encoded dictionary.",
|
620
677
|
)
|
621
678
|
|
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
679
|
pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
632
680
|
begin, end = pipe.parse_date_bounds(begin, end)
|
633
681
|
results = get_api_connector(instance_keys=instance_keys).clear_pipe(
|
@@ -637,7 +685,7 @@ def clear_pipe(
|
|
637
685
|
params=_params,
|
638
686
|
debug=debug,
|
639
687
|
)
|
640
|
-
pipes(instance_keys, refresh=True)
|
688
|
+
_ = pipes(instance_keys, refresh=True)
|
641
689
|
return results
|
642
690
|
|
643
691
|
|
@@ -653,9 +701,7 @@ def get_pipe_csv(
|
|
653
701
|
begin: Union[str, int, None] = None,
|
654
702
|
end: Union[str, int, None] = None,
|
655
703
|
params: Optional[str] = None,
|
656
|
-
curr_user = (
|
657
|
-
fastapi.Depends(manager) if not no_auth else None
|
658
|
-
),
|
704
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
|
659
705
|
) -> str:
|
660
706
|
"""
|
661
707
|
Get a pipe's data as a CSV file. Optionally set query boundaries.
|
@@ -720,9 +766,7 @@ def get_pipe_id(
|
|
720
766
|
metric_key: str,
|
721
767
|
location_key: str,
|
722
768
|
instance_keys: Optional[str] = None,
|
723
|
-
curr_user = (
|
724
|
-
fastapi.Depends(manager) if not no_auth else None
|
725
|
-
),
|
769
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
|
726
770
|
) -> Union[int, str]:
|
727
771
|
"""
|
728
772
|
Get a pipe's ID.
|
@@ -742,9 +786,7 @@ def get_pipe_attributes(
|
|
742
786
|
metric_key: str,
|
743
787
|
location_key: str,
|
744
788
|
instance_keys: Optional[str] = None,
|
745
|
-
curr_user=(
|
746
|
-
fastapi.Depends(manager) if not no_auth else None
|
747
|
-
),
|
789
|
+
curr_user=fastapi.Security(ScopedAuth(['pipes:read'])),
|
748
790
|
) -> Dict[str, Any]:
|
749
791
|
"""Get a pipe's attributes."""
|
750
792
|
return get_pipe(
|
@@ -765,9 +807,7 @@ def get_pipe_exists(
|
|
765
807
|
metric_key: str,
|
766
808
|
location_key: str,
|
767
809
|
instance_keys: Optional[str] = None,
|
768
|
-
curr_user = (
|
769
|
-
fastapi.Depends(manager) if not no_auth else None
|
770
|
-
),
|
810
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
|
771
811
|
) -> bool:
|
772
812
|
"""Determine whether a pipe's target table exists."""
|
773
813
|
return get_pipe(connector_keys, metric_key, location_key, instance_keys).exists(debug=debug)
|
@@ -776,9 +816,7 @@ def get_pipe_exists(
|
|
776
816
|
@app.post(endpoints['metadata'], tags=['Misc'])
|
777
817
|
def create_metadata(
|
778
818
|
instance_keys: Optional[str] = None,
|
779
|
-
curr_user = (
|
780
|
-
fastapi.Depends(manager) if not no_auth else None
|
781
|
-
),
|
819
|
+
curr_user = fastapi.Security(ScopedAuth(['actions:execute'])),
|
782
820
|
) -> bool:
|
783
821
|
"""Create pipe instance metadata tables."""
|
784
822
|
conn = get_api_connector(instance_keys)
|
@@ -804,9 +842,7 @@ def get_pipe_rowcount(
|
|
804
842
|
end: Union[str, int, None] = None,
|
805
843
|
params: Optional[Dict[str, Any]] = None,
|
806
844
|
remote: bool = False,
|
807
|
-
curr_user = (
|
808
|
-
fastapi.Depends(manager) if not no_auth else None
|
809
|
-
),
|
845
|
+
curr_user = fastapi.Security(ScopedAuth(['pipes:read'])),
|
810
846
|
) -> int:
|
811
847
|
"""
|
812
848
|
Return a pipe's rowcount.
|
@@ -852,9 +888,7 @@ def get_pipe_columns_types(
|
|
852
888
|
metric_key: str,
|
853
889
|
location_key: str,
|
854
890
|
instance_keys: Optional[str] = None,
|
855
|
-
curr_user=(
|
856
|
-
fastapi.Depends(manager) if not no_auth else None
|
857
|
-
),
|
891
|
+
curr_user=fastapi.Security(ScopedAuth(['pipes:read'])),
|
858
892
|
) -> Dict[str, str]:
|
859
893
|
"""
|
860
894
|
Return a dictionary of column names and types.
|
@@ -868,7 +902,12 @@ def get_pipe_columns_types(
|
|
868
902
|
}
|
869
903
|
```
|
870
904
|
"""
|
871
|
-
|
905
|
+
pipe = get_pipe(connector_keys, metric_key, location_key, instance_keys)
|
906
|
+
columns_types = (
|
907
|
+
pipe.get_columns_types(debug=debug)
|
908
|
+
or pipe.get_dtypes(refresh=True, debug=debug)
|
909
|
+
)
|
910
|
+
return columns_types
|
872
911
|
|
873
912
|
|
874
913
|
@app.get(
|
@@ -880,9 +919,7 @@ def get_pipe_columns_indices(
|
|
880
919
|
metric_key: str,
|
881
920
|
location_key: str,
|
882
921
|
instance_keys: Optional[str] = None,
|
883
|
-
curr_user=(
|
884
|
-
fastapi.Depends(manager) if not no_auth else None
|
885
|
-
),
|
922
|
+
curr_user=fastapi.Security(ScopedAuth(['pipes:read'])),
|
886
923
|
) -> Dict[str, List[Dict[str, str]]]:
|
887
924
|
"""
|
888
925
|
Return a dictionary of column names and related indices.
|
@@ -934,9 +971,7 @@ def get_pipe_index_names(
|
|
934
971
|
metric_key: str,
|
935
972
|
location_key: str,
|
936
973
|
instance_keys: Optional[str] = None,
|
937
|
-
curr_user=(
|
938
|
-
fastapi.Depends(manager) if not no_auth else None
|
939
|
-
),
|
974
|
+
curr_user=fastapi.Security(ScopedAuth(['pipes:read'])),
|
940
975
|
) -> Dict[str, Union[str, Dict[str, str], List[Dict[str, str]]]]:
|
941
976
|
"""
|
942
977
|
Return a dictionary of index keys and index names.
|