validio-cli 0.20.2__tar.gz → 0.22.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 (27) hide show
  1. {validio_cli-0.20.2 → validio_cli-0.22.0}/PKG-INFO +1 -1
  2. {validio_cli-0.20.2 → validio_cli-0.22.0}/pyproject.toml +1 -1
  3. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/code.py +67 -0
  4. validio_cli-0.22.0/validio_cli/bin/entities/namespaces.py +203 -0
  5. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/users.py +2 -6
  6. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/main.py +2 -0
  7. {validio_cli-0.20.2 → validio_cli-0.22.0}/LICENSE +0 -0
  8. {validio_cli-0.20.2 → validio_cli-0.22.0}/README_PUBLIC.md +0 -0
  9. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/__init__.py +0 -0
  10. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/channels.py +0 -0
  11. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/config.py +0 -0
  12. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/credentials.py +0 -0
  13. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/dbt.py +0 -0
  14. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/incidents.py +0 -0
  15. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/metrics.py +0 -0
  16. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/notification_rules.py +0 -0
  17. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/recommendations.py +0 -0
  18. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/resources.py +0 -0
  19. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/segmentations.py +0 -0
  20. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/segments.py +0 -0
  21. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/sources.py +0 -0
  22. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/validators.py +0 -0
  23. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/bin/entities/windows.py +0 -0
  24. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/components.py +0 -0
  25. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/metadata.py +0 -0
  26. {validio_cli-0.20.2 → validio_cli-0.22.0}/validio_cli/namespace.py +0 -0
  27. {validio_cli-0.20.2 → validio_cli-0.22.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.20.2
3
+ Version: 0.22.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.20.2"
6
+ version = "0.22.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"
@@ -15,6 +15,10 @@ from validio_sdk.code import scaffold
15
15
  from validio_sdk.resource._diff import DiffContext, GraphDiff, ResourceUpdate
16
16
  from validio_sdk.resource._resource import Resource
17
17
  from validio_sdk.resource._util import SourceSchemaReinference
18
+ from validio_sdk.resource.replacement import (
19
+ ImmutableFieldReplacementReason,
20
+ ReplacementReason,
21
+ )
18
22
  from validio_sdk.validio_client import ValidioAPIClient
19
23
 
20
24
  from validio_cli import AsyncTyper, ConfigDir, Namespace, get_client_and_config
@@ -368,6 +372,7 @@ def _show_resources_diff(
368
372
  for t in resource_types:
369
373
  _show_create_resource_diff(
370
374
  getattr(diff.to_create, t),
375
+ getattr(diff.replacement_ctx, t),
371
376
  show_schema,
372
377
  show_secrets,
373
378
  escape,
@@ -378,6 +383,7 @@ def _show_resources_diff(
378
383
  for t in resource_types:
379
384
  _show_delete_resource_diff(
380
385
  getattr(diff.to_delete, t),
386
+ getattr(diff.replacement_ctx, t),
381
387
  show_schema,
382
388
  show_secrets,
383
389
  escape,
@@ -394,6 +400,16 @@ def _show_resources_diff(
394
400
  diff_output=diff_output,
395
401
  )
396
402
 
403
+ for t in resource_types:
404
+ _show_replace_resource_diff(
405
+ getattr(diff.to_create, t),
406
+ getattr(diff.replacement_ctx, t),
407
+ show_schema,
408
+ show_secrets,
409
+ escape,
410
+ diff_output,
411
+ )
412
+
397
413
  _show_deprecations(deprecations)
398
414
 
399
415
  print(
@@ -405,12 +421,16 @@ def _show_resources_diff(
405
421
 
406
422
  def _show_create_resource_diff(
407
423
  resources: Mapping[str, Resource],
424
+ replaced_resources: Mapping[str, ReplacementReason],
408
425
  show_schema: bool,
409
426
  show_secrets: bool,
410
427
  color: bool,
411
428
  diff_output: DiffOutput,
412
429
  ) -> None:
413
430
  for r in resources.values():
431
+ if r.name in replaced_resources:
432
+ continue
433
+
414
434
  class_key = r.__class__.__name__
415
435
 
416
436
  print(f"{class_key} '{r.name}' will be created")
@@ -425,12 +445,16 @@ def _show_create_resource_diff(
425
445
 
426
446
  def _show_delete_resource_diff(
427
447
  resources: Mapping[str, Resource],
448
+ replaced_resources: Mapping[str, ReplacementReason],
428
449
  show_schema: bool,
429
450
  show_secrets: bool,
430
451
  color: bool,
431
452
  diff_output: DiffOutput,
432
453
  ) -> None:
433
454
  for r in resources.values():
455
+ if r.name in replaced_resources:
456
+ continue
457
+
434
458
  class_key = r.__class__.__name__
435
459
 
436
460
  print(f"{class_key} '{r.name}' will be deleted")
@@ -475,6 +499,49 @@ def _show_update_resource_diff(
475
499
  _show_diff(b_value, a_value, color, diff_output, class_key)
476
500
 
477
501
 
502
+ def _show_replace_resource_diff(
503
+ resources: Mapping[str, Resource],
504
+ replaced_resources: Mapping[str, ReplacementReason],
505
+ show_schema: bool,
506
+ show_secrets: bool,
507
+ color: bool,
508
+ diff_output: DiffOutput,
509
+ ) -> None:
510
+ for r in resources.values():
511
+ reason = replaced_resources.get(r.name)
512
+ if not reason:
513
+ continue
514
+
515
+ class_key = r.__class__.__name__
516
+
517
+ print(f"{class_key} '{r.name}' will be replaced: {reason._reason()}")
518
+
519
+ rewrites = _diff_field_rewrites(show_schema)
520
+ a_value, b_value = (None, None)
521
+ if isinstance(reason, ImmutableFieldReplacementReason):
522
+ update = reason.resource_update
523
+ a_value = code_plan._create_resource_diff_object(
524
+ update.manifest,
525
+ show_secrets=show_secrets,
526
+ rewrites=rewrites,
527
+ secrets_changed=update.secrets_changed,
528
+ is_manifest=True,
529
+ )
530
+ b_value = code_plan._create_resource_diff_object(
531
+ update.server,
532
+ show_secrets=show_secrets,
533
+ rewrites=rewrites,
534
+ secrets_changed=update.secrets_changed,
535
+ is_manifest=False,
536
+ )
537
+ else:
538
+ b_value = code_plan._create_resource_diff_object(
539
+ r, rewrites=rewrites, show_secrets=show_secrets
540
+ )
541
+
542
+ _show_diff(a_value, b_value, color, diff_output, class_key)
543
+
544
+
478
545
  def _show_diff(
479
546
  current_value: Any,
480
547
  new_value: Any,
@@ -0,0 +1,203 @@
1
+ from typing import Optional
2
+
3
+ import typer
4
+ from validio_sdk import ValidioError
5
+ from validio_sdk.graphql_client.enums import Role
6
+ from validio_sdk.graphql_client.input_types import (
7
+ NamespaceCreateInput,
8
+ NamespaceRolesUpdateInput,
9
+ NamespaceRoleWithId,
10
+ )
11
+
12
+ from validio_cli import (
13
+ AsyncTyper,
14
+ ConfigDir,
15
+ OutputFormat,
16
+ OutputFormatOption,
17
+ OutputSettings,
18
+ _single_resource_if_specified,
19
+ get_client_and_config,
20
+ output_json,
21
+ output_text,
22
+ )
23
+
24
+ app = AsyncTyper(help="Namespaces used to separate resources")
25
+
26
+
27
+ @app.async_command(help="Get namespaces")
28
+ async def get(
29
+ config_dir: str = ConfigDir,
30
+ output_format: OutputFormat = OutputFormatOption,
31
+ identifier: str = typer.Argument(default=None, help="Namespace name"),
32
+ ) -> None:
33
+ vc, _ = await get_client_and_config(config_dir)
34
+
35
+ namespaces = await vc.list_namespaces()
36
+ # TODO(UI-2311): Fully support list/get/get_by_resource_name
37
+ namespaces = _single_resource_if_specified(namespaces, identifier)
38
+
39
+ if output_format == OutputFormat.JSON:
40
+ return output_json(namespaces, identifier)
41
+
42
+ return output_text(
43
+ namespaces,
44
+ fields={
45
+ "name": OutputSettings(attribute_name="id"),
46
+ },
47
+ )
48
+
49
+
50
+ @app.async_command(help="[red][bold]BETA[/bold][/red] Create a namespace")
51
+ async def create(
52
+ config_dir: str = ConfigDir,
53
+ output_format: OutputFormat = OutputFormatOption,
54
+ identifier: str = typer.Argument(..., help="Namespace name"),
55
+ role: Role = typer.Option(None, help="The role to assign --api-key and --member"),
56
+ api_key: list[str] = typer.Option(
57
+ [],
58
+ "--api-key",
59
+ help="API key to assign the --role. Can be specified multiple times",
60
+ ),
61
+ member: list[str] = typer.Option(
62
+ [],
63
+ "--member",
64
+ help="Member id to assign to the --role. Can be specified multiple times",
65
+ ),
66
+ name: Optional[str] = None,
67
+ ) -> None:
68
+ vc, cfg = await get_client_and_config(config_dir)
69
+
70
+ if (member or api_key) and not role:
71
+ raise ValidioError("--role is required when specifying --member or --api-key")
72
+
73
+ if role and not (member or api_key):
74
+ raise ValidioError(
75
+ "--role is specified but no --member or --api-key to assign the role"
76
+ )
77
+
78
+ ns_api_keys = [
79
+ NamespaceRoleWithId(
80
+ id=cfg.access_key,
81
+ role=Role.ADMIN,
82
+ )
83
+ ]
84
+ for ak in api_key:
85
+ ns_api_keys.append(NamespaceRoleWithId(id=ak, role=role))
86
+
87
+ ns_members = []
88
+ for m in member:
89
+ ns_members.append(NamespaceRoleWithId(id=m, role=role))
90
+
91
+ namespaces = await vc.create_namespace(
92
+ NamespaceCreateInput(
93
+ id=identifier,
94
+ name=name or identifier,
95
+ members=ns_members,
96
+ api_keys=ns_api_keys,
97
+ )
98
+ )
99
+
100
+ if output_format == OutputFormat.JSON:
101
+ return output_json(namespaces, identifier)
102
+
103
+ return output_text(
104
+ namespaces.namespace,
105
+ fields={
106
+ "id": None,
107
+ "name": None,
108
+ },
109
+ )
110
+
111
+
112
+ @app.async_command(help="[red][bold]BETA[/bold][/red] Update a namespace")
113
+ async def update(
114
+ config_dir: str = ConfigDir,
115
+ output_format: OutputFormat = OutputFormatOption,
116
+ identifier: str = typer.Argument(..., help="Namespace name"),
117
+ role: Role = typer.Option(None, help="The role to assign --api-key and --member"),
118
+ api_key: list[str] = typer.Option(
119
+ [],
120
+ "--api-key",
121
+ help="API key to assign the --role. Can be specified multiple times",
122
+ ),
123
+ member: list[str] = typer.Option(
124
+ [],
125
+ "--member",
126
+ help="Member id to assign to the --role. Can be specified multiple times",
127
+ ),
128
+ ) -> None:
129
+ vc, _ = await get_client_and_config(config_dir)
130
+
131
+ if (member or api_key) and not role:
132
+ raise ValidioError("--role is required when specifying --member or --api-key")
133
+
134
+ if role and not (member or api_key):
135
+ raise ValidioError(
136
+ "--role is specified but no --member or --api-key to assign the role"
137
+ )
138
+
139
+ ns_api_keys = []
140
+ for ak in api_key:
141
+ ns_api_keys.append(NamespaceRoleWithId(id=ak, role=role))
142
+
143
+ ns_members = []
144
+ for m in member:
145
+ ns_members.append(NamespaceRoleWithId(id=m, role=role))
146
+
147
+ namespace = await vc.get_namespace(identifier)
148
+ if namespace is None:
149
+ raise ValidioError(f"namespace '{identifier}' not found")
150
+
151
+ api_keys = [
152
+ NamespaceRoleWithId(id=x.api_key.id, role=x.role)
153
+ for x in namespace.api_keys
154
+ if x.api_key.id not in api_key
155
+ ] + ns_api_keys
156
+
157
+ members = [
158
+ NamespaceRoleWithId(id=x.user.id, role=x.role)
159
+ for x in namespace.members
160
+ if x.user.id not in member
161
+ ] + ns_members
162
+
163
+ namespaces = await vc.update_namespace_roles(
164
+ NamespaceRolesUpdateInput(
165
+ namespace_id=identifier,
166
+ members=members,
167
+ api_keys=api_keys,
168
+ )
169
+ )
170
+
171
+ if output_format == OutputFormat.JSON:
172
+ return output_json(namespaces, identifier)
173
+
174
+ return output_text(
175
+ namespaces.namespace,
176
+ fields={
177
+ "id": None,
178
+ "name": None,
179
+ },
180
+ )
181
+
182
+
183
+ @app.async_command(help="Remove a namespace")
184
+ async def remove(
185
+ config_dir: str = ConfigDir,
186
+ output_format: OutputFormat = OutputFormatOption,
187
+ identifier: str = typer.Argument(..., help="Namespace name"),
188
+ ) -> None:
189
+ vc, _ = await get_client_and_config(config_dir)
190
+
191
+ result = await vc.delete_namespaces(ids=[identifier])
192
+
193
+ if output_format == OutputFormat.JSON:
194
+ return output_json(result, identifier)
195
+
196
+ status = "OK" if len(result.errors) == 0 else "ERROR"
197
+
198
+ return output_text(
199
+ {"status": status},
200
+ fields={
201
+ "status": OutputSettings(pass_full_object=True, reformat=lambda _: status),
202
+ },
203
+ )
@@ -4,7 +4,6 @@ from validio_cli import (
4
4
  AsyncTyper,
5
5
  ConfigDir,
6
6
  Identifier,
7
- Namespace,
8
7
  OutputFormat,
9
8
  OutputFormatOption,
10
9
  OutputSettings,
@@ -12,7 +11,6 @@ from validio_cli import (
12
11
  output_json,
13
12
  output_text,
14
13
  )
15
- from validio_cli.namespace import get_namespace
16
14
 
17
15
  app = AsyncTyper(help="Users in the Validio platform")
18
16
 
@@ -21,7 +19,6 @@ app = AsyncTyper(help="Users in the Validio platform")
21
19
  async def get(
22
20
  config_dir: str = ConfigDir,
23
21
  output_format: OutputFormat = OutputFormatOption,
24
- namespace: str = Namespace(),
25
22
  identifier: str = Identifier,
26
23
  ) -> None:
27
24
  vc, cfg = await get_client_and_config(config_dir)
@@ -30,7 +27,6 @@ async def get(
30
27
  users = [
31
28
  await vc.get_user_by_resource_name(
32
29
  resource_name=identifier,
33
- namespace_id=get_namespace(namespace, cfg),
34
30
  )
35
31
  ]
36
32
  else:
@@ -43,8 +39,8 @@ async def get(
43
39
  users,
44
40
  fields={
45
41
  "name": OutputSettings(attribute_name="resource_name"),
46
- "role": None,
47
- "status": None,
42
+ "global_role": OutputSettings(reformat=lambda x: x.value),
43
+ "status": OutputSettings(reformat=lambda x: x.value),
48
44
  "identities": OutputSettings(reformat=lambda x: len(x)),
49
45
  "age": OutputSettings(attribute_name="created_at"),
50
46
  },
@@ -25,6 +25,7 @@ from validio_cli.bin.entities import (
25
25
  dbt,
26
26
  incidents,
27
27
  metrics,
28
+ namespaces,
28
29
  notification_rules,
29
30
  recommendations,
30
31
  resources,
@@ -50,6 +51,7 @@ app.add_typer(credentials.app, no_args_is_help=True, name="credentials")
50
51
  app.add_typer(dbt.app, no_args_is_help=True, name="dbt")
51
52
  app.add_typer(incidents.app, no_args_is_help=True, name="incidents")
52
53
  app.add_typer(metrics.app, no_args_is_help=True, name="metrics")
54
+ app.add_typer(namespaces.app, no_args_is_help=True, name="namespaces")
53
55
  app.add_typer(resources.app, no_args_is_help=True, name="resources")
54
56
  app.add_typer(notification_rules.app, no_args_is_help=True, name="notification-rules")
55
57
  app.add_typer(recommendations.app, no_args_is_help=True, name="recommendations")
File without changes