Skip to content
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
20 changes: 17 additions & 3 deletions geonode/metadata/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from geonode.base.utils import remove_country_from_languagecode
from geonode.base.views import LinkedResourcesAutocomplete, RegionAutocomplete, HierarchicalKeywordAutocomplete
from geonode.groups.models import GroupProfile
from geonode.metadata.handlers.abstract import MetadataHandler
from geonode.metadata.i18n import get_localized_label
from geonode.metadata.manager import metadata_manager
from geonode.people.utils import get_available_users
Expand Down Expand Up @@ -77,7 +78,7 @@ def schema(self, request, pk=None):
# Handle the JSON schema instance
@action(
detail=False,
methods=["get", "put"],
methods=["get", "put", "patch"],
url_path=r"instance/(?P<pk>\d+)",
url_name="schema_instance",
permission_classes=[
Expand All @@ -86,6 +87,7 @@ def schema(self, request, pk=None):
"default": {
"GET": ["base.view_resourcebase"],
"PUT": ["change_resourcebase_metadata"],
"PATCH": ["change_resourcebase_metadata"],
}
}
)
Expand All @@ -102,13 +104,25 @@ def schema_instance(self, request, pk=None):
schema_instance, content_type="application/schema-instance+json", json_dumps_params={"indent": 3}
)

elif request.method == "PUT":
elif request.method in ("PUT", "PATCH"):
logger.debug(f"handling request {request.method}")
# try:
# logger.debug(f"handling content {json.dumps(request.data, indent=3)}")
# except Exception as e:
# logger.warning(f"Can't parse JSON {request.data}: {e}")
errors = metadata_manager.update_schema_instance(resource, request, lang)
errors = {}
try:
errors = (
metadata_manager.update_schema_instance(resource, request, lang)
if request.method == "PUT"
else metadata_manager.update_schema_instance_partial(resource, request.data, request.user, lang)
)
resource.save() # we want to trigger all the post_save signals
except Exception as e:
logger.warning(f"Error while updating schema instance: {e}")
MetadataHandler._set_error(
errors, [], MetadataHandler.localize_message({}, "metadata_error_save", {"exc": e})
)

msg_t = (
("m_metadata_update_error", "Some errors were found while updating the resource")
Expand Down
2 changes: 1 addition & 1 deletion geonode/metadata/handlers/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def _localize_label(context, lang: str, text: str):

@staticmethod
def _get_tkl_labels(context, lang: str | None, text: str):
return context["labels"].get(text, None)
return context.get("labels", {}).get(text, None)

@staticmethod
def _localize_subschema_labels(context, subschema: dict, lang: str, property_name: str = None):
Expand Down
1 change: 1 addition & 0 deletions geonode/metadata/handlers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ def update_resource(self, resource, field_name, json_instance, context, errors,
field_value = SUBHANDLERS[field_name].deserialize(field_value)

setattr(resource, field_name, field_value)
context.setdefault("base", {})[field_name] = field_value
except Exception as e:
logger.warning(f"Error setting field {field_name}={field_value}: {e}")
self._set_error(
Expand Down
1 change: 1 addition & 0 deletions geonode/metadata/handlers/doi.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,4 @@ def get_jsonschema_instance(self, resource, field_name, context, errors, lang=No

def update_resource(self, resource, field_name, json_instance, context, errors, **kwargs):
resource.doi = json_instance.get(field_name, None)
context.setdefault("base", {})[field_name] = json_instance.get(field_name, None)
23 changes: 20 additions & 3 deletions geonode/metadata/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ def build_schema_instance(self, resource, lang=None):

return instance

def update_schema_instance(self, resource, request_obj, lang=None) -> dict:

def update_schema_instance(self, resource, request_obj, lang=None, partial=None) -> dict:
# Definition of the json instance
json_instance = request_obj.data

Expand All @@ -134,6 +133,11 @@ def update_schema_instance(self, resource, request_obj, lang=None) -> dict:
errors = {}

for fieldname, subschema in schema["properties"].items():
if partial:
if fieldname not in partial:
continue
else:
logger.debug(f"Storing partial field {fieldname}")
handler = self.handlers[subschema["geonode:handler"]]
try:
handler.update_resource(resource, fieldname, json_instance, context, errors)
Expand All @@ -160,7 +164,10 @@ def update_schema_instance(self, resource, request_obj, lang=None) -> dict:
),
)
try:
resource.save()
resource.get_real_concrete_instance_class().objects.filter(id=resource.id).update(
**context.setdefault("base")
)
resource.refresh_from_db()
except Exception as e:
logger.warning(f"Error while updating schema instance: {e}")
MetadataHandler._set_error(
Expand All @@ -185,6 +192,16 @@ def update_schema_instance(self, resource, request_obj, lang=None) -> dict:

return errors

def update_schema_instance_partial(self, resource, json_instance, user, lang=None) -> dict:
# We can't loop on the payload's field, since post_ or pre_ methods may rely on the whole instance
# Let's create a full instance by using the old one, merged with the payload
old_instance = self.build_schema_instance(resource, lang)
old_instance.update(json_instance)
fake_req = lambda: None
fake_req.data = old_instance
fake_req.user = user
return self.update_schema_instance(resource, fake_req, lang, partial=json_instance.keys())


def _create_test_errors(schema, errors, path, msg_template, create_message=True):
if create_message:
Expand Down
11 changes: 8 additions & 3 deletions geonode/resource/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,14 @@
from django.utils.module_loading import import_string
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError, FieldDoesNotExist
from tomli import _re


from geonode.assets.utils import create_asset_and_link_dict, rollback_asset_and_link, copy_assets_and_links, create_link
from geonode.base.models import ResourceBase, LinkedResource
from geonode.documents.tasks import create_document_thumbnail
from geonode.metadata.manager import metadata_manager
from geonode.thumbs.thumbnails import _generate_thumbnail_name
from geonode.thumbs.utils import ThumbnailAlgorithms
from geonode.documents.tasks import create_document_thumbnail
from geonode.security.permissions import PermSpecCompact, DATA_STYLABLE_RESOURCES_SUBTYPES
from geonode.security.utils import (
perms_as_set,
Expand All @@ -54,7 +56,6 @@

from . import settings as rm_settings
from .utils import update_resource, resourcebase_post_save
from geonode.assets.utils import create_asset_and_link_dict, rollback_asset_and_link, copy_assets_and_links, create_link

from ..base import enumerations
from ..security.utils import AdvancedSecurityWorkflowManager
Expand Down Expand Up @@ -360,6 +361,10 @@ def update(
vals=vals,
extra_metadata=extra_metadata,
)

if ji:=custom.get("jsoninstance", None):
metadata_manager.update_schema_instance_partial(_resource, ji, user=None)

_resource = self._concrete_resource_manager.update(uuid, instance=_resource, notify=notify)

# The following is only a demo proof of concept for a pluggable WF subsystem
Expand Down