meerschaum 2.7.9__py3-none-any.whl → 2.8.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/_internal/arguments/_parser.py +17 -5
- meerschaum/_internal/term/TermPageHandler.py +1 -1
- meerschaum/_internal/term/__init__.py +1 -1
- meerschaum/actions/api.py +36 -10
- meerschaum/actions/copy.py +3 -1
- meerschaum/actions/index.py +1 -1
- meerschaum/actions/show.py +7 -7
- meerschaum/actions/sync.py +5 -1
- meerschaum/actions/verify.py +14 -1
- meerschaum/api/__init__.py +77 -41
- meerschaum/api/_exceptions.py +18 -0
- meerschaum/api/dash/__init__.py +4 -2
- meerschaum/api/dash/callbacks/dashboard.py +30 -1
- meerschaum/api/dash/components.py +2 -2
- meerschaum/api/dash/webterm.py +23 -4
- meerschaum/api/models/_pipes.py +8 -8
- meerschaum/api/resources/static/css/dash.css +2 -2
- meerschaum/api/resources/templates/termpage.html +5 -1
- meerschaum/api/routes/__init__.py +15 -12
- meerschaum/api/routes/_connectors.py +30 -28
- meerschaum/api/routes/_index.py +16 -7
- meerschaum/api/routes/_misc.py +30 -22
- meerschaum/api/routes/_pipes.py +244 -148
- meerschaum/api/routes/_plugins.py +58 -47
- meerschaum/api/routes/_users.py +39 -31
- meerschaum/api/routes/_version.py +8 -10
- meerschaum/api/routes/_webterm.py +2 -2
- meerschaum/config/_default.py +10 -0
- meerschaum/config/_version.py +1 -1
- meerschaum/config/static/__init__.py +5 -2
- meerschaum/connectors/api/_APIConnector.py +4 -3
- meerschaum/connectors/api/_login.py +21 -17
- meerschaum/connectors/api/_pipes.py +1 -0
- meerschaum/connectors/api/_request.py +9 -10
- meerschaum/connectors/sql/_cli.py +11 -3
- meerschaum/connectors/sql/_instance.py +1 -1
- meerschaum/connectors/sql/_pipes.py +77 -57
- meerschaum/connectors/sql/_sql.py +26 -9
- meerschaum/core/Pipe/__init__.py +2 -0
- meerschaum/core/Pipe/_attributes.py +13 -2
- meerschaum/core/Pipe/_data.py +85 -0
- meerschaum/core/Pipe/_deduplicate.py +6 -8
- meerschaum/core/Pipe/_sync.py +63 -30
- meerschaum/core/Pipe/_verify.py +242 -77
- meerschaum/core/User/__init__.py +2 -6
- meerschaum/jobs/_Job.py +1 -1
- meerschaum/jobs/__init__.py +15 -0
- meerschaum/utils/dataframe.py +2 -0
- meerschaum/utils/dtypes/sql.py +26 -0
- meerschaum/utils/formatting/_pipes.py +1 -1
- meerschaum/utils/misc.py +11 -7
- meerschaum/utils/packages/_packages.py +1 -1
- meerschaum/utils/sql.py +6 -2
- {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/METADATA +4 -4
- {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/RECORD +61 -60
- {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/LICENSE +0 -0
- {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/NOTICE +0 -0
- {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/WHEEL +0 -0
- {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/entry_points.txt +0 -0
- {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/top_level.txt +0 -0
- {meerschaum-2.7.9.dist-info → meerschaum-2.8.0.dist-info}/zip-safe +0 -0
@@ -7,41 +7,47 @@ Routes for managing plugins
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
-
|
10
|
+
import json
|
11
|
+
import shutil
|
12
|
+
import pathlib
|
13
|
+
import os
|
14
|
+
|
15
|
+
from meerschaum.utils.typing import Optional, List, SuccessTuple, Any, Dict
|
11
16
|
|
12
17
|
from meerschaum.api import (
|
13
18
|
fastapi,
|
14
19
|
app,
|
15
20
|
endpoints,
|
16
21
|
get_api_connector,
|
17
|
-
pipes,
|
18
|
-
get_pipe,
|
19
22
|
manager,
|
20
23
|
debug,
|
21
|
-
private,
|
24
|
+
private,
|
25
|
+
no_auth,
|
26
|
+
default_instance_keys,
|
22
27
|
)
|
23
|
-
import fastapi
|
24
28
|
from meerschaum.api.tables import get_tables
|
25
|
-
from fastapi import
|
29
|
+
from fastapi import File, UploadFile
|
26
30
|
from meerschaum.utils.packages import attempt_import
|
27
|
-
import meerschaum.core
|
28
31
|
from meerschaum.core import Plugin
|
29
|
-
starlette_responses = attempt_import('starlette.responses', warn=False)
|
32
|
+
starlette_responses = attempt_import('starlette.responses', warn=False, lazy=False)
|
30
33
|
FileResponse = starlette_responses.FileResponse
|
31
34
|
|
32
35
|
sqlalchemy = attempt_import('sqlalchemy', lazy=False)
|
33
36
|
plugins_endpoint = endpoints['plugins']
|
34
37
|
|
38
|
+
PLUGINS_INSTANCE_KEYS = default_instance_keys
|
39
|
+
|
40
|
+
|
35
41
|
@app.post(plugins_endpoint + '/{name}', tags=['Plugins'])
|
36
42
|
def register_plugin(
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
name: str,
|
44
|
+
version: str = None,
|
45
|
+
attributes: str = None,
|
46
|
+
archive: UploadFile = File(...),
|
47
|
+
curr_user = (
|
48
|
+
fastapi.Depends(manager) if not no_auth else None
|
49
|
+
),
|
50
|
+
) -> SuccessTuple:
|
45
51
|
"""
|
46
52
|
Register a plugin and save its archive file.
|
47
53
|
|
@@ -78,7 +84,6 @@ def register_plugin(
|
|
78
84
|
"you can toggle various registration types."
|
79
85
|
)
|
80
86
|
|
81
|
-
import json, shutil, pathlib, os
|
82
87
|
get_tables()
|
83
88
|
if attributes is None:
|
84
89
|
attributes = json.dumps({})
|
@@ -86,7 +91,7 @@ def register_plugin(
|
|
86
91
|
if isinstance(attributes, str) and attributes[0] == '{':
|
87
92
|
try:
|
88
93
|
attributes = json.loads(attributes)
|
89
|
-
except Exception
|
94
|
+
except Exception:
|
90
95
|
pass
|
91
96
|
|
92
97
|
plugin = Plugin(name, version=version, attributes=attributes)
|
@@ -97,13 +102,15 @@ def register_plugin(
|
|
97
102
|
)
|
98
103
|
|
99
104
|
if curr_user is not None:
|
100
|
-
plugin_user_id = get_api_connector().get_plugin_user_id(plugin)
|
101
|
-
curr_user_id = get_api_connector().get_user_id(curr_user) if curr_user is not None else -1
|
105
|
+
plugin_user_id = get_api_connector(PLUGINS_INSTANCE_KEYS).get_plugin_user_id(plugin)
|
106
|
+
curr_user_id = get_api_connector(PLUGINS_INSTANCE_KEYS).get_user_id(curr_user) if curr_user is not None else -1
|
102
107
|
if plugin_user_id is not None and plugin_user_id != curr_user_id:
|
103
108
|
return False, f"User '{curr_user.username}' cannot edit plugin '{plugin}'."
|
104
109
|
plugin.user_id = curr_user_id
|
105
110
|
|
106
|
-
success, msg = get_api_connector(
|
111
|
+
success, msg = get_api_connector(
|
112
|
+
PLUGINS_INSTANCE_KEYS
|
113
|
+
).register_plugin(plugin, make_archive=False, debug=debug)
|
107
114
|
|
108
115
|
if success:
|
109
116
|
archive_path = plugin.archive_path
|
@@ -117,11 +124,11 @@ def register_plugin(
|
|
117
124
|
|
118
125
|
@app.get(plugins_endpoint + '/{name}', tags=['Plugins'])
|
119
126
|
def get_plugin(
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
127
|
+
name: str,
|
128
|
+
curr_user = (
|
129
|
+
fastapi.Depends(manager) if private else None
|
130
|
+
),
|
131
|
+
) -> Any:
|
125
132
|
"""
|
126
133
|
Download a plugin's archive file.
|
127
134
|
"""
|
@@ -133,24 +140,25 @@ def get_plugin(
|
|
133
140
|
|
134
141
|
@app.get(plugins_endpoint + '/{name}/attributes', tags=['Plugins'])
|
135
142
|
def get_plugin_attributes(
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
143
|
+
name: str,
|
144
|
+
curr_user = (
|
145
|
+
fastapi.Depends(manager) if private else None
|
146
|
+
),
|
147
|
+
) -> Dict[str, Any]:
|
141
148
|
"""
|
142
149
|
Get a plugin's attributes.
|
143
150
|
"""
|
144
|
-
return get_api_connector().get_plugin_attributes(Plugin(name))
|
151
|
+
return get_api_connector(PLUGINS_INSTANCE_KEYS).get_plugin_attributes(Plugin(name))
|
152
|
+
|
145
153
|
|
146
154
|
@app.get(plugins_endpoint, tags=['Plugins'])
|
147
155
|
def get_plugins(
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
156
|
+
user_id: Optional[int] = None,
|
157
|
+
search_term: Optional[str] = None,
|
158
|
+
curr_user = (
|
159
|
+
fastapi.Depends(manager) if private else None
|
160
|
+
),
|
161
|
+
) -> List[str]:
|
154
162
|
"""
|
155
163
|
Get a list of plugins.
|
156
164
|
|
@@ -166,26 +174,29 @@ def get_plugins(
|
|
166
174
|
-------
|
167
175
|
A list of strings.
|
168
176
|
"""
|
169
|
-
return get_api_connector(
|
177
|
+
return get_api_connector(
|
178
|
+
PLUGINS_INSTANCE_KEYS
|
179
|
+
).get_plugins(user_id=user_id, search_term=search_term)
|
180
|
+
|
170
181
|
|
171
182
|
@app.delete(plugins_endpoint + '/{name}', tags=['Plugins'])
|
172
183
|
def delete_plugin(
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
184
|
+
name: str,
|
185
|
+
curr_user = (
|
186
|
+
fastapi.Depends(manager) if private else None
|
187
|
+
),
|
188
|
+
) -> SuccessTuple:
|
178
189
|
"""
|
179
190
|
Delete a plugin and its archive file from the repository.
|
180
191
|
"""
|
181
192
|
get_tables()
|
182
193
|
plugin = Plugin(name)
|
183
|
-
plugin_user_id = get_api_connector().get_plugin_user_id(plugin)
|
194
|
+
plugin_user_id = get_api_connector(PLUGINS_INSTANCE_KEYS).get_plugin_user_id(plugin)
|
184
195
|
if plugin_user_id is None:
|
185
196
|
return False, f"Plugin '{plugin}' is not registered."
|
186
197
|
|
187
198
|
if curr_user is not None:
|
188
|
-
curr_user_id = get_api_connector().get_user_id(curr_user)
|
199
|
+
curr_user_id = get_api_connector(PLUGINS_INSTANCE_KEYS).get_user_id(curr_user)
|
189
200
|
if plugin_user_id != curr_user_id:
|
190
201
|
return False, f"User '{curr_user.username}' cannot delete plugin '{plugin}'."
|
191
202
|
else:
|
@@ -196,7 +207,7 @@ def delete_plugin(
|
|
196
207
|
if not _remove_success[0]:
|
197
208
|
return _remove_success
|
198
209
|
|
199
|
-
_delete_success = get_api_connector().delete_plugin(plugin, debug=debug)
|
210
|
+
_delete_success = get_api_connector(PLUGINS_INSTANCE_KEYS).delete_plugin(plugin, debug=debug)
|
200
211
|
if not _delete_success[0]:
|
201
212
|
return _delete_success
|
202
213
|
|
meerschaum/api/routes/_users.py
CHANGED
@@ -16,7 +16,7 @@ from meerschaum.utils.packages import attempt_import
|
|
16
16
|
from meerschaum.api import (
|
17
17
|
fastapi, app, endpoints, get_api_connector, manager,
|
18
18
|
debug, check_allow_chaining, DISALLOW_CHAINING_MESSAGE,
|
19
|
-
no_auth, private,
|
19
|
+
no_auth, private, default_instance_keys,
|
20
20
|
)
|
21
21
|
from meerschaum.utils.misc import string_to_dict
|
22
22
|
from meerschaum.config import get_config
|
@@ -28,31 +28,38 @@ users_endpoint = endpoints['users']
|
|
28
28
|
import fastapi
|
29
29
|
from fastapi import HTTPException, Form
|
30
30
|
|
31
|
+
USERS_INSTANCE_KEYS = default_instance_keys
|
32
|
+
|
31
33
|
|
32
34
|
@app.get(users_endpoint + "/me", tags=['Users'])
|
33
35
|
def read_current_user(
|
34
36
|
curr_user = (
|
35
37
|
fastapi.Depends(manager) if not no_auth else None
|
36
38
|
),
|
37
|
-
) -> Dict[str, Union[str, int]]:
|
39
|
+
) -> Dict[str, Union[str, int, None, Dict[str, Any]]]:
|
38
40
|
"""
|
39
41
|
Get information about the currently logged-in user.
|
40
42
|
"""
|
41
43
|
return {
|
42
44
|
'username': (
|
43
|
-
curr_user.username
|
45
|
+
curr_user.username
|
46
|
+
if curr_user is not None
|
47
|
+
else 'no_auth'
|
44
48
|
),
|
45
49
|
'user_id': (
|
46
|
-
get_api_connector().get_user_id(curr_user)
|
47
|
-
if curr_user is not None
|
50
|
+
get_api_connector(USERS_INSTANCE_KEYS).get_user_id(curr_user)
|
51
|
+
if curr_user is not None
|
52
|
+
else -1
|
48
53
|
),
|
49
54
|
'user_type': (
|
50
|
-
get_api_connector().get_user_type(curr_user)
|
51
|
-
if curr_user is not None
|
55
|
+
get_api_connector(USERS_INSTANCE_KEYS).get_user_type(curr_user)
|
56
|
+
if curr_user is not None
|
57
|
+
else 'admin'
|
52
58
|
),
|
53
59
|
'attributes': (
|
54
|
-
get_api_connector().get_user_attributes(curr_user)
|
55
|
-
if curr_user is not None
|
60
|
+
get_api_connector(USERS_INSTANCE_KEYS).get_user_attributes(curr_user)
|
61
|
+
if curr_user is not None
|
62
|
+
else {}
|
56
63
|
),
|
57
64
|
}
|
58
65
|
|
@@ -66,7 +73,7 @@ def get_users(
|
|
66
73
|
"""
|
67
74
|
Get a list of the registered users.
|
68
75
|
"""
|
69
|
-
return get_api_connector().get_users(debug=debug)
|
76
|
+
return get_api_connector(USERS_INSTANCE_KEYS).get_users(debug=debug)
|
70
77
|
|
71
78
|
|
72
79
|
@app.post(users_endpoint + "/register", tags=['Users'])
|
@@ -90,25 +97,25 @@ def register_user(
|
|
90
97
|
try:
|
91
98
|
attributes = string_to_dict(attributes)
|
92
99
|
except Exception:
|
93
|
-
return False,
|
100
|
+
return False, "Invalid dictionary string received for attributes."
|
94
101
|
|
95
102
|
allow_users = get_config('system', 'api', 'permissions', 'registration', 'users')
|
96
103
|
if not allow_users:
|
97
104
|
return False, (
|
98
105
|
"The administrator for this server has not allowed user registration.\n\n"
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
106
|
+
"Please contact the system administrator, or if you are running this server, "
|
107
|
+
"open the configuration file with `edit config system` and search for 'permissions'. "
|
108
|
+
" Under the keys api:permissions:registration, "
|
109
|
+
"you can toggle various registration types."
|
103
110
|
)
|
104
111
|
if type == 'admin':
|
105
112
|
return False, (
|
106
|
-
"New users cannot be of type 'admin' when using the API connector. "
|
107
|
-
"Register a normal user first, then edit the user from an authorized account, "
|
113
|
+
"New users cannot be of type 'admin' when using the API connector. "
|
114
|
+
"Register a normal user first, then edit the user from an authorized account, "
|
108
115
|
"or use a SQL connector instead."
|
109
116
|
)
|
110
117
|
user = User(username, password, type=type, email=email, attributes=attributes)
|
111
|
-
return get_api_connector().register_user(user, debug=debug)
|
118
|
+
return get_api_connector(USERS_INSTANCE_KEYS).register_user(user, debug=debug)
|
112
119
|
|
113
120
|
|
114
121
|
@app.post(users_endpoint + "/edit", tags=['Users'])
|
@@ -129,21 +136,21 @@ def edit_user(
|
|
129
136
|
try:
|
130
137
|
attributes = string_to_dict(attributes)
|
131
138
|
except Exception:
|
132
|
-
return False,
|
139
|
+
return False, "Invalid dictionary string received for attributes."
|
133
140
|
|
134
141
|
user = User(username, password, email=email, attributes=attributes)
|
135
|
-
user_type = get_api_connector().get_user_type(curr_user) if curr_user is not None else 'admin'
|
142
|
+
user_type = get_api_connector(USERS_INSTANCE_KEYS).get_user_type(curr_user) if curr_user is not None else 'admin'
|
136
143
|
if user_type == 'admin' and type is not None:
|
137
144
|
user.type = type
|
138
145
|
if user_type == 'admin' or curr_user.username == user.username:
|
139
|
-
return get_api_connector().edit_user(user, debug=debug)
|
146
|
+
return get_api_connector(USERS_INSTANCE_KEYS).edit_user(user, debug=debug)
|
140
147
|
|
141
148
|
return False, f"Cannot edit user '{user}': Permission denied"
|
142
149
|
|
143
150
|
|
144
151
|
@app.get(users_endpoint + "/{username}/id", tags=['Users'])
|
145
152
|
def get_user_id(
|
146
|
-
username
|
153
|
+
username: str,
|
147
154
|
curr_user = (
|
148
155
|
fastapi.Depends(manager) if not no_auth else None
|
149
156
|
),
|
@@ -151,12 +158,12 @@ def get_user_id(
|
|
151
158
|
"""
|
152
159
|
Get a user's ID.
|
153
160
|
"""
|
154
|
-
return get_api_connector().get_user_id(User(username), debug=debug)
|
161
|
+
return get_api_connector(USERS_INSTANCE_KEYS).get_user_id(User(username), debug=debug)
|
155
162
|
|
156
163
|
|
157
164
|
@app.get(users_endpoint + "/{username}/attributes", tags=['Users'])
|
158
165
|
def get_user_attributes(
|
159
|
-
username
|
166
|
+
username: str,
|
160
167
|
curr_user = (
|
161
168
|
fastapi.Depends(manager) if private else None
|
162
169
|
),
|
@@ -164,7 +171,7 @@ def get_user_attributes(
|
|
164
171
|
"""
|
165
172
|
Get a user's attributes.
|
166
173
|
"""
|
167
|
-
return get_api_connector().get_user_attributes(User(username), debug=debug)
|
174
|
+
return get_api_connector(USERS_INSTANCE_KEYS).get_user_attributes(User(username), debug=debug)
|
168
175
|
|
169
176
|
|
170
177
|
@app.delete(users_endpoint + "/{username}", tags=['Users'])
|
@@ -179,11 +186,12 @@ def delete_user(
|
|
179
186
|
"""
|
180
187
|
user = User(username)
|
181
188
|
user_type = (
|
182
|
-
get_api_connector().get_user_type(curr_user, debug=debug)
|
183
|
-
if curr_user is not None
|
189
|
+
get_api_connector(USERS_INSTANCE_KEYS).get_user_type(curr_user, debug=debug)
|
190
|
+
if curr_user is not None
|
191
|
+
else 'admin'
|
184
192
|
)
|
185
193
|
if user_type == 'admin' or curr_user.username == user.username:
|
186
|
-
return get_api_connector().delete_user(user, debug=debug)
|
194
|
+
return get_api_connector(USERS_INSTANCE_KEYS).delete_user(user, debug=debug)
|
187
195
|
|
188
196
|
return False, f"Cannot delete user '{user}': Permission denied"
|
189
197
|
|
@@ -203,12 +211,12 @@ def get_user_password_hash(
|
|
203
211
|
"""
|
204
212
|
if not check_allow_chaining():
|
205
213
|
raise HTTPException(status_code=403, detail=DISALLOW_CHAINING_MESSAGE)
|
206
|
-
return get_api_connector().get_user_password_hash(User(username), debug=debug)
|
214
|
+
return get_api_connector(USERS_INSTANCE_KEYS).get_user_password_hash(User(username), debug=debug)
|
207
215
|
|
208
216
|
|
209
217
|
@app.get(users_endpoint + '/{username}/type', tags=['Users'])
|
210
218
|
def get_user_type(
|
211
|
-
username
|
219
|
+
username: str,
|
212
220
|
curr_user = (
|
213
221
|
fastapi.Depends(manager) if not no_auth else None
|
214
222
|
),
|
@@ -218,4 +226,4 @@ def get_user_type(
|
|
218
226
|
"""
|
219
227
|
if not check_allow_chaining():
|
220
228
|
raise HTTPException(status_code=403, detail=DISALLOW_CHAINING_MESSAGE)
|
221
|
-
return get_api_connector().get_user_type(User(username))
|
229
|
+
return get_api_connector(USERS_INSTANCE_KEYS).get_user_type(User(username))
|
@@ -8,14 +8,13 @@ Return version information
|
|
8
8
|
|
9
9
|
import fastapi
|
10
10
|
from meerschaum.api import app, endpoints, private, manager
|
11
|
-
from meerschaum.utils.typing import Union
|
12
11
|
|
13
12
|
@app.get(endpoints['version'], tags=['Version'])
|
14
13
|
def get_api_version(
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
curr_user = (
|
15
|
+
fastapi.Depends(manager) if private else None
|
16
|
+
),
|
17
|
+
):
|
19
18
|
"""
|
20
19
|
Get the Meerschaum API version.
|
21
20
|
"""
|
@@ -24,13 +23,12 @@ def get_api_version(
|
|
24
23
|
|
25
24
|
@app.get(endpoints['version'] + "/mrsm", tags=['Version'])
|
26
25
|
def get_meerschaum_version(
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
26
|
+
curr_user = (
|
27
|
+
fastapi.Depends(manager) if private else None
|
28
|
+
),
|
29
|
+
):
|
31
30
|
"""
|
32
31
|
Get the Meerschaum instance version.
|
33
32
|
"""
|
34
33
|
from meerschaum import __version__ as version
|
35
34
|
return version
|
36
|
-
|
@@ -76,7 +76,7 @@ async def get_webterm(
|
|
76
76
|
text = response.text
|
77
77
|
if request.url.scheme == 'https':
|
78
78
|
text = text.replace('ws://', 'wss://')
|
79
|
-
text = text.replace(f'
|
79
|
+
text = text.replace(f'websocket/{username}', f'websocket/{session_id}')
|
80
80
|
return HTMLResponse(
|
81
81
|
content=text,
|
82
82
|
status_code=response.status_code,
|
@@ -100,7 +100,7 @@ async def webterm_websocket(websocket: WebSocket, session_id: str):
|
|
100
100
|
|
101
101
|
username = get_username_from_session(session_id)
|
102
102
|
|
103
|
-
ws_url = f"ws://localhost:8765/
|
103
|
+
ws_url = f"ws://localhost:8765/websocket/{username or session_id}"
|
104
104
|
async with websockets.connect(ws_url) as ws:
|
105
105
|
async def forward_messages():
|
106
106
|
try:
|
meerschaum/config/_default.py
CHANGED
@@ -111,6 +111,12 @@ default_system_config = {
|
|
111
111
|
'connector': 'valkey:main',
|
112
112
|
'session_expires_minutes': 43200,
|
113
113
|
},
|
114
|
+
'data': {
|
115
|
+
'max_response_row_limit': 100_000,
|
116
|
+
},
|
117
|
+
'endpoints': {
|
118
|
+
'docs_in_production': True,
|
119
|
+
},
|
114
120
|
'permissions': {
|
115
121
|
'registration': {
|
116
122
|
'users': True,
|
@@ -124,6 +130,10 @@ default_system_config = {
|
|
124
130
|
'insecure_parent_instance': False,
|
125
131
|
'child_apis': False,
|
126
132
|
},
|
133
|
+
'instances': {
|
134
|
+
'allow_multiple_instances': True,
|
135
|
+
'allowed_instance_keys': ['*']
|
136
|
+
},
|
127
137
|
},
|
128
138
|
'protocol': default_meerschaum_config['connectors']['api']['default']['protocol'],
|
129
139
|
},
|
meerschaum/config/_version.py
CHANGED
@@ -7,7 +7,6 @@ Insert non-user-editable configuration files here.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
import os
|
10
|
-
import uuid
|
11
10
|
from typing import Dict, Any
|
12
11
|
from meerschaum.utils.misc import generate_password
|
13
12
|
|
@@ -33,9 +32,12 @@ STATIC_CONFIG: Dict[str, Any] = {
|
|
33
32
|
'websocket': '/ws',
|
34
33
|
'dash': '/dash',
|
35
34
|
'webterm': r'/webterm/{session_id}',
|
36
|
-
'webterm_websocket': r'/
|
35
|
+
'webterm_websocket': r'/websocket/{session_id}',
|
37
36
|
'info': '/info',
|
38
37
|
'healthcheck': '/healthcheck',
|
38
|
+
'docs': '/docs',
|
39
|
+
'redoc': '/redoc',
|
40
|
+
'openapi': '/openapi.json',
|
39
41
|
},
|
40
42
|
'oauth': {
|
41
43
|
'token_expires_minutes': 720,
|
@@ -149,6 +151,7 @@ STATIC_CONFIG: Dict[str, Any] = {
|
|
149
151
|
},
|
150
152
|
'exists_timeout_seconds': 5.0,
|
151
153
|
'static_schema_cache_seconds': 60.0,
|
154
|
+
'max_bound_time_days': 36525,
|
152
155
|
},
|
153
156
|
'jobs': {
|
154
157
|
'check_restart_seconds': 1.0,
|
@@ -169,12 +169,12 @@ class APIConnector(Connector):
|
|
169
169
|
@property
|
170
170
|
def session(self):
|
171
171
|
if self._session is None:
|
172
|
-
|
172
|
+
_ = attempt_import('certifi', lazy=False)
|
173
173
|
requests = attempt_import('requests', lazy=False)
|
174
174
|
if requests:
|
175
175
|
self._session = requests.Session()
|
176
176
|
if self._session is None:
|
177
|
-
error(
|
177
|
+
error("Failed to import requests. Is requests installed?")
|
178
178
|
return self._session
|
179
179
|
|
180
180
|
@property
|
@@ -191,6 +191,7 @@ class APIConnector(Connector):
|
|
191
191
|
|
192
192
|
if self._token is None or expired:
|
193
193
|
success, msg = self.login()
|
194
|
-
if not success:
|
194
|
+
if not success and not self.__dict__.get('_emitted_warning'):
|
195
195
|
warn(msg, stack=False)
|
196
|
+
self._emitted_warning = True
|
196
197
|
return self._token
|
@@ -7,19 +7,21 @@ Log into the API instance or refresh the token.
|
|
7
7
|
"""
|
8
8
|
|
9
9
|
from __future__ import annotations
|
10
|
+
|
11
|
+
import json
|
12
|
+
import datetime
|
10
13
|
from meerschaum.utils.typing import SuccessTuple, Any, Union
|
14
|
+
from meerschaum.config.static import STATIC_CONFIG
|
15
|
+
from meerschaum.utils.warnings import warn as _warn
|
16
|
+
|
11
17
|
|
12
18
|
def login(
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
19
|
+
self,
|
20
|
+
debug: bool = False,
|
21
|
+
warn: bool = True,
|
22
|
+
**kw: Any
|
23
|
+
) -> SuccessTuple:
|
18
24
|
"""Log in and set the session token."""
|
19
|
-
from meerschaum.utils.warnings import warn as _warn, info, error
|
20
|
-
from meerschaum.core import User
|
21
|
-
from meerschaum.config.static import STATIC_CONFIG
|
22
|
-
import json, datetime
|
23
25
|
try:
|
24
26
|
login_data = {
|
25
27
|
'username': self.username,
|
@@ -27,11 +29,12 @@ def login(
|
|
27
29
|
}
|
28
30
|
except AttributeError:
|
29
31
|
return False, f"Please login with the command `login {self}`."
|
32
|
+
|
30
33
|
response = self.post(
|
31
34
|
STATIC_CONFIG['api']['endpoints']['login'],
|
32
|
-
data
|
33
|
-
use_token
|
34
|
-
debug
|
35
|
+
data=login_data,
|
36
|
+
use_token=False,
|
37
|
+
debug=debug,
|
35
38
|
)
|
36
39
|
if response:
|
37
40
|
msg = f"Successfully logged into '{self}' as user '{login_data['username']}'."
|
@@ -45,16 +48,17 @@ def login(
|
|
45
48
|
f"Failed to log into '{self}' as user '{login_data['username']}'.\n" +
|
46
49
|
f" Please verify login details for connector '{self}'."
|
47
50
|
)
|
48
|
-
if warn:
|
51
|
+
if warn and not self.__dict__.get('_emitted_warning', False):
|
49
52
|
_warn(msg, stack=False)
|
53
|
+
self._emitted_warning = True
|
50
54
|
|
51
55
|
return response.__bool__(), msg
|
52
56
|
|
53
57
|
|
54
58
|
def test_connection(
|
55
|
-
|
56
|
-
|
57
|
-
|
59
|
+
self,
|
60
|
+
**kw: Any
|
61
|
+
) -> Union[bool, None]:
|
58
62
|
"""Test if a successful connection to the API may be made."""
|
59
63
|
from meerschaum.connectors.poll import retry_connect
|
60
64
|
_default_kw = {
|
@@ -65,5 +69,5 @@ def test_connection(
|
|
65
69
|
_default_kw.update(kw)
|
66
70
|
try:
|
67
71
|
return retry_connect(**_default_kw)
|
68
|
-
except Exception
|
72
|
+
except Exception:
|
69
73
|
return False
|
@@ -248,16 +248,15 @@ def delete(self, r_url: str, **kwargs: Any) -> 'requests.Response':
|
|
248
248
|
|
249
249
|
|
250
250
|
def wget(
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
"""Mimic wget with requests.
|
260
|
-
"""
|
251
|
+
self,
|
252
|
+
r_url: str,
|
253
|
+
dest: Optional[Union[str, pathlib.Path]] = None,
|
254
|
+
headers: Optional[Dict[str, Any]] = None,
|
255
|
+
use_token: bool = True,
|
256
|
+
debug: bool = False,
|
257
|
+
**kw: Any
|
258
|
+
) -> pathlib.Path:
|
259
|
+
"""Mimic wget with requests."""
|
261
260
|
from meerschaum.utils.misc import wget
|
262
261
|
if headers is None:
|
263
262
|
headers = {}
|
@@ -109,13 +109,21 @@ def _cli_exit(
|
|
109
109
|
### and because `main.cli()` is not defined.
|
110
110
|
launch_cli = f"cli_main.cli(['{cli_arg_str}'])"
|
111
111
|
if self.flavor == 'mssql':
|
112
|
+
attrs = self.parse_uri(self.URI)
|
113
|
+
host = attrs.get('host', None)
|
114
|
+
port = attrs.get('port', None)
|
115
|
+
database = attrs.get('database', None)
|
116
|
+
username = attrs.get('username', None)
|
117
|
+
password = attrs.get('password', None)
|
118
|
+
if not host or not port or not database:
|
119
|
+
raise ValueError(f"Cannot determine attributes for '{self}'.")
|
112
120
|
launch_cli = (
|
113
121
|
"mssqlclioptionsparser, mssql_cli = attempt_import("
|
114
122
|
+ "'mssqlcli.mssqlclioptionsparser', 'mssqlcli.mssql_cli', lazy=False)\n"
|
115
123
|
+ "ms_parser = mssqlclioptionsparser.create_parser()\n"
|
116
|
-
+ f"ms_options = ms_parser.parse_args(['--server', 'tcp:{
|
117
|
-
+ f"'--database', '{
|
118
|
-
+ f"'--username', '{
|
124
|
+
+ f"ms_options = ms_parser.parse_args(['--server', 'tcp:{host},{port}', "
|
125
|
+
+ f"'--database', '{database}', "
|
126
|
+
+ f"'--username', '{username}', '--password', '{password}'])\n"
|
119
127
|
+ "ms_object = mssql_cli.MssqlCli(ms_options)\n"
|
120
128
|
+ "try:\n"
|
121
129
|
+ " ms_object.connect_to_database()\n"
|
@@ -186,7 +186,7 @@ def _drop_old_temporary_tables(
|
|
186
186
|
]
|
187
187
|
if docs:
|
188
188
|
queries = [sqlalchemy.insert(temp_tables_table).values(**doc) for doc in docs]
|
189
|
-
|
189
|
+
_ = [self.exec(query, silent=True, debug=debug) for query in queries]
|
190
190
|
_in_memory_temp_tables.update(
|
191
191
|
{
|
192
192
|
table: True
|