From da7d4cb1d7d792ae4c2398d64bdd3e1d9036d81c Mon Sep 17 00:00:00 2001 From: Petr Kalis Date: Fri, 23 Sep 2022 12:57:17 +0200 Subject: [PATCH] OP-3682 - updates to match to v4 payload Parsing should match payload from localhost:5000/api/addons?details=1 --- .../distribution/addon_distribution.py | 29 ++++- .../tests/test_addon_distributtion.py | 104 +++++++++++++----- 2 files changed, 98 insertions(+), 35 deletions(-) diff --git a/common/openpype_common/distribution/addon_distribution.py b/common/openpype_common/distribution/addon_distribution.py index ad17a831d8..fec8cb762b 100644 --- a/common/openpype_common/distribution/addon_distribution.py +++ b/common/openpype_common/distribution/addon_distribution.py @@ -44,12 +44,18 @@ class WebAddonSource(AddonSource): url = attr.ib(default=None) +@attr.s +class VersionData(object): + version_data = attr.ib(default=None) + + @attr.s class AddonInfo(object): """Object matching json payload from Server""" name = attr.ib() version = attr.ib() - sources = attr.ib(default=attr.Factory(list)) + title = attr.ib(default=None) + sources = attr.ib(default=attr.Factory(dict)) hash = attr.ib(default=None) description = attr.ib(default=None) license = attr.ib(default=None) @@ -58,7 +64,16 @@ class AddonInfo(object): @classmethod def from_dict(cls, data): sources = [] - for source in data.get("sources", []): + + production_version = data.get("productionVersion") + if not production_version: + return + + # server payload contains info about all versions + # active addon must have 'productionVersion' and matching version info + version_data = data.get("versions", {})[production_version] + + for source in version_data.get("clientSourceInfo", []): if source.get("type") == UrlType.FILESYSTEM.value: source_addon = LocalAddonSource(type=source["type"], path=source["path"]) @@ -69,10 +84,11 @@ class AddonInfo(object): sources.append(source_addon) return cls(name=data.get("name"), - version=data.get("version"), + version=production_version, + sources=sources, hash=data.get("hash"), description=data.get("description"), - sources=sources, + title=data.get("title"), license=data.get("license"), authors=data.get("authors")) @@ -228,8 +244,9 @@ def update_addon_state(addon_infos, destination_folder, factory, for source in addon.sources: download_states[full_name] = UpdateState.FAILED.value try: - downloader = factory.get_downloader(source["type"]) - zip_file_path = downloader.download(source, addon_dest) + downloader = factory.get_downloader(source.type) + zip_file_path = downloader.download(attr.asdict(source), + addon_dest) downloader.check_hash(zip_file_path, addon.hash) downloader.unzip(zip_file_path, addon_dest) download_states[full_name] = UpdateState.UPDATED.value diff --git a/common/openpype_common/distribution/tests/test_addon_distributtion.py b/common/openpype_common/distribution/tests/test_addon_distributtion.py index faf4e01e22..46bcd276cd 100644 --- a/common/openpype_common/distribution/tests/test_addon_distributtion.py +++ b/common/openpype_common/distribution/tests/test_addon_distributtion.py @@ -35,23 +35,50 @@ def temp_folder(): @pytest.fixture def sample_addon_info(): addon_info = { - "name": "openpype_slack", - "version": "1.0.0", - "sources": [ - { - "type": "http", - "url": "https://drive.google.com/file/d/1TcuV8c2OV8CcbPeWi7lxOdqWsEqQNPYy/view?usp=sharing" # noqa - }, - { - "type": "filesystem", - "path": { - "windows": ["P:/sources/some_file.zip", "W:/sources/some_file.zip"], # noqa - "linux": ["/mnt/srv/sources/some_file.zip"], - "darwin": ["/Volumes/srv/sources/some_file.zip"] - } + "versions": { + "1.0.0": { + "clientPyproject": { + "tool": { + "poetry": { + "dependencies": { + "nxtools": "^1.6", + "orjson": "^3.6.7", + "typer": "^0.4.1", + "email-validator": "^1.1.3", + "python": "^3.10", + "fastapi": "^0.73.0" + } + } + } + }, + "hasSettings": True, + "clientSourceInfo": [ + { + "type": "http", + "url": "https://drive.google.com/file/d/1TcuV8c2OV8CcbPeWi7lxOdqWsEqQNPYy/view?usp=sharing" # noqa + }, + { + "type": "filesystem", + "path": { + "windows": ["P:/sources/some_file.zip", + "W:/sources/some_file.zip"], # noqa + "linux": ["/mnt/srv/sources/some_file.zip"], + "darwin": ["/Volumes/srv/sources/some_file.zip"] + } + } + ], + "frontendScopes": { + "project": { + "sidebar": "hierarchy" + } + } } - ], - "hash": "4be25eb6215e91e5894d3c5475aeb1e379d081d3f5b43b4ee15b0891cf5f5658" # noqa + }, + "description": "", + "title": "Slack addon", + "name": "openpype_slack", + "productionVersion": "1.0.0", + "hash": "4be25eb6215e91e5894d3c5475aeb1e379d081d3f5b43b4ee15b0891cf5f5658" # noqa } yield addon_info @@ -73,16 +100,39 @@ def test_get_downloader(printer, addon_downloader): def test_addon_info(printer, sample_addon_info): - valid_minimum = {"name": "openpype_slack", "version": "1.0.0"} + """Tests parsing of expected payload from v4 server into AadonInfo.""" + valid_minimum = { + "name": "openpype_slack", + "productionVersion": "1.0.0", + "versions": { + "1.0.0": { + "clientSourceInfo": [ + { + "type": "filesystem", + "path": { + "windows": [ + "P:/sources/some_file.zip", + "W:/sources/some_file.zip"], + "linux": [ + "/mnt/srv/sources/some_file.zip"], + "darwin": [ + "/Volumes/srv/sources/some_file.zip"] # noqa + } + } + ] + } + } + } assert AddonInfo.from_dict(valid_minimum), "Missing required fields" - assert AddonInfo(name=valid_minimum["name"], - version=valid_minimum["version"]), \ - "Missing required fields" - with pytest.raises(TypeError): - # TODO should be probably implemented - assert AddonInfo(valid_minimum), "Wrong argument format" + valid_minimum["versions"].pop("1.0.0") + with pytest.raises(KeyError): + assert not AddonInfo.from_dict(valid_minimum), "Must fail without version data" # noqa + + valid_minimum.pop("productionVersion") + assert not AddonInfo.from_dict( + valid_minimum), "none if not productionVersion" # noqa addon = AddonInfo.from_dict(sample_addon_info) assert addon, "Should be created" @@ -95,15 +145,11 @@ def test_addon_info(printer, sample_addon_info): addon_as_dict = attr.asdict(addon) assert addon_as_dict["name"], "Dict approach should work" - with pytest.raises(TypeError): - # TODO should be probably implemented as . not dict - first_source = addon.sources[0] - assert first_source["type"] == "http", "Not implemented" - def test_update_addon_state(printer, sample_addon_info, temp_folder, addon_downloader): - addon_info = AddonInfo(**sample_addon_info) + """Tests possible cases of addon update.""" + addon_info = AddonInfo.from_dict(sample_addon_info) orig_hash = addon_info.hash addon_info.hash = "brokenhash"