remotivelabs-cli 0.0.26__tar.gz → 0.0.27__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 (44) hide show
  1. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/PKG-INFO +1 -1
  2. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/broker/lib/broker.py +24 -3
  3. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/broker/signals.py +9 -1
  4. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/auth_tokens.py +37 -4
  5. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/cloud_cli.py +2 -16
  6. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/configs.py +9 -0
  7. remotivelabs_cli-0.0.27/cli/cloud/organisations.py +30 -0
  8. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/projects.py +2 -2
  9. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/recordings.py +22 -10
  10. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/pyproject.toml +1 -1
  11. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/LICENSE +0 -0
  12. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/README.md +0 -0
  13. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/__about__.py +0 -0
  14. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/__init__.py +0 -0
  15. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/broker/brokers.py +0 -0
  16. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/broker/export.py +0 -0
  17. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/broker/files.py +0 -0
  18. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/broker/lib/__about__.py +0 -0
  19. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/broker/license_flows.py +0 -0
  20. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/broker/licenses.py +0 -0
  21. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/broker/playback.py +0 -0
  22. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/broker/record.py +0 -0
  23. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/broker/scripting.py +0 -0
  24. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/__init__.py +0 -0
  25. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/auth.py +0 -0
  26. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/brokers.py +0 -0
  27. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/filestorage.py +0 -0
  28. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/recordings_playback.py +0 -0
  29. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/rest_helper.py +0 -0
  30. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/resumable_upload.py +0 -0
  31. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/sample_recordings.py +0 -0
  32. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/service_account_tokens.py +0 -0
  33. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/cloud/service_accounts.py +0 -0
  34. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/connect/__init__.py +0 -0
  35. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/connect/connect.py +0 -0
  36. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/connect/protopie/protopie.py +0 -0
  37. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/errors.py +0 -0
  38. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/remotive.py +0 -0
  39. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/requirements.txt +0 -0
  40. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/settings.py +0 -0
  41. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/tools/__init__.py +0 -0
  42. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/tools/can/__init__.py +0 -0
  43. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/tools/can/can.py +0 -0
  44. {remotivelabs_cli-0.0.26 → remotivelabs_cli-0.0.27}/cli/tools/tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: remotivelabs-cli
3
- Version: 0.0.26
3
+ Version: 0.0.27
4
4
  Summary: CLI for operating RemotiveCloud and RemotiveBroker
5
5
  Author: Johan Rask
6
6
  Author-email: johan.rask@remotivelabs.com
@@ -360,7 +360,7 @@ class Broker:
360
360
  namespaces.append(network_info.namespace.name)
361
361
  return namespaces
362
362
 
363
- def list_signal_names(self) -> List[Dict[str, str]]:
363
+ def list_signal_names(self) -> List[Dict[str, Any]]:
364
364
  # Lists available signals
365
365
  configuration = self.system_stub.GetConfiguration(br.common_pb2.Empty())
366
366
 
@@ -369,9 +369,30 @@ class Broker:
369
369
  res = self.system_stub.ListSignals(network_info.namespace)
370
370
  for finfo in res.frame:
371
371
  # f: br.common_pb2.FrameInfo = finfo
372
- signal_names.append({"signal": finfo.signalInfo.id.name, "namespace": network_info.namespace.name})
372
+ receivers = []
373
373
  for sinfo in finfo.childInfo:
374
- signal_names.append({"signal": sinfo.id.name, "namespace": network_info.namespace.name})
374
+ rec = list(map(lambda r: r, sinfo.metaData.receiver))
375
+ receivers.extend(rec)
376
+ signal_names.append(
377
+ {
378
+ "signal": sinfo.id.name,
379
+ "namespace": network_info.namespace.name,
380
+ "receivers": rec,
381
+ "min": sinfo.metaData.min,
382
+ "max": sinfo.metaData.max,
383
+ }
384
+ )
385
+
386
+ signal_names.append(
387
+ {
388
+ "signal": finfo.signalInfo.id.name,
389
+ "namespace": network_info.namespace.name,
390
+ "senders": list(map(lambda s: s, finfo.signalInfo.metaData.sender)),
391
+ "receivers": list(set(receivers)),
392
+ "cycletime": finfo.signalInfo.metaData.cycleTime,
393
+ }
394
+ )
395
+
375
396
  return signal_names
