validio-cli 10.0.7__tar.gz → 10.2.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 (29) hide show
  1. {validio_cli-10.0.7 → validio_cli-10.2.0}/PKG-INFO +1 -1
  2. {validio_cli-10.0.7 → validio_cli-10.2.0}/pyproject.toml +3 -3
  3. {validio_cli-10.0.7 → validio_cli-10.2.0}/pyproject.toml.orig +3 -3
  4. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/__init__.py +13 -1
  5. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/channels.py +6 -3
  6. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/code.py +5 -1
  7. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/filters.py +6 -3
  8. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/integrations.py +2 -1
  9. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/notification_rules.py +6 -3
  10. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/resources.py +1 -1
  11. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/segmentations.py +6 -3
  12. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/sources.py +8 -5
  13. validio_cli-10.2.0/validio_cli/bin/entities/validator_debug_queries.py +133 -0
  14. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/validators.py +6 -3
  15. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/windows.py +6 -3
  16. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/main.py +6 -0
  17. {validio_cli-10.0.7 → validio_cli-10.2.0}/README_PUBLIC.md +0 -0
  18. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/config.py +0 -0
  19. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/credentials.py +0 -0
  20. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/dbt.py +0 -0
  21. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/incidents.py +0 -0
  22. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/metrics.py +0 -0
  23. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/namespaces.py +0 -0
  24. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/segments.py +0 -0
  25. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/bin/entities/users.py +0 -0
  26. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/components.py +0 -0
  27. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/metadata.py +0 -0
  28. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/namespace.py +0 -0
  29. {validio_cli-10.0.7 → validio_cli-10.2.0}/validio_cli/schema.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: validio-cli
3
- Version: 10.0.7
3
+ Version: 10.2.0
4
4
  Summary: CLI tool to interact with the Validio platform
5
5
  Author: Validio
6
6
  Author-email: Validio <support@validio.io>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "validio-cli"
3
- version = "10.0.7"
3
+ version = "10.2.0"
4
4
  description = "CLI tool to interact with the Validio platform"
5
5
  requires-python = ">=3.10,<4"
6
6
  readme = "README_PUBLIC.md"
@@ -39,7 +39,7 @@ dev = [
39
39
  "ruff==0.16.1",
40
40
  "mypy>=1.2.0,<2",
41
41
  "licensecheck>=2025.1.0,<2026",
42
- "pytest>=9.0.3,<10",
42
+ "pytest>=9.1.1,<10",
43
43
  "flameprof>=0.4,<0.5",
44
44
  "validio-sdk",
45
45
  ]
@@ -107,5 +107,5 @@ ignore_packages = ["MYPY-EXTENSIONS"]
107
107
  zero = true
108
108
 
109
109
  [build-system]
110
- requires = ["uv_build>=0.12.1,<0.13.0"]
110
+ requires = ["uv_build>=0.12.3,<0.13.0"]
111
111
  build-backend = "uv_build"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "validio-cli"
3
- version = "10.0.7"
3
+ version = "10.2.0"
4
4
  description = "CLI tool to interact with the Validio platform"
5
5
  authors = [{ name = "Validio", email = "support@validio.io" }]
6
6
  requires-python = ">=3.10,<4"
@@ -36,7 +36,7 @@ dev = [
36
36
  "ruff==0.16.1",
37
37
  "mypy>=1.2.0,<2",
38
38
  "licensecheck>=2025.1.0,<2026",
39
- "pytest>=9.0.3,<10",
39
+ "pytest>=9.1.1,<10",
40
40
  "flameprof>=0.4,<0.5",
41
41
  "validio-sdk",
42
42
  ]
@@ -52,7 +52,7 @@ module-name = ["validio_cli"]
52
52
  module-root = ""
53
53
 
54
54
  [build-system]
55
- requires = ["uv_build>=0.12.1,<0.13.0"]
55
+ requires = ["uv_build>=0.12.3,<0.13.0"]
56
56
  build-backend = "uv_build"
57
57
 
58
58
  [tool.ruff]
@@ -2,7 +2,7 @@ import asyncio
2
2
  import contextlib
