Skip to content

[Fixes #12456] Implement the ResourceHandler concept #12469

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 4 additions & 41 deletions geonode/base/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -43,7 +42,7 @@
from avatar.templatetags.avatar_tags import avatar_url
from geonode.utils import bbox_swap
from geonode.base.api.exceptions import InvalidResourceException

from geonode.assets.handlers import asset_handler_registry
from geonode.favorite.models import Favorite
from geonode.base.models import (
Link,
Expand All @@ -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

Expand Down Expand Up @@ -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):
Expand Down
9 changes: 1 addition & 8 deletions geonode/base/api/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@
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
Expand Down Expand Up @@ -2487,12 +2485,7 @@ def test_base_resources_return_download_links_for_documents(self):
Ensure we can access the Resource Base list.
"""
doc = Document.objects.first()
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)},
]
expected_payload = [{"url": build_absolute_uri(doc.download_url), "ajax_safe": doc.download_is_ajax_safe}]
# From resource base API
json = self._get_for_object(doc, "base-resources-detail")
download_url = json.get("resource").get("download_urls")
Expand Down
4 changes: 3 additions & 1 deletion geonode/base/populate_test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@
f.write(result)


def create_single_dataset(name, keywords=None, owner=None, group=None, **kwargs):
def create_single_dataset(name, keywords=None, owner=None, group=None, with_asset=False, **kwargs):
admin, created = get_user_model().objects.get_or_create(username="admin")
if created:
admin.is_superuser = True
Expand Down Expand Up @@ -415,6 +415,8 @@

if isinstance(keywords, list):
dataset = add_keywords_to_resource(dataset, keywords)
if with_asset:
_, _ = create_asset_and_link(dataset, dataset.owner, dfile)

Check warning on line 419 in geonode/base/populate_test_data.py

View check run for this annotation

Codecov / codecov/patch

geonode/base/populate_test_data.py#L419

Added line #L419 was not covered by tests

dataset.set_default_permissions(owner=owner or admin)
dataset.clear_dirty_state()
Expand Down
24 changes: 24 additions & 0 deletions geonode/documents/handlers.py
Original file line number Diff line number Diff line change
@@ -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, **kwargs):
"""
Specific method that return the download URL of the document
"""
super().download_urls()
return [
{
"url": self.instance.download_url if not self.instance.doc_url else self.instance.doc_url,
"ajax_safe": self.instance.download_is_ajax_safe,
},
]
15 changes: 15 additions & 0 deletions geonode/geoapps/handlers.py
Original file line number Diff line number Diff line change
@@ -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, **kwargs):
logger.debug("Download is not available for maps")
return []
2 changes: 1 addition & 1 deletion geonode/layers/download_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def get_resource(self):
self.request,
self.resource_name,
"base.download_resourcebase",
_("You do not have download permissions for this dataset."),
_("You do not have download permissions for this dataset.")
)
except Exception as e:
logger.debug(e)
Expand Down
49 changes: 49 additions & 0 deletions geonode/layers/handlers.py
Original file line number Diff line number Diff line change
@@ -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)

Check warning on line 22 in geonode/layers/handlers.py

View check run for this annotation

Codecov / codecov/patch

geonode/layers/handlers.py#L21-L22

Added lines #L21 - L22 were not covered by tests
if asset is not None:
asset_url = asset_handler_registry.get_handler(asset).create_download_url(asset)
return [{"url": asset_url, "ajax_safe": True, "default": True}]

Check warning on line 25 in geonode/layers/handlers.py

View check run for this annotation

Codecov / codecov/patch

geonode/layers/handlers.py#L24-L25

Added lines #L24 - L25 were not covered by tests


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)

Check warning on line 46 in geonode/layers/handlers.py

View check run for this annotation

Codecov / codecov/patch

geonode/layers/handlers.py#L46

Added line #L46 was not covered by tests
if obj.download_url:
download_urls.append({"url": obj.download_url, "ajax_safe": obj.is_ajax_safe, "default": False})

Check warning on line 48 in geonode/layers/handlers.py

View check run for this annotation

Codecov / codecov/patch

geonode/layers/handlers.py#L48

Added line #L48 was not covered by tests
return download_urls
15 changes: 15 additions & 0 deletions geonode/maps/handlers.py
Original file line number Diff line number Diff line change
@@ -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 []
5 changes: 5 additions & 0 deletions geonode/resource/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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()
59 changes: 59 additions & 0 deletions geonode/resource/handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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, instance=None) -> None:
self.instance = instance

def __str__(self):
return f"{self.__module__}.{self.__class__.__name__}"

Check warning on line 21 in geonode/resource/handler.py

View check run for this annotation

Codecov / codecov/patch

geonode/resource/handler.py#L21

Added line #L21 was not covered by tests

def __repr__(self):
return self.__str__()

Check warning on line 24 in geonode/resource/handler.py

View check run for this annotation

Codecov / codecov/patch

geonode/resource/handler.py#L24

Added line #L24 was not covered by tests

@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):
return handler(instance)
logger.error("No handlers found for the given resource")
return self

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
return []

def download_response(self, **kwargs):
"""
Return the download response for the resource
"""


resource_hander = BaseResourceHandler()
5 changes: 5 additions & 0 deletions geonode/resource/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Loading
Loading