376
397
 
377
398
  def subscribe_on_script(
@@ -30,7 +30,15 @@ class Signals(TypedDict):
30
30
  signal_values: Dict[Any, Any] = {}
31
31
 
32
32
 
33
- @app.command(help="List signals names on broker")
33
+ @app.command(name="list", help="List frame and signal metadata on broker")
34
+ def list_signals(
35
+ url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
36
+ api_key: str = typer.Option(None, help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
37
+ ) -> None:
38
+ signal_names(url, api_key)
39
+
40
+
41
+ @app.command(help="List signals names on broker", deprecated=True)
34
42
  def signal_names(
35
43
  url: str = typer.Option(..., help="Broker URL", envvar="REMOTIVE_BROKER_URL"),
36
44
  api_key: str = typer.Option(None, help="Cloud Broker API-KEY or access token", envvar="REMOTIVE_BROKER_API_KEY"),
@@ -1,6 +1,7 @@
1
1
  import json
2
2
  import os
3
3
  import sys
4
+ from json.decoder import JSONDecodeError
4
5
  from pathlib import Path
5
6
 
6
7
  import typer
@@ -42,14 +43,37 @@ def list_personal_access_tokens() -> None:
42
43
  Rest.handle_get("/api/me/keys")
43
44
 
44
45
 
45
- @app.command(name="revoke", help="Revoke the specified access token")
46
- def revoke(name: str = typer.Option(..., help="Name of the access token to revoke")) -> None:
46
+ @app.command(name="revoke")
47
+ def revoke(name_or_file: str = typer.Argument(help="Name or file path of the access token to revoke")) -> None:
48
+ """
49
+ Revoke an access token by token name or path to a file containing that token
50
+
51
+ Name is found in the json file
52
+
53
+ {
54
+ "expires": "2034-07-31",
55
+ "token": "xxx",
56
+ "created": "2024-07-31T09:18:50.406+02:00",
57
+ "name": "token_name"
58
+ }
59
+ """
60
+ name = name_or_file
61
+ if "." in name_or_file:
62
+ json_str = read_file(name_or_file)
63
+ try:
64
+ name = json.loads(json_str)["name"]
65
+ except JSONDecodeError:
66
+ sys.stderr.write("Failed to parse json, make sure its a correct access token file\n")
67
+ sys.exit(1)
68
+ except KeyError:
69
+ sys.stderr.write("Json does not contain a name property, make sure its a correct access token file\n")
70
+ sys.exit(1)
47
71
  Rest.ensure_auth_token()
48
72
  Rest.handle_delete(f"/api/me/keys/{name}")
49
73
 
50
74
 
51
75
  @app.command()
52
- def describe(file: str = typer.Option(..., help="File name")) -> None:
76
+ def describe(file: str = typer.Argument(help="File name")) -> None:
53
77
  """
54
78
  Show contents of specified access token file
55
79
  """
@@ -92,7 +116,16 @@ def list_files() -> None:
92
116
 
93
117
 
94
118
  def read_file(file: str) -> str:
95
- with open(str(Path.home()) + f"/.config/.remotive/{file}", "r", encoding="utf8") as f:
119
+ """
120
+ Reads a file using file path or if that does not exist check under ~/.config/.remotive
121
+ """
122
+ path = file
123
+ if not Path(file).exists():
124
+ path = str(Path.home()) + f"/.config/.remotive/{file}"
125
+ if not Path(path).exists():
126
+ sys.stderr.write(f"Failed to find file using {file} or {path}\n")
127
+ sys.exit(1)
128
+ with open(path, "r", encoding="utf8") as f:
96
129
  token = f.read()
97
130
  f.close()
98
131
  return token
@@ -1,10 +1,8 @@
1
- import json
2
-
3
1
  import typer
4
2
 
5
3
  from cli.cloud.rest_helper import RestHelper
6
4
 
7
- from . import auth, brokers, configs, filestorage, projects, recordings, sample_recordings, service_accounts
5
+ from . import auth, brokers, configs, filestorage, organisations, projects, recordings, sample_recordings, service_accounts
8
6
 
9
7
  app = typer.Typer()
10
8
 
@@ -17,19 +15,7 @@ def licenses(
17
15
  RestHelper.handle_get(f"/api/bu/{organisation}/licenses", {"filter": filter_option})
18
16
 
19
17
 
20
- @app.command(help="List your available organisations")
21
- def organisations() -> None:
22
- r = RestHelper.handle_get("/api/home", return_response=True)
23
- if r is None:
24
- return
25
- if r.status_code == 200:
26
- j = list(map(lambda x: x["billableUnitUser"]["billableUnit"]["uid"], r.json()))
27
- print(json.dumps(j))
28
- else:
29
- print(f"Got status code: {r.status_code}")
30
- print(r.text)
31
-
32
-
18
+ app.add_typer(organisations.app, name="organisations", help="Manage organisations")
33
19
  app.add_typer(projects.app, name="projects", help="Manage projects")
34
20
  app.add_typer(auth.app, name="auth")
35
21
  app.add_typer(brokers.app, name="brokers", help="Manage cloud broker lifecycle")
@@ -14,6 +14,9 @@ app = typer.Typer()
14
14
 
15
15
  @app.command("list")
16
16
  def list_signal_databases(project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT")) -> None:
17
+ """
18
+ List available signal databases in project
19
+ """
17
20
  Rest.handle_get(f"/api/project/{project}/files/config")
18
21
 
19
22
 
@@ -22,6 +25,9 @@ def delete(
22
25
  signal_db_file: str = typer.Argument("", help="Signal database file"),
23
26
  project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
24
27
  ) -> None:
28
+ """
29
+ Deletes the specified signal database
30
+ """
25
31
  Rest.handle_delete(f"/api/project/{project}/files/config/{signal_db_file}")
26
32
 
27
33
 
@@ -54,6 +60,9 @@ def download(
54
60
  signal_db_file: str = typer.Argument("", help="Signal database file"),
55
61
  project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
56
62
  ) -> None:
63
+ """
64
+ Downloads the specified signal database to disk
65
+ """
57
66
  with Progress(
58
67
  SpinnerColumn(),
59
68
  TextColumn("[progress.description]{task.description}"),
@@ -0,0 +1,30 @@
1
+ import json
2
+ import sys
3
+
4
+ import typer
5
+
6
+ from cli.cloud.rest_helper import RestHelper
7
+
8
+ app = typer.Typer()
9
+
10
+
11
+ @app.command(name="list", help="List your available organisations")
12
+ def list_orgs() -> None:
13
+ r = RestHelper.handle_get("/api/home", return_response=True)
14
+ if r is None:
15
+ return
16
+ if r.status_code == 200:
17
+ j = list(
18
+ map(
19
+ lambda x: {
20
+ "uid": x["billableUnitUser"]["billableUnit"]["uid"],
21
+ "displayName": x["billableUnitUser"]["billableUnit"]["displayName"],
22
+ },
23
+ r.json(),
24
+ )
25
+ )
26
+ print(json.dumps(j))
27
+ else:
28
+ print(f"Got status code: {r.status_code}")
29
+ print(r.text)
30
+ sys.exit(1)
@@ -25,8 +25,8 @@ def list_projects(organisation: str = typer.Option(..., help="Organisation ID",
25
25
 
26
26
  @app.command(name="create")
27
27
  def create_project(
28
+ project_uid: str = typer.Argument(help="Project UID"),
28
29
  organisation: str = typer.Option(..., help="Organisation ID", envvar="REMOTIVE_CLOUD_ORGANISATION"),
29
- project_uid: str = typer.Option(..., help="Project UID"),
30
30
  project_display_name: str = typer.Option(default="", help="Project display name"),
31
31
  ) -> None:
32
32
  create_project_req = {
@@ -39,5 +39,5 @@ def create_project(
39
39
 
40
40
 
41
41
  @app.command(name="delete")
42
- def delete(project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT")) -> None:
42
+ def delete(project: str = typer.Argument(help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT")) -> None:
43
43
  Rest.handle_delete(url=f"/api/project/{project}")
@@ -309,23 +309,35 @@ def upload( # noqa: C901
309
309
  err_console.print(f":boom: [bold red]Got status code[/bold red]: {upload_response.status_code} {upload_response.text}")
310
310
 
311
311
 
312
+ # TODO - Change to use Path for directory # pylint: disable=W0511
312
313
  @app.command()
313
314
  def upload_broker_configuration( # noqa: C901
314
- directory: str = typer.Argument(..., help="Configuration directory"),
315
+ directory: Path = typer.Argument(
316
+ ...,
317
+ exists=True,
318
+ file_okay=False,
319
+ dir_okay=True,
320
+ writable=False,
321
+ readable=True,
322
+ resolve_path=True,
323
+ help="Directory to upload",
324
+ ),
315
325
  recording_session: str = typer.Option(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
316
326
  project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
317
327
  overwrite: bool = typer.Option(False, help="Overwrite existing configuration if it exists"),
318
328
  ) -> None:
329
+ """
330
+ Uploads a broker configuration directory
331
+ """
319
332
  # pylint: disable=R0914,R0915
333
+
320
334
  # Must end with /
321
- if not directory.endswith("/"):
322
- directory = f"{directory}/"
323
335
 
324
336
  #
325
337
  # List files in specified directory. Look for interfaces.json and use that directory where this is located
326
338
  # as configuration home directory
327
339
  #
328
- files = list(filter(lambda item: "interfaces.json" in item, glob.iglob(directory + "**/**", recursive=True)))
340
+ files = list(filter(lambda item: "interfaces.json" in item, glob.iglob(str(directory) + "/**/**", recursive=True)))
329
341
  if len(files) == 0:
330
342
  sys.stderr.write("No interfaces.json found in directory, this file is required")
331
343
  raise typer.Exit(1)
@@ -362,7 +374,7 @@ def upload_broker_configuration( # noqa: C901
362
374
  file_infos = list(
363
375
  map(
364
376
  lambda item: {"local_path": item, "remote_path": f"/{broker_config_dir_name}{item.rsplit(broker_config_dir_name, 1)[-1]}"},
365
- glob.iglob(directory + "**/*.*", recursive=True),
377
+ glob.iglob(str(directory) + "/**/*.*", recursive=True),
366
378
  )
367
379
  )
368
380
 
@@ -406,7 +418,7 @@ def upload_broker_configuration( # noqa: C901
406
418
 
407
419
 
408
420
  @app.command(help="Downloads the specified broker configuration directory as zip file")
409
- def download_configuration(
421
+ def download_broker_configuration(
410
422
  broker_config_name: str = typer.Argument(..., help="Broker config name"),
411
423
  recording_session: str = typer.Option(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
412
424
  project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
@@ -425,7 +437,7 @@ def download_configuration(
425
437
 
426
438
 
427
439
  @app.command(help="Delete the specified broker configuration")
428
- def delete_configuration(
440
+ def delete_broker_configuration(
429
441
  broker_config_name: str = typer.Argument(..., help="Broker config name"),
430
442
  recording_session: str = typer.Option(..., help="Recording session id", envvar="REMOTIVE_CLOUD_RECORDING_SESSION"),
431
443
  project: str = typer.Option(..., help="Project ID", envvar="REMOTIVE_CLOUD_PROJECT"),
@@ -436,11 +448,11 @@ def delete_configuration(
436
448
  @app.command(help="Copy recording to another project")
437
449
  def copy(
438
450
  recording_session: str = typer.Argument(..., help="Recording session id"),
439
- project: str = typer.Option(..., help="Source project", envvar="REMOTIVE_CLOUD_PROJECT"),
440
- destination_project: str = typer.Option(..., help="Destination project"),
451
+ from_project: str = typer.Option(..., help="Source project"),
452
+ to_project: str = typer.Option(..., help="Destination project"),
441
453
  ) -> None:
442
454
  Rest.handle_post(
443
- url=f"/api/project/{project}/files/recording/{recording_session}/copy", body=json.dumps({"projectUid": destination_project})
455
+ url=f"/api/project/{from_project}/files/recording/{recording_session}/copy", body=json.dumps({"projectUid": to_project})
444
456
  )
445
457
 
446
458
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "remotivelabs-cli"
3
- version = "0.0.26"
3
+ version = "0.0.27"
4
4
  description = "CLI for operating RemotiveCloud and RemotiveBroker"
5
5
  authors = ["Johan Rask <johan.rask@remotivelabs.com>"]
6
6
  readme = "README.md"