Skip to content
Snippets Groups Projects
Unverified Commit 503b2ff0 authored by Brian Mesick's avatar Brian Mesick Committed by GitHub
Browse files

Merge pull request #17818 from edx/bmedx/lms_forums_retire_api

Create API endpoint to retire cs_comments_service users
parents fa8b05a6 ada7ded5
No related merge requests found
This diff is collapsed.
......@@ -216,6 +216,16 @@ class CommentsServiceMockMixin(object):
status=200
)
def register_get_user_retire_response(self, user, status=200, body=""):
"""Register a mock response for GET on the CS user retirement endpoint"""
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
httpretty.register_uri(
httpretty.POST,
"http://localhost:4567/api/v1/users/{id}/retire".format(id=user.id),
body=body,
status=status
)
def register_subscribed_threads_response(self, user, threads, page, num_pages):
"""Register a mock response for GET on the CS user instance endpoint"""
assert httpretty.is_enabled(), 'httpretty must be enabled to mock calls.'
......
......@@ -5,7 +5,7 @@ from django.conf import settings
from django.conf.urls import include, url
from rest_framework.routers import SimpleRouter
from discussion_api.views import CommentViewSet, CourseTopicsView, CourseView, ThreadViewSet
from discussion_api.views import CommentViewSet, CourseTopicsView, CourseView, ThreadViewSet, RetireUserView
ROUTER = SimpleRouter()
ROUTER.register("threads", ThreadViewSet, base_name="thread")
......@@ -17,6 +17,7 @@ urlpatterns = [
CourseView.as_view(),
name="discussion_course"
),
url(r"^v1/users/{}".format(settings.USERNAME_PATTERN), RetireUserView.as_view(), name="retire_discussion_user"),
url(
r"^v1/course_topics/{}".format(settings.COURSE_ID_PATTERN),
CourseTopicsView.as_view(),
......
......@@ -2,13 +2,19 @@
Discussion API views
"""
from django.core.exceptions import ValidationError
from django.contrib.auth import get_user_model
from edx_rest_framework_extensions.authentication import JwtAuthentication
from opaque_keys.edx.keys import CourseKey
from rest_framework import permissions
from rest_framework import status
from rest_framework.exceptions import UnsupportedMediaType
from rest_framework.parsers import JSONParser
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSet
from six import text_type
from lms.lib import comment_client
from discussion_api.api import (
create_comment,
create_thread,
......@@ -26,6 +32,8 @@ from discussion_api.api import (
from discussion_api.forms import CommentGetForm, CommentListGetForm, ThreadListGetForm
from openedx.core.lib.api.parsers import MergePatchParser
from openedx.core.lib.api.view_utils import DeveloperErrorViewMixin, view_auth_classes
from openedx.core.djangoapps.user_api.accounts.permissions import CanRetireUser
from student.models import get_potentially_retired_user_by_username_and_hash
from xmodule.modulestore.django import modulestore
......@@ -512,3 +520,48 @@ class CommentViewSet(DeveloperErrorViewMixin, ViewSet):
if request.content_type != MergePatchParser.media_type:
raise UnsupportedMediaType(request.content_type)
return Response(update_comment(request, comment_id, request.data))
class RetireUserView(APIView):
"""
**Use Cases**
A superuser or the user with the settings.RETIREMENT_SERVICE_WORKER_USERNAME
can "retire" the user's data from the comments service, which will remove
personal information and blank all posts / comments the user has made.
**Example Requests**:
POST /api/discussion/v1/retire_user/
{
"retired_username": "old_user_name"
}
**Example Response**:
Empty string
"""
authentication_classes = (JwtAuthentication,)
permission_classes = (permissions.IsAuthenticated, CanRetireUser)
def post(self, request, username):
"""
Implements the retirement endpoint.
"""
user_model = get_user_model()
retired_username = request.data['retired_username']
try:
user = get_potentially_retired_user_by_username_and_hash(username, retired_username)
cc_user = comment_client.User.from_django_user(user)
# We can't count on the LMS username being un-retired at this point,
# so we pass the old username as a parameter to describe which
# user to retire. This will either succeed or throw an error which
# should be good to raise from here.
cc_user.retire(username)
except user_model.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
except Exception as exc: # pylint: disable=broad-except
return Response(text_type(exc), status=status.HTTP_500_INTERNAL_SERVER_ERROR)
return Response(status=status.HTTP_204_NO_CONTENT)
......@@ -2,9 +2,7 @@
from six import text_type
import settings
import models
import utils
......@@ -169,6 +167,19 @@ class User(models.Model):
raise
self._update_from_response(response)
def retire(self, retired_username):
url = _url_for_retire(self.id)
params = {'retired_username': retired_username}
utils.perform_request(
'post',
url,
params,
raw=True,
metric_action='user.retire',
metric_tags=self._metric_tags
)
def _url_for_vote_comment(comment_id):
return "{prefix}/comments/{comment_id}/votes".format(prefix=settings.PREFIX, comment_id=comment_id)
......@@ -195,3 +206,10 @@ def _url_for_read(user_id):
Returns cs_comments_service url endpoint to mark thread as read for given user_id
"""
return "{prefix}/users/{user_id}/read".format(prefix=settings.PREFIX, user_id=user_id)
def _url_for_retire(user_id):
"""
Returns cs_comments_service url endpoint to retire a user (remove all post content, etc.)
"""
return "{prefix}/users/{user_id}/retire".format(prefix=settings.PREFIX, user_id=user_id)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment