validio-cli 0.25.2__tar.gz → 0.26.1__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-0.25.2 → validio_cli-0.26.1}/PKG-INFO +1 -1
- {validio_cli-0.25.2 → validio_cli-0.26.1}/pyproject.toml +4 -3
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/__init__.py +13 -2
- validio_cli-0.26.1/validio_cli/bin/entities/channels.py +124 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/code.py +19 -12
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/credentials.py +46 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/dbt.py +6 -33
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/filters.py +66 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/incidents.py +7 -7
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/metrics.py +1 -1
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/namespaces.py +2 -1
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/notification_rules.py +31 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/segmentations.py +31 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/segments.py +2 -2
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/sources.py +35 -71
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/validators.py +31 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/windows.py +39 -5
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/main.py +3 -1
- validio_cli-0.25.2/validio_cli/bin/entities/channels.py +0 -57
- {validio_cli-0.25.2 → validio_cli-0.26.1}/LICENSE +0 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/README_PUBLIC.md +0 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/config.py +0 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/recommendations.py +0 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/resources.py +0 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/bin/entities/users.py +0 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/components.py +0 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/metadata.py +0 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/namespace.py +0 -0
- {validio_cli-0.25.2 → validio_cli-0.26.1}/validio_cli/schema.py +0 -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.
|
|
6
|
+
version = "0.26.1"
|
|
7
7
|
description = "CLI tool to interact with the Validio platform"
|
|
8
8
|
authors = ["Validio <support@validio.io>"]
|
|
9
9
|
license = "Apache-2.0"
|
|
@@ -24,7 +24,7 @@ pydantic = "2.4.2"
|
|
|
24
24
|
validio-sdk = "*"
|
|
25
25
|
|
|
26
26
|
[tool.poetry.group.dev.dependencies]
|
|
27
|
-
ruff = "^0.
|
|
27
|
+
ruff = "^0.9.6"
|
|
28
28
|
mypy = "^1.2.0"
|
|
29
29
|
licensecheck = "^2023.1.1"
|
|
30
30
|
pytest = "^7.4.3"
|
|
@@ -41,6 +41,7 @@ validio = "validio_cli.bin.main:main"
|
|
|
41
41
|
[tool.ruff]
|
|
42
42
|
fix = true
|
|
43
43
|
|
|
44
|
+
[tool.ruff.lint]
|
|
44
45
|
select = [
|
|
45
46
|
"ARG", # flake8-unused-argument
|
|
46
47
|
"C4", # flake8-comprehension
|
|
@@ -66,7 +67,7 @@ ignore = [
|
|
|
66
67
|
"PLR0913", # `too-many-arguments` - Not relevant with only 6
|
|
67
68
|
]
|
|
68
69
|
|
|
69
|
-
[tool.ruff.per-file-ignores]
|
|
70
|
+
[tool.ruff.lint.per-file-ignores]
|
|
70
71
|
# No need for documentation in these files
|
|
71
72
|
"__init__.py" = ["D"]
|
|
72
73
|
"*/bin/*.py" = ["D"]
|
|
@@ -198,6 +198,17 @@ def output_text(items: Any, fields: dict[str, OutputSettings | None]) -> None:
|
|
|
198
198
|
sys.stdout.flush()
|
|
199
199
|
|
|
200
200
|
|
|
201
|
+
def output_ok_or_error(result: Any) -> None:
|
|
202
|
+
status = "OK" if not result.get("errors") else "ERROR"
|
|
203
|
+
|
|
204
|
+
return output_text(
|
|
205
|
+
{"status": status},
|
|
206
|
+
fields={
|
|
207
|
+
"status": OutputSettings(pass_full_object=True, reformat=lambda _: status),
|
|
208
|
+
},
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
201
212
|
# ruff: noqa: PLR2004, PLR0911
|
|
202
213
|
def _format_relative_time(d: datetime) -> str:
|
|
203
214
|
diff = datetime.now(timezone.utc) - d
|
|
@@ -214,11 +225,11 @@ def _format_relative_time(d: datetime) -> str:
|
|
|
214
225
|
if seconds < 120:
|
|
215
226
|
return "1m"
|
|
216
227
|
if seconds < 3600:
|
|
217
|
-
return f"{int(seconds/60)}m"
|
|
228
|
+
return f"{int(seconds / 60)}m"
|
|
218
229
|
if seconds < 7200:
|
|
219
230
|
return "1h"
|
|
220
231
|
|
|
221
|
-
return f"{int(seconds/3600)}h"
|
|
232
|
+
return f"{int(seconds / 3600)}h"
|
|
222
233
|
|
|
223
234
|
|
|
224
235
|
def _single_resource_if_specified(items: list[Any], identifier: str | None) -> Any:
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from camel_converter import to_snake
|
|
3
|
+
from validio_sdk.exception import ValidioError
|
|
4
|
+
|
|
5
|
+
from validio_cli import (
|
|
6
|
+
APIClient,
|
|
7
|
+
AsyncTyper,
|
|
8
|
+
ConfigDir,
|
|
9
|
+
Identifier,
|
|
10
|
+
Identifiers,
|
|
11
|
+
Namespace,
|
|
12
|
+
OutputFormat,
|
|
13
|
+
OutputFormatOption,
|
|
14
|
+
OutputSettings,
|
|
15
|
+
ValidioConfig,
|
|
16
|
+
_single_resource_if_specified,
|
|
17
|
+
get_client,
|
|
18
|
+
output_json,
|
|
19
|
+
output_ok_or_error,
|
|
20
|
+
output_text,
|
|
21
|
+
)
|
|
22
|
+
from validio_cli.namespace import get_namespace
|
|
23
|
+
|
|
24
|
+
app = AsyncTyper(help="Channels used for notifications")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@app.async_command(help="Get channels")
|
|
28
|
+
async def get(
|
|
29
|
+
config_dir: str = ConfigDir,
|
|
30
|
+
output_format: OutputFormat = OutputFormatOption,
|
|
31
|
+
namespace: str = Namespace(),
|
|
32
|
+
identifier: str = Identifier,
|
|
33
|
+
) -> None:
|
|
34
|
+
client, cfg = get_client(config_dir)
|
|
35
|
+
|
|
36
|
+
channels = await client.get_channels(
|
|
37
|
+
channel_id=identifier,
|
|
38
|
+
namespace_id=get_namespace(namespace, cfg),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# TODO(UI-2311): Fully support list/get/get_by_resource_name
|
|
42
|
+
if isinstance(channels, list):
|
|
43
|
+
channels = _single_resource_if_specified(channels, identifier)
|
|
44
|
+
|
|
45
|
+
if output_format == OutputFormat.JSON:
|
|
46
|
+
return output_json(channels, identifier)
|
|
47
|
+
|
|
48
|
+
return output_text(
|
|
49
|
+
channels,
|
|
50
|
+
fields={
|
|
51
|
+
"name": OutputSettings(attribute_name="resourceName"),
|
|
52
|
+
"type": OutputSettings(
|
|
53
|
+
attribute_name="__typename",
|
|
54
|
+
reformat=lambda x: to_snake(x.removesuffix("Channel")).upper(),
|
|
55
|
+
),
|
|
56
|
+
"age": OutputSettings.string_as_datetime("createdAt"),
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@app.async_command(help="Delete channel")
|
|
62
|
+
async def delete(
|
|
63
|
+
config_dir: str = ConfigDir,
|
|
64
|
+
output_format: OutputFormat = OutputFormatOption,
|
|
65
|
+
namespace: str = Namespace(),
|
|
66
|
+
identifiers: list[str] = Identifiers,
|
|
67
|
+
) -> None:
|
|
68
|
+
if not identifiers:
|
|
69
|
+
raise ValidioError("no identifiers are provided")
|
|
70
|
+
|
|
71
|
+
vc, cfg = get_client(config_dir)
|
|
72
|
+
|
|
73
|
+
channel_ids = [
|
|
74
|
+
channel_id
|
|
75
|
+
for channel_id in [
|
|
76
|
+
await get_channel_id(vc, cfg, identifier, namespace)
|
|
77
|
+
for identifier in identifiers
|
|
78
|
+
]
|
|
79
|
+
if channel_id
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
result = await vc.delete_channels(channel_ids)
|
|
83
|
+
|
|
84
|
+
if output_format == OutputFormat.JSON:
|
|
85
|
+
return output_json(result)
|
|
86
|
+
|
|
87
|
+
return output_ok_or_error(result)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
async def get_channel_id(
|
|
91
|
+
vc: APIClient, cfg: ValidioConfig, identifier: str, namespace: str
|
|
92
|
+
) -> str | None:
|
|
93
|
+
"""
|
|
94
|
+
Ensure the identifier is a resource id.
|
|
95
|
+
|
|
96
|
+
If it doesn't have the expected prefix, do a resource lookup by name.
|
|
97
|
+
"""
|
|
98
|
+
identifier_type = "channel"
|
|
99
|
+
prefix = "CNL_"
|
|
100
|
+
|
|
101
|
+
if not identifier:
|
|
102
|
+
print(f"Missing {identifier_type} id or name")
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
if identifier.startswith(prefix):
|
|
106
|
+
return identifier
|
|
107
|
+
|
|
108
|
+
resource = await vc.get_channels(
|
|
109
|
+
channel_id=identifier,
|
|
110
|
+
namespace_id=get_namespace(namespace, cfg),
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if resource is None:
|
|
114
|
+
print(f"No {identifier_type} with name or id {identifier} found")
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
if not isinstance(resource, dict):
|
|
118
|
+
raise ValidioError("failed to get channel id")
|
|
119
|
+
|
|
120
|
+
return resource["id"]
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if __name__ == "__main__":
|
|
124
|
+
typer.run(app())
|
|
@@ -65,7 +65,7 @@ diff_option = typer.Option(
|
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
color_option = typer.Option(
|
|
68
|
-
not os.environ.get("NO_COLOR", False),
|
|
68
|
+
not os.environ.get("NO_COLOR", False) and sys.stdout.isatty(),
|
|
69
69
|
help="Enable colored output",
|
|
70
70
|
)
|
|
71
71
|
|
|
@@ -99,7 +99,7 @@ update_all_schemas_option = typer.Option(
|
|
|
99
99
|
)
|
|
100
100
|
|
|
101
101
|
show_progress_option = typer.Option(
|
|
102
|
-
|
|
102
|
+
sys.stdout.isatty(),
|
|
103
103
|
help=(
|
|
104
104
|
f"{BETA_BANNER} "
|
|
105
105
|
"If enabled, progress updates will be displayed during "
|
|
@@ -113,7 +113,7 @@ def target_option(resource_type: str) -> Any:
|
|
|
113
113
|
[],
|
|
114
114
|
help=(
|
|
115
115
|
f"{BETA_BANNER} "
|
|
116
|
-
f
|
|
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
|
),
|
|
119
119
|
)
|
|
@@ -210,6 +210,14 @@ async def apply(
|
|
|
210
210
|
validator: list[str] = target_option("validator"),
|
|
211
211
|
window: list[str] = target_option("window"),
|
|
212
212
|
progress: bool = show_progress_option,
|
|
213
|
+
dry_run_sql: bool = typer.Option(
|
|
214
|
+
False,
|
|
215
|
+
help=f"""
|
|
216
|
+
{BETA_BANNER} Dry run SQL queries before creating/updating SQL Filters
|
|
217
|
+
or Validators.
|
|
218
|
+
Note: this feature is unsupported on Azure based warehouses.
|
|
219
|
+
""",
|
|
220
|
+
),
|
|
213
221
|
) -> None:
|
|
214
222
|
dir_path = directory_or_default(directory)
|
|
215
223
|
client, _ = get_client(config_dir)
|
|
@@ -259,6 +267,7 @@ async def apply(
|
|
|
259
267
|
diff=diff,
|
|
260
268
|
show_secrets=show_secrets,
|
|
261
269
|
show_progress=progress,
|
|
270
|
+
dry_run_sql=dry_run_sql,
|
|
262
271
|
)
|
|
263
272
|
|
|
264
273
|
print(
|
|
@@ -406,7 +415,7 @@ async def _plan(
|
|
|
406
415
|
diff=plan_result.graph_diff,
|
|
407
416
|
show_schema=show_schema,
|
|
408
417
|
show_secrets=show_secrets,
|
|
409
|
-
escape=
|
|
418
|
+
escape=color,
|
|
410
419
|
diff_output=diff_output,
|
|
411
420
|
)
|
|
412
421
|
|
|
@@ -533,9 +542,7 @@ def _show_update_resource_diff(
|
|
|
533
542
|
cascade_update = ""
|
|
534
543
|
if r.replacement_cascaded_update_parent:
|
|
535
544
|
(cls, parent_name) = r.replacement_cascaded_update_parent
|
|
536
|
-
cascade_update = (
|
|
537
|
-
f" (due to replacement of " f"{cls.__name__}({parent_name!r}))"
|
|
538
|
-
)
|
|
545
|
+
cascade_update = f" (due to replacement of {cls.__name__}({parent_name!r}))"
|
|
539
546
|
|
|
540
547
|
print(f"{class_key} '{r.manifest.name}' will be updated{cascade_update}")
|
|
541
548
|
|
|
@@ -545,14 +552,14 @@ def _show_update_resource_diff(
|
|
|
545
552
|
r.manifest,
|
|
546
553
|
show_secrets=show_secrets,
|
|
547
554
|
rewrites=rewrites,
|
|
548
|
-
|
|
555
|
+
secret_fields_changed=r.secret_fields_changed,
|
|
549
556
|
is_manifest=True,
|
|
550
557
|
)
|
|
551
558
|
b_value = code_plan._create_resource_diff_object(
|
|
552
559
|
r.server,
|
|
553
560
|
show_secrets=show_secrets,
|
|
554
561
|
rewrites=rewrites,
|
|
555
|
-
|
|
562
|
+
secret_fields_changed=r.secret_fields_changed,
|
|
556
563
|
is_manifest=False,
|
|
557
564
|
)
|
|
558
565
|
|
|
@@ -584,14 +591,14 @@ def _show_replace_resource_diff(
|
|
|
584
591
|
update.manifest,
|
|
585
592
|
show_secrets=show_secrets,
|
|
586
593
|
rewrites=rewrites,
|
|
587
|
-
|
|
594
|
+
secret_fields_changed=update.secret_fields_changed,
|
|
588
595
|
is_manifest=True,
|
|
589
596
|
)
|
|
590
597
|
b_value = code_plan._create_resource_diff_object(
|
|
591
598
|
update.server,
|
|
592
599
|
show_secrets=show_secrets,
|
|
593
600
|
rewrites=rewrites,
|
|
594
|
-
|
|
601
|
+
secret_fields_changed=update.secret_fields_changed,
|
|
595
602
|
is_manifest=False,
|
|
596
603
|
)
|
|
597
604
|
else:
|
|
@@ -647,7 +654,7 @@ def _show_deprecations(deprecations: list[ResourceDeprecation]) -> None:
|
|
|
647
654
|
print()
|
|
648
655
|
print(f"\033[93m{title}\033[0m") # Make the text yellow
|
|
649
656
|
for i, message in enumerate(sorted(deprecations)):
|
|
650
|
-
print(f"\033[93m{i+1:>2}. {message}\033[0m")
|
|
657
|
+
print(f"\033[93m{i + 1:>2}. {message}\033[0m")
|
|
651
658
|
|
|
652
659
|
print(footer, end="")
|
|
653
660
|
|
|
@@ -7,6 +7,7 @@ from validio_cli import (
|
|
|
7
7
|
AsyncTyper,
|
|
8
8
|
ConfigDir,
|
|
9
9
|
Identifier,
|
|
10
|
+
Identifiers,
|
|
10
11
|
Namespace,
|
|
11
12
|
OutputFormat,
|
|
12
13
|
OutputFormatOption,
|
|
@@ -14,6 +15,7 @@ from validio_cli import (
|
|
|
14
15
|
_single_resource_if_specified,
|
|
15
16
|
get_client,
|
|
16
17
|
output_json,
|
|
18
|
+
output_ok_or_error,
|
|
17
19
|
output_text,
|
|
18
20
|
)
|
|
19
21
|
from validio_cli.namespace import get_namespace
|
|
@@ -52,6 +54,50 @@ async def get(
|
|
|
52
54
|
)
|
|
53
55
|
|
|
54
56
|
|
|
57
|
+
@app.async_command(help="Delete credential")
|
|
58
|
+
async def delete(
|
|
59
|
+
config_dir: str = ConfigDir,
|
|
60
|
+
output_format: OutputFormat = OutputFormatOption,
|
|
61
|
+
namespace: str = Namespace(),
|
|
62
|
+
identifiers: list[str] = Identifiers,
|
|
63
|
+
) -> None:
|
|
64
|
+
if not identifiers:
|
|
65
|
+
raise ValidioError("no identifiers are provided")
|
|
66
|
+
|
|
67
|
+
vc, cfg = get_client(config_dir)
|
|
68
|
+
|
|
69
|
+
credential_ids = [
|
|
70
|
+
credential_id
|
|
71
|
+
for credential_id in [
|
|
72
|
+
await get_credential_id(vc, cfg, identifier, namespace)
|
|
73
|
+
for identifier in identifiers
|
|
74
|
+
]
|
|
75
|
+
if credential_id
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
sources = await vc.get_sources(namespace_id=namespace)
|
|
79
|
+
if not isinstance(sources, list):
|
|
80
|
+
raise ValidioError("failed to get sources")
|
|
81
|
+
|
|
82
|
+
credentials_with_sources = {
|
|
83
|
+
s["credential"]["resourceName"]
|
|
84
|
+
for s in sources
|
|
85
|
+
if s["credential"]["id"] in credential_ids
|
|
86
|
+
}
|
|
87
|
+
if len(credentials_with_sources) > 0:
|
|
88
|
+
raise ValidioError(
|
|
89
|
+
"Following credentials are connected to sources:\n"
|
|
90
|
+
+ "\n".join(credentials_with_sources)
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
result = await vc.delete_credentials(credential_ids)
|
|
94
|
+
|
|
95
|
+
if output_format == OutputFormat.JSON:
|
|
96
|
+
return output_json(result)
|
|
97
|
+
|
|
98
|
+
return output_ok_or_error(result)
|
|
99
|
+
|
|
100
|
+
|
|
55
101
|
async def get_credential_id(
|
|
56
102
|
vc: APIClient, cfg: ValidioConfig, identifier: str, namespace: str
|
|
57
103
|
) -> str | None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
from pathlib import Path
|
|
3
|
-
from typing import Any, Optional
|
|
3
|
+
from typing import Any, Optional
|
|
4
4
|
|
|
5
5
|
import typer
|
|
6
6
|
from validio_sdk import ValidioError, dbt, util
|
|
@@ -26,23 +26,8 @@ RUN_RESULTS_FILE_NAME = "run_results.json"
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def _resolve_file_paths(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
target_path: Optional[Path],
|
|
32
|
-
) -> Tuple[Path, Optional[Path]]:
|
|
33
|
-
if explicit_manifest_path or explicit_run_result_path:
|
|
34
|
-
print(
|
|
35
|
-
"Deprecation notice ⚠️: The --manifest and --run-results flags "
|
|
36
|
-
"have been deprecated and will be removed in a future version. "
|
|
37
|
-
"Please use the --target-path argument instead."
|
|
38
|
-
)
|
|
39
|
-
if not explicit_manifest_path:
|
|
40
|
-
raise ValidioError(
|
|
41
|
-
"--manifest is required when combined with the --run-results argument"
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
return explicit_manifest_path, explicit_run_result_path
|
|
45
|
-
|
|
29
|
+
target_path: Path,
|
|
30
|
+
) -> tuple[Path, Optional[Path]]:
|
|
46
31
|
if not target_path:
|
|
47
32
|
raise ValidioError("Missing --target-path argument")
|
|
48
33
|
|
|
@@ -66,23 +51,11 @@ async def upload(
|
|
|
66
51
|
config_dir: str = ConfigDir,
|
|
67
52
|
namespace: str = Namespace(),
|
|
68
53
|
credential_id: str = typer.Option(..., help="Credential name or ID"),
|
|
69
|
-
manifest: Optional[Path] = typer.Option(
|
|
70
|
-
help="Path to the manifest file. "
|
|
71
|
-
"(deprecated in favor of the --target-path flag)",
|
|
72
|
-
default=None,
|
|
73
|
-
),
|
|
74
54
|
job_name: str = typer.Option(
|
|
75
55
|
..., help="The job that the dbt execution belongs to, e.g. `staging-pipeline`"
|
|
76
56
|
),
|
|
77
|
-
|
|
78
|
-
help="Path to the
|
|
79
|
-
"(deprecated in favor of the --target-path flag)",
|
|
80
|
-
default=None,
|
|
81
|
-
),
|
|
82
|
-
target_path: Optional[Path] = typer.Option(
|
|
83
|
-
help="Path to the dbt artifacts target directory. "
|
|
84
|
-
"(supersedes the --manifest flag)",
|
|
85
|
-
default=None,
|
|
57
|
+
target_path: Path = typer.Option(
|
|
58
|
+
..., help="Path to the dbt artifacts target directory. "
|
|
86
59
|
),
|
|
87
60
|
) -> None:
|
|
88
61
|
client, cfg = get_client(config_dir)
|
|
@@ -93,7 +66,7 @@ async def upload(
|
|
|
93
66
|
if resolved_credential_id is None:
|
|
94
67
|
raise ValidioError(f"Credential '{credential_id}' not found")
|
|
95
68
|
|
|
96
|
-
manifest, run_results = _resolve_file_paths(
|
|
69
|
+
manifest, run_results = _resolve_file_paths(target_path)
|
|
97
70
|
|
|
98
71
|
run_results_content = None
|
|
99
72
|
try:
|
|
@@ -3,15 +3,19 @@ from validio_sdk.exception import ValidioError
|
|
|
3
3
|
|
|
4
4
|
import validio_cli
|
|
5
5
|
from validio_cli import (
|
|
6
|
+
APIClient,
|
|
6
7
|
AsyncTyper,
|
|
7
8
|
ConfigDir,
|
|
8
9
|
Identifier,
|
|
10
|
+
Identifiers,
|
|
9
11
|
Namespace,
|
|
10
12
|
OutputFormat,
|
|
11
13
|
OutputFormatOption,
|
|
12
14
|
OutputSettings,
|
|
15
|
+
ValidioConfig,
|
|
13
16
|
get_client,
|
|
14
17
|
output_json,
|
|
18
|
+
output_ok_or_error,
|
|
15
19
|
output_text,
|
|
16
20
|
)
|
|
17
21
|
from validio_cli.namespace import get_namespace
|
|
@@ -62,5 +66,67 @@ async def get(
|
|
|
62
66
|
)
|
|
63
67
|
|
|
64
68
|
|
|
69
|
+
@app.async_command(help="Delete filter")
|
|
70
|
+
async def delete(
|
|
71
|
+
config_dir: str = ConfigDir,
|
|
72
|
+
output_format: OutputFormat = OutputFormatOption,
|
|
73
|
+
namespace: str = Namespace(),
|
|
74
|
+
identifiers: list[str] = Identifiers,
|
|
75
|
+
) -> None:
|
|
76
|
+
if not identifiers:
|
|
77
|
+
raise ValidioError("no identifiers are provided")
|
|
78
|
+
|
|
79
|
+
vc, cfg = get_client(config_dir)
|
|
80
|
+
|
|
81
|
+
filter_ids = [
|
|
82
|
+
filter_id
|
|
83
|
+
for filter_id in [
|
|
84
|
+
await get_filter_id(vc, cfg, identifier, namespace)
|
|
85
|
+
for identifier in identifiers
|
|
86
|
+
]
|
|
87
|
+
if filter_id
|
|
88
|
+
]
|
|
89
|
+
|
|
90
|
+
result = await vc.delete_filters(filter_ids)
|
|
91
|
+
|
|
92
|
+
if output_format == OutputFormat.JSON:
|
|
93
|
+
return output_json(result)
|
|
94
|
+
|
|
95
|
+
return output_ok_or_error(result)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
async def get_filter_id(
|
|
99
|
+
vc: APIClient, cfg: ValidioConfig, identifier: str, namespace: str
|
|
100
|
+
) -> str | None:
|
|
101
|
+
"""
|
|
102
|
+
Ensure the identifier is a resource id.
|
|
103
|
+
|
|
104
|
+
If it doesn't have the expected prefix, do a resource lookup by name.
|
|
105
|
+
"""
|
|
106
|
+
identifier_type = "filter"
|
|
107
|
+
prefix = "FTR_"
|
|
108
|
+
|
|
109
|
+
if not identifier:
|
|
110
|
+
print(f"Missing {identifier_type} id or name")
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
if identifier.startswith(prefix):
|
|
114
|
+
return identifier
|
|
115
|
+
|
|
116
|
+
resource = await vc.get_filters(
|
|
117
|
+
filter_id=identifier,
|
|
118
|
+
namespace_id=get_namespace(namespace, cfg),
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if resource is None:
|
|
122
|
+
print(f"No {identifier_type} with name or id {identifier} found")
|
|
123
|
+
return None
|
|
124
|
+
|
|
125
|
+
if not isinstance(resource, dict):
|
|
126
|
+
raise ValidioError("failed to get filter id")
|
|
127
|
+
|
|
128
|
+
return resource["id"]
|
|
129
|
+
|
|
130
|
+
|
|
65
131
|
if __name__ == "__main__":
|
|
66
132
|
typer.run(app())
|
|
@@ -80,8 +80,8 @@ async def get(
|
|
|
80
80
|
"bound": OutputSettings(
|
|
81
81
|
pass_full_object=True,
|
|
82
82
|
reformat=lambda x: (
|
|
83
|
-
f
|
|
84
|
-
f
|
|
83
|
+
f"{format_number(x['lowerBound'])} - "
|
|
84
|
+
f"{format_number(x['upperBound'])}"
|
|
85
85
|
),
|
|
86
86
|
),
|
|
87
87
|
"value": OutputSettings(reformat=format_number),
|
|
@@ -96,10 +96,10 @@ async def get(
|
|
|
96
96
|
def calculate_operator(item: Any) -> str:
|
|
97
97
|
type_ = item["__typename"][len("ValidatorMetricWith") :]
|
|
98
98
|
if type_ == "DynamicThreshold":
|
|
99
|
-
return f
|
|
99
|
+
return f"{type_}/{item['decisionBoundsType']}"
|
|
100
100
|
|
|
101
101
|
if type_ == "FixedThreshold":
|
|
102
|
-
return f
|
|
102
|
+
return f"{type_}/{item['operator']}"
|
|
103
103
|
|
|
104
104
|
return type_
|
|
105
105
|
|
|
@@ -107,10 +107,10 @@ def calculate_operator(item: Any) -> str:
|
|
|
107
107
|
def calculate_bound(item: Any) -> str:
|
|
108
108
|
type_ = item["__typename"][len("ValidatorMetricWith") :]
|
|
109
109
|
if type_ == "DynamicThreshold":
|
|
110
|
-
bound = f
|
|
110
|
+
bound = f"{item['lowerBound']:.2f} - {item['upperBound']:.2f}"
|
|
111
111
|
elif type_ == "DifferenceThreshold":
|
|
112
|
-
lower = f
|
|
113
|
-
upper = f
|
|
112
|
+
lower = f"{item['lowerBound']:.2f}" if item["lowerBound"] is not None else "-"
|
|
113
|
+
upper = f"{item['upperBound']:.2f}" if item["upperBound"] is not None else "-"
|
|
114
114
|
|
|
115
115
|
bound = f"{lower} - {upper}"
|
|
116
116
|
elif type_ == "FixedThreshold":
|
|
@@ -8,6 +8,7 @@ from validio_cli import (
|
|
|
8
8
|
AsyncTyper,
|
|
9
9
|
ConfigDir,
|
|
10
10
|
Identifier,
|
|
11
|
+
Identifiers,
|
|
11
12
|
Namespace,
|
|
12
13
|
OutputFormat,
|
|
13
14
|
OutputFormatOption,
|
|
@@ -15,6 +16,7 @@ from validio_cli import (
|
|
|
15
16
|
_single_resource_if_specified,
|
|
16
17
|
get_client,
|
|
17
18
|
output_json,
|
|
19
|
+
output_ok_or_error,
|
|
18
20
|
output_text,
|
|
19
21
|
)
|
|
20
22
|
from validio_cli.namespace import get_namespace
|
|
@@ -61,6 +63,35 @@ async def get(
|
|
|
61
63
|
)
|
|
62
64
|
|
|
63
65
|
|
|
66
|
+
@app.async_command(help="Delete notification rule")
|
|
67
|
+
async def delete(
|
|
68
|
+
config_dir: str = ConfigDir,
|
|
69
|
+
output_format: OutputFormat = OutputFormatOption,
|
|
70
|
+
namespace: str = Namespace(),
|
|
71
|
+
identifiers: list[str] = Identifiers,
|
|
72
|
+
) -> None:
|
|
73
|
+
if not identifiers:
|
|
74
|
+
raise ValidioError("no identifiers are provided")
|
|
75
|
+
|
|
76
|
+
vc, cfg = get_client(config_dir)
|
|
77
|
+
|
|
78
|
+
notification_rule_ids = [
|
|
79
|
+
notification_rule_id
|
|
80
|
+
for notification_rule_id in [
|
|
81
|
+
await get_notification_rule_id(vc, cfg, identifier, namespace)
|
|
82
|
+
for identifier in identifiers
|
|
83
|
+
]
|
|
84
|
+
if notification_rule_id
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
result = await vc.delete_notification_rules(notification_rule_ids)
|
|
88
|
+
|
|
89
|
+
if output_format == OutputFormat.JSON:
|
|
90
|
+
return output_json(result)
|
|
91
|
+
|
|
92
|
+
return output_ok_or_error(result)
|
|
93
|
+
|
|
94
|
+
|
|
64
95
|
async def get_notification_rule_id(
|
|
65
96
|
vc: APIClient, cfg: ValidioConfig, identifier: str, namespace: str
|
|
66
97
|
) -> str | None:
|
|
@@ -8,12 +8,14 @@ from validio_cli import (
|
|
|
8
8
|
AsyncTyper,
|
|
9
9
|
ConfigDir,
|
|
10
10
|
Identifier,
|
|
11
|
+
Identifiers,
|
|
11
12
|
Namespace,
|
|
12
13
|
OutputFormat,
|
|
13
14
|
OutputFormatOption,
|
|
14
15
|
OutputSettings,
|
|
15
16
|
get_client,
|
|
16
17
|
output_json,
|
|
18
|
+
output_ok_or_error,
|
|
17
19
|
output_text,
|
|
18
20
|
)
|
|
19
21
|
from validio_cli.namespace import get_namespace
|
|
@@ -65,6 +67,35 @@ async def get(
|
|
|
65
67
|
)
|
|
66
68
|
|
|
67
69
|
|
|
70
|
+
@app.async_command(help="Delete segmentation")
|
|
71
|
+
async def delete(
|
|
72
|
+
config_dir: str = ConfigDir,
|
|
73
|
+
output_format: OutputFormat = OutputFormatOption,
|
|
74
|
+
namespace: str = Namespace(),
|
|
75
|
+
identifiers: list[str] = Identifiers,
|
|
76
|
+
) -> None:
|
|
77
|
+
if not identifiers:
|
|
78
|
+
raise ValidioError("no identifiers are provided")
|
|
79
|
+
|
|
80
|
+
vc, cfg = get_client(config_dir)
|
|
81
|
+
|
|
82
|
+
segmentation_ids = [
|
|
83
|
+
segmentation_id
|
|
84
|
+
for segmentation_id in [
|
|
85
|
+
await get_segmentation_id(vc, cfg, identifier, namespace)
|
|
86
|
+
for identifier in identifiers
|
|
87
|
+
]
|
|
88
|
+
if segmentation_id
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
result = await vc.delete_segmentations(segmentation_ids)
|
|
92
|
+
|
|
93
|
+
if output_format == OutputFormat.JSON:
|
|
94
|
+
return output_json(result)
|
|
95
|
+
|
|
96
|
+
return output_ok_or_error(result)
|
|
97
|
+
|
|
98
|
+
|
|
68
99
|
async def get_segmentation_id(
|
|
69
100
|
vc: APIClient, cfg: ValidioConfig, identifier: str, namespace: str
|
|
70
101
|
) -> str | None:
|
|
@@ -46,11 +46,11 @@ async def get(
|
|
|
46
46
|
for segment in segments:
|
|
47
47
|
field_and_value = []
|
|
48
48
|
for f in segment["fields"]:
|
|
49
|
-
field_and_value.append(f
|
|
49
|
+
field_and_value.append(f"{f['field']}={f['value']}")
|
|
50
50
|
|
|
51
51
|
additional = ""
|
|
52
52
|
if len(field_and_value) > max_fields:
|
|
53
|
-
additional = f" and {len(field_and_value)-max_fields} more"
|
|
53
|
+
additional = f" and {len(field_and_value) - max_fields} more"
|
|
54
54
|
field_and_value = field_and_value[:max_fields]
|
|
55
55
|
|
|
56
56
|
resources.append(
|
|
@@ -32,6 +32,7 @@ from validio_cli import (
|
|
|
32
32
|
components,
|
|
33
33
|
get_client,
|
|
34
34
|
output_json,
|
|
35
|
+
output_ok_or_error,
|
|
35
36
|
output_text,
|
|
36
37
|
)
|
|
37
38
|
from validio_cli.bin.entities import credentials
|
|
@@ -102,6 +103,35 @@ async def get(
|
|
|
102
103
|
)
|
|
103
104
|
|
|
104
105
|
|
|
106
|
+
@app.async_command(help="Delete source")
|
|
107
|
+
async def delete(
|
|
108
|
+
config_dir: str = ConfigDir,
|
|
109
|
+
output_format: OutputFormat = OutputFormatOption,
|
|
110
|
+
namespace: str = Namespace(),
|
|
111
|
+
identifiers: list[str] = Identifiers,
|
|
112
|
+
) -> None:
|
|
113
|
+
if not identifiers:
|
|
114
|
+
raise ValidioError("no identifiers are provided")
|
|
115
|
+
|
|
116
|
+
vc, cfg = get_client(config_dir)
|
|
117
|
+
|
|
118
|
+
source_ids = [
|
|
119
|
+
source_id
|
|
120
|
+
for source_id in [
|
|
121
|
+
await get_source_id(vc, cfg, identifier, namespace)
|
|
122
|
+
for identifier in identifiers
|
|
123
|
+
]
|
|
124
|
+
if source_id
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
result = await vc.delete_sources(source_ids)
|
|
128
|
+
|
|
129
|
+
if output_format == OutputFormat.JSON:
|
|
130
|
+
return output_json(result)
|
|
131
|
+
|
|
132
|
+
return output_ok_or_error(result)
|
|
133
|
+
|
|
134
|
+
|
|
105
135
|
@app.async_command(help="Start sources")
|
|
106
136
|
async def start(
|
|
107
137
|
config_dir: str = ConfigDir,
|
|
@@ -208,6 +238,8 @@ def main(
|
|
|
208
238
|
return None
|
|
209
239
|
|
|
210
240
|
|
|
241
|
+
# Not much to do with this complexity
|
|
242
|
+
# ruff: noqa: PLR0915,PLR0912
|
|
211
243
|
async def _interactive(config_dir: str, filename: Path, namespace: str) -> None:
|
|
212
244
|
vc, cfg = get_client(config_dir)
|
|
213
245
|
|
|
@@ -247,7 +279,6 @@ async def _interactive(config_dir: str, filename: Path, namespace: str) -> None:
|
|
|
247
279
|
("bigquery", "Google BigQuery"),
|
|
248
280
|
("gcs", "Google Cloud Storage"),
|
|
249
281
|
("pubsub", "Google Pub/Sub"),
|
|
250
|
-
("pubsub_lite", "Google Pub/Sub Lite"),
|
|
251
282
|
],
|
|
252
283
|
"PostgreSqlCredential": [("postgresql", "PostgreSQL")],
|
|
253
284
|
"RedshiftCredential": [("redshift", "Amazon Redshift")],
|
|
@@ -411,16 +442,6 @@ async def _interactive(config_dir: str, filename: Path, namespace: str) -> None:
|
|
|
411
442
|
("Message schema", [], ""),
|
|
412
443
|
]
|
|
413
444
|
)
|
|
414
|
-
await _infer_schema_pubsub_lite(
|
|
415
|
-
vc,
|
|
416
|
-
filename,
|
|
417
|
-
credential_id,
|
|
418
|
-
info.get("project", ""),
|
|
419
|
-
info.get("subscription_id", ""),
|
|
420
|
-
info.get("location", ""),
|
|
421
|
-
info.get("message_format", ""),
|
|
422
|
-
info.get("message_schema", ""),
|
|
423
|
-
)
|
|
424
445
|
case "kafka":
|
|
425
446
|
info = await _multip_prompt(
|
|
426
447
|
[
|
|
@@ -657,36 +678,6 @@ async def pubsub(
|
|
|
657
678
|
)
|
|
658
679
|
|
|
659
680
|
|
|
660
|
-
@infer_schema_app.async_command(help="Infer GCP Pub/Sub Lite schema")
|
|
661
|
-
async def pubsub_lite(
|
|
662
|
-
config_dir: str = ConfigDir,
|
|
663
|
-
filename: Path = schema_filename_option("pubsub-lite"),
|
|
664
|
-
credential_id: str = typer.Option(..., help="Credential name or ID"),
|
|
665
|
-
project: str = typer.Option(..., help="GCP project"),
|
|
666
|
-
subscription_id: str = typer.Option(..., help="GCP Pub/Sub subscription ID"),
|
|
667
|
-
location: str = typer.Option(..., help="GCP Pub/Sub Lite location"),
|
|
668
|
-
message_format: str = typer.Option(default=None, help="Message format"),
|
|
669
|
-
message_schema: str = typer.Option(default=None, help="Message schema"),
|
|
670
|
-
namespace: str = Namespace(),
|
|
671
|
-
) -> None:
|
|
672
|
-
vc, cfg = get_client(config_dir)
|
|
673
|
-
|
|
674
|
-
resolved_credential_id = await _resolve_credential(
|
|
675
|
-
vc, cfg, credential_id, namespace
|
|
676
|
-
)
|
|
677
|
-
|
|
678
|
-
await _infer_schema_pubsub_lite(
|
|
679
|
-
vc,
|
|
680
|
-
filename,
|
|
681
|
-
resolved_credential_id,
|
|
682
|
-
project,
|
|
683
|
-
subscription_id,
|
|
684
|
-
location,
|
|
685
|
-
message_format,
|
|
686
|
-
message_schema,
|
|
687
|
-
)
|
|
688
|
-
|
|
689
|
-
|
|
690
681
|
@infer_schema_app.async_command(help="Infer Kafka schema")
|
|
691
682
|
async def kafka(
|
|
692
683
|
config_dir: str = ConfigDir,
|
|
@@ -934,30 +925,6 @@ async def _infer_schema_pubsub(
|
|
|
934
925
|
_write_schema(filename, schema)
|
|
935
926
|
|
|
936
927
|
|
|
937
|
-
async def _infer_schema_pubsub_lite(
|
|
938
|
-
vc: APIClient,
|
|
939
|
-
filename: Path,
|
|
940
|
-
credential_id: str,
|
|
941
|
-
project: str,
|
|
942
|
-
subscription_id: str,
|
|
943
|
-
location: str,
|
|
944
|
-
message_format: str | None = None,
|
|
945
|
-
message_schema: str | None = None,
|
|
946
|
-
) -> None:
|
|
947
|
-
message_format_input = _get_message_format_input(message_format, message_schema)
|
|
948
|
-
schema = await vc.infer_schema(
|
|
949
|
-
class_name="GcpPubSubLite",
|
|
950
|
-
variable_values={
|
|
951
|
-
"credentialId": credential_id,
|
|
952
|
-
"project": project,
|
|
953
|
-
"subscriptionId": subscription_id,
|
|
954
|
-
"location": location,
|
|
955
|
-
"messageFormat": message_format_input,
|
|
956
|
-
},
|
|
957
|
-
)
|
|
958
|
-
_write_schema(filename, schema)
|
|
959
|
-
|
|
960
|
-
|
|
961
928
|
async def _infer_schema_kafka(
|
|
962
929
|
vc: APIClient,
|
|
963
930
|
filename: Path,
|
|
@@ -1199,13 +1166,10 @@ def _resource_filter(
|
|
|
1199
1166
|
if credential_id is not None and source["credential"]["id"] != credential_id:
|
|
1200
1167
|
return False
|
|
1201
1168
|
|
|
1202
|
-
|
|
1169
|
+
return (
|
|
1203
1170
|
credential_name is not None
|
|
1204
|
-
and source["credential"]["resourceName"]
|
|
1205
|
-
)
|
|
1206
|
-
return False
|
|
1207
|
-
|
|
1208
|
-
return True
|
|
1171
|
+
and source["credential"]["resourceName"] == credential_name
|
|
1172
|
+
)
|
|
1209
1173
|
|
|
1210
1174
|
|
|
1211
1175
|
async def _resolve_credential(
|
|
@@ -8,12 +8,14 @@ from validio_cli import (
|
|
|
8
8
|
AsyncTyper,
|
|
9
9
|
ConfigDir,
|
|
10
10
|
Identifier,
|
|
11
|
+
Identifiers,
|
|
11
12
|
Namespace,
|
|
12
13
|
OutputFormat,
|
|
13
14
|
OutputFormatOption,
|
|
14
15
|
OutputSettings,
|
|
15
16
|
get_client,
|
|
16
17
|
output_json,
|
|
18
|
+
output_ok_or_error,
|
|
17
19
|
output_text,
|
|
18
20
|
)
|
|
19
21
|
from validio_cli.bin.entities.sources import get_source_id
|
|
@@ -76,6 +78,35 @@ async def get(
|
|
|
76
78
|
)
|
|
77
79
|
|
|
78
80
|
|
|
81
|
+
@app.async_command(help="Delete validator")
|
|
82
|
+
async def delete(
|
|
83
|
+
config_dir: str = ConfigDir,
|
|
84
|
+
output_format: OutputFormat = OutputFormatOption,
|
|
85
|
+
namespace: str = Namespace(),
|
|
86
|
+
identifiers: list[str] = Identifiers,
|
|
87
|
+
) -> None:
|
|
88
|
+
if not identifiers:
|
|
89
|
+
raise ValidioError("no identifiers are provided")
|
|
90
|
+
|
|
91
|
+
vc, cfg = get_client(config_dir)
|
|
92
|
+
|
|
93
|
+
validator_ids = [
|
|
94
|
+
validator_id
|
|
95
|
+
for validator_id in [
|
|
96
|
+
await get_validator_id(vc, cfg, identifier, namespace)
|
|
97
|
+
for identifier in identifiers
|
|
98
|
+
]
|
|
99
|
+
if validator_id
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
result = await vc.delete_validators(validator_ids)
|
|
103
|
+
|
|
104
|
+
if output_format == OutputFormat.JSON:
|
|
105
|
+
return output_json(result)
|
|
106
|
+
|
|
107
|
+
return output_ok_or_error(result)
|
|
108
|
+
|
|
109
|
+
|
|
79
110
|
async def get_validator_id(
|
|
80
111
|
vc: APIClient, cfg: ValidioConfig, identifier: str, namespace: str
|
|
81
112
|
) -> str | None:
|
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
import typer
|
|
2
2
|
from camel_converter import to_snake
|
|
3
|
+
from validio_sdk._api.api import APIClient
|
|
3
4
|
from validio_sdk.config import ValidioConfig
|
|
4
5
|
from validio_sdk.exception import ValidioError
|
|
5
|
-
from validio_sdk.validio_client import ValidioAPIClient
|
|
6
6
|
|
|
7
7
|
import validio_cli
|
|
8
8
|
from validio_cli import (
|
|
9
9
|
AsyncTyper,
|
|
10
10
|
ConfigDir,
|
|
11
11
|
Identifier,
|
|
12
|
+
Identifiers,
|
|
12
13
|
Namespace,
|
|
13
14
|
OutputFormat,
|
|
14
15
|
OutputFormatOption,
|
|
15
16
|
OutputSettings,
|
|
16
17
|
get_client,
|
|
17
18
|
output_json,
|
|
19
|
+
output_ok_or_error,
|
|
18
20
|
output_text,
|
|
19
21
|
)
|
|
20
22
|
from validio_cli.namespace import get_namespace
|
|
@@ -69,8 +71,37 @@ async def get(
|
|
|
69
71
|
)
|
|
70
72
|
|
|
71
73
|
|
|
74
|
+
@app.async_command(help="Delete window")
|
|
75
|
+
async def delete(
|
|
76
|
+
config_dir: str = ConfigDir,
|
|
77
|
+
output_format: OutputFormat = OutputFormatOption,
|
|
78
|
+
namespace: str = Namespace(),
|
|
79
|
+
identifiers: list[str] = Identifiers,
|
|
80
|
+
) -> None:
|
|
81
|
+
if not identifiers:
|
|
82
|
+
raise ValidioError("no identifiers are provided")
|
|
83
|
+
|
|
84
|
+
vc, cfg = get_client(config_dir)
|
|
85
|
+
|
|
86
|
+
window_ids = [
|
|
87
|
+
window_id
|
|
88
|
+
for window_id in [
|
|
89
|
+
await get_window_id(vc, cfg, identifier, namespace)
|
|
90
|
+
for identifier in identifiers
|
|
91
|
+
]
|
|
92
|
+
if window_id
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
result = await vc.delete_windows(window_ids)
|
|
96
|
+
|
|
97
|
+
if output_format == OutputFormat.JSON:
|
|
98
|
+
return output_json(result)
|
|
99
|
+
|
|
100
|
+
return output_ok_or_error(result)
|
|
101
|
+
|
|
102
|
+
|
|
72
103
|
async def get_window_id(
|
|
73
|
-
vc:
|
|
104
|
+
vc: APIClient, cfg: ValidioConfig, identifier: str, namespace: str
|
|
74
105
|
) -> str | None:
|
|
75
106
|
"""
|
|
76
107
|
Ensure the identifier is a resource id.
|
|
@@ -87,8 +118,8 @@ async def get_window_id(
|
|
|
87
118
|
if identifier.startswith(prefix):
|
|
88
119
|
return identifier
|
|
89
120
|
|
|
90
|
-
resource = await vc.
|
|
91
|
-
|
|
121
|
+
resource = await vc.get_windows(
|
|
122
|
+
window_id=identifier,
|
|
92
123
|
namespace_id=get_namespace(namespace, cfg),
|
|
93
124
|
)
|
|
94
125
|
|
|
@@ -96,7 +127,10 @@ async def get_window_id(
|
|
|
96
127
|
print(f"No {identifier_type} with name or id {identifier} found")
|
|
97
128
|
return None
|
|
98
129
|
|
|
99
|
-
|
|
130
|
+
if not isinstance(resource, dict):
|
|
131
|
+
raise ValidioError(f"failed to get {identifier_type} id")
|
|
132
|
+
|
|
133
|
+
return resource["id"]
|
|
100
134
|
|
|
101
135
|
|
|
102
136
|
if __name__ == "__main__":
|
|
@@ -49,7 +49,9 @@ app.add_typer(metrics.app, no_args_is_help=True, name="metrics")
|
|
|
49
49
|
app.add_typer(namespaces.app, no_args_is_help=True, name="namespaces")
|
|
50
50
|
app.add_typer(resources.app, no_args_is_help=True, name="resources")
|
|
51
51
|
app.add_typer(notification_rules.app, no_args_is_help=True, name="notification-rules")
|
|
52
|
-
app.add_typer(
|
|
52
|
+
app.add_typer(
|
|
53
|
+
recommendations.app, no_args_is_help=True, name="recommendations", deprecated=True
|
|
54
|
+
)
|
|
53
55
|
app.add_typer(segmentations.app, no_args_is_help=True, name="segmentations")
|
|
54
56
|
app.add_typer(segments.app, no_args_is_help=True, name="segments")
|
|
55
57
|
app.add_typer(sources.app, no_args_is_help=True, name="sources")
|
|
@@ -1,57 +0,0 @@
|
|
|
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())
|
|
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
|