diff --git a/hc/front/forms.py b/hc/front/forms.py index d80b74f2..64f84102 100644 --- a/hc/front/forms.py +++ b/hc/front/forms.py @@ -175,3 +175,8 @@ class AddMatrixForm(forms.Form): class AddAppriseForm(forms.Form): error_css_class = "has-error" url = forms.CharField(max_length=512) + + +class AddPdForm(forms.Form): + error_css_class = "has-error" + value = forms.CharField(max_length=32) diff --git a/hc/front/tests/test_add_pd.py b/hc/front/tests/test_add_pd.py new file mode 100644 index 00000000..b2a8dc87 --- /dev/null +++ b/hc/front/tests/test_add_pd.py @@ -0,0 +1,32 @@ +from hc.api.models import Channel +from hc.test import BaseTestCase + + +class AddPdTestCase(BaseTestCase): + url = "/integrations/add_pd/" + + def test_instructions_work(self): + self.client.login(username="alice@example.org", password="password") + r = self.client.get(self.url) + self.assertContains(r, "Paste the Integration Key down below") + + def test_it_works(self): + # Integration key is 32 characters long + form = {"value": "12345678901234567890123456789012"} + + self.client.login(username="alice@example.org", password="password") + r = self.client.post(self.url, form) + self.assertRedirects(r, "/integrations/") + + c = Channel.objects.get() + self.assertEqual(c.kind, "pd") + self.assertEqual(c.value, "12345678901234567890123456789012") + + def test_it_trims_whitespace(self): + form = {"value": " 123456 "} + + self.client.login(username="alice@example.org", password="password") + self.client.post(self.url, form) + + c = Channel.objects.get() + self.assertEqual(c.value, "123456") diff --git a/hc/front/urls.py b/hc/front/urls.py index 8e5c57b1..a30c3ad0 100644 --- a/hc/front/urls.py +++ b/hc/front/urls.py @@ -27,6 +27,7 @@ channel_urls = [ path("add_email/", views.add_email, name="hc-add-email"), path("add_webhook/", views.add_webhook, name="hc-add-webhook"), path("add_shell/", views.add_shell, name="hc-add-shell"), + path("add_pd/", views.add_pd, name="hc-add-pd"), path("add_pdc/", views.add_pdc, name="hc-add-pdc"), path("add_pdc//", views.add_pdc, name="hc-add-pdc-state"), path("add_pagertree/", views.add_pagertree, name="hc-add-pagertree"), diff --git a/hc/front/views.py b/hc/front/views.py index 53b5beb7..7b654f4a 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -34,19 +34,20 @@ from hc.api.models import ( ) from hc.api.transports import Telegram from hc.front.forms import ( - AddWebhookForm, - NameTagsForm, - TimeoutForm, - AddUrlForm, + AddAppriseForm, AddEmailForm, + AddMatrixForm, AddOpsGenieForm, - CronForm, + AddPdForm, + AddShellForm, AddSmsForm, + AddUrlForm, + AddWebhookForm, ChannelNameForm, + CronForm, EmailSettingsForm, - AddMatrixForm, - AddAppriseForm, - AddShellForm, + NameTagsForm, + TimeoutForm, ) from hc.front.schemas import telegram_callback from hc.front.templatetags.hc_extras import num_down_title, down_title, sortchecks @@ -252,7 +253,6 @@ def index(request): "enable_telegram": settings.TELEGRAM_TOKEN is not None, "enable_sms": settings.TWILIO_AUTH is not None, "enable_whatsapp": settings.TWILIO_USE_WHATSAPP, - "enable_pd": settings.PD_VENDOR_KEY is not None, "enable_trello": settings.TRELLO_APP_KEY is not None, "enable_matrix": settings.MATRIX_ACCESS_TOKEN is not None, "enable_apprise": settings.APPRISE_ENABLED is True, @@ -649,7 +649,7 @@ def channels(request): "enable_telegram": settings.TELEGRAM_TOKEN is not None, "enable_sms": settings.TWILIO_AUTH is not None, "enable_whatsapp": settings.TWILIO_USE_WHATSAPP, - "enable_pd": settings.PD_VENDOR_KEY is not None, + "enable_pdc": settings.PD_VENDOR_KEY is not None, "enable_trello": settings.TRELLO_APP_KEY is not None, "enable_matrix": settings.MATRIX_ACCESS_TOKEN is not None, "enable_apprise": settings.APPRISE_ENABLED is True, @@ -863,6 +863,24 @@ def _get_validated_code(request, session_key, key="code"): return request.GET.get(key) +@login_required +def add_pd(request): + if request.method == "POST": + form = AddPdForm(request.POST) + if form.is_valid(): + channel = Channel(project=request.project, kind="pd") + channel.value = form.cleaned_data["value"] + channel.save() + + channel.assign_all_checks() + return redirect("hc-channels") + else: + form = AddPdForm() + + ctx = {"page": "channels", "form": form} + return render(request, "integrations/add_pd.html", ctx) + + def add_pdc(request, state=None): if settings.PD_VENDOR_KEY is None: raise Http404("pagerduty integration is not available") diff --git a/static/img/integrations/setup_pd_s1.png b/static/img/integrations/setup_pd_s1.png new file mode 100644 index 00000000..d2076feb Binary files /dev/null and b/static/img/integrations/setup_pd_s1.png differ diff --git a/static/img/integrations/setup_pd_s2.png b/static/img/integrations/setup_pd_s2.png new file mode 100644 index 00000000..cb14d55b Binary files /dev/null and b/static/img/integrations/setup_pd_s2.png differ diff --git a/templates/front/channels.html b/templates/front/channels.html index 72d72637..a7325511 100644 --- a/templates/front/channels.html +++ b/templates/front/channels.html @@ -251,7 +251,6 @@ Add Integration - {% if enable_pd %}
  • PagerDuty icon @@ -259,9 +258,12 @@

    PagerDuty

    On-call scheduling, alerting, and incident tracking.

    - Add Integration + {% if enable_pdc %} + Add Integration + {% else %} + Add Integration + {% endif %}
  • - {% endif %}
  • - {% if enable_pd %}

    PagerDuty
    Incident Management

    - {% endif %}
    diff --git a/templates/integrations/add_pd.html b/templates/integrations/add_pd.html new file mode 100644 index 00000000..af2392eb --- /dev/null +++ b/templates/integrations/add_pd.html @@ -0,0 +1,94 @@ +{% extends "base.html" %} +{% load compress humanize staticfiles hc_extras %} + +{% block title %}Add PagerDuty - {% site_name %}{% endblock %} + + +{% block content %} +
    +
    +

    PagerDuty

    + +

    + If your team uses PagerDuty, + you can set up {% site_name %} to create a PagerDuty incident + when a check goes down, and resolve it when a check goes back up. +

    + +

    Setup Guide

    +
    +
    + 1 +

    + Log into your PagerDuty account, + go to Configuration > Services, + and click on New Service. +

    +

    + Give it a descriptive name, and + for Integration Type select + Use our API directly. +

    + +
    +
    + Screenshot +
    +
    +
    +
    + 2 + After adding the new service, take note of its + Integration Key, a long string + of letters and digits. + +
    +
    + Screenshot +
    +
    + +
    +
    + 3 +

    Paste the Integration Key down below. Save the integration, and it's done!

    +
    +
    + +

    Integration Settings

    + +
    + {% csrf_token %} +
    + +
    + + + {% if form.value.errors %} +
    + {{ form.value.errors|join:"" }} +
    + {% endif %} +
    +
    +
    +
    + +
    +
    +
    +
    +
    +{% endblock %}