From 082fb99bd567f563da2d22d66a2ce43e613932e5 Mon Sep 17 00:00:00 2001 From: Sabrina Demagny Date: Tue, 16 Apr 2024 17:06:43 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8(api)=20allow=20to=20list=20and=20crea?= =?UTF-8?q?te=20Mailboxes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simply display all Mailboxes create for a MailDomain. LDAP connection is not yet available, it will be implemented soon. Read and create permissions will be refined soon too. --- src/backend/mailbox_manager/api/__init__.py | 0 .../mailbox_manager/api/serializers.py | 21 ++++ src/backend/mailbox_manager/api/viewsets.py | 47 ++++++++ .../tests/test_api_mailboxes_create.py | 103 ++++++++++++++++++ .../tests/test_api_mailboxes_list.py | 53 +++++++++ src/backend/people/api_urls.py | 18 +++ 6 files changed, 242 insertions(+) create mode 100644 src/backend/mailbox_manager/api/__init__.py create mode 100644 src/backend/mailbox_manager/api/serializers.py create mode 100644 src/backend/mailbox_manager/api/viewsets.py create mode 100644 src/backend/mailbox_manager/tests/test_api_mailboxes_create.py create mode 100644 src/backend/mailbox_manager/tests/test_api_mailboxes_list.py diff --git a/src/backend/mailbox_manager/api/__init__.py b/src/backend/mailbox_manager/api/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/backend/mailbox_manager/api/serializers.py b/src/backend/mailbox_manager/api/serializers.py new file mode 100644 index 0000000..150ae5e --- /dev/null +++ b/src/backend/mailbox_manager/api/serializers.py @@ -0,0 +1,21 @@ +"""Client serializers for the People mailbox_manager app.""" + +from rest_framework import serializers + +from mailbox_manager import models + + +class MailboxSerializer(serializers.ModelSerializer): + """Serialize mailbox.""" + + class Meta: + model = models.Mailbox + fields = ["id", "local_part", "secondary_email"] + + +class MailDomainSerializer(serializers.ModelSerializer): + """Serialize mail domain.""" + + class Meta: + model = models.MailDomain + fields = ["id", "name"] diff --git a/src/backend/mailbox_manager/api/viewsets.py b/src/backend/mailbox_manager/api/viewsets.py new file mode 100644 index 0000000..e11234f --- /dev/null +++ b/src/backend/mailbox_manager/api/viewsets.py @@ -0,0 +1,47 @@ +"""API endpoints""" + +from rest_framework import mixins, viewsets +from rest_framework import permissions as drf_permissions + +from mailbox_manager import models + +from . import serializers + + +class MailDomainViewSet( + mixins.ListModelMixin, + viewsets.GenericViewSet, +): + """MailDomain ViewSet""" + + permission_classes = [drf_permissions.IsAuthenticated] + serializer_class = serializers.MailDomainSerializer + queryset = models.MailDomain.objects.all() + + +class MailBoxViewSet( + mixins.CreateModelMixin, + mixins.ListModelMixin, + viewsets.GenericViewSet, +): + """MailBox ViewSet""" + + permission_classes = [drf_permissions.IsAuthenticated] + serializer_class = serializers.MailboxSerializer + queryset = models.Mailbox.objects.all() + + def get_queryset(self): + """Custom queryset to get mailboxes related to a mail domain.""" + domain_id = self.kwargs.get("domain_id", "") + if domain_id: + return self.queryset.filter(domain__id=domain_id) + return self.queryset + + def perform_create(self, serializer): + """Create new mailbox.""" + domain_id = self.kwargs.get("domain_id", "") + if domain_id: + serializer.validated_data["domain"] = models.MailDomain.objects.get( + id=domain_id + ) + super().perform_create(serializer) diff --git a/src/backend/mailbox_manager/tests/test_api_mailboxes_create.py b/src/backend/mailbox_manager/tests/test_api_mailboxes_create.py new file mode 100644 index 0000000..5ed4bec --- /dev/null +++ b/src/backend/mailbox_manager/tests/test_api_mailboxes_create.py @@ -0,0 +1,103 @@ +""" +Unit tests for the mailbox API +""" + +import pytest +from rest_framework import status +from rest_framework.test import APIClient + +from core import factories as core_factories + +from mailbox_manager import factories, models + +pytestmark = pytest.mark.django_db + + +def test_api_mailboxes__create_anonymous_forbidden(): + """Anonymous users should not be able to create a new mailbox via the API.""" + mail_domain = factories.MailDomainFactory() + + response = APIClient().post( + f"/api/v1.0/mail-domains/{mail_domain.id}/mailboxes/", + { + "first_name": "jean", + "last_name": "doe", + "local_part": "jean.doe", + "secondary_email": "jean.doe@gmail.com", + "phone_number": "+33150142700", + }, + ) + assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert not models.Mailbox.objects.exists() + + +def test_api_mailboxes__create_authenticated_missing_fields(): + """ + Authenticated users should not be able to create mailboxes + without local part or secondary mail. + """ + user = core_factories.UserFactory(admin_email="tester@ministry.fr") + core_factories.IdentityFactory(user=user, email=user.admin_email, name="john doe") + + client = APIClient() + client.force_login(user) + + mail_domain = factories.MailDomainFactory() + response = client.post( + f"/api/v1.0/mail-domains/{mail_domain.id}/mailboxes/", + { + "first_name": "jean", + "last_name": "doe", + "secondary_email": "jean.doe@gmail.com", + "phone_number": "+33150142700", + }, + format="json", + ) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert models.Mailbox.objects.exists() is False + assert response.json() == {"local_part": ["This field is required."]} + + response = client.post( + f"/api/v1.0/mail-domains/{mail_domain.id}/mailboxes/", + { + "first_name": "jean", + "last_name": "doe", + "local_part": "jean.doe", + "phone_number": "+33150142700", + }, + format="json", + ) + assert response.status_code == status.HTTP_400_BAD_REQUEST + assert models.Mailbox.objects.exists() is False + assert response.json() == {"secondary_email": ["This field is required."]} + + +def test_api_mailboxes__create_authenticated_successful(): + """Authenticated users should be able to create mailbox.""" + user = core_factories.UserFactory(admin_email="tester@ministry.fr") + core_factories.IdentityFactory(user=user, email=user.admin_email, name="john doe") + + client = APIClient() + client.force_login(user) + + mail_domain = factories.MailDomainFactory(name="saint-jean.collectivite.fr") + response = client.post( + f"/api/v1.0/mail-domains/{mail_domain.id}/mailboxes/", + { + "first_name": "jean", + "last_name": "doe", + "local_part": "jean.doe", + "secondary_email": "jean.doe@gmail.com", + "phone_number": "+33150142700", + }, + format="json", + ) + assert response.status_code == status.HTTP_201_CREATED + mailbox = models.Mailbox.objects.get() + assert mailbox.local_part == "jean.doe" + assert mailbox.secondary_email == "jean.doe@gmail.com" + assert response.json() == { + "id": str(mailbox.id), + "local_part": str(mailbox.local_part), + "secondary_email": str(mailbox.secondary_email), + } diff --git a/src/backend/mailbox_manager/tests/test_api_mailboxes_list.py b/src/backend/mailbox_manager/tests/test_api_mailboxes_list.py new file mode 100644 index 0000000..9eefa1a --- /dev/null +++ b/src/backend/mailbox_manager/tests/test_api_mailboxes_list.py @@ -0,0 +1,53 @@ +""" +Unit tests for the mailbox API +""" + +import pytest +from rest_framework import status +from rest_framework.test import APIClient + +from core import factories as core_factories + +from mailbox_manager import factories + +pytestmark = pytest.mark.django_db + + +def test_api_mailboxes__list_anonymous(): + """Anonymous users should not be allowed to list mailboxes.""" + mail_domain = factories.MailDomainFactory() + factories.MailboxFactory.create_batch(2, domain=mail_domain) + + response = APIClient().get(f"/api/v1.0/mail-domains/{mail_domain.id}/mailboxes/") + assert response.status_code == status.HTTP_401_UNAUTHORIZED + assert response.json() == { + "detail": "Authentication credentials were not provided." + } + + +def test_api_mailboxes__list_authenticated_no_query(): + """Authenticated users should be able to list mailboxes without applying a query.""" + user = core_factories.UserFactory(admin_email="tester@ministry.fr") + core_factories.IdentityFactory(user=user, email=user.admin_email, name="john doe") + + client = APIClient() + client.force_login(user) + + mail_domain = factories.MailDomainFactory() + mailbox1 = factories.MailboxFactory(domain=mail_domain) + mailbox2 = factories.MailboxFactory(domain=mail_domain) + + response = client.get(f"/api/v1.0/mail-domains/{mail_domain.id}/mailboxes/") + assert response.status_code == status.HTTP_200_OK + assert response.json()["results"] == [ + { + "id": str(mailbox1.id), + "local_part": str(mailbox1.local_part), + "secondary_email": str(mailbox1.secondary_email), + }, + { + "id": str(mailbox2.id), + "local_part": str(mailbox2.local_part), + "secondary_email": str(mailbox2.secondary_email), + }, + ] diff --git a/src/backend/people/api_urls.py b/src/backend/people/api_urls.py index 85f5e2c..094faf8 100644 --- a/src/backend/people/api_urls.py +++ b/src/backend/people/api_urls.py @@ -8,11 +8,16 @@ from rest_framework.routers import DefaultRouter from core.api import viewsets +from mailbox_manager.api import viewsets as mail_viewsets + # - Main endpoints router = DefaultRouter() router.register("contacts", viewsets.ContactViewSet, basename="contacts") router.register("teams", viewsets.TeamViewSet, basename="teams") router.register("users", viewsets.UserViewSet, basename="users") +router.register( + "mail-domains", mail_viewsets.MailDomainViewSet, basename="mail-domains" +) # - Routes nested under a team team_related_router = DefaultRouter() @@ -28,6 +33,15 @@ team_related_router.register( basename="invitations", ) +# - Routes nested under a mail domain +maildomain_related_router = DefaultRouter() +maildomain_related_router.register( + "mailboxes", + mail_viewsets.MailBoxViewSet, + basename="mailboxes", +) + + urlpatterns = [ path( f"api/{settings.API_VERSION}/", @@ -39,6 +53,10 @@ urlpatterns = [ r"^teams/(?P[0-9a-z-]*)/", include(team_related_router.urls), ), + re_path( + r"^mail-domains/(?P[0-9a-z-]*)/", + include(maildomain_related_router.urls), + ), ] ), )