Browse Source

Added "When paused, ignore pings" option in the Filtering Rules dialog (#369)

pull/374/head
Pēteris Caune 5 years ago
parent
commit
3eebd8968d
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
15 changed files with 172 additions and 11 deletions
  1. +1
    -0
      CHANGELOG.md
  2. +18
    -0
      hc/api/migrations/0071_check_manual_resume.py
  3. +4
    -0
      hc/api/models.py
  4. +15
    -0
      hc/api/tests/test_ping.py
  5. +2
    -1
      hc/front/forms.py
  6. +27
    -1
      hc/front/tests/test_filtering_rules.py
  7. +1
    -1
      hc/front/tests/test_pause.py
  8. +28
    -0
      hc/front/tests/test_resume.py
  9. +12
    -0
      hc/front/tests/test_status_single.py
  10. +1
    -0
      hc/front/urls.py
  11. +16
    -4
      hc/front/views.py
  12. +8
    -1
      static/js/details.js
  13. +11
    -3
      templates/front/details.html
  14. +23
    -0
      templates/front/filtering_rules_modal.html
  15. +5
    -0
      templates/front/log_status_text.html

+ 1
- 0
CHANGELOG.md View File

@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
- Add a "Transfer Ownership" feature in Project Settings
- In checks list, the pause button asks for confirmation (#356)
- Added /api/v1/metrics/ endpoint, useful for monitoring the service itself
- Added "When paused, ignore pings" option in the Filtering Rules dialog (#369)
### Bug Fixes
- "Get a single check" API call now supports read-only API keys (#346)


+ 18
- 0
hc/api/migrations/0071_check_manual_resume.py View File

@ -0,0 +1,18 @@
# Generated by Django 3.0.4 on 2020-06-02 07:35
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('api', '0070_auto_20200411_1310'),
]
operations = [
migrations.AddField(
model_name='check',
name='manual_resume',
field=models.NullBooleanField(default=False),
),
]

+ 4
- 0
hc/api/models.py View File

@ -75,6 +75,7 @@ class Check(models.Model):
tz = models.CharField(max_length=36, default="UTC")
subject = models.CharField(max_length=100, blank=True)
methods = models.CharField(max_length=30, blank=True)
manual_resume = models.NullBooleanField(default=False)
n_pings = models.IntegerField(default=0)
last_ping = models.DateTimeField(null=True, blank=True)
@ -243,6 +244,9 @@ class Check(models.Model):
def ping(self, remote_addr, scheme, method, ua, body, action):
now = timezone.now()
if self.status == "paused" and self.manual_resume:
action = "ign"
if action == "start":
self.last_start = now
# Don't update "last_ping" field.


+ 15
- 0
hc/api/tests/test_ping.py View File

@ -209,3 +209,18 @@ class PingTestCase(BaseTestCase):
ping = Ping.objects.latest("id")
self.assertEqual(len(ping.body), 20000)
def test_it_handles_manual_resume_flag(self):
self.check.status = "paused"
self.check.manual_resume = True
self.check.save()
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.check.refresh_from_db()
self.assertEqual(self.check.status, "paused")
ping = Ping.objects.latest("id")
self.assertEqual(ping.scheme, "http")
self.assertEqual(ping.kind, "ign")

+ 2
- 1
hc/front/forms.py View File

@ -63,8 +63,9 @@ class NameTagsForm(forms.Form):
class FilteringRulesForm(forms.Form):
subject = forms.CharField(max_length=100)
subject = forms.CharField(required=False, max_length=100)
methods = forms.ChoiceField(required=False, choices=(("", "Any"), ("POST", "POST")))
manual_resume = forms.BooleanField(required=False)
class TimeoutForm(forms.Form):


+ 27
- 1
hc/front/tests/test_filtering_rules.py View File

@ -12,12 +12,16 @@ class FilteringRulesTestCase(BaseTestCase):
def test_it_works(self):
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, data={"subject": "SUCCESS", "methods": "POST"})
r = self.client.post(
self.url,
data={"subject": "SUCCESS", "methods": "POST", "manual_resume": "1"},
)
self.assertRedirects(r, self.redirect_url)
self.check.refresh_from_db()
self.assertEqual(self.check.subject, "SUCCESS")
self.assertEqual(self.check.methods, "POST")
self.assertTrue(self.check.manual_resume)
def test_it_clears_method(self):
self.check.method = "POST"
@ -29,3 +33,25 @@ class FilteringRulesTestCase(BaseTestCase):
self.check.refresh_from_db()
self.assertEqual(self.check.methods, "")
def test_it_clears_subject(self):
self.check.subject = "SUCCESS"
self.check.save()
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, data={"methods": ""})
self.assertRedirects(r, self.redirect_url)
self.check.refresh_from_db()
self.assertEqual(self.check.subject, "")
def test_it_clears_manual_resume_flag(self):
self.check.manual_resume = True
self.check.save()
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url, data={})
self.assertRedirects(r, self.redirect_url)
self.check.refresh_from_db()
self.assertFalse(self.check.manual_resume)

+ 1
- 1
hc/front/tests/test_pause.py View File

@ -10,7 +10,7 @@ class PauseTestCase(BaseTestCase):
super(PauseTestCase, self).setUp()
self.check = Check.objects.create(project=self.project, status="up")
self.url = "/checks/%s/pause/" % self.check.code
self.redirect_url = "/projects/%s/checks/" % self.project.code
self.redirect_url = "/checks/%s/details/" % self.check.code
def test_it_pauses(self):
self.client.login(username="[email protected]", password="password")


+ 28
- 0
hc/front/tests/test_resume.py View File

