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.
78 lines
2.6 KiB
Python
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
|
|
}
|
|
)
|