diff --git a/hc/api/migrations/0038_auto_20180318_1216.py b/hc/api/migrations/0038_auto_20180318_1216.py new file mode 100644 index 00000000..bd1ca613 --- /dev/null +++ b/hc/api/migrations/0038_auto_20180318_1216.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.6 on 2018-03-18 12:16 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0037_auto_20180127_1215'), + ] + + operations = [ + migrations.AddField( + model_name='check', + name='has_confirmation_link', + field=models.BooleanField(default=False), + ), + migrations.AddField( + model_name='ping', + name='body', + field=models.CharField(blank=True, max_length=10000), + ), + ] diff --git a/hc/api/models.py b/hc/api/models.py index 650f9d2f..2d7fce3f 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -71,6 +71,7 @@ class Check(models.Model): n_pings = models.IntegerField(default=0) last_ping = models.DateTimeField(null=True, blank=True) last_ping_body = models.CharField(max_length=10000, blank=True) + has_confirmation_link = models.BooleanField(default=False) alert_after = models.DateTimeField(null=True, blank=True, editable=False) status = models.CharField(max_length=6, choices=STATUSES, default="new") @@ -182,13 +183,10 @@ class Check(models.Model): return result - def has_confirmation_link(self): - return "confirm" in self.last_ping_body.lower() - def ping(self, remote_addr, scheme, method, ua, body): self.n_pings = models.F("n_pings") + 1 self.last_ping = timezone.now() - self.last_ping_body = body[:10000] + self.has_confirmation_link = "confirm" in str(body).lower() self.alert_after = self.get_alert_after() if self.status in ("new", "paused"): self.status = "up" @@ -203,6 +201,7 @@ class Check(models.Model): ping.method = method # If User-Agent is longer than 200 characters, truncate it: ping.ua = ua[:200] + ping.body = body[:10000] ping.save() @@ -215,6 +214,7 @@ class Ping(models.Model): remote_addr = models.GenericIPAddressField(blank=True, null=True) method = models.CharField(max_length=10, blank=True) ua = models.CharField(max_length=200, blank=True) + body = models.CharField(max_length=10000, blank=True) class Channel(models.Model): diff --git a/hc/api/tests/test_ping.py b/hc/api/tests/test_ping.py index 94080061..66a5f1a6 100644 --- a/hc/api/tests/test_ping.py +++ b/hc/api/tests/test_ping.py @@ -36,11 +36,9 @@ class PingTestCase(TestCase): content_type="text/plain") self.assertEqual(r.status_code, 200) - self.check.refresh_from_db() - self.assertEqual(self.check.last_ping_body, "hello world") - ping = Ping.objects.latest("id") self.assertEqual(ping.method, "POST") + self.assertEqual(ping.body, "hello world") def test_head_works(self): csrf_client = Client(enforce_csrf_checks=True) @@ -107,3 +105,12 @@ class PingTestCase(TestCase): def test_it_never_caches(self): r = self.client.get("/ping/%s/" % self.check.code) assert "no-cache" in r.get("Cache-Control") + + def test_it_updates_confirmation_flag(self): + payload = "Please Confirm ..." + r = self.client.post("/ping/%s/" % self.check.code, data=payload, + content_type="text/plain") + self.assertEqual(r.status_code, 200) + + self.check.refresh_from_db() + self.assertTrue(self.check.has_confirmation_link) diff --git a/hc/api/views.py b/hc/api/views.py index 44ee5f97..9d9ae468 100644 --- a/hc/api/views.py +++ b/hc/api/views.py @@ -28,9 +28,8 @@ def ping(request, code): scheme = headers.get("HTTP_X_FORWARDED_PROTO", "http") method = headers["REQUEST_METHOD"] ua = headers.get("HTTP_USER_AGENT", "") - body = request.body[:10000] - check.ping(remote_addr, scheme, method, ua, body) + check.ping(remote_addr, scheme, method, ua, request.body) response = HttpResponse("OK") response["Access-Control-Allow-Origin"] = "*" diff --git a/hc/front/tests/test_last_ping.py b/hc/front/tests/test_last_ping.py deleted file mode 100644 index ce96be08..00000000 --- a/hc/front/tests/test_last_ping.py +++ /dev/null @@ -1,21 +0,0 @@ -from hc.api.models import Check, Ping -from hc.test import BaseTestCase - - -class LastPingTestCase(BaseTestCase): - - def test_it_works(self): - check = Check(user=self.alice) - check.last_ping_body = "this is body" - check.save() - - Ping.objects.create(owner=check) - - self.client.login(username="alice@example.org", password="password") - r = self.client.post("/checks/%s/last_ping/" % check.code) - self.assertContains(r, "this is body", status_code=200) - - def test_it_requires_user(self): - check = Check.objects.create() - r = self.client.post("/checks/%s/last_ping/" % check.code) - self.assertEqual(r.status_code, 403) diff --git a/hc/front/tests/test_ping_details.py b/hc/front/tests/test_ping_details.py new file mode 100644 index 00000000..52e8c990 --- /dev/null +++ b/hc/front/tests/test_ping_details.py @@ -0,0 +1,37 @@ +from hc.api.models import Check, Ping +from hc.test import BaseTestCase + + +class LastPingTestCase(BaseTestCase): + + def test_it_works(self): + check = Check(user=self.alice) + check.save() + + Ping.objects.create(owner=check, body="this is body") + + self.client.login(username="alice@example.org", password="password") + r = self.client.post("/checks/%s/last_ping/" % check.code) + self.assertContains(r, "this is body", status_code=200) + + def test_it_requires_user(self): + check = Check.objects.create() + r = self.client.post("/checks/%s/last_ping/" % check.code) + self.assertEqual(r.status_code, 403) + + def test_it_accepts_n(self): + check = Check(user=self.alice) + check.last_ping_body = "this is body" + check.save() + + # remote_addr, scheme, method, ua, body): + check.ping("1.2.3.4", "http", "post", "tester", "foo-123") + check.ping("1.2.3.4", "http", "post", "tester", "bar-456") + + self.client.login(username="alice@example.org", password="password") + + r = self.client.post("/checks/%s/pings/1/" % check.code) + self.assertContains(r, "foo-123", status_code=200) + + r = self.client.post("/checks/%s/pings/2/" % check.code) + self.assertContains(r, "bar-456", status_code=200) diff --git a/hc/front/urls.py b/hc/front/urls.py index 314959c4..8cb1b0f6 100644 --- a/hc/front/urls.py +++ b/hc/front/urls.py @@ -8,7 +8,8 @@ check_urls = [ url(r'^pause/$', views.pause, name="hc-pause"), url(r'^remove/$', views.remove_check, name="hc-remove-check"), url(r'^log/$', views.log, name="hc-log"), - url(r'^last_ping/$', views.last_ping, name="hc-last-ping"), + url(r'^last_ping/$', views.ping_details, name="hc-last-ping"), + url(r'^pings/([\d]+)/$', views.ping_details, name="hc-ping-details"), ] channel_urls = [ diff --git a/hc/front/views.py b/hc/front/views.py index 2dc1bfb8..1523b53f 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -249,7 +249,7 @@ def cron_preview(request): @require_POST -def last_ping(request, code): +def ping_details(request, code, n=None): if not request.user.is_authenticated: return HttpResponseForbidden() @@ -257,14 +257,18 @@ def last_ping(request, code): if check.user_id != request.team.user.id: return HttpResponseForbidden() - ping = Ping.objects.filter(owner=check).latest("created") + q = Ping.objects.filter(owner=check) + if n: + q = q.filter(n=n) + + ping = q.latest("created") ctx = { "check": check, "ping": ping } - return render(request, "front/last_ping.html", ctx) + return render(request, "front/ping_details.html", ctx) @require_POST diff --git a/static/css/last_ping.css b/static/css/ping_details.css similarity index 56% rename from static/css/last_ping.css rename to static/css/ping_details.css index fdf51c44..b6e4012c 100644 --- a/static/css/last_ping.css +++ b/static/css/ping_details.css @@ -1,26 +1,26 @@ -#last-ping-body .modal-body { +#ping-details-body .modal-body { padding: 40px; } -#last-ping-body h3 { +#ping-details-body h3 { font-size: 18px; margin: 0 0 24px 0; } -#last-ping-body .ua { +#ping-details-body .ua { font-size: 11px; font-family: monospace; } -#last-ping-body p strong { +#ping-details-body p strong { display: block; } -#last-ping-body h4 { +#ping-details-body h4 { margin-top: 24px; } -#last-ping-body pre { +#ping-details-body pre { padding: 16px; margin: 0; } diff --git a/static/js/checks.js b/static/js/checks.js index 79ec8b6d..f790b6e9 100644 --- a/static/js/checks.js +++ b/static/js/checks.js @@ -176,8 +176,8 @@ $(function () { }); $(".last-ping-cell").on("click", ".last-ping", function() { - $("#last-ping-body").text("Updating..."); - $('#last-ping-modal').modal("show"); + $("#ping-details-body").text("Updating..."); + $('#ping-details-modal').modal("show"); var token = $('input[name=csrfmiddlewaretoken]').val(); $.ajax({ @@ -185,7 +185,7 @@ $(function () { type: "post", headers: {"X-CSRFToken": token}, success: function(data) { - $("#last-ping-body" ).html(data); + $("#ping-details-body" ).html(data); } }); diff --git a/static/js/log.js b/static/js/log.js index d070812a..582da054 100644 --- a/static/js/log.js +++ b/static/js/log.js @@ -1,4 +1,21 @@ $(function () { + $(".details-btn").on("click", function() { + $("#ping-details-body").text("Updating..."); + $('#ping-details-modal').modal("show"); + + var token = $('input[name=csrfmiddlewaretoken]').val(); + $.ajax({ + url: this.dataset.url, + type: "post", + headers: {"X-CSRFToken": token}, + success: function(data) { + $("#ping-details-body" ).html(data); + } + }); + + return false; + }); + function switchDateFormat(format) { $("#log td.datetime").each(function(index, cell) { var dt = moment(cell.getAttribute("data-raw")); diff --git a/templates/base.html b/templates/base.html index 84f29f02..72a30412 100644 --- a/templates/base.html +++ b/templates/base.html @@ -36,7 +36,7 @@ - + diff --git a/templates/front/log.html b/templates/front/log.html index 5f302e29..b3f83927 100644 --- a/templates/front/log.html +++ b/templates/front/log.html @@ -40,6 +40,7 @@
Showing last {{ limit }} pings. Want to see more? @@ -128,6 +135,21 @@ {% endif %} + +
{{ check.last_ping_body }}+
{{ ping.body }}{% endif %}