remotivelabs-cli 0.1.1__tar.gz → 0.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.
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/PKG-INFO +1 -1
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/auth/cmd.py +2 -5
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/auth/login.py +2 -1
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/auth_tokens.py +29 -14
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/organisations.py +3 -3
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/recordings.py +7 -5
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/errors.py +1 -1
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/remotive.py +55 -19
- remotivelabs_cli-0.2.0/cli/settings/__init__.py +21 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/settings/config_file.py +32 -31
- remotivelabs_cli-0.2.0/cli/settings/core.py +301 -0
- {remotivelabs_cli-0.1.1/cli/settings → remotivelabs_cli-0.2.0/cli/settings/migration}/migrate_all_token_files.py +18 -12
- remotivelabs_cli-0.2.0/cli/settings/migration/migrate_config_file.py +59 -0
- remotivelabs_cli-0.2.0/cli/settings/migration/migrate_legacy_dirs.py +50 -0
- remotivelabs_cli-0.2.0/cli/settings/migration/migration_tools.py +36 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/settings/token_file.py +14 -0
- remotivelabs_cli-0.2.0/cli/topology/cmd.py +101 -0
- remotivelabs_cli-0.2.0/cli/utils/__init__.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/pyproject.toml +5 -1
- remotivelabs_cli-0.1.1/cli/settings/__init__.py +0 -4
- remotivelabs_cli-0.1.1/cli/settings/core.py +0 -404
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/LICENSE +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/README.md +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/.DS_Store +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/__init__.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/api/cloud/tokens.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/broker/brokers.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/broker/export.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/broker/files.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/broker/lib/__about__.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/broker/lib/broker.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/broker/license_flows.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/broker/licenses.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/broker/playback.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/broker/record.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/broker/scripting.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/broker/signals.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/__init__.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/auth/__init__.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/brokers.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/cloud_cli.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/configs.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/projects.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/recordings_playback.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/resumable_upload.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/sample_recordings.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/service_account_tokens.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/service_accounts.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/storage/__init__.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/storage/cmd.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/storage/copy.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/storage/uri_or_path.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/cloud/uri.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/connect/__init__.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/connect/connect.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/connect/protopie/protopie.py +0 -0
- {remotivelabs_cli-0.1.1/cli/tools → remotivelabs_cli-0.2.0/cli/settings/migration}/__init__.py +0 -0
- {remotivelabs_cli-0.1.1/cli/settings → remotivelabs_cli-0.2.0/cli/settings/migration}/migrate_token_file.py +0 -0
- {remotivelabs_cli-0.1.1/cli/tools/can → remotivelabs_cli-0.2.0/cli/tools}/__init__.py +0 -0
- {remotivelabs_cli-0.1.1/cli/typer → remotivelabs_cli-0.2.0/cli/tools/can}/__init__.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/tools/can/can.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/tools/tools.py +0 -0
- {remotivelabs_cli-0.1.1/cli/utils → remotivelabs_cli-0.2.0/cli/typer}/__init__.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/typer/typer_utils.py +0 -0
- {remotivelabs_cli-0.1.1 → remotivelabs_cli-0.2.0}/cli/utils/rest_helper.py +0 -0
@@ -27,9 +27,6 @@ def login(browser: bool = typer.Option(default=True, help="Does not automaticall
|
|
27
27
|
If not able to open a browser it will show fallback to headless login and show a link that
|
28
28
|
users can copy into any browser when this is unsupported where running the cli - such as in docker,
|
29
29
|
virtual machine or ssh sessions.
|
30
|
-
|
31
|
-
This will be used as the current access token in all subsequent requests. This would
|
32
|
-
be the same as activating a personal access key or service-account access key.
|
33
30
|
"""
|
34
31
|
do_login(headless=not browser)
|
35
32
|
|
@@ -61,9 +58,9 @@ def print_access_token(
|
|
61
58
|
else:
|
62
59
|
config = settings.get_cli_config()
|
63
60
|
if account in config.accounts:
|
64
|
-
|
61
|
+
token_file_name = config.accounts[account].credentials_file
|
65
62
|
try:
|
66
|
-
print(settings.get_token_file(
|
63
|
+
print(settings.get_token_file(token_file_name).token)
|
67
64
|
except TokenNotFoundError:
|
68
65
|
ErrorPrinter.print_generic_error(f"Token file for {account} could not be found", exit_code=1)
|
69
66
|
else:
|
@@ -18,7 +18,8 @@ from typing_extensions import override
|
|
18
18
|
|
19
19
|
from cli.cloud.auth_tokens import do_activate, prompt_to_set_org
|
20
20
|
from cli.errors import ErrorPrinter
|
21
|
-
from cli.settings import
|
21
|
+
from cli.settings import TokenNotFoundError, settings
|
22
|
+
from cli.settings.token_file import TokenFile
|
22
23
|
from cli.utils.rest_helper import RestHelper as Rest
|
23
24
|
|
24
25
|
httpd: HTTPServer
|
@@ -9,7 +9,9 @@ from rich.table import Table
|
|
9
9
|
from cli.api.cloud import tokens
|
10
10
|
from cli.cloud.organisations import do_select_default_org
|
11
11
|
from cli.errors import ErrorPrinter
|
12
|
-
from cli.settings import
|
12
|
+
from cli.settings import TokenNotFoundError, settings
|
13
|
+
from cli.settings.config_file import Account
|
14
|
+
from cli.settings.token_file import TokenFile
|
13
15
|
from cli.typer import typer_utils
|
14
16
|
from cli.utils.rest_helper import RestHelper as Rest
|
15
17
|
|
@@ -27,10 +29,6 @@ def _prompt_choice( # noqa: C901, PLR0912
|
|
27
29
|
info_message: Optional[str] = None,
|
28
30
|
) -> Optional[TokenFile]:
|
29
31
|
accounts = settings.get_cli_config().accounts
|
30
|
-
try:
|
31
|
-
active_account = settings.get_cli_config().get_active()
|
32
|
-
except TokenNotFoundError:
|
33
|
-
active_account = None
|
34
32
|
|
35
33
|
table = Table("#", "Active", "Type", "Token", "Account", "Created", "Expires")
|
36
34
|
|
@@ -39,8 +37,13 @@ def _prompt_choice( # noqa: C901, PLR0912
|
|
39
37
|
|
40
38
|
for token in choices:
|
41
39
|
account = accounts.get(token.account.email)
|
42
|
-
if account and account.
|
43
|
-
|
40
|
+
if account and account.credentials_file:
|
41
|
+
try:
|
42
|
+
token_file = settings.get_token_file(account.credentials_file)
|
43
|
+
if token_file.name in (token.name or ""):
|
44
|
+
included_tokens.append(token)
|
45
|
+
except TokenNotFoundError:
|
46
|
+
excluded_tokens.append(token)
|
44
47
|
else:
|
45
48
|
excluded_tokens.append(token)
|
46
49
|
|
@@ -49,10 +52,27 @@ def _prompt_choice( # noqa: C901, PLR0912
|
|
49
52
|
|
50
53
|
included_tokens.sort(key=lambda token: token.created, reverse=True)
|
51
54
|
|
55
|
+
def get_active_account_or_none() -> Optional[Account]:
|
56
|
+
try:
|
57
|
+
return settings.get_cli_config().get_active()
|
58
|
+
except TokenNotFoundError:
|
59
|
+
return None
|
60
|
+
|
61
|
+
def get_active_token_or_none() -> Optional[TokenFile]:
|
62
|
+
try:
|
63
|
+
active_account = get_active_account_or_none()
|
64
|
+
if active_account is not None:
|
65
|
+
return settings.get_token_file(active_account.credentials_file)
|
66
|
+
except TokenNotFoundError:
|
67
|
+
pass
|
68
|
+
return None
|
69
|
+
|
70
|
+
active_token = get_active_token_or_none()
|
52
71
|
active_token_index = None
|
53
72
|
for idx, choice in enumerate(included_tokens, start=1):
|
54
|
-
is_active =
|
73
|
+
is_active = active_token is not None and active_token.name == choice.name
|
55
74
|
active_token_index = idx if is_active else active_token_index
|
75
|
+
|
56
76
|
table.add_row(
|
57
77
|
f"[yellow]{idx}",
|
58
78
|
":white_check_mark:" if is_active else "",
|
@@ -62,7 +82,6 @@ def _prompt_choice( # noqa: C901, PLR0912
|
|
62
82
|
str(choice.created),
|
63
83
|
str(choice.expires),
|
64
84
|
)
|
65
|
-
# console.print("It seems like you have access tokens from previous login, you can select one of these instead of logging in")
|
66
85
|
console.print(table)
|
67
86
|
|
68
87
|
if skip_prompt:
|
@@ -165,9 +184,7 @@ def select_personal_token(
|
|
165
184
|
do_activate(token_name)
|
166
185
|
|
167
186
|
|
168
|
-
def do_activate(
|
169
|
-
token_name: Optional[str],
|
170
|
-
) -> Optional[TokenFile]:
|
187
|
+
def do_activate(token_name: Optional[str]) -> Optional[TokenFile]:
|
171
188
|
if token_name is not None:
|
172
189
|
try:
|
173
190
|
token_file = settings.get_token_file(token_name)
|
@@ -208,8 +225,6 @@ def list_and_select_personal_token(
|
|
208
225
|
sa_tokens = settings.list_service_account_tokens()
|
209
226
|
personal_tokens.extend(sa_tokens)
|
210
227
|
|
211
|
-
# merged = _merge_local_tokens_with_cloud(personal_tokens)
|
212
|
-
|
213
228
|
selected_token = _prompt_choice(personal_tokens, skip_prompt=skip_prompt, info_message=info_message)
|
214
229
|
if selected_token is not None:
|
215
230
|
settings.activate_token(selected_token)
|
@@ -76,7 +76,7 @@ def do_select_default_org(organisation_uid: Optional[str] = None, get: bool = Fa
|
|
76
76
|
"""
|
77
77
|
if get:
|
78
78
|
default_organisation = settings.get_cli_config().get_active_default_organisation()
|
79
|
-
if default_organisation
|
79
|
+
if default_organisation:
|
80
80
|
console.print(default_organisation)
|
81
81
|
else:
|
82
82
|
console.print("No default organization set")
|
@@ -84,8 +84,8 @@ def do_select_default_org(organisation_uid: Optional[str] = None, get: bool = Fa
|
|
84
84
|
settings.set_default_organisation(organisation_uid)
|
85
85
|
else:
|
86
86
|
account = settings.get_cli_config().get_active()
|
87
|
-
if account
|
88
|
-
token = settings.get_token_file(account.
|
87
|
+
if account:
|
88
|
+
token = settings.get_token_file(account.credentials_file)
|
89
89
|
if token.type != "authorized_user":
|
90
90
|
ErrorPrinter.print_hint(
|
91
91
|
"You must supply the organization name as argument when using a service-account since the "
|
@@ -137,14 +137,13 @@ def mount( # noqa: C901
|
|
137
137
|
r = Rest.handle_get(url=f"/api/project/{project}/brokers/personal", return_response=True, allow_status_codes=[404])
|
138
138
|
|
139
139
|
if r.status_code == 200:
|
140
|
-
|
141
|
-
|
140
|
+
broker_info = r.json()
|
141
|
+
broker = broker_info["shortName"]
|
142
142
|
elif r.status_code == 404:
|
143
143
|
r = do_start("personal", project, "", return_response=True)
|
144
144
|
if r.status_code != 200:
|
145
145
|
print(r.text)
|
146
146
|
sys.exit(0)
|
147
|
-
broker = r.json()["shortName"]
|
148
147
|
else:
|
149
148
|
sys.stderr.write(f"Got http status code {r.status_code}")
|
150
149
|
raise typer.Exit(0)
|
@@ -154,7 +153,6 @@ def mount( # noqa: C901
|
|
154
153
|
if r.status_code == 404:
|
155
154
|
if ensure_broker_started:
|
156
155
|
r = do_start(broker, project, "", return_response=True)
|
157
|
-
|
158
156
|
if r.status_code != 200:
|
159
157
|
print(r.text)
|
160
158
|
sys.exit(1)
|
@@ -164,6 +162,9 @@ def mount( # noqa: C901
|
|
164
162
|
elif r.status_code != 200:
|
165
163
|
sys.stderr.write(f"Got http status code {r.status_code}")
|
166
164
|
raise typer.Exit(1)
|
165
|
+
|
166
|
+
broker_info = r.json()
|
167
|
+
broker = broker_info["shortName"]
|
167
168
|
broker_config_query = ""
|
168
169
|
if transformation_name != "default":
|
169
170
|
broker_config_query = f"?brokerConfigName={transformation_name}"
|
@@ -174,7 +175,8 @@ def mount( # noqa: C901
|
|
174
175
|
return_response=True,
|
175
176
|
progress_label="Preparing recording on broker...",
|
176
177
|
)
|
177
|
-
print("Successfully mounted recording on broker")
|
178
|
+
err_console.print("Successfully mounted recording on broker")
|
179
|
+
print(json.dumps(broker_info))
|
178
180
|
|
179
181
|
|
180
182
|
@app.command(help="Downloads the specified recording file to disk")
|
@@ -6,20 +6,29 @@ from importlib.metadata import version
|
|
6
6
|
import typer
|
7
7
|
from rich import print as rich_print
|
8
8
|
from rich.console import Console
|
9
|
-
from trogon import Trogon
|
9
|
+
from trogon import Trogon
|
10
10
|
from typer.main import get_group
|
11
11
|
|
12
|
-
from cli.
|
13
|
-
|
14
|
-
from .
|
15
|
-
from .
|
16
|
-
from .
|
17
|
-
from .settings import
|
18
|
-
from .
|
19
|
-
from .
|
12
|
+
from cli.broker.brokers import app as broker_app
|
13
|
+
from cli.cloud.cloud_cli import app as cloud_app
|
14
|
+
from cli.connect.connect import app as connect_app
|
15
|
+
from cli.settings import settings
|
16
|
+
from cli.settings.core import Settings
|
17
|
+
from cli.settings.migration.migrate_all_token_files import migrate_any_legacy_tokens
|
18
|
+
from cli.settings.migration.migrate_config_file import migrate_config_file
|
19
|
+
from cli.settings.migration.migrate_legacy_dirs import migrate_legacy_settings_dirs
|
20
|
+
from cli.tools.tools import app as tools_app
|
21
|
+
from cli.topology.cmd import app as topology_app
|
22
|
+
from cli.typer import typer_utils
|
20
23
|
|
21
24
|
err_console = Console(stderr=True)
|
22
25
|
|
26
|
+
|
27
|
+
def is_featue_flag_enabled(env_var: str) -> bool:
|
28
|
+
"""Check if an environment variable indicates a feature is enabled."""
|
29
|
+
return os.getenv(env_var, "").lower() in ("true", "1", "yes", "on")
|
30
|
+
|
31
|
+
|
23
32
|
if os.getenv("GRPC_VERBOSITY") is None:
|
24
33
|
os.environ["GRPC_VERBOSITY"] = "NONE"
|
25
34
|
|
@@ -32,8 +41,6 @@ For documentation - https://docs.remotivelabs.com
|
|
32
41
|
""",
|
33
42
|
)
|
34
43
|
|
35
|
-
# settings.set_default_config_as_env()
|
36
|
-
|
37
44
|
|
38
45
|
def version_callback(value: bool) -> None:
|
39
46
|
if value:
|
@@ -48,14 +55,26 @@ def test_callback(value: int) -> None:
|
|
48
55
|
raise typer.Exit()
|
49
56
|
|
50
57
|
|
51
|
-
def
|
52
|
-
|
53
|
-
|
54
|
-
|
58
|
+
def run_migrations(settings: Settings) -> None:
|
59
|
+
"""
|
60
|
+
Run all migration scripts.
|
61
|
+
|
62
|
+
Each migration script is responsible for a particular migration, and order matters.
|
63
|
+
"""
|
64
|
+
# 1. Migrate legacy settings dirs
|
65
|
+
migrate_legacy_settings_dirs(settings.config_dir)
|
66
|
+
|
67
|
+
# 2. Migrate any legacy tokens
|
68
|
+
has_migrated_tokens = migrate_any_legacy_tokens(settings)
|
69
|
+
|
70
|
+
# 3. Migrate legacy config file format
|
71
|
+
migrate_config_file(settings.config_file_path, settings)
|
72
|
+
|
73
|
+
if has_migrated_tokens:
|
55
74
|
err_console.print("Migrated old credentials and configuration files, you may need to login again or activate correct credentials")
|
56
75
|
|
57
76
|
|
58
|
-
def
|
77
|
+
def set_default_org_as_env(settings: Settings) -> None:
|
59
78
|
"""
|
60
79
|
If not already set, take the default organisation from file and set as env
|
61
80
|
This has to be done early before it is read
|
@@ -68,10 +87,16 @@ def _set_default_org_as_env() -> None:
|
|
68
87
|
|
69
88
|
@app.callback()
|
70
89
|
def main(
|
71
|
-
_the_version: bool = typer.Option(
|
90
|
+
_the_version: bool = typer.Option(
|
91
|
+
None,
|
92
|
+
"--version",
|
93
|
+
callback=version_callback,
|
94
|
+
is_eager=False,
|
95
|
+
help="Print current version",
|
96
|
+
),
|
72
97
|
) -> None:
|
73
|
-
|
74
|
-
|
98
|
+
run_migrations(settings)
|
99
|
+
set_default_org_as_env(settings)
|
75
100
|
# Do other global stuff, handle other global options here
|
76
101
|
|
77
102
|
|
@@ -92,3 +117,14 @@ app.add_typer(
|
|
92
117
|
)
|
93
118
|
app.add_typer(connect_app, name="connect", help="Integrations with other systems")
|
94
119
|
app.add_typer(tools_app, name="tools")
|
120
|
+
|
121
|
+
if is_featue_flag_enabled("REMOTIVE_TOPOLOGY_ENABLED"):
|
122
|
+
app.add_typer(
|
123
|
+
topology_app,
|
124
|
+
name="topology",
|
125
|
+
help="""
|
126
|
+
RemotiveTopology actions
|
127
|
+
|
128
|
+
Read more at https://docs.remotivelabs.com/docs/remotive-topology
|
129
|
+
""",
|
130
|
+
)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from cli.settings.config_file import Account, ConfigFile
|
2
|
+
from cli.settings.config_file import dumps as dumps_config_file
|
3
|
+
from cli.settings.config_file import loads as loads_config_file
|
4
|
+
from cli.settings.core import InvalidSettingsFilePathError, Settings, TokenNotFoundError, settings
|
5
|
+
from cli.settings.token_file import TokenFile
|
6
|
+
from cli.settings.token_file import dumps as dumps_token_file
|
7
|
+
from cli.settings.token_file import loads as loads_token_file
|
8
|
+
|
9
|
+
__all__ = [
|
10
|
+
"settings",
|
11
|
+
"TokenNotFoundError",
|
12
|
+
"InvalidSettingsFilePathError",
|
13
|
+
"Settings",
|
14
|
+
"TokenFile",
|
15
|
+
"ConfigFile",
|
16
|
+
"Account",
|
17
|
+
"dumps_config_file",
|
18
|
+
"loads_config_file",
|
19
|
+
"dumps_token_file",
|
20
|
+
"loads_token_file",
|
21
|
+
]
|
@@ -3,21 +3,16 @@ from __future__ import annotations
|
|
3
3
|
import dataclasses
|
4
4
|
import json
|
5
5
|
from dataclasses import dataclass
|
6
|
-
from
|
7
|
-
from typing import Dict, Optional
|
6
|
+
from typing import Any, Optional
|
8
7
|
|
9
8
|
from dacite import from_dict
|
10
9
|
|
10
|
+
from cli.settings.token_file import TokenFile
|
11
|
+
|
11
12
|
|
12
13
|
def loads(data: str) -> ConfigFile:
|
13
|
-
|
14
|
-
|
15
|
-
return from_dict(ConfigFile, d)
|
16
|
-
except JSONDecodeError as e:
|
17
|
-
# ErrorPrinter.print_generic_error("Invalid json format, config.json")
|
18
|
-
raise JSONDecodeError(
|
19
|
-
f"File config.json is not valid json, please edit or remove file to have it re-created ({e.msg})", pos=e.pos, doc=e.doc
|
20
|
-
)
|
14
|
+
d = json.loads(data)
|
15
|
+
return from_dict(ConfigFile, d)
|
21
16
|
|
22
17
|
|
23
18
|
def dumps(config: ConfigFile) -> str:
|
@@ -26,7 +21,7 @@ def dumps(config: ConfigFile) -> str:
|
|
26
21
|
|
27
22
|
@dataclass
|
28
23
|
class Account:
|
29
|
-
|
24
|
+
credentials_file: str
|
30
25
|
default_organization: Optional[str] = None
|
31
26
|
# Add project as well
|
32
27
|
|
@@ -35,53 +30,51 @@ class Account:
|
|
35
30
|
class ConfigFile:
|
36
31
|
version: str = "1.0"
|
37
32
|
active: Optional[str] = None
|
38
|
-
accounts:
|
33
|
+
accounts: dict[str, Account] = dataclasses.field(default_factory=dict)
|
39
34
|
|
40
35
|
def get_active_default_organisation(self) -> Optional[str]:
|
41
36
|
active_account = self.get_active()
|
42
|
-
return active_account.default_organization if active_account
|
37
|
+
return active_account.default_organization if active_account else None
|
43
38
|
|
44
39
|
def get_active(self) -> Optional[Account]:
|
45
|
-
if self.active
|
46
|
-
|
47
|
-
|
48
|
-
|
40
|
+
if not self.active:
|
41
|
+
return None
|
42
|
+
account = self.get_account(self.active)
|
43
|
+
if not account:
|
49
44
|
raise KeyError(f"Activated account {self.active} is not a valid account")
|
50
|
-
return
|
45
|
+
return account
|
51
46
|
|
52
47
|
def activate(self, email: str) -> None:
|
53
|
-
account = self.
|
54
|
-
|
55
|
-
if account is not None:
|
56
|
-
self.active = email
|
57
|
-
else:
|
48
|
+
account = self.get_account(email)
|
49
|
+
if not account:
|
58
50
|
raise KeyError(f"Account {email} does not exists")
|
51
|
+
self.active = email
|
59
52
|
|
60
53
|
def get_account(self, email: str) -> Optional[Account]:
|
61
|
-
if self.accounts:
|
62
|
-
return
|
63
|
-
return None
|
54
|
+
if not self.accounts:
|
55
|
+
return None
|
56
|
+
return self.accounts.get(email, None)
|
64
57
|
|
65
58
|
def remove_account(self, email: str) -> None:
|
66
59
|
if self.accounts:
|
67
60
|
self.accounts.pop(email, None)
|
68
61
|
|
69
|
-
def init_account(self, email: str,
|
62
|
+
def init_account(self, email: str, token_file: TokenFile) -> None:
|
70
63
|
if self.accounts is None:
|
71
64
|
self.accounts = {}
|
72
65
|
|
73
|
-
account = self.
|
66
|
+
account = self.get_account(email)
|
74
67
|
if not account:
|
75
|
-
account = Account(
|
68
|
+
account = Account(credentials_file=token_file.get_token_file_name())
|
76
69
|
else:
|
77
|
-
account.
|
70
|
+
account.credentials_file = token_file.get_token_file_name()
|
78
71
|
self.accounts[email] = account
|
79
72
|
|
80
73
|
def set_account_field(self, email: str, default_organization: Optional[str] = None) -> ConfigFile:
|
81
74
|
if self.accounts is None:
|
82
75
|
self.accounts = {}
|
83
76
|
|
84
|
-
account = self.
|
77
|
+
account = self.get_account(email)
|
85
78
|
if not account:
|
86
79
|
raise KeyError(f"Account with email {email} has not been initialized with token")
|
87
80
|
|
@@ -90,3 +83,11 @@ class ConfigFile:
|
|
90
83
|
account.default_organization = default_organization
|
91
84
|
|
92
85
|
return self
|
86
|
+
|
87
|
+
@staticmethod
|
88
|
+
def from_json_str(data: str) -> ConfigFile:
|
89
|
+
return loads(data)
|
90
|
+
|
91
|
+
@staticmethod
|
92
|
+
def from_dict(data: dict[str, Any]) -> ConfigFile:
|
93
|
+
return from_dict(ConfigFile, data)
|