Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
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-platform
Commits
2c7590d1
Commit
2c7590d1
authored
Jun 08, 2015
by
Greg Price
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use parent_id returned from the comments service
This depends on cs_comments_service commit 0487891.
parent
77395bdf
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
39 additions
and
39 deletions
+39
-39
lms/djangoapps/discussion_api/api.py
+3
-4
lms/djangoapps/discussion_api/serializers.py
+4
-16
lms/djangoapps/discussion_api/tests/test_api.py
+1
-2
lms/djangoapps/discussion_api/tests/test_serializers.py
+22
-10
lms/djangoapps/discussion_api/tests/test_views.py
+0
-1
lms/djangoapps/discussion_api/tests/utils.py
+9
-6
No files found.
lms/djangoapps/discussion_api/api.py
View file @
2c7590d1
...
@@ -41,7 +41,7 @@ def _get_course_or_404(course_key, user):
...
@@ -41,7 +41,7 @@ def _get_course_or_404(course_key, user):
return
course
return
course
def
_get_thread_and_context
(
request
,
thread_id
,
parent_id
=
None
,
retrieve_kwargs
=
None
):
def
_get_thread_and_context
(
request
,
thread_id
,
retrieve_kwargs
=
None
):
"""
"""
Retrieve the given thread and build a serializer context for it, returning
Retrieve the given thread and build a serializer context for it, returning
both. This function also enforces access control for the thread (checking
both. This function also enforces access control for the thread (checking
...
@@ -56,7 +56,7 @@ def _get_thread_and_context(request, thread_id, parent_id=None, retrieve_kwargs=
...
@@ -56,7 +56,7 @@ def _get_thread_and_context(request, thread_id, parent_id=None, retrieve_kwargs=
cc_thread
=
Thread
(
id
=
thread_id
)
.
retrieve
(
**
retrieve_kwargs
)
cc_thread
=
Thread
(
id
=
thread_id
)
.
retrieve
(
**
retrieve_kwargs
)
course_key
=
CourseKey
.
from_string
(
cc_thread
[
"course_id"
])
course_key
=
CourseKey
.
from_string
(
cc_thread
[
"course_id"
])
course
=
_get_course_or_404
(
course_key
,
request
.
user
)
course
=
_get_course_or_404
(
course_key
,
request
.
user
)
context
=
get_context
(
course
,
request
,
cc_thread
,
parent_id
)
context
=
get_context
(
course
,
request
,
cc_thread
)
if
(
if
(
not
context
[
"is_requester_privileged"
]
and
not
context
[
"is_requester_privileged"
]
and
cc_thread
[
"group_id"
]
and
cc_thread
[
"group_id"
]
and
...
@@ -350,11 +350,10 @@ def create_comment(request, comment_data):
...
@@ -350,11 +350,10 @@ def create_comment(request, comment_data):
detail.
detail.
"""
"""
thread_id
=
comment_data
.
get
(
"thread_id"
)
thread_id
=
comment_data
.
get
(
"thread_id"
)
parent_id
=
comment_data
.
get
(
"parent_id"
)
if
not
thread_id
:
if
not
thread_id
:
raise
ValidationError
({
"thread_id"
:
[
"This field is required."
]})
raise
ValidationError
({
"thread_id"
:
[
"This field is required."
]})
try
:
try
:
cc_thread
,
context
=
_get_thread_and_context
(
request
,
thread_id
,
parent_id
)
cc_thread
,
context
=
_get_thread_and_context
(
request
,
thread_id
)
except
Http404
:
except
Http404
:
raise
ValidationError
({
"thread_id"
:
[
"Invalid value."
]})
raise
ValidationError
({
"thread_id"
:
[
"Invalid value."
]})
...
...
lms/djangoapps/discussion_api/serializers.py
View file @
2c7590d1
...
@@ -24,7 +24,7 @@ from openedx.core.djangoapps.course_groups.cohorts import get_cohort_names
...
@@ -24,7 +24,7 @@ from openedx.core.djangoapps.course_groups.cohorts import get_cohort_names
from
openedx.core.lib.api.fields
import
NonEmptyCharField
from
openedx.core.lib.api.fields
import
NonEmptyCharField
def
get_context
(
course
,
request
,
thread
=
None
,
parent_id
=
None
):
def
get_context
(
course
,
request
,
thread
=
None
):
"""
"""
Returns a context appropriate for use with ThreadSerializer or
Returns a context appropriate for use with ThreadSerializer or
(if thread is provided) CommentSerializer.
(if thread is provided) CommentSerializer.
...
@@ -48,7 +48,6 @@ def get_context(course, request, thread=None, parent_id=None):
...
@@ -48,7 +48,6 @@ def get_context(course, request, thread=None, parent_id=None):
"course"
:
course
,
"course"
:
course
,
"request"
:
request
,
"request"
:
request
,
"thread"
:
thread
,
"thread"
:
thread
,
"parent_id"
:
parent_id
,
# For now, the only groups are cohorts
# For now, the only groups are cohorts
"group_ids_to_names"
:
get_cohort_names
(
course
),
"group_ids_to_names"
:
get_cohort_names
(
course
),
"is_requester_privileged"
:
requester
.
id
in
staff_user_ids
or
requester
.
id
in
ta_user_ids
,
"is_requester_privileged"
:
requester
.
id
in
staff_user_ids
or
requester
.
id
in
ta_user_ids
,
...
@@ -219,25 +218,18 @@ class CommentSerializer(_ContentSerializer):
...
@@ -219,25 +218,18 @@ class CommentSerializer(_ContentSerializer):
"""
"""
A serializer for comment data.
A serializer for comment data.
Because it is not a field in the underlying data, parent_id must be provided
in the context for both serialization and deserialization.
N.B. This should not be used with a comment_client Comment object that has
N.B. This should not be used with a comment_client Comment object that has
not had retrieve() called, because of the interaction between DRF's attempts
not had retrieve() called, because of the interaction between DRF's attempts
at introspection and Comment's __getattr__.
at introspection and Comment's __getattr__.
"""
"""
thread_id
=
serializers
.
CharField
()
thread_id
=
serializers
.
CharField
()
parent_id
=
serializers
.
SerializerMethodField
(
"get_parent_id"
)
parent_id
=
serializers
.
CharField
(
required
=
False
)
endorsed
=
serializers
.
BooleanField
(
read_only
=
True
)
endorsed
=
serializers
.
BooleanField
(
read_only
=
True
)
endorsed_by
=
serializers
.
SerializerMethodField
(
"get_endorsed_by"
)
endorsed_by
=
serializers
.
SerializerMethodField
(
"get_endorsed_by"
)
endorsed_by_label
=
serializers
.
SerializerMethodField
(
"get_endorsed_by_label"
)
endorsed_by_label
=
serializers
.
SerializerMethodField
(
"get_endorsed_by_label"
)
endorsed_at
=
serializers
.
SerializerMethodField
(
"get_endorsed_at"
)
endorsed_at
=
serializers
.
SerializerMethodField
(
"get_endorsed_at"
)
children
=
serializers
.
SerializerMethodField
(
"get_children"
)
children
=
serializers
.
SerializerMethodField
(
"get_children"
)
def
get_parent_id
(
self
,
_obj
):
"""Returns the comment's parent's id (taken from the context)."""
return
self
.
context
[
"parent_id"
]
def
get_endorsed_by
(
self
,
obj
):
def
get_endorsed_by
(
self
,
obj
):
"""
"""
Returns the username of the endorsing user, if the information is
Returns the username of the endorsing user, if the information is
...
@@ -272,11 +264,8 @@ class CommentSerializer(_ContentSerializer):
...
@@ -272,11 +264,8 @@ class CommentSerializer(_ContentSerializer):
return
endorsement
[
"time"
]
if
endorsement
else
None
return
endorsement
[
"time"
]
if
endorsement
else
None
def
get_children
(
self
,
obj
):
def
get_children
(
self
,
obj
):
"""Returns the list of the comment's children, serialized."""
child_context
=
dict
(
self
.
context
)
child_context
[
"parent_id"
]
=
obj
[
"id"
]
return
[
return
[
CommentSerializer
(
child
,
context
=
child_
context
)
.
data
CommentSerializer
(
child
,
context
=
self
.
context
)
.
data
for
child
in
obj
.
get
(
"children"
,
[])
for
child
in
obj
.
get
(
"children"
,
[])
]
]
...
@@ -285,7 +274,7 @@ class CommentSerializer(_ContentSerializer):
...
@@ -285,7 +274,7 @@ class CommentSerializer(_ContentSerializer):
Ensure that parent_id identifies a comment that is actually in the
Ensure that parent_id identifies a comment that is actually in the
thread identified by thread_id.
thread identified by thread_id.
"""
"""
parent_id
=
self
.
context
[
"parent_id"
]
parent_id
=
attrs
.
get
(
"parent_id"
)
if
parent_id
:
if
parent_id
:
parent
=
None
parent
=
None
try
:
try
:
...
@@ -303,7 +292,6 @@ class CommentSerializer(_ContentSerializer):
...
@@ -303,7 +292,6 @@ class CommentSerializer(_ContentSerializer):
raise
ValueError
(
"CommentSerializer cannot be used for updates."
)
raise
ValueError
(
"CommentSerializer cannot be used for updates."
)
return
Comment
(
return
Comment
(
course_id
=
self
.
context
[
"thread"
][
"course_id"
],
course_id
=
self
.
context
[
"thread"
][
"course_id"
],
parent_id
=
self
.
context
[
"parent_id"
],
user_id
=
self
.
context
[
"cc_requester"
][
"id"
],
user_id
=
self
.
context
[
"cc_requester"
][
"id"
],
**
attrs
**
attrs
)
)
lms/djangoapps/discussion_api/tests/test_api.py
View file @
2c7590d1
...
@@ -1220,12 +1220,11 @@ class CreateCommentTest(CommentsServiceMockMixin, UrlResetMixin, ModuleStoreTest
...
@@ -1220,12 +1220,11 @@ class CreateCommentTest(CommentsServiceMockMixin, UrlResetMixin, ModuleStoreTest
self
.
register_post_comment_response
(
self
.
register_post_comment_response
(
{
{
"id"
:
"test_comment"
,
"id"
:
"test_comment"
,
"thread_id"
:
"test_thread"
,
"username"
:
self
.
user
.
username
,
"username"
:
self
.
user
.
username
,
"created_at"
:
"2015-05-27T00:00:00Z"
,
"created_at"
:
"2015-05-27T00:00:00Z"
,
"updated_at"
:
"2015-05-27T00:00:00Z"
,
"updated_at"
:
"2015-05-27T00:00:00Z"
,
},
},
thread_id
=
(
None
if
parent_id
else
"test_thread"
)
,
thread_id
=
"test_thread"
,
parent_id
=
parent_id
parent_id
=
parent_id
)
)
data
=
self
.
minimal_data
.
copy
()
data
=
self
.
minimal_data
.
copy
()
...
...
lms/djangoapps/discussion_api/tests/test_serializers.py
View file @
2c7590d1
...
@@ -354,10 +354,17 @@ class CommentSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
...
@@ -354,10 +354,17 @@ class CommentSerializerTest(SerializerTestMixin, ModuleStoreTestCase):
"children"
:
[
"children"
:
[
self
.
make_cs_content
({
self
.
make_cs_content
({
"id"
:
"test_child_1"
,
"id"
:
"test_child_1"
,
"parent_id"
:
"test_root"
,
}),
}),
self
.
make_cs_content
({
self
.
make_cs_content
({
"id"
:
"test_child_2"
,
"id"
:
"test_child_2"
,
"children"
:
[
self
.
make_cs_content
({
"id"
:
"test_grandchild"
})],
"parent_id"
:
"test_root"
,
"children"
:
[
self
.
make_cs_content
({
"id"
:
"test_grandchild"
,
"parent_id"
:
"test_child_2"
})
],
}),
}),
],
],
})
})
...
@@ -547,7 +554,7 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
...
@@ -547,7 +554,7 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
"raw_body"
:
"Test body"
,
"raw_body"
:
"Test body"
,
}
}
def
save_and_reserialize
(
self
,
data
,
parent_id
=
None
):
def
save_and_reserialize
(
self
,
data
):
"""
"""
Create a serializer with the given data, ensure that it is valid, save
Create a serializer with the given data, ensure that it is valid, save
the result, and return the full comment data from the serializer.
the result, and return the full comment data from the serializer.
...
@@ -555,8 +562,7 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
...
@@ -555,8 +562,7 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
context
=
get_context
(
context
=
get_context
(
self
.
course
,
self
.
course
,
self
.
request
,
self
.
request
,
make_minimal_cs_thread
({
"course_id"
:
unicode
(
self
.
course
.
id
)}),
make_minimal_cs_thread
({
"course_id"
:
unicode
(
self
.
course
.
id
)})
parent_id
)
)
serializer
=
CommentSerializer
(
data
=
data
,
context
=
context
)
serializer
=
CommentSerializer
(
data
=
data
,
context
=
context
)
self
.
assertTrue
(
serializer
.
is_valid
())
self
.
assertTrue
(
serializer
.
is_valid
())
...
@@ -565,14 +571,16 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
...
@@ -565,14 +571,16 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
@ddt.data
(
None
,
"test_parent"
)
@ddt.data
(
None
,
"test_parent"
)
def
test_success
(
self
,
parent_id
):
def
test_success
(
self
,
parent_id
):
data
=
self
.
minimal_data
.
copy
()
if
parent_id
:
if
parent_id
:
data
[
"parent_id"
]
=
parent_id
self
.
register_get_comment_response
({
"thread_id"
:
"test_thread"
,
"id"
:
parent_id
})
self
.
register_get_comment_response
({
"thread_id"
:
"test_thread"
,
"id"
:
parent_id
})
self
.
register_post_comment_response
(
self
.
register_post_comment_response
(
{
"id"
:
"test_comment"
},
{
"id"
:
"test_comment"
},
thread_id
=
(
None
if
parent_id
else
"test_thread"
)
,
thread_id
=
"test_thread"
,
parent_id
=
parent_id
parent_id
=
parent_id
)
)
saved
=
self
.
save_and_reserialize
(
self
.
minimal_data
,
parent_id
=
parent_id
)
saved
=
self
.
save_and_reserialize
(
data
)
expected_url
=
(
expected_url
=
(
"/api/v1/comments/{}"
.
format
(
parent_id
)
if
parent_id
else
"/api/v1/comments/{}"
.
format
(
parent_id
)
if
parent_id
else
"/api/v1/threads/test_thread/comments"
"/api/v1/threads/test_thread/comments"
...
@@ -591,8 +599,10 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
...
@@ -591,8 +599,10 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
def
test_parent_id_nonexistent
(
self
):
def
test_parent_id_nonexistent
(
self
):
self
.
register_get_comment_error_response
(
"bad_parent"
,
404
)
self
.
register_get_comment_error_response
(
"bad_parent"
,
404
)
context
=
get_context
(
self
.
course
,
self
.
request
,
make_minimal_cs_thread
(),
"bad_parent"
)
data
=
self
.
minimal_data
.
copy
()
serializer
=
CommentSerializer
(
data
=
self
.
minimal_data
,
context
=
context
)
data
[
"parent_id"
]
=
"bad_parent"
context
=
get_context
(
self
.
course
,
self
.
request
,
make_minimal_cs_thread
())
serializer
=
CommentSerializer
(
data
=
data
,
context
=
context
)
self
.
assertFalse
(
serializer
.
is_valid
())
self
.
assertFalse
(
serializer
.
is_valid
())
self
.
assertEqual
(
self
.
assertEqual
(
serializer
.
errors
,
serializer
.
errors
,
...
@@ -605,8 +615,10 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
...
@@ -605,8 +615,10 @@ class CommentSerializerDeserializationTest(CommentsServiceMockMixin, ModuleStore
def
test_parent_id_wrong_thread
(
self
):
def
test_parent_id_wrong_thread
(
self
):
self
.
register_get_comment_response
({
"thread_id"
:
"different_thread"
,
"id"
:
"test_parent"
})
self
.
register_get_comment_response
({
"thread_id"
:
"different_thread"
,
"id"
:
"test_parent"
})
context
=
get_context
(
self
.
course
,
self
.
request
,
make_minimal_cs_thread
(),
"test_parent"
)
data
=
self
.
minimal_data
.
copy
()
serializer
=
CommentSerializer
(
data
=
self
.
minimal_data
,
context
=
context
)
data
[
"parent_id"
]
=
"test_parent"
context
=
get_context
(
self
.
course
,
self
.
request
,
make_minimal_cs_thread
())
serializer
=
CommentSerializer
(
data
=
data
,
context
=
context
)
self
.
assertFalse
(
serializer
.
is_valid
())
self
.
assertFalse
(
serializer
.
is_valid
())
self
.
assertEqual
(
self
.
assertEqual
(
serializer
.
errors
,
serializer
.
errors
,
...
...
lms/djangoapps/discussion_api/tests/test_views.py
View file @
2c7590d1
...
@@ -539,7 +539,6 @@ class CommentViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
...
@@ -539,7 +539,6 @@ class CommentViewSetCreateTest(DiscussionAPIViewTestMixin, ModuleStoreTestCase):
self
.
register_post_comment_response
(
self
.
register_post_comment_response
(
{
{
"id"
:
"test_comment"
,
"id"
:
"test_comment"
,
"thread_id"
:
"test_thread"
,
"username"
:
self
.
user
.
username
,
"username"
:
self
.
user
.
username
,
"created_at"
:
"2015-05-27T00:00:00Z"
,
"created_at"
:
"2015-05-27T00:00:00Z"
,
"updated_at"
:
"2015-05-27T00:00:00Z"
,
"updated_at"
:
"2015-05-27T00:00:00Z"
,
...
...
lms/djangoapps/discussion_api/tests/utils.py
View file @
2c7590d1
...
@@ -84,7 +84,7 @@ class CommentsServiceMockMixin(object):
...
@@ -84,7 +84,7 @@ class CommentsServiceMockMixin(object):
status
=
200
status
=
200
)
)
def
register_post_comment_response
(
self
,
response_overrides
,
thread_id
=
None
,
parent_id
=
None
):
def
register_post_comment_response
(
self
,
response_overrides
,
thread_id
,
parent_id
=
None
):
"""
"""
Register a mock response for POST on the CS comments endpoint for the
Register a mock response for POST on the CS comments endpoint for the
given thread or parent; exactly one of thread_id and parent_id must be
given thread or parent; exactly one of thread_id and parent_id must be
...
@@ -99,14 +99,16 @@ class CommentsServiceMockMixin(object):
...
@@ -99,14 +99,16 @@ class CommentsServiceMockMixin(object):
{
key
:
val
[
0
]
for
key
,
val
in
request
.
parsed_body
.
items
()}
{
key
:
val
[
0
]
for
key
,
val
in
request
.
parsed_body
.
items
()}
)
)
response_data
.
update
(
response_overrides
or
{})
response_data
.
update
(
response_overrides
or
{})
# thread_id and parent_id are not included in request payload but
# are returned by the comments service
response_data
[
"thread_id"
]
=
thread_id
response_data
[
"parent_id"
]
=
parent_id
return
(
200
,
headers
,
json
.
dumps
(
response_data
))
return
(
200
,
headers
,
json
.
dumps
(
response_data
))
if
thread_id
and
not
parent_id
:
if
parent_id
:
url
=
"http://localhost:4567/api/v1/threads/{}/comments"
.
format
(
thread_id
)
elif
parent_id
and
not
thread_id
:
url
=
"http://localhost:4567/api/v1/comments/{}"
.
format
(
parent_id
)
url
=
"http://localhost:4567/api/v1/comments/{}"
.
format
(
parent_id
)
else
:
# pragma: no cover
else
:
raise
ValueError
(
"Exactly one of thread_id and parent_id must be provided."
)
url
=
"http://localhost:4567/api/v1/threads/{}/comments"
.
format
(
thread_id
)
httpretty
.
register_uri
(
httpretty
.
POST
,
url
,
body
=
callback
)
httpretty
.
register_uri
(
httpretty
.
POST
,
url
,
body
=
callback
)
...
@@ -228,6 +230,7 @@ def make_minimal_cs_comment(overrides=None):
...
@@ -228,6 +230,7 @@ def make_minimal_cs_comment(overrides=None):
ret
=
{
ret
=
{
"id"
:
"dummy"
,
"id"
:
"dummy"
,
"thread_id"
:
"dummy"
,
"thread_id"
:
"dummy"
,
"parent_id"
:
None
,
"user_id"
:
"0"
,
"user_id"
:
"0"
,
"username"
:
"dummy"
,
"username"
:
"dummy"
,
"anonymous"
:
False
,
"anonymous"
:
False
,
...
...
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