forked from GithubBackups/healthchecks
Clean up Pushover subscription flow, add Setup Instructions with screenshots.
This commit is contained in:
parent
9bf82b706c
commit
827939deb7
@ -3,52 +3,68 @@ from hc.api.models import Channel
|
|||||||
from hc.test import BaseTestCase
|
from hc.test import BaseTestCase
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PUSHOVER_API_TOKEN="token", PUSHOVER_SUBSCRIPTION_URL="url")
|
@override_settings(PUSHOVER_API_TOKEN="token", PUSHOVER_SUBSCRIPTION_URL="http://example.org")
|
||||||
class AddPushoverTestCase(BaseTestCase):
|
class AddPushoverTestCase(BaseTestCase):
|
||||||
def test_instructions_work(self):
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
r = self.client.get("/integrations/add_pushover/")
|
|
||||||
self.assertContains(r, "Subscribe with Pushover")
|
|
||||||
|
|
||||||
def test_it_adds_channel(self):
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
|
|
||||||
session = self.client.session
|
|
||||||
session["po_nonce"] = "n"
|
|
||||||
session.save()
|
|
||||||
|
|
||||||
params = "pushover_user_key=a&nonce=n&prio=0"
|
|
||||||
r = self.client.get("/integrations/add_pushover/?%s" % params)
|
|
||||||
assert r.status_code == 302
|
|
||||||
|
|
||||||
channels = list(Channel.objects.all())
|
|
||||||
assert len(channels) == 1
|
|
||||||
assert channels[0].value == "a|0"
|
|
||||||
|
|
||||||
@override_settings(PUSHOVER_API_TOKEN=None)
|
@override_settings(PUSHOVER_API_TOKEN=None)
|
||||||
def test_it_requires_api_token(self):
|
def test_it_requires_api_token(self):
|
||||||
self.client.login(username="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", password="password")
|
||||||
r = self.client.get("/integrations/add_pushover/")
|
r = self.client.get("/integrations/add_pushover/")
|
||||||
self.assertEqual(r.status_code, 404)
|
self.assertEqual(r.status_code, 404)
|
||||||
|
|
||||||
|
def test_instructions_work_without_login(self):
|
||||||
|
r = self.client.get("/integrations/add_pushover/")
|
||||||
|
self.assertContains(r, "Setup Guide")
|
||||||
|
|
||||||
|
def test_it_shows_form(self):
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get("/integrations/add_pushover/")
|
||||||
|
self.assertContains(r, "Subscribe with Pushover")
|
||||||
|
|
||||||
|
def test_post_redirects(self):
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
payload = {"po_priority": 2}
|
||||||
|
r = self.client.post("/integrations/add_pushover/", form=payload)
|
||||||
|
self.assertEqual(r.status_code, 302)
|
||||||
|
|
||||||
|
def test_post_requires_authenticated_user(self):
|
||||||
|
payload = {"po_priority": 2}
|
||||||
|
r = self.client.post("/integrations/add_pushover/", form=payload)
|
||||||
|
self.assertEqual(r.status_code, 200)
|
||||||
|
self.assertContains(r, "Setup Guide")
|
||||||
|
|
||||||
|
def test_it_adds_channel(self):
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
|
session = self.client.session
|
||||||
|
session["pushover"] = "foo"
|
||||||
|
session.save()
|
||||||
|
|
||||||
|
params = "pushover_user_key=a&state=foo&prio=0"
|
||||||
|
r = self.client.get("/integrations/add_pushover/?%s" % params)
|
||||||
|
self.assertEqual(r.status_code, 302)
|
||||||
|
|
||||||
|
channels = list(Channel.objects.all())
|
||||||
|
assert len(channels) == 1
|
||||||
|
assert channels[0].value == "a|0"
|
||||||
|
|
||||||
def test_it_validates_priority(self):
|
def test_it_validates_priority(self):
|
||||||
self.client.login(username="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session["po_nonce"] = "n"
|
session["pushover"] = "foo"
|
||||||
session.save()
|
session.save()
|
||||||
|
|
||||||
params = "pushover_user_key=a&nonce=n&prio=abc"
|
params = "pushover_user_key=a&state=foo&prio=abc"
|
||||||
r = self.client.get("/integrations/add_pushover/?%s" % params)
|
r = self.client.get("/integrations/add_pushover/?%s" % params)
|
||||||
assert r.status_code == 400
|
self.assertEqual(r.status_code, 400)
|
||||||
|
|
||||||
def test_it_validates_nonce(self):
|
def test_it_validates_state(self):
|
||||||
self.client.login(username="alice@example.org", password="password")
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
|
||||||
session = self.client.session
|
session = self.client.session
|
||||||
session["po_nonce"] = "n"
|
session["pushover"] = "foo"
|
||||||
session.save()
|
session.save()
|
||||||
|
|
||||||
params = "pushover_user_key=a&nonce=INVALID&prio=0"
|
params = "pushover_user_key=a&state=INVALID&prio=0"
|
||||||
r = self.client.get("/integrations/add_pushover/?%s" % params)
|
r = self.client.get("/integrations/add_pushover/?%s" % params)
|
||||||
assert r.status_code == 403
|
self.assertEqual(r.status_code, 400)
|
@ -738,19 +738,21 @@ def add_discord(request):
|
|||||||
return render(request, "integrations/add_discord.html", ctx)
|
return render(request, "integrations/add_discord.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def add_pushover(request):
|
def add_pushover(request):
|
||||||
if settings.PUSHOVER_API_TOKEN is None or settings.PUSHOVER_SUBSCRIPTION_URL is None:
|
if settings.PUSHOVER_API_TOKEN is None or settings.PUSHOVER_SUBSCRIPTION_URL is None:
|
||||||
raise Http404("pushover integration is not available")
|
raise Http404("pushover integration is not available")
|
||||||
|
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
ctx = {"page": "channels"}
|
||||||
|
return render(request, "integrations/add_pushover.html", ctx)
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
# Initiate the subscription
|
# Initiate the subscription
|
||||||
nonce = get_random_string()
|
state = _prepare_state(request, "pushover")
|
||||||
request.session["po_nonce"] = nonce
|
|
||||||
|
|
||||||
failure_url = settings.SITE_ROOT + reverse("hc-channels")
|
failure_url = settings.SITE_ROOT + reverse("hc-channels")
|
||||||
success_url = settings.SITE_ROOT + reverse("hc-add-pushover") + "?" + urlencode({
|
success_url = settings.SITE_ROOT + reverse("hc-add-pushover") + "?" + urlencode({
|
||||||
"nonce": nonce,
|
"state": state,
|
||||||
"prio": request.POST.get("po_priority", "0"),
|
"prio": request.POST.get("po_priority", "0"),
|
||||||
})
|
})
|
||||||
subscription_url = settings.PUSHOVER_SUBSCRIPTION_URL + "?" + urlencode({
|
subscription_url = settings.PUSHOVER_SUBSCRIPTION_URL + "?" + urlencode({
|
||||||
@ -762,34 +764,28 @@ def add_pushover(request):
|
|||||||
|
|
||||||
# Handle successful subscriptions
|
# Handle successful subscriptions
|
||||||
if "pushover_user_key" in request.GET:
|
if "pushover_user_key" in request.GET:
|
||||||
if "nonce" not in request.GET or "prio" not in request.GET:
|
key = _get_validated_code(request, "pushover", "pushover_user_key")
|
||||||
|
if key is None:
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
# Validate nonce
|
|
||||||
if request.GET["nonce"] != request.session.get("po_nonce"):
|
|
||||||
return HttpResponseForbidden()
|
|
||||||
|
|
||||||
# Validate priority
|
# Validate priority
|
||||||
if request.GET["prio"] not in ("-2", "-1", "0", "1", "2"):
|
prio = request.GET.get("prio")
|
||||||
|
if prio not in ("-2", "-1", "0", "1", "2"):
|
||||||
return HttpResponseBadRequest()
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
# All looks well--
|
|
||||||
del request.session["po_nonce"]
|
|
||||||
|
|
||||||
if request.GET.get("pushover_unsubscribed") == "1":
|
if request.GET.get("pushover_unsubscribed") == "1":
|
||||||
# Unsubscription: delete all Pushover channels for this user
|
# Unsubscription: delete all Pushover channels for this user
|
||||||
Channel.objects.filter(user=request.user, kind="po").delete()
|
Channel.objects.filter(user=request.user, kind="po").delete()
|
||||||
return redirect("hc-channels")
|
return redirect("hc-channels")
|
||||||
else:
|
|
||||||
# Subscription
|
|
||||||
user_key = request.GET["pushover_user_key"]
|
|
||||||
priority = int(request.GET["prio"])
|
|
||||||
|
|
||||||
channel = Channel(user=request.team.user, kind="po")
|
# Subscription
|
||||||
channel.value = "%s|%d" % (user_key, priority)
|
channel = Channel(user=request.team.user, kind="po")
|
||||||
channel.save()
|
channel.value = "%s|%s" % (key, prio)
|
||||||
channel.assign_all_checks()
|
channel.save()
|
||||||
return redirect("hc-channels")
|
channel.assign_all_checks()
|
||||||
|
|
||||||
|
messages.success(request, "The Pushover integration has been added!")
|
||||||
|
return redirect("hc-channels")
|
||||||
|
|
||||||
# Show Integration Settings form
|
# Show Integration Settings form
|
||||||
ctx = {
|
ctx = {
|
||||||
|
BIN
static/img/integrations/setup_pushover_1.png
Normal file
BIN
static/img/integrations/setup_pushover_1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
static/img/integrations/setup_pushover_2.png
Normal file
BIN
static/img/integrations/setup_pushover_2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
@ -7,13 +7,99 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<h1>Pushover</h1>
|
<div class="jumbotron">
|
||||||
|
{% if request.user.is_authenticated %}
|
||||||
|
<p>
|
||||||
|
<a href="https://www.pushover.net/">Pushover</a> delivers
|
||||||
|
real-time notifications on your Android, iPhone, iPad, and Desktop,
|
||||||
|
Android Wear and Apple Watch. You can set up {% site_name %} to
|
||||||
|
receive Pushover notifications in a few simple steps.
|
||||||
|
</p>
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
{% site_name %} is a <strong>free</strong> and
|
||||||
|
<a href="https://github.com/healthchecks/healthchecks">open source</a>
|
||||||
|
service for monitoring your cron jobs, background processes and
|
||||||
|
scheduled tasks. Before adding Pushover integration, please log into
|
||||||
|
{% site_name %}:</p>
|
||||||
|
|
||||||
<p><a href="https://www.pushover.net/">Pushover</a> is a service to receive
|
<div class="text-center">
|
||||||
instant push notifications on your phone or tablet from a variety of
|
<form class="form-inline" action="{% url 'hc-login' %}" method="post">
|
||||||
sources. If you bought the app on your mobile device, you can integrate it
|
{% csrf_token %}
|
||||||
with your {% site_name %} account in a few simple steps.</p>
|
<div class="form-group">
|
||||||
|
<div class="input-group input-group-lg">
|
||||||
|
<div class="input-group-addon">@</div>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
class="form-control"
|
||||||
|
name="email"
|
||||||
|
autocomplete="email"
|
||||||
|
placeholder="Email">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<button type="submit" class="btn btn-lg btn-primary pull-right">
|
||||||
|
Log In
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if not request.user.is_authenticated %}
|
||||||
|
<h2>Setup Guide</h2>
|
||||||
|
|
||||||
|
<div class="row ai-step">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<span class="step-no">1</span>
|
||||||
|
<p>
|
||||||
|
After logging in, go to "Integrations → Add Pushover".
|
||||||
|
Pushover supports different notification priorities from
|
||||||
|
silent to "Emergency". Select your preferred priority
|
||||||
|
and click "Subscribe with Pushover".
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<img
|
||||||
|
class="ai-guide-screenshot"
|
||||||
|
alt="Screenshot"
|
||||||
|
src="{% static 'img/integrations/setup_pushover_1.png' %}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row ai-step">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<span class="step-no">2</span>
|
||||||
|
<p>
|
||||||
|
You will be redirected to
|
||||||
|
<a href="https://pushover.net">pushover.net</a> and
|
||||||
|
asked to confirm a subscription. Review the subscription
|
||||||
|
details and click "Subscribe Me".
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<img
|
||||||
|
class="ai-guide-screenshot"
|
||||||
|
alt="Screenshot"
|
||||||
|
src="{% static 'img/integrations/setup_pushover_2.png' %}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row ai-step">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<span class="step-no">3</span>
|
||||||
|
<p>
|
||||||
|
That is all! You will now be redirected back to
|
||||||
|
"Integrations" page on {% site_name %} and see
|
||||||
|
the new integration!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% else %}
|
||||||
<h2>Integration Settings</h2>
|
<h2>Integration Settings</h2>
|
||||||
|
|
||||||
<form method="post" id="add-pushover" class="form-horizontal" action="{% url 'hc-add-pushover' %}">
|
<form method="post" id="add-pushover" class="form-horizontal" action="{% url 'hc-add-pushover' %}">
|
||||||
@ -82,6 +168,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -58,7 +58,7 @@
|
|||||||
|
|
||||||
<div class="row ai-step">
|
<div class="row ai-step">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<span class="step-no">2</span>
|
<span class="step-no">1</span>
|
||||||
<p>
|
<p>
|
||||||
After {% if request.user.is_authenticated %}{% else %}logging in and{% endif %}
|
After {% if request.user.is_authenticated %}{% else %}logging in and{% endif %}
|
||||||
clicking on "Add to Slack", you should
|
clicking on "Add to Slack", you should
|
||||||
@ -77,7 +77,7 @@
|
|||||||
|
|
||||||
<div class="row ai-step">
|
<div class="row ai-step">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<span class="step-no">3</span>
|
<span class="step-no">2</span>
|
||||||
<p>
|
<p>
|
||||||
You should now be on a page that says "{% site_name %} would
|
You should now be on a page that says "{% site_name %} would
|
||||||
like access to <i>TEAM NAME</i>". Select the channel you want to
|
like access to <i>TEAM NAME</i>". Select the channel you want to
|
||||||
@ -94,7 +94,7 @@
|
|||||||
|
|
||||||
<div class="row ai-step">
|
<div class="row ai-step">
|
||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<span class="step-no">4</span>
|
<span class="step-no">3</span>
|
||||||
<p>
|
<p>
|
||||||
That is all! You will now be redirected back to
|
That is all! You will now be redirected back to
|
||||||
"Integrations" page on {% site_name %} and see
|
"Integrations" page on {% site_name %} and see
|
||||||
|
Loading…
x
Reference in New Issue
Block a user