validio-cli 7.5.0__tar.gz → 8.1.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.
- {validio_cli-7.5.0 → validio_cli-8.1.0}/PKG-INFO +7 -3
- {validio_cli-7.5.0 → validio_cli-8.1.0}/pyproject.toml +1 -1
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/__init__.py +0 -8
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/code.py +23 -18
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/namespaces.py +77 -24
- {validio_cli-7.5.0 → validio_cli-8.1.0}/LICENSE +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/README_PUBLIC.md +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/channels.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/config.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/credentials.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/dbt.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/filters.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/incidents.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/metrics.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/notification_rules.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/resources.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/segmentations.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/segments.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/sources.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/users.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/validators.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/entities/windows.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/bin/main.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/components.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/metadata.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/namespace.py +0 -0
- {validio_cli-7.5.0 → validio_cli-8.1.0}/validio_cli/schema.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: validio-cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 8.1.0
|
|
4
4
|
Summary: CLI tool to interact with the Validio platform
|
|
5
|
-
Home-page: https://validio.io/
|
|
6
5
|
License: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
7
|
Author: Validio
|
|
8
8
|
Author-email: support@validio.io
|
|
9
9
|
Requires-Python: >=3.10,<4.0
|
|
@@ -11,6 +11,9 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
17
|
Requires-Dist: camel-converter (>=3.0.0,<4.0.0)
|
|
15
18
|
Requires-Dist: classdiff (>=0.5.2,<0.6.0)
|
|
16
19
|
Requires-Dist: prompt-toolkit (>=3.0.38,<4.0.0)
|
|
@@ -19,6 +22,7 @@ Requires-Dist: typer[all] (>=0.8.0,<0.16.0)
|
|
|
19
22
|
Requires-Dist: types-tabulate (>=0.9.0.2,<0.10.0.0)
|
|
20
23
|
Requires-Dist: validio-sdk
|
|
21
24
|
Project-URL: Documentation, https://docs.validio.io/
|
|
25
|
+
Project-URL: Homepage, https://validio.io/
|
|
22
26
|
Description-Content-Type: text/markdown
|
|
23
27
|
|
|
24
28
|
# validio-cli
|
|
@@ -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 = "
|
|
6
|
+
version = "8.1.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"
|
|
@@ -25,14 +25,6 @@ T = TypeVar("T", bound="OutputSettings")
|
|
|
25
25
|
DEFAULT_NAMESPACE: str = "default"
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
class Role(str, Enum):
|
|
29
|
-
"""User roles."""
|
|
30
|
-
|
|
31
|
-
ADMIN = "ADMIN"
|
|
32
|
-
EDITOR = "EDITOR"
|
|
33
|
-
VIEWER = "VIEWER"
|
|
34
|
-
|
|
35
|
-
|
|
36
28
|
class OutputFormat(str, Enum):
|
|
37
29
|
"""Available output formats for the CLI"""
|
|
38
30
|
|
|
@@ -17,6 +17,7 @@ from validio_sdk.resource._resource import DiffContext, Resource, ResourceDeprec
|
|
|
17
17
|
from validio_sdk.resource._util import SourceSchemaReinference
|
|
18
18
|
from validio_sdk.resource.replacement import (
|
|
19
19
|
ImmutableFieldReplacementReason,
|
|
20
|
+
RecreateOnChangeReplacementReason,
|
|
20
21
|
ReplacementReason,
|
|
21
22
|
)
|
|
22
23
|
|
|
@@ -101,7 +102,6 @@ update_all_schemas_option = typer.Option(
|
|
|
101
102
|
show_progress_option = typer.Option(
|
|
102
103
|
sys.stdout.isatty(),
|
|
103
104
|
help=(
|
|
104
|
-
f"{BETA_BANNER} "
|
|
105
105
|
"If enabled, progress updates will be displayed during "
|
|
106
106
|
"execution of this command"
|
|
107
107
|
),
|
|
@@ -111,8 +111,8 @@ show_progress_option = typer.Option(
|
|
|
111
111
|
def target_option(resource_type: str) -> Any:
|
|
112
112
|
return typer.Option(
|
|
113
113
|
[],
|
|
114
|
+
f"--{resource_type}",
|
|
114
115
|
help=(
|
|
115
|
-
f"{BETA_BANNER} "
|
|
116
116
|
f"Only target selected {resource_type.replace('-', ' ')}(s) and ignore "
|
|
117
117
|
f"changes for others. E.g. `--{resource_type} resource-a"
|
|
118
118
|
),
|
|
@@ -148,6 +148,7 @@ async def plan(
|
|
|
148
148
|
color: bool = color_option,
|
|
149
149
|
channel: list[str] = target_option("channel"),
|
|
150
150
|
credential: list[str] = target_option("credential"),
|
|
151
|
+
filter_: list[str] = target_option("filter"),
|
|
151
152
|
notification_rule: list[str] = target_option("notification-rule"),
|
|
152
153
|
segmentation: list[str] = target_option("segmentation"),
|
|
153
154
|
source: list[str] = target_option("source"),
|
|
@@ -177,6 +178,7 @@ async def plan(
|
|
|
177
178
|
targets=code_plan.ResourceNames(
|
|
178
179
|
channels=set(channel),
|
|
179
180
|
credentials=set(credential),
|
|
181
|
+
filters=set(filter_),
|
|
180
182
|
notification_rules=set(notification_rule),
|
|
181
183
|
segmentations=set(segmentation),
|
|
182
184
|
sources=set(source),
|
|
@@ -204,6 +206,7 @@ async def apply(
|
|
|
204
206
|
color: bool = color_option,
|
|
205
207
|
channel: list[str] = target_option("channel"),
|
|
206
208
|
credential: list[str] = target_option("credential"),
|
|
209
|
+
filter_: list[str] = target_option("filter"),
|
|
207
210
|
notification_rule: list[str] = target_option("notification-rule"),
|
|
208
211
|
segmentation: list[str] = target_option("segmentation"),
|
|
209
212
|
source: list[str] = target_option("source"),
|
|
@@ -212,14 +215,12 @@ async def apply(
|
|
|
212
215
|
progress: bool = show_progress_option,
|
|
213
216
|
test_credentials: bool = typer.Option(
|
|
214
217
|
True,
|
|
215
|
-
help=
|
|
216
|
-
f"{BETA_BANNER} Test that credentials are valid before creating or updating"
|
|
217
|
-
),
|
|
218
|
+
help="Test that credentials are valid before creating or updating",
|
|
218
219
|
),
|
|
219
220
|
dry_run_sql: bool = typer.Option(
|
|
220
221
|
False,
|
|
221
|
-
help=
|
|
222
|
-
|
|
222
|
+
help="""
|
|
223
|
+
Dry run SQL queries before creating/updating SQL Filters,
|
|
223
224
|
SQL Sources or SQL Validators.
|
|
224
225
|
Note: this feature is unsupported on Azure based warehouses.
|
|
225
226
|
""",
|
|
@@ -247,6 +248,7 @@ async def apply(
|
|
|
247
248
|
targets=code_plan.ResourceNames(
|
|
248
249
|
channels=set(channel),
|
|
249
250
|
credentials=set(credential),
|
|
251
|
+
filters=set(filter_),
|
|
250
252
|
notification_rules=set(notification_rule),
|
|
251
253
|
segmentations=set(segmentation),
|
|
252
254
|
sources=set(source),
|
|
@@ -307,21 +309,21 @@ async def _import(
|
|
|
307
309
|
credential: list[str] = typer.Option(
|
|
308
310
|
[],
|
|
309
311
|
help=(
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
312
|
+
"Target credential resources to move into "
|
|
313
|
+
"the current project's namespace. "
|
|
314
|
+
"This moves any specified credentials together with their "
|
|
315
|
+
"attached sources, windows, filters, segmentations and validators. "
|
|
316
|
+
"Only applicable with '--import-namespace'"
|
|
315
317
|
),
|
|
316
318
|
),
|
|
317
319
|
channel: list[str] = typer.Option(
|
|
318
320
|
[],
|
|
319
321
|
help=(
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
322
|
+
"Target channel resources to move into "
|
|
323
|
+
"the current project's namespace. "
|
|
324
|
+
"This moves any specified channels together with their "
|
|
325
|
+
"attached notification rules. "
|
|
326
|
+
"Only applicable with '--import-namespace'"
|
|
325
327
|
),
|
|
326
328
|
),
|
|
327
329
|
progress: bool = show_progress_option,
|
|
@@ -598,7 +600,10 @@ def _show_replace_resource_diff(
|
|
|
598
600
|
|
|
599
601
|
rewrites = _diff_field_rewrites(show_schema)
|
|
600
602
|
a_value, b_value = (None, None)
|
|
601
|
-
if isinstance(
|
|
603
|
+
if isinstance(
|
|
604
|
+
reason,
|
|
605
|
+
(ImmutableFieldReplacementReason, RecreateOnChangeReplacementReason),
|
|
606
|
+
):
|
|
602
607
|
update = reason.resource_update
|
|
603
608
|
a_value = code_plan._create_resource_diff_object(
|
|
604
609
|
update.manifest,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
from typing import Optional
|
|
1
|
+
from typing import Any, Optional
|
|
2
2
|
|
|
3
|
+
import click
|
|
3
4
|
import typer
|
|
4
5
|
from validio_sdk import ValidioError
|
|
5
6
|
|
|
@@ -9,12 +10,27 @@ from validio_cli import (
|
|
|
9
10
|
OutputFormat,
|
|
10
11
|
OutputFormatOption,
|
|
11
12
|
OutputSettings,
|
|
12
|
-
Role,
|
|
13
13
|
get_client,
|
|
14
14
|
output_json,
|
|
15
15
|
output_text,
|
|
16
16
|
)
|
|
17
17
|
|
|
18
|
+
|
|
19
|
+
class RoleSuggestion(click.ParamType):
|
|
20
|
+
name = "role"
|
|
21
|
+
|
|
22
|
+
def get_metavar(self, _: click.Parameter) -> str | None:
|
|
23
|
+
return "[Admin|Editor|Viewer|<Custom>]"
|
|
24
|
+
|
|
25
|
+
def convert(
|
|
26
|
+
self,
|
|
27
|
+
value: Any,
|
|
28
|
+
_param: click.Parameter | None,
|
|
29
|
+
_ctx: click.Context | None,
|
|
30
|
+
) -> Any:
|
|
31
|
+
return value
|
|
32
|
+
|
|
33
|
+
|
|
18
34
|
app = AsyncTyper(help="Namespaces used to separate resources")
|
|
19
35
|
|
|
20
36
|
|
|
@@ -40,12 +56,16 @@ async def get(
|
|
|
40
56
|
)
|
|
41
57
|
|
|
42
58
|
|
|
43
|
-
@app.async_command(help="
|
|
59
|
+
@app.async_command(help="Create a namespace")
|
|
44
60
|
async def create(
|
|
45
61
|
config_dir: str = ConfigDir,
|
|
46
62
|
output_format: OutputFormat = OutputFormatOption,
|
|
47
63
|
identifier: str = typer.Argument(..., help="Namespace name"),
|
|
48
|
-
role:
|
|
64
|
+
role: str = typer.Option(
|
|
65
|
+
None,
|
|
66
|
+
click_type=RoleSuggestion(),
|
|
67
|
+
help="The role to assign --api-key and --member",
|
|
68
|
+
),
|
|
49
69
|
api_key: list[str] = typer.Option(
|
|
50
70
|
[],
|
|
51
71
|
"--api-key",
|
|
@@ -68,19 +88,23 @@ async def create(
|
|
|
68
88
|
"--role is specified but no --member or --api-key to assign the role"
|
|
69
89
|
)
|
|
70
90
|
|
|
91
|
+
role_configs = await vc.get_namespace_role_configs()
|
|
92
|
+
|
|
71
93
|
ns_api_keys = [
|
|
72
94
|
{
|
|
73
95
|
"id": cfg.access_key,
|
|
74
|
-
"
|
|
96
|
+
"roleId": _find_admin_role_id(role_configs),
|
|
75
97
|
}
|
|
76
98
|
]
|
|
77
99
|
|
|
78
|
-
|
|
79
|
-
|
|
100
|
+
if role:
|
|
101
|
+
role_id = _find_role_id(role_configs, role)
|
|
102
|
+
for ak in api_key:
|
|
103
|
+
ns_api_keys.append({"id": ak, "roleId": role_id})
|
|
80
104
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
ns_members
|
|
105
|
+
ns_members = [{"id": m, "roleId": role_id} for m in member]
|
|
106
|
+
else:
|
|
107
|
+
ns_members = []
|
|
84
108
|
|
|
85
109
|
namespaces = await vc.create_namespace(
|
|
86
110
|
namespace_id=identifier,
|
|
@@ -101,12 +125,16 @@ async def create(
|
|
|
101
125
|
)
|
|
102
126
|
|
|
103
127
|
|
|
104
|
-
@app.async_command(help="
|
|
128
|
+
@app.async_command(help="Update a namespace")
|
|
105
129
|
async def update(
|
|
106
130
|
config_dir: str = ConfigDir,
|
|
107
131
|
output_format: OutputFormat = OutputFormatOption,
|
|
108
132
|
identifier: str = typer.Argument(..., help="Namespace name"),
|
|
109
|
-
role:
|
|
133
|
+
role: str = typer.Option(
|
|
134
|
+
None,
|
|
135
|
+
click_type=RoleSuggestion(),
|
|
136
|
+
help="The role to assign --api-key and --member",
|
|
137
|
+
),
|
|
110
138
|
api_key: list[str] = typer.Option(
|
|
111
139
|
[],
|
|
112
140
|
"--api-key",
|
|
@@ -128,13 +156,16 @@ async def update(
|
|
|
128
156
|
"--role is specified but no --member or --api-key to assign the role"
|
|
129
157
|
)
|
|
130
158
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
ns_api_keys.append({"id": ak, "role": role})
|
|
159
|
+
role_configs = await vc.get_namespace_role_configs()
|
|
160
|
+
ns_api_keys, ns_members = [], []
|
|
134
161
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
162
|
+
if role:
|
|
163
|
+
role_id = _find_role_id(role_configs, role)
|
|
164
|
+
for ak in api_key:
|
|
165
|
+
ns_api_keys.append({"id": ak, "roleId": role_id})
|
|
166
|
+
|
|
167
|
+
for m in member:
|
|
168
|
+
ns_members.append({"id": m, "roleId": role_id})
|
|
138
169
|
|
|
139
170
|
namespace = await vc.get_namespaces(identifier)
|
|
140
171
|
if namespace is None:
|
|
@@ -144,15 +175,21 @@ async def update(
|
|
|
144
175
|
raise ValidioError("failed to get namespace")
|
|
145
176
|
|
|
146
177
|
api_keys = [
|
|
147
|
-
{
|
|
148
|
-
|
|
149
|
-
|
|
178
|
+
{
|
|
179
|
+
"id": ns_roles["apiKey"]["id"],
|
|
180
|
+
"roleId": _find_role_id(role_configs, ns_roles["role"]),
|
|
181
|
+
}
|
|
182
|
+
for ns_roles in namespace.get("apiKeys", {})
|
|
183
|
+
if ns_roles["apiKey"]["id"] not in api_key
|
|
150
184
|
] + ns_api_keys
|
|
151
185
|
|
|
152
186
|
members = [
|
|
153
|
-
{
|
|
154
|
-
|
|
155
|
-
|
|
187
|
+
{
|
|
188
|
+
"id": ns_roles["user"]["id"],
|
|
189
|
+
"roleId": _find_role_id(role_configs, ns_roles["role"]),
|
|
190
|
+
}
|
|
191
|
+
for ns_roles in namespace["members"]
|
|
192
|
+
if ns_roles["user"]["id"] not in member
|
|
156
193
|
] + ns_members
|
|
157
194
|
|
|
158
195
|
namespaces = await vc.update_namespace_roles(
|
|
@@ -194,3 +231,19 @@ async def remove(
|
|
|
194
231
|
"status": OutputSettings(pass_full_object=True, reformat=lambda _: status),
|
|
195
232
|
},
|
|
196
233
|
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _find_role_id(role_configs: list[dict[str, Any]], role_name: str) -> str:
|
|
237
|
+
for rc in role_configs:
|
|
238
|
+
if rc["name"].upper() == role_name.upper():
|
|
239
|
+
return rc["id"]
|
|
240
|
+
|
|
241
|
+
raise ValidioError(f"role '{role_name}' not found")
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _find_admin_role_id(role_configs: list[dict[str, Any]]) -> str:
|
|
245
|
+
for rc in role_configs:
|
|
246
|
+
if rc.get("isSystemRole") and rc["name"] == "Admin":
|
|
247
|
+
return rc["id"]
|
|
248
|
+
|
|
249
|
+
raise ValidioError("could not find system Admin role for namespace")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|