forked from GithubBackups/healthchecks
Pushbullet integration. Fixes #24
This commit is contained in:
parent
d2ce8c13d8
commit
468c4b4b2c
@ -176,6 +176,8 @@ class Channel(models.Model):
|
|||||||
return transports.PagerDuty(self)
|
return transports.PagerDuty(self)
|
||||||
elif self.kind == "victorops":
|
elif self.kind == "victorops":
|
||||||
return transports.VictorOps(self)
|
return transports.VictorOps(self)
|
||||||
|
elif self.kind == "pushbullet":
|
||||||
|
return transports.Pushbullet(self)
|
||||||
elif self.kind == "po":
|
elif self.kind == "po":
|
||||||
return transports.Pushover(self)
|
return transports.Pushover(self)
|
||||||
else:
|
else:
|
||||||
|
@ -65,8 +65,12 @@ class HttpTransport(Transport):
|
|||||||
def request(self, method, url, **kwargs):
|
def request(self, method, url, **kwargs):
|
||||||
try:
|
try:
|
||||||
options = dict(kwargs)
|
options = dict(kwargs)
|
||||||
|
if "headers" not in options:
|
||||||
|
options["headers"] = {}
|
||||||
|
|
||||||
options["timeout"] = 5
|
options["timeout"] = 5
|
||||||
options["headers"] = {"User-Agent": "healthchecks.io"}
|
options["headers"]["User-Agent"] = "healthchecks.io"
|
||||||
|
|
||||||
r = requests.request(method, url, **options)
|
r = requests.request(method, url, **options)
|
||||||
if r.status_code not in (200, 201, 204):
|
if r.status_code not in (200, 201, 204):
|
||||||
return "Received status code %d" % r.status_code
|
return "Received status code %d" % r.status_code
|
||||||
@ -79,8 +83,8 @@ class HttpTransport(Transport):
|
|||||||
def get(self, url):
|
def get(self, url):
|
||||||
return self.request("get", url)
|
return self.request("get", url)
|
||||||
|
|
||||||
def post(self, url, json):
|
def post(self, url, json, **kwargs):
|
||||||
return self.request("post", url, json=json)
|
return self.request("post", url, json=json, **kwargs)
|
||||||
|
|
||||||
def post_form(self, url, data):
|
def post_form(self, url, data):
|
||||||
return self.request("post", url, data=data)
|
return self.request("post", url, data=data)
|
||||||
@ -154,6 +158,23 @@ class PagerDuty(HttpTransport):
|
|||||||
return self.post(self.URL, payload)
|
return self.post(self.URL, payload)
|
||||||
|
|
||||||
|
|
||||||
|
class Pushbullet(HttpTransport):
|
||||||
|
def notify(self, check):
|
||||||
|
text = tmpl("pushbullet_message.html", check=check)
|
||||||
|
url = "https://api.pushbullet.com/v2/pushes"
|
||||||
|
headers = {
|
||||||
|
"Access-Token": self.channel.value,
|
||||||
|
"Conent-Type": "application/json"
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
"type": "note",
|
||||||
|
"title": "healthchecks.io",
|
||||||
|
"body": text
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.post(url, payload, headers=headers)
|
||||||
|
|
||||||
|
|
||||||
class Pushover(HttpTransport):
|
class Pushover(HttpTransport):
|
||||||
URL = "https://api.pushover.net/1/messages.json"
|
URL = "https://api.pushover.net/1/messages.json"
|
||||||
|
|
||||||
@ -161,7 +182,7 @@ class Pushover(HttpTransport):
|
|||||||
others = self.checks().filter(status="down").exclude(code=check.code)
|
others = self.checks().filter(status="down").exclude(code=check.code)
|
||||||
ctx = {
|
ctx = {
|
||||||
"check": check,
|
"check": check,
|
||||||
"down_checks": others,
|
"down_checks": others,
|
||||||
}
|
}
|
||||||
text = tmpl("pushover_message.html", **ctx)
|
text = tmpl("pushover_message.html", **ctx)
|
||||||
title = tmpl("pushover_title.html", **ctx)
|
title = tmpl("pushover_title.html", **ctx)
|
||||||
|
@ -150,3 +150,9 @@ class AddChannelTestCase(BaseTestCase):
|
|||||||
|
|
||||||
c = Channel.objects.get()
|
c = Channel.objects.get()
|
||||||
self.assertEqual(c.value, "\nhttp://foo.com")
|
self.assertEqual(c.value, "\nhttp://foo.com")
|
||||||
|
|
||||||
|
@override_settings(PUSHBULLET_CLIENT_ID="foo")
|
||||||
|
def test_pushbullet_instructions_work(self):
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get("/integrations/add_pushbullet/")
|
||||||
|
self.assertContains(r, "www.pushbullet.com/authorize", status_code=200)
|
||||||
|
27
hc/front/tests/test_pushbullet_callback.py
Normal file
27
hc/front/tests/test_pushbullet_callback.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from django.test.utils import override_settings
|
||||||
|
from hc.api.models import Channel
|
||||||
|
from hc.test import BaseTestCase
|
||||||
|
from mock import patch
|
||||||
|
|
||||||
|
|
||||||
|
@override_settings(PUSHBULLET_CLIENT_ID="t1", PUSHBULLET_CLIENT_SECRET="s1")
|
||||||
|
class PushbulletCallbackTestCase(BaseTestCase):
|
||||||
|
|
||||||
|
@patch("hc.front.views.requests.post")
|
||||||
|
def test_it_works(self, mock_post):
|
||||||
|
oauth_response = {"access_token": "test-token"}
|
||||||
|
|
||||||
|
mock_post.return_value.text = json.dumps(oauth_response)
|
||||||
|
mock_post.return_value.json.return_value = oauth_response
|
||||||
|
|
||||||
|
url = "/integrations/add_pushbullet/?code=12345678"
|
||||||
|
|
||||||
|
self.client.login(username="alice@example.org", password="password")
|
||||||
|
r = self.client.get(url, follow=True)
|
||||||
|
self.assertRedirects(r, "/integrations/")
|
||||||
|
self.assertContains(r, "The Pushbullet integration has been added!")
|
||||||
|
|
||||||
|
ch = Channel.objects.get()
|
||||||
|
self.assertEqual(ch.value, "test-token")
|
@ -1,12 +1,10 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
from django.test.utils import override_settings
|
|
||||||
from hc.api.models import Channel
|
from hc.api.models import Channel
|
||||||
from hc.test import BaseTestCase
|
from hc.test import BaseTestCase
|
||||||
from mock import patch
|
from mock import patch
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PUSHOVER_API_TOKEN="token", PUSHOVER_SUBSCRIPTION_URL="url")
|
|
||||||
class SlackCallbackTestCase(BaseTestCase):
|
class SlackCallbackTestCase(BaseTestCase):
|
||||||
|
|
||||||
@patch("hc.front.views.requests.post")
|
@patch("hc.front.views.requests.post")
|
||||||
|
@ -23,6 +23,7 @@ urlpatterns = [
|
|||||||
url(r'^integrations/add_slack/$', views.add_slack, name="hc-add-slack"),
|
url(r'^integrations/add_slack/$', views.add_slack, name="hc-add-slack"),
|
||||||
url(r'^integrations/add_slack_btn/$', views.add_slack_btn, name="hc-add-slack-btn"),
|
url(r'^integrations/add_slack_btn/$', views.add_slack_btn, name="hc-add-slack-btn"),
|
||||||
url(r'^integrations/add_hipchat/$', views.add_hipchat, name="hc-add-hipchat"),
|
url(r'^integrations/add_hipchat/$', views.add_hipchat, name="hc-add-hipchat"),
|
||||||
|
url(r'^integrations/add_pushbullet/$', views.add_pushbullet, name="hc-add-pushbullet"),
|
||||||
url(r'^integrations/add_pushover/$', views.add_pushover, name="hc-add-pushover"),
|
url(r'^integrations/add_pushover/$', views.add_pushover, name="hc-add-pushover"),
|
||||||
url(r'^integrations/add_victorops/$', views.add_victorops, name="hc-add-victorops"),
|
url(r'^integrations/add_victorops/$', views.add_victorops, name="hc-add-victorops"),
|
||||||
url(r'^integrations/([\w-]+)/checks/$', views.channel_checks, name="hc-channel-checks"),
|
url(r'^integrations/([\w-]+)/checks/$', views.channel_checks, name="hc-channel-checks"),
|
||||||
|
@ -270,6 +270,7 @@ def channels(request):
|
|||||||
"page": "channels",
|
"page": "channels",
|
||||||
"channels": channels,
|
"channels": channels,
|
||||||
"num_checks": num_checks,
|
"num_checks": num_checks,
|
||||||
|
"enable_pushbullet": settings.PUSHBULLET_CLIENT_ID is not None,
|
||||||
"enable_pushover": settings.PUSHOVER_API_TOKEN is not None
|
"enable_pushover": settings.PUSHOVER_API_TOKEN is not None
|
||||||
}
|
}
|
||||||
return render(request, "front/channels.html", ctx)
|
return render(request, "front/channels.html", ctx)
|
||||||
@ -418,6 +419,48 @@ def add_hipchat(request):
|
|||||||
return render(request, "integrations/add_hipchat.html", ctx)
|
return render(request, "integrations/add_hipchat.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def add_pushbullet(request):
|
||||||
|
if "code" in request.GET:
|
||||||
|
code = request.GET.get("code", "")
|
||||||
|
if len(code) < 8:
|
||||||
|
return HttpResponseBadRequest()
|
||||||
|
|
||||||
|
result = requests.post("https://api.pushbullet.com/oauth2/token", {
|
||||||
|
"client_id": settings.PUSHBULLET_CLIENT_ID,
|
||||||
|
"client_secret": settings.PUSHBULLET_CLIENT_SECRET,
|
||||||
|
"code": code,
|
||||||
|
"grant_type": "authorization_code"
|
||||||
|
})
|
||||||
|
|
||||||
|
doc = result.json()
|
||||||
|
if "access_token" in doc:
|
||||||
|
channel = Channel(kind="pushbullet")
|
||||||
|
channel.user = request.team.user
|
||||||
|
channel.value = doc["access_token"]
|
||||||
|
channel.save()
|
||||||
|
channel.assign_all_checks()
|
||||||
|
messages.success(request,
|
||||||
|
"The Pushbullet integration has been added!")
|
||||||
|
else:
|
||||||
|
messages.warning(request, "Something went wrong")
|
||||||
|
|
||||||
|
return redirect("hc-channels")
|
||||||
|
|
||||||
|
redirect_uri = settings.SITE_ROOT + reverse("hc-add-pushbullet")
|
||||||
|
authorize_url = "https://www.pushbullet.com/authorize?" + urlencode({
|
||||||
|
"client_id": settings.PUSHBULLET_CLIENT_ID,
|
||||||
|
"redirect_uri": redirect_uri,
|
||||||
|
"response_type": "code"
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx = {
|
||||||
|
"page": "channels",
|
||||||
|
"authorize_url": authorize_url
|
||||||
|
}
|
||||||
|
return render(request, "integrations/add_pushbullet.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@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:
|
||||||
|
@ -131,14 +131,9 @@ table.channels-table > tbody > tr > th {
|
|||||||
line-height: 40px;
|
line-height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.ai-icon img {
|
|
||||||
width: 48px;
|
|
||||||
height: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn img.ai-icon {
|
.btn img.ai-icon {
|
||||||
height: 1.4em;
|
height: 1.4em;
|
||||||
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.add-integration h2 {
|
.add-integration h2 {
|
||||||
|
BIN
static/img/integrations/pushbullet.png
Normal file
BIN
static/img/integrations/pushbullet.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 7.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 16 KiB |
BIN
static/img/logo-512-green.png
Normal file
BIN
static/img/logo-512-green.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
@ -34,6 +34,7 @@
|
|||||||
{% if ch.kind == "pd" %} PagerDuty {% endif %}
|
{% if ch.kind == "pd" %} PagerDuty {% endif %}
|
||||||
{% if ch.kind == "po" %} Pushover {% endif %}
|
{% if ch.kind == "po" %} Pushover {% endif %}
|
||||||
{% if ch.kind == "victorops" %} VictorOps {% endif %}
|
{% if ch.kind == "victorops" %} VictorOps {% endif %}
|
||||||
|
{% if ch.kind == "pushbullet" %} Pushbullet {% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="value-cell">
|
<td class="value-cell">
|
||||||
{% if ch.kind == "email" %}
|
{% if ch.kind == "email" %}
|
||||||
@ -76,6 +77,9 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
|
{% elif ch.kind == "pushbullet" %}
|
||||||
|
<span class="preposition">API key</span>
|
||||||
|
{{ ch.value }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ ch.value }}
|
{{ ch.value }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -175,6 +179,17 @@
|
|||||||
|
|
||||||
<a href="{% url 'hc-add-victorops' %}" class="btn btn-primary">Add Integration</a>
|
<a href="{% url 'hc-add-victorops' %}" class="btn btn-primary">Add Integration</a>
|
||||||
</li>
|
</li>
|
||||||
|
{% if enable_pushbullet %}
|
||||||
|
<li>
|
||||||
|
<img src="{% static 'img/integrations/pushbullet.png' %}"
|
||||||
|
class="icon" alt="Pushbullet icon" />
|
||||||
|
|
||||||
|
<h2>Pushbullet</h2>
|
||||||
|
<p>Pushbullet connects your devices, making them feel like one.</p>
|
||||||
|
|
||||||
|
<a href="{% url 'hc-add-pushbullet' %}" class="btn btn-primary">Add Integration</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
{% if enable_pushover %}
|
{% if enable_pushover %}
|
||||||
<li>
|
<li>
|
||||||
<img src="{% static 'img/integrations/pushover.png' %}"
|
<img src="{% static 'img/integrations/pushover.png' %}"
|
||||||
|
37
templates/integrations/add_pushbullet.html
Normal file
37
templates/integrations/add_pushbullet.html
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load compress humanize staticfiles hc_extras %}
|
||||||
|
|
||||||
|
{% block title %}Add Pushbullet - healthchecks.io{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<h1>Pushbullet</h1>
|
||||||
|
|
||||||
|
<div class="jumbotron">
|
||||||
|
<p>
|
||||||
|
With this integration, healthchecks.io will send
|
||||||
|
a <a href="http://pushbullet.com/">Pushbullet</a>
|
||||||
|
notification when a check
|
||||||
|
goes <strong>up</strong> or <strong>down</strong>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="post" class="text-center">
|
||||||
|
{% csrf_token %}
|
||||||
|
<a href="{{ authorize_url }}" class="btn btn-default">
|
||||||
|
<img class="ai-icon" src="{% static 'img/integrations/pushbullet.png' %}" alt="Pushbullet" />
|
||||||
|
Connect Pushbullet
|
||||||
|
</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block scripts %}
|
||||||
|
{% compress js %}
|
||||||
|
<script src="{% static 'js/jquery-2.1.4.min.js' %}"></script>
|
||||||
|
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
||||||
|
{% endcompress %}
|
||||||
|
{% endblock %}
|
7
templates/integrations/pushbullet_message.html
Normal file
7
templates/integrations/pushbullet_message.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{% load humanize %}
|
||||||
|
|
||||||
|
{% if check.status == "down" %}
|
||||||
|
The check "{{ check.name_then_code }}" is DOWN. Last ping was {{ check.last_ping|naturaltime }}
|
||||||
|
{% else %}
|
||||||
|
The check "{{ check.name_then_code }}" received a ping and is now UP.
|
||||||
|
{% endif %}
|
Loading…
x
Reference in New Issue
Block a user