def test_create_parent_id_too_deep(self, max_depth): with mock.patch("lms.djangoapps.discussion.django_comment_client.utils.MAX_COMMENT_DEPTH", max_depth): data = self.minimal_data.copy() context = get_context(self.course, self.request, make_minimal_cs_thread()) if max_depth is None or max_depth >= 0: if max_depth != 0: self.register_get_comment_response({ "id": "not_too_deep", "thread_id": "test_thread", "depth": max_depth - 1 if max_depth else 100 }) data["parent_id"] = "not_too_deep" else: data["parent_id"] = None serializer = CommentSerializer(data=data, context=context) self.assertTrue(serializer.is_valid(), serializer.errors) if max_depth is not None: if max_depth >= 0: self.register_get_comment_response({ "id": "too_deep", "thread_id": "test_thread", "depth": max_depth }) data["parent_id"] = "too_deep" else: data["parent_id"] = None serializer = CommentSerializer(data=data, context=context) self.assertFalse(serializer.is_valid()) self.assertEqual(serializer.errors, {"non_field_errors": ["Comment level is too deep."]})
def test_update_empty_raw_body(self, value): serializer = CommentSerializer(self.existing_comment, data={"raw_body": value}, partial=True, context=get_context( self.course, self.request)) self.assertFalse(serializer.is_valid()) self.assertEqual(serializer.errors, {"raw_body": ["This field may not be blank."]})
def test_update_non_updatable(self, field): serializer = CommentSerializer(self.existing_comment, data={field: "different_value"}, partial=True, context=get_context( self.course, self.request)) self.assertFalse(serializer.is_valid()) self.assertEqual(serializer.errors, {field: ["This field is not allowed in an update."]})
def test_update_empty_raw_body(self, value): serializer = CommentSerializer( self.existing_comment, data={"raw_body": value}, partial=True, context=get_context(self.course, self.request) ) assert not serializer.is_valid() assert serializer.errors == {'raw_body': ['This field may not be blank.']}
def test_create_parent_id_wrong_thread(self): self.register_get_comment_response({"thread_id": "different_thread", "id": "test_parent"}) data = self.minimal_data.copy() data["parent_id"] = "test_parent" context = get_context(self.course, self.request, make_minimal_cs_thread()) serializer = CommentSerializer(data=data, context=context) assert not serializer.is_valid() assert serializer.errors == { 'non_field_errors': ['parent_id does not identify a comment in the thread identified by thread_id.'] }
def test_create_missing_field(self): for field in self.minimal_data: data = self.minimal_data.copy() data.pop(field) serializer = CommentSerializer(data=data, context=get_context( self.course, self.request, make_minimal_cs_thread())) assert not serializer.is_valid() assert serializer.errors == {field: ['This field is required.']}
def test_update_non_updatable(self, field): serializer = CommentSerializer( self.existing_comment, data={field: "different_value"}, partial=True, context=get_context(self.course, self.request) ) self.assertFalse(serializer.is_valid()) self.assertEqual( serializer.errors, {field: ["This field is not allowed in an update."]} )
def test_update_empty_raw_body(self, value): serializer = CommentSerializer( self.existing_comment, data={"raw_body": value}, partial=True, context=get_context(self.course, self.request) ) self.assertFalse(serializer.is_valid()) self.assertEqual( serializer.errors, {"raw_body": ["This field may not be blank."]} )
def test_create_missing_field(self): for field in self.minimal_data: data = self.minimal_data.copy() data.pop(field) serializer = CommentSerializer( data=data, context=get_context(self.course, self.request, make_minimal_cs_thread()) ) self.assertFalse(serializer.is_valid()) self.assertEqual( serializer.errors, {field: ["This field is required."]} )
def create_comment(request, comment_data): """ Create a comment. Arguments: request: The django request object used for build_absolute_uri and determining the requesting user. comment_data: The data for the created comment. Returns: The created comment; see discussion.rest_api.views.CommentViewSet for more detail. """ thread_id = comment_data.get("thread_id") if not thread_id: raise ValidationError({"thread_id": ["This field is required."]}) cc_thread, context = _get_thread_and_context(request, thread_id) course = context["course"] if not discussion_open_for_user(course, request.user): raise DiscussionBlackOutException # if a thread is closed; no new comments could be made to it if cc_thread["closed"]: raise PermissionDenied _check_initializable_comment_fields(comment_data, context) serializer = CommentSerializer(data=comment_data, context=context) actions_form = CommentActionsForm(comment_data) if not (serializer.is_valid() and actions_form.is_valid()): raise ValidationError( dict( list(serializer.errors.items()) + list(actions_form.errors.items()))) serializer.save() cc_comment = serializer.instance comment_created.send(sender=None, user=request.user, post=cc_comment) api_comment = serializer.data _do_extra_actions(api_comment, cc_comment, list(comment_data.keys()), actions_form, context, request) track_comment_created_event(request, course, cc_comment, cc_thread["commentable_id"], followed=False) return api_comment
def test_create_parent_id_nonexistent(self): self.register_get_comment_error_response("bad_parent", 404) data = self.minimal_data.copy() 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.assertEqual( serializer.errors, { "non_field_errors": [ "parent_id does not identify a comment in the thread identified by thread_id." ] })
def update_comment(request, comment_id, update_data): """ Update a comment. Arguments: request: The django request object used for build_absolute_uri and determining the requesting user. comment_id: The id for the comment to update. update_data: The data to update in the comment. Returns: The updated comment; see discussion.rest_api.views.CommentViewSet for more detail. Raises: CommentNotFoundError: if the comment does not exist or is not accessible to the requesting user PermissionDenied: if the comment is accessible to but not editable by the requesting user ValidationError: if there is an error applying the update (e.g. raw_body is empty or thread_id is included) """ cc_comment, context = _get_comment_and_context(request, comment_id) _check_editable_fields(cc_comment, update_data, context) serializer = CommentSerializer(cc_comment, data=update_data, partial=True, context=context) actions_form = CommentActionsForm(update_data) if not (serializer.is_valid() and actions_form.is_valid()): raise ValidationError( dict( list(serializer.errors.items()) + list(actions_form.errors.items()))) # Only save comment object if some of the edited fields are in the comment data, not extra actions if set(update_data) - set(actions_form.fields): serializer.save() comment_edited.send(sender=None, user=request.user, post=cc_comment) api_comment = serializer.data _do_extra_actions(api_comment, cc_comment, list(update_data.keys()), actions_form, context, request) return api_comment
def test_create_parent_id_wrong_thread(self): self.register_get_comment_response({"thread_id": "different_thread", "id": "test_parent"}) data = self.minimal_data.copy() 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.assertEqual( serializer.errors, { "non_field_errors": [ "parent_id does not identify a comment in the thread identified by thread_id." ] } )
def save_and_reserialize(self, data, instance=None): """ Create a serializer with the given data, ensure that it is valid, save the result, and return the full comment data from the serializer. """ context = get_context( self.course, self.request, make_minimal_cs_thread({"course_id": str(self.course.id)})) serializer = CommentSerializer(instance, data=data, partial=(instance is not None), context=context) assert serializer.is_valid() serializer.save() return serializer.data
def serialize(self, comment, thread_data=None): """ Create a serializer with an appropriate context and use it to serialize the given comment, returning the result. """ context = get_context(self.course, self.request, make_minimal_cs_thread(thread_data)) return CommentSerializer(comment, context=context).data
def save_and_reserialize(self, data, instance=None): """ Create a serializer with the given data, ensure that it is valid, save the result, and return the full comment data from the serializer. """ context = get_context( self.course, self.request, make_minimal_cs_thread({"course_id": six.text_type(self.course.id)}) ) serializer = CommentSerializer( instance, data=data, partial=(instance is not None), context=context ) self.assertTrue(serializer.is_valid()) serializer.save() return serializer.data
def update_comment(request, comment_id, update_data): """ Update a comment. Arguments: request: The django request object used for build_absolute_uri and determining the requesting user. comment_id: The id for the comment to update. update_data: The data to update in the comment. Returns: The updated comment; see discussion.rest_api.views.CommentViewSet for more detail. Raises: CommentNotFoundError: if the comment does not exist or is not accessible to the requesting user PermissionDenied: if the comment is accessible to but not editable by the requesting user ValidationError: if there is an error applying the update (e.g. raw_body is empty or thread_id is included) """ cc_comment, context = _get_comment_and_context(request, comment_id) _check_editable_fields(cc_comment, update_data, context) serializer = CommentSerializer(cc_comment, data=update_data, partial=True, context=context) actions_form = CommentActionsForm(update_data) if not (serializer.is_valid() and actions_form.is_valid()): raise ValidationError(dict(serializer.errors.items() + actions_form.errors.items())) # Only save comment object if some of the edited fields are in the comment data, not extra actions if set(update_data) - set(actions_form.fields): serializer.save() comment_edited.send(sender=None, user=request.user, post=cc_comment) api_comment = serializer.data _do_extra_actions(api_comment, cc_comment, update_data.keys(), actions_form, context, request) return api_comment
def test_create_parent_id_too_deep(self, max_depth): with mock.patch("lms.djangoapps.discussion.django_comment_client.utils.MAX_COMMENT_DEPTH", max_depth): data = self.minimal_data.copy() context = get_context(self.course, self.request, make_minimal_cs_thread()) if max_depth is None or max_depth >= 0: if max_depth != 0: self.register_get_comment_response({ "id": "not_too_deep", "thread_id": "test_thread", "depth": max_depth - 1 if max_depth else 100 }) data["parent_id"] = "not_too_deep" else: data["parent_id"] = None serializer = CommentSerializer(data=data, context=context) assert serializer.is_valid(), serializer.errors if max_depth is not None: if max_depth >= 0: self.register_get_comment_response({ "id": "too_deep", "thread_id": "test_thread", "depth": max_depth }) data["parent_id"] = "too_deep" else: data["parent_id"] = None serializer = CommentSerializer(data=data, context=context) assert not serializer.is_valid() assert serializer.errors == {'non_field_errors': ['Comment level is too deep.']}
def create_comment(request, comment_data): """ Create a comment. Arguments: request: The django request object used for build_absolute_uri and determining the requesting user. comment_data: The data for the created comment. Returns: The created comment; see discussion.rest_api.views.CommentViewSet for more detail. """ thread_id = comment_data.get("thread_id") if not thread_id: raise ValidationError({"thread_id": ["This field is required."]}) cc_thread, context = _get_thread_and_context(request, thread_id) # if a thread is closed; no new comments could be made to it if cc_thread['closed']: raise PermissionDenied _check_initializable_comment_fields(comment_data, context) serializer = CommentSerializer(data=comment_data, context=context) actions_form = CommentActionsForm(comment_data) if not (serializer.is_valid() and actions_form.is_valid()): raise ValidationError(dict(serializer.errors.items() + actions_form.errors.items())) serializer.save() cc_comment = serializer.instance comment_created.send(sender=None, user=request.user, post=cc_comment) api_comment = serializer.data _do_extra_actions(api_comment, cc_comment, comment_data.keys(), actions_form, context, request) track_comment_created_event(request, context["course"], cc_comment, cc_thread["commentable_id"], followed=False) return api_comment
def _serialize_discussion_entities(request, context, discussion_entities, requested_fields, discussion_entity_type): """ It serializes Discussion Entity (Thread or Comment) and add additional data if requested. For a given list of Thread/Comment; it serializes and add additional information to the object as per requested_fields list (i.e. profile_image). Parameters: request: The django request object context: The context appropriate for use with the thread or comment discussion_entities: List of Thread or Comment objects requested_fields: Indicates which additional fields to return for each thread. discussion_entity_type: DiscussionEntity Enum value for Thread or Comment Returns: A list of serialized discussion entities """ results = [] usernames = [] include_profile_image = _include_profile_image(requested_fields) for entity in discussion_entities: if discussion_entity_type == DiscussionEntity.thread: serialized_entity = ThreadSerializer(entity, context=context).data elif discussion_entity_type == DiscussionEntity.comment: serialized_entity = CommentSerializer(entity, context=context).data results.append(serialized_entity) if include_profile_image: if serialized_entity['author'] and serialized_entity[ 'author'] not in usernames: usernames.append(serialized_entity['author']) if ('endorsed' in serialized_entity and serialized_entity['endorsed'] and 'endorsed_by' in serialized_entity and serialized_entity['endorsed_by'] and serialized_entity['endorsed_by'] not in usernames): usernames.append(serialized_entity['endorsed_by']) results = _add_additional_response_fields(request, results, usernames, discussion_entity_type, include_profile_image) return results