Skip to content

Commit

Permalink
feat: Implement ObjectTag retrieve REST API
Browse files Browse the repository at this point in the history
Retrieve ObjectTags for given Object IDs, and optionally filter by
taxonomy.
  • Loading branch information
yusuf-musleh committed Aug 3, 2023
1 parent 762fc48 commit 4c89229
Show file tree
Hide file tree
Showing 5 changed files with 394 additions and 6 deletions.
14 changes: 13 additions & 1 deletion openedx_tagging/core/tagging/rest_api/v1/permissions.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
Taxonomy permissions
Tagging permissions
"""

from rest_framework.permissions import DjangoObjectPermissions
Expand All @@ -15,3 +15,15 @@ class TaxonomyObjectPermissions(DjangoObjectPermissions):
"PATCH": ["%(app_label)s.change_%(model_name)s"],
"DELETE": ["%(app_label)s.delete_%(model_name)s"],
}


class ObjectTagObjectPermissions(DjangoObjectPermissions):
perms_map = {
"GET": ["%(app_label)s.view_%(model_name)s"],
"OPTIONS": [],
"HEAD": ["%(app_label)s.view_%(model_name)s"],
"POST": ["%(app_label)s.add_%(model_name)s"],
"PUT": ["%(app_label)s.change_%(model_name)s"],
"PATCH": ["%(app_label)s.change_%(model_name)s"],
"DELETE": ["%(app_label)s.delete_%(model_name)s"],
}
28 changes: 27 additions & 1 deletion openedx_tagging/core/tagging/rest_api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from rest_framework import serializers

from openedx_tagging.core.tagging.models import Taxonomy
from openedx_tagging.core.tagging.models import Taxonomy, ObjectTag


class TaxonomyListQueryParamsSerializer(serializers.Serializer):
Expand All @@ -29,3 +29,29 @@ class Meta:
"system_defined",
"visible_to_authors",
]


class ObjectTagListQueryParamsSerializer(serializers.Serializer):
"""
Serializer for the query params for the ObjectTag GET view
"""

taxonomy = serializers.PrimaryKeyRelatedField(
queryset=Taxonomy.objects.all(), required=False
)


class ObjectTagSerializer(serializers.ModelSerializer):
"""
Serializer for the ObjectTag model.
"""

class Meta:
model = ObjectTag
fields = [
"name",
"value",
"taxonomy_id",
"tag_ref",
"is_valid",
]
1 change: 1 addition & 0 deletions openedx_tagging/core/tagging/rest_api/v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@

router = DefaultRouter()
router.register("taxonomies", views.TaxonomyView, basename="taxonomy")
router.register("object_tags", views.ObjectTagView, basename="object_tag")

urlpatterns = [path("", include(router.urls))]
94 changes: 91 additions & 3 deletions openedx_tagging/core/tagging/rest_api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,29 @@
Tagging API Views
"""
from django.http import Http404
from rest_framework.viewsets import ModelViewSet
from rest_framework import status
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination

from ...api import (
create_taxonomy,
get_taxonomy,
get_taxonomies,
get_object_tags,
)
from .serializers import TaxonomyListQueryParamsSerializer, TaxonomySerializer
from .permissions import TaxonomyObjectPermissions
from .serializers import (
TaxonomyListQueryParamsSerializer,
TaxonomySerializer,
ObjectTagListQueryParamsSerializer,
ObjectTagSerializer,
)
from .permissions import TaxonomyObjectPermissions, ObjectTagObjectPermissions


class ObjectTagPagination(PageNumberPagination):
page_size = 100
max_page_size = 100


class TaxonomyView(ModelViewSet):
Expand Down Expand Up @@ -144,3 +158,77 @@ def perform_create(self, serializer):
Create a new taxonomy.
"""
serializer.instance = create_taxonomy(**serializer.validated_data)


class ObjectTagView(ReadOnlyModelViewSet):
"""
View to retrieve paginated ObjectTags for a provided Object ID (object_id).
**Retrieve Parameters**
* object_id (required): - The Object ID to retrieve ObjectTags for.
**Retrieve Query Parameters**
* taxonomy (optional) - PK of taxonomy to filter ObjectTags for.
* page (optional) - Page number of paginated results.
* page_size (optional) - Number of results included in each page.
**Retrieve Example Requests**
GET api/tagging/v1/object_tags/:object_id
GET api/tagging/v1/object_tags/:object_id?taxonomy=1
GET api/tagging/v1/object_tags/:object_id?taxonomy=1&page=2
GET api/tagging/v1/object_tags/:object_id?taxonomy=1&page=2&page_size=10
**Retrieve Query Returns**
* 200 - Success
* 400 - Invalid query parameter
* 403 - Permission denied
**Create Query Returns**
* 403 - Permission denied
* 405 - Method not allowed
**Update Query Returns**
* 403 - Permission denied
* 405 - Method not allowed
**Delete Query Returns**
* 403 - Permission denied
* 405 - Method not allowed
"""

serializer_class = ObjectTagSerializer
permission_classes = [ObjectTagObjectPermissions]
pagination_class = ObjectTagPagination
lookup_field = "object_id"

def get_queryset(self):
"""
Return a queryset of object tags for a given object.
If a taxonomy is passed in, object tags are limited to that taxonomy.
"""
object_id = self.kwargs.get("object_id")
query_params = ObjectTagListQueryParamsSerializer(
data=self.request.query_params.dict()
)
query_params.is_valid(raise_exception=True)
taxonomy = query_params.data.get("taxonomy", None)
taxonomy = get_taxonomy(taxonomy)
return get_object_tags(object_id, taxonomy)

def retrieve(self, request, object_id=None):
"""
Retrieve ObjectTags that belong to a given object_id and
return paginated results.
Note: We override `retrieve` here instead of `list` because we are
passing in the Object ID (object_id) in the path (as opposed to passing
it in as a query_param) to retrieve the related ObjectTags.
By default retrieve would expect an ObjectTag ID to be passed in the
path and returns a it as a single result however that is not
behavior we want.
"""
object_tags = self.get_queryset()
paginated_object_tags = self.paginate_queryset(object_tags)
serializer = ObjectTagSerializer(paginated_object_tags, many=True)
return self.get_paginated_response(serializer.data)
Loading

0 comments on commit 4c89229

Please sign in to comment.