From c218819728f641a7f4d666fffeab5cc864da5433 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Fri, 10 Feb 2017 18:16:46 -0400 Subject: [PATCH] Add API endpoints for the message of the day app. --- docs/releases/2.1.8.rst | 80 ++++++++++++++ docs/releases/index.rst | 1 + mayan/apps/motd/api_views.py | 76 +++++++++++++ mayan/apps/motd/apps.py | 3 + mayan/apps/motd/migrations/0001_initial.py | 37 ++++++- .../migrations/0005_auto_20160510_0025.py | 20 +++- mayan/apps/motd/serializers.py | 17 +++ mayan/apps/motd/tests/literals.py | 6 + mayan/apps/motd/tests/test_api.py | 103 ++++++++++++++++++ mayan/apps/motd/tests/test_models.py | 3 +- mayan/apps/motd/urls.py | 13 ++- 11 files changed, 346 insertions(+), 13 deletions(-) create mode 100644 docs/releases/2.1.8.rst create mode 100644 mayan/apps/motd/api_views.py create mode 100644 mayan/apps/motd/serializers.py create mode 100644 mayan/apps/motd/tests/literals.py create mode 100644 mayan/apps/motd/tests/test_api.py diff --git a/docs/releases/2.1.8.rst b/docs/releases/2.1.8.rst new file mode 100644 index 0000000000..196d21b395 --- /dev/null +++ b/docs/releases/2.1.8.rst @@ -0,0 +1,80 @@ +=============================== +Mayan EDMS v2.1.8 release notes +=============================== + +Released: February XX, 2017 + +What's new +========== + +This is a bug-fix release and all users are encouraged to upgrade. The focus +of this micro release was REST API improvement. + +Changes +------------- + +- Fixes in the trashed document API endpoints. +- Improved tags API PUT and PATCH endpoints. +- Bulk document adding when creating and editing tags. +- The version of django-mptt is preserved in case mayan-cabinets is installed. +- Add Django GPG API endpoints for singing keys. +- Add API endpoints for the document states app. +- Add API endpoints for the messsage of the day (MOTD) app. + + +Removals +-------- +* None + +Upgrading from a previous version +--------------------------------- + +Using PIP +~~~~~~~~~ + +Type in the console:: + + $ pip install -U mayan-edms + +the requirements will also be updated automatically. + +Using Git +~~~~~~~~~ + +If you installed Mayan EDMS by cloning the Git repository issue the commands:: + + $ git reset --hard HEAD + $ git pull + +otherwise download the compressed archived and uncompress it overriding the +existing installation. + +Next upgrade/add the new requirements:: + + $ pip install --upgrade -r requirements.txt + +Common steps +~~~~~~~~~~~~ + +Migrate existing database schema with:: + + $ mayan-edms.py performupgrade + +Add new static media:: + + $ mayan-edms.py collectstatic --noinput + +The upgrade procedure is now complete. + + +Backward incompatible changes +============================= + +* None + +Bugs fixed or issues closed +=========================== + +* None + +.. _PyPI: https://pypi.python.org/pypi/mayan-edms/ diff --git a/docs/releases/index.rst b/docs/releases/index.rst index de40317d59..84cb057583 100644 --- a/docs/releases/index.rst +++ b/docs/releases/index.rst @@ -22,6 +22,7 @@ versions of the documentation contain the release notes for any later releases. .. toctree:: :maxdepth: 1 + 2.1.8 2.1.7 2.1.6 2.1.5 diff --git a/mayan/apps/motd/api_views.py b/mayan/apps/motd/api_views.py new file mode 100644 index 0000000000..4799ab357d --- /dev/null +++ b/mayan/apps/motd/api_views.py @@ -0,0 +1,76 @@ +from __future__ import absolute_import, unicode_literals + +from rest_framework import generics + +from rest_api.filters import MayanObjectPermissionsFilter +from rest_api.permissions import MayanPermission + +from .models import Message +from .permissions import ( + permission_message_create, permission_message_delete, + permission_message_edit, permission_message_view +) +from .serializers import MessageSerializer + + +class APIMessageListView(generics.ListCreateAPIView): + filter_backends = (MayanObjectPermissionsFilter,) + mayan_object_permissions = {'GET': (permission_message_view,)} + mayan_view_permissions = {'POST': (permission_message_create,)} + permission_classes = (MayanPermission,) + queryset = Message.objects.all() + serializer_class = MessageSerializer + + def get(self, *args, **kwargs): + """ + Returns a list of all the messages. + """ + + return super(APIMessageListView, self).get(*args, **kwargs) + + def post(self, *args, **kwargs): + """ + Create a new message. + """ + + return super(APIMessageListView, self).post(*args, **kwargs) + + +class APIMessageView(generics.RetrieveUpdateDestroyAPIView): + filter_backends = (MayanObjectPermissionsFilter,) + mayan_object_permissions = { + 'DELETE': (permission_message_delete,), + 'GET': (permission_message_view,), + 'PATCH': (permission_message_edit,), + 'PUT': (permission_message_edit,) + } + queryset = Message.objects.all() + serializer_class = MessageSerializer + + def delete(self, *args, **kwargs): + """ + Delete the selected message. + """ + + return super(APIMessageView, self).delete(*args, **kwargs) + + def get(self, *args, **kwargs): + """ + Return the details of the selected message. + """ + + return super(APIMessageView, self).get(*args, **kwargs) + + def patch(self, *args, **kwargs): + """ + Edit the selected message. + """ + + return super(APIMessageView, self).patch(*args, **kwargs) + + def put(self, *args, **kwargs): + """ + Edit the selected message. + """ + + return super(APIMessageView, self).put(*args, **kwargs) diff --git a/mayan/apps/motd/apps.py b/mayan/apps/motd/apps.py index 0258d066da..afb6c8f132 100644 --- a/mayan/apps/motd/apps.py +++ b/mayan/apps/motd/apps.py @@ -6,6 +6,7 @@ from django.utils.translation import ugettext_lazy as _ from common import MayanAppConfig, menu_object, menu_secondary, menu_setup from navigation import SourceColumn +from rest_api.classes import APIEndPoint from .links import ( link_message_create, link_message_delete, link_message_edit, @@ -23,6 +24,8 @@ class MOTDApp(MayanAppConfig): def ready(self): super(MOTDApp, self).ready() + APIEndPoint(app=self, version_string='1') + Message = self.get_model('Message') SourceColumn( diff --git a/mayan/apps/motd/migrations/0001_initial.py b/mayan/apps/motd/migrations/0001_initial.py index 545f550f5d..f83b25ed46 100644 --- a/mayan/apps/motd/migrations/0001_initial.py +++ b/mayan/apps/motd/migrations/0001_initial.py @@ -13,12 +13,37 @@ class Migration(migrations.Migration): migrations.CreateModel( name='MessageOfTheDay', fields=[ - ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), - ('label', models.CharField(max_length=32, verbose_name='Label')), - ('message', models.TextField(verbose_name='Message', blank=True)), - ('enabled', models.BooleanField(default=True, verbose_name='Enabled')), - ('start_datetime', models.DateTimeField(verbose_name='Start date time', blank=True)), - ('end_datetime', models.DateTimeField(verbose_name='End date time', blank=True)), + ( + 'id', models.AutoField( + verbose_name='ID', serialize=False, + auto_created=True, primary_key=True + ) + ), + ( + 'label', models.CharField( + max_length=32, verbose_name='Label' + ) + ), + ( + 'message', models.TextField( + verbose_name='Message', blank=True + ) + ), + ( + 'enabled', models.BooleanField( + default=True, verbose_name='Enabled' + ) + ), + ( + 'start_datetime', models.DateTimeField( + verbose_name='Start date time', blank=True + ) + ), + ( + 'end_datetime', models.DateTimeField( + verbose_name='End date time', blank=True + ) + ), ], options={ 'verbose_name': 'Message of the day', diff --git a/mayan/apps/motd/migrations/0005_auto_20160510_0025.py b/mayan/apps/motd/migrations/0005_auto_20160510_0025.py index 2f87651d4c..469fa0c76d 100644 --- a/mayan/apps/motd/migrations/0005_auto_20160510_0025.py +++ b/mayan/apps/motd/migrations/0005_auto_20160510_0025.py @@ -14,21 +14,33 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='message', name='end_datetime', - field=models.DateTimeField(help_text='Date and time until when this message is to be displayed.', null=True, verbose_name='End date time', blank=True), + field=models.DateTimeField( + help_text='Date and time until when this message is to be displayed.', + null=True, verbose_name='End date time', blank=True + ), ), migrations.AlterField( model_name='message', name='label', - field=models.CharField(help_text='Short description of this message.', max_length=32, verbose_name='Label'), + field=models.CharField( + help_text='Short description of this message.', max_length=32, + verbose_name='Label' + ), ), migrations.AlterField( model_name='message', name='message', - field=models.TextField(help_text='The actual message to be displayed.', verbose_name='Message'), + field=models.TextField( + help_text='The actual message to be displayed.', + verbose_name='Message' + ), ), migrations.AlterField( model_name='message', name='start_datetime', - field=models.DateTimeField(help_text='Date and time after which this message will be displayed.', null=True, verbose_name='Start date time', blank=True), + field=models.DateTimeField( + help_text='Date and time after which this message will be displayed.', + null=True, verbose_name='Start date time', blank=True + ), ), ] diff --git a/mayan/apps/motd/serializers.py b/mayan/apps/motd/serializers.py new file mode 100644 index 0000000000..1637ac91b6 --- /dev/null +++ b/mayan/apps/motd/serializers.py @@ -0,0 +1,17 @@ +from __future__ import absolute_import, unicode_literals + +from rest_framework import serializers + +from .models import Message + + +class MessageSerializer(serializers.HyperlinkedModelSerializer): + class Meta: + extra_kwargs = { + 'url': {'view_name': 'rest_api:message-detail'}, + } + fields = ( + 'end_datetime', 'enabled', 'label', 'message', 'start_datetime', + 'id', 'url' + ) + model = Message diff --git a/mayan/apps/motd/tests/literals.py b/mayan/apps/motd/tests/literals.py new file mode 100644 index 0000000000..431ef052bb --- /dev/null +++ b/mayan/apps/motd/tests/literals.py @@ -0,0 +1,6 @@ +from __future__ import unicode_literals + +TEST_LABEL = 'test label' +TEST_LABEL_EDITED = 'test label edited' +TEST_MESSAGE = 'test message' +TEST_MESSAGE_EDITED = 'test message edited' diff --git a/mayan/apps/motd/tests/test_api.py b/mayan/apps/motd/tests/test_api.py new file mode 100644 index 0000000000..6cb6e8bdfa --- /dev/null +++ b/mayan/apps/motd/tests/test_api.py @@ -0,0 +1,103 @@ +from __future__ import unicode_literals + +from django.contrib.auth import get_user_model +from django.core.urlresolvers import reverse +from django.test import override_settings + +from rest_framework.test import APITestCase + +from user_management.tests.literals import ( + TEST_ADMIN_EMAIL, TEST_ADMIN_PASSWORD, TEST_ADMIN_USERNAME +) + +from ..models import Message + +from .literals import ( + TEST_LABEL, TEST_LABEL_EDITED, TEST_MESSAGE, TEST_MESSAGE_EDITED +) + + +@override_settings(OCR_AUTO_OCR=False) +class MOTDAPITestCase(APITestCase): + def setUp(self): + self.admin_user = get_user_model().objects.create_superuser( + username=TEST_ADMIN_USERNAME, email=TEST_ADMIN_EMAIL, + password=TEST_ADMIN_PASSWORD + ) + + self.client.login( + username=TEST_ADMIN_USERNAME, password=TEST_ADMIN_PASSWORD + ) + + def _create_message(self): + return Message.objects.create( + label=TEST_LABEL, message=TEST_MESSAGE + ) + + def test_message_create_view(self): + response = self.client.post( + reverse('rest_api:message-list'), { + 'label': TEST_LABEL, 'message': TEST_MESSAGE + } + ) + + message = Message.objects.first() + self.assertEqual(response.data['id'], message.pk) + self.assertEqual(response.data['label'], TEST_LABEL) + self.assertEqual(response.data['message'], TEST_MESSAGE) + + self.assertEqual(Message.objects.count(), 1) + self.assertEqual(message.label, TEST_LABEL) + self.assertEqual(message.message, TEST_MESSAGE) + + def test_message_delete_view(self): + message = self._create_message() + + self.client.delete( + reverse('rest_api:message-detail', args=(message.pk,)) + ) + + self.assertEqual(Message.objects.count(), 0) + + def test_message_detail_view(self): + message = self._create_message() + + response = self.client.get( + reverse('rest_api:message-detail', args=(message.pk,)) + ) + + self.assertEqual( + response.data['label'], TEST_LABEL + ) + + def test_message_path_view(self): + message = self._create_message() + + self.client.patch( + reverse('rest_api:message-detail', args=(message.pk,)), + { + 'label': TEST_LABEL_EDITED, + 'message': TEST_MESSAGE_EDITED + } + ) + + message.refresh_from_db() + + self.assertEqual(message.label, TEST_LABEL_EDITED) + self.assertEqual(message.message, TEST_MESSAGE_EDITED) + + def test_message_put_view(self): + message = self._create_message() + + self.client.put( + reverse('rest_api:message-detail', args=(message.pk,)), + { + 'label': TEST_LABEL_EDITED, + 'message': TEST_MESSAGE_EDITED + } + ) + + message.refresh_from_db() + + self.assertEqual(message.label, TEST_LABEL_EDITED) + self.assertEqual(message.message, TEST_MESSAGE_EDITED) diff --git a/mayan/apps/motd/tests/test_models.py b/mayan/apps/motd/tests/test_models.py index bb250982fa..457cacedac 100644 --- a/mayan/apps/motd/tests/test_models.py +++ b/mayan/apps/motd/tests/test_models.py @@ -7,8 +7,7 @@ from django.utils import timezone from ..models import Message -TEST_LABEL = 'test label' -TEST_MESSAGE = 'test message' +from .literals import TEST_LABEL, TEST_MESSAGE class MOTDTestCase(TestCase): diff --git a/mayan/apps/motd/urls.py b/mayan/apps/motd/urls.py index 709e8d7488..e639db5f57 100644 --- a/mayan/apps/motd/urls.py +++ b/mayan/apps/motd/urls.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from django.conf.urls import patterns, url +from .api_views import APIMessageListView, APIMessageView from .views import ( MessageCreateView, MessageDeleteView, MessageEditView, MessageListView ) @@ -10,9 +11,19 @@ urlpatterns = patterns( '', url(r'^list/$', MessageListView.as_view(), name='message_list'), url(r'^create/$', MessageCreateView.as_view(), name='message_create'), - url(r'^(?P\d+)/edit/$', MessageEditView.as_view(), name='message_edit'), + url( + r'^(?P\d+)/edit/$', MessageEditView.as_view(), name='message_edit' + ), url( r'^(?P\d+)/delete/$', MessageDeleteView.as_view(), name='message_delete' ), ) + +api_urls = [ + url(r'^messages/$', APIMessageListView.as_view(), name='message-list'), + url( + r'^messages/(?P[0-9]+)/$', APIMessageView.as_view(), + name='message-detail' + ), +]