-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fix formatting * created tests for send_email endpoint * Fix code scanning alert no. 45: Information exposure through an exception Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * fixed errors in send_email and in tests * lint * added tests for empty lists and for sending mail to multiple users --------- Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
- Loading branch information
1 parent
3b9004e
commit 527d0c2
Showing
6 changed files
with
346 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
app/communication/migrations/0011_alter_usernotificationsetting_notification_type.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# Generated by Django 4.2.5 on 2024-09-23 18:02 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
("communication", "0010_alter_usernotificationsetting_notification_type"), | ||
] | ||
|
||
operations = [ | ||
migrations.AlterField( | ||
model_name="usernotificationsetting", | ||
name="notification_type", | ||
field=models.CharField( | ||
choices=[ | ||
("REGISTRATION", "Påmeldingsoppdateringer"), | ||
("UNREGISTRATION", "Avmeldingsoppdateringer"), | ||
("STRIKE", "Prikkoppdateringer"), | ||
("EVENT_SIGN_UP_START", "Arrangementer - påmeldingsstart"), | ||
("EVENT_SIGN_OFF_DEADLINE", "Arrangementer - avmeldingsfrist"), | ||
("EVENT_EVALUATION", "Arrangementer - evaluering"), | ||
("EVENT_INFO", "Arrangementer - info fra arrangør"), | ||
("FINE", "Grupper - bot"), | ||
("GROUP_MEMBERSHIP", "Grupper - medlemsskap"), | ||
("OTHER", "Andre"), | ||
("RESERVATION NEW", "Ny reservasjon"), | ||
("RESERVATION APPROVED", "Godkjent reservasjon"), | ||
("RESERVATION CANCELLED", "Avslått reservasjon"), | ||
("KONTRES", "Kontres"), | ||
("BLITZED", "Blitzed"), | ||
], | ||
max_length=30, | ||
), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
import os | ||
from unittest.mock import patch | ||
|
||
from rest_framework import status | ||
|
||
import pytest | ||
|
||
from app.communication.enums import UserNotificationSettingType | ||
from app.communication.notifier import Notify | ||
from app.content.factories import UserFactory | ||
from app.util.test_utils import get_api_client | ||
|
||
EMAIL_URL = "/send-email/" | ||
EMAIL_API_KEY = os.environ.get("EMAIL_API_KEY") | ||
|
||
|
||
def _get_email_url(): | ||
return f"{EMAIL_URL}" | ||
|
||
|
||
@pytest.mark.django_db | ||
@patch.object(Notify, "send", return_value=None) | ||
def test_send_email_success(mock_send): | ||
""" | ||
Test that the send_email endpoint sends an email successfully. | ||
""" | ||
test_user = UserFactory() | ||
|
||
data = { | ||
"user_id_list": [test_user.user_id], | ||
"notification_type": "KONTRES", | ||
"title": "Test Notification", | ||
"paragraphs": ["This is a test paragraph.", "This is another paragraph."], | ||
} | ||
|
||
client = get_api_client(user=test_user) | ||
url = _get_email_url() | ||
headers = {"api_key": EMAIL_API_KEY} | ||
response = client.post(url, data, format="json", **headers) | ||
|
||
assert response.status_code == status.HTTP_201_CREATED | ||
mock_send.assert_called_once() | ||
|
||
|
||
@pytest.mark.django_db | ||
@patch.object(Notify, "send", return_value=None) | ||
def test_send_email_fails_when_field_missing(mock_send): | ||
""" | ||
Test that the send_email endpoint returns 400 when one of the fields is missing. | ||
""" | ||
test_user = UserFactory() | ||
|
||
data = { | ||
"user_id_list": [test_user.user_id], | ||
"title": "Test Notification", | ||
"paragraphs": ["This is a test paragraph.", "This is another paragraph."], | ||
} | ||
|
||
client = get_api_client(user=test_user) | ||
url = _get_email_url() | ||
headers = {"api_key": EMAIL_API_KEY} | ||
response = client.post(url, data, format="json", **headers) | ||
|
||
assert response.status_code == status.HTTP_400_BAD_REQUEST | ||
mock_send.assert_not_called() | ||
|
||
|
||
@pytest.mark.django_db | ||
@patch.object(Notify, "send", return_value=None) | ||
def test_send_email_fails_when_wrong_api_key(mock_send): | ||
""" | ||
Test that the send_email endpoint returns 403 when the API key is invalid. | ||
""" | ||
test_user = UserFactory() | ||
|
||
data = { | ||
"user_id_list": [test_user.user_id], | ||
"notification_type": "KONTRES", | ||
"title": "Test Notification", | ||
"paragraphs": ["This is a test paragraph.", "This is another paragraph."], | ||
} | ||
|
||
client = get_api_client(user=test_user) | ||
url = _get_email_url() | ||
headers = {"api_key": "wrong_key"} | ||
response = client.post(url, data, format="json", **headers) | ||
|
||
assert response.status_code == status.HTTP_403_FORBIDDEN | ||
mock_send.assert_not_called() | ||
|
||
|
||
@pytest.mark.django_db | ||
@patch.object(Notify, "send", return_value=None) | ||
def test_send_email_fails_when_user_id_invalid(mock_send): | ||
""" | ||
Test that the send_email endpoint returns 404 when the user id is invalid. | ||
""" | ||
test_user = UserFactory() | ||
|
||
data = { | ||
"user_id_list": [999], | ||
"notification_type": "KONTRES", | ||
"title": "Test Notification", | ||
"paragraphs": ["This is a test paragraph.", "This is another paragraph."], | ||
} | ||
|
||
client = get_api_client(user=test_user) | ||
url = _get_email_url() | ||
headers = {"api_key": EMAIL_API_KEY} | ||
response = client.post(url, data, format="json", **headers) | ||
|
||
assert response.status_code == status.HTTP_404_NOT_FOUND | ||
mock_send.assert_not_called() | ||
|
||
|
||
@pytest.mark.django_db | ||
@patch.object(Notify, "send", return_value=None) | ||
@pytest.mark.parametrize( | ||
"notification_type", UserNotificationSettingType.get_kontres_and_blitzed() | ||
) | ||
def test_email_success_with_kontres_and_blitzed(mock_send, notification_type): | ||
""" | ||
Tests that the send_email endpoint works with both KONTRES and BLITZED notification types. | ||
""" | ||
test_user = UserFactory() | ||
|
||
data = { | ||
"user_id_list": [test_user.user_id], | ||
"notification_type": notification_type, | ||
"title": "Test Notification", | ||
"paragraphs": ["This is a test paragraph.", "This is another paragraph."], | ||
} | ||
|
||
client = get_api_client(user=test_user) | ||
url = _get_email_url() | ||
headers = {"api_key": EMAIL_API_KEY} | ||
response = client.post(url, data, format="json", **headers) | ||
|
||
assert response.status_code == status.HTTP_201_CREATED | ||
mock_send.assert_called_once() | ||
|
||
|
||
@pytest.mark.django_db | ||
@patch.object(Notify, "send", return_value=None) | ||
def test_send_email_success_with_user_id_list(mock_send): | ||
""" | ||
Test that the send_email endpoint sends an email successfully to a list of users. | ||
""" | ||
test_user1 = UserFactory() | ||
test_user2 = UserFactory() | ||
test_user3 = UserFactory() | ||
|
||
data = { | ||
"user_id_list": [ | ||
test_user.user_id for test_user in [test_user1, test_user2, test_user3] | ||
], | ||
"notification_type": "KONTRES", | ||
"title": "Test Notification", | ||
"paragraphs": ["This is a test paragraph.", "This is another paragraph."], | ||
} | ||
|
||
client = get_api_client(user=test_user1) | ||
url = _get_email_url() | ||
headers = {"api_key": EMAIL_API_KEY} | ||
response = client.post(url, data, format="json", **headers) | ||
|
||
assert response.status_code == status.HTTP_201_CREATED | ||
mock_send.assert_called_once() | ||
|
||
|
||
@pytest.mark.django_db | ||
@patch.object(Notify, "send", return_value=None) | ||
def test_send_email_fails_when_user_id_list_is_empty(mock_send): | ||
""" | ||
Test that the send_email endpoint returns 400 when the user id list is empty. | ||
""" | ||
test_user = UserFactory() | ||
|
||
data = { | ||
"user_id_list": [], | ||
"notification_type": "KONTRES", | ||
"title": "Test Notification", | ||
"paragraphs": ["This is a test paragraph.", "This is another paragraph."], | ||
} | ||
|
||
client = get_api_client(user=test_user) | ||
url = _get_email_url() | ||
headers = {"api_key": EMAIL_API_KEY} | ||
response = client.post(url, data, format="json", **headers) | ||
|
||
assert response.status_code == status.HTTP_400_BAD_REQUEST | ||
mock_send.assert_not_called() | ||
|
||
|
||
@pytest.mark.django_db | ||
@patch.object(Notify, "send", return_value=None) | ||
def test_send_email_fails_when_user_paragraph_list_is_empty(mock_send): | ||
""" | ||
Test that the send_email endpoint returns 400 when the paragraph list is empty. | ||
""" | ||
test_user = UserFactory() | ||
|
||
data = { | ||
"user_id_list": [test_user.user_id], | ||
"notification_type": "KONTRES", | ||
"title": "Test Notification", | ||
"paragraphs": [], | ||
} | ||
|
||
client = get_api_client(user=test_user) | ||
url = _get_email_url() | ||
headers = {"api_key": EMAIL_API_KEY} | ||
response = client.post(url, data, format="json", **headers) | ||
|
||
assert response.status_code == status.HTTP_400_BAD_REQUEST | ||
mock_send.assert_not_called() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import os | ||
|
||
from rest_framework import status | ||
from rest_framework.decorators import api_view | ||
from rest_framework.response import Response | ||
|
||
from app.communication.enums import UserNotificationSettingType | ||
from app.communication.notifier import Notify | ||
from app.content.models import User | ||
|
||
|
||
@api_view(["POST"]) | ||
def send_email(request): | ||
""" | ||
Endpoint for sending a notification and email to a user. | ||
Body should contain: | ||
- 'user_id_list': A list of user ids to send the email to. | ||
- 'notification_type': KONTRES or BLITZED. | ||
- 'title': The title of the notification. | ||
- 'paragraphs': A list of paragraphs to include in the notification. | ||
The header should contain: | ||
- 'api_key': A key for validating access. | ||
""" | ||
try: | ||
EMAIL_API_KEY = os.environ.get("EMAIL_API_KEY") | ||
api_key = request.META.get("api_key") | ||
if api_key != EMAIL_API_KEY: | ||
return Response( | ||
{"detail": "Feil API nøkkel"}, | ||
status=status.HTTP_403_FORBIDDEN, | ||
) | ||
|
||
user_id_list = request.data.get("user_id_list") | ||
paragraphs = request.data.get("paragraphs") | ||
title = request.data.get("title") | ||
notification_type = request.data.get("notification_type") | ||
|
||
if not isinstance(user_id_list, list) or not user_id_list: | ||
return Response( | ||
{"detail": "En ikke-tom liste med bruker id-er må inkluderes"}, | ||
status=status.HTTP_400_BAD_REQUEST, | ||
) | ||
|
||
if not isinstance(paragraphs, list) or not paragraphs: | ||
return Response( | ||
{"detail": "En ikke-tom liste med paragrafer må inkluderes"}, | ||
status=status.HTTP_400_BAD_REQUEST, | ||
) | ||
|
||
if not notification_type or not title: | ||
return Response( | ||
{ | ||
"detail": "Notifikasjonstype (KONTRES/BLITZED) og tittel må være satt" | ||
}, | ||
status=status.HTTP_400_BAD_REQUEST, | ||
) | ||
|
||
users = list(User.objects.filter(user_id__in=user_id_list)) | ||
if not users or len(users) != len(user_id_list): | ||
return Response( | ||
{"detail": "En eller flere brukere ble ikke funnet"}, | ||
status=status.HTTP_404_NOT_FOUND, | ||
) | ||
|
||
email = Notify( | ||
users, | ||
f"{title}", | ||
UserNotificationSettingType(notification_type), | ||
) | ||
|
||
for paragraph in paragraphs: | ||
email.add_paragraph(paragraph) | ||
|
||
email.send() | ||
return Response({"detail": "Emailen ble sendt"}, status=status.HTTP_201_CREATED) | ||
except Exception as e: | ||
print(e) | ||
return Response( | ||
{"detail": "Det oppsto en feil under sending av email"}, | ||
status=status.HTTP_500_INTERNAL_SERVER_ERROR, | ||
) |