diff --git a/froide/conftest.py b/froide/conftest.py index 302546fb0..4fe94f802 100644 --- a/froide/conftest.py +++ b/froide/conftest.py @@ -10,6 +10,7 @@ from froide.foirequest.models import FoiMessage, FoiRequest from froide.foirequest.signals import email_left_queue from froide.foirequest.tests.factories import ( + FoiMessageDraftFactory, FoiMessageFactory, FoiProjectFactory, FoiRequestFactory, @@ -33,6 +34,7 @@ register(FoiRequestFollowerFactory) register(PublicBodyFactory) register(FoiMessageFactory) +register(FoiMessageDraftFactory) register(ClassificationFactory) register(FoiLawFactory) register(JurisdictionFactory) diff --git a/froide/foirequest/api_views/message.py b/froide/foirequest/api_views/message.py index 45abd297c..f812ea5bb 100644 --- a/froide/foirequest/api_views/message.py +++ b/froide/foirequest/api_views/message.py @@ -1,4 +1,5 @@ from django.contrib.auth import get_user_model +from django.utils.translation import gettext_lazy as _ from django_filters import rest_framework as filters from rest_framework import permissions, viewsets @@ -77,6 +78,18 @@ def publish(self, request, pk=None): if not message.is_response: message.sender_user = request.user + if not message.can_be_published(): + if message.is_response: + error_message = _( + "Response messages must have a sender public body, no sender user and no recipient public body." + ) + else: + error_message = _( + "Non-response messages must have a recipent public body, but no sender public body." + ) + + return Response({"error": error_message}, status=400) + message.save() if message.is_response: diff --git a/froide/foirequest/models/message.py b/froide/foirequest/models/message.py index 6a6b2f28e..16bdf8a27 100644 --- a/froide/foirequest/models/message.py +++ b/froide/foirequest/models/message.py @@ -764,6 +764,20 @@ class Meta: verbose_name = _("Freedom of Information Message Draft") verbose_name_plural = _("Freedom of Information Message Drafts") + def can_be_published(self) -> bool: + # see constraints of FoiMessage + if self.is_response: + return ( + self.sender_public_body is not None + and self.recipient_public_body is None + and self.sender_user is None + ) + else: + return ( + self.sender_public_body is None + and self.recipient_public_body is not None + ) + class Delivery(models.TextChoices): STATUS_UNKNOWN = ("unknown", _("unknown")) diff --git a/froide/foirequest/tests/factories.py b/froide/foirequest/tests/factories.py index daeddcadf..97cf5995f 100644 --- a/froide/foirequest/tests/factories.py +++ b/froide/foirequest/tests/factories.py @@ -31,6 +31,7 @@ FoiAttachment, FoiEvent, FoiMessage, + FoiMessageDraft, FoiProject, FoiRequest, PublicBodySuggestion, @@ -156,6 +157,11 @@ class Meta: not_publishable = False +class FoiMessageDraftFactory(FoiMessageFactory): + class Meta: + model = FoiMessageDraft + + class FoiAttachmentFactory(DjangoModelFactory): class Meta: model = FoiAttachment diff --git a/froide/foirequest/tests/test_api_message.py b/froide/foirequest/tests/test_api_message.py index c9dc4b188..24daac5fb 100644 --- a/froide/foirequest/tests/test_api_message.py +++ b/froide/foirequest/tests/test_api_message.py @@ -84,9 +84,28 @@ def test_message_draft(client: Client, user): response = client.get(reverse("api:message-detail", kwargs={"pk": message_id})) assert response.status_code == 404 + # can't publish without recipient + publish_uri = reverse("api:message-draft-publish", kwargs={"pk": message_id}) + response = client.post(publish_uri) + assert response.status_code == 400 + + # letter was sent by public body to user + public_body = factories.PublicBodyFactory.create() + response = client.patch( + resource_uri, + data={ + "sender_public_body": reverse( + "api:publicbody-detail", kwargs={"pk": public_body.pk} + ), + "recipient_public_body": None, + "is_response": True, + }, + content_type="application/json", + ) + assert response.status_code == 200 + # publish - resource_uri = reverse("api:message-draft-publish", kwargs={"pk": message_id}) - response = client.post(resource_uri) + response = client.post(publish_uri) assert response.status_code == 200 assert "/draft/" not in response.json()["resource_uri"] @@ -104,6 +123,28 @@ def test_message_draft(client: Client, user): response = client.delete(resource_uri) assert response.status_code == 404 + # create message draft + response = client.post( + "/api/v1/message/draft/", + data={ + "request": reverse("api:request-detail", kwargs={"pk": request.pk}), + "kind": "post", + "recipient_public_body": reverse( + "api:publicbody-detail", kwargs={"pk": public_body.pk} + ), + "sender_public_body": None, + "is_response": False, + }, + content_type="application/json", + ) + assert response.status_code == 201 + + message_id = response.json()["id"] + publish_uri = reverse("api:message-draft-publish", kwargs={"pk": message_id}) + + response = client.post(publish_uri) + assert response.status_code == 200 + @pytest.mark.django_db def test_auth(client, user): @@ -122,6 +163,13 @@ def test_auth(client, user): ) assert response.status_code == 401 + # can't publish drafts + draft = factories.FoiMessageDraftFactory.create(request=request) + response = client.post( + reverse("api:message-draft-publish", kwargs={"pk": draft.pk}) + ) + assert response.status_code == 401 + # needs to be own request client.login(email=user.email, password="froide") response = client.post(