From 9bbbf4f85616194a7acea5b10847bbf17b0927b9 Mon Sep 17 00:00:00 2001 From: mattiagiupponi <51856725+mattiagiupponi@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:32:37 +0200 Subject: [PATCH] [Fixes #12341] Asset download handler and link generator (#12343) * [Fixes #12341] Download handler fix * [Fixes #12341] Assets: link generation (#12342) --------- Co-authored-by: Emanuele Tajariol --- .gitignore | 1 + geonode/assets/handlers.py | 4 ++-- geonode/assets/local.py | 24 +++++++++++++++++-- geonode/assets/tests/data/one.json | 2 +- geonode/assets/tests/data/three.json | 2 +- geonode/assets/tests/data/two.json | 2 +- geonode/base/api/serializers.py | 24 +++++++++++++++---- geonode/base/api/tests.py | 9 ++++++- ...2_migrate and_remove_resourcebase_files.py | 2 +- geonode/layers/utils.py | 2 +- geonode/settings.py | 2 +- 11 files changed, 58 insertions(+), 16 deletions(-) diff --git a/.gitignore b/.gitignore index 7a455624878..2080d18191d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ geonode/local_settings.py # Uploaded files geonode/uploaded +geonode/assets_data #Testing output .coverage diff --git a/geonode/assets/handlers.py b/geonode/assets/handlers.py index 6a6a175daf9..3df019c521c 100644 --- a/geonode/assets/handlers.py +++ b/geonode/assets/handlers.py @@ -38,10 +38,10 @@ def clone(self, asset: Asset) -> Asset: def create_link_url(self, asset: Asset) -> str: raise NotImplementedError() - def get_download_handler(self, asset: Asset) -> AssetDownloadHandlerInterface: + def get_download_handler(self, asset: Asset = None) -> AssetDownloadHandlerInterface: raise NotImplementedError() - def get_storage_manager(self, asset): + def get_storage_manager(self, asset=None): raise NotImplementedError() diff --git a/geonode/assets/local.py b/geonode/assets/local.py index 9487debf844..45f24bda82e 100644 --- a/geonode/assets/local.py +++ b/geonode/assets/local.py @@ -20,17 +20,33 @@ ) +class DefaultLocalLinkUrlHandler: + def get_link_url(self, asset: LocalAsset): + return build_absolute_uri(reverse("assets-link", args=(asset.pk,))) + + +class IndexLocalLinkUrlHandler: + def get_link_url(self, asset: LocalAsset): + return build_absolute_uri(reverse("assets-link", args=(asset.pk,))) + f"/{os.path.basename(asset.location[0])}" + + class LocalAssetHandler(AssetHandlerInterface): + + link_url_handlers = {"gpkg": IndexLocalLinkUrlHandler()} + @staticmethod def handled_asset_class(): return LocalAsset - def get_download_handler(self, asset): + def get_download_handler(self, asset=None): return LocalAssetDownloadHandler() def get_storage_manager(self, asset): return _asset_storage_manager + def get_link_url_handler(self, asset): + return self.link_url_handlers.get(asset.type, None) or DefaultLocalLinkUrlHandler() + def _create_asset_dir(self): return os.path.normpath( mkdtemp(dir=settings.ASSETS_ROOT, prefix=datetime.datetime.now().strftime("%Y%m%d%H%M%S")) @@ -126,7 +142,7 @@ def create_download_url(self, asset) -> str: return build_absolute_uri(reverse("assets-download", args=(asset.pk,))) def create_link_url(self, asset) -> str: - return build_absolute_uri(reverse("assets-link", args=(asset.pk,))) + f"/{os.path.basename(asset.location[0])}" + return self.get_link_url_handler(asset).get_link_url(asset) @classmethod def _is_file_managed(cls, file) -> bool: @@ -175,6 +191,10 @@ def _get_managed_dir(cls, asset): base_common = base managed_dir = os.path.join(assets_root, base_common) + + if not os.path.exists(managed_dir): + raise ValueError(f"Common dir '{managed_dir}' does not exist - Asset {asset.pk}") + if not os.path.isdir(managed_dir): raise ValueError(f"Common dir '{managed_dir}' does not seem to be a directory - Asset {asset.pk}") diff --git a/geonode/assets/tests/data/one.json b/geonode/assets/tests/data/one.json index 9be950ff72e..27a1ec027ca 100644 --- a/geonode/assets/tests/data/one.json +++ b/geonode/assets/tests/data/one.json @@ -1,3 +1,3 @@ { - "one": 1 + "one": 1 } \ No newline at end of file diff --git a/geonode/assets/tests/data/three.json b/geonode/assets/tests/data/three.json index 7f0f3fb0cf3..8ced1fb5978 100644 --- a/geonode/assets/tests/data/three.json +++ b/geonode/assets/tests/data/three.json @@ -1,3 +1,3 @@ { - "three": 3 + "three": 3 } \ No newline at end of file diff --git a/geonode/assets/tests/data/two.json b/geonode/assets/tests/data/two.json index 48b9101b35a..c8b2fcf3d89 100644 --- a/geonode/assets/tests/data/two.json +++ b/geonode/assets/tests/data/two.json @@ -1,3 +1,3 @@ { - "two": 2 + "two": 2 } \ No newline at end of file diff --git a/geonode/base/api/serializers.py b/geonode/base/api/serializers.py index 6dd5b92f9b2..f042fe45032 100644 --- a/geonode/base/api/serializers.py +++ b/geonode/base/api/serializers.py @@ -28,6 +28,7 @@ from django.forms.models import model_to_dict from django.contrib.auth import get_user_model from django.db.models.query import QuerySet +from geonode.assets.utils import get_default_asset from geonode.people import Roles from django.http import QueryDict from deprecated import deprecated @@ -62,7 +63,8 @@ from geonode.geoapps.models import GeoApp from geonode.groups.models import GroupCategory, GroupProfile from geonode.base.api.fields import ComplexDynamicRelationField -from geonode.layers.utils import get_dataset_download_handlers, get_default_dataset_download_handler +from geonode.layers.utils import get_download_handlers, get_default_dataset_download_handler +from geonode.assets.handlers import asset_handler_registry from geonode.utils import build_absolute_uri from geonode.security.utils import get_resources_with_perms, get_geoapp_subtypes from geonode.resource.models import ExecutionRequest @@ -298,15 +300,24 @@ def get_attribute(self, instance): except Exception as e: logger.exception(e) raise e + + asset = get_default_asset(_instance) + if asset is not None: + asset_url = asset_handler_registry.get_handler(asset).create_download_url(asset) + if _instance.resource_type in ["map"] + get_geoapp_subtypes(): return [] elif _instance.resource_type in ["document"]: - return [ + payload = [ { "url": _instance.download_url, "ajax_safe": _instance.download_is_ajax_safe, - } + }, ] + if asset: + payload.append({"url": asset_url, "ajax_safe": False, "default": False}) + return payload + elif _instance.resource_type in ["dataset"]: download_urls = [] # lets get only the default one first to set it @@ -315,11 +326,14 @@ def get_attribute(self, instance): if obj.download_url: download_urls.append({"url": obj.download_url, "ajax_safe": obj.is_ajax_safe, "default": True}) # then let's prepare the payload with everything - handler_list = get_dataset_download_handlers() - for handler in handler_list: + for handler in get_download_handlers(): obj = handler(self.context.get("request"), _instance.alternate) if obj.download_url: download_urls.append({"url": obj.download_url, "ajax_safe": obj.is_ajax_safe, "default": False}) + + if asset: + download_urls.append({"url": asset_url, "ajax_safe": True, "default": False if download_urls else True}) + return download_urls else: return [] diff --git a/geonode/base/api/tests.py b/geonode/base/api/tests.py index 74e758704b9..0955916cb09 100644 --- a/geonode/base/api/tests.py +++ b/geonode/base/api/tests.py @@ -49,6 +49,8 @@ from geonode.assets.utils import create_asset_and_link from geonode.maps.models import Map, MapLayer from geonode.tests.base import GeoNodeBaseTestSupport +from geonode.assets.utils import get_default_asset +from geonode.assets.handlers import asset_handler_registry from geonode.base import enumerations from geonode.base.api.serializers import ResourceBaseSerializer @@ -2477,7 +2479,12 @@ def test_base_resources_return_download_links_for_documents(self): Ensure we can access the Resource Base list. """ doc = Document.objects.first() - expected_payload = [{"url": build_absolute_uri(doc.download_url), "ajax_safe": doc.download_is_ajax_safe}] + asset = get_default_asset(doc) + handler = asset_handler_registry.get_handler(asset) + expected_payload = [ + {"url": build_absolute_uri(doc.download_url), "ajax_safe": doc.download_is_ajax_safe}, + {"ajax_safe": False, "default": False, "url": handler.create_download_url(asset)}, + ] # From resource base API json = self._get_for_object(doc, "base-resources-detail") download_url = json.get("resource").get("download_urls") diff --git a/geonode/base/migrations/0092_migrate and_remove_resourcebase_files.py b/geonode/base/migrations/0092_migrate and_remove_resourcebase_files.py index f8077479217..76b564cd918 100644 --- a/geonode/base/migrations/0092_migrate and_remove_resourcebase_files.py +++ b/geonode/base/migrations/0092_migrate and_remove_resourcebase_files.py @@ -50,7 +50,7 @@ def get_ext(filename): # no existing "uploaded" links exist, so create them right away # otherwise we create the link with the assigned asset if dataset_hm := Dataset_hm.objects.filter(pk=res_hm.id).first(): - url = build_absolute_uri(reverse("assets-download", args=(asset.pk,))) + url = build_absolute_uri(reverse("assets-link", args=(asset.pk,))) elif doc_hm := Document_hm.objects.filter(pk=res_hm.id).first(): url = build_absolute_uri(reverse("assets-link", args=(asset.pk,))) else: diff --git a/geonode/layers/utils.py b/geonode/layers/utils.py index 53b4bf7af76..b7102116c9e 100644 --- a/geonode/layers/utils.py +++ b/geonode/layers/utils.py @@ -507,7 +507,7 @@ def get_uuid_handler(): dataset_download_handler_list = [] -def get_dataset_download_handlers(): +def get_download_handlers(): if not dataset_download_handler_list and getattr(settings, "DATASET_DOWNLOAD_HANDLERS", None): dataset_download_handler_list.append(import_string(settings.DATASET_DOWNLOAD_HANDLERS[0])) diff --git a/geonode/settings.py b/geonode/settings.py index ad2d2e33a76..dc554b8e4bf 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -303,7 +303,7 @@ # Absolute path to the directory that hold assets files # This dir should not be made publicly accessible by nginx, since its content may be private # Using a sibling of MEDIA_ROOT as default -ASSETS_ROOT = os.getenv("ASSETS_ROOT", os.path.join(os.path.dirname(MEDIA_ROOT.rstrip("/")), "assets")) +ASSETS_ROOT = os.getenv("ASSETS_ROOT", os.path.join(os.path.dirname(MEDIA_ROOT.rstrip("/")), "assets_data")) # Cache Bustin Settings: enable WhiteNoise compression and caching support # ref: http://whitenoise.evans.io/en/stable/django.html#add-compression-and-caching-support