Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API endpoint for setting user email address (STAM-5) #177

Merged
merged 3 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/respa-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
# the tests for that module which breaks. Also remove the location of source code.
- name: Install python packages
run: |
pip install setuptools wheel
pip install -r requirements.txt

# Not sure why some of the strings are not translated during test. Thus,
Expand Down
2 changes: 1 addition & 1 deletion requirements.in
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ django-ckeditor==5.9.0
django-cors-headers
django-image-cropping
easy_thumbnails
git+https://github.com/Tampere/django-tamusers.git@9eb5f84171b1a2f525f28de408171608dd122574#egg=django-tamusers
git+https://github.com/Tampere/django-tamusers.git@31b9251bcd9e769bd446528009377834cb68e760#egg=django-tamusers
django-allauth
djangorestframework-jwt
django-storages[google]
Expand Down
4 changes: 2 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# This file is autogenerated by pip-compile
# To update, run:
#
# pip-compile
# pip-compile requirements.in
#
--no-binary psycopg2

Expand Down Expand Up @@ -45,7 +45,7 @@ django-parler==1.9.2 # via -r requirements.in, django-munigeo, django-parle
django-reversion==3.0.2 # via -r requirements.in
django-sanitized-dump==0.2.2 # via -r requirements.in
django-storages[google]==1.12.3 # via -r requirements.in
git+https://github.com/Tampere/django-tamusers.git@9eb5f84171b1a2f525f28de408171608dd122574#egg=django-tamusers # via -r requirements.in
git+https://github.com/Tampere/django-tamusers.git@31b9251bcd9e769bd446528009377834cb68e760#egg=django-tamusers # via -r requirements.in
django==2.2.11 # via -r requirements.in, django-admin-json-editor, django-allauth, django-anymail, django-filter, django-jinja, django-mptt, django-munigeo, django-reversion, django-storages, django-tamusers, drf-oidc-auth, easy-thumbnails
djangorestframework-jwt==1.11.0 # via -r requirements.in
djangorestframework==3.9.1 # via -r requirements.in, django-parler-rest, drf-oidc-auth
Expand Down
49 changes: 48 additions & 1 deletion users/api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from allauth.socialaccount.models import EmailAddress
from django.contrib.auth import get_user_model
from rest_framework import permissions, serializers, generics, mixins, viewsets
from rest_framework import permissions, serializers, generics, viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response

from resources.models.utils import build_ical_feed_url
from resources.models import Unit
Expand Down Expand Up @@ -69,6 +72,50 @@ def get_object(self):
else:
obj = self.request.user
return obj

@action(detail=True, methods=["post"], url_path="set_email")
def set_email(self, request, pk=None):
user = self.get_object()
email = request.data.get("email")
requesting_user = request.user

if user != requesting_user:
return Response(
{"detail": "Not allowed to set email for this user."},
status=status.HTTP_403_FORBIDDEN
)

if not email:
return Response(
{"detail": "Email address is required."},
status=status.HTTP_400_BAD_REQUEST
)

if user.email:
return Response(
{"detail": "User already has an email set."},
status=status.HTTP_400_BAD_REQUEST
)

if EmailAddress.objects.filter(email=email):
return Response(
{"detail": "The email address is already in use."},
status=status.HTTP_400_BAD_REQUEST
)

# Update the existing EmailAddress
email_address = EmailAddress.objects.filter(user=user)
if email_address:
email_address.update(email=email)

# Update the User
user.email = email
user.save()

return Response(
{"detail": "Email set successfully."},
status=status.HTTP_200_OK
)

permission_classes = [permissions.IsAuthenticated]
queryset = get_user_model().objects.all()
Expand Down
3 changes: 0 additions & 3 deletions users/tests.py

This file was deleted.

Empty file added users/tests/__init__.py
Empty file.
76 changes: 76 additions & 0 deletions users/tests/test_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from allauth.account.models import EmailAddress
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APIClient, APITestCase

User = get_user_model()


class SetUserEmailTests(APITestCase):
def setUp(self):
# Create a test user
self.user = User.objects.create_user(
username="testuser", password="testpassword", email=""
)
self.client = APIClient()
self.client.force_authenticate(user=self.user)
EmailAddress.objects.create(user=self.user, email="")

# Create another user
self.other_user = User.objects.create_user(
username="otheruser", password="otherpassword", email="[email protected]"
)
EmailAddress.objects.create(user=self.other_user, email="[email protected]")

def test_set_email_success(self):
url = reverse("user-set-email", kwargs={"pk": self.user.pk})
data = {"email": "[email protected]"}

response = self.client.post(url, data, format="json")

self.user.refresh_from_db()
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data, {"detail": "Email set successfully."})
self.assertEqual(self.user.email, "[email protected]")
self.assertTrue(
EmailAddress.objects.filter(
user=self.user, email="[email protected]"
).exists()
)

def test_set_email_already_set(self):
# Set an email for the user first
self.user.email = "[email protected]"
self.user.save()

url = reverse("user-set-email", kwargs={"pk": self.user.pk})
data = {"email": "[email protected]"}

response = self.client.post(url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.data, {"detail": "User already has an email set."})
self.assertEqual(self.user.email, "[email protected]")

def test_set_email_not_provided(self):
url = reverse("user-set-email", kwargs={"pk": self.user.pk})
data = {"email": ""} # No email in the data

response = self.client.post(url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(response.data, {"detail": "Email address is required."})
self.assertEqual(self.user.email, "")

def test_set_email_already_in_use(self):
url = reverse("user-set-email", kwargs={"pk": self.user.pk})
data = {"email": "[email protected]"} # Email already used by other_user

response = self.client.post(url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(
response.data, {"detail": "The email address is already in use."}
)
self.assertEqual(self.user.email, "")
Loading