diff --git a/src/infuse_iot/tools/cloud.py b/src/infuse_iot/tools/cloud.py index 4e015ed..012ff5a 100644 --- a/src/infuse_iot/tools/cloud.py +++ b/src/infuse_iot/tools/cloud.py @@ -20,6 +20,8 @@ create_release_diff, get_application_by_organisation_id_and_application_id, get_applications_by_organisation_id, + get_diffs_by_organisation_id_and_application_id_and_release_id, + get_release_by_organisation_id_and_application_id_and_release_id, get_releases_by_organisation_id_and_application_id, ) from infuse_iot.api_client.api.board import ( @@ -57,7 +59,8 @@ def run(self): def client(self): """Get API client object ready to use""" - return Client(base_url="https://api.infuse-iot.com").with_headers({"x-api-key": f"Bearer {get_api_key()}"}) + bearer = self.args.api_key if self.args.api_key else get_api_key() + return Client(base_url="https://api.infuse-iot.com").with_headers({"x-api-key": f"Bearer {bearer}"}) class Organisations(CloudSubCommand): @@ -349,6 +352,8 @@ def add_parser(cls, parser): info_parser = tool_parser.add_parser("info", help="Display summary of application releases") info_parser.add_argument("--org", "-o", type=str, required=True, help="Organisation ID") info_parser.add_argument("--app", "-a", type=lambda x: int(x, 16), required=True, help="Application ID (hex)") + info_parser.add_argument("--rel", "-r", type=str, help="Release ID") + info_parser.add_argument("--coap", action="store_true", help="Display CoAP file information") info_parser.set_defaults(command_fn=cls.info) upload_parser = tool_parser.add_parser("upload", help="Upload application release") @@ -384,7 +389,64 @@ def list(self, client: Client): ) ) - def info(self, client: Client): + def _info_one(self, client: Client): + application = get_application_by_organisation_id_and_application_id.sync( + client=client, + id=UUID(self.args.org), + application_id=self.args.app, + ) + if application is None: + sys.exit("Get application: No response") + elif isinstance(application, models.Error): + sys.exit(f"<{application.code}>: {application.message}") + release = get_release_by_organisation_id_and_application_id_and_release_id.sync( + client=client, id=UUID(self.args.org), application_id=self.args.app, release_id=self.args.rel + ) + if release is None: + sys.exit("Get release: No response") + elif isinstance(release, models.Error): + sys.exit(f"<{release.code}>: {release.message}") + diffs = get_diffs_by_organisation_id_and_application_id_and_release_id.sync( + client=client, id=UUID(self.args.org), application_id=self.args.app, release_id=self.args.rel + ) + if diffs is None: + sys.exit("Get diffs: No response") + elif isinstance(diffs, models.Error): + sys.exit(f"<{diffs.code}>: {diffs.message}") + + version = release.version + version_str = f"{version.major}.{version.minor}.{version.revision}+{version.build_num:08x}" + + print( + tabulate( + [ + ["Application Name", application.name], + ["Application Description", application.description], + ["Board Target", release.board_target], + ["Version", version_str], + ], + tablefmt="simple", + ) + ) + diff_info = [] + for diff in diffs: + from_release = get_release_by_organisation_id_and_application_id_and_release_id.sync( + client=client, id=UUID(self.args.org), application_id=self.args.app, release_id=diff.from_release_id + ) + if not isinstance(from_release, models.ApplicationRelease): + print(f"Failed to query information about source release {diff.from_release_id}") + continue + from_version = from_release.version + from_version_str = ( + f"{from_version.major}.{from_version.minor}.{from_version.revision}+{from_version.build_num:08x}" + ) + diff_info.append([from_version_str, diff.file.coap_path, diff.file.len_, diff.file.crc]) + + if len(diff_info) > 0: + print("~~~ Diffs ~~~") + print(tabulate(diff_info, headers=["From Version", "Path", "Length", "CRC"])) + + def _info_all(self, client: Client): releases = get_releases_by_organisation_id_and_application_id.sync( client=client, id=UUID(self.args.org), application_id=self.args.app ) @@ -398,22 +460,36 @@ def info(self, client: Client): for release in releases: version = release.version version_str = f"{version.major}.{version.minor}.{version.revision}+{version.build_num:08x}" - release_list.append( - [ - f"{release.board_target}", - version_str, - f"{release.id}", + info = [ + f"{release.board_target}", + version_str, + f"{release.id}", + ] + if self.args.coap: + info += [release.file.coap_path, str(release.file.len_), str(release.file.crc)] + else: + info += [ f"{release.file.len_ / 1024:.2f} kB", str(release.created_at), ] - ) + release_list.append(info) + if self.args.coap: + headers = ["Board Target", "Version", "ID", "Path", "Length", "CRC"] + else: + headers = ["Board Target", "Version", "ID", "Full OTA", "Created"] print( tabulate( release_list, - headers=["Board Target", "Version", "ID", "Full OTA", "Created"], + headers=headers, ) ) + def info(self, client: Client): + if self.args.rel: + self._info_one(client) + else: + self._info_all(client) + def upload(self, client: Client): try: self._board = UUID(self.args.board) if self.args.board else None @@ -465,11 +541,11 @@ def upload(self, client: Client): client=client, id=self._org, application_id=app_id ) - if application is None: - dialog = f"Application 0x{app_id:08x} does not exist in organisation {self.args.org}, create?" + if application is None or (isinstance(application, models.Error) and application.code == 404): + dialog = f"Application 0x{app_id:08x} does not exist in organisation {self._org_name}, create?" if not user_confirm(dialog): return - print(f"Creating application 0x{app_id:08x} in organisation {self.args.org}") + print(f"Creating application 0x{app_id:08x} in organisation {self._org_name}") description = user_response("Application description:") body = models.NewApplication(id=app_id, name=name, description=description) application = create_application.sync(client=client, id=self._org, body=body) @@ -574,6 +650,7 @@ class SubCommand(InfuseCommand): @classmethod def add_parser(cls, parser): + parser.add_argument("--api-key", type=str, help="Cloud API key to use instead of stored credentials") subparser = parser.add_subparsers(title="commands", metavar="", required=True) Organisations.add_parser(subparser)