validio-cli 0.22.2__tar.gz → 0.24.0__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 (30) hide show
  1. {validio_cli-0.22.2 → validio_cli-0.24.0}/PKG-INFO +1 -1
  2. {validio_cli-0.22.2 → validio_cli-0.24.0}/pyproject.toml +3 -3
  3. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/__init__.py +44 -13
  4. validio_cli-0.24.0/validio_cli/bin/entities/channels.py +57 -0
  5. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/code.py +104 -79
  6. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/config.py +3 -13
  7. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/credentials.py +26 -27
  8. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/dbt.py +4 -4
  9. validio_cli-0.24.0/validio_cli/bin/entities/filters.py +66 -0
  10. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/incidents.py +28 -33
  11. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/metrics.py +13 -22
  12. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/namespaces.py +37 -45
  13. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/notification_rules.py +25 -26
  14. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/recommendations.py +10 -25
  15. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/resources.py +39 -29
  16. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/segmentations.py +29 -32
  17. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/segments.py +10 -13
  18. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/sources.py +293 -224
  19. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/users.py +7 -14
  20. validio_cli-0.24.0/validio_cli/bin/entities/validators.py +113 -0
  21. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/entities/windows.py +22 -27
  22. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/bin/main.py +3 -15
  23. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/components.py +2 -0
  24. validio_cli-0.22.2/validio_cli/bin/entities/channels.py +0 -96
  25. validio_cli-0.22.2/validio_cli/bin/entities/validators.py +0 -125
  26. {validio_cli-0.22.2 → validio_cli-0.24.0}/LICENSE +0 -0
  27. {validio_cli-0.22.2 → validio_cli-0.24.0}/README_PUBLIC.md +0 -0
  28. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/metadata.py +0 -0
  29. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/namespace.py +0 -0
  30. {validio_cli-0.22.2 → validio_cli-0.24.0}/validio_cli/schema.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: validio-cli
3
- Version: 0.22.2
3
+ Version: 0.24.0
4
4
  Summary: CLI tool to interact with the Validio platform
5
5
  Home-page: https://validio.io/
6
6
  License: Apache-2.0
@@ -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.22.2"
6
+ version = "0.24.0"
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,20 +16,20 @@ readme = "README_PUBLIC.md"
16
16
  python = "^3.10"
17
17
  camel-converter = "^3.0.0"
18
18
  classdiff = "^0.5.0"
19
- validio-sdk = "*"
20
19
  typer = { extras = ["all"], version = "^0.7.0" }
21
20
  prompt-toolkit = "^3.0.38"
22
21
  tabulate = "^0.9.0"
23
22
  types-tabulate = "^0.9.0.2"
24
23
  pydantic = "2.4.2"
24
+ validio-sdk = "*"
25
25
 
26
26
  [tool.poetry.group.dev.dependencies]
27
27
  ruff = "^0.1.11"
28
28
  mypy = "^1.2.0"
29
- validio-sdk = { path = "../validio-sdk", develop = true }
30
29
  licensecheck = "^2023.1.1"
31
30
  pytest = "^7.4.3"
32
31
  flameprof = "^0.4"
32
+ validio-sdk = { path = "../validio-sdk" }
33
33
 
34
34
  [build-system]
35
35
  requires = ["poetry-core"]
@@ -15,6 +15,7 @@ import typer
15
15
  from camel_converter import to_snake
16
16
  from tabulate import tabulate
17
17
  from validio_sdk import config
18
+ from validio_sdk._api.api import APIClient
18
19
  from validio_sdk.config import Config, ValidioConfig
19
20
  from validio_sdk.util import ClassJSONEncoder
20
21
  from validio_sdk.validio_client import ValidioAPIClient
@@ -25,6 +26,14 @@ T = TypeVar("T", bound="OutputSettings")
25
26
  DEFAULT_NAMESPACE: str = "default"
26
27
 
27
28
 
29
+ class Role(str, Enum):
30
+ """User roles."""
31
+
32
+ ADMIN = "ADMIN"
33
+ EDITOR = "EDITOR"
34
+ VIEWER = "VIEWER"
35
+
36
+
28
37
  class OutputFormat(str, Enum):
29
38
  """Available output formats for the CLI"""
30
39
 
