]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Fix: fix some API crashes (#10196)
authorshamoon <4887959+shamoon@users.noreply.github.com>
Tue, 17 Jun 2025 05:44:39 +0000 (22:44 -0700)
committerGitHub <noreply@github.com>
Tue, 17 Jun 2025 05:44:39 +0000 (22:44 -0700)
src/documents/serialisers.py
src/documents/tests/test_api_app_config.py
src/documents/tests/test_api_uisettings.py
src/paperless/views.py
src/paperless_mail/serialisers.py
src/paperless_mail/tests/test_mail.py

index 3b9b2062a283984e7ea8e282a495b0652aa58625..d0770bada09bdafee217dc005cff16e598424bc4 100644 (file)
@@ -1189,7 +1189,6 @@ class SavedViewSerializer(OwnedObjectSerializer):
             "owner",
             "permissions",
             "user_can_change",
-            "set_permissions",
         ]
 
     def validate(self, attrs):
@@ -1754,6 +1753,8 @@ class StoragePathSerializer(MatchingModelSerializer, OwnedObjectSerializer):
 
 
 class UiSettingsViewSerializer(serializers.ModelSerializer):
+    settings = serializers.DictField(required=False, allow_null=True)
+
     class Meta:
         model = UiSettings
         depth = 1
@@ -2020,8 +2021,9 @@ class WorkflowTriggerSerializer(serializers.ModelSerializer):
         ):
             attrs["filter_path"] = None
 
+        trigger_type = attrs.get("type", getattr(self.instance, "type", None))
         if (
-            attrs["type"] == WorkflowTrigger.WorkflowTriggerType.CONSUMPTION
+            trigger_type == WorkflowTrigger.WorkflowTriggerType.CONSUMPTION
             and "filter_mailrule" not in attrs
             and ("filter_filename" not in attrs or attrs["filter_filename"] is None)
             and ("filter_path" not in attrs or attrs["filter_path"] is None)
index 479229af276395c8977816a048a7f6db4b5eee66..5968b16701f2b63b8286f85532457c1a85862ef2 100644 (file)
@@ -167,3 +167,25 @@ class TestApiAppConfig(DirectoriesMixin, APITestCase):
                 },
             )
         self.assertFalse(Path(old_logo.path).exists())
+
+    def test_create_not_allowed(self):
+        """
+        GIVEN:
+            - API request to create a new app config
+        WHEN:
+            - API is called
+        THEN:
+            - Correct HTTP response
+            - No new config is created
+        """
+        response = self.client.post(
+            self.ENDPOINT,
+            json.dumps(
+                {
+                    "output_type": "pdf",
+                },
+            ),
+            content_type="application/json",
+        )
+        self.assertEqual(response.status_code, status.HTTP_405_METHOD_NOT_ALLOWED)
+        self.assertEqual(ApplicationConfiguration.objects.count(), 1)
index e3b9d4999b3bef3b899dd875fc6f7f3e84a79b6c..2837298983ba6007da6f7f37031007c3030b6f32 100644 (file)
@@ -117,6 +117,30 @@ class TestApiUiSettings(DirectoriesMixin, APITestCase):
 
         self.assertEqual(response.status_code, status.HTTP_200_OK)
 
