diff --git a/mayan/apps/tags/api_views.py b/mayan/apps/tags/api_views.py index 7018c54599..32ccc9a576 100644 --- a/mayan/apps/tags/api_views.py +++ b/mayan/apps/tags/api_views.py @@ -1,7 +1,9 @@ from __future__ import absolute_import, unicode_literals -from rest_framework import generics, status -from rest_framework.decorators import action +from drf_yasg.utils import swagger_auto_schema + +from rest_framework import generics, serializers, status, routers, viewsets +from rest_framework.decorators import action, detail_route from rest_framework.exceptions import ValidationError from rest_framework.response import Response @@ -10,12 +12,7 @@ from mayan.apps.documents.api_views import DocumentViewSet from mayan.apps.documents.models import Document from mayan.apps.documents.permissions import permission_document_view from mayan.apps.documents.serializers import DocumentSerializer -from mayan.apps.rest_api.filters import MayanObjectPermissionsFilter -from mayan.apps.rest_api.generics import ( - ListAPIView, ListCreateAPIView, RetrieveDestroyAPIView, - RetrieveUpdateDestroyAPIView -) -from mayan.apps.rest_api.permissions import MayanPermission +from mayan.apps.rest_api.viewsets import MayanAPIModelViewSet from .models import Tag from .permissions import ( @@ -23,121 +20,84 @@ from .permissions import ( permission_tag_edit, permission_tag_remove, permission_tag_view ) from .serializers import ( - DocumentTagAttachSerializer, DocumentTagSerializer, TagAttachSerializer, - TagRemoveSerializer, TagSerializer, + DocumentTagAttachSerializer, DocumentTagSerializer, + TagDocumentAttachRemoveSerializer, + TagSerializer, + ) - -from django.conf.urls import url, include -from django.contrib.auth.models import User - -from rest_framework import routers, serializers, viewsets -from rest_framework.decorators import detail_route -from rest_framework.response import Response - -from drf_yasg.utils import swagger_auto_schema - - -class TagViewSet(viewsets.ModelViewSet): - filter_backends = (MayanObjectPermissionsFilter,) - lookup_field = 'pk' +class TagViewSet(MayanAPIModelViewSet): lookup_url_kwarg='tag_id' - permission_classes = (MayanPermission,) + object_permission_map = { + 'destroy': permission_tag_delete, + 'document_attach': permission_tag_attach, + 'document_list': permission_tag_view, + 'document_remove': permission_tag_remove, + 'list': permission_tag_view, + 'partial_update': permission_tag_edit, + 'update': permission_tag_edit + } queryset = Tag.objects.all() serializer_class = TagSerializer + view_permission_map = { + 'create': permission_tag_create + } - - #@swagger_auto_schema(operation_description='GET /articles/today/') - @swagger_auto_schema( - operation_description="partial_update description override", responses={200: TagAttachSerializer} - ) @action( - detail=True, lookup_field='pk', lookup_url_kwarg='tag_id', - methods=('post',), serializer_class=TagAttachSerializer, - url_name='document-attach', url_path='attach' + detail=True, lookup_url_kwarg='tag_id', methods=('post',), + serializer_class=TagDocumentAttachRemoveSerializer, + url_name='document-attach', url_path='documents/attach' ) - def attach(self, request, *args, **kwargs): - #print '!!! attach', args, kwargs#, self.context - #return Response({}) + def document_attach(self, request, *args, **kwargs): + instance = self.get_object() serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) - #print '((((((((', serializer.validated_data - #self.perform_attach(serializer=serializer) - serializer.attach(instance=self.get_object()) + serializer.attach(instance=instance) headers = self.get_success_headers(data=serializer.data) return Response( serializer.data, status=status.HTTP_200_OK, headers=headers ) - #def perform_attach(self, serializer): - # #print '!!!!', serializer - # serializer.attach(instance=self.get_object()) - - #def get_success_headers(self, data): - # try: - # return {'Location': str(data[api_settings.URL_FIELD_NAME])} - # except (TypeError, KeyError): - # return {} - @action( - detail=True, lookup_field='pk', lookup_url_kwarg='tag_id', - url_name='document-list', url_path='documents' + detail=True, lookup_url_kwarg='tag_id', + serializer_class=DocumentSerializer, url_name='document-list', + url_path='documents' ) def document_list(self, request, *args, **kwargs): - queryset = self.get_object().documents.all() - - #TODO:Filter queryset - #queryset = self.filter_queryset(self.get_queryset()) - + queryset = self.get_object().get_documents(user=self.request.user) page = self.paginate_queryset(queryset) + + serializer = self.get_serializer( + queryset, many=True, context={'request': request} + ) + if page is not None: - #serializer = self.get_serializer(page, many=True) - serializer = DocumentSerializer(page, many=True, context={'request': request}) return self.get_paginated_response(serializer.data) - #serializer = self.get_serializer(queryset, many=True) - serializer = DocumentSerializer(queryset, many=True, context={'request': request}) return Response(serializer.data) - - #serializer = DocumentSerializer( - # instance=, many=True, - # context={'request': request} - #) - #return Response(serializer.data) - - @action( detail=True, lookup_field='pk', lookup_url_kwarg='tag_id', - methods=('post',), serializer_class=TagRemoveSerializer, - url_name='document-remove', url_path='remove' + methods=('post',), serializer_class=TagDocumentAttachRemoveSerializer, + url_name='document-remove', url_path='documents/remove' ) - def remove(self, request, *args, **kwargs): - #print '!!! attach', args, kwargs#, self.context - #return Response({}) + def document_remove(self, request, *args, **kwargs): + instance = self.get_object() serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) - #print '((((((((', serializer.validated_data - #self.perform_attach(serializer=serializer) - serializer.remove(instance=self.get_object()) + serializer.attach(instance=instance) headers = self.get_success_headers(data=serializer.data) return Response( serializer.data, status=status.HTTP_200_OK, headers=headers ) - #def get_serializer_class(self, *args, **kwargs): - # #if self.action == 'attach': - # print '!!!!get_serializer_class', args, kwargs - # return TagAttachSerializer - - class DocumentTagViewSet(ExternalObjectMixin, viewsets.ReadOnlyModelViewSet): external_object_class = Document external_object_pk_url_kwarg = 'document_id' external_object_permission = permission_tag_view - lookup_field = 'pk' + #lookup_field = 'pk' object_permission = { 'list': permission_document_view, 'retrieve': permission_document_view @@ -145,7 +105,8 @@ class DocumentTagViewSet(ExternalObjectMixin, viewsets.ReadOnlyModelViewSet): serializer_class = DocumentTagSerializer @action( - detail=True, lookup_field='pk', lookup_url_kwarg='document_id', + #detail=True, lookup_field='pk', lookup_url_kwarg='document_id', + detail=True, lookup_url_kwarg='document_id', methods=('post',), serializer_class=DocumentTagAttachSerializer, url_name='tag-attach', url_path='attach' ) diff --git a/mayan/apps/tags/models.py b/mayan/apps/tags/models.py index 1662fc1c27..142f2208af 100644 --- a/mayan/apps/tags/models.py +++ b/mayan/apps/tags/models.py @@ -65,7 +65,7 @@ class Tag(models.Model): Return a filtered queryset documents that have this tag attached. """ return AccessControlList.objects.restrict_queryset( - permission=permission_document_view, queryset=self.documents, + permission=permission_document_view, queryset=self.documents.all(), user=user ) diff --git a/mayan/apps/tags/serializers.py b/mayan/apps/tags/serializers.py index 9f4d5c3012..fe4c62a0d0 100644 --- a/mayan/apps/tags/serializers.py +++ b/mayan/apps/tags/serializers.py @@ -1,5 +1,6 @@ from __future__ import absolute_import, unicode_literals +from django.core.exceptions import ImproperlyConfigured from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers @@ -9,7 +10,7 @@ from rest_framework.reverse import reverse from mayan.apps.acls.models import AccessControlList from mayan.apps.documents.models import Document from mayan.apps.documents.serializers import DocumentSerializer -from mayan.apps.rest_api.relations import MultiKwargHyperlinkedIdentityField +from mayan.apps.rest_api.mixins import ExternalObjectListSerializerMixin from .models import Tag from .permissions import permission_tag_attach @@ -42,55 +43,6 @@ class TagSerializer(serializers.HyperlinkedModelSerializer): model = Tag - -""" -class WritableTagSerializer(serializers.ModelSerializer): - documents_pk_list = serializers.CharField( - help_text=_( - 'Comma separated list of document primary keys to which this tag ' - 'will be attached.' - ), required=False - ) - - class Meta: - fields = ( - 'color', 'documents_pk_list', 'id', 'label', - ) - model = Tag - - def _add_documents(self, documents_pk_list, instance): - instance.documents.add( - *Document.objects.filter(pk__in=documents_pk_list.split(',')) - ) - - def create(self, validated_data): - documents_pk_list = validated_data.pop('documents_pk_list', '') - - instance = super(WritableTagSerializer, self).create(validated_data) - - if documents_pk_list: - self._add_documents( - documents_pk_list=documents_pk_list, instance=instance - ) - - return instance - - def update(self, instance, validated_data): - documents_pk_list = validated_data.pop('documents_pk_list', '') - - instance = super(WritableTagSerializer, self).update( - instance, validated_data - ) - - if documents_pk_list: - instance.documents.clear() - self._add_documents( - documents_pk_list=documents_pk_list, instance=instance - ) - - return instance -""" - class DocumentTagSerializer(TagSerializer): #document_attach_url = serializers.HyperlinkedIdentityField( # lookup_url_kwarg='document_id', view_name='rest_api:document-tag-attach' @@ -165,227 +117,32 @@ class DocumentTagAttachSerializer(serializers.Serializer): ) -class TagAttachSerializer(serializers.Serializer): -#class TagAttachSerializer(TagSerializer): - documents_pk_list = serializers.CharField( +class TagDocumentAttachRemoveSerializer(ExternalObjectListSerializerMixin, serializers.Serializer): + document_id = serializers.CharField( + help_text=_( + 'Primary key of document to which this tag will be attached or ' + 'removed.' + ), required=False, write_only=True + ) + documents_id_list = serializers.CharField( help_text=_( 'Comma separated list of document primary keys to which this ' - 'tag will be attached.' - ), write_only=True + 'tag will be attached or removed.' + ), required=False, write_only=True ) - #class Meta(TagSerializer.Meta): - # fields = TagSerializer.Meta.fields + ('documents_pk_list',) - # read_only_fields = TagSerializer.Meta.fields + class Meta: + external_object_list_model = Document + external_object_list_permission = permission_tag_attach + external_object_list_pk_field = 'document_id' + external_object_list_pk_list_field = 'document_id_list' def attach(self, instance): - queryset = AccessControlList.objects.restrict_queryset( - permission=permission_tag_attach, queryset=Document.objects.all(), - user=self.context['request'].user - ) - - for document in queryset.filter(pk__in=self.validated_data['documents_pk_list'].split(',')): + queryset = self.get_external_object_list() + for document in queryset: instance.attach_to(document=document, user=self.context['request'].user) - #print '@@@@@@@', self.validated_data['document_pk_list'] - #print '@@@@@@@', instance - #print '22222', validated_data - #print '!!!', self.data['document_pk_list'] - - -class TagRemoveSerializer(serializers.Serializer): - documents_pk_list = serializers.CharField( - help_text=_( - 'Comma separated list of document primary keys from which this ' - 'tag will be removed.' - ), write_only=True - ) - def remove(self, instance): - queryset = AccessControlList.objects.restrict_queryset( - permission=permission_tag_attach, queryset=Document.objects.all(), - user=self.context['request'].user - ) - - for document in queryset.filter(pk__in=self.validated_data['documents_pk_list'].split(',')): - instance.remove_from(document=document, user=self.context['request'].user) - - - -class RelatedModel(object): - @classmethod - def generate(cls, serializer, validated_data): - result = [] - - kwargs = getattr(serializer.Meta, 'related_model_kwargs', {}) - kwargs.update({'serializer': serializer}) - - for field_name in getattr(serializer.Meta, 'related_models', []): - kwargs.update({'field_name': field_name}) - related_field = cls(**kwargs) - related_field.pop_pk_list(validated_data=validated_data) - result.append(related_field) - - return result - - def __init__(self, field_name, serializer, pk_list_field=None, model=None, object_permission=None): - self.field_name = field_name - self._pk_list_field = pk_list_field - self.model = model - self.object_permission = object_permission - self.serializer = serializer - - def create(self, instance): - field = self.get_field(instance=instance) - field.clear() - #model = self.get_model() - - queryset = self.get_model().objects.filter(pk__in=self.pk_list.split(',')) - - permission = self.object_permission.get('create') - - if permission: - queryset = AccessControlList.objects.restrict_queryset( - permission=permission, - queryset=queryset, - user=self.serializer.context['request'].user - ) - - self.related_add() - - field.add(*queryset) - #fieldqueryset=queryset) - - #def related_add(self, queryset): - # self.get_field().add(*queryset) - - - #def _get_m2m_field(self, instance): - # getattr(instance, m2m_field_name).all() - - """ - def _add_m2m(self, instance, m2m_pk_list, permission): - m2m_field = self._get_m2m_field() - m2m_field.clear() - - queryset = AccessControlList.objects.restrict_queryset( - permission=permission, - queryset=m2m_model.objects.filter(pk__in=m2m_pk_list.split(',')), - user=self.context['request'].user - ) - - #m2m_field.add(*queryset) - self._m2m_add(m2m_field=m2m_field, queryset=queryset) - """ - - def get_model(self): - return self.model or self.get_field.model - - def get_field(self, instance): - return getattr(instance, self.field_name) - - def get_pk_list_field_name(self): - return self._pk_list_field or '{}_pk_list'.format(self.field_name) - - def pop_pk_list(self, validated_data): - self.pk_list = validated_data.pop(self.get_pk_list_field_name(), '') - - -class RelatedModelSerializerMixin(object): - #m2m_pk_list_name = 'documents_pk_list' - #m2m_field_name = 'documents' - #m2m_model = Document - - """ - class Meta: - extra_kwargs = { - 'url': { - 'lookup_field': 'pk', 'lookup_url_kwarg': 'tag_pk', - 'view_name': 'rest_api:tag-detail' - } - } - fields = ( - 'color', 'documents_count', 'documents_pk_list', 'documents_url', - 'id', 'label', 'url' - ) - model = Tag - related_models = ('documents',) - related_models_kwargs = { - 'documents': { - 'pk_list_field': 'documents_pk_list', 'model': Document, - 'permissions': {'create': permission_tag_attach} - } - } - """ - - def _get_m2m_field(self, instance): - getattr(instance, m2m_field_name).all() - - def _add_m2m(self, instance, m2m_pk_list, permission): - m2m_field = self._get_m2m_field() - m2m_field.clear() - - queryset = AccessControlList.objects.restrict_queryset( - permission=permission, - queryset=m2m_model.objects.filter(pk__in=m2m_pk_list.split(',')), - user=self.context['request'].user - ) - - #m2m_field.add(*queryset) - self._m2m_add(m2m_field=m2m_field, queryset=queryset) - - #def _m2m_add(self, m2m_field, queryset): - # m2m_field.add(*queryset) - - def _m2m_add(self, m2m_field, queryset): - for document in queryset.all(): - m2m_field.add(document=document, user=self.context['request'].user) - - def create(self, validated_data): - related_objects = RelatedModel.generate( - serializer=self, validated_data=validated_data - ) - - instance = super(RelatedModelSerializerMixin, self).create( - validated_data=validated_data - ) - - #TODO: return a container class - #TODO:related_objects.create(instance=instance) - for related_object in related_objects: - related_object.create(instance=instance) - - #if m2m_pk_list: - ## self._add_m2m( - # instance=instance, m2m_pk_list=m2m_pk_list, - # permission=permission_tag_add - # ) - - return instance - - ''' - # Extract the related field data before calling the superclass - # .create() and avoid an error due to unknown field data. - - #related_models = self.Meta.related_models - - #self.Meta.related_models - related_models_dictionary = {} - for related_model in self.Meta.related_models: - - #if self.m2m_pk_list_name: - m2m_pk_list = validated_data.pop(self.get_related_model_pk_list(), '') - - instance = super(RelatedObjectSerializerMixin, self).create( - validated_data=validated_data - ) - - if m2m_pk_list: - self._add_m2m( - instance=instance, m2m_pk_list=m2m_pk_list, - permission=permission_tag_add - ) - - return instance - ''' - + queryset = self.get_external_object_list() + for document in queryset: + instance.attach_from(document=document, user=self.context['request'].user) diff --git a/mayan/apps/tags/tests/test_api.py b/mayan/apps/tags/tests/test_api.py index 4c259f2de9..ac2f8de517 100644 --- a/mayan/apps/tags/tests/test_api.py +++ b/mayan/apps/tags/tests/test_api.py @@ -18,93 +18,7 @@ from .literals import ( TEST_TAG_COLOR, TEST_TAG_COLOR_EDITED, TEST_TAG_LABEL, TEST_TAG_LABEL_EDITED ) -from .mixins import TagTestMixin - - -class TagAPITestCase(TagTestMixin, BaseAPITestCase): - def test_tag_create_view_no_permission(self): - response = self._request_api_tag_create_view() - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) - self.assertEqual(Tag.objects.count(), 0) - - def test_tag_create_view_with_permission(self): - self.grant_permission(permission=permission_tag_create) - response = self._request_api_tag_create_view() - self.assertEqual(response.status_code, status.HTTP_201_CREATED) - - tag = Tag.objects.first() - self.assertEqual(response.data['id'], tag.pk) - self.assertEqual(response.data['label'], TEST_TAG_LABEL) - self.assertEqual(response.data['color'], TEST_TAG_COLOR) - - self.assertEqual(Tag.objects.count(), 1) - self.assertEqual(tag.label, TEST_TAG_LABEL) - self.assertEqual(tag.color, TEST_TAG_COLOR) - - def test_tag_delete_view_no_access(self): - self._create_test_tag() - response = self._request_api_tag_delete_view() - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertTrue(self.test_tag in Tag.objects.all()) - self.assertEqual(Tag.objects.all().count(), 1) - - def test_tag_delete_view_with_access(self): - self._create_test_tag() - self.grant_access(obj=self.test_tag, permission=permission_tag_delete) - response = self._request_api_tag_delete_view() - self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - self.assertEqual(Tag.objects.all().count(), 0) - - def test_tag_edit_patch_view_no_access(self): - self._create_test_tag() - response = self._request_api_tag_edit_patch_view() - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.test_tag.refresh_from_db() - self.assertEqual(self.test_tag.label, TEST_TAG_LABEL) - self.assertEqual(self.test_tag.color, TEST_TAG_COLOR) - self.assertEqual(Tag.objects.all().count(), 1) - - def test_tag_edit_patch_view_with_access(self): - self._create_test_tag() - self.grant_access(obj=self.test_tag, permission=permission_tag_edit) - response = self._request_api_tag_edit_patch_view() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.test_tag.refresh_from_db() - self.assertEqual(self.test_tag.label, TEST_TAG_LABEL_EDITED) - self.assertEqual(self.test_tag.color, TEST_TAG_COLOR_EDITED) - self.assertEqual(Tag.objects.all().count(), 1) - - def test_tag_edit_put_view_no_access(self): - self._create_test_tag() - response = self._request_api_tag_edit_put_view() - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.test_tag.refresh_from_db() - self.assertEqual(self.test_tag.label, TEST_TAG_LABEL) - self.assertEqual(self.test_tag.color, TEST_TAG_COLOR) - self.assertEqual(Tag.objects.all().count(), 1) - - def test_tag_edit_put_view_with_access(self): - self._create_test_tag() - self.grant_access(obj=self.test_tag, permission=permission_tag_edit) - response = self._request_api_tag_edit_put_view() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.test_tag.refresh_from_db() - self.assertEqual(self.test_tag.label, TEST_TAG_LABEL_EDITED) - self.assertEqual(self.test_tag.color, TEST_TAG_COLOR_EDITED) - self.assertEqual(Tag.objects.all().count(), 1) - - def test_tag_list_view_no_access(self): - self._create_test_tag() - response = self._request_api_tag_list_view() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['count'], 0) - - def test_tag_list_view_with_access(self): - self._create_test_tag() - self.grant_access(obj=self.test_tag, permission=permission_tag_view) - response = self._request_api_tag_list_view() - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['count'], 1) +from .mixins import TagAPITestMixin, TagTestMixin class DocumentTagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): @@ -117,13 +31,12 @@ class DocumentTagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): data={'tag_id': self.test_tag.pk} ) - def test_document_tag_attach_view_no_access(self): + def test_document_tag_attach_view_no_permission(self): self._create_test_tag() self.test_document = self.upload_document() response = self._request_api_document_tag_attach_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertTrue(self.test_tag not in self.test_document.tags.all()) - self.assertEqual(Tag.objects.all().count(), 1) def test_document_tag_attach_view_with_document_access(self): self._create_test_tag() @@ -132,7 +45,6 @@ class DocumentTagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): response = self._request_api_document_tag_attach_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertTrue(self.test_tag not in self.test_document.tags.all()) - self.assertEqual(Tag.objects.all().count(), 1) def test_document_tag_attach_view_with_tag_access(self): self._create_test_tag() @@ -141,7 +53,6 @@ class DocumentTagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): response = self._request_api_document_tag_attach_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertTrue(self.test_tag not in self.test_document.tags.all()) - self.assertEqual(Tag.objects.all().count(), 1) def test_document_tag_attach_view_with_full_access(self): self._create_test_tag() @@ -153,7 +64,6 @@ class DocumentTagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): response = self._request_api_document_tag_attach_view() self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertTrue(self.test_tag in self.test_document.tags.all()) - self.assertEqual(Tag.objects.all().count(), 1) def _request_api_document_tag_detail_view(self): return self.get( @@ -205,7 +115,7 @@ class DocumentTagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): kwargs={'document_id': self.test_document.pk} ) - def test_document_tag_list_view_no_access(self): + def test_document_tag_list_view_no_permission(self): self._create_test_tag() self.test_document = self.upload_document() self.test_tag.documents.add(self.test_document) @@ -248,7 +158,7 @@ class DocumentTagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): } ) - def test_document_tag_remove_view_no_access(self): + def test_document_tag_remove_view_no_permission(self): self._create_test_tag() self.test_document = self.upload_document() self.test_tag.documents.add(self.test_document) @@ -291,16 +201,143 @@ class DocumentTagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): self.assertEqual(Tag.objects.all().count(), 1) +class TagAPITestCase(TagAPITestMixin, TagTestMixin, BaseAPITestCase): + def test_tag_create_view_no_permission(self): + response = self._request_api_tag_create_view() + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.assertEqual(Tag.objects.count(), 0) + + def test_tag_create_view_with_permission(self): + self.grant_permission(permission=permission_tag_create) + response = self._request_api_tag_create_view() + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + tag = Tag.objects.first() + self.assertEqual(response.data['id'], tag.pk) + self.assertEqual(response.data['label'], TEST_TAG_LABEL) + self.assertEqual(response.data['color'], TEST_TAG_COLOR) + + self.assertEqual(Tag.objects.count(), 1) + self.assertEqual(tag.label, TEST_TAG_LABEL) + self.assertEqual(tag.color, TEST_TAG_COLOR) + + def test_tag_delete_view_no_permission(self): + self._create_test_tag() + response = self._request_api_tag_delete_view() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertTrue(self.test_tag in Tag.objects.all()) + self.assertEqual(Tag.objects.all().count(), 1) + + def test_tag_delete_view_with_access(self): + self._create_test_tag() + self.grant_access(obj=self.test_tag, permission=permission_tag_delete) + response = self._request_api_tag_delete_view() + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + self.assertEqual(Tag.objects.all().count(), 0) + + def test_tag_edit_patch_view_no_permission(self): + self._create_test_tag() + response = self._request_api_tag_edit_patch_view() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.test_tag.refresh_from_db() + self.assertEqual(self.test_tag.label, TEST_TAG_LABEL) + self.assertEqual(self.test_tag.color, TEST_TAG_COLOR) + self.assertEqual(Tag.objects.all().count(), 1) + + def test_tag_edit_patch_view_with_access(self): + self._create_test_tag() + self.grant_access(obj=self.test_tag, permission=permission_tag_edit) + response = self._request_api_tag_edit_patch_view() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.test_tag.refresh_from_db() + self.assertEqual(self.test_tag.label, TEST_TAG_LABEL_EDITED) + self.assertEqual(self.test_tag.color, TEST_TAG_COLOR_EDITED) + self.assertEqual(Tag.objects.all().count(), 1) + + def test_tag_edit_put_view_no_permission(self): + self._create_test_tag() + response = self._request_api_tag_edit_put_view() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.test_tag.refresh_from_db() + self.assertEqual(self.test_tag.label, TEST_TAG_LABEL) + self.assertEqual(self.test_tag.color, TEST_TAG_COLOR) + self.assertEqual(Tag.objects.all().count(), 1) + + def test_tag_edit_put_view_with_access(self): + self._create_test_tag() + self.grant_access(obj=self.test_tag, permission=permission_tag_edit) + response = self._request_api_tag_edit_put_view() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.test_tag.refresh_from_db() + self.assertEqual(self.test_tag.label, TEST_TAG_LABEL_EDITED) + self.assertEqual(self.test_tag.color, TEST_TAG_COLOR_EDITED) + self.assertEqual(Tag.objects.all().count(), 1) + + def test_tag_list_view_no_permission(self): + self._create_test_tag() + response = self._request_api_tag_list_view() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 0) + + def test_tag_list_view_with_access(self): + self._create_test_tag() + self.grant_access(obj=self.test_tag, permission=permission_tag_view) + response = self._request_api_tag_list_view() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 1) + + class TagDocumentAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): auto_upload_document = False + def _request_api_tag_document_attach_view(self): + return self.post( + viewname='rest_api:tag-document-attach', + kwargs={'tag_id': self.test_tag.pk}, + data={'document_id': self.test_document.pk} + ) + + def test_tag_document_attach_view_no_permission(self): + self._create_test_tag() + self.test_document = self.upload_document() + response = self._request_api_tag_document_attach_view() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertTrue(self.test_tag not in self.test_document.tags.all()) + + def test_tag_document_attach_view_with_document_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.grant_access(obj=self.test_document, permission=permission_tag_attach) + response = self._request_api_tag_document_attach_view() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertTrue(self.test_tag not in self.test_document.tags.all()) + + def test_tag_document_attach_view_with_tag_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.grant_access(obj=self.test_tag, permission=permission_tag_attach) + response = self._request_api_tag_document_attach_view() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(self.test_tag not in self.test_document.tags.all()) + + def test_tag_document_attach_view_with_full_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.grant_access( + obj=self.test_document, permission=permission_tag_attach + ) + self.grant_access(obj=self.test_tag, permission=permission_tag_attach) + response = self._request_api_tag_document_attach_view() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(self.test_tag in self.test_document.tags.all()) + def _request_api_tag_document_list_view(self): return self.get( viewname='rest_api:tag-document-list', kwargs={'tag_id': self.test_tag.pk} ) - def test_tag_document_list_view_no_access(self): + def test_tag_document_list_view_no_permission(self): self._create_test_tag() self.test_document = self.upload_document() self.test_tag.documents.add(self.test_document) @@ -340,3 +377,52 @@ class TagDocumentAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): response.data['results'][0]['uuid'], force_text(self.test_document.uuid) ) + + def _request_api_tag_document_remove_view(self): + return self.post( + viewname='rest_api:tag-document-remove', kwargs={ + 'tag_id': self.test_tag.pk + }, data={'document_id': self.test_document.pk} + ) + + def test_tag_document_remove_view_no_permission(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + response = self._request_api_tag_document_remove_view() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertTrue(self.test_tag in self.test_document.tags.all()) + self.assertEqual(Tag.objects.all().count(), 1) + + def test_tag_document_remove_view_with_document_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access(obj=self.test_document, permission=permission_tag_remove) + response = self._request_api_tag_document_remove_view() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + self.assertTrue(self.test_tag in self.test_document.tags.all()) + self.assertEqual(Tag.objects.all().count(), 1) + + def test_tag_document_remove_view_with_tag_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access(obj=self.test_tag, permission=permission_tag_remove) + response = self._request_api_tag_document_remove_view() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertTrue(self.test_tag in self.test_document.tags.all()) + self.assertEqual(Tag.objects.all().count(), 1) + + def test_tag_document_remove_view_with_full_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access( + obj=self.test_document, permission=permission_tag_remove + ) + self.grant_access(obj=self.test_tag, permission=permission_tag_remove) + response = self._request_api_tag_document_remove_view() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertFalse(self.test_tag not in self.test_document.tags.all()) + self.assertEqual(Tag.objects.all().count(), 1) diff --git a/mayan/apps/tags/urls.py b/mayan/apps/tags/urls.py index 66ac0b2659..89df42fb9e 100644 --- a/mayan/apps/tags/urls.py +++ b/mayan/apps/tags/urls.py @@ -2,10 +2,6 @@ from __future__ import unicode_literals from django.conf.urls import url -#from .api_views import ( -# APIDocumentTagView, APIDocumentTagListView, APITagDocumentListView, -# APITagListView, APITagView -#) from .api_views import DocumentTagViewSet, TagViewSet from .views import ( @@ -61,31 +57,10 @@ urlpatterns = [ ) ] - api_router_entries = ( {'prefix': r'tags', 'viewset': TagViewSet, 'basename': 'tag'}, { 'prefix': r'documents/(?P\d+)/tags', - 'viewset': DocumentTagViewSet, 'basename': 'document_tag' + 'viewset': DocumentTagViewSet, 'basename': 'document-tag' }, ) - -""" - url( - regex=r'^tags/(?P\d+)/documents/$', - name='tag-document-list', view=APITagDocumentListView.as_view(), - ), - url( - regex=r'^tags/(?P\d+)/$', name='tag-detail', - view=APITagView.as_view() - ), - url(regex=r'^tags/$', name='tag-list', view=APITagListView.as_view()), - url( - regex=r'^documents/(?P\d+)/tags/$', - name='document-tag-list', view=APIDocumentTagListView.as_view() - ), - url( - regex=r'^documents/(?P\d+)/tags/(?P[0-9]+)/$', - name='document-tag-detail', view=APIDocumentTagView.as_view() - ), -"""