Commit 4310403b by ehtesham

[TNL-4029] user can't add more than maximum number of allowed annotations

parent 05dba592
...@@ -18,6 +18,7 @@ from rest_framework.test import APITestCase ...@@ -18,6 +18,7 @@ from rest_framework.test import APITestCase
from .helpers import get_id_token from .helpers import get_id_token
TEST_USER = "test_user_id" TEST_USER = "test_user_id"
TEST_OTHER_USER = "test_other_user_id"
if not settings.ES_DISABLED: if not settings.ES_DISABLED:
import haystack import haystack
...@@ -65,6 +66,16 @@ class BaseAnnotationViewTests(APITestCase): ...@@ -65,6 +66,16 @@ class BaseAnnotationViewTests(APITestCase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED) self.assertEqual(response.status_code, status.HTTP_201_CREATED)
return response.data.copy() return response.data.copy()
def _create_annotation_without_assert(self, **kwargs):
"""
Create annotation but do not check for 201
"""
opts = self.payload.copy()
opts.update(kwargs)
url = reverse('api:v1:annotations')
response = self.client.post(url, opts, format='json')
return response
def _do_annotation_update(self, data, updated_fields): def _do_annotation_update(self, data, updated_fields):
""" """
Helper method for updating an annotation. Helper method for updating an annotation.
...@@ -98,7 +109,7 @@ class BaseAnnotationViewTests(APITestCase): ...@@ -98,7 +109,7 @@ class BaseAnnotationViewTests(APITestCase):
result = self.client.get(url, data=data) result = self.client.get(url, data=data)
return result.data return result.data
@ddt.ddt
class AnnotationListViewTests(BaseAnnotationViewTests): class AnnotationListViewTests(BaseAnnotationViewTests):
""" """
Test annotation creation and listing by user and course Test annotation creation and listing by user and course
...@@ -222,6 +233,62 @@ class AnnotationListViewTests(BaseAnnotationViewTests): ...@@ -222,6 +233,62 @@ class AnnotationListViewTests(BaseAnnotationViewTests):
del annotation['created'] del annotation['created']
self.assertEqual(annotation, note) self.assertEqual(annotation, note)
@ddt.data(
(6, False),
(4, True)
)
@ddt.unpack
def test_create_maximum_allowed(self, num_notes, should_create):
"""
Tests user can not create more than allowed notes/annotations per course
"""
for i in xrange(num_notes - 1):
kwargs = {'text': 'Foo_{}'.format(i)}
self._create_annotation(**kwargs)
# Creating more notes should result in 400 error
kwargs = {'text': 'Foo_{}'.format(num_notes)}
response = self._create_annotation_without_assert(**kwargs)
if not should_create:
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST, 'Creating more than allowed notes')
self.assertEqual(
response.data['error_msg'],
u'You can create up to {0} notes.'
u' You must remove some notes before you can add new ones.'.format(settings.MAX_ANNOTATIONS_PER_COURSE)
)
else:
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
def test_create_maximum_allowed_other(self):
# if user tries to create notes in another course it should succeed
for i in xrange(5):
kwargs = {'text': 'Foo_{}'.format(i)}
self._create_annotation(**kwargs)
# Creating more notes should result in 400 error
kwargs = {'text': 'Foo_{}'.format(6)}
response = self._create_annotation_without_assert(**kwargs)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST, 'Creating more than allowed notes')
self.assertEqual(
response.data['error_msg'],
u'You can create up to {0} notes.'
u' You must remove some notes before you can add new ones.'.format(settings.MAX_ANNOTATIONS_PER_COURSE)
)
# if user tries to create note in a different course it should succeed
kwargs = {'course_id': 'test-course-id-2'}
response = self._create_annotation(**kwargs)
self.assertFalse(hasattr(response, 'data'))
# if another user to tries to create note in first course it should succeed
token = get_id_token(TEST_OTHER_USER)
self.client.credentials(HTTP_X_ANNOTATOR_AUTH_TOKEN=token)
self.headers = {'user': TEST_OTHER_USER}
kwargs = {'user': TEST_OTHER_USER}
response = self._create_annotation(**kwargs)
self.assertFalse(hasattr(response, 'data'))
def test_read_all_no_annotations(self): def test_read_all_no_annotations(self):
""" """
Tests list all annotations endpoint when no annotations are present in database. Tests list all annotations endpoint when no annotations are present in database.
......
...@@ -5,6 +5,7 @@ from django.conf import settings ...@@ -5,6 +5,7 @@ from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.db.models import Q from django.db.models import Q
from django.utils.translation import ugettext_noop
from rest_framework import status from rest_framework import status
from rest_framework.response import Response from rest_framework.response import Response
...@@ -20,6 +21,13 @@ if not settings.ES_DISABLED: ...@@ -20,6 +21,13 @@ if not settings.ES_DISABLED:
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class AnnotationsLimitReachedError(Exception):
"""
Exception when trying to create more than allowed annotations
"""
pass
class AnnotationSearchView(APIView): class AnnotationSearchView(APIView):
""" """
Search annotations. Search annotations.
...@@ -117,20 +125,35 @@ class AnnotationListView(APIView): ...@@ -117,20 +125,35 @@ class AnnotationListView(APIView):
Returns 400 request if bad payload is sent or it was empty object. Returns 400 request if bad payload is sent or it was empty object.
""" """
if 'id' in self.request.data: if not self.request.data or 'id' in self.request.data:
return Response(status=status.HTTP_400_BAD_REQUEST) return Response(status=status.HTTP_400_BAD_REQUEST)
try: try:
if Note.objects.filter(
user_id=self.request.data['user'], course_id=self.request.data['course_id']
).count() >= settings.MAX_ANNOTATIONS_PER_COURSE:
raise AnnotationsLimitReachedError
note = Note.create(self.request.data) note = Note.create(self.request.data)
note.full_clean() note.full_clean()
except ValidationError as error: except ValidationError as error:
log.debug(error, exc_info=True) log.debug(error, exc_info=True)
return Response(status=status.HTTP_400_BAD_REQUEST) return Response(status=status.HTTP_400_BAD_REQUEST)
except AnnotationsLimitReachedError:
error_message = ugettext_noop(
u'You can create up to {max_num_annotations_per_course} notes.'
u' You must remove some notes before you can add new ones.'
).format(max_num_annotations_per_course=settings.MAX_ANNOTATIONS_PER_COURSE)
log.info(
u'Attempted to create more than %s annotations',
settings.MAX_ANNOTATIONS_PER_COURSE
)
return Response({
'error_msg': error_message
}, status=status.HTTP_400_BAD_REQUEST)
note.save() note.save()
location = reverse('api:v1:annotations_detail', kwargs={'annotation_id': note.id}) location = reverse('api:v1:annotations_detail', kwargs={'annotation_id': note.id})
return Response(note.as_dict(), status=status.HTTP_201_CREATED, headers={'Location': location}) return Response(note.as_dict(), status=status.HTTP_201_CREATED, headers={'Location': location})
......
...@@ -80,3 +80,6 @@ CORS_ALLOW_HEADERS = ( ...@@ -80,3 +80,6 @@ CORS_ALLOW_HEADERS = (
TEMPLATE_DIRS = ( TEMPLATE_DIRS = (
'templates', 'templates',
) )
### Maximum number of allowed notes per course ###
MAX_ANNOTATIONS_PER_COURSE = 500
...@@ -42,3 +42,6 @@ LOGGING = { ...@@ -42,3 +42,6 @@ LOGGING = {
} }
}, },
} }
### Maximum number of allowed notes per course ###
MAX_ANNOTATIONS_PER_COURSE = 5
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment