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

Draft
wants to merge 14 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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_handler(_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 @@ -2531,12 +2529,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
23 changes: 23 additions & 0 deletions geonode/documents/handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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
"""
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,
},
] or super().download_urls()
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 []
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 []
3 changes: 3 additions & 0 deletions geonode/resource/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#########################################################################
from django.apps import AppConfig
from django.urls import include, re_path
from geonode.resource.handler import resource_registry


class GeoNodeResourceConfig(AppConfig):
Expand All @@ -28,3 +29,5 @@ def ready(self):
from geonode.urls import urlpatterns

urlpatterns += [re_path(r"^api/v2/", include("geonode.resource.api.urls"))]

resource_registry.init_registry()
69 changes: 69 additions & 0 deletions geonode/resource/handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from abc import ABC
import logging

from django.conf import settings
from django.utils.module_loading import import_string

logger = logging.getLogger(__file__)


class ResourceHandlerRegistry:

REGISTRY = []

def init_registry(self):
self.register()

def register(self):
for module_path in settings.RESOURCE_HANDLERS:
self.REGISTRY.append(import_string(module_path))

@classmethod
def get_registry(cls):
return ResourceHandlerRegistry.REGISTRY

def get_handler(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 BaseResourceHandler()


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
"""

def __init__(self, instance=None) -> None:
self.instance = instance

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

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

View check run for this annotation

Codecov / codecov/patch

geonode/resource/handler.py#L48

Added line #L48 was not covered by tests

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

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

View check run for this annotation

Codecov / codecov/patch

geonode/resource/handler.py#L51

Added line #L51 was not covered by tests

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
"""
raise NotImplementedError()

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

View check run for this annotation

Codecov / codecov/patch

geonode/resource/handler.py#L66

Added line #L66 was not covered by tests


resource_registry = ResourceHandlerRegistry()
5 changes: 5 additions & 0 deletions geonode/resource/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -978,5 +978,10 @@ def set_thumbnail(
logger.exception(e)
return False

def get_handler(self, instance):
from geonode.resource.handler import resource_registry

return resource_registry.get_handler(instance=instance)


resource_manager = ResourceManager()
Loading
Loading