@ -0,0 +1,28 @@
from hc.api.models import Check
from hc.test import BaseTestCase
class ResumeTestCase(BaseTestCase):
def setUp(self):
super(ResumeTestCase, self).setUp()
self.check = Check.objects.create(project=self.project, status="paused")
self.url = "/checks/%s/resume/" % self.check.code
self.redirect_url = "/checks/%s/details/" % self.check.code
def test_it_resumes(self):
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url)
self.assertRedirects(r, self.redirect_url)
self.check.refresh_from_db()
self.assertEqual(self.check.status, "new")
def test_it_rejects_get(self):
self.client.login(username="[email protected]", password="password")
r = self.client.get(self.url)
self.assertEqual(r.status_code, 405)
def test_it_allows_cross_team_access(self):
self.client.login(username="[email protected]", password="password")
r = self.client.post(self.url)
self.assertRedirects(r, self.redirect_url)

+ 12
- 0
hc/front/tests/test_status_single.py View File

@ -50,3 +50,15 @@ class StatusSingleTestCase(BaseTestCase):
self.client.login(username="[email protected]", password="password")
r = self.client.get("/checks/%s/status/" % self.check.code)
self.assertEqual(r.status_code, 200)
def test_it_handles_manual_resume(self):
self.check.status = "paused"
self.check.manual_resume = True
self.check.save()
self.client.login(username="[email protected]", password="password")
r = self.client.get("/checks/%s/status/" % self.check.code)
doc = r.json()
self.assertEqual(doc["status"], "paused")
self.assertTrue("will ignore pings until resumed" in doc["status_text"])

+ 1
- 0
hc/front/urls.py View File

@ -8,6 +8,7 @@ check_urls = [
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("resume/", views.resume, name="hc-resume"),
path("remove/", views.remove_check, name="hc-remove-check"),
path("log/", views.log, name="hc-log"),
path("status/", views.status_single, name="hc-status-single"),


+ 16
- 4
hc/front/views.py View File

@ -344,6 +344,7 @@ def filtering_rules(request, code):
if form.is_valid():
check.subject = form.cleaned_data["subject"]
check.methods = form.cleaned_data["methods"]
check.manual_resume = form.cleaned_data["manual_resume"]
check.save()
return redirect("hc-details", code)
@ -442,14 +443,25 @@ def pause(request, code):
check.alert_after = None
check.save()
if "/details/" in request.META.get("HTTP_REFERER", ""):
return redirect("hc-details", code)
# Don't redirect after an AJAX request:
if request.META.get("HTTP_X_REQUESTED_WITH") == "XMLHttpRequest":
return HttpResponse()
return redirect("hc-checks", check.project.code)
return redirect("hc-details", code)
@require_POST
@login_required
def resume(request, code):
check = _get_check_for_user(request, code)
check.status = "new"
check.last_start = None
check.last_ping = None
check.alert_after = None
check.save()
return redirect("hc-details", code)
@require_POST


+ 8
- 1
static/js/details.js View File

@ -36,6 +36,11 @@ $(function () {
return false;
});
$("#log-status-text").on("click", "#resume-btn", function() {
$("#resume-form").submit();
return false;
});
$("#pause").click(function(e) {
$("#pause-form").submit();
return false;
@ -79,7 +84,9 @@ $(function () {
if (data.status_text != lastStatusText) {
lastStatusText = data.status_text;
$("#log-status-icon").attr("class", "status icon-" + data.status);
$("#log-status-text").text(data.status_text);
$("#log-status-text").html(data.status_text);
$('#pause-btn').prop('disabled', data.status == "paused");
}
if (data.events) {


+ 11
- 3
templates/front/details.html View File

@ -127,7 +127,11 @@
<div class="text-right">
<form action="{% url 'hc-pause' check.code %}" method="post">
{% csrf_token %}
<input type="submit" class="btn btn-sm btn-default" value="Pause" />
<input
id="pause-btn"
type="submit"
{% if check.status == "paused" %}disabled{% endif %}
class="btn btn-sm btn-default" value="Pause" />
</form>
<button
@ -289,8 +293,12 @@
</div>
</div>
<div id="transfer-modal" class="modal">
</div>
<div id="transfer-modal" class="modal"></div>
<form id="resume-form" action="{% url 'hc-resume' check.code %}" method="post">
{% csrf_token %}
</form>
{% include "front/update_name_modal.html" %}
{% include "front/update_timeout_modal.html" %}


+ 23
- 0
templates/front/filtering_rules_modal.html View File

@ -58,6 +58,29 @@
</div>
</div>
</div>
<div class="modal-body">
<p>Handling of pings while paused:</p>
<label class="radio-container">
<input
type="radio"
name="manual_resume"
value=""
{% if not check.manual_resume %}checked{% endif %}>
<span class="radiomark"></span>
Leave the paused state (default)
</label>
<label class="radio-container">
<input
type="radio"
name="manual_resume"
value="1"
{% if check.manual_resume %}checked{% endif %}>
<span class="radiomark"></span>
Ignore pings, stay in the paused state
</label>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary">Save</button>


+ 5
- 0
templates/front/log_status_text.html View File

@ -6,8 +6,13 @@
This check is up. Last ping was {{ check.last_ping|naturaltime }}.
{% elif status == "grace" %}
This check is late. Last ping was {{ check.last_ping|naturaltime }}.
{% elif status == "paused" and check.manual_resume %}
This check is paused, and will ignore pings until resumed.
<a id="resume-btn" href="#">(Resume&nbsp;Now)</a>
{% elif status == "paused" %}
This check is paused.
{% elif status == "new" and check.n_pings %}
This check is ready for pings.
{% elif status == "new" %}
This check has never received a ping.
{% elif status == "started" %}


Loading…
Cancel
Save