Modernize MOTD app

Update API code to use viewsets. Update links and URLs to use
keyword arguments.

Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2019-01-19 01:00:58 -04:00
parent 9ed93b54af
commit 16d8fb9fea
13 changed files with 281 additions and 120 deletions

View File

@@ -1,6 +1,6 @@
from __future__ import absolute_import, unicode_literals
from rest_framework import generics
from rest_framework import viewsets
from mayan.apps.rest_api.filters import MayanObjectPermissionsFilter
from mayan.apps.rest_api.permissions import MayanPermission
@@ -13,32 +13,34 @@ from .permissions import (
from .serializers import MessageSerializer
class APIMessageListView(generics.ListCreateAPIView):
class APIMessageViewSet(viewsets.ModelViewSet):
"""
get: Returns a list of all the messages.
post: Create a new message.
create:
Create a new message.
delete:
Delete the given message.
edit:
Edit the given message.
list:
Return a list of all the messages.
retrieve:
Return the given message details.
"""
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
class APIMessageView(generics.RetrieveUpdateDestroyAPIView):
"""
delete: Delete the selected message.
get: Return the details of the selected message.
patch: Edit the selected message.
put: Edit the selected message.
"""
filter_backends = (MayanObjectPermissionsFilter,)
mayan_object_permissions = {
'DELETE': (permission_message_delete,),
'GET': (permission_message_view,),
'PATCH': (permission_message_edit,),
'PUT': (permission_message_edit,)
lookup_url_kwarg = 'message_id'
object_permission = {
'DELETE': permission_message_delete,
'GET': permission_message_view,
'PATCH': permission_message_edit,
'PUT': permission_message_edit,
}
queryset = Message.objects.all()
permission_classes = (MayanPermission,)
serializer_class = MessageSerializer
view_permission = {
'POST': permission_message_create
}

View File

@@ -38,7 +38,7 @@ class MOTDApp(MayanAppConfig):
def ready(self):
super(MOTDApp, self).ready()
Message = self.get_model('Message')
Message = self.get_model(model_name='Message')
ModelPermission.register(
model=Message, permissions=(
permission_acl_edit, permission_acl_view,
@@ -48,16 +48,20 @@ class MOTDApp(MayanAppConfig):
)
SourceColumn(
attribute='label', is_identifier=True, source=Message
attribute='label', is_identifier=True, is_sortable=True,
source=Message
)
SourceColumn(
attribute='enabled', source=Message, widget=TwoStateWidget
attribute='enabled', include_label=True, is_sortable=True,
source=Message, widget=TwoStateWidget
)
SourceColumn(
attribute='start_datetime', empty_value=_('None'), source=Message
attribute='start_datetime', empty_value=_('None'),
include_label=True, is_sortable=True, source=Message
)
SourceColumn(
attribute='end_datetime', empty_value=_('None'), source=Message
attribute='end_datetime', empty_value=_('None'),
include_label=True, is_sortable=True, source=Message
)
menu_list_facet.bind_links(

View File

@@ -2,5 +2,10 @@ from __future__ import absolute_import, unicode_literals
from mayan.apps.appearance.classes import Icon
icon_message_create = Icon(driver_name='fontawesome', symbol='plus')
icon_message_create = icon_tag_create = Icon(
driver_name='fontawesome-dual', primary_symbol='bullhorn',
secondary_symbol='plus'
)
icon_message_delete = Icon(driver_name='fontawesome', symbol='times')
icon_message_edit = Icon(driver_name='fontawesome', symbol='pencil-alt')
icon_message_list = Icon(driver_name='fontawesome', symbol='bullhorn')

View File

@@ -4,7 +4,10 @@ from django.utils.translation import ugettext_lazy as _
from mayan.apps.navigation import Link, get_cascade_condition
from .icons import icon_message_create, icon_message_list
from .icons import (
icon_message_create, icon_message_delete, icon_message_edit,
icon_message_list
)
from .permissions import (
permission_message_create, permission_message_delete,
permission_message_edit, permission_message_view
@@ -15,11 +18,13 @@ link_message_create = Link(
text=_('Create message'), view='motd:message_create'
)
link_message_delete = Link(
args='object.pk', permissions=(permission_message_delete,),
tags='dangerous', text=_('Delete'), view='motd:message_delete'
icon_class=icon_message_delete, kwargs={'message_id': 'object.pk'},
permissions=(permission_message_delete,), tags='dangerous',
text=_('Delete'), view='motd:message_delete'
)
link_message_edit = Link(
args='object.pk', permissions=(permission_message_edit,), text=_('Edit'),
icon_class=icon_message_edit, kwargs={'message_id': 'object.pk'},
permissions=(permission_message_edit,), text=_('Edit'),
view='motd:message_edit'
)
link_message_list = Link(

View File

@@ -7,14 +7,14 @@ from mayan.apps.permissions import PermissionNamespace
namespace = PermissionNamespace(label=_('Message of the day'), name='motd')
permission_message_create = namespace.add_permission(
name='message_create', label=_('Create messages')
label=_('Create messages'), name='message_create'
)
permission_message_delete = namespace.add_permission(
name='message_delete', label=_('Delete messages')
label=_('Delete messages'), name='message_delete'
)
permission_message_edit = namespace.add_permission(
name='message_edit', label=_('Edit messages')
label=_('Edit messages'), name='message_edit'
)
permission_message_view = namespace.add_permission(
name='message_view', label=_('View messages')
label=_('View messages'), name='message_view'
)

View File

@@ -8,7 +8,10 @@ from .models import Message
class MessageSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
extra_kwargs = {
'url': {'view_name': 'rest_api:message-detail'},
'url': {
'lookup_url_kwarg': 'message_id',
'view_name': 'rest_api:message-detail'
},
}
fields = (
'end_datetime', 'enabled', 'label', 'message', 'start_datetime',

View File

@@ -1,6 +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'
TEST_MESSAGE_LABEL = 'test label'
TEST_MESSAGE_LABEL_EDITED = '{} edited'.format(TEST_MESSAGE_LABEL)
TEST_MESSAGE_TEXT = 'test message'
TEST_MESSAGE_TEXT_EDITED = '{} edited'.format(TEST_MESSAGE_TEXT)

View File

@@ -0,0 +1,41 @@
from __future__ import unicode_literals
from ..models import Message
from .literals import (
TEST_MESSAGE_LABEL, TEST_MESSAGE_LABEL_EDITED, TEST_MESSAGE_TEXT,
TEST_MESSAGE_TEXT_EDITED
)
class MOTDTestMixin(object):
def _create_test_message(self):
self.test_message = Message.objects.create(
label=TEST_MESSAGE_LABEL, message=TEST_MESSAGE_TEXT
)
def _request_message_create_view(self):
return self.post(
viewname='motd:message_create', data={
'label': TEST_MESSAGE_LABEL, 'message': TEST_MESSAGE_TEXT
}
)
def _request_message_delete_view(self):
return self.post(
viewname='motd:message_delete',
kwargs={'message_id': self.test_message.pk}
)
def _request_message_edit_view(self):
return self.post(
viewname='motd:message_edit',
kwargs={'message_id': self.test_message.pk},
data={
'label': TEST_MESSAGE_LABEL_EDITED,
'message': TEST_MESSAGE_TEXT_EDITED
}
)
def _request_message_list_view(self):
return self.get(viewname='motd:message_list')

View File

@@ -11,24 +11,18 @@ from ..permissions import (
)
from .literals import (
TEST_LABEL, TEST_LABEL_EDITED, TEST_MESSAGE, TEST_MESSAGE_EDITED
TEST_MESSAGE_LABEL, TEST_MESSAGE_LABEL_EDITED, TEST_MESSAGE_TEXT,
TEST_MESSAGE_TEXT_EDITED
)
from .mixins import MOTDTestMixin
class MOTDAPITestCase(BaseAPITestCase):
def setUp(self):
super(MOTDAPITestCase, self).setUp()
self.login_user()
def _create_message(self):
return Message.objects.create(
label=TEST_LABEL, message=TEST_MESSAGE
)
class MOTDAPITestCase(MOTDTestMixin, BaseAPITestCase):
def _request_message_create_view(self):
return self.post(
viewname='rest_api:message-list', data={
'label': TEST_LABEL, 'message': TEST_MESSAGE
'label': TEST_MESSAGE_LABEL,
'message': TEST_MESSAGE_TEXT
}
)
@@ -44,103 +38,107 @@ class MOTDAPITestCase(BaseAPITestCase):
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(response.data['label'], TEST_MESSAGE_LABEL)
self.assertEqual(response.data['message'], TEST_MESSAGE_TEXT)
self.assertEqual(Message.objects.count(), 1)
self.assertEqual(message.label, TEST_LABEL)
self.assertEqual(message.message, TEST_MESSAGE)
self.assertEqual(message.label, TEST_MESSAGE_LABEL)
self.assertEqual(message.message, TEST_MESSAGE_TEXT)
def _request_message_delete_view(self):
return self.delete(
viewname='rest_api:message-detail', args=(self.message.pk,)
viewname='rest_api:message-detail',
kwargs={'message_id': self.test_message.pk},
)
def test_message_delete_view_no_access(self):
self.message = self._create_message()
self._create_test_message()
response = self._request_message_delete_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.assertEqual(Message.objects.count(), 1)
def test_message_delete_view_with_access(self):
self.message = self._create_message()
self.grant_access(permission=permission_message_delete, obj=self.message)
self._create_test_message()
self.grant_access(permission=permission_message_delete, obj=self.test_message)
response = self._request_message_delete_view()
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(Message.objects.count(), 0)
def _request_message_detail_view(self):
return self.get(
viewname='rest_api:message-detail', args=(self.message.pk,)
viewname='rest_api:message-detail',
kwargs={'message_id': self.test_message.pk},
)
def test_message_detail_view_no_access(self):
self.message = self._create_message()
self._create_test_message()
response = self._request_message_detail_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_message_detail_view_with_access(self):
self.message = self._create_message()
self.grant_access(permission=permission_message_view, obj=self.message)
self._create_test_message()
self.grant_access(permission=permission_message_view, obj=self.test_message)
response = self._request_message_detail_view()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['label'], TEST_LABEL)
self.assertEqual(response.data['label'], TEST_MESSAGE_LABEL)
def _request_message_edit_via_patch_view(self):
return self.patch(
viewname='rest_api:message-detail', args=(self.message.pk,),
viewname='rest_api:message-detail',
kwargs={'message_id': self.test_message.pk},
data={
'label': TEST_LABEL_EDITED,
'message': TEST_MESSAGE_EDITED
'label': TEST_MESSAGE_LABEL_EDITED,
'message': TEST_MESSAGE_TEXT_EDITED
}
)
def test_message_edit_via_patch_view_no_access(self):
self.message = self._create_message()
self._create_test_message()
response = self._request_message_edit_via_patch_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.message.refresh_from_db()
self.test_message.refresh_from_db()
self.assertEqual(self.message.label, TEST_LABEL)
self.assertEqual(self.message.message, TEST_MESSAGE)
self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL)
self.assertEqual(self.test_message.message, TEST_MESSAGE_TEXT)
def test_message_edit_via_patch_view_with_access(self):
self.message = self._create_message()
self.grant_access(permission=permission_message_edit, obj=self.message)
self._create_test_message()
self.grant_access(permission=permission_message_edit, obj=self.test_message)
response = self._request_message_edit_via_patch_view()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.message.refresh_from_db()
self.assertEqual(self.message.label, TEST_LABEL_EDITED)
self.assertEqual(self.message.message, TEST_MESSAGE_EDITED)
self.test_message.refresh_from_db()
self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL_EDITED)
self.assertEqual(self.test_message.message, TEST_MESSAGE_TEXT_EDITED)
def _request_message_edit_via_put_view(self):
return self.put(
viewname='rest_api:message-detail', args=(self.message.pk,),
viewname='rest_api:message-detail',
kwargs={'message_id': self.test_message.pk},
data={
'label': TEST_LABEL_EDITED,
'message': TEST_MESSAGE_EDITED
'label': TEST_MESSAGE_LABEL_EDITED,
'message': TEST_MESSAGE_TEXT_EDITED
}
)
def test_message_edit_via_put_view_no_access(self):
self.message = self._create_message()
self._create_test_message()
response = self._request_message_edit_via_put_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.message.refresh_from_db()
self.test_message.refresh_from_db()
self.assertEqual(self.message.label, TEST_LABEL)
self.assertEqual(self.message.message, TEST_MESSAGE)
self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL)
self.assertEqual(self.test_message.message, TEST_MESSAGE_TEXT)
def test_message_edit_via_put_view_with_access(self):
self.message = self._create_message()
self.grant_access(permission=permission_message_edit, obj=self.message)
self._create_test_message()
self.grant_access(permission=permission_message_edit, obj=self.test_message)
response = self._request_message_edit_via_put_view()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.message.refresh_from_db()
self.assertEqual(self.message.label, TEST_LABEL_EDITED)
self.assertEqual(self.message.message, TEST_MESSAGE_EDITED)
self.test_message.refresh_from_db()
self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL_EDITED)
self.assertEqual(self.test_message.message, TEST_MESSAGE_TEXT_EDITED)

View File

@@ -7,14 +7,12 @@ from django.utils import timezone
from ..models import Message
from .literals import TEST_LABEL, TEST_MESSAGE
from .mixins import MOTDTestMixin
class MOTDTestCase(TestCase):
class MOTDTestCase(MOTDTestMixin, TestCase):
def setUp(self):
self.motd = Message.objects.create(
label=TEST_LABEL, message=TEST_MESSAGE
)
self._create_test_message()
def test_basic(self):
queryset = Message.objects.get_for_now()
@@ -22,25 +20,25 @@ class MOTDTestCase(TestCase):
self.assertEqual(queryset.exists(), True)
def test_start_datetime(self):
self.motd.start_datetime = timezone.now() - timedelta(days=1)
self.motd.save()
self.test_message.start_datetime = timezone.now() - timedelta(days=1)
self.test_message.save()
queryset = Message.objects.get_for_now()
self.assertEqual(queryset.first(), self.motd)
self.assertEqual(queryset.first(), self.test_message)
def test_end_datetime(self):
self.motd.start_datetime = timezone.now() - timedelta(days=2)
self.motd.end_datetime = timezone.now() - timedelta(days=1)
self.motd.save()
self.test_message.start_datetime = timezone.now() - timedelta(days=2)
self.test_message.end_datetime = timezone.now() - timedelta(days=1)
self.test_message.save()
queryset = Message.objects.get_for_now()
self.assertEqual(queryset.exists(), False)
def test_enable(self):
self.motd.enabled = False
self.motd.save()
self.test_message.enabled = False
self.test_message.save()
queryset = Message.objects.get_for_now()

View File

@@ -0,0 +1,100 @@
from __future__ import unicode_literals
from django.utils.encoding import force_text
from mayan.apps.common.tests import GenericViewTestCase
from ..models import Message
from ..permissions import (
permission_message_create, permission_message_delete,
permission_message_edit, permission_message_view
)
from .literals import (
TEST_MESSAGE_LABEL, TEST_MESSAGE_LABEL_EDITED, TEST_MESSAGE_TEXT,
TEST_MESSAGE_TEXT_EDITED
)
from .mixins import MOTDTestMixin
class MOTDViewTestCase(MOTDTestMixin, GenericViewTestCase):
def test_message_create_view_no_permissions(self):
response = self._request_message_create_view()
self.assertEqual(response.status_code, 403)
self.assertEqual(Message.objects.count(), 0)
def test_message_create_view_with_permissions(self):
self.grant_permission(permission=permission_message_create)
response = self._request_message_create_view()
self.assertEqual(response.status_code, 302)
self.assertEqual(Message.objects.count(), 1)
message = Message.objects.first()
self.assertEqual(message.label, TEST_MESSAGE_LABEL)
self.assertEqual(message.message, TEST_MESSAGE_TEXT)
def test_message_delete_view_no_permissions(self):
self._create_test_message()
response = self._request_message_delete_view()
self.assertEqual(response.status_code, 404)
self.assertEqual(Message.objects.count(), 1)
def test_message_delete_view_with_access(self):
self._create_test_message()
self.grant_access(
obj=self.test_message, permission=permission_message_delete
)
response = self._request_message_delete_view()
self.assertEqual(response.status_code, 302)
self.assertEqual(Message.objects.count(), 0)
def test_message_edit_view_no_permissions(self):
self._create_test_message()
response = self._request_message_edit_view()
self.assertEqual(response.status_code, 404)
self.test_message.refresh_from_db()
self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL)
self.assertEqual(self.test_message.message, TEST_MESSAGE_TEXT)
def test_message_edit_view_with_access(self):
self._create_test_message()
self.grant_access(
obj=self.test_message, permission=permission_message_edit
)
response = self._request_message_edit_view()
self.assertEqual(response.status_code, 302)
self.test_message.refresh_from_db()
self.assertEqual(self.test_message.label, TEST_MESSAGE_LABEL_EDITED)
self.assertEqual(
self.test_message.message, TEST_MESSAGE_TEXT_EDITED
)
def test_message_list_view_no_permissions(self):
self._create_test_message()
response = self._request_message_list_view()
self.assertNotContains(
response=response, text=force_text(self.test_message),
status_code=200
)
def test_message_list_view_with_access(self):
self._create_test_message()
self.grant_access(
obj=self.test_message, permission=permission_message_view
)
response = self._request_message_list_view()
self.assertContains(
response=response, text=force_text(self.test_message),
status_code=200
)

View File

@@ -2,27 +2,30 @@ from __future__ import unicode_literals
from django.conf.urls import url
from .api_views import APIMessageListView, APIMessageView
from .api_views import APIMessageViewSet
from .views import (
MessageCreateView, MessageDeleteView, MessageEditView, MessageListView
)
urlpatterns = [
url(r'^list/$', MessageListView.as_view(), name='message_list'),
url(r'^create/$', MessageCreateView.as_view(), name='message_create'),
url(
r'^(?P<pk>\d+)/edit/$', MessageEditView.as_view(), name='message_edit'
regex=r'^messages/$', name='message_list',
view=MessageListView.as_view()
),
url(
r'^(?P<pk>\d+)/delete/$', MessageDeleteView.as_view(),
name='message_delete'
regex=r'^messages/create/$', name='message_create',
view=MessageCreateView.as_view()
),
url(
regex=r'^messages/(?P<message_id>\d+)/delete/$', name='message_delete',
view=MessageDeleteView.as_view()
),
url(
regex=r'^messages/(?P<message_id>\d+)/edit/$', name='message_edit',
view=MessageEditView.as_view()
),
]
api_urls = [
url(r'^messages/$', APIMessageListView.as_view(), name='message-list'),
url(
r'^messages/(?P<pk>[0-9]+)/$', APIMessageView.as_view(),
name='message-detail'
),
]
api_router_entries = (
{'prefix': r'messages', 'viewset': APIMessageViewSet, 'basename': 'message'},
)

View File

@@ -36,7 +36,8 @@ class MessageCreateView(SingleObjectCreateView):
class MessageDeleteView(SingleObjectDeleteView):
model = Message
object_permission = permission_message_delete
post_action_redirect = reverse_lazy('motd:message_list')
pk_url_kwarg = 'message_id'
post_action_redirect = reverse_lazy(viewname='motd:message_list')
def get_extra_context(self):
return {
@@ -50,7 +51,8 @@ class MessageEditView(SingleObjectEditView):
fields = ('label', 'message', 'enabled', 'start_datetime', 'end_datetime')
model = Message
object_permission = permission_message_edit
post_action_redirect = reverse_lazy('motd:message_list')
pk_url_kwarg = 'message_id'
post_action_redirect = reverse_lazy(viewname='motd:message_list')
def get_extra_context(self):
return {