validio-cli 0.29.0__tar.gz → 5.3.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-0.29.0 → validio_cli-5.3.0}/PKG-INFO +1 -1
- {validio_cli-0.29.0 → validio_cli-5.3.0}/pyproject.toml +1 -1
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/__init__.py +6 -3
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/sources.py +128 -221
- {validio_cli-0.29.0 → validio_cli-5.3.0}/LICENSE +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/README_PUBLIC.md +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/channels.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/code.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/config.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/credentials.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/dbt.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/filters.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/incidents.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/metrics.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/namespaces.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/notification_rules.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/resources.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/segmentations.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/segments.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/users.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/validators.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/entities/windows.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/bin/main.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/components.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/metadata.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/validio_cli/namespace.py +0 -0
- {validio_cli-0.29.0 → validio_cli-5.3.0}/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 = "
|
|
6
|
+
version = "5.3.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"
|
|
@@ -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(
|
|
@@ -271,13 +344,11 @@ async def _interactive(config_dir: str, filename: Path, namespace: str) -> None:
|
|
|
271
344
|
credential_type_to_source_type = {
|
|
272
345
|
"AwsCredential": [
|
|
273
346
|
("kinesis", "Amazon Kinesis"),
|
|
274
|
-
("s3", "Amazon S3"),
|
|
275
347
|
],
|
|
276
348
|
"DatabricksCredential": [("databricks", "Databricks")],
|
|
277
349
|
"DemoCredential": [("demo", "Demo")],
|
|
278
350
|
"GcpCredential": [
|
|
279
351
|
("bigquery", "Google BigQuery"),
|
|
280
|
-
("gcs", "Google Cloud Storage"),
|
|
281
352
|
("pubsub", "Google Pub/Sub"),
|
|
282
353
|
],
|
|
283
354
|
"PostgreSqlCredential": [("postgresql", "PostgreSQL")],
|
|
@@ -458,29 +529,6 @@ async def _interactive(config_dir: str, filename: Path, namespace: str) -> None:
|
|
|
458
529
|
info.get("message_format", ""),
|
|
459
530
|
info.get("message_schema", ""),
|
|
460
531
|
)
|
|
461
|
-
case "s3":
|
|
462
|
-
info = await _multip_prompt(
|
|
463
|
-
[
|
|
464
|
-
("Bucket", [], ""),
|
|
465
|
-
("File pattern", [], "*.csv"),
|
|
466
|
-
("Prefix", [], ""),
|
|
467
|
-
("CSV delimiter", [",", "|"], ","),
|
|
468
|
-
]
|
|
469
|
-
)
|
|
470
|
-
|
|
471
|
-
print()
|
|
472
|
-
null_marker = await _get_null_marker()
|
|
473
|
-
|
|
474
|
-
await _infer_schema_s3(
|
|
475
|
-
vc,
|
|
476
|
-
filename,
|
|
477
|
-
credential_id,
|
|
478
|
-
info.get("bucket", ""),
|
|
479
|
-
info.get("file_pattern", ""),
|
|
480
|
-
info.get("prefix", ""),
|
|
481
|
-
info.get("csv_delimiter", ""),
|
|
482
|
-
null_marker,
|
|
483
|
-
)
|
|
484
532
|
case "postgresql":
|
|
485
533
|
info = await _multip_prompt(
|
|
486
534
|
[
|
|
@@ -549,31 +597,6 @@ async def _interactive(config_dir: str, filename: Path, namespace: str) -> None:
|
|
|
549
597
|
info.get("dataset", ""),
|
|
550
598
|
info.get("table", ""),
|
|
551
599
|
)
|
|
552
|
-
case "gcs":
|
|
553
|
-
info = await _multip_prompt(
|
|
554
|
-
[
|
|
555
|
-
("Project", [], ""),
|
|
556
|
-
("Bucket", [], ""),
|
|
557
|
-
("Folder", [], ""),
|
|
558
|
-
("File pattern", ["*.csv"], "*.csv"),
|
|
559
|
-
("CSV delimiter", [",", "|"], ","),
|
|
560
|
-
]
|
|
561
|
-
)
|
|
562
|
-
|
|
563
|
-
print()
|
|
564
|
-
null_marker = await _get_null_marker()
|
|
565
|
-
|
|
566
|
-
await _infer_schema_gcs(
|
|
567
|
-
vc,
|
|
568
|
-
filename,
|
|
569
|
-
credential_id,
|
|
570
|
-
info.get("project", ""),
|
|
571
|
-
info.get("bucket", ""),
|
|
572
|
-
info.get("folder", ""),
|
|
573
|
-
info.get("file_pattern", ""),
|
|
574
|
-
info.get("csv_delimiter", ""),
|
|
575
|
-
null_marker,
|
|
576
|
-
)
|
|
577
600
|
case _:
|
|
578
601
|
print("Not yet implemented...")
|
|
579
602
|
return
|
|
@@ -588,40 +611,6 @@ async def demo(
|
|
|
588
611
|
await _infer_schema_demo(vc, filename)
|
|
589
612
|
|
|
590
613
|
|
|
591
|
-
@infer_schema_app.async_command(help="Infer Amazon S3 schema")
|
|
592
|
-
async def s3(
|
|
593
|
-
config_dir: str = ConfigDir,
|
|
594
|
-
filename: Path = schema_filename_option("s3"),
|
|
595
|
-
namespace: str = Namespace(),
|
|
596
|
-
credential_id: str = typer.Option(..., help="Credential name or ID"),
|
|
597
|
-
bucket: str = typer.Option(..., help="S3 bucket name"),
|
|
598
|
-
file_pattern: str = typer.Option(
|
|
599
|
-
"*.csv", help="File glob pattern - files to use for inference"
|
|
600
|
-
),
|
|
601
|
-
prefix: str = typer.Option("", help="Prefix in the bucket, a directory"),
|
|
602
|
-
csv_delimiter: str = typer.Option(",", help="Delimiter between columns in file"),
|
|
603
|
-
null_marker: str = typer.Option(None, help="Null marker (values to treat as NULL)"),
|
|
604
|
-
) -> None:
|
|
605
|
-
vc, cfg = get_client(config_dir)
|
|
606
|
-
|
|
607
|
-
resolved_credential_id = await credentials.get_credential_id(
|
|
608
|
-
vc, cfg, credential_id, namespace
|
|
609
|
-
)
|
|
610
|
-
if resolved_credential_id is None:
|
|
611
|
-
return
|
|
612
|
-
|
|
613
|
-
await _infer_schema_s3(
|
|
614
|
-
vc,
|
|
615
|
-
filename,
|
|
616
|
-
resolved_credential_id,
|
|
617
|
-
bucket,
|
|
618
|
-
file_pattern,
|
|
619
|
-
prefix,
|
|
620
|
-
csv_delimiter,
|
|
621
|
-
null_marker,
|
|
622
|
-
)
|
|
623
|
-
|
|
624
|
-
|
|
625
614
|
@infer_schema_app.async_command(help="Infer Amazon Kinesis schema")
|
|
626
615
|
async def kinesis(
|
|
627
616
|
config_dir: str = ConfigDir,
|
|
@@ -792,40 +781,6 @@ async def bigquery(
|
|
|
792
781
|
)
|
|
793
782
|
|
|
794
783
|
|
|
795
|
-
@infer_schema_app.async_command(help="Infer Google Cloud Storage schema")
|
|
796
|
-
async def gcs(
|
|
797
|
-
config_dir: str = ConfigDir,
|
|
798
|
-
filename: Path = schema_filename_option("gcs"),
|
|
799
|
-
namespace: str = Namespace(),
|
|
800
|
-
credential_id: str = typer.Option(..., help="Credential name or ID"),
|
|
801
|
-
project: str = typer.Option(..., help="Google project name"),
|
|
802
|
-
bucket: str = typer.Option(..., help="GCS bucket"),
|
|
803
|
-
folder: str = typer.Option("", help="Folder in the bucket"),
|
|
804
|
-
file_pattern: str = typer.Option("*.csv", help="File glob"),
|
|
805
|
-
csv_delimiter: str = typer.Option(",", help="Delimiter between columns in file"),
|
|
806
|
-
null_marker: str = typer.Option(None, help="Null marker (values to treat as NULL)"),
|
|
807
|
-
) -> None:
|
|
808
|
-
vc, cfg = get_client(config_dir)
|
|
809
|
-
|
|
810
|
-
resolved_credential_id = await credentials.get_credential_id(
|
|
811
|
-
vc, cfg, credential_id, namespace
|
|
812
|
-
)
|
|
813
|
-
if resolved_credential_id is None:
|
|
814
|
-
return
|
|
815
|
-
|
|
816
|
-
await _infer_schema_gcs(
|
|
817
|
-
vc,
|
|
818
|
-
filename,
|
|
819
|
-
resolved_credential_id,
|
|
820
|
-
project,
|
|
821
|
-
bucket,
|
|
822
|
-
folder,
|
|
823
|
-
file_pattern,
|
|
824
|
-
csv_delimiter,
|
|
825
|
-
null_marker,
|
|
826
|
-
)
|
|
827
|
-
|
|
828
|
-
|
|
829
784
|
async def _infer_schema_databricks(
|
|
830
785
|
vc: APIClient,
|
|
831
786
|
filename: Path,
|
|
@@ -855,32 +810,6 @@ async def _infer_schema_demo(
|
|
|
855
810
|
_write_schema(filename, schema)
|
|
856
811
|
|
|
857
812
|
|
|
858
|
-
async def _infer_schema_s3(
|
|
859
|
-
vc: APIClient,
|
|
860
|
-
filename: Path,
|
|
861
|
-
credential_id: str,
|
|
862
|
-
bucket: str,
|
|
863
|
-
file_pattern: str,
|
|
864
|
-
prefix: str,
|
|
865
|
-
csv_delimiter: str,
|
|
866
|
-
null_marker: str | None,
|
|
867
|
-
) -> None:
|
|
868
|
-
schema = await vc.infer_schema(
|
|
869
|
-
class_name="AwsS3",
|
|
870
|
-
variable_values={
|
|
871
|
-
"credentialId": credential_id,
|
|
872
|
-
"bucket": bucket,
|
|
873
|
-
"filePattern": file_pattern,
|
|
874
|
-
"prefix": prefix,
|
|
875
|
-
"csv": {
|
|
876
|
-
"delimiter": csv_delimiter,
|
|
877
|
-
"nullMarker": null_marker,
|
|
878
|
-
},
|
|
879
|
-
},
|
|
880
|
-
)
|
|
881
|
-
_write_schema(filename, schema)
|
|
882
|
-
|
|
883
|
-
|
|
884
813
|
async def _infer_schema_kinesis(
|
|
885
814
|
vc: APIClient,
|
|
886
815
|
filename: Path,
|
|
@@ -1029,34 +958,6 @@ async def _infer_schema_bigquery(
|
|
|
1029
958
|
_write_schema(filename, schema)
|
|
1030
959
|
|
|
1031
960
|
|
|
1032
|
-
async def _infer_schema_gcs(
|
|
1033
|
-
vc: APIClient,
|
|
1034
|
-
filename: Path,
|
|
1035
|
-
credential_id: str,
|
|
1036
|
-
project: str,
|
|
1037
|
-
bucket: str,
|
|
1038
|
-
folder: str,
|
|
1039
|
-
file_pattern: str,
|
|
1040
|
-
csv_delimiter: str,
|
|
1041
|
-
null_marker: str | None,
|
|
1042
|
-
) -> None:
|
|
1043
|
-
schema = await vc.infer_schema(
|
|
1044
|
-
class_name="GcpStorage",
|
|
1045
|
-
variable_values={
|
|
1046
|
-
"credentialId": credential_id,
|
|
1047
|
-
"project": project,
|
|
1048
|
-
"bucket": bucket,
|
|
1049
|
-
"folder": folder,
|
|
1050
|
-
"filePattern": file_pattern,
|
|
1051
|
-
"csv": {
|
|
1052
|
-
"delimiter": csv_delimiter,
|
|
1053
|
-
"nullMarker": null_marker,
|
|
1054
|
-
},
|
|
1055
|
-
},
|
|
1056
|
-
)
|
|
1057
|
-
_write_schema(filename, schema)
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
961
|
def _write_schema(filename: Path, schema: Any) -> None:
|
|
1061
962
|
filename.write_text(json.dumps(schema, indent=2))
|
|
1062
963
|
|
|
@@ -1081,38 +982,6 @@ async def _multip_prompt(questions: list[tuple[str, list[str], str]]) -> dict[st
|
|
|
1081
982
|
return answers
|
|
1082
983
|
|
|
1083
984
|
|
|
1084
|
-
async def _get_null_marker() -> str | None:
|
|
1085
|
-
null_marker = await components.radiolist_dialog(
|
|
1086
|
-
title="Null marker - What value should be treated as NULL",
|
|
1087
|
-
values=[
|
|
1088
|
-
("none", "None (No value is treated as NULL)"),
|
|
1089
|
-
("NULL", "The literal string 'NULL', f.ex. 'a,NULL,c'"),
|
|
1090
|
-
("empty", "Empty (\"\", f.ex 'a,,c')"),
|
|
1091
|
-
("other", "Set manually"),
|
|
1092
|
-
],
|
|
1093
|
-
)
|
|
1094
|
-
|
|
1095
|
-
# User hit escape sequence, don't return None in this case.
|
|
1096
|
-
if null_marker is None:
|
|
1097
|
-
raise typer.Exit(code=1)
|
|
1098
|
-
|
|
1099
|
-
if null_marker == "other":
|
|
1100
|
-
session: PromptSession = PromptSession()
|
|
1101
|
-
null_marker = await session.prompt_async(
|
|
1102
|
-
validio_cli._fixed_width("Null marker"),
|
|
1103
|
-
)
|
|
1104
|
-
|
|
1105
|
-
if null_marker == "none":
|
|
1106
|
-
return None
|
|
1107
|
-
|
|
1108
|
-
# We can't map the value to '""' because it will make it pre-selected in the
|
|
1109
|
-
# dialog which we don't want.
|
|
1110
|
-
if null_marker == "empty":
|
|
1111
|
-
return ""
|
|
1112
|
-
|
|
1113
|
-
return null_marker
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
985
|
async def _get_source_id(
|
|
1117
986
|
session: Session,
|
|
1118
987
|
cfg: ValidioConfig,
|
|
@@ -1227,7 +1096,11 @@ async def apply_source_action_on_chunks(
|
|
|
1227
1096
|
]
|
|
1228
1097
|
)
|
|
1229
1098
|
|
|
1230
|
-
return handle_source_action_response(
|
|
1099
|
+
return await handle_source_action_response(
|
|
1100
|
+
results,
|
|
1101
|
+
action,
|
|
1102
|
+
output_format,
|
|
1103
|
+
)
|
|
1231
1104
|
|
|
1232
1105
|
|
|
1233
1106
|
async def apply_batch_source_action(
|
|
@@ -1253,15 +1126,17 @@ async def apply_single_source_action(
|
|
|
1253
1126
|
source_id = await _get_source_id(session, cfg, identifier, namespace)
|
|
1254
1127
|
if source_id is None:
|
|
1255
1128
|
return {
|
|
1256
|
-
f"source{
|
|
1257
|
-
"errors": [{"message": f"No source with id or name {identifier} found"}]
|
|
1258
|
-
}
|
|
1129
|
+
"errors": [{"message": f"No source with id or name {identifier} found"}]
|
|
1259
1130
|
}
|
|
1260
1131
|
|
|
1261
|
-
|
|
1132
|
+
result = await apply_source_action(session, action, source_id)
|
|
1133
|
+
return {
|
|
1134
|
+
"id": source_id,
|
|
1135
|
+
**result,
|
|
1136
|
+
}
|
|
1262
1137
|
|
|
1263
1138
|
|
|
1264
|
-
def handle_source_action_response(
|
|
1139
|
+
async def handle_source_action_response(
|
|
1265
1140
|
results: list[list[dict[str, Any]]],
|
|
1266
1141
|
action: SourceAction,
|
|
1267
1142
|
output_format: OutputFormat,
|
|
@@ -1290,7 +1165,19 @@ def handle_source_action_response(
|
|
|
1290
1165
|
if output_format == OutputFormat.JSON:
|
|
1291
1166
|
return output_json(result)
|
|
1292
1167
|
|
|
1293
|
-
|
|
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(
|
|
1294
1181
|
result,
|
|
1295
1182
|
fields={
|
|
1296
1183
|
field_name: None,
|
|
@@ -1298,6 +1185,26 @@ def handle_source_action_response(
|
|
|
1298
1185
|
},
|
|
1299
1186
|
)
|
|
1300
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
|
+
|
|
1301
1208
|
|
|
1302
1209
|
if __name__ == "__main__":
|
|
1303
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
|