@@ -45,6 +54,9 @@ OutputFormatOption = typer.Option(
45
54
  Identifier = typer.Argument(
46
55
  default=None, help="Name or ID of the resource to get of this type"
47
56
  )
57
+ Identifiers = typer.Argument(
58
+ default=None, help="Names or IDs of the sources to apply the command on"
59
+ )
48
60
 
49
61
 
50
62
  # ruff: noqa: N802
@@ -82,13 +94,20 @@ class AsyncTyper(typer.Typer):
82
94
  return decorator
83
95
 
84
96
 
97
+ def get_client(
98
+ config_dir: str = ConfigDir,
99
+ ) -> tuple[APIClient, ValidioConfig]:
100
+ cfg = Config(Path(config_dir)).read()
101
+ return APIClient(config=cfg), cfg
102
+
103
+
85
104
  async def get_client_and_config(
86
105
  config_dir: str = ConfigDir,
87
106
  ) -> tuple[ValidioAPIClient, ValidioConfig]:
88
107
  cfg = Config(Path(config_dir)).read()
89
108
  vc = ValidioAPIClient(validio_config=cfg)
90
109
 
91
- return (vc, cfg)
110
+ return vc, cfg
92
111
 
93
112
 
94
113
  @dataclass
@@ -104,6 +123,16 @@ class OutputSettings:
104
123
  reformat=lambda x: to_snake(x.removesuffix(trim)).upper(),
105
124
  )
106
125
 
126
+ @classmethod
127
+ def string_as_datetime(cls: type[T], attribute_name: str | None) -> T:
128
+ format = "%Y-%m-%dT%H:%M:%S.%fZ"
129
+ return cls(
130
+ attribute_name=attribute_name,
131
+ reformat=lambda x: datetime.strptime(x, format).replace(
132
+ tzinfo=timezone.utc
133
+ ),
134
+ )
135
+
107
136
 
108
137
  def output_json(obj: Any, identifier: str | None = None) -> None:
109
138
  # If we have an identifier we only want to display a single object so grab
@@ -135,17 +164,20 @@ def output_text(items: Any, fields: dict[str, OutputSettings | None]) -> None:
135
164
  row.append(settings.reformat(item))
136
165
  continue
137
166
 
138
- if not hasattr(item, attribute_name):
167
+ if isinstance(item, dict):
168
+ value = item.get(attribute_name)
169
+ elif hasattr(item, attribute_name):
170
+ value = getattr(item, attribute_name)
171
+ else:
139
172
  row.append("UNKNOWN")
140
173
  continue
141
174
 
142
- value = getattr(item, attribute_name)
143
- if isinstance(value, datetime):
144
- value = _format_relative_time(value)
145
-
146
175
  if settings is not None and settings.reformat is not None:
147
176
  value = settings.reformat(value)
148
177
 
178
+ if isinstance(value, datetime):
179
+ value = _format_relative_time(value)
180
+
149
181
  row.append(value)
150
182
 
151
183
  table.append(row)
@@ -199,7 +231,10 @@ def _single_resource_if_specified(items: list[Any], identifier: str | None) -> A
199
231
  for item in items
200
232
  if (hasattr(item, "resource_name") and item.resource_name == identifier)
201
233
  or (hasattr(item, "name") and item.name == identifier)
202
- or item.id == identifier
234
+ or (hasattr(item, "id") and item.id == identifier)
235
+ or (isinstance(item, dict) and item.get("resourceName") == identifier)
236
+ or (isinstance(item, dict) and item.get("name") == identifier)
237
+ or (isinstance(item, dict) and item.get("id") == identifier)
203
238
  ),
204
239
  None,
205
240
  )