+    def test_settings_must_be_dict(self):
+        """
+        GIVEN:
+            - API request to update ui_settings with settings not being a dict
+        WHEN:
+            - API is called
+        THEN:
+            - Correct HTTP 400 response
+        """
+        response = self.client.post(
+            self.ENDPOINT,
+            json.dumps(
+                {
+                    "settings": "not a dict",
+                },
+            ),
+            content_type="application/json",
+        )
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertIn(
+            "Expected a dictionary",
+            str(response.data["settings"]),
+        )
+
     @override_settings(
         OAUTH_CALLBACK_BASE_URL="http://localhost:8000",
         GMAIL_OAUTH_CLIENT_ID="abc123",
index 4d102029f92aadac70c69f552cf5080f246070d3..8cb394c8e8741e9f55728002863083f94fc6788b 100644 (file)
@@ -342,6 +342,10 @@ class ApplicationConfigurationViewSet(ModelViewSet):
     serializer_class = ApplicationConfigurationSerializer
     permission_classes = (IsAuthenticated, DjangoModelPermissions)
 
+    @extend_schema(exclude=True)
+    def create(self, request, *args, **kwargs):
+        return Response(status=405)  # Not Allowed
+
 
 @extend_schema_view(
     post=extend_schema(
index c7a20acbf93bd04977d63dbfa3435f410cf35691..402a53c3b31cae7fd59da080d4e0ca556e2412b0 100644 (file)
@@ -108,18 +108,20 @@ class MailRuleSerializer(OwnedObjectSerializer):
         return instance
 
     def create(self, validated_data):
-        if "assign_tags" in validated_data:
-            assign_tags = validated_data.pop("assign_tags")
+        assign_tags = validated_data.pop("assign_tags", [])
         mail_rule = super().create(validated_data)
         if assign_tags:
             mail_rule.assign_tags.set(assign_tags)
         return mail_rule
 
     def validate(self, attrs):
+        action = attrs.get("action")
+        action_parameter = attrs.get("action_parameter")
+
         if (
-            attrs["action"] == MailRule.MailAction.TAG
-            or attrs["action"] == MailRule.MailAction.MOVE
-        ) and attrs["action_parameter"] is None:
+            action in [MailRule.MailAction.TAG, MailRule.MailAction.MOVE]
+            and not action_parameter
+        ):
             raise serializers.ValidationError("An action parameter is required.")
 
         return attrs
index a73f9cf34e532600004c55c04d26a852cfc68c1a..d7138fe4118fa1b0744bdc64a96f5c6f9d076a15 100644 (file)
@@ -1822,3 +1822,66 @@ class TestMailAccountProcess(APITestCase):
         response = self.client.post(self.url)
         self.assertEqual(response.status_code, status.HTTP_200_OK)
         m.assert_called_once()
+
+
+class TestMailRuleAPI(APITestCase):
+    def setUp(self):
+        self.user = User.objects.create_superuser(
+            username="testuser",
+            password="testpassword",
+        )
+        self.client.force_authenticate(user=self.user)
+        self.account = MailAccount.objects.create(
+            imap_server="imap.example.com",
+            imap_port=993,
+            imap_security=MailAccount.ImapSecurity.SSL,
+            username="admin",
+            password="secret",
+            account_type=MailAccount.MailAccountType.IMAP,
+            owner=self.user,
+        )
+        self.url = "/api/mail_rules/"
+
+    def test_create_mail_rule(self):
+        """
+        GIVEN:
+            - Valid data for creating a mail rule
+        WHEN:
+            - A POST request is made to the mail rules endpoint
+        THEN:
+            - The rule should be created successfully
+            - The response should contain the created rule's details
+        """
+        data = {
+            "name": "Test Rule",
+            "account": self.account.pk,
+            "action": MailRule.MailAction.MOVE,
+            "action_parameter": "inbox",
+        }
+        response = self.client.post(self.url, data, format="json")
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertEqual(MailRule.objects.count(), 1)
+        rule = MailRule.objects.first()
+        self.assertEqual(rule.name, "Test Rule")
+
+    def test_mail_rule_action_parameter_required_for_tag_or_move(self):
+        """
+        GIVEN:
+            - Valid data for creating a mail rule without action_parameter
+        WHEN:
+            - A POST request is made to the mail rules endpoint
+        THEN:
+            - The request should fail with a 400 Bad Request status
+            - The response should indicate that action_parameter is required
+        """
+        data = {
+            "name": "Test Rule",
+            "account": self.account.pk,
+            "action": MailRule.MailAction.MOVE,
+        }
+        response = self.client.post(self.url, data, format="json")
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertIn(
+            "action parameter is required",
+            str(response.data["non_field_errors"]),
+        )