Files
edx-platform/lms/djangoapps/bulk_user_retirement/views.py
Ned Batchelder fee4ec26f5 fix: return a sorted list so tests can be deterministic
Tests on Maple were failing:

```
    def test_retirement_for_multiple_users(self):
        user_retirement_url = reverse('bulk_retirement_api')
        expected_response = {
            'successful_user_retirements': [self.user3.username, self.user4.username],
            'failed_user_retirements': []
        }
        with self.settings(RETIREMENT_SERVICE_WORKER_USERNAME=self.user1.username):
            response = self.client.post(user_retirement_url, {
                "usernames": f'{self.user3.username},{self.user4.username}'
            })
            assert response.status_code == 200
>           assert response.data == expected_response
E           AssertionError: assert {'failed_user... 'testuser3']} == {'failed_user... 'testuser4']}
E             Omitting 1 identical items, use -vv to show
E             Differing items:
E             {'successful_user_retirements': ['testuser4', 'testuser3']} != {'successful_user_retirements': ['testuser3', 'testuser4']}
E             Use -v to get the full diff
```

`sorted(set(...))` still produces a list, and I guess we didn't care
about the order before, since it wasn't determined.  So this should be
an acceptable change.
2022-01-31 04:09:45 -08:00

78 lines
2.6 KiB
Python

"""
An API for retiring user accounts.
"""
import logging
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from django.contrib.auth import get_user_model
from django.db import transaction
from rest_framework import permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView
from openedx.core.djangoapps.user_api.accounts.permissions import CanRetireUser
from openedx.core.djangoapps.user_api.accounts.utils import create_retirement_request_and_deactivate_account
log = logging.getLogger(__name__)
class BulkUsersRetirementView(APIView):
"""
**Use Case**
Implementation for Bulk User Retirement API. Creates a retirement request
for one or more users.
**Example Request**
POST /v1/accounts/bulk_retire_users {
"usernames": "test_user1, test_user2"
}
**POST Parameters**
A POST request can include the following parameter.
* usernames: Comma separated strings of usernames that should be retired.
"""
authentication_classes = (JwtAuthentication, )
permission_classes = (permissions.IsAuthenticated, CanRetireUser)
def post(self, request, **kwargs): # pylint: disable=unused-argument
"""
Initiates the bulk retirement process for the given users.
"""
request_usernames = request.data.get('usernames')
if request_usernames:
usernames_to_retire = [each_username.strip() for each_username in request_usernames.split(',')]
else:
usernames_to_retire = []
User = get_user_model()
successful_user_retirements, failed_user_retirements = [], []
for username in usernames_to_retire:
try:
user_to_retire = User.objects.get(username=username)
with transaction.atomic():
create_retirement_request_and_deactivate_account(user_to_retire)
except User.DoesNotExist:
log.exception(f'The user "{username}" does not exist.')
failed_user_retirements.append(username)
except Exception as exc: # pylint: disable=broad-except
log.exception(f'500 error retiring account {exc}')
failed_user_retirements.append(username)
successful_user_retirements = sorted(set(usernames_to_retire).difference(failed_user_retirements))
return Response(
status=status.HTTP_200_OK,
data={
"successful_user_retirements": successful_user_retirements,
"failed_user_retirements": failed_user_retirements
}
)