@@ -220,18 +255,14 @@ def _resource_filter(
220
255
 
221
256
  # Traverse the object til the resource we need for filtering.
222
257
  for field in field_path:
223
- # If the object doesn't even have the field we can't do comparison so filter
224
- # it out.
225
- if not hasattr(resource, field):
226
- return False
258
+ resource = resource.get(field)
227
259
 
228
- resource = getattr(resource, field)
229
260
  # If the object field value we need for filtering out is none we filter
230
261
  # it out.
231
262
  if resource is None:
232
263
  return False
233
264
 
234
- return value in (resource.id, resource.resource_name)
265
+ return value in [resource["id"], resource["resourceName"]]
235
266
 
236
267
 
237
268
  def _fixed_width(message: str, width: int = 25) -> str:
@@ -0,0 +1,57 @@
1
+ import typer
2
+ from camel_converter import to_snake
3
+
4
+ from validio_cli import (
5
+ AsyncTyper,
6
+ ConfigDir,
7
+ Identifier,
8
+ Namespace,
9
+ OutputFormat,
10
+ OutputFormatOption,
11
+ OutputSettings,
12
+ _single_resource_if_specified,
13
+ get_client,
14
+ output_json,
15
+ output_text,
16
+ )
17
+ from validio_cli.namespace import get_namespace
18
+
19
+ app = AsyncTyper(help="Channels used for notifications")
20
+
21
+
22
+ @app.async_command(help="Get channels")
23
+ async def get(
24
+ config_dir: str = ConfigDir,
25
+ output_format: OutputFormat = OutputFormatOption,
26
+ namespace: str = Namespace(),
27
+ identifier: str = Identifier,
28
+ ) -> None:
29
+ client, cfg = get_client(config_dir)
30
+
31
+ channels = await client.get_channels(
32
+ channel_id=identifier,
33
+ namespace_id=get_namespace(namespace, cfg),
34
+ )
35
+
36
+ # TODO(UI-2311): Fully support list/get/get_by_resource_name
37
+ if isinstance(channels, list):
38
+ channels = _single_resource_if_specified(channels, identifier)
39
+
40
+ if output_format == OutputFormat.JSON:
41
+ return output_json(channels, identifier)
42
+
43
+ return output_text(
44
+ channels,
45
+ fields={
46
+ "name": OutputSettings(attribute_name="resourceName"),
47
+ "type": OutputSettings(
48
+ attribute_name="__typename",
49
+ reformat=lambda x: to_snake(x.removesuffix("Channel")).upper(),
50
+ ),
51
+ "age": OutputSettings.string_as_datetime("createdAt"),
52
+ },
53
+ )
54
+
55
+
56
+ if __name__ == "__main__":
57
+ typer.run(app())
@@ -8,22 +8,22 @@ from typing import Any
8
8
  import classdiff # type: ignore
9
9
  import typer
10
10
  from validio_sdk import ValidioError
11
+ from validio_sdk._api.api import APIClient
11
12
  from validio_sdk.code import _import as code_import
12
13
  from validio_sdk.code import apply as code_apply
13
14
  from validio_sdk.code import plan as code_plan
14
15
  from validio_sdk.code import scaffold
15
- from validio_sdk.resource._diff import DiffContext, GraphDiff, ResourceUpdate
16
- from validio_sdk.resource._resource import Resource
16
+ from validio_sdk.resource._diff import GraphDiff, ResourceUpdate
17
+ from validio_sdk.resource._resource import DiffContext, Resource, ResourceDeprecation
17
18
  from validio_sdk.resource._util import SourceSchemaReinference
18
19
  from validio_sdk.resource.replacement import (
19
20
  ImmutableFieldReplacementReason,
20
21
  ReplacementReason,
21
22
  )
22
- from validio_sdk.validio_client import ValidioAPIClient
23
23
 
24
- from validio_cli import AsyncTyper, ConfigDir, Namespace, get_client_and_config
25
- from validio_cli.bin.entities.resources import do_move
26
- from validio_cli.components import proceed_with_operation
24
+ from validio_cli import AsyncTyper, ConfigDir, Namespace, get_client
25
+ from validio_cli.bin.entities.resources import ResourceNamesToMove, do_move
26
+ from validio_cli.components import BETA_BANNER, proceed_with_operation
27
27
 
28
28
  """Maximum number of deprecation warnings to show."""
29
29
  MAX_DEPRECATIONS_TO_SHOW = 20
@@ -99,15 +99,16 @@ update_all_schemas_option = typer.Option(
99
99
  ),
100
100
  )
101
101
 
102
- target_option = typer.Option(
103
- [],
104
- help=(
105
- "[red][bold]BETA[/bold][/red] "
106
- "Only target selected resources and ignore changes for others."
107
- "Format is '<type>:<name>', e.g. 'source:my-bq-source' or "
108
- "'window:my-global-window'"
109
- ),
110
- )
102
+
103
+ def target_option(resource_type: str) -> Any:
104
+ return typer.Option(
105
+ [],
106
+ help=(
107
+ f"{BETA_BANNER} "
108
+ f'Only target selected {resource_type.replace("-", " ")}(s) and ignore '
109
+ f"changes for others. E.g. `--{resource_type} resource-a"
110
+ ),
111
+ )
111
112
 
112
113
 
113
114
  @app.command(help="Initialize a new IaC project")