3
3
  import json
4
4
  import sys
5
- from collections.abc import Callable
5
+ from collections.abc import Callable, Coroutine
6
6
  from dataclasses import dataclass
7
7
  from datetime import datetime, timezone
8
8
  from enum import Enum
@@ -19,12 +19,14 @@ from validio_sdk import config
19
19
  from validio_sdk._api.api import APIClient
20
20
  from validio_sdk.client import USER_AGENT as SDK_USER_AGENT
21
21
  from validio_sdk.config import Config, ValidioConfig
22
+ from validio_sdk.exception import ResourceNotFoundError
22
23
  from validio_sdk.features import Feature, all_features, set_enabled
23
24
  from validio_sdk.util import ClassJSONEncoder
24
25
 
25
26
  from validio_cli.metadata import version as cli_version
26
27
 
27
28
  F = TypeVar("F", bound=Callable[..., Any])
29
+ R = TypeVar("R")
28
30
  T = TypeVar("T", bound="OutputSettings")
29
31
 
30
32
  DEFAULT_NAMESPACE: str = "default"
@@ -132,6 +134,16 @@ def get_client(
132
134
  return APIClient(config=cfg, user_agent=user_agent, headers=headers), cfg
133
135
 
134
136
 
137
+ async def none_if_404(
138
+ lookup: Coroutine[Any, Any, R],
139
+ ) -> R | None:
140
+ """Return None if response is a 404."""
141
+ try:
142
+ return await lookup
143
+ except ResourceNotFoundError:
144
+ return None
145
+
146
+
135
147
  @dataclass
136
148
  class OutputSettings:
137
149
  attribute_name: str | None = None
@@ -15,6 +15,7 @@ from validio_cli import (
15
15
  ValidioConfig,
16
16
  _single_resource_if_specified,
17
17
  get_client,
18
+ none_if_404,
18
19
  output_json,
19
20
  output_ok_or_error,
20
21
  output_text,
@@ -105,9 +106,11 @@ async def get_channel_id(
105
106
  if identifier.startswith(prefix):
106
107
  return identifier
107
108
 
108
- resource = await vc.get_channels(
109
- channel_id=identifier,
110
- namespace_id=get_namespace(namespace, cfg),
109
+ resource = await none_if_404(
110
+ vc.get_channels(
111
+ channel_id=identifier,
112
+ namespace_id=get_namespace(namespace, cfg),
113
+ )
111
114
  )
112
115
 
113
116
  if resource is None:
@@ -209,6 +209,7 @@ async def plan(
209
209
  segmentation: list[str] = target_option("segmentation"),
210
210
  source: list[str] = target_option("source"),
211
211
  validator: list[str] = target_option("validator"),
212
+ validator_debug_query: list[str] = target_option("validator-debug-query"),
212
213
  window: list[str] = target_option("window"),
213
214
  progress: bool = show_progress_option,
214
215
  ) -> None:
@@ -243,6 +244,7 @@ async def plan(
243
244
  segmentations=set(segmentation),
244
245
  sources=set(source),
245
246
  validators=set(validator),
247
+ validator_debug_queries=set(validator_debug_query),
246
248
  windows=set(window),
247
249
  ),
248
250
  show_progress=progress,
@@ -273,6 +275,7 @@ async def apply(
273
275
  segmentation: list[str] = target_option("segmentation"),
274
276
  source: list[str] = target_option("source"),
275
277
  validator: list[str] = target_option("validator"),
278
+ validator_debug_query: list[str] = target_option("validator-debug-query"),
276
279
  window: list[str] = target_option("window"),
277
280
  progress: bool = show_progress_option,
278
281
  test_credentials: bool = typer.Option(
@@ -319,6 +322,7 @@ async def apply(
319
322
  segmentations=set(segmentation),
320
323
  sources=set(source),
321
324
  validators=set(validator),
325
+ validator_debug_queries=set(validator_debug_query),
322
326
  windows=set(window),
323
327
  ),
324
328
  show_progress=progress,
@@ -450,7 +454,7 @@ async def _import(
450
454
  tags_ctx=plan_result.tags_context,
451
455
  include_schema=include_schema,
452
456
  )
453
- output.write_text(s)
457
+ output.write_text(s, encoding="utf-8")
454
458
  print(f"Generated file {output.absolute()}")
455
459
 
456
460
 
@@ -14,6 +14,7 @@ from validio_cli import (
14
14
  OutputSettings,
15
15
  ValidioConfig,
16
16
  get_client,
17
+ none_if_404,
17
18
  output_json,
18
19
  output_ok_or_error,
19
20
  output_text,
@@ -113,9 +114,11 @@ async def get_filter_id(
113
114
  if identifier.startswith(prefix):
114
115
  return identifier
115
116
 
116
- resource = await vc.get_filters(
117
- filter_id=identifier,
118
- namespace_id=get_namespace(namespace, cfg),
117
+ resource = await none_if_404(
118
+ vc.get_filters(
119
+ filter_id=identifier,
120
+ namespace_id=get_namespace(namespace, cfg),
121
+ )
119
122
  )
120
123
 
121
124
  if resource is None:
@@ -13,6 +13,7 @@ from validio_cli import (
13
13
  OutputSettings,
14
14
  _single_resource_if_specified,
15
15
  get_client,
16
+ none_if_404,
16
17
  output_json,
17
18
  output_ok_or_error,
18
19
  output_text,
@@ -93,7 +94,7 @@ async def get_integration_id(vc: APIClient, identifier: str) -> str | None:
93
94
  if identifier.startswith(prefix):
94
95
  return identifier
95
96
 
96
- resource = await vc.get_integrations(integration_id=identifier)
97
+ resource = await none_if_404(vc.get_integrations(integration_id=identifier))
97
98
 
98
99
  if resource is None:
99
100
  print(f"No {identifier_type} with name or id {identifier} found")
@@ -15,6 +15,7 @@ from validio_cli import (
15
15
  OutputSettings,
16
16
  _single_resource_if_specified,
17
17
  get_client,
18
+ none_if_404,
18
19
  output_json,
19
20
  output_ok_or_error,
20
21
  output_text,
@@ -110,9 +111,11 @@ async def get_notification_rule_id(
110
111
  if identifier.startswith(prefix):
111
112
  return identifier
112
113
 
113
- resource = await vc.get_notification_rules(
114
- notification_rule_id=identifier,
115
- namespace_id=get_namespace(namespace, cfg),
114
+ resource = await none_if_404(
115
+ vc.get_notification_rules(
116
+ notification_rule_id=identifier,
117
+ namespace_id=get_namespace(namespace, cfg),
118
+ )
116
119
  )
117
120
 
118
121
  if resource is None:
@@ -113,7 +113,7 @@ def _read_resource_file(file: pathlib.Path) -> dict:
113
113
  if not file.is_file():
114
114
  raise ValidioError(f"'{file.absolute()}' is not a file")
115
115
 
116
- obj = json.loads(file.read_text())
116
+ obj = json.loads(file.read_text(encoding="utf-8"))
117
117
  if not isinstance(obj, dict):
118
118
  raise ValidioError(f"expected json object in resource file: got {type(obj)}")
119
119
 
@@ -14,6 +14,7 @@ from validio_cli import (
14
14
  OutputFormatOption,
15
15
  OutputSettings,
16
16
  get_client,
17
+ none_if_404,
17
18
  output_json,
18
19
  output_ok_or_error,
19
20
  output_text,
@@ -114,9 +115,11 @@ async def get_segmentation_id(
114
115
  if identifier.startswith(prefix):
115
116
  return identifier
116
117
 
117
- resource = await vc.get_segmentations(
118
- segmentation_id=identifier,
119
- namespace_id=get_namespace(namespace, cfg),
118
+ resource = await none_if_404(
119
+ vc.get_segmentations(
120
+ segmentation_id=identifier,
121
+ namespace_id=get_namespace(namespace, cfg),
122
+ )
120
123
  )
121
124
 
122
125
  if resource is None:
@@ -31,6 +31,7 @@ from validio_cli import (
31
31
  components,
32
32
  format_text,
33
33
  get_client,
34
+ none_if_404,
34
35
  output_json,
35
36
  output_ok_or_error,
36
37
  output_text,
@@ -953,7 +954,7 @@ async def _infer_schema_bigquery(
953
954
 
954
955
 
955
956
  def _write_schema(filename: Path, schema: Any) -> None:
956
- filename.write_text(json.dumps(schema, indent=2))
957
+ filename.write_text(json.dumps(schema, indent=2), encoding="utf-8")
957
958
 
958
959
  print(f"Schema written to {filename}")
959
960
 
@@ -997,10 +998,12 @@ async def _get_source_id(
997
998
  if identifier.startswith(prefix):
998
999
  return identifier
999
1000
 
1000
- resource = await get_sources(
1001
- session,
1002
- source_id=identifier,
1003
- namespace_id=get_namespace(namespace, cfg),
1001
+ resource = await none_if_404(
1002
+ get_sources(
1003
+ session,
1004
+ source_id=identifier,
1005
+ namespace_id=get_namespace(namespace, cfg),
1006
+ )
1004
1007
  )
1005
1008
 
1006
1009
  if resource is None:
@@ -0,0 +1,133 @@
1
+ import typer
2
+ from validio_sdk._api.api import APIClient
3
+ from validio_sdk.config import ValidioConfig
4
+ from validio_sdk.exception import ValidioError
5
+
6
+ from validio_cli import (
7
+ AsyncTyper,
8
+ ConfigDir,
9
+ Identifier,
10
+ Identifiers,
11
+ Namespace,
12
+ OutputFormat,
13
+ OutputFormatOption,
14
+ OutputSettings,
15
+ get_client,
16
+ none_if_404,
17
+ output_json,
18
+ output_ok_or_error,
19
+ output_text,
20
+ )
21
+ from validio_cli.bin.entities.validators import get_validator_id
22
+ from validio_cli.namespace import get_namespace
23
+
24
+ app = AsyncTyper(help="Debug queries saved on validators")
25
+
26
+
27
+ @app.async_command(help="List validator debug queries")
28
+ async def get(
29
+ config_dir: str = ConfigDir,
30
+ output_format: OutputFormat = OutputFormatOption,
31
+ namespace: str = Namespace(),
32
+ identifier: str = Identifier,
33
+ validator: str = typer.Option(
34
+ None, help="List debug queries for this Validator (name or ID)"
35
+ ),
36
+ ) -> None:
37
+ if identifier and validator:
38
+ raise ValidioError("--validator can't be used together with an identifier")
39
+
40
+ vc, cfg = get_client(config_dir)
41
+
42
+ validator_id = (
43
+ await get_validator_id(vc, cfg, validator, namespace) if validator else None
44
+ )
45
+
46
+ debug_queries = await vc.get_validator_debug_queries(
47
+ validator_debug_query_id=identifier,
48
+ validator_id=validator_id,
49
+ namespace_id=get_namespace(namespace, cfg),
50
+ )
51
+
52
+ if output_format == OutputFormat.JSON:
53
+ return output_json(debug_queries, identifier)
54
+
55
+ return output_text(
56
+ debug_queries,
57
+ fields={
58
+ "name": OutputSettings(attribute_name="resourceName"),
59
+ "validator": OutputSettings(
60
+ reformat=lambda validator: validator["resourceName"]
61
+ ),
62
+ "default": OutputSettings(attribute_name="defaultQuery"),
63
+ "age": OutputSettings.string_as_datetime(attribute_name="createdAt"),
64
+ },
65
+ )
66
+
67
+
68
+ @app.async_command(help="Delete validator debug query")
69
+ async def delete(
70
+ config_dir: str = ConfigDir,
71
+ output_format: OutputFormat = OutputFormatOption,
72
+ namespace: str = Namespace(),
73
+ identifiers: list[str] = Identifiers,
74
+ ) -> None:
75
+ if not identifiers:
76
+ raise ValidioError("no identifiers are provided")
77
+
78
+ vc, cfg = get_client(config_dir)
79
+
80
+ debug_query_ids = [
81
+ debug_query_id
82
+ for debug_query_id in [
83
+ await get_validator_debug_query_id(vc, cfg, identifier, namespace)
84
+ for identifier in identifiers
85
+ ]
86
+ if debug_query_id
87
+ ]
88
+
89
+ result = await vc.delete_validator_debug_queries(debug_query_ids)
90
+
91
+ if output_format == OutputFormat.JSON:
92
+ return output_json(result)
93
+
94
+ return output_ok_or_error(result)
95
+
96
+
97
+ async def get_validator_debug_query_id(
98
+ vc: APIClient, cfg: ValidioConfig, identifier: str, namespace: str
99
+ ) -> str | None:
100
+ """
101
+ Ensure the identifier is a resource id.
102
+
103
+ If it doesn't have the expected prefix, do a resource lookup by name.
104
+ """
105
+ identifier_type = "validator debug query"
106
+ prefix = "VDQ_"
107
+
108
+ if not identifier:
109
+ print(f"Missing {identifier_type} id or name")
110
+ return None
111
+
112
+ if identifier.startswith(prefix):
113
+ return identifier
114
+
115
+ resource = await none_if_404(
116
+ vc.get_validator_debug_queries(
117
+ validator_debug_query_id=identifier,
118
+ namespace_id=get_namespace(namespace, cfg),
119
+ )
120
+ )
121
+
122
+ if resource is None:
123
+ print(f"No {identifier_type} with name or id {identifier} found")
124
+ return None
125
+
126
+ if not isinstance(resource, dict):
127
+ raise ValidioError("failed to get validator debug query id")
128
+
129
+ return resource["id"]
130
+
131
+
132
+ if __name__ == "__main__":
133
+ app()
@@ -14,6 +14,7 @@ from validio_cli import (
14
14
  OutputFormatOption,
15
15
  OutputSettings,
16
16
  get_client,
17
+ none_if_404,
17
18
  output_json,
18
19
  output_ok_or_error,
19
20
  output_text,
@@ -125,9 +126,11 @@ async def get_validator_id(
125
126
  if identifier.startswith(prefix):
126
127
  return identifier
127
128
 
128
- resource = await vc.get_validators(
129
- validator_id=identifier,
130
- namespace_id=get_namespace(namespace, cfg),
129
+ resource = await none_if_404(
130
+ vc.get_validators(
131
+ validator_id=identifier,
132
+ namespace_id=get_namespace(namespace, cfg),
133
+ )
131
134
  )
132
135
 
133
136
  if resource is None:
@@ -15,6 +15,7 @@ from validio_cli import (
15
15
  OutputFormatOption,
16
16
  OutputSettings,
17
17
  get_client,
18
+ none_if_404,
18
19
  output_json,
19
20
  output_ok_or_error,
20
21
  output_text,
@@ -118,9 +119,11 @@ async def get_window_id(
118
119
  if identifier.startswith(prefix):
119
120
  return identifier
120
121
 
121
- resource = await vc.get_windows(
122
- window_id=identifier,
123
- namespace_id=get_namespace(namespace, cfg),
122
+ resource = await none_if_404(
123
+ vc.get_windows(
124
+ window_id=identifier,
125
+ namespace_id=get_namespace(namespace, cfg),
126
+ )
124
127
  )
125
128
 
126
129
  if resource is None:
@@ -30,6 +30,7 @@ from validio_cli.bin.entities import (
30
30
  segments,
31
31
  sources,
32
32
  users,
33
+ validator_debug_queries,
33
34
  validators,
34
35
  windows,
35
36
  )
@@ -78,6 +79,11 @@ app.add_typer(segments.app, no_args_is_help=True, name="segments")
78
79
  app.add_typer(sources.app, no_args_is_help=True, name="sources")
79
80
  app.add_typer(users.app, no_args_is_help=True, name="users")
80
81
  app.add_typer(validators.app, no_args_is_help=True, name="validators")
82
+ app.add_typer(
83
+ validator_debug_queries.app,
84
+ no_args_is_help=True,
85
+ name="validator-debug-queries",
86
+ )
81
87
  app.add_typer(windows.app, no_args_is_help=True, name="windows")
82
88
 
83
89