validio-cli 0.24.1__tar.gz → 0.25.2__tar.gz

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 (28) hide show
  1. {validio_cli-0.24.1 → validio_cli-0.25.2}/PKG-INFO +2 -2
  2. {validio_cli-0.24.1 → validio_cli-0.25.2}/pyproject.toml +2 -2
  3. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/code.py +30 -18
  4. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/sources.py +85 -55
  5. {validio_cli-0.24.1 → validio_cli-0.25.2}/LICENSE +0 -0
  6. {validio_cli-0.24.1 → validio_cli-0.25.2}/README_PUBLIC.md +0 -0
  7. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/__init__.py +0 -0
  8. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/channels.py +0 -0
  9. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/config.py +0 -0
  10. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/credentials.py +0 -0
  11. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/dbt.py +0 -0
  12. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/filters.py +0 -0
  13. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/incidents.py +0 -0
  14. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/metrics.py +0 -0
  15. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/namespaces.py +0 -0
  16. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/notification_rules.py +0 -0
  17. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/recommendations.py +0 -0
  18. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/resources.py +0 -0
  19. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/segmentations.py +0 -0
  20. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/segments.py +0 -0
  21. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/users.py +0 -0
  22. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/validators.py +0 -0
  23. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/entities/windows.py +0 -0
  24. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/bin/main.py +0 -0
  25. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/components.py +0 -0
  26. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/metadata.py +0 -0
  27. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/namespace.py +0 -0
  28. {validio_cli-0.24.1 → validio_cli-0.25.2}/validio_cli/schema.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: validio-cli
3
- Version: 0.24.1
3
+ Version: 0.25.2
4
4
  Summary: CLI tool to interact with the Validio platform
5
5
  Home-page: https://validio.io/
6
6
  License: Apache-2.0
@@ -16,7 +16,7 @@ Requires-Dist: classdiff (>=0.5.0,<0.6.0)
16
16
  Requires-Dist: prompt-toolkit (>=3.0.38,<4.0.0)
17
17
  Requires-Dist: pydantic (==2.4.2)
18
18
  Requires-Dist: tabulate (>=0.9.0,<0.10.0)
19
- Requires-Dist: typer[all] (>=0.7.0,<0.8.0)
19
+ Requires-Dist: typer[all] (>=0.8.0,<0.9.0)
20
20
  Requires-Dist: types-tabulate (>=0.9.0.2,<0.10.0.0)
21
21
  Requires-Dist: validio-sdk
22
22
  Project-URL: Documentation, https://docs.validio.io/
@@ -3,7 +3,7 @@ name = "validio-cli"
3
3
  # This version does not represent the released version or any tag. For each
4
4
  # release we automatically bump this before building and publishing so this
5
5
  # should be kept at 0.0.1dev1
6
- version = "0.24.1"
6
+ version = "0.25.2"
7
7
  description = "CLI tool to interact with the Validio platform"
8
8
  authors = ["Validio <support@validio.io>"]
9
9
  license = "Apache-2.0"
@@ -16,7 +16,7 @@ readme = "README_PUBLIC.md"
16
16
  python = "^3.10"
17
17
  camel-converter = "^3.0.0"
18
18
  classdiff = "^0.5.0"
19
- typer = { extras = ["all"], version = "^0.7.0" }
19
+ typer = { extras = ["all"], version = "^0.8.0" }
20
20
  prompt-toolkit = "^3.0.38"
21
21
  tabulate = "^0.9.0"
22
22
  types-tabulate = "^0.9.0.2"
@@ -7,7 +7,6 @@ from typing import Any
7
7
 
8
8
  import classdiff # type: ignore
9
9
  import typer
10
- from validio_sdk import ValidioError
11
10
  from validio_sdk._api.api import APIClient
12
11
  from validio_sdk.code import _import as code_import
13
12
  from validio_sdk.code import apply as code_apply
@@ -99,6 +98,15 @@ update_all_schemas_option = typer.Option(
99
98
  ),
100
99
  )
101
100
 
101
+ show_progress_option = typer.Option(
102
+ True,
103
+ help=(
104
+ f"{BETA_BANNER} "
105
+ "If enabled, progress updates will be displayed during "
106
+ "execution of this command"
107
+ ),
108
+ )
109
+
102
110
 
103
111
  def target_option(resource_type: str) -> Any:
104
112
  return typer.Option(
@@ -145,6 +153,7 @@ async def plan(
145
153
  source: list[str] = target_option("source"),
146
154
  validator: list[str] = target_option("validator"),
147
155
  window: list[str] = target_option("window"),
156
+ progress: bool = show_progress_option,
148
157
  ) -> None:
149
158
  dir_path = directory_or_default(directory)
150
159
  client, _ = get_client(config_dir)
@@ -174,6 +183,7 @@ async def plan(
174
183
  validators=set(validator),
175
184
  windows=set(window),
176
185
  ),
186
+ show_progress=progress,
177
187
  )
