From 0971ff1e771fe90eabd3efc976da488600f970ed Mon Sep 17 00:00:00 2001 From: James Lee <82937700+SafetyInObscurity@users.noreply.github.com> Date: Sat, 18 Jan 2025 11:34:46 +0800 Subject: [PATCH] Issue 90 create api view to modify user preferences (#100) * Getting started * Trailing slash * Finish view * Wrote tests * Adding bio * More testing * Requested changes --- server/bingo/serializers.py | 7 ++++ server/bingo/tests/test_user_model.py | 50 +++++++++++++++++++++++++++ server/bingo/urls.py | 2 +- server/bingo/views.py | 13 ++++++- 4 files changed, 70 insertions(+), 2 deletions(-) diff --git a/server/bingo/serializers.py b/server/bingo/serializers.py index c07c991..aaada2d 100644 --- a/server/bingo/serializers.py +++ b/server/bingo/serializers.py @@ -1,4 +1,5 @@ from rest_framework import serializers +from rest_framework.serializers import ModelSerializer from django.contrib.auth import get_user_model from django.contrib.auth.password_validation import validate_password from .models import BingoGrid, Challenge @@ -80,3 +81,9 @@ class BingoGridSerializer(serializers.ModelSerializer): class Meta: model = BingoGrid fields = ['grid_id', 'challenges'] + + +class UpdatePreferencesSerializer(ModelSerializer): + class Meta: + model = User + fields = ["avatar", "bio", "visibility"] diff --git a/server/bingo/tests/test_user_model.py b/server/bingo/tests/test_user_model.py index be551eb..e65465d 100644 --- a/server/bingo/tests/test_user_model.py +++ b/server/bingo/tests/test_user_model.py @@ -1,8 +1,10 @@ from django.db.utils import IntegrityError from django.test import TestCase +from django.urls import reverse from ..models import User from django.core.exceptions import ValidationError from datetime import date +from rest_framework.test import APIClient class UsersTest(TestCase): @@ -63,3 +65,51 @@ def test_email_uniqueness(self): password="A string of random characters", email="test@example.com" ) + + +class UpdatePreferencesTest(TestCase): + def setUp(self): + self.user = User.objects.create( + username="Test", + password="A generic, valid password", + email="test@example.com", + first_name="Jane", + last_name="Doe", + birthdate=date(1, 1, 1), + avatar=0, + visibility=User.Visibility.BLUECREW + ) + self.client = APIClient() + self.client.force_authenticate(self.user) + + def request(self, avatar=1, visibility=User.Visibility.FRIENDS, bio="words"): + return self.client.put( + reverse("update_preferences"), + {"avatar": avatar, "visibility": visibility, "bio": bio} + ) + + def test_updates(self): + self.assertEqual(self.request().status_code, 200) + self.assertEqual(self.user.avatar, 1) + self.assertEqual(self.user.visibility, User.Visibility.FRIENDS) + self.assertEqual(self.user.bio, "words") + + def test_invalid(self): + self.assertEqual(self.request(-1, User.Visibility.BLUECREW).status_code, 400) + self.assertEqual(self.request('a', User.Visibility.PUBLIC).status_code, 400) + self.assertEqual(self.request(5, 3).status_code, 400) + self.assertEqual(self.request(bio="!"*301).status_code, 400) + + def test_subsequent(self): + self.assertEqual(self.request(5, 2, "").status_code, 200) + self.assertEqual(self.request(3, 1, "other words").status_code, 200) + self.assertEqual(self.user.visibility, 1) + self.assertEqual(self.user.avatar, 3) + self.assertEqual(self.user.bio, "other words") + + def test_unauthenitcated(self): + response = APIClient().put( + reverse("update_preferences"), + {"avatar": 1, "visibility": User.Visibility.FRIENDS, "bio": "Stuff and things"} + ) + self.assertEqual(response.status_code, 401) diff --git a/server/bingo/urls.py b/server/bingo/urls.py index fb7cc9e..959cc0e 100644 --- a/server/bingo/urls.py +++ b/server/bingo/urls.py @@ -10,6 +10,7 @@ path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), path('register/', views.register_user, name='register_user'), + path('update-preferences/', views.update_user_preferences, name='update_preferences'), path('leaderboard/', views.get_leaderboard, name='get_leaderboard'), path('user/me/', views.get_current_user, name='current_user'), path('delete-friendship//', @@ -23,5 +24,4 @@ path('bingo-grid/', views.get_bingo_grid, name='get_bingo_grid'), path('accept-friendship//', views.accept_friendship, name='accept_friendship'), - ] diff --git a/server/bingo/views.py b/server/bingo/views.py index 1ce9e00..ab73fbb 100644 --- a/server/bingo/views.py +++ b/server/bingo/views.py @@ -1,5 +1,5 @@ from rest_framework import status, permissions -from .serializers import UserRegisterSerializer, UserProfileSerializer, LeaderboardUserSerializer, BingoGridSerializer +from .serializers import UserRegisterSerializer, UserProfileSerializer, LeaderboardUserSerializer, BingoGridSerializer, UpdatePreferencesSerializer from django.shortcuts import get_object_or_404 from django.core.exceptions import ObjectDoesNotExist from rest_framework.decorators import api_view, permission_classes @@ -44,6 +44,17 @@ def get_current_user(request): return Response(serializer.data, status=status.HTTP_200_OK) +@api_view(['PUT']) +@permission_classes([IsAuthenticated]) +def update_user_preferences(request): + serializer = UpdatePreferencesSerializer(instance=request.user, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(status=status.HTTP_200_OK) + else: + return Response(data=serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + @api_view(['POST']) @permission_classes([IsAuthenticated]) def start_challenge(request):