From e97cee9b690dfda1f4980f3c234b7fd5c23d667a Mon Sep 17 00:00:00 2001 From: David Larlet Date: Tue, 7 Nov 2023 16:19:43 -0500 Subject: [PATCH] Improve the DRF API --- umap/api_views.py | 44 ++++++++++++++++++++++++-------------------- umap/decorators.py | 4 ++-- umap/serializers.py | 18 +++++++++++++++++- umap/urls.py | 10 +++++----- 4 files changed, 48 insertions(+), 28 deletions(-) diff --git a/umap/api_views.py b/umap/api_views.py index 01c00dab..e1a6e037 100644 --- a/umap/api_views.py +++ b/umap/api_views.py @@ -1,4 +1,6 @@ +from django.shortcuts import get_object_or_404 from rest_framework import generics, status +from rest_framework.permissions import BasePermission from rest_framework.response import Response from .models import Map @@ -16,23 +18,6 @@ class MapList(generics.ListCreateAPIView): else: serializer.save() - def get_map_permissions(self, map_): - permissions = {} - permissions["edit_status"] = map_.edit_status - permissions["share_status"] = map_.share_status - if map_.owner: - permissions["owner"] = { - "id": map_.owner.pk, - "name": str(map_.owner), - "url": map_.owner.get_url(), - } - permissions["editors"] = [ - {"id": editor.pk, "name": str(editor)} for editor in map_.editors.all() - ] - if not map_.owner and map_.is_anonymous_owner(self.request): - permissions["anonymous_edit_url"] = map_.get_anonymous_edit_url() - return permissions - def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) @@ -40,11 +25,9 @@ class MapList(generics.ListCreateAPIView): map_ = serializer.instance headers = self.get_success_headers(serializer.data) data = serializer.data - permissions = self.get_map_permissions(map_) if not map_.owner: anonymous_url = map_.get_anonymous_edit_url() - permissions["anonymous_edit_url"] = anonymous_url - data["permissions"] = permissions + data["permissions"]["anonymous_edit_url"] = anonymous_url response = Response(data, status=status.HTTP_201_CREATED, headers=headers) if not self.request.user.is_authenticated: key, value = map_.signed_cookie_elements @@ -54,6 +37,27 @@ class MapList(generics.ListCreateAPIView): return response +class MapPermission(BasePermission): + def has_permission(self, request, view): + map_inst = get_object_or_404(Map, pk=view.kwargs.get("pk")) + if request.method == "PUT": + return map_inst.can_edit(user=request.user, request=request) + if request.method == "GET": + return map_inst.can_view(request) + + class MapDetail(generics.RetrieveUpdateDestroyAPIView): queryset = Map.public.all().order_by("-modified_at") serializer_class = MapSerializer + + permission_classes = [MapPermission] + + def retrieve(self, request, *args, **kwargs): + instance = self.get_object() + serializer = self.get_serializer(instance) + data = serializer.data + if not instance.owner and instance.is_anonymous_owner(request): + data["permissions"][ + "anonymous_edit_url" + ] = instance.get_anonymous_edit_url() + return Response(data) diff --git a/umap/decorators.py b/umap/decorators.py index 2c36ebf6..b9b5232a 100644 --- a/umap/decorators.py +++ b/umap/decorators.py @@ -33,7 +33,7 @@ def can_edit_map(view_func): @wraps(view_func) def wrapper(request, *args, **kwargs): - map_inst = get_object_or_404(Map, pk=kwargs.get("map_id", kwargs.get("pk"))) + map_inst = get_object_or_404(Map, pk=kwargs["map_id"]) user = request.user kwargs["map_inst"] = map_inst # Avoid rerequesting the map in the view if map_inst.edit_status >= map_inst.EDITORS: @@ -54,7 +54,7 @@ def can_view_map(view_func): @wraps(view_func) def wrapper(request, *args, **kwargs): - map_inst = get_object_or_404(Map, pk=kwargs.get("map_id", kwargs.get("pk"))) + map_inst = get_object_or_404(Map, pk=kwargs["map_id"]) kwargs["map_inst"] = map_inst # Avoid rerequesting the map in the view if not map_inst.can_view(request): return HttpResponseForbidden() diff --git a/umap/serializers.py b/umap/serializers.py index 946923e7..8dc1f053 100644 --- a/umap/serializers.py +++ b/umap/serializers.py @@ -16,10 +16,26 @@ DEFAULT_CENTER = Point(DEFAULT_LONGITUDE, DEFAULT_LATITUDE) class MapSerializer(serializers.HyperlinkedModelSerializer): slug = serializers.CharField(required=False) + permissions = serializers.SerializerMethodField() class Meta: model = Map - fields = ["id", "name", "slug", "center", "settings"] + fields = ["id", "name", "slug", "center", "settings", "permissions"] + + def get_permissions(self, obj): + permissions = {} + permissions["edit_status"] = obj.edit_status + permissions["share_status"] = obj.share_status + if obj.owner: + permissions["owner"] = { + "id": obj.owner.pk, + "name": str(obj.owner), + "url": obj.owner.get_url(), + } + permissions["editors"] = [ + {"id": editor.pk, "name": str(editor)} for editor in obj.editors.all() + ] + return permissions def validate(self, data): slug = data.get("slug") diff --git a/umap/urls.py b/umap/urls.py index 638fe4f3..2744273d 100644 --- a/umap/urls.py +++ b/umap/urls.py @@ -63,6 +63,11 @@ i18n_urls = [ views.PictogramJSONList.as_view(), name="pictogram_list_json", ), + re_path( + r"^api/map/(?P[\d]+)/$", + api_views.MapDetail.as_view(), + name="map_detail", + ), ] i18n_urls += decorated_patterns( [can_view_map, cache_control(must_revalidate=True)], @@ -115,11 +120,6 @@ i18n_urls += decorated_patterns( ), ) map_urls = [ - re_path( - r"^api/map/(?P[\d]+)/$", - api_views.MapDetail.as_view(), - name="map_detail", - ), re_path( r"^map/(?P[\d]+)/update/permissions/$", views.UpdateMapPermissions.as_view(),