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.
Files changed (200) 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 +33 -4
  5. meerschaum/_internal/cli/__init__.py +6 -0
  6. meerschaum/_internal/cli/daemons.py +103 -0
  7. meerschaum/_internal/cli/entry.py +220 -0
  8. meerschaum/_internal/cli/workers.py +435 -0
  9. meerschaum/_internal/docs/index.py +48 -2
  10. meerschaum/_internal/entry.py +50 -14
  11. meerschaum/_internal/shell/Shell.py +121 -29
  12. meerschaum/_internal/shell/__init__.py +4 -1
  13. meerschaum/_internal/static.py +359 -0
  14. meerschaum/_internal/term/TermPageHandler.py +1 -2
  15. meerschaum/_internal/term/__init__.py +40 -6
  16. meerschaum/_internal/term/tools.py +33 -8
  17. meerschaum/actions/__init__.py +6 -4
  18. meerschaum/actions/api.py +53 -13
  19. meerschaum/actions/attach.py +1 -0
  20. meerschaum/actions/bootstrap.py +8 -8
  21. meerschaum/actions/delete.py +4 -2
  22. meerschaum/actions/edit.py +171 -25
  23. meerschaum/actions/login.py +8 -8
  24. meerschaum/actions/register.py +143 -6
  25. meerschaum/actions/reload.py +22 -5
  26. meerschaum/actions/restart.py +14 -0
  27. meerschaum/actions/show.py +184 -31
  28. meerschaum/actions/start.py +166 -17
  29. meerschaum/actions/stop.py +38 -2
  30. meerschaum/actions/sync.py +7 -2
  31. meerschaum/actions/tag.py +9 -8
  32. meerschaum/actions/verify.py +5 -8
  33. meerschaum/api/__init__.py +45 -15
  34. meerschaum/api/_events.py +46 -4
  35. meerschaum/api/_oauth2.py +162 -9
  36. meerschaum/api/_tokens.py +102 -0
  37. meerschaum/api/dash/__init__.py +0 -3
  38. meerschaum/api/dash/callbacks/__init__.py +1 -0
  39. meerschaum/api/dash/callbacks/custom.py +4 -3
  40. meerschaum/api/dash/callbacks/dashboard.py +198 -118
  41. meerschaum/api/dash/callbacks/jobs.py +14 -7
  42. meerschaum/api/dash/callbacks/login.py +10 -1
  43. meerschaum/api/dash/callbacks/pipes.py +194 -14
  44. meerschaum/api/dash/callbacks/plugins.py +0 -1
  45. meerschaum/api/dash/callbacks/register.py +10 -3
  46. meerschaum/api/dash/callbacks/settings/password_reset.py +2 -2
  47. meerschaum/api/dash/callbacks/tokens.py +389 -0
  48. meerschaum/api/dash/components.py +36 -15
  49. meerschaum/api/dash/jobs.py +1 -1
  50. meerschaum/api/dash/keys.py +35 -93
  51. meerschaum/api/dash/pages/__init__.py +2 -1
  52. meerschaum/api/dash/pages/dashboard.py +1 -20
  53. meerschaum/api/dash/pages/{job.py → jobs.py} +10 -7
  54. meerschaum/api/dash/pages/login.py +2 -2
  55. meerschaum/api/dash/pages/pipes.py +16 -5
  56. meerschaum/api/dash/pages/settings/password_reset.py +1 -1
  57. meerschaum/api/dash/pages/tokens.py +53 -0
  58. meerschaum/api/dash/pipes.py +382 -95
  59. meerschaum/api/dash/sessions.py +12 -0
  60. meerschaum/api/dash/tokens.py +603 -0
  61. meerschaum/api/dash/websockets.py +1 -1
  62. meerschaum/api/dash/webterm.py +18 -6
  63. meerschaum/api/models/__init__.py +23 -3
  64. meerschaum/api/models/_actions.py +22 -0
  65. meerschaum/api/models/_pipes.py +91 -7
  66. meerschaum/api/models/_tokens.py +81 -0
  67. meerschaum/api/resources/static/js/terminado.js +3 -0
  68. meerschaum/api/resources/static/js/xterm-addon-unicode11.js +2 -0
  69. meerschaum/api/resources/templates/termpage.html +13 -0
  70. meerschaum/api/routes/__init__.py +1 -0
  71. meerschaum/api/routes/_actions.py +3 -4
  72. meerschaum/api/routes/_connectors.py +3 -7
  73. meerschaum/api/routes/_jobs.py +26 -35
  74. meerschaum/api/routes/_login.py +120 -15
  75. meerschaum/api/routes/_misc.py +5 -10
  76. meerschaum/api/routes/_pipes.py +178 -143
  77. meerschaum/api/routes/_plugins.py +38 -28
  78. meerschaum/api/routes/_tokens.py +236 -0
  79. meerschaum/api/routes/_users.py +47 -35
  80. meerschaum/api/routes/_version.py +3 -3
  81. meerschaum/api/routes/_webterm.py +3 -3
  82. meerschaum/config/__init__.py +100 -30
  83. meerschaum/config/_default.py +132 -64
  84. meerschaum/config/_edit.py +38 -32
  85. meerschaum/config/_formatting.py +2 -0
  86. meerschaum/config/_patch.py +10 -8
  87. meerschaum/config/_paths.py +133 -13
  88. meerschaum/config/_read_config.py +87 -36
  89. meerschaum/config/_sync.py +6 -3
  90. meerschaum/config/_version.py +1 -1
  91. meerschaum/config/environment.py +262 -0
  92. meerschaum/config/stack/__init__.py +37 -15
  93. meerschaum/config/static.py +18 -0
  94. meerschaum/connectors/_Connector.py +11 -6
  95. meerschaum/connectors/__init__.py +41 -22
  96. meerschaum/connectors/api/_APIConnector.py +34 -6
  97. meerschaum/connectors/api/_actions.py +2 -2
  98. meerschaum/connectors/api/_jobs.py +12 -1
  99. meerschaum/connectors/api/_login.py +33 -7
  100. meerschaum/connectors/api/_misc.py +2 -2
  101. meerschaum/connectors/api/_pipes.py +23 -32
  102. meerschaum/connectors/api/_plugins.py +2 -2
  103. meerschaum/connectors/api/_request.py +1 -1
  104. meerschaum/connectors/api/_tokens.py +146 -0
  105. meerschaum/connectors/api/_users.py +70 -58
  106. meerschaum/connectors/instance/_InstanceConnector.py +83 -0
  107. meerschaum/connectors/instance/__init__.py +10 -0
  108. meerschaum/connectors/instance/_pipes.py +442 -0
  109. meerschaum/connectors/instance/_plugins.py +159 -0
  110. meerschaum/connectors/instance/_tokens.py +317 -0
  111. meerschaum/connectors/instance/_users.py +188 -0
  112. meerschaum/connectors/parse.py +5 -2
  113. meerschaum/connectors/sql/_SQLConnector.py +22 -5
  114. meerschaum/connectors/sql/_cli.py +12 -11
  115. meerschaum/connectors/sql/_create_engine.py +12 -168
  116. meerschaum/connectors/sql/_fetch.py +2 -18
  117. meerschaum/connectors/sql/_pipes.py +295 -278
  118. meerschaum/connectors/sql/_plugins.py +29 -0
  119. meerschaum/connectors/sql/_sql.py +46 -21
  120. meerschaum/connectors/sql/_users.py +36 -2
  121. meerschaum/connectors/sql/tables/__init__.py +254 -122
  122. meerschaum/connectors/valkey/_ValkeyConnector.py +5 -7
  123. meerschaum/connectors/valkey/_pipes.py +60 -31
  124. meerschaum/connectors/valkey/_plugins.py +2 -26
  125. meerschaum/core/Pipe/__init__.py +115 -85
  126. meerschaum/core/Pipe/_attributes.py +425 -124
  127. meerschaum/core/Pipe/_bootstrap.py +54 -24
  128. meerschaum/core/Pipe/_cache.py +555 -0
  129. meerschaum/core/Pipe/_clear.py +0 -11
  130. meerschaum/core/Pipe/_data.py +96 -68
  131. meerschaum/core/Pipe/_deduplicate.py +0 -13
  132. meerschaum/core/Pipe/_delete.py +12 -21
  133. meerschaum/core/Pipe/_drop.py +11 -23
  134. meerschaum/core/Pipe/_dtypes.py +49 -19
  135. meerschaum/core/Pipe/_edit.py +14 -4
  136. meerschaum/core/Pipe/_fetch.py +1 -1
  137. meerschaum/core/Pipe/_index.py +8 -14
  138. meerschaum/core/Pipe/_show.py +5 -5
  139. meerschaum/core/Pipe/_sync.py +123 -204
  140. meerschaum/core/Pipe/_verify.py +4 -4
  141. meerschaum/{plugins → core/Plugin}/_Plugin.py +16 -12
  142. meerschaum/core/Plugin/__init__.py +1 -1
  143. meerschaum/core/Token/_Token.py +220 -0
  144. meerschaum/core/Token/__init__.py +12 -0
  145. meerschaum/core/User/_User.py +35 -10
  146. meerschaum/core/User/__init__.py +9 -1
  147. meerschaum/core/__init__.py +1 -0
  148. meerschaum/jobs/_Executor.py +88 -4
  149. meerschaum/jobs/_Job.py +149 -38
  150. meerschaum/jobs/__init__.py +3 -2
  151. meerschaum/jobs/systemd.py +8 -3
  152. meerschaum/models/__init__.py +35 -0
  153. meerschaum/models/pipes.py +247 -0
  154. meerschaum/models/tokens.py +38 -0
  155. meerschaum/models/users.py +26 -0
  156. meerschaum/plugins/__init__.py +301 -88
  157. meerschaum/plugins/bootstrap.py +510 -4
  158. meerschaum/utils/_get_pipes.py +97 -30
  159. meerschaum/utils/daemon/Daemon.py +199 -43
  160. meerschaum/utils/daemon/FileDescriptorInterceptor.py +0 -1
  161. meerschaum/utils/daemon/RotatingFile.py +63 -36
  162. meerschaum/utils/daemon/StdinFile.py +53 -13
  163. meerschaum/utils/daemon/__init__.py +47 -6
  164. meerschaum/utils/daemon/_names.py +6 -3
  165. meerschaum/utils/dataframe.py +479 -81
  166. meerschaum/utils/debug.py +49 -19
  167. meerschaum/utils/dtypes/__init__.py +476 -34
  168. meerschaum/utils/dtypes/sql.py +369 -29
  169. meerschaum/utils/formatting/__init__.py +5 -2
  170. meerschaum/utils/formatting/_jobs.py +1 -1
  171. meerschaum/utils/formatting/_pipes.py +52 -50
  172. meerschaum/utils/formatting/_pprint.py +1 -0
  173. meerschaum/utils/formatting/_shell.py +44 -18
  174. meerschaum/utils/misc.py +268 -186
  175. meerschaum/utils/packages/__init__.py +25 -40
  176. meerschaum/utils/packages/_packages.py +42 -34
  177. meerschaum/utils/pipes.py +213 -0
  178. meerschaum/utils/process.py +2 -2
  179. meerschaum/utils/prompt.py +175 -144
  180. meerschaum/utils/schedule.py +2 -1
  181. meerschaum/utils/sql.py +134 -47
  182. meerschaum/utils/threading.py +42 -0
  183. meerschaum/utils/typing.py +1 -4
  184. meerschaum/utils/venv/_Venv.py +2 -2
  185. meerschaum/utils/venv/__init__.py +7 -7
  186. meerschaum/utils/warnings.py +19 -13
  187. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/METADATA +94 -96
  188. meerschaum-3.0.0.dist-info/RECORD +289 -0
  189. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/WHEEL +1 -1
  190. meerschaum-3.0.0.dist-info/licenses/NOTICE +2 -0
  191. meerschaum/api/models/_interfaces.py +0 -15
  192. meerschaum/api/models/_locations.py +0 -15
  193. meerschaum/api/models/_metrics.py +0 -15
  194. meerschaum/config/_environment.py +0 -145
  195. meerschaum/config/static/__init__.py +0 -186
  196. meerschaum-2.9.5.dist-info/RECORD +0 -263
  197. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/entry_points.txt +0 -0
  198. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/licenses/LICENSE +0 -0
  199. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/top_level.txt +0 -0
  200. {meerschaum-2.9.5.dist-info → meerschaum-3.0.0.dist-info}/zip-safe +0 -0
