forked from GithubBackups/healthchecks
Use client-side installation flow for installing HipChat integration.
This commit is contained in:
parent
9a9ec547d3
commit
012a86495f
@ -388,8 +388,7 @@ class Channel(models.Model):
|
|||||||
if time.time() < doc.get("expires_at", 0):
|
if time.time() < doc.get("expires_at", 0):
|
||||||
return # Current access token is still valid
|
return # Current access token is still valid
|
||||||
|
|
||||||
endpoints = requests.get(doc["capabilitiesUrl"])
|
url = "https://api.hipchat.com/v2/oauth/token"
|
||||||
url = endpoints.json()["capabilities"]["oauth2Provider"]["tokenUrl"]
|
|
||||||
auth = (doc["oauthId"], doc["oauthSecret"])
|
auth = (doc["oauthId"], doc["oauthSecret"])
|
||||||
r = requests.post(url, auth=auth, data={
|
r = requests.post(url, auth=auth, data={
|
||||||
"grant_type": "client_credentials",
|
"grant_type": "client_credentials",
|
||||||
|
@ -8,28 +8,16 @@ from mock import patch
|
|||||||
class ChannelModelTestCase(BaseTestCase):
|
class ChannelModelTestCase(BaseTestCase):
|
||||||
|
|
||||||
@patch("hc.api.models.requests.post")
|
@patch("hc.api.models.requests.post")
|
||||||
@patch("hc.api.models.requests.get")
|
def test_it_refreshes_hipchat_access_token(self, mock_post):
|
||||||
def test_it_refreshes_hipchat_access_token(self, mock_get, mock_post):
|
|
||||||
mock_get.return_value.json.return_value = {
|
|
||||||
"capabilities": {
|
|
||||||
"oauth2Provider": {"tokenUrl": "http://example.org"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mock_post.return_value.json.return_value = {"expires_in": 100}
|
mock_post.return_value.json.return_value = {"expires_in": 100}
|
||||||
|
|
||||||
channel = Channel(kind="hipchat", user=self.alice, value=json.dumps({
|
channel = Channel(kind="hipchat", user=self.alice, value=json.dumps({
|
||||||
"oauthId": "foo",
|
"oauthId": "foo",
|
||||||
"oauthSecret": "bar",
|
"oauthSecret": "bar"
|
||||||
"capabilitiesUrl": "http://example.org/capabilities.json"
|
|
||||||
}))
|
}))
|
||||||
|
|
||||||
channel.refresh_hipchat_access_token()
|
channel.refresh_hipchat_access_token()
|
||||||
# It should fetch the remote capabilities document
|
|
||||||
mock_get.assert_called()
|
|
||||||
|
|
||||||
# It should request a token using a correct tokenUrl
|
# It should request a token using a correct tokenUrl
|
||||||
mock_post.assert_called()
|
mock_post.assert_called()
|
||||||
args, kwargs = mock_post.call_args
|
|
||||||
self.assertEqual(args[0], "http://example.org")
|
|
||||||
|
|
||||||
self.assertTrue("expires_at" in channel.value)
|
self.assertTrue("expires_at" in channel.value)
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
import json
|
|
||||||
|
|
||||||
from django.core import signing
|
|
||||||
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
|
||||||
@ -14,38 +11,26 @@ class AddHipChatTestCase(BaseTestCase):
|
|||||||
r = self.client.get(self.url)
|
r = self.client.get(self.url)
|
||||||
self.assertContains(r, "appropriate HipChat room")
|
self.assertContains(r, "appropriate HipChat room")
|
||||||
|
|
||||||
def test_instructions_work_when_logged_out(self):
|
|
||||||
r = self.client.get(self.url)
|
|
||||||
self.assertContains(r, "Before adding HipChat integration, please")
|
|
||||||
|
|
||||||
def test_it_redirects_to_addons_install(self):
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
r = self.client.post(self.url)
|
|
||||||
self.assertEqual(r.status_code, 302)
|
|
||||||
|
|
||||||
def test_it_returns_capabilities(self):
|
def test_it_returns_capabilities(self):
|
||||||
r = self.client.get("/integrations/hipchat/capabilities/")
|
r = self.client.get("/integrations/hipchat/capabilities/")
|
||||||
self.assertContains(r, "callbackUrl")
|
self.assertContains(r, "installedUrl")
|
||||||
|
|
||||||
@patch("hc.api.models.Channel.refresh_hipchat_access_token")
|
@patch("hc.front.views.Channel.refresh_hipchat_access_token")
|
||||||
def test_callback_works(self, mock_refresh):
|
@patch("hc.front.views.requests.get")
|
||||||
state = signing.TimestampSigner().sign("alice")
|
def test_it_adds_channel(self, mock_get, mock_refresh):
|
||||||
payload = json.dumps({"relayState": state, "foo": "foobar"})
|
mock_get.return_value.json.return_value = {
|
||||||
|
"oauthId": "test-id"
|
||||||
|
}
|
||||||
|
mock_get.return_value.text = "{}"
|
||||||
|
|
||||||
r = self.client.post("/integrations/hipchat/callback/", payload,
|
self.client.login(username="alice@example.org", password="password")
|
||||||
content_type="application/json")
|
|
||||||
|
|
||||||
self.assertEqual(r.status_code, 200)
|
s = "https://api.hipchat.com/foo"
|
||||||
|
r = self.client.post(self.url + "?installable_url=%s" % s)
|
||||||
|
self.assertEqual(r.status_code, 302)
|
||||||
|
|
||||||
|
self.assertTrue(mock_refresh.called)
|
||||||
|
|
||||||
c = Channel.objects.get()
|
c = Channel.objects.get()
|
||||||
self.assertEqual(c.kind, "hipchat")
|
self.assertEqual(c.kind, "hipchat")
|
||||||
self.assertTrue("foobar" in c.value)
|
self.assertEqual(c.value, "{}")
|
||||||
|
|
||||||
@patch("hc.api.models.Channel.refresh_hipchat_access_token")
|
|
||||||
def test_callback_rejects_bad_signature(self, mock_refresh):
|
|
||||||
payload = json.dumps({"relayState": "alice:bad:sig", "foo": "foobar"})
|
|
||||||
|
|
||||||
r = self.client.post("/integrations/hipchat/callback/", payload,
|
|
||||||
content_type="application/json")
|
|
||||||
|
|
||||||
self.assertEqual(r.status_code, 400)
|
|
||||||
|
@ -47,10 +47,3 @@ class ChannelsTestCase(BaseTestCase):
|
|||||||
self.assertEqual(r.status_code, 200)
|
self.assertEqual(r.status_code, 200)
|
||||||
self.assertContains(r, "fake-key")
|
self.assertContains(r, "fake-key")
|
||||||
self.assertContains(r, "(normal priority)")
|
self.assertContains(r, "(normal priority)")
|
||||||
|
|
||||||
def test_it_shows_added_message(self):
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
r = self.client.get("/integrations/?added=hipchat")
|
|
||||||
|
|
||||||
self.assertEqual(r.status_code, 200)
|
|
||||||
self.assertContains(r, "The HipChat integration has been added!")
|
|
||||||
|
@ -21,7 +21,6 @@ channel_urls = [
|
|||||||
url(r'^add_slack_btn/$', views.add_slack_btn, name="hc-add-slack-btn"),
|
url(r'^add_slack_btn/$', views.add_slack_btn, name="hc-add-slack-btn"),
|
||||||
url(r'^add_hipchat/$', views.add_hipchat, name="hc-add-hipchat"),
|
url(r'^add_hipchat/$', views.add_hipchat, name="hc-add-hipchat"),
|
||||||
url(r'^hipchat/capabilities/$', views.hipchat_capabilities, name="hc-hipchat-capabilities"),
|
url(r'^hipchat/capabilities/$', views.hipchat_capabilities, name="hc-hipchat-capabilities"),
|
||||||
url(r'^hipchat/callback/$', views.hipchat_callback, name="hc-hipchat-callback"),
|
|
||||||
url(r'^add_pushbullet/$', views.add_pushbullet, name="hc-add-pushbullet"),
|
url(r'^add_pushbullet/$', views.add_pushbullet, name="hc-add-pushbullet"),
|
||||||
url(r'^add_discord/$', views.add_discord, name="hc-add-discord"),
|
url(r'^add_discord/$', views.add_discord, name="hc-add-discord"),
|
||||||
url(r'^add_pushover/$', views.add_pushover, name="hc-add-pushover"),
|
url(r'^add_pushover/$', views.add_pushover, name="hc-add-pushover"),
|
||||||
|
@ -353,7 +353,6 @@ def channels(request):
|
|||||||
"enable_telegram": settings.TELEGRAM_TOKEN is not None,
|
"enable_telegram": settings.TELEGRAM_TOKEN is not None,
|
||||||
"enable_sms": settings.TWILIO_AUTH is not None,
|
"enable_sms": settings.TWILIO_AUTH is not None,
|
||||||
"enable_pd": settings.PD_VENDOR_KEY is not None,
|
"enable_pd": settings.PD_VENDOR_KEY is not None,
|
||||||
"added": request.GET.get("added"),
|
|
||||||
"use_payments": settings.USE_PAYMENTS
|
"use_payments": settings.USE_PAYMENTS
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -571,18 +570,34 @@ def add_slack_btn(request):
|
|||||||
return redirect("hc-channels")
|
return redirect("hc-channels")
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
def add_hipchat(request):
|
def add_hipchat(request):
|
||||||
if request.method == "POST":
|
if "installable_url" in request.GET:
|
||||||
username = request.team.user.username
|
url = request.GET["installable_url"]
|
||||||
state = signing.TimestampSigner().sign(username)
|
assert url.startswith("https://api.hipchat.com")
|
||||||
capabilities = settings.SITE_ROOT + reverse("hc-hipchat-capabilities")
|
response = requests.get(url)
|
||||||
|
if "oauthId" not in response.json():
|
||||||
|
messages.warning(request, "Something went wrong!")
|
||||||
|
return redirect("hc-channels")
|
||||||
|
|
||||||
url = "https://www.hipchat.com/addons/install?url=%s&relayState=%s" % \
|
channel = Channel(kind="hipchat")
|
||||||
(capabilities, state)
|
channel.user = request.team.user
|
||||||
|
channel.value = response.text
|
||||||
|
channel.save()
|
||||||
|
|
||||||
return redirect(url)
|
channel.refresh_hipchat_access_token()
|
||||||
|
channel.assign_all_checks()
|
||||||
|
messages.success(request, "The HipChat integration has been added!")
|
||||||
|
return redirect("hc-channels")
|
||||||
|
|
||||||
ctx = {"page": "channels"}
|
install_url = "https://www.hipchat.com/addons/install?" + urlencode({
|
||||||
|
"url": settings.SITE_ROOT + reverse("hc-hipchat-capabilities")
|
||||||
|
})
|
||||||
|
|
||||||
|
ctx = {
|
||||||
|
"page": "channels",
|
||||||
|
"install_url": install_url
|
||||||
|
}
|
||||||
return render(request, "integrations/add_hipchat.html", ctx)
|
return render(request, "integrations/add_hipchat.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@ -591,27 +606,6 @@ def hipchat_capabilities(request):
|
|||||||
content_type="application/json")
|
content_type="application/json")
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
@require_POST
|
|
||||||
def hipchat_callback(request):
|
|
||||||
doc = json.loads(request.body.decode("utf-8"))
|
|
||||||
try:
|
|
||||||
signer = signing.TimestampSigner()
|
|
||||||
username = signer.unsign(doc.get("relayState"), max_age=300)
|
|
||||||
except signing.BadSignature:
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
|
|
||||||
channel = Channel(kind="hipchat")
|
|
||||||
channel.user = User.objects.get(username=username)
|
|
||||||
channel.value = json.dumps(doc)
|
|
||||||
channel.save()
|
|
||||||
|
|
||||||
channel.refresh_hipchat_access_token()
|
|
||||||
channel.assign_all_checks()
|
|
||||||
|
|
||||||
return HttpResponse()
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def add_pushbullet(request):
|
def add_pushbullet(request):
|
||||||
if settings.PUSHBULLET_CLIENT_ID is None:
|
if settings.PUSHBULLET_CLIENT_ID is None:
|
||||||
|
@ -6,16 +6,11 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% if messages or added %}
|
{% if messages %}
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
<p class="alert alert-{{ message.tags }}">{{ message }}</p>
|
<p class="alert alert-{{ message.tags }}">{{ message }}</p>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% if added == "hipchat" %}
|
|
||||||
<p class="alert alert-info">
|
|
||||||
The HipChat integration has been added!
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
@ -10,51 +10,17 @@
|
|||||||
<h1>HipChat</h1>
|
<h1>HipChat</h1>
|
||||||
|
|
||||||
<div class="jumbotron">
|
<div class="jumbotron">
|
||||||
{% if request.user.is_authenticated %}
|
|
||||||
<p>If your team uses <a href="https://www.hipchat.com/">HipChat</a>,
|
<p>If your team uses <a href="https://www.hipchat.com/">HipChat</a>,
|
||||||
you can set up {% site_name %} to post status updates directly to an
|
you can set up {% site_name %} to post status updates directly to an
|
||||||
appropriate HipChat room.</p>
|
appropriate HipChat room.</p>
|
||||||
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<form method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<button type="submit" class="btn btn-lg btn-primary">
|
|
||||||
Install HipChat Integration
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% 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 HipChat integration, please log into
|
|
||||||
{% site_name %}:</p>
|
|
||||||
|
|
||||||
<div class="text-center">
|
|
||||||
<form class="form-inline" action="{% url 'hc-login' %}" method="post">
|
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
<a href="{{ install_url }}" class="btn btn-default">
|
||||||
<div class="form-group">
|
<img class="ai-icon" src="{% static 'img/integrations/hipchat.png' %}" alt="HipChat" />
|
||||||
<div class="input-group input-group-lg">
|
Connect HipChat
|
||||||
<div class="input-group-addon">@</div>
|
</a>
|
||||||
<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>
|
</div>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h2>Setup Guide</h2>
|
<h2>Setup Guide</h2>
|
||||||
@ -63,8 +29,8 @@
|
|||||||
<div class="col-sm-6">
|
<div class="col-sm-6">
|
||||||
<span class="step-no">1</span>
|
<span class="step-no">1</span>
|
||||||
<p>
|
<p>
|
||||||
After {% if request.user.is_authenticated %}{% else %}logging in and{% endif %}
|
After
|
||||||
clicking on "Install HipChat Integration", you will be
|
clicking on "Connect HipChat", you will be
|
||||||
asked to log into HipChat.
|
asked to log into HipChat.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@ -125,7 +91,6 @@
|
|||||||
src="{% static 'img/integrations/setup_hipchat_4.png' %}">
|
src="{% static 'img/integrations/setup_hipchat_4.png' %}">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -11,8 +11,7 @@
|
|||||||
"installable": {
|
"installable": {
|
||||||
"allowGlobal": false,
|
"allowGlobal": false,
|
||||||
"allowRoom": true,
|
"allowRoom": true,
|
||||||
"callbackUrl": "{% site_root %}{% url 'hc-hipchat-callback'%}",
|
"installedUrl": "{% site_root %}{% url 'hc-add-hipchat'%}"
|
||||||
"installedUrl": "{% site_root %}{% url 'hc-channels'%}?added=hipchat"
|
|
||||||
},
|
},
|
||||||
"hipchatApiConsumer": {
|
"hipchatApiConsumer": {
|
||||||
"avatar": "{% site_root %}{% static 'img/logo-512-green.png' %}",
|
"avatar": "{% site_root %}{% static 'img/logo-512-green.png' %}",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user