@@ -137,10 +138,16 @@ async def plan(
137
138
  destroy: bool = destroy_option,
138
139
  show_secrets: bool = show_secrets_option,
139
140
  color: bool = color_option,
140
- target: list[str] = target_option,
141
+ channel: list[str] = target_option("channel"),
142
+ credential: list[str] = target_option("credential"),
143
+ notification_rule: list[str] = target_option("notification-rule"),
144
+ segmentation: list[str] = target_option("segmentation"),
145
+ source: list[str] = target_option("source"),
146
+ validator: list[str] = target_option("validator"),
147
+ window: list[str] = target_option("window"),
141
148
  ) -> None:
142
149
  dir_path = directory_or_default(directory)
143
- client, _ = await get_client_and_config(config_dir)
150
+ client, _ = get_client(config_dir)
144
151
 
145
152
  schema_reinference = create_source_schema_reinference(
146
153
  update_schema, update_all_schemas
@@ -158,7 +165,15 @@ async def plan(
158
165
  show_schema=show_schema,
159
166
  diff_output=diff_output,
160
167
  show_secrets=show_secrets,
161
- targets=set(target),
168
+ targets=code_plan.ResourceNames(
169
+ channels=set(channel),
170
+ credentials=set(credential),
171
+ notification_rules=set(notification_rule),
172
+ segmentations=set(segmentation),
173
+ sources=set(source),
174
+ validators=set(validator),
175
+ windows=set(window),
176
+ ),
162
177
  )
163
178
 
164
179
 
@@ -177,10 +192,16 @@ async def apply(
177
192
  destroy: bool = destroy_option,
178
193
  show_secrets: bool = show_secrets_option,
179
194
  color: bool = color_option,
180
- target: list[str] = target_option,
195
+ channel: list[str] = target_option("channel"),
196
+ credential: list[str] = target_option("credential"),
197
+ notification_rule: list[str] = target_option("notification-rule"),
198
+ segmentation: list[str] = target_option("segmentation"),
199
+ source: list[str] = target_option("source"),
200
+ validator: list[str] = target_option("validator"),
201
+ window: list[str] = target_option("window"),
181
202
  ) -> None:
182
203
  dir_path = directory_or_default(directory)
183
- client, _ = await get_client_and_config(config_dir)
204
+ client, _ = get_client(config_dir)
184
205
 
185
206
  schema_reinference = create_source_schema_reinference(
186
207
  update_schema, update_all_schemas
@@ -198,7 +219,15 @@ async def apply(
198
219
  show_schema=show_schema,
199
220
  diff_output=diff_output,
200
221
  show_secrets=show_secrets,
201
- targets=set(target),
222
+ targets=code_plan.ResourceNames(
223
+ channels=set(channel),
224
+ credentials=set(credential),
225
+ notification_rules=set(notification_rule),
226
+ segmentations=set(segmentation),
227
+ sources=set(source),
228
+ validators=set(validator),
229
+ windows=set(window),
230
+ ),
202
231
  )
203
232
 
204
233
  if diff.num_operations() == 0:
@@ -241,33 +270,45 @@ async def _import(
241
270
  None,
242
271
  "--import-namespace",
243
272
  help=(
244
- "If a file is specified, resources in that file will be moved."
245
- " Otherwise all resources in this namespace will be moved."
246
- " If providing file to move resources, this is a mandatory parameter"
273
+ "Shorthand to first 'move' resources into the current "
274
+ "project's namespace before performing the import. "
247
275
  ),
248
276
  ),
249
- file: pathlib.Path = typer.Option(
277
+ credential: list[str] = typer.Option(
250
278
  None,
251
- "--file",
252
- "-f",
253
279
  help=(
254
- "Path to a JSON-encoded file containing a list of resource to be"
255
- " moved into the current project before generating resource declarations."
256
- " If provided, then --import-namespace must be specified"
280
+ f"{BETA_BANNER} Target credential resources to move into "
281
+ f"the current project's namespace. "
282
+ f"This moves any specified credentials together with their "
283
+ f"attached sources, windows, filters, segmentations and validators. "
284
+ f"Only applicable with '--import-namespace'"
285
+ ),
286
+ ),
287
+ channel: list[str] = typer.Option(
288
+ None,
289
+ help=(
290
+ f"{BETA_BANNER} Target channel resources to move into "
291
+ f"the current project's namespace. "
292
+ f"This moves any specified channels together with their "
293
+ f"attached notification rules. "
294
+ f"Only applicable with '--import-namespace'"
257
295
  ),
258
296
  ),
259
297
  auto_approve: bool = typer.Option(
260
298
  False, help="Automatically approve and perform the import operation"
261
299
  ),
262
300
  ) -> None:
263
- if file is not None and import_namespace is None:
301
+ resource_names = ResourceNamesToMove(
302
+ credentials=set(credential), channels=set(channel)
303
+ )
304
+
305
+ if resource_names.is_empty() and import_namespace is None:
264
306
  raise ValidioError(
265
- "--import-namespace must be provided if -f is provided and vice versa"
307
+ "--import-namespace is required when targeting resources to move"
266
308
  )
267
309
 
268
310
  dir_path = directory_or_default(directory)
269
- client, _ = await get_client_and_config(config_dir)
270
-
311
+ client, _ = get_client(config_dir)
271
312
  namespace = get_namespace(dir_path)
272
313
 
273
314
  if import_namespace:
@@ -275,7 +316,7 @@ async def _import(
275
316
  client=client,
276
317
  namespace=import_namespace,
277
318
  target_namespace=namespace,
278
- file=file,
319
+ resource_names=resource_names,
279
320
  auto_approve=auto_approve,
280
321
  )
281
322
  if num_moved_resources is None:
@@ -284,7 +325,7 @@ async def _import(
284
325
  if num_moved_resources > 0:
285
326
  print(f"Moved {num_moved_resources} resources")
286
327
 
287
- diff, manifest_ctx = await _plan(
328
+ diff, _ = await _plan(
288
329
  namespace=namespace,
289
330
  client=client,
290
331
  directory=dir_path,
@@ -295,6 +336,7 @@ async def _import(
295
336
  show_schema=False,
296
337
  show_secrets=False,
297
338
  show_diff=False,
339
+ import_mode=True,
298
340
  )
299
341
 
300
342
  if diff.num_deletes() == 0:
@@ -317,7 +359,7 @@ def directory_or_default(directory: str) -> pathlib.Path:
317
359
 
318
360
  async def _plan(
319
361
  namespace: str,
320
- client: ValidioAPIClient,
362
+ client: APIClient,
321
363
  directory: pathlib.Path,
322
364
  schema_reinference: SourceSchemaReinference,
323
365
  destroy: bool,
@@ -327,7 +369,8 @@ async def _plan(
327
369
  diff_output: DiffOutput = DiffOutput.FULL,
328
370
  show_secrets: bool = False,
329
371
  show_diff: bool = True,
330
- targets: set[str] = set(),
372
+ targets: code_plan.ResourceNames = code_plan.ResourceNames(),
373
+ import_mode: bool = False,
331
374
  ) -> tuple[GraphDiff, DiffContext]:
332
375
  plan_result = await code_plan.plan(
333
376
  namespace=namespace,
@@ -337,7 +380,8 @@ async def _plan(
337
380
  destroy=destroy,
338
381
  no_capture=no_capture,
339
382
  show_secrets=show_secrets,
340
- targets=_create_resource_names(targets),
383
+ targets=targets,
384
+ import_mode=import_mode,
341
385
  )
342
386
 
343
387
  diff, manifest_ctx = plan_result.graph_diff, plan_result.diff_context
@@ -345,16 +389,16 @@ async def _plan(
345
389
  if show_diff:
346
390
  if diff.num_operations() == 0:
347
391
  _print_no_changes()
348
- return diff, manifest_ctx
392
+ else:
393
+ _show_resources_diff(
394
+ diff=diff,
395
+ show_schema=show_schema,
396
+ show_secrets=show_secrets,
397
+ escape=sys.stdout.isatty() and color,
398
+ diff_output=diff_output,
399
+ )
349
400
 
350
- _show_resources_diff(
351
- diff=diff,
352
- show_schema=show_schema,
353
- show_secrets=show_secrets,
354
- escape=sys.stdout.isatty() and color,
355
- diff_output=diff_output,
356
- deprecations=plan_result.deprecations,
357
- )
401
+ _show_deprecations(plan_result.deprecations)
358
402
 
359
403
  return diff, manifest_ctx
360
404
 
@@ -365,7 +409,6 @@ def _show_resources_diff(
365
409
  show_secrets: bool,
366
410
  escape: bool,
367
411
  diff_output: DiffOutput,
368
- deprecations: list[code_plan.ResourceDeprecation],
369
412
  ) -> None:
370
413
  resource_types = DiffContext.fields()
371
414
  if diff.num_creates() > 0:
@@ -410,8 +453,6 @@ def _show_resources_diff(
410
453
  diff_output,
411
454
  )
412
455
 
413
- _show_deprecations(deprecations)
414
-
415
456
  print(
416
457
  "\n"
417
458
  f"Plan: {diff.num_creates()} to create, {diff.num_updates()} to update, "
@@ -477,7 +518,14 @@ def _show_update_resource_diff(
477
518
  for r in resources.values():
478
519
  class_key = r.manifest.__class__.__name__
479
520
 
480
- print(f"{class_key} '{r.manifest.name}' will be updated")
521
+ cascade_update = ""
522
+ if r.replacement_cascaded_update_parent:
523
+ (cls, parent_name) = r.replacement_cascaded_update_parent
524
+ cascade_update = (
525
+ f" (due to replacement of " f"{cls.__name__}({repr(parent_name)}))"
526
+ )
527
+
528
+ print(f"{class_key} '{r.manifest.name}' will be updated{cascade_update}")
481
529
 
482
530
  rewrites = _diff_field_rewrites(show_schema)
483
531
 
@@ -573,7 +621,7 @@ def _diff_field_rewrites(show_schema: bool) -> dict[str, Any]:
573
621
  return {} if show_schema else {"jtd_schema": "[NOT SHOWN]"}
574
622
 
575
623
 
576
- def _show_deprecations(deprecations: list[code_plan.ResourceDeprecation]) -> None:
624
+ def _show_deprecations(deprecations: list[ResourceDeprecation]) -> None:
577
625
  if not deprecations:
578
626
  return
579
627
 
@@ -601,32 +649,9 @@ def create_source_schema_reinference(
601
649
  elif len(schemas_to_update) > 0:
602
650
  source_names = schemas_to_update
603
651
 
604
- return SourceSchemaReinference(set(source_names) if source_names else None)
605
-
606
-
607
- def _create_resource_names(strings: set[str]) -> code_plan.ResourceNames:
608
- """Construct object from a set of strings.
609
-
610
- Strings in format <resource_type>:<resource_name> will be converted by
611
- splitting on `:` and parsing the values.
612
- """
613
- resource_names = code_plan.ResourceNames()
614
-
615
- for target in strings:
616
- # A resource name can't contain `:` so it's safe to split like this.
617
- resource_type, name = target.split(":")
618
-
619
- # User specifies resources in singular (credential) but the class fields
620
- # throughout the code uses plural so we add an 's'
621
- resource_type = f"{resource_type}s"
622
-
623
- if not hasattr(resource_names, resource_type):
624
- raise ValidioError(f"Invalid resource type '{resource_type}'")
625
-
626
- v = getattr(resource_names, resource_type)
627
- v.add(name)
628
-
629
- return resource_names
652
+ return SourceSchemaReinference(
653
+ set(source_names) if source_names is not None else None
654
+ )
630
655
 
631
656
 
632
657
  def _print_no_changes() -> None:
@@ -1,19 +1,18 @@
1
1
  from pathlib import Path
2
2
  from typing import Any
3
3
 
4
- import httpx
5
4
  import typer
6
5
  from prompt_toolkit import PromptSession
7
6
  from prompt_toolkit.completion import WordCompleter
8
7
  from prompt_toolkit.shortcuts import confirm
9
8
  from tabulate import tabulate
9
+ from validio_sdk._api.api import APIClient
10
10
  from validio_sdk.config import (
11
11
  Config,
12
12
  ConfigInvalidError,
13
13
  ConfigNotFoundError,
14
14
  ValidioConfig,
15
15
  )
16
- from validio_sdk.validio_client import UnauthorizedError, ValidioAPIClient
17
16
 
18
17
  import validio_cli
19
18
  from validio_cli import (
@@ -178,14 +177,5 @@ async def _prompt_for_config(current_config: ValidioConfig | None) -> ValidioCon
178
177
 
179
178
 
180
179
  async def _test_configuration(config: ValidioConfig) -> str | None:
181
- vc = ValidioAPIClient(validio_config=config)
182
-
183
- try:
184
- # NOTE: Do we need an explicit ping command here?
185
- await vc.list_users()
186
- except UnauthorizedError:
187
- return "Unauthorized"
188
- except httpx.ConnectError:
189
- return "Failed to connect to server"
190
-
191
- return None
180
+ vc = APIClient(config=config)
181
+ return await vc.test_connection()