Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-notes-api
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-notes-api
Commits
0796dd67
Commit
0796dd67
authored
Feb 08, 2016
by
ehtesham
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[TNL-4029] user can't add more than maximum number of allowed annotations
parent
ec24a5d4
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
71 additions
and
5 deletions
+71
-5
notesapi/v1/tests/test_views.py
+35
-2
notesapi/v1/views.py
+32
-2
notesserver/settings/common.py
+3
-0
requirements/base.txt
+1
-0
requirements/optional.txt
+0
-1
No files found.
notesapi/v1/tests/test_views.py
View file @
0796dd67
...
@@ -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
...
@@ -54,7 +55,7 @@ class BaseAnnotationViewTests(APITestCase):
...
@@ -54,7 +55,7 @@ class BaseAnnotationViewTests(APITestCase):
"tags"
:
[
"pink"
,
"lady"
]
"tags"
:
[
"pink"
,
"lady"
]
}
}
def
_create_annotation
(
self
,
**
kwargs
):
def
_create_annotation
(
self
,
expected_status
=
status
.
HTTP_201_CREATED
,
**
kwargs
):
"""
"""
Create annotation
Create annotation
"""
"""
...
@@ -62,7 +63,7 @@ class BaseAnnotationViewTests(APITestCase):
...
@@ -62,7 +63,7 @@ class BaseAnnotationViewTests(APITestCase):
opts
.
update
(
kwargs
)
opts
.
update
(
kwargs
)
url
=
reverse
(
'api:v1:annotations'
)
url
=
reverse
(
'api:v1:annotations'
)
response
=
self
.
client
.
post
(
url
,
opts
,
format
=
'json'
)
response
=
self
.
client
.
post
(
url
,
opts
,
format
=
'json'
)
self
.
assertEqual
(
response
.
status_code
,
status
.
HTTP_201_CREATED
)
self
.
assertEqual
(
response
.
status_code
,
expected_status
)
return
response
.
data
.
copy
()
return
response
.
data
.
copy
()
def
_do_annotation_update
(
self
,
data
,
updated_fields
):
def
_do_annotation_update
(
self
,
data
,
updated_fields
):
...
@@ -346,6 +347,38 @@ class AnnotationListViewTests(BaseAnnotationViewTests):
...
@@ -346,6 +347,38 @@ class AnnotationListViewTests(BaseAnnotationViewTests):
del
annotation
[
'created'
]
del
annotation
[
'created'
]
self
.
assertEqual
(
annotation
,
note
)
self
.
assertEqual
(
annotation
,
note
)
@patch
(
'django.conf.settings.MAX_NOTES_PER_COURSE'
,
5
)
def
test_create_maximum_allowed
(
self
):
"""
Tests if user can create more than maximum allowed notes per course
Also test if other user can create notes and Same user can create notes in other course
"""
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
(
expected_status
=
status
.
HTTP_400_BAD_REQUEST
,
**
kwargs
)
self
.
assertEqual
(
response
[
'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_NOTES_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
.
assertTrue
(
'id'
in
response
)
# 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
.
assertTrue
(
'id'
in
response
)
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.
...
...
notesapi/v1/views.py
View file @
0796dd67
import
logging
import
logging
import
json
import
json
import
newrelic.agent
from
django.conf
import
settings
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
as
_
from
rest_framework
import
status
from
rest_framework
import
status
from
rest_framework.generics
import
GenericAPIView
from
rest_framework.generics
import
GenericAPIView
...
@@ -22,6 +24,13 @@ if not settings.ES_DISABLED:
...
@@ -22,6 +24,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
(
GenericAPIView
):
class
AnnotationSearchView
(
GenericAPIView
):
"""
"""
**Use Case**
**Use Case**
...
@@ -275,20 +284,41 @@ class AnnotationListView(GenericAPIView):
...
@@ -275,20 +284,41 @@ class AnnotationListView(GenericAPIView):
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
:
total_notes
=
Note
.
objects
.
filter
(
user_id
=
self
.
request
.
data
[
'user'
],
course_id
=
self
.
request
.
data
[
'course_id'
]
)
.
count
()
if
total_notes
>=
settings
.
MAX_NOTES_PER_COURSE
:
raise
AnnotationsLimitReachedError
note
=
Note
.
create
(
self
.
request
.
data
)
note
=
Note
.
create
(
self
.
request
.
data
)
note
.
full_clean
()
note
.
full_clean
()
# Gather metrics for New Relic so we can slice data in New Relic Insights
newrelic
.
agent
.
add_custom_parameter
(
'notes.count'
,
total_notes
)
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
=
_
(
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_NOTES_PER_COURSE
)
log
.
info
(
u'Attempted to create more than
%
s annotations'
,
settings
.
MAX_NOTES_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
})
serializer
=
NoteSerializer
(
note
)
serializer
=
NoteSerializer
(
note
)
return
Response
(
serializer
.
data
,
status
=
status
.
HTTP_201_CREATED
,
headers
=
{
'Location'
:
location
})
return
Response
(
serializer
.
data
,
status
=
status
.
HTTP_201_CREATED
,
headers
=
{
'Location'
:
location
})
...
...
notesserver/settings/common.py
View file @
0796dd67
...
@@ -83,3 +83,6 @@ TEMPLATE_DIRS = (
...
@@ -83,3 +83,6 @@ TEMPLATE_DIRS = (
)
)
DEFAULT_NOTES_PAGE_SIZE
=
25
DEFAULT_NOTES_PAGE_SIZE
=
25
### Maximum number of allowed notes for each student per course ###
MAX_NOTES_PER_COURSE
=
500
requirements/base.txt
View file @
0796dd67
...
@@ -10,3 +10,4 @@ MySQL-python==1.2.5 # GPL License
...
@@ -10,3 +10,4 @@ MySQL-python==1.2.5 # GPL License
gunicorn==19.1.1 # MIT
gunicorn==19.1.1 # MIT
path.py==3.0.1
path.py==3.0.1
python-dateutil==2.4.0
python-dateutil==2.4.0
newrelic==2.40.0.34 # New Relic
requirements/optional.txt
deleted
100644 → 0
View file @
ec24a5d4
newrelic==2.40.0.34 # New Relic
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment