From 6500451a93c317df6fe57740abc5f5b70853bfa5 Mon Sep 17 00:00:00 2001 From: Gagan Trivedi Date: Tue, 9 Jan 2024 14:32:59 +0530 Subject: [PATCH] feat(tags/view): Add api to get tag by uuid (#3229) --- api/projects/tags/permissions.py | 7 +-- api/projects/tags/serializers.py | 2 +- api/projects/tags/views.py | 18 +++++- api/tests/unit/projects/tags/test_views.py | 71 ++++++++++++++++++++++ 4 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 api/tests/unit/projects/tags/test_views.py diff --git a/api/projects/tags/permissions.py b/api/projects/tags/permissions.py index 69bf5cb50763..f004ea08a3b0 100644 --- a/api/projects/tags/permissions.py +++ b/api/projects/tags/permissions.py @@ -9,16 +9,13 @@ def has_permission(self, request, view): project_pk = view.kwargs.get("project_pk") if not project_pk: return False - project = Project.objects.get(pk=project_pk) if request.user.is_project_admin(project): return True - if view.action == "list" and request.user.has_project_permission( - VIEW_PROJECT, project - ): - return True + if view.action in ["list", "get_by_uuid"]: + return request.user.has_project_permission(VIEW_PROJECT, project) # move on to object specific permissions return view.detail diff --git a/api/projects/tags/serializers.py b/api/projects/tags/serializers.py index 4cef5e897907..4026623b7276 100644 --- a/api/projects/tags/serializers.py +++ b/api/projects/tags/serializers.py @@ -6,5 +6,5 @@ class TagSerializer(serializers.ModelSerializer): class Meta: model = Tag - fields = ("id", "label", "color", "description", "project") + fields = ("id", "label", "color", "description", "project", "uuid") read_only_fields = ("project",) diff --git a/api/projects/tags/views.py b/api/projects/tags/views.py index ec89b8969140..d87e96a608bc 100644 --- a/api/projects/tags/views.py +++ b/api/projects/tags/views.py @@ -1,6 +1,9 @@ from rest_framework import viewsets +from rest_framework.decorators import action from rest_framework.generics import get_object_or_404 from rest_framework.permissions import IsAuthenticated +from rest_framework.request import Request +from rest_framework.response import Response from projects.permissions import VIEW_PROJECT @@ -26,9 +29,20 @@ def get_queryset(self): return queryset def perform_create(self, serializer): - project_id = self.kwargs["project_pk"] + project_id = int(self.kwargs["project_pk"]) serializer.save(project_id=project_id) def perform_update(self, serializer): - project_id = self.kwargs["project_pk"] + project_id = int(self.kwargs["project_pk"]) serializer.save(project_id=project_id) + + @action( + detail=False, + url_path=r"get-by-uuid/(?P[0-9a-f-]+)", + methods=["get"], + ) + def get_by_uuid(self, request: Request, project_pk: int, uuid: str): + qs = self.get_queryset() + tag = get_object_or_404(qs, uuid=uuid) + serializer = self.get_serializer(tag) + return Response(serializer.data) diff --git a/api/tests/unit/projects/tags/test_views.py b/api/tests/unit/projects/tags/test_views.py new file mode 100644 index 000000000000..3c61c0be725f --- /dev/null +++ b/api/tests/unit/projects/tags/test_views.py @@ -0,0 +1,71 @@ +from typing import Callable + +import pytest +from django.urls import reverse +from pytest_lazyfixture import lazy_fixture +from rest_framework import status +from rest_framework.test import APIClient + +from projects.models import Project +from projects.permissions import VIEW_PROJECT +from projects.tags.models import Tag + + +@pytest.mark.parametrize( + "client", + [(lazy_fixture("admin_master_api_key_client")), (lazy_fixture("admin_client"))], +) +def test_get_tag_by_uuid(client: APIClient, project: Project, tag: Tag): + url = reverse("api-v1:projects:tags-get-by-uuid", args=[project.id, str(tag.uuid)]) + + # When + response = client.get(url) + + # Then + assert response.status_code == status.HTTP_200_OK + assert response.json()["uuid"] == str(tag.uuid) + + +def test_get_tag_by_uuid__returns_403_for_user_without_permission( + staff_client: APIClient, + organisation_one_project_two: Project, + project: Project, + tag: Tag, + with_project_permissions: Callable[[list[str], int], None], +): + # Given + # user with view permission for a different project + with_project_permissions([VIEW_PROJECT], organisation_one_project_two.id) + + url = reverse( + "api-v1:projects:tags-get-by-uuid", + args=[project.id, str(tag.uuid)], + ) + + # When + response = staff_client.get(url) + + # Then + assert response.status_code == status.HTTP_403_FORBIDDEN + + +def test_get_tag_by__uuid_returns_200_for_user_with_view_project_permission( + staff_client: APIClient, + project: Project, + tag: Tag, + with_project_permissions: Callable[[list[str], int], None], +): + # Given + with_project_permissions([VIEW_PROJECT]) + + url = reverse( + "api-v1:projects:tags-get-by-uuid", + args=[project.id, str(tag.uuid)], + ) + + # When + response = staff_client.get(url) + + # Then + assert response.status_code == status.HTTP_200_OK + assert response.json()["uuid"] == str(tag.uuid)