forked from openedx/openedx-learning
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
19 changed files
with
694 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
""" | ||
Taxonomies API URLs. | ||
""" | ||
|
||
from django.urls import path, include | ||
|
||
from .v1 import urls as v1_urls | ||
|
||
urlpatterns = [path("v1/", include(v1_urls))] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
""" | ||
Taxonomy permissions | ||
""" | ||
|
||
from rest_framework.permissions import DjangoObjectPermissions | ||
|
||
|
||
class TaxonomyObjectPermissions(DjangoObjectPermissions): | ||
perms_map = { | ||
'GET': ['%(app_label)s.view_%(model_name)s'], | ||
# 'GET': [], | ||
'OPTIONS': [], | ||
# 'HEAD': ['%(app_label)s.view_%(model_name)s'], | ||
'HEAD': [], | ||
'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'], | ||
} | ||
|
||
def has_permission(self, request, view): | ||
# Workaround to allow 'retrieve' view to pass through the | ||
# method GET permission check. Actual object permission check | ||
# is done in the get_object() method. | ||
if view.action == 'retrieve': | ||
return bool(request.user and request.user.is_authenticated) | ||
else: | ||
return super().has_permission(request, view) | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
""" | ||
API Serializers for taxonomies | ||
""" | ||
|
||
from django.utils.module_loading import import_string | ||
from rest_framework import serializers | ||
|
||
from openedx_tagging.core.tagging.models import Taxonomy | ||
|
||
class TaxonomyListQueryParamsSerializer(serializers.Serializer): | ||
""" | ||
Serializer for the query params for the GET view | ||
""" | ||
|
||
enabled = serializers.BooleanField(required=False) | ||
|
||
class ClassField(serializers.Field): | ||
""" | ||
Class field | ||
""" | ||
def to_representation(self, class_type): | ||
if class_type: | ||
# ref: https://stackoverflow.com/a/2020083 | ||
return ".".join( | ||
[class_type.__module__, class_type.__qualname__] | ||
) | ||
return None | ||
|
||
def to_internal_value(self, value): | ||
if value: | ||
try: | ||
# Ensure that the class exists | ||
return import_string(value) | ||
except (ImportError, AttributeError): | ||
raise serializers.ValidationError( | ||
f"Invalid taxonomy_class: {value}." | ||
) | ||
return value | ||
|
||
class TaxonomySerializer(serializers.ModelSerializer): | ||
taxonomy_class = ClassField(required=False, allow_null=True) | ||
class Meta: | ||
model = Taxonomy | ||
fields = [ | ||
"id", | ||
"name", | ||
"description", | ||
"enabled", | ||
"required", | ||
"allow_multiple", | ||
"allow_free_text", | ||
"system_defined", | ||
"visible_to_authors", | ||
"taxonomy_class", | ||
] | ||
|
||
def save(self): | ||
raise NotImplementedError( | ||
"Cannot save a taxonomy through serializer. Use the create_taxonomy or" | ||
"update_taxonomy function instead." | ||
) | ||
|
||
def update(self): | ||
raise NotImplementedError( | ||
"Cannot save a taxonomy through serializer. Use the create_taxonomy or" | ||
"update_taxonomy function instead." | ||
) | ||
def create(self): | ||
raise NotImplementedError( | ||
"Cannot save a taxonomy through serializer. Use the create_taxonomy or" | ||
"update_taxonomy function instead." | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
""" | ||
Taxonomies API v1 URLs. | ||
""" | ||
|
||
from rest_framework.routers import DefaultRouter | ||
|
||
from django.urls.conf import path, include | ||
|
||
from . import views | ||
|
||
router = DefaultRouter() | ||
router.register("taxonomies", views.TaxonomyView, basename="taxonomy") | ||
|
||
urlpatterns = [ | ||
path('', include(router.urls)) | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
""" | ||
Tagging API Views | ||
""" | ||
from django.http import Http404 | ||
from rest_framework import status | ||
from rest_framework.response import Response | ||
from rest_framework.viewsets import ModelViewSet | ||
|
||
from ...api import ( | ||
create_taxonomy, | ||
delete_taxonomy, | ||
get_taxonomy, | ||
get_taxonomies, | ||
update_taxonomy, | ||
) | ||
from .serializers import TaxonomyListQueryParamsSerializer, TaxonomySerializer | ||
from .permissions import TaxonomyObjectPermissions | ||
|
||
|
||
class TaxonomyView(ModelViewSet): | ||
""" | ||
View to list, create, retrieve, update, or delete Taxonomies. | ||
NOTE: Only the GET request requires a request parameter, otherwise pass the uuid as part | ||
of the post body | ||
**List Query Parameters** | ||
* enabled (optional) - Filter by enabled status (default: None). Valid values: true, false, 1, 0, "true", "false", "1" | ||
**List Example Requests** | ||
GET api/tagging/v1/taxonomy - Get all taxonomies | ||
GET api/tagging/v1/taxonomy?enabled=true - Get all enabled taxonomies | ||
GET api/tagging/v1/taxonomy?enabled=false - Get all disabled taxonomies | ||
**List Query Returns** | ||
* 200 - Success | ||
* 400 - Invalid query parameter | ||
* 403 - Permission denied | ||
**Retrieve Parameters** | ||
* id (required): - The id of the taxonomy to retrieve | ||
**Retrieve Example Requests** | ||
GET api/tagging/v1/taxonomy/:id - Get a specific taxonomy | ||
**Retrieve Query Returns** | ||
* 200 - Success | ||
* 404 - Taxonomy not found or User does not have permission to access the taxonomy | ||
**Create Parameters** | ||
* name (required): User-facing label used when applying tags from this taxonomy to Open edX objects. | ||
* description (optional): Provides extra information for the user when applying tags from this taxonomy to an object. | ||
* enabled (optional): Only enabled taxonomies will be shown to authors (default: true). | ||
* required (optional): Indicates that one or more tags from this taxonomy must be added to an object (default: False). | ||
* allow_multiple (optional): Indicates that multiple tags from this taxonomy may be added to an object (default: False). | ||
* allow_free_text (optional): Indicates that tags in this taxonomy need not be predefined; authors may enter their own tag values (default: False). | ||
* taxonomy_class (optional): Taxonomy subclass used to instantiate this instance. Must be a fully-qualified | ||
module and class name (default: None). | ||
**Create Example Requests** | ||
POST api/tagging/v1/taxonomy - Create a taxonomy | ||
{ | ||
"name": "Taxonomy Name", - User-facing label used when applying tags from this taxonomy to Open edX objects." | ||
"description": "This is a description", | ||
"enabled": True, | ||
"required": True, | ||
"allow_multiple": True, | ||
"allow_free_text": True, | ||
"taxonomy_class": "class", #ToDo: add a example here | ||
} | ||
**Create Query Returns** | ||
* 201 - Success | ||
* 403 - Permission denied | ||
**Update Parameters** | ||
* id (required): - The id of the taxonomy to update | ||
**Update Request Body** | ||
* name (optional): User-facing label used when applying tags from this taxonomy to Open edX objects. | ||
* description (optional): Provides extra information for the user when applying tags from this taxonomy to an object. | ||
* enabled (optional): Only enabled taxonomies will be shown to authors. | ||
* required (optional): Indicates that one or more tags from this taxonomy must be added to an object. | ||
* allow_multiple (optional): Indicates that multiple tags from this taxonomy may be added to an object. | ||
* allow_free_text (optional): Indicates that tags in this taxonomy need not be predefined; authors may enter their own tag values. | ||
* taxonomy_class (optional): Taxonomy subclass used to instantiate this instance. Must be a fully-qualified | ||
module and class name. | ||
**Update Example Requests** | ||
PUT api/tagging/v1/taxonomy/:id - Update a taxonomy | ||
{ | ||
"name": "Taxonomy New Name", | ||
"description": "This is a new description", | ||
"enabled": False, | ||
"required": False, | ||
"allow_multiple": False, | ||
"allow_free_text": True, | ||
"taxonomy_class": "class", #ToDo: add a example here | ||
} | ||
PATCH api/tagging/v1/taxonomy/:id - Partially update a taxonomy | ||
{ | ||
"name": "Taxonomy New Name", | ||
} | ||
**Update Query Returns** | ||
* 200 - Success | ||
* 403 - Permission denied | ||
**Delete Parameters** | ||
* id (required): - The id of the taxonomy to delete | ||
**Delete Example Requests** | ||
DELETE api/tagging/v1/taxonomy/:id - Delete a taxonomy | ||
**Delete Query Returns** | ||
* 200 - Success | ||
* 404 - Taxonomy not found | ||
* 403 - Permission denied | ||
""" | ||
|
||
|
||
serializer_class = TaxonomySerializer | ||
lookup_field = "id" | ||
permission_classes = [TaxonomyObjectPermissions] | ||
|
||
def get_object(self): | ||
""" | ||
Return the requested taxonomy object, if the user has appropriate | ||
permissions. | ||
""" | ||
id = self.kwargs.get("id") | ||
taxonomy = get_taxonomy(id) | ||
if not taxonomy: | ||
raise Http404("Taxonomy not found") | ||
self.check_object_permissions(self.request, taxonomy) | ||
|
||
return taxonomy | ||
|
||
def get_queryset(self): | ||
""" | ||
Return a list of taxonomies. If you want the disabled taxonomies, pass enabled=False. If you want all taxonomies (both enabled and disabled), pass enabled=None. | ||
""" | ||
query_params = TaxonomyListQueryParamsSerializer( | ||
data=self.request.query_params.dict() | ||
) | ||
query_params.is_valid(raise_exception=True) | ||
enabled = query_params.data.get("enabled", None) | ||
|
||
return get_taxonomies(enabled) | ||
|
||
def create(self, request): | ||
""" | ||
Create a new taxonomy. | ||
""" | ||
serializer = self.get_serializer(data=request.data) | ||
serializer.is_valid(raise_exception=True) | ||
taxonomy = create_taxonomy(**serializer.validated_data) | ||
return Response( | ||
self.serializer_class(taxonomy).data, status=status.HTTP_201_CREATED | ||
) | ||
|
||
def update(self, request, *args, **kwargs): | ||
""" | ||
Update a taxonomy. | ||
""" | ||
partial = kwargs.pop('partial', False) | ||
instance = self.get_object() | ||
serializer = self.get_serializer(instance, data=request.data, partial=partial) | ||
serializer.is_valid(raise_exception=True) | ||
update_taxonomy(instance.id, **serializer.validated_data) | ||
|
||
return Response(serializer.data) | ||
|
||
def perform_destroy(self, instance): | ||
""" | ||
Delete a taxonomy. | ||
""" | ||
delete_taxonomy(instance.id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
""" | ||
Tagging API URLs. | ||
""" | ||
|
||
from django.urls import path, include | ||
|
||
from .rest_api import urls | ||
|
||
app_name = "oel_tagging" | ||
urlpatterns = [path("", include(urls))] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.