remotivelabs-cli 0.1.2__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.
Files changed (65) hide show
  1. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/PKG-INFO +1 -1
  2. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/auth/cmd.py +2 -5
  3. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/auth/login.py +2 -1
  4. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/auth_tokens.py +29 -14
  5. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/organisations.py +3 -3
  6. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/errors.py +1 -1
  7. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/remotive.py +55 -19
  8. remotivelabs_cli-0.2.0/cli/settings/__init__.py +21 -0
  9. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/settings/config_file.py +32 -31
  10. remotivelabs_cli-0.2.0/cli/settings/core.py +301 -0
  11. {remotivelabs_cli-0.1.2/cli/settings → remotivelabs_cli-0.2.0/cli/settings/migration}/migrate_all_token_files.py +18 -12
  12. remotivelabs_cli-0.2.0/cli/settings/migration/migrate_config_file.py +59 -0
  13. remotivelabs_cli-0.2.0/cli/settings/migration/migrate_legacy_dirs.py +50 -0
  14. remotivelabs_cli-0.2.0/cli/settings/migration/migration_tools.py +36 -0
  15. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/settings/token_file.py +14 -0
  16. remotivelabs_cli-0.2.0/cli/topology/cmd.py +101 -0
  17. remotivelabs_cli-0.2.0/cli/utils/__init__.py +0 -0
  18. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/pyproject.toml +5 -1
  19. remotivelabs_cli-0.1.2/cli/settings/__init__.py +0 -4
  20. remotivelabs_cli-0.1.2/cli/settings/core.py +0 -404
  21. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/LICENSE +0 -0
  22. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/README.md +0 -0
  23. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/.DS_Store +0 -0
  24. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/__init__.py +0 -0
  25. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/api/cloud/tokens.py +0 -0
  26. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/broker/brokers.py +0 -0
  27. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/broker/export.py +0 -0
  28. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/broker/files.py +0 -0
  29. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/broker/lib/__about__.py +0 -0
  30. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/broker/lib/broker.py +0 -0
  31. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/broker/license_flows.py +0 -0
  32. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/broker/licenses.py +0 -0
  33. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/broker/playback.py +0 -0
  34. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/broker/record.py +0 -0
  35. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/broker/scripting.py +0 -0
  36. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/broker/signals.py +0 -0
  37. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/__init__.py +0 -0
  38. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/auth/__init__.py +0 -0
  39. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/brokers.py +0 -0
  40. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/cloud_cli.py +0 -0
  41. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/configs.py +0 -0
  42. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/projects.py +0 -0
  43. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/recordings.py +0 -0
  44. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/recordings_playback.py +0 -0
  45. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/resumable_upload.py +0 -0
  46. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/sample_recordings.py +0 -0
  47. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/service_account_tokens.py +0 -0
  48. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/service_accounts.py +0 -0
  49. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/storage/__init__.py +0 -0
  50. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/storage/cmd.py +0 -0
  51. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/storage/copy.py +0 -0
  52. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/storage/uri_or_path.py +0 -0
  53. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/cloud/uri.py +0 -0
  54. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/connect/__init__.py +0 -0
  55. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/connect/connect.py +0 -0
  56. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/connect/protopie/protopie.py +0 -0
  57. {remotivelabs_cli-0.1.2/cli/tools → remotivelabs_cli-0.2.0/cli/settings/migration}/__init__.py +0 -0
  58. {remotivelabs_cli-0.1.2/cli/settings → remotivelabs_cli-0.2.0/cli/settings/migration}/migrate_token_file.py +0 -0
  59. {remotivelabs_cli-0.1.2/cli/tools/can → remotivelabs_cli-0.2.0/cli/tools}/__init__.py +0 -0
  60. {remotivelabs_cli-0.1.2/cli/typer → remotivelabs_cli-0.2.0/cli/tools/can}/__init__.py +0 -0
  61. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/tools/can/can.py +0 -0
  62. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/tools/tools.py +0 -0
  63. {remotivelabs_cli-0.1.2/cli/utils → remotivelabs_cli-0.2.0/cli/typer}/__init__.py +0 -0
  64. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/typer/typer_utils.py +0 -0
  65. {remotivelabs_cli-0.1.2 → remotivelabs_cli-0.2.0}/cli/utils/rest_helper.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: remotivelabs-cli
3
- Version: 0.1.2
3
+ Version: 0.2.0
4
4
  Summary: CLI for operating RemotiveCloud and RemotiveBroker
5
5
  Author: Johan Rask
6
6
  Author-email: johan.rask@remotivelabs.com
@@ -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
- token_name = config.accounts[account].credentials_name
61
+ token_file_name = config.accounts[account].credentials_file
65
62
  try:
66
- print(settings.get_token_file(token_name).token)
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 TokenFile, TokenNotFoundError, settings
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 TokenFile, TokenNotFoundError, settings
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.credentials_name and account.credentials_name in (token.name or ""):
43
- included_tokens.append(token)
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 = active_account is not None and active_account.credentials_name == choice.name
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 is not None:
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 is not None:
88
- token = settings.get_token_file(account.credentials_name)
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 "
@@ -41,4 +41,4 @@ class ErrorPrinter:
41
41
 
42
42
  @staticmethod
43
43
  def print_generic_message(message: str) -> None:
44
- err_console.print(f"[bold]{message}[/bold]:")
44
+ err_console.print(f"[bold]{message}[/bold]")
@@ -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 # type: ignore
9
+ from trogon import Trogon
10
10
  from typer.main import get_group
11
11
 
12
- from cli.settings.migrate_all_token_files import migrate_any_legacy_tokens
13
-
14
- from .broker.brokers import app as broker_app
15
- from .cloud.cloud_cli import app as cloud_app
16
- from .connect.connect import app as connect_app
17
- from .settings import settings
18
- from .tools.tools import app as tools_app
19
- from .typer import typer_utils
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 _migrate_old_tokens() -> None:
52
- tokens = settings.list_personal_tokens()
53
- tokens.extend(settings.list_service_account_tokens())
54
- if migrate_any_legacy_tokens(tokens):
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 _set_default_org_as_env() -> None:
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(None, "--version", callback=version_callback, is_eager=False, help="Print current version"),
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
- _set_default_org_as_env()
74
- _migrate_old_tokens()
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 json import JSONDecodeError
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
- try:
14
- d = json.loads(data)
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
- credentials_name: str
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: Dict[str, Account] = dataclasses.field(default_factory=dict)
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 is not None else None
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 is not None:
46
- account = self.accounts.get(self.active)
47
- if account is not None:
48
- return account
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 None
45
+ return account
51
46
 
52
47
  def activate(self, email: str) -> None:
53
- account = self.accounts.get(email)
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 self.accounts[email]
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, token_name: str) -> None:
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.accounts.get(email)
66
+ account = self.get_account(email)
74
67
  if not account:
75
- account = Account(credentials_name=token_name)
68
+ account = Account(credentials_file=token_file.get_token_file_name())
76
69
  else:
77
- account.credentials_name = token_name
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.accounts.get(email)
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)