From ac0cdb59d65341e76223d8847d4e660c54fe039f Mon Sep 17 00:00:00 2001 From: Codex Date: Mon, 8 Jun 2026 22:23:03 +0200 Subject: [PATCH] (bugfix) Allow blank notification webhook headers Normalize blank notification webhook headers to an empty JSON object so creating email targets from the browser does not try to store NULL in the JSON field. Closes #95 --- src/pobsync_backend/forms.py | 4 ++++ src/pobsync_backend/tests/test_views.py | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/pobsync_backend/forms.py b/src/pobsync_backend/forms.py index b1c8037..99f9328 100644 --- a/src/pobsync_backend/forms.py +++ b/src/pobsync_backend/forms.py @@ -208,6 +208,10 @@ class NotificationTargetForm(forms.ModelForm): recipients = [line.strip() for line in value.replace(",", "\n").splitlines() if line.strip()] return "\n".join(recipients) + def clean_webhook_headers(self) -> dict[str, object]: + value = self.cleaned_data.get("webhook_headers") + return value or {} + class SshCredentialForm(forms.ModelForm): private_key_file = forms.FileField( diff --git a/src/pobsync_backend/tests/test_views.py b/src/pobsync_backend/tests/test_views.py index f9dd32c..5f85bfd 100644 --- a/src/pobsync_backend/tests/test_views.py +++ b/src/pobsync_backend/tests/test_views.py @@ -318,6 +318,27 @@ class ViewTests(TestCase): self.assertEqual(target.channel, NotificationTarget.Channel.EMAIL) self.assertEqual(target.statuses, [BackupRun.Status.FAILED, BackupRun.Status.WARNING]) self.assertEqual(target.email_to, "ops@example.test\nbackup@example.test") + self.assertEqual(target.webhook_headers, {}) + + def test_notification_target_form_allows_blank_webhook_headers(self) -> None: + self.client.force_login(self.staff_user) + + response = self.client.post( + reverse("create_notification_target"), + { + "name": "ops", + "enabled": "on", + "channel": NotificationTarget.Channel.EMAIL, + "statuses": [BackupRun.Status.FAILED], + "email_to": "ops@example.test", + "webhook_headers": "", + }, + follow=True, + ) + + self.assertRedirects(response, reverse("notification_targets")) + target = NotificationTarget.objects.get(name="ops") + self.assertEqual(target.webhook_headers, {}) def test_notification_target_form_requires_channel_destination(self) -> None: self.client.force_login(self.staff_user) -- 2.43.0