From b57f222eb6fbdd609219876c915cc86d25019b2d Mon Sep 17 00:00:00 2001 From: Mattia Date: Wed, 31 Jul 2024 11:55:54 +0200 Subject: [PATCH] [Fixes #12456] Implement the ResourceHandler concept --- geonode/base/api/serializers.py | 43 ++-------------------- geonode/documents/handlers.py | 24 +++++++++++++ geonode/geoapps/handlers.py | 15 ++++++++ geonode/layers/handlers.py | 49 +++++++++++++++++++++++++ geonode/maps/handlers.py | 15 ++++++++ geonode/resource/apps.py | 5 +++ geonode/resource/handler.py | 63 +++++++++++++++++++++++++++++++++ geonode/resource/manager.py | 5 +++ geonode/settings.py | 9 +++++ 9 files changed, 188 insertions(+), 40 deletions(-) create mode 100644 geonode/documents/handlers.py create mode 100644 geonode/geoapps/handlers.py create mode 100644 geonode/layers/handlers.py create mode 100644 geonode/maps/handlers.py create mode 100644 geonode/resource/handler.py diff --git a/geonode/base/api/serializers.py b/geonode/base/api/serializers.py index 9a689e4839e..0a784eef406 100644 --- a/geonode/base/api/serializers.py +++ b/geonode/base/api/serializers.py @@ -28,7 +28,6 @@ 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 @@ -63,10 +62,9 @@ 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_download_handlers, get_default_dataset_download_handler -from geonode.assets.handlers import asset_handler_registry +from geonode.resource.manager import resource_manager from geonode.utils import build_absolute_uri -from geonode.security.utils import get_resources_with_perms, get_geoapp_subtypes +from geonode.security.utils import get_resources_with_perms from geonode.resource.models import ExecutionRequest from django.contrib.gis.geos import Polygon @@ -301,42 +299,7 @@ def get_attribute(self, instance): 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"]: - 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 - default_handler = get_default_dataset_download_handler() - obj = default_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": True}) - # then let's prepare the payload with everything - 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 [] + return resource_manager.get_manager(_instance).download_urls(**self.context) class FavoriteField(DynamicComputedField): diff --git a/geonode/documents/handlers.py b/geonode/documents/handlers.py new file mode 100644 index 00000000000..eb98c7f8f68 --- /dev/null +++ b/geonode/documents/handlers.py @@ -0,0 +1,24 @@ +from geonode.documents.models import Document +from geonode.resource.handler import BaseResourceHandler +import logging + +logger = logging.getLogger() + + +class DocumentHandler(BaseResourceHandler): + + @staticmethod + def can_handle(instance): + return isinstance(instance, Document) + + def download_urls(self): + """ + Specific method that return the download URL of the document + """ + super().download_urls() + return [ + { + "url": self.instance.download_url, + "ajax_safe": self.instance.download_is_ajax_safe, + }, + ] diff --git a/geonode/geoapps/handlers.py b/geonode/geoapps/handlers.py new file mode 100644 index 00000000000..40b433a1354 --- /dev/null +++ b/geonode/geoapps/handlers.py @@ -0,0 +1,15 @@ +from geonode.geoapps.models import GeoApp +from geonode.resource.handler import BaseResourceHandler +import logging + +logger = logging.getLogger() + + +class GeoAppHandler(BaseResourceHandler): + @staticmethod + def can_handle(instance): + return isinstance(instance, GeoApp) + + def download_urls(self): + logger.debug("Download is not available for maps") + return [] diff --git a/geonode/layers/handlers.py b/geonode/layers/handlers.py new file mode 100644 index 00000000000..adafe06506c --- /dev/null +++ b/geonode/layers/handlers.py @@ -0,0 +1,49 @@ +from geonode.assets.utils import get_default_asset +from geonode.base.models import ResourceBase +from geonode.layers.models import Dataset +from geonode.resource.handler import BaseResourceHandler +import logging +from geonode.assets.handlers import asset_handler_registry +from geonode.layers.utils import get_download_handlers, get_default_dataset_download_handler + +logger = logging.getLogger() + + +class Tiles3DHandler(BaseResourceHandler): + @staticmethod + def can_handle(instance): + return isinstance(instance, ResourceBase) and instance.subtype == "3dtiles" + + def download_urls(self, **kwargs): + """ + Specific method that return the download URL of the document + """ + super().download_urls() + asset = get_default_asset(self.instance) + if asset is not None: + asset_url = asset_handler_registry.get_handler(asset).create_download_url(asset) + return asset_url + + +class DatasetHandler(BaseResourceHandler): + @staticmethod + def can_handle(instance): + return isinstance(instance, Dataset) + + def download_urls(self, **kwargs): + super().download_urls() + """ + Specific method that return the download URL of the document + """ + download_urls = [] + # lets get only the default one first to set it + default_handler = get_default_dataset_download_handler() + obj = default_handler(kwargs.get("request"), self.instance.alternate) + 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 + for handler in get_download_handlers(): + obj = handler(kwargs.get("request"), self.instance.alternate) + if obj.download_url: + download_urls.append({"url": obj.download_url, "ajax_safe": obj.is_ajax_safe, "default": False}) + return download_urls diff --git a/geonode/maps/handlers.py b/geonode/maps/handlers.py new file mode 100644 index 00000000000..1b0334ec768 --- /dev/null +++ b/geonode/maps/handlers.py @@ -0,0 +1,15 @@ +from geonode.maps.models import Map +from geonode.resource.handler import BaseResourceHandler +import logging + +logger = logging.getLogger() + + +class MapHandler(BaseResourceHandler): + @staticmethod + def can_handle(instance): + return isinstance(instance, Map) + + def download_urls(self, **kwargs): + logger.debug("Download is not available for maps") + return [] diff --git a/geonode/resource/apps.py b/geonode/resource/apps.py index 791f51317a7..94eeb231229 100644 --- a/geonode/resource/apps.py +++ b/geonode/resource/apps.py @@ -18,6 +18,8 @@ ######################################################################### from django.apps import AppConfig from django.urls import include, re_path +from django.conf import settings +from django.utils.module_loading import import_string class GeoNodeResourceConfig(AppConfig): @@ -28,3 +30,6 @@ def ready(self): from geonode.urls import urlpatterns urlpatterns += [re_path(r"^api/v2/", include("geonode.resource.api.urls"))] + + for el in settings.RESOURCE_HANDLERS: + import_string(el).register() diff --git a/geonode/resource/handler.py b/geonode/resource/handler.py new file mode 100644 index 00000000000..814e1e243f9 --- /dev/null +++ b/geonode/resource/handler.py @@ -0,0 +1,63 @@ +from abc import ABC +import logging + +logger = logging.getLogger(__file__) + + +class BaseResourceHandler(ABC): + """ + Base abstract resource handler object + define the required method needed to define a resource handler + As first implementation it will take care of the download url + and the download response for a resource + """ + + REGISTRY = [] + + def __init__(self) -> None: + self.resource = None + + def __str__(self): + return f"{self.__module__}.{self.__class__.__name__}" + + def __repr__(self): + return self.__str__() + + @classmethod + def init_class(cls, instance): + cls.instance = instance + + @classmethod + def register(cls): + BaseResourceHandler.REGISTRY.append(cls) + + @classmethod + def get_registry(cls): + return BaseResourceHandler.REGISTRY + + def get_handler_by_instance(self, instance): + """ + Given a resource, should return it's handler + """ + for handler in self.get_registry(): + if handler.can_handle(instance): + self.init_class(instance) + return handler() + logger.error("No handlers found for the given resource") + return None + + def download_urls(self, **kwargs): + """ + return the download url for each resource + """ + if not self.instance: + logger.warning("No instance declared, so is not possible to return the download url") + return None + + def download_response(self, **kwargs): + """ + Return the download response for the resource + """ + + +resource_hander = BaseResourceHandler() diff --git a/geonode/resource/manager.py b/geonode/resource/manager.py index eefab451b4a..bae12a532db 100644 --- a/geonode/resource/manager.py +++ b/geonode/resource/manager.py @@ -976,5 +976,10 @@ def set_thumbnail( logger.exception(e) return False + def get_manager(self, instance): + from geonode.resource.handler import resource_hander + + return resource_hander.get_handler_by_instance(instance=instance) + resource_manager = ResourceManager() diff --git a/geonode/settings.py b/geonode/settings.py index f1ea768baae..ef902195391 100644 --- a/geonode/settings.py +++ b/geonode/settings.py @@ -2379,3 +2379,12 @@ def get_geonode_catalogue_service(): ] INSTALLED_APPS += ("geonode.assets",) GEONODE_APPS += ("geonode.assets",) + + +RESOURCE_HANDLERS = [ + "geonode.layers.handlers.Tiles3DHandler", + "geonode.layers.handlers.DatasetHandler", + "geonode.maps.handlers.MapHandler", + "geonode.documents.handlers.DocumentHandler", + "geonode.geoapps.handlers.GeoAppHandler", +]