validio-cli 5.2.0__tar.gz → 5.3.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-5.2.0 → validio_cli-5.3.1}/PKG-INFO +1 -1
- {validio_cli-5.2.0 → validio_cli-5.3.1}/pyproject.toml +1 -1
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/__init__.py +6 -3
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/sources.py +128 -17
- {validio_cli-5.2.0 → validio_cli-5.3.1}/LICENSE +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/README_PUBLIC.md +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/channels.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/code.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/config.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/credentials.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/dbt.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/filters.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/incidents.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/metrics.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/namespaces.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/notification_rules.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/resources.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/segmentations.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/segments.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/users.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/validators.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/entities/windows.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/bin/main.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/components.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/metadata.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.1}/validio_cli/namespace.py +0 -0
- {validio_cli-5.2.0 → validio_cli-5.3.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 = "5.
|
|
6
|
+
version = "5.3.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"
|
|
@@ -138,7 +138,7 @@ def output_json(obj: Any, identifier: str | None = None) -> None:
|
|
|
138
138
|
print(j)
|
|
139
139
|
|
|
140
140
|
|
|
141
|
-
def
|
|
141
|
+
def format_text(items: Any, fields: dict[str, OutputSettings | None]) -> str:
|
|
142
142
|
if items is None:
|
|
143
143
|
items = []
|
|
144
144
|
elif not isinstance(items, list):
|
|
@@ -175,8 +175,11 @@ def output_text(items: Any, fields: dict[str, OutputSettings | None]) -> None:
|
|
|
175
175
|
|
|
176
176
|
table.append(row)
|
|
177
177
|
|
|
178
|
-
|
|
179
|
-
|
|
178
|
+
return tabulate(table, tablefmt="plain")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def output_text(items: Any, fields: dict[str, OutputSettings | None]) -> None:
|
|
182
|
+
print(format_text(items=items, fields=fields))
|
|
180
183
|
|
|
181
184
|
# We flush stdout here so we can avoid an unhandled broken pipe error.
|
|
182
185
|
# Catching the exception according to the documentation at
|
|
@@ -2,7 +2,7 @@ import asyncio
|
|
|
2
2
|
import json
|
|
3
3
|
import uuid
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Any, cast
|
|
5
|
+
from typing import Any, Optional, cast
|
|
6
6
|
|
|
7
7
|
import click
|
|
8
8
|
import typer
|
|
@@ -30,12 +30,14 @@ from validio_cli import (
|
|
|
30
30
|
OutputFormatOption,
|
|
31
31
|
OutputSettings,
|
|
32
32
|
components,
|
|
33
|
+
format_text,
|
|
33
34
|
get_client,
|
|
34
35
|
output_json,
|
|
35
36
|
output_ok_or_error,
|
|
36
37
|
output_text,
|
|
37
38
|
)
|
|
38
39
|
from validio_cli.bin.entities import credentials
|
|
40
|
+
from validio_cli.components import proceed_with_operation
|
|
39
41
|
from validio_cli.namespace import get_namespace
|
|
40
42
|
|
|
41
43
|
app = AsyncTyper(help="Data sources to ingest data from")
|
|
@@ -132,25 +134,96 @@ async def delete(
|
|
|
132
134
|
return output_ok_or_error(result)
|
|
133
135
|
|
|
134
136
|
|
|
137
|
+
async def prompt_for_start_all_sources(
|
|
138
|
+
client: APIClient,
|
|
139
|
+
cfg: ValidioConfig,
|
|
140
|
+
namespace: str,
|
|
141
|
+
auto_approve: bool,
|
|
142
|
+
) -> list[str] | None:
|
|
143
|
+
if not namespace:
|
|
144
|
+
raise typer.BadParameter("Namespace must be supplied when using --all")
|
|
145
|
+
|
|
146
|
+
sources = await client.get_sources(
|
|
147
|
+
namespace_id=get_namespace(namespace, cfg),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if not isinstance(sources, list):
|
|
151
|
+
raise ValidioError("failed to get sources")
|
|
152
|
+
|
|
153
|
+
if len(sources) == 0:
|
|
154
|
+
print("No sources exist in namespace to start")
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
sources_to_start = [
|
|
158
|
+
source for source in sources if source.get("state") in ["IDLE", "INIT"]
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
if len(sources_to_start) == 0:
|
|
162
|
+
print("No sources in namespace that need starting")
|
|
163
|
+
return None
|
|
164
|
+
|
|
165
|
+
prompt = "The following sources will be started:\n"
|
|
166
|
+
prompt += format_text(
|
|
167
|
+
sources_to_start,
|
|
168
|
+
fields={
|
|
169
|
+
"id": OutputSettings(attribute_name="id"),
|
|
170
|
+
"resource_name": OutputSettings(attribute_name="resourceName"),
|
|
171
|
+
"type": OutputSettings.trimmed_upper_snake(
|
|
172
|
+
attribute_name="__typename", trim="Source"
|
|
173
|
+
),
|
|
174
|
+
"state": None,
|
|
175
|
+
},
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
print(prompt)
|
|
179
|
+
|
|
180
|
+
if not await proceed_with_operation(auto_approve):
|
|
181
|
+
return None
|
|
182
|
+
print()
|
|
183
|
+
|
|
184
|
+
return [source["id"] for source in sources_to_start]
|
|
185
|
+
|
|
186
|
+
|
|
135
187
|
@app.async_command(help="Start sources")
|
|
136
188
|
async def start(
|
|
137
189
|
config_dir: str = ConfigDir,
|
|
138
190
|
output_format: OutputFormat = OutputFormatOption,
|
|
139
191
|
# ruff: noqa: ARG001
|
|
140
192
|
namespace: str = Namespace(),
|
|
141
|
-
|
|
193
|
+
all: bool = typer.Option(False, "--all", help="Start all unstarted sources"),
|
|
194
|
+
auto_approve: bool = typer.Option(
|
|
195
|
+
False,
|
|
196
|
+
help="When used with --all, automatically approve starting of all sources",
|
|
197
|
+
),
|
|
198
|
+
identifiers: Optional[list[str]] = Identifiers,
|
|
142
199
|
) -> None:
|
|
143
200
|
client, cfg = get_client(config_dir)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
split_to_chunks(identifiers),
|
|
150
|
-
namespace,
|
|
151
|
-
output_format,
|
|
201
|
+
|
|
202
|
+
if all and identifiers and len(identifiers) > 0:
|
|
203
|
+
raise typer.BadParameter(
|
|
204
|
+
"Cannot use both --all and supplying source "
|
|
205
|
+
"identifiers. You must only use one of them"
|
|
152
206
|
)
|
|
153
207
|
|
|
208
|
+
if all:
|
|
209
|
+
identifiers = await prompt_for_start_all_sources(
|
|
210
|
+
client=client,
|
|
211
|
+
cfg=cfg,
|
|
212
|
+
namespace=namespace,
|
|
213
|
+
auto_approve=auto_approve,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
if identifiers:
|
|
217
|
+
async with client.client as session:
|
|
218
|
+
await apply_source_action_on_chunks(
|
|
219
|
+
session,
|
|
220
|
+
cfg,
|
|
221
|
+
SourceAction.START,
|
|
222
|
+
split_to_chunks(identifiers),
|
|
223
|
+
namespace,
|
|
224
|
+
output_format,
|
|
225
|
+
)
|
|
226
|
+
|
|
154
227
|
|
|
155
228
|
@app.async_command(help="Stop source")
|
|
156
229
|
async def stop(
|
|
@@ -1023,7 +1096,11 @@ async def apply_source_action_on_chunks(
|
|
|
1023
1096
|
]
|
|
1024
1097
|
)
|
|
1025
1098
|
|
|
1026
|
-
return handle_source_action_response(
|
|
1099
|
+
return await handle_source_action_response(
|
|
1100
|
+
results,
|
|
1101
|
+
action,
|
|
1102
|
+
output_format,
|
|
1103
|
+
)
|
|
1027
1104
|
|
|
1028
1105
|
|
|
1029
1106
|
async def apply_batch_source_action(
|
|
@@ -1049,15 +1126,17 @@ async def apply_single_source_action(
|
|
|
1049
1126
|
source_id = await _get_source_id(session, cfg, identifier, namespace)
|
|
1050
1127
|
if source_id is None:
|
|
1051
1128
|
return {
|
|
1052
|
-
f"source{
|
|
1053
|
-
"errors": [{"message": f"No source with id or name {identifier} found"}]
|
|
1054
|
-
}
|
|
1129
|
+
"errors": [{"message": f"No source with id or name {identifier} found"}]
|
|
1055
1130
|
}
|
|
1056
1131
|
|
|
1057
|
-
|
|
1132
|
+
result = await apply_source_action(session, action, source_id)
|
|
1133
|
+
return {
|
|
1134
|
+
"id": source_id,
|
|
1135
|
+
**result,
|
|
1136
|
+
}
|
|
1058
1137
|
|
|
1059
1138
|
|
|
1060
|
-
def handle_source_action_response(
|
|
1139
|
+
async def handle_source_action_response(
|
|
1061
1140
|
results: list[list[dict[str, Any]]],
|
|
1062
1141
|
action: SourceAction,
|
|
1063
1142
|
output_format: OutputFormat,
|
|
@@ -1086,7 +1165,19 @@ def handle_source_action_response(
|
|
|
1086
1165
|
if output_format == OutputFormat.JSON:
|
|
1087
1166
|
return output_json(result)
|
|
1088
1167
|
|
|
1089
|
-
|
|
1168
|
+
output_action_word = ""
|
|
1169
|
+
match action:
|
|
1170
|
+
case SourceAction.START:
|
|
1171
|
+
output_action_word = "starting"
|
|
1172
|
+
case SourceAction.STOP:
|
|
1173
|
+
output_action_word = "stopping"
|
|
1174
|
+
case SourceAction.BACKFILL:
|
|
1175
|
+
output_action_word = "initiating backfilling"
|
|
1176
|
+
case SourceAction.RESET:
|
|
1177
|
+
output_action_word = "resetting"
|
|
1178
|
+
|
|
1179
|
+
print(f"Summary of results when {output_action_word} sources:")
|
|
1180
|
+
output_text(
|
|
1090
1181
|
result,
|
|
1091
1182
|
fields={
|
|
1092
1183
|
field_name: None,
|
|
@@ -1094,6 +1185,26 @@ def handle_source_action_response(
|
|
|
1094
1185
|
},
|
|
1095
1186
|
)
|
|
1096
1187
|
|
|
1188
|
+
if len(error_responses) > 0:
|
|
1189
|
+
print()
|
|
1190
|
+
print(f"Failures details when {output_action_word} sources:")
|
|
1191
|
+
output_text(
|
|
1192
|
+
error_responses,
|
|
1193
|
+
fields={
|
|
1194
|
+
"source": OutputSettings(
|
|
1195
|
+
attribute_name="id",
|
|
1196
|
+
),
|
|
1197
|
+
"error": OutputSettings(
|
|
1198
|
+
attribute_name="errors",
|
|
1199
|
+
reformat=lambda errors: ",".join(
|
|
1200
|
+
[error["message"] for error in errors]
|
|
1201
|
+
),
|
|
1202
|
+
),
|
|
1203
|
+
},
|
|
1204
|
+
)
|
|
1205
|
+
|
|
1206
|
+
return None
|
|
1207
|
+
|
|
1097
1208
|
|
|
1098
1209
|
if __name__ == "__main__":
|
|
1099
1210
|
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
|
|
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
|