@@ -14,7 +14,7 @@ from functools import partial
14
14
 
15
15
  import meerschaum as mrsm
16
16
  from meerschaum.utils.typing import SuccessTuple, List, Callable, Optional
17
- from meerschaum.config.static import STATIC_CONFIG
17
+ from meerschaum._internal.static import STATIC_CONFIG
18
18
 
19
19
  ACTIONS_ENDPOINT: str = STATIC_CONFIG['api']['endpoints']['actions']
20
20
  TEMP_PREFIX: str = STATIC_CONFIG['api']['jobs']['temp_prefix']
@@ -88,7 +88,7 @@ def do_action_legacy(
88
88
  """
89
89
  import sys, json
90
90
  from meerschaum.utils.debug import dprint
91
- from meerschaum.config.static import STATIC_CONFIG
91
+ from meerschaum._internal.static import STATIC_CONFIG
92
92
  from meerschaum.utils.misc import json_serialize_datetime
93
93
  if action is None:
94
94
  action = []
@@ -14,7 +14,7 @@ from datetime import datetime
14
14
  import meerschaum as mrsm
15
15
  from meerschaum.utils.typing import Dict, Any, SuccessTuple, List, Union, Callable, Optional
16
16
  from meerschaum.jobs import Job
17
- from meerschaum.config.static import STATIC_CONFIG
17
+ from meerschaum._internal.static import STATIC_CONFIG
18
18
  from meerschaum.utils.warnings import warn, dprint
19
19
 
20
20
  JOBS_ENDPOINT: str = STATIC_CONFIG['api']['endpoints']['jobs']
@@ -380,3 +380,14 @@ def get_job_is_blocking_on_stdin(self, name: str, debug: bool = False) -> bool:
380
380
  return False
381
381
 
382
382
  return response.json()
383
+
384
+
385
+ def get_job_prompt_kwargs(self, name: str, debug: bool = False) -> Dict[str, Any]:
386
+ """
387
+ Return the kwargs to the blocking `prompt()`, if available.
388
+ """
389
+ response = self.get(JOBS_ENDPOINT + f'/{name}/prompt_kwargs', debug=debug)
390
+ if not response:
391
+ return {}
392
+
393
+ return response.json()
@@ -11,7 +11,7 @@ from __future__ import annotations
11
11
  import json
12
12
  import datetime
13
13
  from meerschaum.utils.typing import SuccessTuple, Any, Union
14
- from meerschaum.config.static import STATIC_CONFIG
14
+ from meerschaum._internal.static import STATIC_CONFIG
15
15
  from meerschaum.utils.warnings import warn as _warn
16
16
 
17
17
 
@@ -22,14 +22,40 @@ def login(
22
22
  **kw: Any
23
23
  ) -> SuccessTuple:
24
24
  """Log in and set the session token."""
25
+ if self.login_scheme == 'api_key':
26
+ validate_response = self.post(
27
+ STATIC_CONFIG['api']['endpoints']['tokens'] + '/validate',
28
+ headers={'Authorization': f'Bearer {self.api_key}'},
29
+ use_token=False,
30
+ debug=debug,
31
+ )
32
+ if not validate_response:
33
+ return False, "API key is not valid."
34
+ return True, "API key is valid."
35
+
25
36
  try:
26
- login_data = {
27
- 'username': self.username,
28
- 'password': self.password,
29
- }
37
+ if self.login_scheme == 'password':
38
+ login_data = {
39
+ 'username': self.username,
40
+ 'password': self.password,
41
+ }
42
+ elif self.login_scheme == 'client_credentials':
43
+ login_data = {
44
+ 'client_id': self.client_id,
45
+ 'client_secret': self.client_secret,
46
+ }
30
47
  except AttributeError:
48
+ login_data = {}
49
+
50
+ if not login_data:
31
51
  return False, f"Please login with the command `login {self}`."
32
52
 
53
+ login_scheme_msg = (
54
+ f" as user '{login_data['username']}'"
55
+ if self.login_scheme == 'username'
56
+ else ''
57
+ )
58
+
33
59
  response = self.post(
34
60
  STATIC_CONFIG['api']['endpoints']['login'],
35
61
  data=login_data,
@@ -37,7 +63,7 @@ def login(
37
63
  debug=debug,
38
64
  )
39
65
  if response:
40
- msg = f"Successfully logged into '{self}' as user '{login_data['username']}'."
66
+ msg = f"Successfully logged into '{self}'{login_scheme_msg}'."
41
67
  self._token = json.loads(response.text)['access_token']
42
68
  self._expires = datetime.datetime.strptime(
43
69
  json.loads(response.text)['expires'],
@@ -45,7 +71,7 @@ def login(
45
71
  )
46
72
  else:
47
73
  msg = (
48
- f"Failed to log into '{self}' as user '{login_data['username']}'.\n" +
74
+ f"Failed to log into '{self}'{login_scheme_msg}.\n" +
49
75
  f" Please verify login details for connector '{self}'."
50
76
  )
51
77
  if warn and not self.__dict__.get('_emitted_warning', False):
@@ -13,7 +13,7 @@ def get_mrsm_version(self, **kw) -> Optional[str]:
13
13
  """
14
14
  Return the Meerschaum version of the API instance.
15
15
  """
16
- from meerschaum.config.static import STATIC_CONFIG
16
+ from meerschaum._internal.static import STATIC_CONFIG
17
17
  try:
18
18
  j = self.get(
19
19
  STATIC_CONFIG['api']['endpoints']['version'] + '/mrsm',
@@ -31,7 +31,7 @@ def get_chaining_status(self, **kw) -> Optional[bool]:
31
31
  """
32
32
  Fetch the chaining status of the API instance.
33
33
  """
34
- from meerschaum.config.static import STATIC_CONFIG
34
+ from meerschaum._internal.static import STATIC_CONFIG
35
35
  try:
36
36
  response = self.get(
37
37
  STATIC_CONFIG['api']['endpoints']['chaining'],
@@ -21,7 +21,7 @@ def pipe_r_url(
21
21
  pipe: mrsm.Pipe
22
22
  ) -> str:
23
23
  """Return a relative URL path from a Pipe's keys."""
24
- from meerschaum.config.static import STATIC_CONFIG
24
+ from meerschaum._internal.static import STATIC_CONFIG
25
25
  location_key = pipe.location_key
26
26
  if location_key is None:
27
27
  location_key = '[None]'
@@ -87,7 +87,7 @@ def edit_pipe(
87
87
  response = self.patch(
88
88
  r_url + '/edit',
89
89
  params={'patch': patch, 'instance_keys': self.get_pipe_instance_keys(pipe)},
90
- json=pipe.parameters,
90
+ json=pipe.get_parameters(apply_symlinks=False),
91
91
  debug=debug,
92
92
  )
93
93
  if debug:
@@ -112,7 +112,13 @@ def fetch_pipes_keys(
112
112
  tags: Optional[List[str]] = None,
113
113
  params: Optional[Dict[str, Any]] = None,
114
114
  debug: bool = False
115
- ) -> Union[List[Tuple[str, str, Union[str, None]]]]:
115
+ ) -> List[
116
+ Union[
117
+ Tuple[str, str, Union[str, None]],
118
+ Tuple[str, str, Union[str, None], List[str]],
119
+ Tuple[str, str, Union[str, None], Dict[str, Any]]
120
+ ]
121
+ ]:
116
122
  """
117
123
  Fetch registered Pipes' keys from the API.
118
124
 
@@ -142,7 +148,7 @@ def fetch_pipes_keys(
142
148
  -------
143
149
  A list of tuples containing pipes' keys.
144
150
  """
145
- from meerschaum.config.static import STATIC_CONFIG
151
+ from meerschaum._internal.static import STATIC_CONFIG
146
152
  if connector_keys is None:
147
153
  connector_keys = []
148
154
  if metric_keys is None:
@@ -167,6 +173,8 @@ def fetch_pipes_keys(
167
173
  debug=debug
168
174
  ).json()
169
175
  except Exception as e:
176
+ import traceback
177
+ traceback.print_exc()
170
178
  error(str(e))
171
179
 
172
180
  if 'detail' in j:
@@ -185,10 +193,11 @@ def sync_pipe(
185
193
  """Sync a DataFrame into a Pipe."""
186
194
  from decimal import Decimal
187
195
  from meerschaum.utils.debug import dprint
188
- from meerschaum.utils.misc import json_serialize_datetime, items_str, interval_str
196
+ from meerschaum.utils.dtypes import json_serialize_value
197
+ from meerschaum.utils.misc import items_str, interval_str
189
198
  from meerschaum.config import get_config
190
199
  from meerschaum.utils.packages import attempt_import
191
- from meerschaum.utils.dataframe import get_numeric_cols, to_json
200
+ from meerschaum.utils.dataframe import get_special_cols, to_json
192
201
  begin = time.perf_counter()
193
202
  more_itertools = attempt_import('more_itertools')
194
203
  if df is None:
@@ -197,8 +206,10 @@ def sync_pipe(
197
206
 
198
207
  def get_json_str(c):
199
208
  ### allow syncing dict or JSON without needing to import pandas (for IOT devices)
200
- if isinstance(c, (dict, list)):
201
- return json.dumps(c, default=json_serialize_datetime)
209
+ if isinstance(c, str):
210
+ return c
211
+ if isinstance(c, (dict, list, tuple)):
212
+ return json.dumps(c, default=json_serialize_value)
202
213
  return to_json(c, orient='columns')
203
214
 
204
215
  df = json.loads(df) if isinstance(df, str) else df
@@ -218,26 +229,6 @@ def sync_pipe(
218
229
  else [partition.compute() for partition in df.partitions]
219
230
  )
220
231
 
221
- numeric_cols = get_numeric_cols(df)
222
- if numeric_cols:
223
- for col in numeric_cols:
224
- df[col] = df[col].apply(lambda x: f'{x:f}' if isinstance(x, Decimal) else x)
225
- pipe_dtypes = pipe.dtypes
226
- new_numeric_cols = [
227
- col
228
- for col in numeric_cols
229
- if pipe_dtypes.get(col, None) != 'numeric'
230
- ]
231
- pipe.dtypes.update({
232
- col: 'numeric'
233
- for col in new_numeric_cols
234
- })
235
- edit_success, edit_msg = pipe.edit(debug=debug)
236
- if not edit_success:
237
- warn(
238
- "Failed to update new numeric columns "
239
- + f"{items_str(new_numeric_cols)}:\n{edit_msg}"
240
- )
241
232
  elif isinstance(df, dict):
242
233
  ### `_chunks` is a dict of lists of dicts.
243
234
  ### e.g. {'a' : [ {'a':[1, 2]}, {'a':[3, 4]} ] }
@@ -313,7 +304,7 @@ def sync_pipe(
313
304
 
314
305
  success_tuple = True, (
315
306
  f"It took {interval_str(timedelta(seconds=(time.perf_counter() - begin)))} "
316
- + "to sync {rowcount:,} row"
307
+ + f"to sync {rowcount:,} row"
317
308
  + ('s' if rowcount != 1 else '')
318
309
  + f" across {num_success_chunks:,} chunk" + ('s' if num_success_chunks != 1 else '') +
319
310
  f" to {pipe}."
@@ -324,7 +315,7 @@ def sync_pipe(
324
315
  def delete_pipe(
325
316
  self,
326
317
  pipe: Optional[mrsm.Pipe] = None,
327
- debug: bool = None,
318
+ debug: bool = False,
328
319
  ) -> SuccessTuple:
329
320
  """Delete a Pipe and drop its table."""
330
321
  if pipe is None:
@@ -576,7 +567,7 @@ def create_metadata(
576
567
  A bool indicating success.
577
568
  """
578
569
  from meerschaum.utils.debug import dprint
579
- from meerschaum.config.static import STATIC_CONFIG
570
+ from meerschaum._internal.static import STATIC_CONFIG
580
571
  r_url = STATIC_CONFIG['api']['endpoints']['metadata']
581
572
  response = self.post(r_url, debug=debug)
582
573
  if debug:
@@ -663,7 +654,7 @@ def drop_pipe(
663
654
  from meerschaum.utils.warnings import error
664
655
  from meerschaum.utils.debug import dprint
665
656
  if pipe is None:
666
- error(f"Pipe cannot be None.")
657
+ error("Pipe cannot be None.")
667
658
  r_url = pipe_r_url(pipe)
668
659
  response = self.delete(
669
660
  r_url + '/drop',
@@ -16,7 +16,7 @@ def plugin_r_url(
16
16
  plugin: Union[mrsm.core.Plugin, str],
17
17
  ) -> str:
18
18
  """Generate a relative URL path from a Plugin."""
19
- from meerschaum.config.static import STATIC_CONFIG
19
+ from meerschaum._internal.static import STATIC_CONFIG
20
20
  return f"{STATIC_CONFIG['api']['endpoints']['plugins']}/{plugin}"
21
21
 
22
22
 
@@ -111,7 +111,7 @@ def get_plugins(
111
111
  """
112
112
  import json
113
113
  from meerschaum.utils.warnings import error
114
- from meerschaum.config.static import STATIC_CONFIG
114
+ from meerschaum._internal.static import STATIC_CONFIG
115
115
  response = self.get(
116
116
  STATIC_CONFIG['api']['endpoints']['plugins'],
117
117
  params = {'user_id': user_id, 'search_term': search_term},
@@ -11,7 +11,7 @@ import urllib.parse
11
11
  import pathlib
12
12
  from meerschaum.utils.typing import Any, Optional, Dict, Union
13
13
  from meerschaum.utils.debug import dprint
14
- from meerschaum.config.static import STATIC_CONFIG
14
+ from meerschaum._internal.static import STATIC_CONFIG
15
15
 
16
16
  METHODS = {
17
17
  'GET',
@@ -0,0 +1,146 @@
1
+ #! /usr/bin/env python3
2
+ # vim:fenc=utf-8
3
+
4
+ """
5
+ Implement the `APIConnector` token methods.
6
+ """
7
+
8
+ import json
9
+ import uuid
10
+ from datetime import datetime
11
+ from typing import Union, List, Optional
12
+
13
+ import meerschaum as mrsm
14
+ from meerschaum.core import Token
15
+ from meerschaum._internal.static import STATIC_CONFIG
16
+ tokens_endpoint = STATIC_CONFIG['api']['endpoints']['tokens']
17
+
18
+
19
+ def register_token(self, token: Token, debug: bool = False) -> mrsm.SuccessTuple:
20
+ """
21
+ Register the provided token to the API.
22
+ """
23
+ from meerschaum.utils.dtypes import json_serialize_value
24
+ r_url = tokens_endpoint + '/register'
25
+ response = self.post(
26
+ r_url,
27
+ data=json.dumps({
28
+ 'label': token.label,
29
+ 'scopes': token.scopes,
30
+ 'expiration': token.expiration,
31
+ }, default=json_serialize_value),
32
+ debug=debug,
33
+ )
34
+ if not response:
35
+ return False, f"Failed to register token:\n{response.text}"
36
+
37
+ data = response.json()
38
+ token.label = data['label']
39
+ token.secret = data['secret']
40
+ token.id = uuid.UUID(data['id'])
41
+ if data.get('expiration', None):
42
+ token.expiration = datetime.fromisoformat(data['expiration'])
43
+
44
+ return True, f"Registered token '{token.label}'."
45
+
46
+
47
+ def get_token_model(self, token_id: uuid.UUID, debug: bool = False) -> 'Union[TokenModel, None]':
48
+ """
49
+ Return a token's model from the API instance.
50
+ """
51
+ from meerschaum.models import TokenModel
52
+ r_url = tokens_endpoint + f'/{token_id}'
53
+ response = self.get(r_url, debug=debug)
54
+ if not response:
55
+ return None
56
+ data = response.json()
57
+ return TokenModel(**data)
58
+
59
+
60
+ def get_tokens(self, labels: Optional[List[str]] = None, debug: bool = False) -> List[Token]:
61
+ """
62
+ Return the tokens registered to the current user.
63
+ """
64
+ from meerschaum.utils.warnings import warn
65
+ r_url = tokens_endpoint
66
+ params = {}
67
+ if labels:
68
+ params['labels'] = ','.join(labels)
69
+ response = self.get(r_url, params={'labels': labels}, debug=debug)
70
+ if not response:
71
+ warn(f"Could not get tokens from '{self}':\n{response.text}")
72
+ return []
73
+
74
+ tokens = [
75
+ Token(instance=self, **payload)
76
+ for payload in response.json()
77
+ ]
78
+ return tokens
79
+
80
+
81
+ def edit_token(self, token: Token, debug: bool = False) -> mrsm.SuccessTuple:
82
+ """
83
+ Persist the token's in-memory state to the API.
84
+ """
85
+ r_url = tokens_endpoint + f"/{token.id}/edit"
86
+ response = self.post(
87
+ r_url,
88
+ json={
89
+ 'creation': token.creation.isoformat() if token.creation else None,
90
+ 'expiration': token.expiration.isoformat() if token.expiration else None,
91
+ 'label': token.label,
92
+ 'is_valid': token.is_valid,
93
+ 'scopes': token.scopes,
94
+ },
95
+ )
96
+ if not response:
97
+ return False, f"Failed to edit token:\n{response.text}"
98
+
99
+ success, msg = response.json()
100
+ return success, msg
101
+
102
+
103
+ def invalidate_token(self, token: Token, debug: bool = False) -> mrsm.SuccessTuple:
104
+ """
105
+ Invalidate the token, disabling it for future requests.
106
+ """
107
+ r_url = tokens_endpoint + f"/{token.id}/invalidate"
108
+ response = self.post(r_url)
109
+ if not response:
110
+ return False, f"Failed to invalidate token:\n{response.text}"
111
+
112
+ success, msg = response.json()
113
+ return success, msg
114
+
115
+
116
+ def get_token_scopes(self, token_id: Union[uuid.UUID, Token], debug: bool = False) -> List[str]:
117
+ """
118
+ Return the scopes for a token.
119
+ """
120
+ _token_id = (token_id.id if isinstance(token_id, Token) else token_id)
121
+ model = self.get_token_model(_token_id, debug=debug).scopes
122
+ return getattr(model, 'scopes', [])
123
+
124
+
125
+ def token_exists(self, token_id: Union[uuid.UUID, Token], debug: bool = False) -> bool:
126
+ """
127
+ Return `True` if a token exists.
128
+ """
129
+ _token_id = (token_id.id if isinstance(token_id, Token) else token_id)
130
+ model = self.get_token_model(_token_id, debug=debug)
131
+ if model is None:
132
+ return False
133
+ return model.creation is not None
134
+
135
+
136
+ def delete_token(self, token: Token, debug: bool = False) -> mrsm.SuccessTuple:
137
+ """
138
+ Delete the token from the API.
139
+ """
140
+ r_url = tokens_endpoint + f"/{token.id}"
141
+ response = self.delete(r_url, debug=debug)
142
+ if not response:
143
+ return False, f"Failed to delete token:\n{response.text}"
144
+
145
+ success, msg = response.json()
146
+ return success, msg
@@ -7,18 +7,23 @@ Manage users via the API Connector.
7
7
  """
8
8
 
9
9
  from __future__ import annotations
10
- from meerschaum.utils.typing import Optional, Any, List, SuccessTuple
10
+
11
+ import json
12
+ from uuid import UUID
13
+
14
+ import meerschaum as mrsm
15
+ from meerschaum.utils.typing import Optional, Any, List, SuccessTuple, Union
16
+
11
17
 
12
18
  def get_users(
13
- self,
14
- debug: bool = False,
15
- **kw : Any
16
- ) -> List[str]:
19
+ self,
20
+ debug: bool = False,
21
+ **kw: Any
22
+ ) -> List[str]:
17
23
  """
18
24
  Return a list of registered usernames.
19
25
  """
20
- from meerschaum.config.static import STATIC_CONFIG
21
- import json
26
+ from meerschaum._internal.static import STATIC_CONFIG
22
27
  response = self.get(
23
28
  f"{STATIC_CONFIG['api']['endpoints']['users']}",
24
29
  debug = debug,
@@ -31,15 +36,15 @@ def get_users(
31
36
  except Exception as e:
32
37
  return []
33
38
 
39
+
34
40
  def edit_user(
35
- self,
36
- user: 'meerschaum.core.User',
37
- debug: bool = False,
38
- **kw: Any
39
- ) -> SuccessTuple:
41
+ self,
42
+ user: mrsm.core.User,
43
+ debug: bool = False,
44
+ **kw: Any
45
+ ) -> SuccessTuple:
40
46
  """Edit an existing user."""
41
- import json
42
- from meerschaum.config.static import STATIC_CONFIG
47
+ from meerschaum._internal.static import STATIC_CONFIG
43
48
  r_url = f"{STATIC_CONFIG['api']['endpoints']['users']}/edit"
44
49
  data = {
45
50
  'username': user.username,
@@ -54,7 +59,7 @@ def edit_user(
54
59
  if isinstance(_json, dict) and 'detail' in _json:
55
60
  return False, _json['detail']
56
61
  success_tuple = tuple(_json)
57
- except Exception as e:
62
+ except Exception:
58
63
  msg = response.text if response else f"Failed to edit user '{user}'."
59
64
  return False, msg
60
65
 
@@ -62,14 +67,13 @@ def edit_user(
62
67
 
63
68
 
64
69
  def register_user(
65
- self,
66
- user: 'meerschaum.core.User',
67
- debug: bool = False,
68
- **kw: Any
69
- ) -> SuccessTuple:
70
+ self,
71
+ user: mrsm.core.User,
72
+ debug: bool = False,
73
+ **kw: Any
74
+ ) -> SuccessTuple:
70
75
  """Register a new user."""
71
- import json
72
- from meerschaum.config.static import STATIC_CONFIG
76
+ from meerschaum._internal.static import STATIC_CONFIG
73
77
  r_url = f"{STATIC_CONFIG['api']['endpoints']['users']}/register"
74
78
  data = {
75
79
  'username': user.username,
@@ -94,31 +98,37 @@ def register_user(
94
98
 
95
99
 
96
100
  def get_user_id(
97
- self,
98
- user: 'meerschaum.core.User',
99
- debug: bool = False,
100
- **kw: Any
101
- ) -> Optional[int]:
101
+ self,
102
+ user: mrsm.core.User,
103
+ debug: bool = False,
104
+ **kw: Any
105
+ ) -> Union[int, str, UUID, None]:
102
106
  """Get a user's ID."""
103
- from meerschaum.config.static import STATIC_CONFIG
104
- import json
107
+ from meerschaum._internal.static import STATIC_CONFIG
108
+ from meerschaum.utils.misc import is_int, is_uuid
105
109
  r_url = f"{STATIC_CONFIG['api']['endpoints']['users']}/{user.username}/id"
106
110
  response = self.get(r_url, debug=debug, **kw)
107
111
  try:
108
- user_id = int(json.loads(response.text))
112
+ id_text = str(json.loads(response.text))
113
+ if is_int(id_text):
114
+ user_id = int(id_text)
115
+ elif is_uuid(id_text):
116
+ user_id = UUID(id_text)
117
+ else:
118
+ user_id = id_text
109
119
  except Exception as e:
110
120
  user_id = None
111
121
  return user_id
112
122
 
123
+
113
124
  def delete_user(
114
- self,
115
- user: 'meerschaum.core.User',
116
- debug: bool = False,
117
- **kw: Any
118
- ) -> SuccessTuple:
125
+ self,
126
+ user: mrsm.core.User,
127
+ debug: bool = False,
128
+ **kw: Any
129
+ ) -> SuccessTuple:
119
130
  """Delete a user."""
120
- from meerschaum.config.static import STATIC_CONFIG
121
- import json
131
+ from meerschaum._internal.static import STATIC_CONFIG
122
132
  r_url = f"{STATIC_CONFIG['api']['endpoints']['users']}/{user.username}"
123
133
  response = self.delete(r_url, debug=debug)
124
134
  try:
@@ -126,53 +136,55 @@ def delete_user(
126
136
  if isinstance(_json, dict) and 'detail' in _json:
127
137
  return False, _json['detail']
128
138
  success_tuple = tuple(_json)
129
- except Exception as e:
139
+ except Exception:
130
140
  success_tuple = False, f"Failed to delete user '{user.username}'."
131
141
  return success_tuple
132
142
 
143
+
133
144
  def get_user_attributes(
134
- self,
135
- user: 'meerschaum.core.User',
136
- debug: bool = False,
137
- **kw
138
- ) -> int:
145
+ self,
146
+ user: mrsm.core.User,
147
+ debug: bool = False,
148
+ **kw
149
+ ) -> int:
139
150
  """Get a user's attributes."""
140
- from meerschaum.config.static import STATIC_CONFIG
141
- import json
151
+ from meerschaum._internal.static import STATIC_CONFIG
142
152
  r_url = f"{STATIC_CONFIG['api']['endpoints']['users']}/{user.username}/attributes"
143
153
  response = self.get(r_url, debug=debug, **kw)
144
154
  try:
145
155
  attributes = json.loads(response.text)
146
- except Exception as e:
156
+ except Exception:
147
157
  attributes = None
148
158
  return attributes
149
159
 
160
+
150
161
  #############################
151
162
  # Chaining functions below. #
152
163
  #############################
153
164
 
154
165
  def get_user_password_hash(
155
- self,
156
- user: 'meerschaum.core.User',
157
- debug: bool = False,
158
- **kw: Any
159
- ) -> Optional[str]:
166
+ self,
167
+ user: mrsm.core.User,
168
+ debug: bool = False,
169
+ **kw: Any
170
+ ) -> Optional[str]:
160
171
  """If configured, get a user's password hash."""
161
- from meerschaum.config.static import STATIC_CONFIG
172
+ from meerschaum._internal.static import STATIC_CONFIG
162
173
  r_url = STATIC_CONFIG['api']['endpoints']['users'] + '/' + user.username + '/password_hash'
163
174
  response = self.get(r_url, debug=debug, **kw)
164
175
  if not response:
165
176
  return None
166
177
  return response.json()
167
178
 
179
+
168
180
  def get_user_type(
169
- self,
170
- user: 'meerschaum.core.User',
171
- debug: bool = False,
172
- **kw: Any
173
- ) -> Optional[str]:
181
+ self,
182
+ user: mrsm.core.User,
183
+ debug: bool = False,
184
+ **kw: Any
185
+ ) -> Optional[str]:
174
186
  """If configured, get a user's type."""
175
- from meerschaum.config.static import STATIC_CONFIG
187
+ from meerschaum._internal.static import STATIC_CONFIG
176
188
  r_url = STATIC_CONFIG['api']['endpoints']['users'] + '/' + user.username + '/type'
177
189
  response = self.get(r_url, debug=debug, **kw)
178
190
  if not response: