Merge branch 'feature/metadata_api' into development

This commit is contained in:
Roberto Rosario
2017-02-23 02:14:59 -04:00
5 changed files with 545 additions and 319 deletions

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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
)

View File

@@ -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<pk>[0-9]+)/$', APIMetadataTypeView.as_view(),
name='metadatatype-detail'
r'^metadata_types/(?P<metadata_type_pk>\d+)/$',
APIMetadataTypeView.as_view(), name='metadatatype-detail'
),
url(
r'^document/metadata/(?P<pk>[0-9]+)/$',
APIDocumentMetadataView.as_view(), name='documentmetadata-detail'
r'^document_types/(?P<document_type_pk>\d+)/metadata_types/$',
APIDocumentTypeMetadataTypeListView.as_view(),
name='documenttypemetadatatype-list'
),
url(
r'^document/(?P<pk>\d+)/metadata/$',
APIDocumentMetadataListView.as_view(), name='documentmetadata-list'
),
url(
r'^document_type/(?P<document_type_pk>[0-9]+)/metadata_types/optional/$',
APIDocumentTypeMetadataTypeOptionalListView.as_view(),
name='documenttypeoptionalmetadatatype-list'
),
url(
r'^document_type/(?P<document_type_pk>[0-9]+)/metadata_types/required/$',
APIDocumentTypeMetadataTypeRequiredListView.as_view(),
name='documenttyperequiredmetadatatype-list'
),
url(
r'^document_type_metadata_type/(?P<pk>\d+)/$',
r'^document_types/(?P<document_type_pk>\d+)/metadata_types/(?P<metadata_type_pk>\d+)/$',
APIDocumentTypeMetadataTypeView.as_view(),
name='documenttypemetadatatype-detail'
),
url(
r'^documents/(?P<document_pk>\d+)/metadata/$',
APIDocumentMetadataListView.as_view(), name='documentmetadata-list'
),
url(
r'^documents/(?P<document_pk>\d+)/metadata/(?P<metadata_pk>\d+)/$',
APIDocumentMetadataView.as_view(), name='documentmetadata-detail'
),
]