diff --git a/docs/releases/2.2.rst b/docs/releases/2.2.rst index c94542332f..81d969ebc4 100644 --- a/docs/releases/2.2.rst +++ b/docs/releases/2.2.rst @@ -9,6 +9,27 @@ What's new API changes ----------- +Refactor of the metadata API URLs to use the resource/sub resource paradigm. + +Before: + +/api/metadata/metadata_types/ +/api/metadata/metadata_types/{pk}/ +/api/metadata/document/metadata/{pk}/ +/api/metadata/document/{pk}/metadata/ +/api/metadata/document_type/{document_type_pk}/metadata_types/optional/ +/api/metadata/document_type/{document_type_pk}/metadata_types/required/ + +After: + +/api/metadata/metadata_types/ +/api/metadata/metadata_types/{metadata_type_pk}/ +/api/metadata/document_types/{document_type_pk}/metadata_types/ +/api/metadata/document_types/{document_type_pk}/metadata_types/{metadata_type_pk}/ +/api/metadata/documents/{document_pk}/metadata/ +/api/metadata/documents/{document_pk}/metadata/{metadata_pk}/ + + Document API URLs updated to use the resource/sub resource paradigm. Before: @@ -95,7 +116,7 @@ Other changes - Add custom test runner replacing the custom management command runtests. The 'test-all' Makefile target that called the `runtests` command has been removed too. -- Testing for orphaned temporary files and orphaned file handles is now optional and +- Testing for orphaned temporary files and orphaned file handles is now optional and controlled by the COMMON_TEST_FILE_HANDLES and COMMON_TEST_FILE_HANDLES settings. Removals diff --git a/mayan/apps/metadata/api_views.py b/mayan/apps/metadata/api_views.py index b5b58bf23b..4d16d62d8f 100644 --- a/mayan/apps/metadata/api_views.py +++ b/mayan/apps/metadata/api_views.py @@ -2,8 +2,7 @@ from __future__ import absolute_import, unicode_literals from django.shortcuts import get_object_or_404 -from rest_framework import generics, status, views -from rest_framework.response import Response +from rest_framework import generics from acls.models import AccessControlList from documents.models import Document, DocumentType @@ -13,7 +12,7 @@ from documents.permissions import ( from rest_api.filters import MayanObjectPermissionsFilter from rest_api.permissions import MayanPermission -from .models import DocumentMetadata, DocumentTypeMetadataType, MetadataType +from .models import MetadataType from .permissions import ( permission_metadata_document_add, permission_metadata_document_remove, permission_metadata_document_edit, permission_metadata_document_view, @@ -21,283 +20,315 @@ from .permissions import ( permission_metadata_type_edit, permission_metadata_type_view ) from .serializers import ( - DocumentMetadataSerializer, DocumentNewMetadataSerializer, - DocumentTypeNewMetadataTypeSerializer, MetadataTypeSerializer, - DocumentTypeMetadataTypeSerializer + DocumentMetadataSerializer, DocumentTypeMetadataTypeSerializer, + MetadataTypeSerializer, NewDocumentMetadataSerializer, + NewDocumentTypeMetadataTypeSerializer, + WritableDocumentTypeMetadataTypeSerializer ) -class APIMetadataTypeListView(generics.ListCreateAPIView): - serializer_class = MetadataTypeSerializer - queryset = MetadataType.objects.all() +class APIDocumentMetadataListView(generics.ListCreateAPIView): + def get(self, *args, **kwargs): + """ + Returns a list of selected document's metadata types and values. + """ - permission_classes = (MayanPermission,) + return super(APIDocumentMetadataListView, self).get(*args, **kwargs) + + def get_document(self): + if self.request.method == 'GET': + permission_required = permission_metadata_document_view + else: + permission_required = permission_metadata_document_add + + document = get_object_or_404( + Document, pk=self.kwargs['document_pk'] + ) + + AccessControlList.objects.check_access( + permissions=permission_required, user=self.request.user, + obj=document + ) + + return document + + def get_queryset(self): + return self.get_document().metadata.all() + + def get_serializer_class(self): + if self.request.method == 'GET': + return DocumentMetadataSerializer + else: + return NewDocumentMetadataSerializer + + def get_serializer_context(self): + """ + Extra context provided to the serializer class. + """ + + return { + 'document': self.get_document(), + 'format': self.format_kwarg, + 'request': self.request, + 'view': self + } + + def post(self, *args, **kwargs): + """ + Add an existing metadata type and value to the selected document. + """ + + return super(APIDocumentMetadataListView, self).post(*args, **kwargs) + + +class APIDocumentMetadataView(generics.RetrieveUpdateDestroyAPIView): + lookup_url_kwarg = 'metadata_pk' + + def delete(self, *args, **kwargs): + """ + Remove this metadata entry from the selected document. + """ + + return super(APIDocumentMetadataView, self).delete(*args, **kwargs) + + def get(self, *args, **kwargs): + """ + Return the details of the selected document metadata type and value. + """ + + return super(APIDocumentMetadataView, self).get(*args, **kwargs) + + def get_document(self): + if self.request.method == 'GET': + permission_required = permission_metadata_document_view + elif self.request.method == 'PUT': + permission_required = permission_metadata_document_edit + elif self.request.method == 'PATCH': + permission_required = permission_metadata_document_edit + elif self.request.method == 'DELETE': + permission_required = permission_metadata_document_remove + + document = get_object_or_404( + Document, pk=self.kwargs['document_pk'] + ) + + AccessControlList.objects.check_access( + permissions=permission_required, user=self.request.user, + obj=document + ) + + return document + + def get_queryset(self): + return self.get_document().metadata.all() + + def get_serializer_class(self): + if self.request.method == 'GET': + return DocumentMetadataSerializer + else: + return DocumentMetadataSerializer + + def patch(self, *args, **kwargs): + """ + Edit the selected document metadata type and value. + """ + + return super(APIDocumentMetadataView, self).patch(*args, **kwargs) + + def put(self, *args, **kwargs): + """ + Edit the selected document metadata type and value. + """ + + return super(APIDocumentMetadataView, self).put(*args, **kwargs) + + +class APIMetadataTypeListView(generics.ListCreateAPIView): filter_backends = (MayanObjectPermissionsFilter,) mayan_object_permissions = {'GET': (permission_metadata_type_view,)} mayan_view_permissions = {'POST': (permission_metadata_type_create,)} + permission_classes = (MayanPermission,) + queryset = MetadataType.objects.all() + serializer_class = MetadataTypeSerializer def get(self, *args, **kwargs): """ Returns a list of all the metadata types. """ + return super(APIMetadataTypeListView, self).get(*args, **kwargs) def post(self, *args, **kwargs): """ Create a new metadata type. """ + return super(APIMetadataTypeListView, self).post(*args, **kwargs) class APIMetadataTypeView(generics.RetrieveUpdateDestroyAPIView): - serializer_class = MetadataTypeSerializer - queryset = MetadataType.objects.all() - - permission_classes = (MayanPermission,) + lookup_url_kwarg = 'metadata_type_pk' mayan_object_permissions = { 'GET': (permission_metadata_type_view,), 'PUT': (permission_metadata_type_edit,), 'PATCH': (permission_metadata_type_edit,), 'DELETE': (permission_metadata_type_delete,) } + permission_classes = (MayanPermission,) + queryset = MetadataType.objects.all() + serializer_class = MetadataTypeSerializer def delete(self, *args, **kwargs): """ Delete the selected metadata type. """ + return super(APIMetadataTypeView, self).delete(*args, **kwargs) def get(self, *args, **kwargs): """ Return the details of the selected metadata type. """ + return super(APIMetadataTypeView, self).get(*args, **kwargs) def patch(self, *args, **kwargs): """ Edit the selected metadata type. """ + return super(APIMetadataTypeView, self).patch(*args, **kwargs) def put(self, *args, **kwargs): """ Edit the selected metadata type. """ + return super(APIMetadataTypeView, self).put(*args, **kwargs) -class APIDocumentMetadataListView(generics.ListCreateAPIView): - permission_classes = (MayanPermission,) - - def get_document(self): - return get_object_or_404(Document, pk=self.kwargs['pk']) - - def get_queryset(self): - document = self.get_document() - - if self.request.method == 'GET': - # Make sure the use has the permission to see the metadata for - # this document - AccessControlList.objects.check_access( - permissions=permission_metadata_document_view, - user=self.request.user, obj=document - ) - - return document.metadata.all() - elif self.request.method == 'POST': - # Make sure the use has the permission to add metadata to this - # document - AccessControlList.objects.check_access( - permissions=permission_metadata_document_add, - user=self.request.user, obj=document - ) - - return document.metadata.all() - - def get_serializer_class(self): - if self.request.method == 'GET': - return DocumentMetadataSerializer - elif self.request.method == 'POST': - return DocumentNewMetadataSerializer +class APIDocumentTypeMetadataTypeListView(generics.ListCreateAPIView): + lookup_url_kwarg = 'metadata_type_pk' def get(self, *args, **kwargs): """ - Returns a list of selected document's metadata types and values. + Returns a list of selected document type's metadata types. """ - return super(APIDocumentMetadataListView, self).get(*args, **kwargs) - def perform_create(self, serializer): - serializer.document = self.get_document() - serializer.save() + return super( + APIDocumentTypeMetadataTypeListView, self + ).get(*args, **kwargs) - def post(self, *args, **kwargs): - """ - Add an existing metadata type and value to the selected document. - """ - return super(APIDocumentMetadataListView, self).post(*args, **kwargs) + def get_document_type(self): + if self.request.method == 'GET': + permission_required = permission_document_type_view + else: + permission_required = permission_document_type_edit - -class APIDocumentMetadataView(generics.RetrieveUpdateDestroyAPIView): - serializer_class = DocumentMetadataSerializer - queryset = DocumentMetadata.objects.all() - - permission_classes = (MayanPermission,) - mayan_object_permissions = { - 'GET': (permission_metadata_document_view,), - 'PUT': (permission_metadata_document_edit,), - 'PATCH': (permission_metadata_document_edit,), - 'DELETE': (permission_metadata_document_remove,) - } - - def delete(self, *args, **kwargs): - """ - Delete the selected document metadata type and value. - """ - try: - return super( - APIDocumentMetadataView, self - ).delete(*args, **kwargs) - except Exception as exception: - return Response( - status=status.HTTP_400_BAD_REQUEST, data={ - 'non_fields_errors': unicode(exception) - } - ) - - def get(self, *args, **kwargs): - """ - Return the details of the selected document metadata type and value. - """ - return super(APIDocumentMetadataView, self).get(*args, **kwargs) - - def patch(self, *args, **kwargs): - """ - Edit the selected document metadata type and value. - """ - try: - return super( - APIDocumentMetadataView, self - ).patch(*args, **kwargs) - except Exception as exception: - return Response( - status=status.HTTP_400_BAD_REQUEST, data={ - 'non_fields_errors': unicode(exception) - } - ) - - def put(self, *args, **kwargs): - """ - Edit the selected document metadata type and value. - """ - try: - return super(APIDocumentMetadataView, self).put(*args, **kwargs) - except Exception as exception: - return Response( - status=status.HTTP_400_BAD_REQUEST, data={ - 'non_fields_errors': unicode(exception) - } - ) - - -class APIDocumentTypeMetadataTypeOptionalListView(generics.ListCreateAPIView): - permission_classes = (MayanPermission,) - - mayan_view_permissions = {'POST': (permission_document_type_edit,)} - - required_metadata = False - - def get_queryset(self): document_type = get_object_or_404( DocumentType, pk=self.kwargs['document_type_pk'] ) + AccessControlList.objects.check_access( - permissions=permission_document_type_view, user=self.request.user, + permissions=permission_required, user=self.request.user, obj=document_type ) - return document_type.metadata.filter(required=self.required_metadata) + return document_type - def get(self, *args, **kwargs): - """ - Returns a list of selected document type's optional metadata types. - """ - return super( - APIDocumentTypeMetadataTypeOptionalListView, self - ).get(*args, **kwargs) + def get_queryset(self): + return self.get_document_type().metadata.all() def get_serializer_class(self): if self.request.method == 'GET': return DocumentTypeMetadataTypeSerializer - elif self.request.method == 'POST': - return DocumentTypeNewMetadataTypeSerializer + else: + return NewDocumentTypeMetadataTypeSerializer - def post(self, request, *args, **kwargs): + def get_serializer_context(self): """ - Add an optional metadata type to a document type. + Extra context provided to the serializer class. """ + + return { + 'document_type': self.get_document_type(), + 'format': self.format_kwarg, + 'request': self.request, + 'view': self + } + + def post(self, *args, **kwargs): + """ + Add a metadata type to the selected document type. + """ + + return super( + APIDocumentTypeMetadataTypeListView, self + ).post(*args, **kwargs) + + +class APIDocumentTypeMetadataTypeView(generics.RetrieveUpdateDestroyAPIView): + lookup_url_kwarg = 'metadata_type_pk' + serializer_class = DocumentTypeMetadataTypeSerializer + + def delete(self, *args, **kwargs): + """ + Remove a metadata type from a document type. + """ + + return super( + APIDocumentTypeMetadataTypeView, self + ).delete(*args, **kwargs) + + def get(self, *args, **kwargs): + """ + Retrieve the details of a document type metadata type. + """ + + return super( + APIDocumentTypeMetadataTypeView, self + ).get(*args, **kwargs) + + def get_document_type(self): + if self.request.method == 'GET': + permission_required = permission_document_type_view + else: + permission_required = permission_document_type_edit + document_type = get_object_or_404( DocumentType, pk=self.kwargs['document_type_pk'] ) AccessControlList.objects.check_access( - permissions=permission_document_type_edit, user=self.request.user, + permissions=permission_required, user=self.request.user, obj=document_type ) - serializer = self.get_serializer(data=self.request.data) + return document_type - if serializer.is_valid(): - metadata_type = get_object_or_404( - MetadataType, pk=serializer.data['metadata_type_pk'] - ) - document_type_metadata_type = document_type.metadata.create( - metadata_type=metadata_type, required=self.required_metadata - ) - return Response( - status=status.HTTP_201_CREATED, - data={ - 'pk': document_type_metadata_type.pk - } - ) + def get_queryset(self): + return self.get_document_type().metadata.all() + + def get_serializer_class(self): + if self.request.method == 'GET': + return DocumentTypeMetadataTypeSerializer else: - return Response(status=status.HTTP_400_BAD_REQUEST) + return WritableDocumentTypeMetadataTypeSerializer - -class APIDocumentTypeMetadataTypeRequiredListView(APIDocumentTypeMetadataTypeOptionalListView): - required_metadata = True - - def get(self, *args, **kwargs): + def patch(self, *args, **kwargs): """ - Returns a list of the selected document type's required metadata - types. + Edit the selected document type metadata type. """ + return super( - APIDocumentTypeMetadataTypeRequiredListView, self - ).get(*args, **kwargs) + APIDocumentTypeMetadataTypeView, self + ).patch(*args, **kwargs) - def post(self, request, *args, **kwargs): + def put(self, *args, **kwargs): """ - Add a required metadata type to a document type. + Edit the selected document type metadata type. """ + return super( - APIDocumentTypeMetadataTypeRequiredListView, self - ).post(request, *args, **kwargs) - - -class APIDocumentTypeMetadataTypeView(views.APIView): - def delete(self, request, *args, **kwargs): - """ - Remove a metadata type from a document type. - """ - - document_type_metadata_type = get_object_or_404( - DocumentTypeMetadataType, pk=self.kwargs['pk'] - ) - - AccessControlList.objects.check_access( - permissions=permission_document_type_edit, user=self.request.user, - obj=document_type_metadata_type.document_type - ) - - document_type_metadata_type.delete() - return Response(status=status.HTTP_204_NO_CONTENT) + APIDocumentTypeMetadataTypeView, self + ).put(*args, **kwargs) diff --git a/mayan/apps/metadata/serializers.py b/mayan/apps/metadata/serializers.py index 36a1abdd27..0afcf95540 100644 --- a/mayan/apps/metadata/serializers.py +++ b/mayan/apps/metadata/serializers.py @@ -1,71 +1,159 @@ from __future__ import unicode_literals -from django.db import IntegrityError +from django.core.exceptions import ValidationError as DjangoValidationError from django.utils.translation import ugettext_lazy as _ from rest_framework import serializers from rest_framework.exceptions import ValidationError +from rest_framework.reverse import reverse -from .models import DocumentMetadata, MetadataType, DocumentTypeMetadataType +from documents.serializers import DocumentSerializer, DocumentTypeSerializer + +from .models import DocumentMetadata, DocumentTypeMetadataType, MetadataType -class MetadataTypeSerializer(serializers.ModelSerializer): +class MetadataTypeSerializer(serializers.HyperlinkedModelSerializer): class Meta: + extra_kwargs = { + 'url': { + 'lookup_field': 'pk', 'lookup_url_kwarg': 'metadata_type_pk', + 'view_name': 'rest_api:metadatatype-detail' + }, + } fields = ( - 'id', 'name', 'label', 'default', 'lookup', 'parser', 'validation' + 'default', 'id', 'label', 'lookup', 'name', 'parser', 'url', + 'validation' ) model = MetadataType -class DocumentMetadataSerializer(serializers.ModelSerializer): - document = serializers.PrimaryKeyRelatedField(read_only=True) +class DocumentTypeMetadataTypeSerializer(serializers.HyperlinkedModelSerializer): + document_type = DocumentTypeSerializer(read_only=True) + metadata_type = MetadataTypeSerializer(read_only=True) + url = serializers.SerializerMethodField() class Meta: - fields = ('document', 'id', 'metadata_type', 'value',) - model = DocumentMetadata - read_only_fields = ('metadata_type',) - - -class DocumentTypeMetadataTypeSerializer(serializers.ModelSerializer): - class Meta: - fields = ('metadata_type',) + fields = ('document_type', 'id', 'metadata_type', 'required', 'url') model = DocumentTypeMetadataType + def get_url(self, instance): + return reverse( + 'rest_api:documenttypemetadatatype-detail', args=( + instance.document_type.pk, instance.pk + ), request=self.context['request'], format=self.context['format'] + ) -class DocumentNewMetadataSerializer(serializers.Serializer): + +class NewDocumentTypeMetadataTypeSerializer(serializers.ModelSerializer): metadata_type_pk = serializers.IntegerField( help_text=_('Primary key of the metadata type to be added.'), write_only=True ) + url = serializers.SerializerMethodField() - metadata_type = MetadataTypeSerializer(read_only=True) - - pk = serializers.IntegerField( - help_text=_('Primary key of the document metadata type.'), - read_only=True - ) - - value = serializers.CharField( - max_length=255, - help_text=_('Value of the corresponding metadata type instance.') - ) - - def create(self, validated_data): - metadata_type = MetadataType.objects.get( - pk=validated_data['metadata_type_pk'] + class Meta: + fields = ( + 'id', 'metadata_type_pk', 'required', 'url' ) + model = DocumentTypeMetadataType + + def get_url(self, instance): + return reverse( + 'rest_api:documenttypemetadatatype-detail', args=( + instance.document_type.pk, instance.pk + ), request=self.context['request'], format=self.context['format'] + ) + + def validate(self, attrs): + attrs['document_type'] = self.context['document_type'] + attrs['metadata_type'] = MetadataType.objects.get( + pk=attrs.pop('metadata_type_pk') + ) + + instance = DocumentTypeMetadataType(**attrs) try: - instance = self.document.metadata.create( - metadata_type=metadata_type, value=validated_data['value'] - ) - return instance - except IntegrityError: - detail = 'Metadata type with pk {} is already defined for Document with pk {}'.format(metadata_type.pk, - self.document.pk) - raise ValidationError(detail) + instance.full_clean() + except DjangoValidationError as exception: + raise ValidationError(exception) + + return attrs -class DocumentTypeNewMetadataTypeSerializer(serializers.Serializer): +class WritableDocumentTypeMetadataTypeSerializer(serializers.ModelSerializer): + url = serializers.SerializerMethodField() + + class Meta: + fields = ( + 'id', 'required', 'url' + ) + model = DocumentTypeMetadataType + + def get_url(self, instance): + return reverse( + 'rest_api:documenttypemetadatatype-detail', args=( + instance.document_type.pk, instance.pk + ), request=self.context['request'], format=self.context['format'] + ) + + +class DocumentMetadataSerializer(serializers.HyperlinkedModelSerializer): + document = DocumentSerializer(read_only=True) + metadata_type = MetadataTypeSerializer(read_only=True) + url = serializers.SerializerMethodField() + + class Meta: + fields = ('document', 'id', 'metadata_type', 'url', 'value') + model = DocumentMetadata + read_only_fields = ('document', 'metadata_type',) + + def get_url(self, instance): + return reverse( + 'rest_api:documentmetadata-detail', args=( + instance.document.pk, instance.pk + ), request=self.context['request'], format=self.context['format'] + ) + + def validate(self, attrs): + self.instance.value = attrs['value'] + + try: + self.instance.full_clean() + except DjangoValidationError as exception: + raise ValidationError(exception) + + return attrs + + +class NewDocumentMetadataSerializer(serializers.ModelSerializer): metadata_type_pk = serializers.IntegerField( - help_text=_('Primary key of the metadata type to be added.') + help_text=_( + 'Primary key of the metadata type to be added to the document.' + ), + write_only=True ) + url = serializers.SerializerMethodField() + + class Meta: + fields = ('id', 'metadata_type_pk', 'url', 'value') + model = DocumentMetadata + + def get_url(self, instance): + return reverse( + 'rest_api:documentmetadata-detail', args=( + instance.document.pk, instance.pk + ), request=self.context['request'], format=self.context['format'] + ) + + def validate(self, attrs): + attrs['document'] = self.context['document'] + attrs['metadata_type'] = MetadataType.objects.get( + pk=attrs.pop('metadata_type_pk') + ) + + instance = DocumentMetadata(**attrs) + try: + instance.full_clean() + except DjangoValidationError as exception: + raise ValidationError(exception) + + return attrs diff --git a/mayan/apps/metadata/tests/test_api.py b/mayan/apps/metadata/tests/test_api.py index 93312509ec..c6bf065d3d 100644 --- a/mayan/apps/metadata/tests/test_api.py +++ b/mayan/apps/metadata/tests/test_api.py @@ -11,7 +11,7 @@ from user_management.tests.literals import ( TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME ) -from ..models import DocumentMetadata, DocumentTypeMetadataType, MetadataType +from ..models import DocumentTypeMetadataType, MetadataType from .literals import ( TEST_METADATA_TYPE_LABEL, TEST_METADATA_TYPE_LABEL_2, @@ -84,7 +84,7 @@ class MetadataTypeAPITestCase(BaseAPITestCase): response.data['label'], TEST_METADATA_TYPE_LABEL ) - def test_metadata_type_edit_via_patch_view(self): + def test_metadata_type_patch_view(self): self._create_metadata_type() response = self.client.patch( @@ -104,7 +104,7 @@ class MetadataTypeAPITestCase(BaseAPITestCase): self.assertEqual(self.metadata_type.label, TEST_METADATA_TYPE_LABEL_2) self.assertEqual(self.metadata_type.name, TEST_METADATA_TYPE_NAME_2) - def test_metadata_type_edit_via_put_view(self): + def test_metadata_type_put_view(self): self._create_metadata_type() response = self.client.put( @@ -160,55 +160,51 @@ class DocumentTypeMetadataTypeAPITestCase(BaseAPITestCase): self.document_type.delete() super(DocumentTypeMetadataTypeAPITestCase, self).tearDown() - def test_document_type_metadata_type_optional_create(self): + def _create_document_type_metadata_type(self): + self.document_type_metadata_type = self.document_type.metadata.create( + metadata_type=self.metadata_type, required=False + ) + + def test_document_type_metadata_type_create_view(self): response = self.client.post( reverse( - 'rest_api:documenttypeoptionalmetadatatype-list', + 'rest_api:documenttypemetadatatype-list', args=(self.document_type.pk,) - ), data={'metadata_type_pk': self.metadata_type.pk} + ), data={ + 'metadata_type_pk': self.metadata_type.pk, 'required': False + } ) self.assertEqual(response.status_code, 201) - document_type_metadata_type = DocumentTypeMetadataType.objects.filter( - document_type=self.document_type, required=False - ).first() + document_type_metadata_type = DocumentTypeMetadataType.objects.first() - self.assertEqual(response.data['pk'], document_type_metadata_type.pk) + self.assertEqual(response.data['id'], document_type_metadata_type.pk) - self.assertEqual( - document_type_metadata_type.metadata_type, self.metadata_type - ) + def test_document_type_metadata_type_create_dupicate_view(self): + self._create_document_type_metadata_type() - def test_document_type_metadata_type_required_create(self): response = self.client.post( reverse( - 'rest_api:documenttyperequiredmetadatatype-list', + 'rest_api:documenttypemetadatatype-list', args=(self.document_type.pk,) - ), data={'metadata_type_pk': self.metadata_type.pk} + ), data={ + 'metadata_type_pk': self.metadata_type.pk, 'required': False + } ) - self.assertEqual(response.status_code, 201) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.data.keys()[0], 'non_field_errors') - document_type_metadata_type = DocumentTypeMetadataType.objects.filter( - document_type=self.document_type, required=True - ).first() - - self.assertEqual(response.data['pk'], document_type_metadata_type.pk) - - self.assertEqual( - document_type_metadata_type.metadata_type, self.metadata_type - ) - - def test_document_type_metadata_type_delete(self): - document_type_metadata_type = self.document_type.metadata.create( - metadata_type=self.metadata_type, required=True - ) + def test_document_type_metadata_type_delete_view(self): + self._create_document_type_metadata_type() response = self.client.delete( reverse( 'rest_api:documenttypemetadatatype-detail', - args=(document_type_metadata_type.pk,) + args=( + self.document_type.pk, self.document_type_metadata_type.pk, + ), ), ) @@ -216,6 +212,63 @@ class DocumentTypeMetadataTypeAPITestCase(BaseAPITestCase): self.assertEqual(self.document_type.metadata.all().count(), 0) + def test_document_type_metadata_type_list_view(self): + self._create_document_type_metadata_type() + + response = self.client.get( + reverse( + 'rest_api:documenttypemetadatatype-list', + args=( + self.document_type.pk, + ), + ), + ) + + self.assertEqual(response.status_code, 200) + + self.assertEqual( + response.data['results'][0]['id'], + self.document_type_metadata_type.pk + ) + + def test_document_type_metadata_type_patch_view(self): + self._create_document_type_metadata_type() + + response = self.client.patch( + reverse( + 'rest_api:documenttypemetadatatype-detail', + args=( + self.document_type.pk, self.document_type_metadata_type.pk, + ) + ), data={ + 'required': True + } + ) + + document_type_metadata_type = DocumentTypeMetadataType.objects.first() + + self.assertEqual(response.status_code, 200) + self.assertEqual(document_type_metadata_type.required, True) + + def test_document_type_metadata_type_put_view(self): + self._create_document_type_metadata_type() + + response = self.client.put( + reverse( + 'rest_api:documenttypemetadatatype-detail', + args=( + self.document_type.pk, self.document_type_metadata_type.pk, + ) + ), data={ + 'required': True + } + ) + + document_type_metadata_type = DocumentTypeMetadataType.objects.first() + + self.assertEqual(response.status_code, 200) + self.assertEqual(document_type_metadata_type.required, True) + class DocumentMetadataAPITestCase(BaseAPITestCase): @override_settings(OCR_AUTO_OCR=False) @@ -253,7 +306,12 @@ class DocumentMetadataAPITestCase(BaseAPITestCase): self.document_type.delete() super(DocumentMetadataAPITestCase, self).tearDown() - def test_document_metadata_create(self): + def _create_document_metadata(self): + self.document_metadata = self.document.metadata.create( + metadata_type=self.metadata_type, value=TEST_METADATA_VALUE + ) + + def test_document_metadata_create_view(self): response = self.client.post( reverse( 'rest_api:documentmetadata-list', @@ -264,22 +322,64 @@ class DocumentMetadataAPITestCase(BaseAPITestCase): } ) - document_metadata = DocumentMetadata.objects.get( - document=self.document - ) + document_metadata = self.document.metadata.first() self.assertEqual(response.status_code, 201) - self.assertEqual(response.data['pk'], document_metadata.pk) + self.assertEqual(response.data['id'], document_metadata.pk) self.assertEqual(document_metadata.metadata_type, self.metadata_type) self.assertEqual(document_metadata.value, TEST_METADATA_VALUE) - def test_document_metadata_list(self): - document_metadata = self.document.metadata.create( - metadata_type=self.metadata_type, value=TEST_METADATA_VALUE + def test_document_metadata_create_duplicate_view(self): + self._create_document_metadata() + + response = self.client.post( + reverse( + 'rest_api:documentmetadata-list', + args=(self.document.pk,) + ), data={ + 'metadata_type_pk': self.metadata_type.pk, + 'value': TEST_METADATA_VALUE + } ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.data.keys()[0], 'non_field_errors') + + def test_document_metadata_create_invalid_lookup_value_view(self): + self.metadata_type.lookup = 'invalid,lookup,values,on,purpose' + self.metadata_type.save() + + response = self.client.post( + reverse( + 'rest_api:documentmetadata-list', + args=(self.document.pk,) + ), data={ + 'metadata_type_pk': self.metadata_type.pk, + 'value': TEST_METADATA_VALUE + } + ) + self.assertEqual(response.status_code, 400) + self.assertEqual(response.data.keys()[0], 'non_field_errors') + + def test_document_metadata_delete_view(self): + self._create_document_metadata() + + response = self.client.delete( + reverse( + 'rest_api:documentmetadata-detail', + args=(self.document.pk, self.document_metadata.pk,) + ) + ) + + self.assertEqual(response.status_code, 204) + + self.assertEqual(self.document.metadata.all().count(), 0) + + def test_document_metadata_list_view(self): + self._create_document_metadata() + response = self.client.get( reverse( 'rest_api:documentmetadata-list', args=(self.document.pk,) @@ -289,56 +389,49 @@ class DocumentMetadataAPITestCase(BaseAPITestCase): self.assertEqual(response.status_code, 200) self.assertEqual( - response.data['results'][0]['document'], self.document.pk + response.data['results'][0]['document']['id'], self.document.pk ) self.assertEqual( - response.data['results'][0]['metadata_type'], self.metadata_type.pk + response.data['results'][0]['metadata_type']['id'], + self.metadata_type.pk ) self.assertEqual( response.data['results'][0]['value'], TEST_METADATA_VALUE ) self.assertEqual( - response.data['results'][0]['id'], document_metadata.pk + response.data['results'][0]['id'], self.document_metadata.pk ) - def test_document_metadata_edit(self): - document_metadata = self.document.metadata.create( - metadata_type=self.metadata_type, value=TEST_METADATA_VALUE - ) + def test_document_metadata_patch_view(self): + self._create_document_metadata() - response = self.client.put( + response = self.client.patch( reverse( 'rest_api:documentmetadata-detail', - args=(document_metadata.pk,) + args=(self.document.pk, self.document_metadata.pk,) ), data={ 'value': TEST_METADATA_VALUE_EDITED } ) self.assertEqual(response.status_code, 200) - - self.assertEqual( - response.data['document'], self.document.pk - ) - self.assertEqual( - response.data['metadata_type'], self.metadata_type.pk - ) self.assertEqual( response.data['value'], TEST_METADATA_VALUE_EDITED ) - def test_document_metadata_delete(self): - document_metadata = self.document.metadata.create( - metadata_type=self.metadata_type, value=TEST_METADATA_VALUE - ) + def test_document_metadata_put_view(self): + self._create_document_metadata() - response = self.client.delete( + response = self.client.put( reverse( 'rest_api:documentmetadata-detail', - args=(document_metadata.pk,) - ) + args=(self.document.pk, self.document_metadata.pk,) + ), data={ + 'value': TEST_METADATA_VALUE_EDITED + } ) - self.assertEqual(response.status_code, 204) - - self.assertEqual(self.document.metadata.all().count(), 0) + self.assertEqual(response.status_code, 200) + self.assertEqual( + response.data['value'], TEST_METADATA_VALUE_EDITED + ) diff --git a/mayan/apps/metadata/urls.py b/mayan/apps/metadata/urls.py index e317927bd1..658368c25e 100644 --- a/mayan/apps/metadata/urls.py +++ b/mayan/apps/metadata/urls.py @@ -4,10 +4,8 @@ from django.conf.urls import url from .api_views import ( APIDocumentMetadataListView, APIDocumentMetadataView, - APIDocumentTypeMetadataTypeOptionalListView, - APIDocumentTypeMetadataTypeRequiredListView, - APIDocumentTypeMetadataTypeView, APIMetadataTypeListView, - APIMetadataTypeView + APIDocumentTypeMetadataTypeListView, APIDocumentTypeMetadataTypeView, + APIMetadataTypeListView, APIMetadataTypeView ) from .views import ( DocumentMetadataAddView, DocumentMetadataEditView, @@ -82,30 +80,25 @@ api_urls = [ name='metadatatype-list' ), url( - r'^metadata_types/(?P[0-9]+)/$', APIMetadataTypeView.as_view(), - name='metadatatype-detail' + r'^metadata_types/(?P\d+)/$', + APIMetadataTypeView.as_view(), name='metadatatype-detail' ), url( - r'^document/metadata/(?P[0-9]+)/$', - APIDocumentMetadataView.as_view(), name='documentmetadata-detail' + r'^document_types/(?P\d+)/metadata_types/$', + APIDocumentTypeMetadataTypeListView.as_view(), + name='documenttypemetadatatype-list' ), url( - r'^document/(?P\d+)/metadata/$', - APIDocumentMetadataListView.as_view(), name='documentmetadata-list' - ), - url( - r'^document_type/(?P[0-9]+)/metadata_types/optional/$', - APIDocumentTypeMetadataTypeOptionalListView.as_view(), - name='documenttypeoptionalmetadatatype-list' - ), - url( - r'^document_type/(?P[0-9]+)/metadata_types/required/$', - APIDocumentTypeMetadataTypeRequiredListView.as_view(), - name='documenttyperequiredmetadatatype-list' - ), - url( - r'^document_type_metadata_type/(?P\d+)/$', + r'^document_types/(?P\d+)/metadata_types/(?P\d+)/$', APIDocumentTypeMetadataTypeView.as_view(), name='documenttypemetadatatype-detail' ), + url( + r'^documents/(?P\d+)/metadata/$', + APIDocumentMetadataListView.as_view(), name='documentmetadata-list' + ), + url( + r'^documents/(?P\d+)/metadata/(?P\d+)/$', + APIDocumentMetadataView.as_view(), name='documentmetadata-detail' + ), ]