From 1e2d9b0066400aa4adf0e87ed9e18f25e212da2a Mon Sep 17 00:00:00 2001 From: Jillian Vogel Date: Fri, 12 Jan 2024 02:06:06 +1030 Subject: [PATCH] feat: adds can_tag_object permission to Taxonomy List response This permission is needed by the Content Tag Drawer, which fetches the list of taxonomies available for tagging a given content item, and shows an "Add tags" button for the ones the user may add Object Tags for. --- .../core/tagging/rest_api/v1/serializers.py | 20 +++++++++++++++++++ .../core/tagging/test_views.py | 12 ++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/openedx_tagging/core/tagging/rest_api/v1/serializers.py b/openedx_tagging/core/tagging/rest_api/v1/serializers.py index 22d2ec33..6b6a514e 100644 --- a/openedx_tagging/core/tagging/rest_api/v1/serializers.py +++ b/openedx_tagging/core/tagging/rest_api/v1/serializers.py @@ -13,6 +13,7 @@ from openedx_tagging.core.tagging.data import TagData from openedx_tagging.core.tagging.import_export.parsers import ParserFormat from openedx_tagging.core.tagging.models import ObjectTag, Tag, TagImportTask, Taxonomy +from openedx_tagging.core.tagging.rules import ObjectTagPermissionItem from ..utils import UserPermissionsHelper @@ -72,6 +73,7 @@ class TaxonomySerializer(UserPermissionsSerializerMixin, serializers.ModelSerial tags_count = serializers.SerializerMethodField() can_change = serializers.SerializerMethodField() can_delete = serializers.SerializerMethodField() + can_tag_object = serializers.SerializerMethodField() class Meta: model = Taxonomy @@ -87,6 +89,7 @@ class Meta: "tags_count", "can_change", "can_delete", + "can_tag_object", ] def to_representation(self, instance): @@ -99,6 +102,23 @@ def to_representation(self, instance): def get_tags_count(self, instance): return instance.tag_set.count() + def get_can_tag_object(self, instance) -> Optional[bool]: + """ + Returns True if the current request user may create object tags on this taxonomy. + + (The object_id test is necessarily skipped because we don't have an object_id to check.) + """ + request = self._request + assert request and request.user + if not self._include_perms: + return None + + model = self._model + app_label = model._meta.app_label + perm_name = f'{app_label}.add_objecttag' + perm_object = ObjectTagPermissionItem(taxonomy=instance, object_id=None) + return request.user.has_perm(perm_name, perm_object) + class ObjectTagListQueryParamsSerializer(serializers.Serializer): # pylint: disable=abstract-method """ diff --git a/tests/openedx_tagging/core/tagging/test_views.py b/tests/openedx_tagging/core/tagging/test_views.py index a31a0253..bb967272 100644 --- a/tests/openedx_tagging/core/tagging/test_views.py +++ b/tests/openedx_tagging/core/tagging/test_views.py @@ -55,6 +55,7 @@ def check_taxonomy( visible_to_authors=True, can_change=None, can_delete=None, + can_tag_object=None, ): """ Check taxonomy data @@ -69,6 +70,7 @@ def check_taxonomy( assert data["visible_to_authors"] == visible_to_authors assert data["can_change"] == can_change assert data["can_delete"] == can_delete + assert data["can_tag_object"] == can_tag_object class TestTaxonomyViewMixin(APITestCase): @@ -170,6 +172,7 @@ def test_list_taxonomy(self, user_attr: str | None, expected_status: int, tags_c # System taxonomy cannot be modified "can_change": False, "can_delete": False, + "can_tag_object": False, }, { "id": taxonomy.id, @@ -184,6 +187,7 @@ def test_list_taxonomy(self, user_attr: str | None, expected_status: int, tags_c # Enabled taxonomy can be modified by staff "can_change": is_admin, "can_delete": is_admin, + "can_tag_object": False, }, ] assert response.data.get("can_add") == is_admin @@ -263,6 +267,7 @@ def test_list_taxonomy_query_count(self, include_perms): for taxonomy in response.data["results"]: assert taxonomy["can_change"] == expected_perm assert taxonomy["can_delete"] == expected_perm + assert taxonomy["can_tag_object"] == expected_perm def test_list_invalid_page(self) -> None: url = TAXONOMY_LIST_URL @@ -320,6 +325,7 @@ def test_detail_taxonomy( expected_data = create_data expected_data["can_change"] = is_admin expected_data["can_delete"] = is_admin + expected_data["can_tag_object"] = False check_taxonomy(response.data, taxonomy.pk, **expected_data) def test_detail_system_taxonomy(self): @@ -431,6 +437,7 @@ def test_update_taxonomy(self, user_attr, expected_status): "enabled": True, "can_change": True, "can_delete": True, + "can_tag_object": False, }, ) @@ -489,6 +496,7 @@ def test_patch_taxonomy(self, user_attr, expected_status): "enabled": True, "can_change": True, "can_delete": True, + "can_tag_object": False, }, ) @@ -1430,7 +1438,7 @@ def test_small_query_count(self, include_perms): expected_perm = None url = f"{self.small_taxonomy_url}?search_term=eU" if include_perms: - url += '?include_perms' + url += '&include_perms' expected_perm = True self.client.force_authenticate(user=self.staff) @@ -1443,6 +1451,7 @@ def test_small_query_count(self, include_perms): for taxonomy in response.data["results"]: assert taxonomy["can_change"] == expected_perm assert taxonomy["can_delete"] == expected_perm + assert not taxonomy["can_tag_object"] def test_empty_results(self): """ @@ -1517,6 +1526,7 @@ def test_large_taxonomy(self, include_perms): ) assert results[0].get("can_change") == expected_perm assert results[0].get("can_delete") == expected_perm + assert not results[0].get("can_tag_object") # Checking pagination values assert data.get("next") == (