From e7dbea49cdb5adc3e6ff0112d15d27c0b0480173 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Fri, 28 Jan 2022 08:52:11 +0100 Subject: [PATCH] #31 API DELETE * adds support for DELETE method for all relevant objects * improves get_obj_from_db functionality * drops custom compensation logic for get_obj_from_db due to improvement of base method --- api/utils/serializer/serializer.py | 12 +++++++++--- api/utils/serializer/v1/compensation.py | 17 ----------------- api/utils/serializer/v1/serializer.py | 22 ++++++++++++++++++++++ api/views/v1/views.py | 23 ++++++++++++++++++++++- konova/models/object.py | 11 ++++++----- 5 files changed, 59 insertions(+), 26 deletions(-) diff --git a/api/utils/serializer/serializer.py b/api/utils/serializer/serializer.py index d0a01b9..ac4bf22 100644 --- a/api/utils/serializer/serializer.py +++ b/api/utils/serializer/serializer.py @@ -11,6 +11,8 @@ from abc import abstractmethod from django.contrib.gis import geos from django.contrib.gis.geos import GEOSGeometry +from konova.utils.message_templates import DATA_UNSHARED + class AbstractModelAPISerializer: model = None @@ -66,7 +68,7 @@ class AbstractModelAPISerializer: # Return all objects del self.lookup["id"] else: - # Return certain objects + # Return certain object self.lookup["id"] = _id self.lookup["users__in"] = [user] @@ -139,10 +141,14 @@ class AbstractModelAPISerializer: Returns: """ - return self.model.objects.get( + obj = self.model.objects.get( id=id, - users__in=[user] + deleted__isnull=True, ) + is_shared = obj.is_shared_with(user) + if not is_shared: + raise PermissionError(DATA_UNSHARED) + return obj @abstractmethod def initialize_objects(self, json_model, user): diff --git a/api/utils/serializer/v1/compensation.py b/api/utils/serializer/v1/compensation.py index 2010ed0..a40df14 100644 --- a/api/utils/serializer/v1/compensation.py +++ b/api/utils/serializer/v1/compensation.py @@ -129,23 +129,6 @@ class CompensationAPISerializerV1(AbstractModelAPISerializerV1, AbstractCompensa return obj.id - def get_obj_from_db(self, id, user): - """ Returns the object from database - - Fails if id not found or user does not have shared access - - Args: - id (str): The object's id - user (User): The API user - - Returns: - - """ - return self.model.objects.get( - id=id, - intervention__users__in=[user] - ) - def update_model_from_json(self, id, json_model, user): """ Updates an entry for the model based on the contents of json_model diff --git a/api/utils/serializer/v1/serializer.py b/api/utils/serializer/v1/serializer.py index 3d23eff..7d6910d 100644 --- a/api/utils/serializer/v1/serializer.py +++ b/api/utils/serializer/v1/serializer.py @@ -17,6 +17,7 @@ from codelist.settings import CODELIST_COMPENSATION_ACTION_ID, CODELIST_BIOTOPES from compensation.models import CompensationAction, UnitChoices, CompensationState from intervention.models import Responsibility, Legal from konova.models import Deadline, DeadlineType +from konova.utils.message_templates import DATA_UNSHARED class AbstractModelAPISerializerV1(AbstractModelAPISerializer): @@ -101,6 +102,27 @@ class AbstractModelAPISerializerV1(AbstractModelAPISerializer): modified_on = modified_on.timestamp if modified_on is not None else None return modified_on + def delete_entry(self, id, user): + """ Marks an entry as deleted + + Args: + id (str): The entry's id + user (User): The API user + + Returns: + + """ + entry = self.get_obj_from_db(id, user) + is_shared = entry.is_shared_with(user) + if not is_shared: + raise PermissionError(DATA_UNSHARED) + # Do not send mails if entry is deleting using API. THere could be hundreds of deletion resulting in hundreds of + # mails at once. + entry.mark_as_deleted(user, send_mail=False) + entry.refresh_from_db() + success = entry.deleted is not None + return success + class DeductableAPISerializerV1Mixin: class Meta: diff --git a/api/views/v1/views.py b/api/views/v1/views.py index c7ebaff..42447bc 100644 --- a/api/views/v1/views.py +++ b/api/views/v1/views.py @@ -79,7 +79,7 @@ class AbstractAPIViewV1(AbstractAPIView): id (str): The entries id Returns: - + response (JsonResponse) """ try: body = request.body.decode("utf-8") @@ -89,6 +89,27 @@ class AbstractAPIViewV1(AbstractAPIView): return self.return_error_response(e, 500) return JsonResponse({"id": updated_id}) + def delete(self, request: HttpRequest, id=None): + """ Handles a DELETE request + + Args: + request (HttpRequest): The incoming request + id (str): The object's id + + Returns: + response (JsonResponse) + """ + + try: + success = self.serializer.delete_entry(id, self.user) + except Exception as e: + return self.return_error_response(e, 500) + return JsonResponse( + { + "success": success, + } + ) + class InterventionAPIViewV1(AbstractAPIViewV1): serializer = InterventionAPISerializerV1 diff --git a/konova/models/object.py b/konova/models/object.py index 4b3959c..78672b4 100644 --- a/konova/models/object.py +++ b/konova/models/object.py @@ -104,7 +104,7 @@ class BaseObject(BaseResource): def set_status_messages(self, request: HttpRequest): raise NotImplementedError - def mark_as_deleted(self, user: User): + def mark_as_deleted(self, user: User, send_mail: bool = True): """ Mark an entry as deleted Does not delete from database but sets a timestamp for being deleted on and which user deleted the object @@ -124,10 +124,11 @@ class BaseObject(BaseResource): self.deleted = action self.log.add(action) - # Send mail - shared_users = self.shared_users.values_list("id", flat=True) - for user_id in shared_users: - celery_send_mail_shared_data_deleted.delay(self.identifier, user_id) + if send_mail: + # Send mail + shared_users = self.shared_users.values_list("id", flat=True) + for user_id in shared_users: + celery_send_mail_shared_data_deleted.delay(self.identifier, user_id) self.save()