diff --git a/apps/accounts/serializers.py b/apps/accounts/serializers.py index 911f8ec5a371b95821235497f11578d16bf0ace3..71523ef14f7cba7ab7fa8c0f520e4509068d702e 100644 --- a/apps/accounts/serializers.py +++ b/apps/accounts/serializers.py @@ -5,6 +5,7 @@ from rest_framework import serializers from apps.accounts.models import Account from ..cases.constants import SUB_DISTRICT_MAPPING +from apps.cases.constants import (DISTRICT_CHOICES, SUB_DISTRICT_CHOICES, SUB_DISTRICT_MAPPING) class AccountSerializer(serializers.ModelSerializer): @@ -85,3 +86,40 @@ class AccountChangePasswordSerializer(serializers.Serializer): class AccountSetRandomPasswordSerializer(serializers.Serializer): email = serializers.EmailField() + + +class AccountUpdateProfileSerializer(serializers.ModelSerializer): + username = serializers.CharField(max_length=128) + password = serializers.CharField(max_length=128) + confirm_password = serializers.CharField(max_length=128) + + class Meta: + model = Account + fields = [ + "id", + "username", + "password", + "confirm_password", + "name", + "email", + "phone_number", + "district", + "sub_district", + ] + + def validate(self, data): + district = data['district'] + sub_district = data['sub_district'] + if sub_district not in SUB_DISTRICT_MAPPING[district]: + raise ValidationError(_('Inconsistent district and sub district value')) + if data['password'] != data['confirm_password']: + raise ValidationError(_('Inconsistent password and confirmation')) + return data + + def validate_password(self, value): + password_validation.validate_password(value) + return value + + def to_representation(self, instance): + serializer = AccountSerializer(instance) + return serializer.data diff --git a/apps/accounts/tests/test_units/test_accounts.py b/apps/accounts/tests/test_units/test_accounts.py index 794232d5afac7264bcf2fa9b58904e0c78196e6b..1093787f4af93af359a52935e7053b64ff28f488 100644 --- a/apps/accounts/tests/test_units/test_accounts.py +++ b/apps/accounts/tests/test_units/test_accounts.py @@ -1,5 +1,6 @@ import json import random +import factory from faker import Faker from django.core import mail from django.core.exceptions import ValidationError @@ -459,3 +460,54 @@ class AccountViewTest(APITestCase): # password reset email sent self.assertEqual(current_outbox_count, prev_outbox_count + 1) + + def test_update_profile_success(self): + url = self.BASE_URL + "update_profile" + "/" + data = { + "username": "changedname", + "password": "changedtest", + "confirm_password": "changedtest", + "name": "changedusername", + "email": "email@email.com", + "phone_number": "+628899889988", + "district": "Beji", + "sub_district": "Pondok Cina", + } + + response = self.client.put(path=url, data=data, format="json",) + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + def test_update_profile_fails_with_inconsistent_confirmation_password(self): + url = self.BASE_URL + "update_profile" + "/" + data = { + "username": "changedname", + "password": "changedtest", + "confirm_password": "changedtestinconsistent", + "name": "changedusername", + "email": "email@email.com", + "phone_number": "+628899889988", + "district": "Beji", + "sub_district": "Pondok Cina", + } + + response = self.client.put(path=url, data=data, format="json",) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_update_profile_fails_with_inconsistent_district_and_sub_district_value(self): + url = self.BASE_URL + "update_profile" + "/" + data = { + "username": "changedname", + "password": "changedtest", + "confirm_password": "changedtestinconsistent", + "name": "changedusername", + "email": "email@email.com", + "phone_number": "+628899889988", + "district": "Beji", + "sub_district": "Limo", + } + + response = self.client.put(path=url, data=data, format="json",) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) diff --git a/apps/accounts/views.py b/apps/accounts/views.py index 2cfba00a90f8b2108be32b8f163746dc1a488130..3dcec4aa63d5717efd0adc1dc52d62678cc89e68 100644 --- a/apps/accounts/views.py +++ b/apps/accounts/views.py @@ -21,6 +21,7 @@ from apps.accounts.serializers import ( AccountRegisterSerializer, AccountChangePasswordSerializer, AccountSetRandomPasswordSerializer, + AccountUpdateProfileSerializer, ) from apps.commons.permissions import ( IsAuthenticated, @@ -67,6 +68,8 @@ class AccountViewSet(viewsets.ModelViewSet): return AccountChangePasswordSerializer if self.action in ['set_random_password']: return AccountSetRandomPasswordSerializer + if self.action in ['update_profile']: + return AccountUpdateProfileSerializer return AccountSerializer def create(self, request): @@ -183,3 +186,40 @@ class AccountViewSet(viewsets.ModelViewSet): ) return Response({"Password successfully changed"}, status=status.HTTP_200_OK) + + @action(detail=False, methods=["put"]) + def update_profile(self, request): + user = request.user + + serializer_class = self.get_serializer_class() + serializer = serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + + new_username = serializer.validated_data.pop("username") + new_password = serializer.validated_data.pop("password") + confirm_new_password = serializer.validated_data.pop("confirm_password") + new_name = serializer.validated_data.pop("name") + new_email = serializer.validated_data.pop("email") + new_phone_number = serializer.validated_data.pop("phone_number") + new_district = serializer.validated_data.pop("district") + new_sub_district = serializer.validated_data.pop("sub_district") + + user.username = new_username + user.set_password(new_password) + user.name = new_name + user.email = new_email + user.phone_number = new_phone_number + user.district = new_district + user.sub_district = new_sub_district + user.save() + + account = Account.objects.get(user=user) + account.username = new_username + account.name = new_name + account.email = new_email + account.phone_number = new_phone_number + account.district = new_district + account.sub_district = new_sub_district + account.save() + + return Response({"Profile successfully updated"}, status=status.HTTP_200_OK)