diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f93f4a9..b57adf9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ # Changelog All notable changes to this project will be documented in this file. +## Unreleased + +### Improvements +- "Filtering Rules" dialog, an option to require HTTP POST (#297) + + ## 1.11.0 - 2019-11-22 ### Improvements @@ -14,6 +20,7 @@ All notable changes to this project will be documented in this file. - On mobile, "My Checks" page, always show the gear (Details) button (#286) - Make log events fit better on mobile screens + ## 1.10.0 - 2019-10-21 ### Improvements @@ -80,6 +87,7 @@ All notable changes to this project will be documented in this file. - Show the Description section even if the description is missing. (#246) - Include the description in email alerts. (#247) + ## 1.6.0 - 2019-04-01 ### Improvements diff --git a/hc/api/migrations/0065_auto_20191127_1240.py b/hc/api/migrations/0065_auto_20191127_1240.py new file mode 100644 index 00000000..435f88db --- /dev/null +++ b/hc/api/migrations/0065_auto_20191127_1240.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.6 on 2019-11-27 12:40 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0064_auto_20191119_1346'), + ] + + operations = [ + migrations.AddField( + model_name='check', + name='methods', + field=models.CharField(blank=True, max_length=30), + ), + migrations.AlterField( + model_name='channel', + name='kind', + field=models.CharField(choices=[('email', 'Email'), ('webhook', 'Webhook'), ('hipchat', 'HipChat'), ('slack', 'Slack'), ('pd', 'PagerDuty'), ('pagertree', 'PagerTree'), ('pagerteam', 'Pager Team'), ('po', 'Pushover'), ('pushbullet', 'Pushbullet'), ('opsgenie', 'OpsGenie'), ('victorops', 'VictorOps'), ('discord', 'Discord'), ('telegram', 'Telegram'), ('sms', 'SMS'), ('zendesk', 'Zendesk'), ('trello', 'Trello'), ('matrix', 'Matrix'), ('whatsapp', 'WhatsApp'), ('apprise', 'Apprise'), ('mattermost', 'Mattermost'), ('msteams', 'Microsoft Teams'), ('shell', 'Shell Command')], max_length=20), + ), + ] diff --git a/hc/api/models.py b/hc/api/models.py index 04893b03..dfb57129 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -72,6 +72,8 @@ class Check(models.Model): schedule = models.CharField(max_length=100, default="* * * * *") tz = models.CharField(max_length=36, default="UTC") subject = models.CharField(max_length=100, blank=True) + methods = models.CharField(max_length=30, blank=True) + n_pings = models.IntegerField(default=0) last_ping = models.DateTimeField(null=True, blank=True) last_start = models.DateTimeField(null=True, blank=True) diff --git a/hc/api/tests/test_ping.py b/hc/api/tests/test_ping.py index fb183bc6..72aa5022 100644 --- a/hc/api/tests/test_ping.py +++ b/hc/api/tests/test_ping.py @@ -186,3 +186,18 @@ class PingTestCase(BaseTestCase): self.check.refresh_from_db() self.assertTrue(self.check.last_duration.total_seconds() >= 10) + + def test_it_requires_post(self): + self.check.methods = "POST" + self.check.save() + + r = self.client.get("/ping/%s/" % self.check.code) + self.assertEqual(r.status_code, 200) + + self.check.refresh_from_db() + self.assertEqual(self.check.status, "new") + self.assertIsNone(self.check.last_ping) + + ping = Ping.objects.latest("id") + self.assertEqual(ping.scheme, "http") + self.assertEqual(ping.kind, "ign") diff --git a/hc/api/views.py b/hc/api/views.py index 0520f1d9..00496930 100644 --- a/hc/api/views.py +++ b/hc/api/views.py @@ -34,6 +34,9 @@ def ping(request, code, action="success"): ua = headers.get("HTTP_USER_AGENT", "") body = request.body.decode() + if check.methods == "POST" and method != "POST": + action = "ign" + check.ping(remote_addr, scheme, method, ua, body, action) response = HttpResponse("OK") diff --git a/hc/front/forms.py b/hc/front/forms.py index 64f84102..cd974cd9 100644 --- a/hc/front/forms.py +++ b/hc/front/forms.py @@ -62,8 +62,9 @@ class NameTagsForm(forms.Form): return " ".join(result) -class EmailSettingsForm(forms.Form): +class FilteringRulesForm(forms.Form): subject = forms.CharField(max_length=100) + methods = forms.ChoiceField(required=False, choices=(("", "Any"), ("POST", "POST"))) class TimeoutForm(forms.Form): diff --git a/hc/front/tests/test_filtering_rules.py b/hc/front/tests/test_filtering_rules.py new file mode 100644 index 00000000..2ed4f956 --- /dev/null +++ b/hc/front/tests/test_filtering_rules.py @@ -0,0 +1,31 @@ +from hc.api.models import Check +from hc.test import BaseTestCase + + +class FilteringRulesTestCase(BaseTestCase): + def setUp(self): + super(FilteringRulesTestCase, self).setUp() + self.check = Check.objects.create(project=self.project) + + self.url = "/checks/%s/filtering_rules/" % self.check.code + self.redirect_url = "/checks/%s/details/" % self.check.code + + def test_it_works(self): + self.client.login(username="alice@example.org", password="password") + r = self.client.post(self.url, data={"subject": "SUCCESS", "methods": "POST"}) + self.assertRedirects(r, self.redirect_url) + + self.check.refresh_from_db() + self.assertEqual(self.check.subject, "SUCCESS") + self.assertEqual(self.check.methods, "POST") + + def test_it_clears_method(self): + self.check.method = "POST" + self.check.save() + + self.client.login(username="alice@example.org", password="password") + r = self.client.post(self.url, data={"subject": "SUCCESS", "methods": ""}) + self.assertRedirects(r, self.redirect_url) + + self.check.refresh_from_db() + self.assertEqual(self.check.methods, "") diff --git a/hc/front/urls.py b/hc/front/urls.py index a30c3ad0..97ed384a 100644 --- a/hc/front/urls.py +++ b/hc/front/urls.py @@ -5,7 +5,7 @@ from hc.front import views check_urls = [ path("name/", views.update_name, name="hc-update-name"), path("details/", views.details, name="hc-details"), - path("email_settings/", views.email_settings, name="hc-email-settings"), + path("filtering_rules/", views.filtering_rules, name="hc-filtering-rules"), path("timeout/", views.update_timeout, name="hc-update-timeout"), path("pause/", views.pause, name="hc-pause"), path("remove/", views.remove_check, name="hc-remove-check"), diff --git a/hc/front/views.py b/hc/front/views.py index 7b654f4a..7c9f050b 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -45,7 +45,7 @@ from hc.front.forms import ( AddWebhookForm, ChannelNameForm, CronForm, - EmailSettingsForm, + FilteringRulesForm, NameTagsForm, TimeoutForm, ) @@ -334,11 +334,12 @@ def update_name(request, code): @require_POST @login_required -def email_settings(request, code): +def filtering_rules(request, code): check = _get_check_for_user(request, code) - form = EmailSettingsForm(request.POST) + form = FilteringRulesForm(request.POST) if form.is_valid(): check.subject = form.cleaned_data["subject"] + check.methods = form.cleaned_data["methods"] check.save() return redirect("hc-details", code) diff --git a/static/js/details.js b/static/js/details.js index 8aecb87f..cf364d4f 100644 --- a/static/js/details.js +++ b/static/js/details.js @@ -25,7 +25,7 @@ $(function () { $("#ping-now").click(function(e) { var button = this; - $.get(this.dataset.url, function() { + $.post(this.dataset.url, function() { button.textContent = "Success!"; }); }); @@ -119,7 +119,7 @@ $(function () { format == "local" ? dt.local() : dt.tz(format); $(".date", row).text(dt.format("MMM D")); - $(".time", row).text(dt.format("HH:mm")); + $(".time", row).text(dt.format("HH:mm")); }) // The table is initially hidden to avoid flickering as we convert dates. diff --git a/templates/front/details.html b/templates/front/details.html index d9986c0a..e6a3152a 100644 --- a/templates/front/details.html +++ b/templates/front/details.html @@ -67,7 +67,11 @@
Keep this check up by making HTTP requests to this URL:
++ Keep this check up by making HTTP + {% if check.methods == "POST" %}POST{% endif %} + requests to this URL: +
{{ check.url }}
{% if check.subject %} @@ -90,8 +94,8 @@