tinybird 0.0.1.dev234__py3-none-any.whl → 0.0.1.dev235__py3-none-any.whl
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.
Potentially problematic release.
This version of tinybird might be problematic. Click here for more details.
- tinybird/tb/__cli__.py +2 -2
- tinybird/tb/check_pypi.py +3 -8
- tinybird/tb/cli.py +0 -6
- tinybird/tb/client.py +314 -340
- tinybird/tb/config.py +4 -5
- tinybird/tb/modules/build.py +21 -24
- tinybird/tb/modules/cicd.py +2 -2
- tinybird/tb/modules/cli.py +18 -28
- tinybird/tb/modules/common.py +123 -138
- tinybird/tb/modules/config.py +2 -4
- tinybird/tb/modules/connection.py +21 -26
- tinybird/tb/modules/copy.py +7 -9
- tinybird/tb/modules/create.py +18 -21
- tinybird/tb/modules/datafile/build.py +39 -39
- tinybird/tb/modules/datafile/build_common.py +9 -9
- tinybird/tb/modules/datafile/build_datasource.py +24 -24
- tinybird/tb/modules/datafile/build_pipe.py +11 -13
- tinybird/tb/modules/datafile/diff.py +12 -12
- tinybird/tb/modules/datafile/format_datasource.py +5 -5
- tinybird/tb/modules/datafile/format_pipe.py +6 -6
- tinybird/tb/modules/datafile/playground.py +42 -42
- tinybird/tb/modules/datafile/pull.py +24 -26
- tinybird/tb/modules/datasource.py +42 -56
- tinybird/tb/modules/endpoint.py +14 -19
- tinybird/tb/modules/info.py +14 -15
- tinybird/tb/modules/infra.py +43 -48
- tinybird/tb/modules/job.py +7 -10
- tinybird/tb/modules/local.py +6 -12
- tinybird/tb/modules/local_common.py +4 -4
- tinybird/tb/modules/login.py +9 -10
- tinybird/tb/modules/materialization.py +7 -10
- tinybird/tb/modules/mock.py +8 -9
- tinybird/tb/modules/open.py +1 -3
- tinybird/tb/modules/pipe.py +2 -4
- tinybird/tb/modules/secret.py +12 -16
- tinybird/tb/modules/shell.py +7 -20
- tinybird/tb/modules/sink.py +6 -8
- tinybird/tb/modules/test.py +9 -14
- tinybird/tb/modules/tinyunit/tinyunit.py +3 -3
- tinybird/tb/modules/token.py +16 -24
- tinybird/tb/modules/watch.py +3 -7
- tinybird/tb/modules/workspace.py +26 -37
- tinybird/tb/modules/workspace_members.py +16 -23
- {tinybird-0.0.1.dev234.dist-info → tinybird-0.0.1.dev235.dist-info}/METADATA +1 -1
- tinybird-0.0.1.dev235.dist-info/RECORD +89 -0
- tinybird-0.0.1.dev234.dist-info/RECORD +0 -89
- {tinybird-0.0.1.dev234.dist-info → tinybird-0.0.1.dev235.dist-info}/WHEEL +0 -0
- {tinybird-0.0.1.dev234.dist-info → tinybird-0.0.1.dev235.dist-info}/entry_points.txt +0 -0
- {tinybird-0.0.1.dev234.dist-info → tinybird-0.0.1.dev235.dist-info}/top_level.txt +0 -0
tinybird/tb/modules/common.py
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
# the function to the proper command file.
|
|
6
6
|
# - Please, **do not** define commands here.
|
|
7
7
|
|
|
8
|
-
import asyncio
|
|
9
8
|
import json
|
|
10
9
|
import os
|
|
11
10
|
import re
|
|
@@ -16,13 +15,11 @@ import uuid
|
|
|
16
15
|
from contextlib import closing
|
|
17
16
|
from copy import deepcopy
|
|
18
17
|
from enum import Enum
|
|
19
|
-
from functools import wraps
|
|
20
18
|
from os import environ, getcwd, getenv
|
|
21
19
|
from pathlib import Path
|
|
22
20
|
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Literal, Optional, Set, Tuple, TypedDict, Union
|
|
23
21
|
from urllib.parse import urlparse
|
|
24
22
|
|
|
25
|
-
import aiofiles
|
|
26
23
|
import click
|
|
27
24
|
import click.formatting
|
|
28
25
|
import humanfriendly
|
|
@@ -58,7 +55,6 @@ from tinybird.tb.modules.table import format_table
|
|
|
58
55
|
if TYPE_CHECKING:
|
|
59
56
|
from tinybird.connectors import Connector
|
|
60
57
|
|
|
61
|
-
from tinybird.syncasync import async_to_sync, sync_to_async
|
|
62
58
|
from tinybird.tb.modules.config import CLIConfig
|
|
63
59
|
from tinybird.tb.modules.exceptions import (
|
|
64
60
|
CLIAuthException,
|
|
@@ -97,22 +93,14 @@ def create_connector(connector: str, options: Dict[str, Any]):
|
|
|
97
93
|
return _create_connector(connector, options)
|
|
98
94
|
|
|
99
95
|
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
async def gather_with_concurrency(n, *tasks):
|
|
109
|
-
semaphore = asyncio.Semaphore(n)
|
|
110
|
-
|
|
111
|
-
async def sem_task(task):
|
|
112
|
-
async with semaphore:
|
|
113
|
-
return await task
|
|
114
|
-
|
|
115
|
-
return await asyncio.gather(*(sem_task(task) for task in tasks))
|
|
96
|
+
def gather_with_concurrency(n, *tasks):
|
|
97
|
+
results = []
|
|
98
|
+
for task in tasks:
|
|
99
|
+
if callable(task):
|
|
100
|
+
results.append(task())
|
|
101
|
+
else:
|
|
102
|
+
results.append(task)
|
|
103
|
+
return results
|
|
116
104
|
|
|
117
105
|
|
|
118
106
|
def format_robust_table(data: Iterable[Any], column_names: List[str]):
|
|
@@ -201,9 +189,9 @@ def generate_datafile(
|
|
|
201
189
|
return f
|
|
202
190
|
|
|
203
191
|
|
|
204
|
-
|
|
192
|
+
def get_current_workspace(config: CLIConfig) -> Optional[Dict[str, Any]]:
|
|
205
193
|
client = config.get_client()
|
|
206
|
-
workspaces: List[Dict[str, Any]] = (
|
|
194
|
+
workspaces: List[Dict[str, Any]] = (client.user_workspaces_and_branches(version="v1")).get("workspaces", [])
|
|
207
195
|
return _get_current_workspace_common(workspaces, config["id"])
|
|
208
196
|
|
|
209
197
|
|
|
@@ -217,8 +205,8 @@ def _get_current_workspace_common(
|
|
|
217
205
|
return next((workspace for workspace in workspaces if workspace["id"] == current_workspace_id), None)
|
|
218
206
|
|
|
219
207
|
|
|
220
|
-
|
|
221
|
-
workspaces: List[Dict[str, Any]] = (
|
|
208
|
+
def get_current_environment(client, config):
|
|
209
|
+
workspaces: List[Dict[str, Any]] = (client.user_workspaces_and_branches(version="v1")).get("workspaces", [])
|
|
222
210
|
return next((workspace for workspace in workspaces if workspace["id"] == config["id"]), None)
|
|
223
211
|
|
|
224
212
|
|
|
@@ -383,36 +371,36 @@ def create_tb_client(ctx: Context) -> TinyB:
|
|
|
383
371
|
return _get_tb_client(token, host)
|
|
384
372
|
|
|
385
373
|
|
|
386
|
-
|
|
374
|
+
def _analyze(filename: str, client: TinyB, format: str, connector: Optional["Connector"] = None):
|
|
387
375
|
data: Optional[bytes] = None
|
|
388
376
|
if not connector:
|
|
389
377
|
parsed = urlparse(filename)
|
|
390
378
|
if parsed.scheme in ("http", "https"):
|
|
391
|
-
meta =
|
|
379
|
+
meta = client.datasource_analyze(filename)
|
|
392
380
|
else:
|
|
393
|
-
|
|
381
|
+
with open(filename, "rb") as file:
|
|
394
382
|
# We need to read the whole file in binary for Parquet, while for the
|
|
395
383
|
# others we just read 1KiB
|
|
396
384
|
if format == "parquet":
|
|
397
|
-
data =
|
|
385
|
+
data = file.read()
|
|
398
386
|
else:
|
|
399
|
-
data =
|
|
387
|
+
data = file.read(1024 * 1024)
|
|
400
388
|
|
|
401
|
-
meta =
|
|
389
|
+
meta = client.datasource_analyze_file(data)
|
|
402
390
|
else:
|
|
403
391
|
meta = connector.datasource_analyze(filename)
|
|
404
392
|
return meta, data
|
|
405
393
|
|
|
406
394
|
|
|
407
|
-
|
|
408
|
-
meta, data =
|
|
395
|
+
def analyze_file(filename: str, client: TinyB, format: str):
|
|
396
|
+
meta, data = _analyze(filename, client, format)
|
|
409
397
|
schema = meta["analysis"]["schema"]
|
|
410
398
|
schema = schema.replace(", ", ",\n ")
|
|
411
399
|
content = f"""DESCRIPTION >\n Generated from {filename}\n\nSCHEMA >\n {schema}"""
|
|
412
400
|
return content
|
|
413
401
|
|
|
414
402
|
|
|
415
|
-
|
|
403
|
+
def _generate_datafile(
|
|
416
404
|
filename: str,
|
|
417
405
|
client: TinyB,
|
|
418
406
|
format: str,
|
|
@@ -420,14 +408,14 @@ async def _generate_datafile(
|
|
|
420
408
|
force: Optional[bool] = False,
|
|
421
409
|
folder: Optional[str] = None,
|
|
422
410
|
):
|
|
423
|
-
meta, data =
|
|
411
|
+
meta, data = _analyze(filename, client, format, connector=connector)
|
|
424
412
|
schema = meta["analysis"]["schema"]
|
|
425
413
|
schema = schema.replace(", ", ",\n ")
|
|
426
414
|
datafile = f"""DESCRIPTION >\n Generated from {filename}\n\nSCHEMA >\n {schema}"""
|
|
427
415
|
return generate_datafile(datafile, filename, data, force, _format=format, folder=folder)
|
|
428
416
|
|
|
429
417
|
|
|
430
|
-
|
|
418
|
+
def configure_connector(connector):
|
|
431
419
|
if connector not in SUPPORTED_CONNECTORS:
|
|
432
420
|
raise CLIException(FeedbackManager.error_invalid_connector(connectors=", ".join(SUPPORTED_CONNECTORS)))
|
|
433
421
|
|
|
@@ -443,7 +431,7 @@ async def configure_connector(connector):
|
|
|
443
431
|
|
|
444
432
|
try:
|
|
445
433
|
config = {"project_id": project, "service_account": service_account, "bucket_name": bucket_name}
|
|
446
|
-
|
|
434
|
+
write_config(config, file_name)
|
|
447
435
|
except Exception:
|
|
448
436
|
raise CLIException(FeedbackManager.error_file_config(config_file=config_file))
|
|
449
437
|
elif connector == "snowflake":
|
|
@@ -483,7 +471,7 @@ async def configure_connector(connector):
|
|
|
483
471
|
"bucket_name": bucket_name,
|
|
484
472
|
"project_id": project,
|
|
485
473
|
}
|
|
486
|
-
|
|
474
|
+
write_config(config, file_name)
|
|
487
475
|
except Exception:
|
|
488
476
|
raise CLIException(FeedbackManager.error_file_config(config_file=config_file))
|
|
489
477
|
|
|
@@ -592,13 +580,13 @@ def ask_for_user_token(action: str, ui_host: str) -> str:
|
|
|
592
580
|
)
|
|
593
581
|
|
|
594
582
|
|
|
595
|
-
|
|
583
|
+
def check_user_token(ctx: Context, token: str):
|
|
596
584
|
client: TinyB = ctx.ensure_object(dict)["client"]
|
|
597
585
|
try:
|
|
598
586
|
user_client: TinyB = deepcopy(client)
|
|
599
587
|
user_client.token = token
|
|
600
588
|
|
|
601
|
-
is_authenticated =
|
|
589
|
+
is_authenticated = user_client.check_auth_login()
|
|
602
590
|
except Exception as e:
|
|
603
591
|
raise CLIWorkspaceException(FeedbackManager.error_exception(error=str(e)))
|
|
604
592
|
|
|
@@ -616,12 +604,12 @@ async def check_user_token(ctx: Context, token: str):
|
|
|
616
604
|
)
|
|
617
605
|
|
|
618
606
|
|
|
619
|
-
|
|
607
|
+
def check_user_token_with_client(client: TinyB, token: str):
|
|
620
608
|
try:
|
|
621
609
|
user_client: TinyB = deepcopy(client)
|
|
622
610
|
user_client.token = token
|
|
623
611
|
|
|
624
|
-
is_authenticated =
|
|
612
|
+
is_authenticated = user_client.check_auth_login()
|
|
625
613
|
except Exception as e:
|
|
626
614
|
raise CLIWorkspaceException(FeedbackManager.error_exception(error=str(e)))
|
|
627
615
|
|
|
@@ -639,15 +627,15 @@ async def check_user_token_with_client(client: TinyB, token: str):
|
|
|
639
627
|
)
|
|
640
628
|
|
|
641
629
|
|
|
642
|
-
|
|
630
|
+
def fork_workspace(client: TinyB, user_client: TinyB, created_workspace):
|
|
643
631
|
config = CLIConfig.get_project_config()
|
|
644
632
|
|
|
645
|
-
datasources =
|
|
633
|
+
datasources = client.datasources()
|
|
646
634
|
for datasource in datasources:
|
|
647
|
-
|
|
635
|
+
user_client.datasource_share(datasource["id"], config["id"], created_workspace["id"])
|
|
648
636
|
|
|
649
637
|
|
|
650
|
-
|
|
638
|
+
def create_workspace_non_interactive(
|
|
651
639
|
ctx: Context,
|
|
652
640
|
workspace_name: str,
|
|
653
641
|
user_token: str,
|
|
@@ -662,7 +650,7 @@ async def create_workspace_non_interactive(
|
|
|
662
650
|
user_client: TinyB = deepcopy(client)
|
|
663
651
|
user_client.token = user_token
|
|
664
652
|
|
|
665
|
-
created_workspace =
|
|
653
|
+
created_workspace = user_client.create_workspace(workspace_name, organization_id, version="v1")
|
|
666
654
|
if organization_id and organization_name:
|
|
667
655
|
click.echo(
|
|
668
656
|
FeedbackManager.success_workspace_created_with_organization(
|
|
@@ -673,13 +661,13 @@ async def create_workspace_non_interactive(
|
|
|
673
661
|
click.echo(FeedbackManager.success_workspace_created(workspace_name=workspace_name))
|
|
674
662
|
|
|
675
663
|
if fork:
|
|
676
|
-
|
|
664
|
+
fork_workspace(client, user_client, created_workspace)
|
|
677
665
|
|
|
678
666
|
except Exception as e:
|
|
679
667
|
raise CLIWorkspaceException(FeedbackManager.error_exception(error=str(e)))
|
|
680
668
|
|
|
681
669
|
|
|
682
|
-
|
|
670
|
+
def create_workspace_interactive(
|
|
683
671
|
ctx: Context,
|
|
684
672
|
workspace_name: Optional[str],
|
|
685
673
|
user_token: str,
|
|
@@ -697,7 +685,7 @@ async def create_workspace_interactive(
|
|
|
697
685
|
if not workspace_name:
|
|
698
686
|
raise CLIException(FeedbackManager.error_workspace_name_required())
|
|
699
687
|
|
|
700
|
-
|
|
688
|
+
create_workspace_non_interactive(
|
|
701
689
|
ctx,
|
|
702
690
|
workspace_name,
|
|
703
691
|
user_token,
|
|
@@ -707,8 +695,8 @@ async def create_workspace_interactive(
|
|
|
707
695
|
)
|
|
708
696
|
|
|
709
697
|
|
|
710
|
-
|
|
711
|
-
response =
|
|
698
|
+
def print_data_branch_summary(client, job_id, response=None):
|
|
699
|
+
response = client.job(job_id) if job_id else response or {"partitions": []}
|
|
712
700
|
columns = ["Data Source", "Partition", "Status", "Error"]
|
|
713
701
|
table = []
|
|
714
702
|
for partition in response["partitions"]:
|
|
@@ -717,7 +705,7 @@ async def print_data_branch_summary(client, job_id, response=None):
|
|
|
717
705
|
echo_safe_humanfriendly_tables_format_smart_table(table, column_names=columns)
|
|
718
706
|
|
|
719
707
|
|
|
720
|
-
|
|
708
|
+
def print_branch_regression_tests_summary(client, job_id, host, response=None):
|
|
721
709
|
def format_metric(metric: Union[str, float], is_percentage: bool = False) -> str:
|
|
722
710
|
if isinstance(metric, float):
|
|
723
711
|
if is_percentage:
|
|
@@ -728,7 +716,7 @@ async def print_branch_regression_tests_summary(client, job_id, host, response=N
|
|
|
728
716
|
return metric
|
|
729
717
|
|
|
730
718
|
failed = False
|
|
731
|
-
response =
|
|
719
|
+
response = client.job(job_id) if job_id else response or {"progress": []}
|
|
732
720
|
output = "\n"
|
|
733
721
|
for step in response["progress"]:
|
|
734
722
|
run = step["run"]
|
|
@@ -927,7 +915,7 @@ def get_format_from_filename_or_url(filename_or_url: str) -> str:
|
|
|
927
915
|
return "csv"
|
|
928
916
|
|
|
929
917
|
|
|
930
|
-
|
|
918
|
+
def push_data(
|
|
931
919
|
client: TinyB,
|
|
932
920
|
datasource_name: str,
|
|
933
921
|
url,
|
|
@@ -966,14 +954,14 @@ async def push_data(
|
|
|
966
954
|
else:
|
|
967
955
|
urls = [url]
|
|
968
956
|
|
|
969
|
-
|
|
957
|
+
def process_url(
|
|
970
958
|
datasource_name: str, url: str, mode: str, sql_condition: Optional[str], replace_options: Optional[Set[str]]
|
|
971
959
|
):
|
|
972
960
|
parsed = urlparse(url)
|
|
973
961
|
# poor man's format detection
|
|
974
962
|
_format = get_format_from_filename_or_url(url)
|
|
975
963
|
if parsed.scheme in ("http", "https"):
|
|
976
|
-
res =
|
|
964
|
+
res = client.datasource_create_from_url(
|
|
977
965
|
datasource_name,
|
|
978
966
|
url,
|
|
979
967
|
mode=mode,
|
|
@@ -983,7 +971,7 @@ async def push_data(
|
|
|
983
971
|
replace_options=replace_options,
|
|
984
972
|
)
|
|
985
973
|
else:
|
|
986
|
-
res =
|
|
974
|
+
res = client.datasource_append_data(
|
|
987
975
|
datasource_name,
|
|
988
976
|
file=url,
|
|
989
977
|
mode=mode,
|
|
@@ -994,7 +982,7 @@ async def push_data(
|
|
|
994
982
|
|
|
995
983
|
datasource_name = res["datasource"]["name"]
|
|
996
984
|
try:
|
|
997
|
-
datasource =
|
|
985
|
+
datasource = client.get_datasource(datasource_name)
|
|
998
986
|
except DoesNotExistException:
|
|
999
987
|
raise CLIException(FeedbackManager.error_datasource_does_not_exist(datasource=datasource_name))
|
|
1000
988
|
except Exception as e:
|
|
@@ -1021,7 +1009,7 @@ async def push_data(
|
|
|
1021
1009
|
|
|
1022
1010
|
try:
|
|
1023
1011
|
tasks = [process_url(datasource_name, url, mode, sql_condition, replace_options) for url in urls]
|
|
1024
|
-
output =
|
|
1012
|
+
output = gather_with_concurrency(concurrency, *tasks)
|
|
1025
1013
|
parser, total_rows, appended_rows = list(output)[-1]
|
|
1026
1014
|
except AuthNoTokenException:
|
|
1027
1015
|
raise
|
|
@@ -1040,9 +1028,9 @@ async def push_data(
|
|
|
1040
1028
|
click.echo(FeedbackManager.success_progress_blocks())
|
|
1041
1029
|
|
|
1042
1030
|
|
|
1043
|
-
|
|
1031
|
+
def sync_data(ctx, datasource_name: str, yes: bool):
|
|
1044
1032
|
client: TinyB = ctx.obj["client"]
|
|
1045
|
-
datasource =
|
|
1033
|
+
datasource = client.get_datasource(datasource_name)
|
|
1046
1034
|
|
|
1047
1035
|
VALID_DATASOURCES = ["bigquery", "snowflake", "s3", "gcs"]
|
|
1048
1036
|
if datasource["type"] not in VALID_DATASOURCES:
|
|
@@ -1056,17 +1044,17 @@ async def sync_data(ctx, datasource_name: str, yes: bool):
|
|
|
1056
1044
|
)
|
|
1057
1045
|
)
|
|
1058
1046
|
if yes or click.confirm(warning_message):
|
|
1059
|
-
|
|
1047
|
+
client.datasource_sync(datasource["id"])
|
|
1060
1048
|
click.echo(FeedbackManager.success_sync_datasource(datasource=datasource_name))
|
|
1061
1049
|
|
|
1062
1050
|
|
|
1063
1051
|
# eval "$(_TB_COMPLETE=source_bash tb)"
|
|
1064
1052
|
def autocomplete_topics(ctx: Context, args, incomplete):
|
|
1065
1053
|
try:
|
|
1066
|
-
config =
|
|
1054
|
+
config = get_config(None, None)
|
|
1067
1055
|
ctx.ensure_object(dict)["config"] = config
|
|
1068
1056
|
client = create_tb_client(ctx)
|
|
1069
|
-
topics =
|
|
1057
|
+
topics = client.kafka_list_topics(args[2])
|
|
1070
1058
|
return [t for t in topics if incomplete in t]
|
|
1071
1059
|
except Exception:
|
|
1072
1060
|
return []
|
|
@@ -1148,8 +1136,8 @@ def validate_string_connector_param(param, s):
|
|
|
1148
1136
|
raise CLIConnectionException(param + " format is not correct, it must be a string")
|
|
1149
1137
|
|
|
1150
1138
|
|
|
1151
|
-
|
|
1152
|
-
if
|
|
1139
|
+
def validate_connection_name(client, connection_name, service):
|
|
1140
|
+
if client.get_connector(connection_name, service) is not None:
|
|
1153
1141
|
raise CLIConnectionException(FeedbackManager.error_connection_already_exists(name=connection_name))
|
|
1154
1142
|
|
|
1155
1143
|
|
|
@@ -1159,9 +1147,9 @@ def _get_setting_value(connection, setting, sensitive_settings):
|
|
|
1159
1147
|
return connection.get(setting, "")
|
|
1160
1148
|
|
|
1161
1149
|
|
|
1162
|
-
|
|
1150
|
+
def switch_workspace(config: CLIConfig, workspace_name_or_id: str) -> None:
|
|
1163
1151
|
try:
|
|
1164
|
-
response =
|
|
1152
|
+
response = config.get_client().user_workspaces(version="v1")
|
|
1165
1153
|
workspaces = response["workspaces"]
|
|
1166
1154
|
|
|
1167
1155
|
workspace = next(
|
|
@@ -1178,7 +1166,7 @@ async def switch_workspace(config: CLIConfig, workspace_name_or_id: str) -> None
|
|
|
1178
1166
|
|
|
1179
1167
|
config.set_token(workspace["token"])
|
|
1180
1168
|
config.set_token_for_host(workspace["token"], config.get_host())
|
|
1181
|
-
_ =
|
|
1169
|
+
_ = try_update_config_with_remote(config)
|
|
1182
1170
|
|
|
1183
1171
|
# Set the id and name afterwards.
|
|
1184
1172
|
# When working with branches the call to try_update_config_with_remote above
|
|
@@ -1197,7 +1185,7 @@ async def switch_workspace(config: CLIConfig, workspace_name_or_id: str) -> None
|
|
|
1197
1185
|
raise CLIException(FeedbackManager.error_exception(error=str(e)))
|
|
1198
1186
|
|
|
1199
1187
|
|
|
1200
|
-
|
|
1188
|
+
def switch_to_workspace_by_user_workspace_data(config: CLIConfig, user_workspace_data: Dict[str, Any]):
|
|
1201
1189
|
try:
|
|
1202
1190
|
config["id"] = user_workspace_data["id"]
|
|
1203
1191
|
config["name"] = user_workspace_data["name"]
|
|
@@ -1210,10 +1198,10 @@ async def switch_to_workspace_by_user_workspace_data(config: CLIConfig, user_wor
|
|
|
1210
1198
|
raise CLIException(FeedbackManager.error_exception(error=str(e)))
|
|
1211
1199
|
|
|
1212
1200
|
|
|
1213
|
-
|
|
1214
|
-
_ =
|
|
1201
|
+
def print_current_workspace(config: CLIConfig) -> None:
|
|
1202
|
+
_ = try_update_config_with_remote(config, only_if_needed=True)
|
|
1215
1203
|
|
|
1216
|
-
current_main_workspace =
|
|
1204
|
+
current_main_workspace = get_current_main_workspace(config)
|
|
1217
1205
|
assert isinstance(current_main_workspace, dict)
|
|
1218
1206
|
|
|
1219
1207
|
columns = ["name", "id", "role", "plan", "current"]
|
|
@@ -1291,7 +1279,7 @@ class ConnectionReplacements:
|
|
|
1291
1279
|
# ======
|
|
1292
1280
|
|
|
1293
1281
|
|
|
1294
|
-
|
|
1282
|
+
def get_host_from_region(
|
|
1295
1283
|
config: CLIConfig, region_name_or_host_or_id: str, host: Optional[str] = None
|
|
1296
1284
|
) -> Tuple[List[Region], str]:
|
|
1297
1285
|
regions: List[Region]
|
|
@@ -1300,7 +1288,7 @@ async def get_host_from_region(
|
|
|
1300
1288
|
host = host or config.get_host(use_defaults_if_needed=True)
|
|
1301
1289
|
|
|
1302
1290
|
try:
|
|
1303
|
-
regions =
|
|
1291
|
+
regions = get_regions(config)
|
|
1304
1292
|
assert isinstance(regions, list)
|
|
1305
1293
|
except Exception:
|
|
1306
1294
|
regions = []
|
|
@@ -1330,10 +1318,10 @@ async def get_host_from_region(
|
|
|
1330
1318
|
return regions, host
|
|
1331
1319
|
|
|
1332
1320
|
|
|
1333
|
-
|
|
1321
|
+
def get_regions(config: CLIConfig) -> List[Region]:
|
|
1334
1322
|
regions: List[Region] = []
|
|
1335
1323
|
try:
|
|
1336
|
-
response =
|
|
1324
|
+
response = config.get_client().regions()
|
|
1337
1325
|
regions = response.get("regions", [])
|
|
1338
1326
|
first_default_region = next((region for region in regions if region["api_host"] == DEFAULT_API_HOST), None)
|
|
1339
1327
|
if first_default_region:
|
|
@@ -1353,7 +1341,7 @@ def get_region_from_host(region_name_or_host: str, regions: List[Region]) -> Opt
|
|
|
1353
1341
|
return None
|
|
1354
1342
|
|
|
1355
1343
|
|
|
1356
|
-
|
|
1344
|
+
def try_update_config_with_remote(
|
|
1357
1345
|
config: CLIConfig, raise_on_errors: bool = True, only_if_needed: bool = False, auto_persist: bool = True
|
|
1358
1346
|
) -> bool:
|
|
1359
1347
|
response: Dict[str, Any]
|
|
@@ -1367,7 +1355,7 @@ async def try_update_config_with_remote(
|
|
|
1367
1355
|
return True
|
|
1368
1356
|
|
|
1369
1357
|
try:
|
|
1370
|
-
response =
|
|
1358
|
+
response = config.get_client().workspace_info(version="v1")
|
|
1371
1359
|
except AuthException:
|
|
1372
1360
|
if raise_on_errors:
|
|
1373
1361
|
raise CLIAuthException(FeedbackManager.error_invalid_token_for_host(host=config.get_host()))
|
|
@@ -1406,7 +1394,7 @@ def ask_for_admin_token_interactively(ui_host: str, default_token: Optional[str]
|
|
|
1406
1394
|
)
|
|
1407
1395
|
|
|
1408
1396
|
|
|
1409
|
-
|
|
1397
|
+
def try_authenticate(
|
|
1410
1398
|
config: CLIConfig,
|
|
1411
1399
|
regions: Optional[List[Region]] = None,
|
|
1412
1400
|
interactive: bool = False,
|
|
@@ -1415,7 +1403,7 @@ async def try_authenticate(
|
|
|
1415
1403
|
host: Optional[str] = config.get_host()
|
|
1416
1404
|
|
|
1417
1405
|
if not regions and interactive:
|
|
1418
|
-
regions =
|
|
1406
|
+
regions = get_regions(config)
|
|
1419
1407
|
|
|
1420
1408
|
selected_region: Optional[Region] = None
|
|
1421
1409
|
default_password: Optional[str] = None
|
|
@@ -1449,18 +1437,18 @@ async def try_authenticate(
|
|
|
1449
1437
|
config.set_token(token)
|
|
1450
1438
|
|
|
1451
1439
|
add_telemetry_event("auth_token", token=token)
|
|
1452
|
-
authenticated: bool =
|
|
1440
|
+
authenticated: bool = try_update_config_with_remote(config, raise_on_errors=not try_all_regions)
|
|
1453
1441
|
|
|
1454
1442
|
# No luck? Let's try auth in all other regions
|
|
1455
1443
|
if not authenticated and try_all_regions and not interactive:
|
|
1456
1444
|
if not regions:
|
|
1457
|
-
regions =
|
|
1445
|
+
regions = get_regions(config)
|
|
1458
1446
|
|
|
1459
1447
|
# Check other regions, ignoring the previously tested region
|
|
1460
1448
|
for region in [r for r in regions if r is not selected_region]:
|
|
1461
1449
|
name, host, ui_host = get_region_info(config, region)
|
|
1462
1450
|
config.set_host(host)
|
|
1463
|
-
authenticated =
|
|
1451
|
+
authenticated = try_update_config_with_remote(config, raise_on_errors=False)
|
|
1464
1452
|
if authenticated:
|
|
1465
1453
|
click.echo(FeedbackManager.success_using_host(name=name, host=get_display_cloud_host(ui_host)))
|
|
1466
1454
|
break
|
|
@@ -1481,7 +1469,7 @@ async def try_authenticate(
|
|
|
1481
1469
|
return True
|
|
1482
1470
|
|
|
1483
1471
|
|
|
1484
|
-
|
|
1472
|
+
def wait_job(
|
|
1485
1473
|
tb_client: TinyB,
|
|
1486
1474
|
job_id: str,
|
|
1487
1475
|
job_url: str,
|
|
@@ -1508,12 +1496,11 @@ async def wait_job(
|
|
|
1508
1496
|
progress_bar.update(progress_bar.length if progress_bar.length else 0)
|
|
1509
1497
|
|
|
1510
1498
|
try:
|
|
1511
|
-
|
|
1512
|
-
result = await wait_job_no_ui(tb_client, job_id, progressbar_cb)
|
|
1499
|
+
result = wait_job_no_ui(tb_client, job_id, progressbar_cb)
|
|
1513
1500
|
if result["status"] != "done":
|
|
1514
1501
|
raise CLIException(FeedbackManager.error_while_running_job(error=result["error"]))
|
|
1515
1502
|
return result
|
|
1516
|
-
except
|
|
1503
|
+
except TimeoutError:
|
|
1517
1504
|
raise CLIException(FeedbackManager.error_while_running_job(error="Reach timeout, job cancelled"))
|
|
1518
1505
|
except JobException as e:
|
|
1519
1506
|
raise CLIException(FeedbackManager.error_while_running_job(error=str(e)))
|
|
@@ -1521,23 +1508,23 @@ async def wait_job(
|
|
|
1521
1508
|
raise CLIException(FeedbackManager.error_getting_job_info(error=str(e), url=job_url))
|
|
1522
1509
|
|
|
1523
1510
|
|
|
1524
|
-
|
|
1511
|
+
def wait_job_no_ui(
|
|
1525
1512
|
tb_client: TinyB,
|
|
1526
1513
|
job_id: str,
|
|
1527
1514
|
status_callback: Optional[Callable[[Dict[str, Any]], None]] = None,
|
|
1528
1515
|
) -> Dict[str, Any]:
|
|
1529
1516
|
try:
|
|
1530
|
-
result =
|
|
1517
|
+
result = tb_client.wait_for_job(job_id, status_callback=status_callback)
|
|
1531
1518
|
if result["status"] != "done":
|
|
1532
1519
|
raise JobException(result.get("error"))
|
|
1533
1520
|
return result
|
|
1534
|
-
except
|
|
1535
|
-
|
|
1521
|
+
except TimeoutError:
|
|
1522
|
+
tb_client.job_cancel(job_id)
|
|
1536
1523
|
raise
|
|
1537
1524
|
|
|
1538
1525
|
|
|
1539
|
-
|
|
1540
|
-
current_workspace =
|
|
1526
|
+
def get_current_main_workspace(config: CLIConfig) -> Optional[Dict[str, Any]]:
|
|
1527
|
+
current_workspace = config.get_client().user_workspaces_and_branches(version="v1")
|
|
1541
1528
|
return _get_current_main_workspace_common(current_workspace, config.get("id", current_workspace["id"]))
|
|
1542
1529
|
|
|
1543
1530
|
|
|
@@ -1618,13 +1605,13 @@ def is_major_semver(new_version: Version, current_version: Version) -> bool:
|
|
|
1618
1605
|
return new_version.major != current_version.major
|
|
1619
1606
|
|
|
1620
1607
|
|
|
1621
|
-
|
|
1608
|
+
def print_release_summary(config: CLIConfig, semver: Optional[str], info: bool = False, dry_run=False):
|
|
1622
1609
|
if not semver:
|
|
1623
1610
|
click.echo(FeedbackManager.info_release_no_rollback())
|
|
1624
1611
|
return
|
|
1625
1612
|
try:
|
|
1626
1613
|
client = config.get_client()
|
|
1627
|
-
response =
|
|
1614
|
+
response = client.release_rm(config["id"], semver, confirmation=config["name"], dry_run=True)
|
|
1628
1615
|
except Exception as e:
|
|
1629
1616
|
raise CLIReleaseException(FeedbackManager.error_exception(error=str(e)))
|
|
1630
1617
|
else:
|
|
@@ -1660,21 +1647,21 @@ async def print_release_summary(config: CLIConfig, semver: Optional[str], info:
|
|
|
1660
1647
|
echo_safe_humanfriendly_tables_format_smart_table(rows, column_names=columns)
|
|
1661
1648
|
|
|
1662
1649
|
|
|
1663
|
-
|
|
1664
|
-
oldest_rollback_response =
|
|
1650
|
+
def get_oldest_rollback(config: CLIConfig, client: TinyB) -> Optional[str]:
|
|
1651
|
+
oldest_rollback_response = client.release_oldest_rollback(config["id"])
|
|
1665
1652
|
return oldest_rollback_response.get("semver") if oldest_rollback_response else None
|
|
1666
1653
|
|
|
1667
1654
|
|
|
1668
|
-
|
|
1655
|
+
def remove_release(
|
|
1669
1656
|
dry_run: bool, config: CLIConfig, semver: Optional[str], client: TinyB, force: bool, show_print=True
|
|
1670
1657
|
):
|
|
1671
1658
|
if semver == OLDEST_ROLLBACK:
|
|
1672
|
-
semver =
|
|
1659
|
+
semver = get_oldest_rollback(config, client)
|
|
1673
1660
|
if show_print:
|
|
1674
|
-
|
|
1661
|
+
print_release_summary(config, semver, info=True, dry_run=True)
|
|
1675
1662
|
if not dry_run:
|
|
1676
1663
|
if semver:
|
|
1677
|
-
response =
|
|
1664
|
+
response = client.release_rm(
|
|
1678
1665
|
config["id"], semver, confirmation=config["name"], dry_run=dry_run, force=force
|
|
1679
1666
|
)
|
|
1680
1667
|
click.echo(FeedbackManager.success_release_delete(semver=response.get("semver")))
|
|
@@ -1682,7 +1669,7 @@ async def remove_release(
|
|
|
1682
1669
|
click.echo(FeedbackManager.info_no_release_deleted())
|
|
1683
1670
|
|
|
1684
1671
|
|
|
1685
|
-
|
|
1672
|
+
def run_aws_iamrole_connection_flow(
|
|
1686
1673
|
client: TinyB,
|
|
1687
1674
|
service: str,
|
|
1688
1675
|
environment: str,
|
|
@@ -1701,7 +1688,7 @@ async def run_aws_iamrole_connection_flow(
|
|
|
1701
1688
|
region = click.prompt("🌐 Region (the region where the bucket is located, e.g. 'us-east-1')", prompt_suffix="\n> ")
|
|
1702
1689
|
validate_string_connector_param("Region", region)
|
|
1703
1690
|
|
|
1704
|
-
access_policy, trust_policy, _ =
|
|
1691
|
+
access_policy, trust_policy, _ = get_aws_iamrole_policies(
|
|
1705
1692
|
client, service=service, policy=policy, bucket=bucket_name, external_id_seed=connection_name
|
|
1706
1693
|
)
|
|
1707
1694
|
|
|
@@ -1759,7 +1746,7 @@ async def run_aws_iamrole_connection_flow(
|
|
|
1759
1746
|
return role_arn, region, bucket_name
|
|
1760
1747
|
|
|
1761
1748
|
|
|
1762
|
-
|
|
1749
|
+
def run_gcp_svc_account_connection_flow(
|
|
1763
1750
|
environment: str,
|
|
1764
1751
|
) -> None:
|
|
1765
1752
|
click.echo(FeedbackManager.prompt_gcs_svc_account_login_gcp())
|
|
@@ -1775,7 +1762,7 @@ async def run_gcp_svc_account_connection_flow(
|
|
|
1775
1762
|
input()
|
|
1776
1763
|
|
|
1777
1764
|
|
|
1778
|
-
|
|
1765
|
+
def production_aws_iamrole_only(
|
|
1779
1766
|
prod_client: TinyB,
|
|
1780
1767
|
service: str,
|
|
1781
1768
|
region: str,
|
|
@@ -1784,7 +1771,7 @@ async def production_aws_iamrole_only(
|
|
|
1784
1771
|
connection_name: str,
|
|
1785
1772
|
policy: str,
|
|
1786
1773
|
) -> Tuple[str, str, str]:
|
|
1787
|
-
_, trust_policy, external_id =
|
|
1774
|
+
_, trust_policy, external_id = get_aws_iamrole_policies(
|
|
1788
1775
|
prod_client, service=service, policy=policy, bucket=bucket_name, external_id_seed=connection_name
|
|
1789
1776
|
)
|
|
1790
1777
|
|
|
@@ -1819,7 +1806,7 @@ async def production_aws_iamrole_only(
|
|
|
1819
1806
|
return role_arn, region, external_id
|
|
1820
1807
|
|
|
1821
1808
|
|
|
1822
|
-
|
|
1809
|
+
def get_aws_iamrole_policies(
|
|
1823
1810
|
client: TinyB,
|
|
1824
1811
|
service: str,
|
|
1825
1812
|
policy: str = "write",
|
|
@@ -1831,9 +1818,9 @@ async def get_aws_iamrole_policies(
|
|
|
1831
1818
|
service = DataConnectorType.AMAZON_S3
|
|
1832
1819
|
try:
|
|
1833
1820
|
if policy == "write":
|
|
1834
|
-
access_policy =
|
|
1821
|
+
access_policy = client.get_access_write_policy(service, bucket)
|
|
1835
1822
|
elif policy == "read":
|
|
1836
|
-
access_policy =
|
|
1823
|
+
access_policy = client.get_access_read_policy(service, bucket)
|
|
1837
1824
|
else:
|
|
1838
1825
|
raise Exception(f"Access policy {policy} not supported. Choose from 'read' or 'write'")
|
|
1839
1826
|
if not len(access_policy) > 0:
|
|
@@ -1843,7 +1830,7 @@ async def get_aws_iamrole_policies(
|
|
|
1843
1830
|
|
|
1844
1831
|
trust_policy: Dict[str, Any] = {}
|
|
1845
1832
|
try:
|
|
1846
|
-
trust_policy =
|
|
1833
|
+
trust_policy = client.get_trust_policy(service, external_id_seed)
|
|
1847
1834
|
if not len(trust_policy) > 0:
|
|
1848
1835
|
raise Exception(f"{service.upper()} Integration not supported in this region")
|
|
1849
1836
|
except Exception as e:
|
|
@@ -1932,7 +1919,7 @@ class DataConnectorType(str, Enum):
|
|
|
1932
1919
|
return self.value
|
|
1933
1920
|
|
|
1934
1921
|
|
|
1935
|
-
|
|
1922
|
+
def create_aws_iamrole_connection(client: TinyB, service: str, connection_name, role_arn, region) -> None:
|
|
1936
1923
|
conn_file_name = f"{connection_name}.connection"
|
|
1937
1924
|
conn_file_path = Path(getcwd(), conn_file_name)
|
|
1938
1925
|
|
|
@@ -1950,14 +1937,14 @@ async def create_aws_iamrole_connection(client: TinyB, service: str, connection_
|
|
|
1950
1937
|
|
|
1951
1938
|
click.echo("** Creating connection...")
|
|
1952
1939
|
try:
|
|
1953
|
-
_ =
|
|
1940
|
+
_ = client.connection_create(params)
|
|
1954
1941
|
except Exception as e:
|
|
1955
1942
|
raise CLIConnectionException(
|
|
1956
1943
|
FeedbackManager.error_connection_create(connection_name=connection_name, error=str(e))
|
|
1957
1944
|
)
|
|
1958
1945
|
|
|
1959
|
-
|
|
1960
|
-
|
|
1946
|
+
with open(conn_file_path, "w") as f:
|
|
1947
|
+
f.write(
|
|
1961
1948
|
f"""TYPE {service}
|
|
1962
1949
|
|
|
1963
1950
|
"""
|
|
@@ -1990,36 +1977,34 @@ def get_ca_pem_content(ca_pem: Optional[str], filename: Optional[str] = None) ->
|
|
|
1990
1977
|
return ca_pem_content
|
|
1991
1978
|
|
|
1992
1979
|
|
|
1993
|
-
requests_get =
|
|
1994
|
-
requests_delete =
|
|
1980
|
+
requests_get = requests.get
|
|
1981
|
+
requests_delete = requests.delete
|
|
1995
1982
|
|
|
1996
1983
|
|
|
1997
1984
|
def format_data_to_ndjson(data: List[Dict[str, Any]]) -> str:
|
|
1998
1985
|
return "\n".join([json.dumps(row) for row in data])
|
|
1999
1986
|
|
|
2000
1987
|
|
|
2001
|
-
|
|
2002
|
-
client: TinyB, datasource_name: str, data: List[Dict[str, Any]], batch_size: int = 10
|
|
2003
|
-
) -> None:
|
|
1988
|
+
def send_batch_events(client: TinyB, datasource_name: str, data: List[Dict[str, Any]], batch_size: int = 10) -> None:
|
|
2004
1989
|
rows = len(data)
|
|
2005
1990
|
time_start = time.time()
|
|
2006
1991
|
for i in range(0, rows, batch_size):
|
|
2007
1992
|
batch = data[i : i + batch_size]
|
|
2008
1993
|
ndjson_data = format_data_to_ndjson(batch)
|
|
2009
|
-
|
|
1994
|
+
client.datasource_events(datasource_name, ndjson_data)
|
|
2010
1995
|
time_end = time.time()
|
|
2011
1996
|
elapsed_time = time_end - time_start
|
|
2012
1997
|
cols = len(data[0].keys()) if len(data) > 0 else 0
|
|
2013
1998
|
click.echo(FeedbackManager.highlight(message=f"» {rows} rows x {cols} cols in {elapsed_time:.1f}s"))
|
|
2014
1999
|
|
|
2015
2000
|
|
|
2016
|
-
|
|
2001
|
+
def get_organizations_by_user(config: CLIConfig, user_token: Optional[str] = None) -> List[Dict[str, str]]:
|
|
2017
2002
|
"""Fetches all organizations by user using the provided user token"""
|
|
2018
2003
|
organizations = []
|
|
2019
2004
|
|
|
2020
2005
|
try:
|
|
2021
2006
|
user_client = config.get_client(token=user_token) if user_token else config.get_user_client()
|
|
2022
|
-
user_workspaces =
|
|
2007
|
+
user_workspaces = user_client.user_workspaces_with_organization(version="v1")
|
|
2023
2008
|
admin_org_id = user_workspaces.get("organization_id")
|
|
2024
2009
|
seen_org_ids = set()
|
|
2025
2010
|
|
|
@@ -2032,7 +2017,7 @@ async def get_organizations_by_user(config: CLIConfig, user_token: Optional[str]
|
|
|
2032
2017
|
|
|
2033
2018
|
# Case: user is admin of an organization but not a member of any workspace in it
|
|
2034
2019
|
if admin_org_id and admin_org_id not in seen_org_ids:
|
|
2035
|
-
org =
|
|
2020
|
+
org = user_client.organization(admin_org_id)
|
|
2036
2021
|
org["id"] = admin_org_id
|
|
2037
2022
|
org["is_admin"] = True
|
|
2038
2023
|
organizations.append(org)
|
|
@@ -2086,7 +2071,7 @@ def sort_organizations_by_user(organizations: List[Dict[str, Any]], user_email:
|
|
|
2086
2071
|
return sorted_organizations
|
|
2087
2072
|
|
|
2088
2073
|
|
|
2089
|
-
|
|
2074
|
+
def ask_for_organization_interactively(organizations: List[Organization]) -> Optional[Organization]:
|
|
2090
2075
|
rows = [(index + 1, org["name"], org["role"], org["id"]) for index, org in enumerate(organizations)]
|
|
2091
2076
|
|
|
2092
2077
|
echo_safe_humanfriendly_tables_format_smart_table(rows, column_names=["Idx", "Name", "Role", "Id"])
|
|
@@ -2107,7 +2092,7 @@ async def ask_for_organization_interactively(organizations: List[Organization])
|
|
|
2107
2092
|
return organizations[org_index - 1]
|
|
2108
2093
|
|
|
2109
2094
|
|
|
2110
|
-
|
|
2095
|
+
def ask_for_organization_name(config: CLIConfig) -> str:
|
|
2111
2096
|
user_email = config.get_user_email()
|
|
2112
2097
|
default_organization_name = (
|
|
2113
2098
|
user_email.split("@")[1].split(".")[0] if user_email else None
|
|
@@ -2125,28 +2110,28 @@ async def ask_for_organization_name(config: CLIConfig) -> str:
|
|
|
2125
2110
|
)
|
|
2126
2111
|
|
|
2127
2112
|
|
|
2128
|
-
|
|
2113
|
+
def create_organization_and_add_workspaces(
|
|
2129
2114
|
config: CLIConfig, organization_name: str, user_token: str
|
|
2130
2115
|
) -> Dict[str, Any]:
|
|
2131
2116
|
client: TinyB = config.get_client(token=user_token)
|
|
2132
2117
|
try:
|
|
2133
|
-
organization =
|
|
2118
|
+
organization = client.create_organization(organization_name)
|
|
2134
2119
|
click.echo(FeedbackManager.success_organization_created(organization_name=organization_name))
|
|
2135
2120
|
except Exception as e:
|
|
2136
2121
|
raise CLIWorkspaceException(FeedbackManager.error_organization_creation(error=str(e)))
|
|
2137
2122
|
|
|
2138
2123
|
# Add existing orphan workspaces to the organization - this is only needed for backwards compatibility
|
|
2139
|
-
user_workspaces =
|
|
2124
|
+
user_workspaces = client.user_workspaces_with_organization(version="v1")
|
|
2140
2125
|
workspaces_to_migrate = []
|
|
2141
2126
|
for workspace in user_workspaces["workspaces"]:
|
|
2142
2127
|
if workspace.get("organization") is None and workspace.get("role") == "admin":
|
|
2143
2128
|
workspaces_to_migrate.append(workspace["id"])
|
|
2144
|
-
|
|
2129
|
+
client.add_workspaces_to_organization(organization["id"], workspaces_to_migrate)
|
|
2145
2130
|
|
|
2146
2131
|
return organization
|
|
2147
2132
|
|
|
2148
2133
|
|
|
2149
|
-
|
|
2134
|
+
def get_user_token(config: CLIConfig, user_token: Optional[str] = None) -> str:
|
|
2150
2135
|
client = config.get_client()
|
|
2151
2136
|
host = config.get_host() or CLIConfig.DEFAULTS["host"]
|
|
2152
2137
|
ui_host = get_display_cloud_host(host)
|
|
@@ -2155,7 +2140,7 @@ async def get_user_token(config: CLIConfig, user_token: Optional[str] = None) ->
|
|
|
2155
2140
|
user_token = config.get_user_token()
|
|
2156
2141
|
if user_token:
|
|
2157
2142
|
try:
|
|
2158
|
-
|
|
2143
|
+
check_user_token_with_client(client, user_token)
|
|
2159
2144
|
except Exception:
|
|
2160
2145
|
user_token = None
|
|
2161
2146
|
pass
|
|
@@ -2168,12 +2153,12 @@ async def get_user_token(config: CLIConfig, user_token: Optional[str] = None) ->
|
|
|
2168
2153
|
)
|
|
2169
2154
|
)
|
|
2170
2155
|
|
|
2171
|
-
|
|
2156
|
+
check_user_token_with_client(client, user_token)
|
|
2172
2157
|
|
|
2173
2158
|
return user_token
|
|
2174
2159
|
|
|
2175
2160
|
|
|
2176
|
-
|
|
2161
|
+
def ask_for_organization(
|
|
2177
2162
|
organizations: Optional[List[Dict[str, Any]]],
|
|
2178
2163
|
organization_id: Optional[str] = None,
|
|
2179
2164
|
user_token: Optional[str] = None,
|
|
@@ -2190,9 +2175,9 @@ async def ask_for_organization(
|
|
|
2190
2175
|
return organization_id, organization_name
|
|
2191
2176
|
|
|
2192
2177
|
if organizations is None or len(organizations) == 0:
|
|
2193
|
-
organization_name =
|
|
2194
|
-
user_token =
|
|
2195
|
-
organization =
|
|
2178
|
+
organization_name = ask_for_organization_name(config)
|
|
2179
|
+
user_token = get_user_token(config, user_token)
|
|
2180
|
+
organization = create_organization_and_add_workspaces(config, organization_name, user_token)
|
|
2196
2181
|
organization_id = organization.get("id")
|
|
2197
2182
|
else:
|
|
2198
2183
|
if len(organizations) == 1:
|
|
@@ -2200,7 +2185,7 @@ async def ask_for_organization(
|
|
|
2200
2185
|
organization_id = organizations[0]["id"]
|
|
2201
2186
|
else:
|
|
2202
2187
|
sorted_organizations = sort_organizations_by_user(organizations, user_email=user_email)
|
|
2203
|
-
current_organization =
|
|
2188
|
+
current_organization = ask_for_organization_interactively(sorted_organizations)
|
|
2204
2189
|
if current_organization:
|
|
2205
2190
|
organization_id = current_organization.get("id")
|
|
2206
2191
|
organization_name = current_organization.get("name")
|