diff --git a/CHANGELOG.md b/CHANGELOG.md index 6be392d0..b631f6cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. ### Improvements - Add /api/v1/badges/ endpoint (#552) -- Add ability to edit existing email, SMS integrations +- Add ability to edit existing email, Signal, SMS integrations ### Bug Fixes - Add handling for non-latin-1 characters in webhook headers diff --git a/hc/api/models.py b/hc/api/models.py index 6048810b..709d9d8c 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -416,7 +416,7 @@ class Channel(models.Model): return {"id": str(self.code), "name": self.name, "kind": self.kind} def is_editable(self): - return self.kind in ("email", "webhook", "sms") + return self.kind in ("email", "webhook", "sms", "signal") def assign_all_checks(self): checks = Check.objects.filter(project=self.project) diff --git a/hc/front/tests/test_add_signal.py b/hc/front/tests/test_add_signal.py index 2cc8dbae..7355d07d 100644 --- a/hc/front/tests/test_add_signal.py +++ b/hc/front/tests/test_add_signal.py @@ -1,5 +1,5 @@ from django.test.utils import override_settings -from hc.api.models import Channel +from hc.api.models import Channel, Check from hc.test import BaseTestCase @@ -7,11 +7,13 @@ from hc.test import BaseTestCase class AddSignalTestCase(BaseTestCase): def setUp(self): super().setUp() - self.url = "/projects/%s/add_signal/" % self.project.code + self.check = Check.objects.create(project=self.project) + self.url = f"/projects/{self.project.code}/add_signal/" def test_instructions_work(self): self.client.login(username="alice@example.org", password="password") r = self.client.get(self.url) + self.assertContains(r, "Add Signal Integration") self.assertContains(r, "Get a Signal message") def test_it_creates_channel(self): @@ -34,6 +36,9 @@ class AddSignalTestCase(BaseTestCase): self.assertTrue(c.signal_notify_up) self.assertEqual(c.project, self.project) + # Make sure it calls assign_all_checks + self.assertEqual(c.checks.count(), 1) + def test_it_obeys_up_down_flags(self): form = {"label": "My Phone", "phone": "+1234567890"} diff --git a/hc/front/tests/test_edit_signal.py b/hc/front/tests/test_edit_signal.py new file mode 100644 index 00000000..cf73359d --- /dev/null +++ b/hc/front/tests/test_edit_signal.py @@ -0,0 +1,73 @@ +import json + +from django.test.utils import override_settings +from hc.api.models import Channel, Check +from hc.test import BaseTestCase + + +@override_settings(SIGNAL_CLI_ENABLED=True) +class AddSignalTestCase(BaseTestCase): + def setUp(self): + super().setUp() + self.check = Check.objects.create(project=self.project) + + self.channel = Channel(project=self.project, kind="signal") + self.channel.value = json.dumps( + {"value": "+12345678", "up": True, "down": True} + ) + self.channel.save() + + self.url = f"/integrations/{self.channel.code}/edit/" + + def test_instructions_work(self): + self.client.login(username="alice@example.org", password="password") + r = self.client.get(self.url) + self.assertContains(r, "Signal Settings") + self.assertContains(r, "Get a Signal message") + self.assertContains(r, "+12345678") + + def test_it_updates_channel(self): + form = { + "label": "My Phone", + "phone": "+1234567890", + "down": "true", + "up": "false", + } + + self.client.login(username="alice@example.org", password="password") + r = self.client.post(self.url, form) + self.assertRedirects(r, self.channels_url) + + self.channel.refresh_from_db() + self.assertEqual(self.channel.phone_number, "+1234567890") + self.assertEqual(self.channel.name, "My Phone") + self.assertTrue(self.channel.signal_notify_down) + self.assertFalse(self.channel.signal_notify_up) + + # Make sure it does not call assign_all_checks + self.assertFalse(self.channel.checks.exists()) + + def test_it_obeys_up_down_flags(self): + form = {"label": "My Phone", "phone": "+1234567890"} + + self.client.login(username="alice@example.org", password="password") + r = self.client.post(self.url, form) + self.assertRedirects(r, self.channels_url) + + c = Channel.objects.get() + self.assertFalse(c.signal_notify_down) + self.assertFalse(c.signal_notify_up) + + @override_settings(SIGNAL_CLI_ENABLED=False) + def test_it_handles_disabled_integration(self): + self.client.login(username="alice@example.org", password="password") + r = self.client.get(self.url) + self.assertEqual(r.status_code, 404) + + def test_it_requires_rw_access(self): + self.bobs_membership.role = "r" + self.bobs_membership.save() + + self.client.login(username="bob@example.org", password="password") + r = self.client.get(self.url) + self.assertEqual(r.status_code, 403) diff --git a/hc/front/urls.py b/hc/front/urls.py index 8664ad1c..ad29a646 100644 --- a/hc/front/urls.py +++ b/hc/front/urls.py @@ -67,7 +67,7 @@ project_urls = [ path("add_pushbullet/", views.add_pushbullet, name="hc-add-pushbullet"), path("add_pushover/", views.add_pushover, name="hc-add-pushover"), path("add_shell/", views.add_shell, name="hc-add-shell"), - path("add_signal/", views.add_signal, name="hc-add-signal"), + path("add_signal/", views.signal_form, name="hc-add-signal"), path("add_slack/", views.add_slack, name="hc-add-slack"), path("add_slack_btn/", views.add_slack_btn, name="hc-add-slack-btn"), path("add_sms/", views.sms_form, name="hc-add-sms"), diff --git a/hc/front/views.py b/hc/front/views.py index d57dd77f..b69c85db 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -1014,6 +1014,8 @@ def edit_channel(request, code): return webhook_form(request, channel=channel) if channel.kind == "sms": return sms_form(request, channel=channel) + if channel.kind == "signal": + return signal_form(request, channel=channel) return HttpResponseBadRequest() @@ -1720,28 +1722,41 @@ def add_whatsapp(request, code): @require_setting("SIGNAL_CLI_ENABLED") @login_required -def add_signal(request, code): - project = _get_rw_project_for_user(request, code) +def signal_form(request, channel=None, code=None): + is_new = channel is None + if is_new: + project = _get_rw_project_for_user(request, code) + channel = Channel(project=project, kind="signal") + if request.method == "POST": form = forms.PhoneUpDownForm(request.POST) if form.is_valid(): - channel = Channel(project=project, kind="signal") channel.name = form.cleaned_data["label"] channel.value = form.get_json() channel.save() - channel.assign_all_checks() - return redirect("hc-channels", project.code) - else: + if is_new: + channel.assign_all_checks() + return redirect("hc-channels", channel.project.code) + elif is_new: form = forms.PhoneUpDownForm() + else: + form = forms.PhoneUpDownForm( + { + "label": channel.name, + "phone": channel.phone_number, + "up": channel.signal_notify_up, + "down": channel.signal_notify_down, + } + ) ctx = { "page": "channels", - "project": project, + "project": channel.project, "form": form, - "profile": project.owner_profile, + "is_new": is_new, } - return render(request, "integrations/add_signal.html", ctx) + return render(request, "integrations/signal_form.html", ctx) @require_setting("TRELLO_APP_KEY") diff --git a/templates/integrations/add_signal.html b/templates/integrations/signal_form.html similarity index 95% rename from templates/integrations/add_signal.html rename to templates/integrations/signal_form.html index a13936df..260c143c 100644 --- a/templates/integrations/add_signal.html +++ b/templates/integrations/signal_form.html @@ -1,7 +1,13 @@ {% extends "base.html" %} {% load humanize static hc_extras %} -{% block title %}Add Signal Integration - {{ site_name }}{% endblock %} +{% block title %} +{% if is_new %} +Add Signal Integration - {% site_name %} +{% else %} +Signal Settings - {% site_name %} +{% endif %} +{% endblock %} {% block content %}