178
188
 
179
189
 
@@ -199,6 +209,7 @@ async def apply(
199
209
  source: list[str] = target_option("source"),
200
210
  validator: list[str] = target_option("validator"),
201
211
  window: list[str] = target_option("window"),
212
+ progress: bool = show_progress_option,
202
213
  ) -> None:
203
214
  dir_path = directory_or_default(directory)
204
215
  client, _ = get_client(config_dir)
@@ -208,7 +219,7 @@ async def apply(
208
219
  )
209
220
 
210
221
  namespace = get_namespace(dir_path)
211
- diff, manifest_ctx = await _plan(
222
+ plan_result = await _plan(
212
223
  namespace=namespace,
213
224
  client=client,
214
225
  directory=dir_path,
@@ -228,8 +239,10 @@ async def apply(
228
239
  validators=set(validator),
229
240
  windows=set(window),
230
241
  ),
242
+ show_progress=progress,
231
243
  )
232
244
 
245
+ diff = plan_result.graph_diff
233
246
  if diff.num_operations() == 0:
234
247
  return
235
248
 
@@ -242,9 +255,10 @@ async def apply(
242
255
  await code_apply.apply(
243
256
  namespace=namespace,
244
257
  client=client,
245
- ctx=manifest_ctx,
258
+ ctx=plan_result.diff_context,
246
259
  diff=diff,
247
260
  show_secrets=show_secrets,
261
+ show_progress=progress,
248
262
  )
249
263
 
250
264
  print(
@@ -302,11 +316,6 @@ async def _import(
302
316
  credentials=set(credential), channels=set(channel)
303
317
  )
304
318
 
305
- if resource_names.is_empty() and import_namespace is None:
306
- raise ValidioError(
307
- "--import-namespace is required when targeting resources to move"
308
- )
309
-
310
319
  dir_path = directory_or_default(directory)
311
320
  client, _ = get_client(config_dir)
312
321
  namespace = get_namespace(dir_path)
@@ -325,7 +334,7 @@ async def _import(
325
334
  if num_moved_resources > 0:
326
335
  print(f"Moved {num_moved_resources} resources")
327
336
 
328
- diff, _ = await _plan(
337
+ plan_result = await _plan(
329
338
  namespace=namespace,
330
339
  client=client,
331
340
  directory=dir_path,
@@ -339,11 +348,14 @@ async def _import(
339
348
  import_mode=True,
340
349
  )
341
350
 
342
- if diff.num_deletes() == 0:
351
+ if plan_result.graph_diff.num_deletes() == 0:
343
352
  _print_no_changes()
344
353
  return
345
354
 
346
- s = await code_import._import(ctx=diff.to_delete)
355
+ s = await code_import._import(
356
+ ctx=plan_result.graph_diff.to_delete,
357
+ tags_ctx=plan_result.tags_context,
358
+ )
347
359
  output.write_text(s)
348
360
  print(f"Generated file {output.absolute()}")
349
361
 
@@ -371,7 +383,8 @@ async def _plan(
371
383
  show_diff: bool = True,
372
384
  targets: code_plan.ResourceNames = code_plan.ResourceNames(),
373
385
  import_mode: bool = False,
374
- ) -> tuple[GraphDiff, DiffContext]:
386
+ show_progress: bool = True,
387
+ ) -> code_plan.PlanResult:
375
388
  plan_result = await code_plan.plan(
376
389
  namespace=namespace,
377
390
  client=client,
@@ -382,16 +395,15 @@ async def _plan(
382
395
  show_secrets=show_secrets,
383
396
  targets=targets,
384
397
  import_mode=import_mode,
398
+ show_progress=show_progress,
385
399
  )
386
400
 
387
- diff, manifest_ctx = plan_result.graph_diff, plan_result.diff_context
388
-
389
401
  if show_diff:
390
- if diff.num_operations() == 0:
402
+ if plan_result.graph_diff.num_operations() == 0:
391
403
  _print_no_changes()
392
404
  else:
393
405
  _show_resources_diff(
394
- diff=diff,
406
+ diff=plan_result.graph_diff,
395
407
  show_schema=show_schema,
396
408
  show_secrets=show_secrets,
397
409
  escape=sys.stdout.isatty() and color,
@@ -400,7 +412,7 @@ async def _plan(
400
412
 
401
413
  _show_deprecations(plan_result.deprecations)
402
414
 
403
- return diff, manifest_ctx
415
+ return plan_result
404
416
 
405
417
 
406
418
  def _show_resources_diff(
@@ -522,7 +534,7 @@ def _show_update_resource_diff(
522
534
  if r.replacement_cascaded_update_parent:
523
535
  (cls, parent_name) = r.replacement_cascaded_update_parent
524
536
  cascade_update = (
525
- f" (due to replacement of " f"{cls.__name__}({repr(parent_name)}))"
537
+ f" (due to replacement of " f"{cls.__name__}({parent_name!r}))"
526
538
  )
527
539
 
528
540
  print(f"{class_key} '{r.manifest.name}' will be updated{cascade_update}")
@@ -14,6 +14,7 @@ from validio_sdk._api.api import (
14
14
  SourceAction,
15
15
  apply_source_action,
16
16
  get_sources,
17
+ split_to_chunks,
17
18
  )
18
19
  from validio_sdk.client import Session
19
20
  from validio_sdk.config import ValidioConfig
@@ -36,8 +37,6 @@ from validio_cli import (
36
37
  from validio_cli.bin.entities import credentials
37
38
  from validio_cli.namespace import get_namespace
38
39
 
39
- NUM_CONCURRENT_CONNECTIONS = 5
40
-
41
40
  app = AsyncTyper(help="Data sources to ingest data from")
42
41
 
43
42
  infer_schema_app = AsyncTyper(help="Infer schema")
@@ -111,15 +110,16 @@ async def start(
111
110
  namespace: str = Namespace(),
112
111
  identifiers: list[str] = Identifiers,
113
112
  ) -> None:
114
- chunks = split_to_chunks(identifiers, NUM_CONCURRENT_CONNECTIONS)
115
- results = await asyncio.gather(
116
- *[
117
- apply_batch_source_action(config_dir, SourceAction.START, chunk, namespace)
118
- for chunk in chunks
119
- ]
120
- )
121
-
122
- return handle_source_action_response(results, SourceAction.START, output_format)
113
+ client, cfg = get_client(config_dir)
114
+ async with client.client as session:
115
+ await apply_source_action_on_chunks(
116
+ session,
117
+ cfg,
118
+ SourceAction.START,
119
+ split_to_chunks(identifiers),
120
+ namespace,
121
+ output_format,
122
+ )
123
123
 
124
124
 
125
125
  @app.async_command(help="Stop source")
@@ -130,15 +130,16 @@ async def stop(
130
130
  namespace: str = Namespace(),
131
131
  identifiers: list[str] = Identifiers,
132
132
  ) -> None:
133
- chunks = split_to_chunks(identifiers, NUM_CONCURRENT_CONNECTIONS)
134
- results = await asyncio.gather(
135
- *[
136
- apply_batch_source_action(config_dir, SourceAction.STOP, chunk, namespace)
137
- for chunk in chunks
138
- ]
139
- )
140
-
141
- return handle_source_action_response(results, SourceAction.STOP, output_format)
133
+ client, cfg = get_client(config_dir)
134
+ async with client.client as session:
135
+ await apply_source_action_on_chunks(
136
+ session,
137
+ cfg,
138
+ SourceAction.STOP,
139
+ split_to_chunks(identifiers),
140
+ namespace,
141
+ output_format,
142
+ )
142
143
 
143
144
 
144
145
  @app.async_command(help="Backfill source")
@@ -149,17 +150,16 @@ async def backfill(
149
150
  namespace: str = Namespace(),
150
151
  identifiers: list[str] = Identifiers,
151
152
  ) -> None:
152
- chunks = split_to_chunks(identifiers, NUM_CONCURRENT_CONNECTIONS)
153
- results = await asyncio.gather(
154
- *[
155
- apply_batch_source_action(
156
- config_dir, SourceAction.BACKFILL, chunk, namespace
157
- )
158
- for chunk in chunks
159
- ]
160
- )
161
-
162
- return handle_source_action_response(results, SourceAction.BACKFILL, output_format)
153
+ client, cfg = get_client(config_dir)
154
+ async with client.client as session:
155
+ await apply_source_action_on_chunks(
156
+ session,
157
+ cfg,
158
+ SourceAction.BACKFILL,
159
+ split_to_chunks(identifiers),
160
+ namespace,
161
+ output_format,
162
+ )
163
163
 
164
164
 
165
165
  @app.async_command(help="Reset source")
@@ -170,15 +170,16 @@ async def reset(
170
170
  namespace: str = Namespace(),
171
171
  identifiers: list[str] = Identifiers,
172
172
  ) -> None:
173
- chunks = split_to_chunks(identifiers, NUM_CONCURRENT_CONNECTIONS)
174
- results = await asyncio.gather(
175
- *[
176
- apply_batch_source_action(config_dir, SourceAction.RESET, chunk, namespace)
177
- for chunk in chunks
178
- ]
179
- )
180
-
181
- return handle_source_action_response(results, SourceAction.RESET, output_format)
173
+ client, cfg = get_client(config_dir)
174
+ async with client.client as session:
175
+ await apply_source_action_on_chunks(
176
+ session,
177
+ cfg,
178
+ SourceAction.RESET,
179
+ split_to_chunks(identifiers),
180
+ namespace,
181
+ output_format,
182
+ )
182
183
 
183
184
 
184
185
  @infer_schema_app.callback(invoke_without_command=True)
@@ -1145,8 +1146,11 @@ async def _get_null_marker() -> str | None:
1145
1146
  return null_marker
1146
1147
 
1147
1148
 
1148
- async def get_source_id(
1149
- executor: APIClient | Session, cfg: ValidioConfig, identifier: str, namespace: str
1149
+ async def _get_source_id(
1150
+ session: Session,
1151
+ cfg: ValidioConfig,
1152
+ identifier: str,
1153
+ namespace: str,
1150
1154
  ) -> str | None:
1151
1155
  """
1152
1156
  Ensure the identifier is a resource id.
@@ -1164,7 +1168,7 @@ async def get_source_id(
1164
1168
  return identifier
1165
1169
 
1166
1170
  resource = await get_sources(
1167
- executor,
1171
+ session,
1168
1172
  source_id=identifier,
1169
1173
  namespace_id=get_namespace(namespace, cfg),
1170
1174
  )
@@ -1179,6 +1183,16 @@ async def get_source_id(
1179
1183
  return resource["id"]
1180
1184
 
1181
1185
 
1186
+ async def get_source_id(
1187
+ client: APIClient,
1188
+ cfg: ValidioConfig,
1189
+ identifier: str,
1190
+ namespace: str,
1191
+ ) -> str | None:
1192
+ async with client.client as session:
1193
+ return await _get_source_id(session, cfg, identifier, namespace)
1194
+
1195
+
1182
1196
  def _resource_filter(
1183
1197
  source: Any, credential_id: str | None, credential_name: str | None
1184
1198
  ) -> bool:
@@ -1234,15 +1248,35 @@ def _interactive_message_format_input() -> tuple[str, list[str], str]:
1234
1248
  )
1235
1249
 
1236
1250
 
1251
+ async def apply_source_action_on_chunks(
1252
+ session: Session,
1253
+ cfg: ValidioConfig,
1254
+ action: SourceAction,
1255
+ chunks: list[list[str]],
1256
+ namespace: str,
1257
+ output_format: OutputFormat = OutputFormatOption,
1258
+ ) -> None:
1259
+ results = await asyncio.gather(
1260
+ *[
1261
+ apply_batch_source_action(session, cfg, action, chunk, namespace)
1262
+ for chunk in chunks
1263
+ ]
1264
+ )
1265
+
1266
+ return handle_source_action_response(results, action, output_format)
1267
+
1268
+
1237
1269
  async def apply_batch_source_action(
1238
- config_dir: str, action: SourceAction, identifiers: list[str], namespace: str
1270
+ session: Session,
1271
+ cfg: ValidioConfig,
1272
+ action: SourceAction,
1273
+ identifiers: list[str],
1274
+ namespace: str,
1239
1275
  ) -> list[dict[str, Any]]:
1240
- client, cfg = get_client(config_dir)
1241
- async with client.client as session:
1242
- return [
1243
- await apply_single_source_action(session, action, cfg, id, namespace)
1244
- for id in identifiers
1245
- ]
1276
+ return [
1277
+ await apply_single_source_action(session, action, cfg, id, namespace)
1278
+ for id in identifiers
1279
+ ]
1246
1280
 
1247
1281
 
1248
1282
  async def apply_single_source_action(
@@ -1252,7 +1286,7 @@ async def apply_single_source_action(
1252
1286
  identifier: str,
1253
1287
  namespace: str,
1254
1288
  ) -> dict[str, Any]:
1255
- source_id = await get_source_id(session, cfg, identifier, namespace)
1289
+ source_id = await _get_source_id(session, cfg, identifier, namespace)
1256
1290
  if source_id is None:
1257
1291
  return {
1258
1292
  f"source{action.value}": {
@@ -1301,9 +1335,5 @@ def handle_source_action_response(
1301
1335
  )
1302
1336
 
1303
1337
 
1304
- def split_to_chunks(data: list[Any], num_of_chunks: int) -> list[list[Any]]:
1305
- return [data[i::num_of_chunks] for i in range(min(num_of_chunks, len(data)))]
1306
-
1307
-
1308
1338
  if __name__ == "__main__":
1309
1339
  typer.run(app())
File without changes