forked from GithubBackups/healthchecks
Remove the Zendesk integration (unfinished, could not get it to work acceptably)
This commit is contained in:
parent
19ef8b3f7b
commit
b9a24a21e7
@ -309,8 +309,6 @@ class Channel(models.Model):
|
|||||||
return transports.Telegram(self)
|
return transports.Telegram(self)
|
||||||
elif self.kind == "sms":
|
elif self.kind == "sms":
|
||||||
return transports.Sms(self)
|
return transports.Sms(self)
|
||||||
elif self.kind == "zendesk":
|
|
||||||
return transports.Zendesk(self)
|
|
||||||
elif self.kind == "trello":
|
elif self.kind == "trello":
|
||||||
return transports.Trello(self)
|
return transports.Trello(self)
|
||||||
else:
|
else:
|
||||||
@ -488,18 +486,6 @@ class Channel(models.Model):
|
|||||||
doc = json.loads(self.value)
|
doc = json.loads(self.value)
|
||||||
return doc["account"]
|
return doc["account"]
|
||||||
|
|
||||||
@property
|
|
||||||
def zendesk_token(self):
|
|
||||||
assert self.kind == "zendesk"
|
|
||||||
doc = json.loads(self.value)
|
|
||||||
return doc["access_token"]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def zendesk_subdomain(self):
|
|
||||||
assert self.kind == "zendesk"
|
|
||||||
doc = json.loads(self.value)
|
|
||||||
return doc["subdomain"]
|
|
||||||
|
|
||||||
def latest_notification(self):
|
def latest_notification(self):
|
||||||
return Notification.objects.filter(channel=self).latest()
|
return Notification.objects.filter(channel=self).latest()
|
||||||
|
|
||||||
|
@ -534,67 +534,3 @@ class NotifyTestCase(BaseTestCase):
|
|||||||
|
|
||||||
self.channel.notify(self.check)
|
self.channel.notify(self.check)
|
||||||
self.assertTrue(mock_post.called)
|
self.assertTrue(mock_post.called)
|
||||||
|
|
||||||
@patch("hc.api.transports.requests.request")
|
|
||||||
def test_zendesk_down(self, mock_post):
|
|
||||||
v = json.dumps({"access_token": "fake-token", "subdomain": "foo"})
|
|
||||||
self._setup_data("zendesk", v)
|
|
||||||
mock_post.return_value.status_code = 200
|
|
||||||
|
|
||||||
self.channel.notify(self.check)
|
|
||||||
assert Notification.objects.count() == 1
|
|
||||||
|
|
||||||
args, kwargs = mock_post.call_args
|
|
||||||
method, url = args
|
|
||||||
self.assertEqual(method, "post")
|
|
||||||
self.assertTrue("foo.zendesk.com" in url)
|
|
||||||
|
|
||||||
payload = kwargs["json"]
|
|
||||||
self.assertEqual(payload["request"]["type"], "incident")
|
|
||||||
self.assertTrue("down" in payload["request"]["subject"])
|
|
||||||
|
|
||||||
headers = kwargs["headers"]
|
|
||||||
self.assertEqual(headers["Authorization"], "Bearer fake-token")
|
|
||||||
|
|
||||||
@patch("hc.api.transports.requests.request")
|
|
||||||
@patch("hc.api.transports.requests.get")
|
|
||||||
def test_zendesk_up(self, mock_get, mock_post):
|
|
||||||
v = json.dumps({"access_token": "fake-token", "subdomain": "foo"})
|
|
||||||
self._setup_data("zendesk", v, status="up")
|
|
||||||
|
|
||||||
mock_post.return_value.status_code = 200
|
|
||||||
mock_get.return_value.status_code = 200
|
|
||||||
mock_get.return_value.json.return_value = {
|
|
||||||
"requests": [{
|
|
||||||
"url": "https://foo.example.org/comment",
|
|
||||||
"description": "code is %s" % self.check.code
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
self.channel.notify(self.check)
|
|
||||||
assert Notification.objects.count() == 1
|
|
||||||
|
|
||||||
args, kwargs = mock_post.call_args
|
|
||||||
self.assertTrue("foo.example.org" in args[1])
|
|
||||||
|
|
||||||
payload = kwargs["json"]
|
|
||||||
self.assertEqual(payload["request"]["type"], "incident")
|
|
||||||
self.assertTrue("UP" in payload["request"]["subject"])
|
|
||||||
|
|
||||||
headers = kwargs["headers"]
|
|
||||||
self.assertEqual(headers["Authorization"], "Bearer fake-token")
|
|
||||||
|
|
||||||
@patch("hc.api.transports.requests.request")
|
|
||||||
@patch("hc.api.transports.requests.get")
|
|
||||||
def test_zendesk_up_with_no_existing_ticket(self, mock_get, mock_post):
|
|
||||||
v = json.dumps({"access_token": "fake-token", "subdomain": "foo"})
|
|
||||||
self._setup_data("zendesk", v, status="up")
|
|
||||||
|
|
||||||
mock_get.return_value.status_code = 200
|
|
||||||
mock_get.return_value.json.return_value = {"requests": []}
|
|
||||||
|
|
||||||
self.channel.notify(self.check)
|
|
||||||
n = Notification.objects.get()
|
|
||||||
self.assertEqual(n.error, "Could not find a ticket to update")
|
|
||||||
|
|
||||||
self.assertFalse(mock_post.called)
|
|
||||||
|
@ -392,51 +392,6 @@ class Sms(HttpTransport):
|
|||||||
return self.post(url, data=data, auth=auth)
|
return self.post(url, data=data, auth=auth)
|
||||||
|
|
||||||
|
|
||||||
class Zendesk(HttpTransport):
|
|
||||||
TMPL = "https://%s.zendesk.com/api/v2/requests.json"
|
|
||||||
|
|
||||||
def get_payload(self, check):
|
|
||||||
return {
|
|
||||||
"request": {
|
|
||||||
"subject": tmpl("zendesk_title.html", check=check),
|
|
||||||
"type": "incident",
|
|
||||||
"comment": {
|
|
||||||
"body": tmpl("zendesk_description.html", check=check)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def notify_down(self, check):
|
|
||||||
headers = {"Authorization": "Bearer %s" % self.channel.zendesk_token}
|
|
||||||
url = self.TMPL % self.channel.zendesk_subdomain
|
|
||||||
return self.post(url, headers=headers, json=self.get_payload(check))
|
|
||||||
|
|
||||||
def notify_up(self, check):
|
|
||||||
# Get the list of requests made by us, in newest-to-oldest order
|
|
||||||
url = self.TMPL % self.channel.zendesk_subdomain
|
|
||||||
url += "?sort_by=created_at&sort_order=desc"
|
|
||||||
headers = {"Authorization": "Bearer %s" % self.channel.zendesk_token}
|
|
||||||
r = requests.get(url, headers=headers, timeout=10)
|
|
||||||
if r.status_code != 200:
|
|
||||||
return "Received status code %d" % r.status_code
|
|
||||||
|
|
||||||
# Update the first request that has check.code in its description
|
|
||||||
doc = r.json()
|
|
||||||
if "requests" in doc:
|
|
||||||
for obj in doc["requests"]:
|
|
||||||
if str(check.code) in obj["description"]:
|
|
||||||
payload = self.get_payload(check)
|
|
||||||
return self.put(obj["url"], headers=headers, json=payload)
|
|
||||||
|
|
||||||
return "Could not find a ticket to update"
|
|
||||||
|
|
||||||
def notify(self, check):
|
|
||||||
if check.status == "down":
|
|
||||||
return self.notify_down(check)
|
|
||||||
if check.status == "up":
|
|
||||||
return self.notify_up(check)
|
|
||||||
|
|
||||||
|
|
||||||
class Trello(HttpTransport):
|
class Trello(HttpTransport):
|
||||||
URL = 'https://api.trello.com/1/cards'
|
URL = 'https://api.trello.com/1/cards'
|
||||||
|
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
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(ZENDESK_CLIENT_ID="t1", ZENDESK_CLIENT_SECRET="s1")
|
|
||||||
class AddZendeskTestCase(BaseTestCase):
|
|
||||||
url = "/integrations/add_zendesk/"
|
|
||||||
|
|
||||||
def test_instructions_work(self):
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
r = self.client.get(self.url)
|
|
||||||
self.assertContains(r, "Connect Zendesk Support", status_code=200)
|
|
||||||
|
|
||||||
def test_post_works(self):
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
r = self.client.post(self.url, {"subdomain": "foo"})
|
|
||||||
self.assertEqual(r.status_code, 302)
|
|
||||||
self.assertTrue("foo.zendesk.com" in r["Location"])
|
|
||||||
|
|
||||||
# There should now be a key in session
|
|
||||||
self.assertTrue("zendesk" in self.client.session)
|
|
||||||
|
|
||||||
@override_settings(ZENDESK_CLIENT_ID=None)
|
|
||||||
def test_it_requires_client_id(self):
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
r = self.client.get(self.url)
|
|
||||||
self.assertEqual(r.status_code, 404)
|
|
||||||
|
|
||||||
@patch("hc.front.views.requests.post")
|
|
||||||
def test_it_handles_oauth_response(self, mock_post):
|
|
||||||
session = self.client.session
|
|
||||||
session["zendesk"] = "foo"
|
|
||||||
session["subdomain"] = "foodomain"
|
|
||||||
session.save()
|
|
||||||
|
|
||||||
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 = self.url + "?code=12345678&state=foo"
|
|
||||||
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
r = self.client.get(url, follow=True)
|
|
||||||
self.assertRedirects(r, "/integrations/")
|
|
||||||
self.assertContains(r, "The Zendesk integration has been added!")
|
|
||||||
|
|
||||||
ch = Channel.objects.get()
|
|
||||||
self.assertEqual(ch.zendesk_token, "test-token")
|
|
||||||
self.assertEqual(ch.zendesk_subdomain, "foodomain")
|
|
||||||
|
|
||||||
# Session should now be clean
|
|
||||||
self.assertFalse("zendesk" in self.client.session)
|
|
||||||
self.assertFalse("subdomain" in self.client.session)
|
|
||||||
|
|
||||||
def test_it_avoids_csrf(self):
|
|
||||||
session = self.client.session
|
|
||||||
session["zendesk"] = "foo"
|
|
||||||
session.save()
|
|
||||||
|
|
||||||
url = self.url + "?code=12345678&state=bar"
|
|
||||||
|
|
||||||
self.client.login(username="alice@example.org", password="password")
|
|
||||||
r = self.client.get(url)
|
|
||||||
self.assertEqual(r.status_code, 400)
|
|
@ -34,7 +34,6 @@ channel_urls = [
|
|||||||
path('telegram/bot/', views.telegram_bot, name="hc-telegram-webhook"),
|
path('telegram/bot/', views.telegram_bot, name="hc-telegram-webhook"),
|
||||||
path('add_telegram/', views.add_telegram, name="hc-add-telegram"),
|
path('add_telegram/', views.add_telegram, name="hc-add-telegram"),
|
||||||
path('add_sms/', views.add_sms, name="hc-add-sms"),
|
path('add_sms/', views.add_sms, name="hc-add-sms"),
|
||||||
path('add_zendesk/', views.add_zendesk, name="hc-add-zendesk"),
|
|
||||||
path('add_trello/', views.add_trello, name="hc-add-trello"),
|
path('add_trello/', views.add_trello, name="hc-add-trello"),
|
||||||
path('add_trello/settings/', views.trello_settings, name="hc-trello-settings"),
|
path('add_trello/settings/', views.trello_settings, name="hc-trello-settings"),
|
||||||
path('<uuid:code>/checks/', views.channel_checks, name="hc-channel-checks"),
|
path('<uuid:code>/checks/', views.channel_checks, name="hc-channel-checks"),
|
||||||
|
@ -481,7 +481,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,
|
||||||
"enable_zendesk": settings.ZENDESK_CLIENT_ID is not None,
|
|
||||||
"enable_trello": settings.TRELLO_APP_KEY is not None,
|
"enable_trello": settings.TRELLO_APP_KEY is not None,
|
||||||
"use_payments": settings.USE_PAYMENTS
|
"use_payments": settings.USE_PAYMENTS
|
||||||
}
|
}
|
||||||
@ -1049,64 +1048,6 @@ def add_sms(request):
|
|||||||
return render(request, "integrations/add_sms.html", ctx)
|
return render(request, "integrations/add_sms.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def add_zendesk(request):
|
|
||||||
if settings.ZENDESK_CLIENT_ID is None:
|
|
||||||
raise Http404("zendesk integration is not available")
|
|
||||||
|
|
||||||
if request.method == "POST":
|
|
||||||
domain = request.POST.get("subdomain")
|
|
||||||
request.session["subdomain"] = domain
|
|
||||||
redirect_uri = settings.SITE_ROOT + reverse("hc-add-zendesk")
|
|
||||||
auth_url = "https://%s.zendesk.com/oauth/authorizations/new?" % domain
|
|
||||||
auth_url += urlencode({
|
|
||||||
"client_id": settings.ZENDESK_CLIENT_ID,
|
|
||||||
"redirect_uri": redirect_uri,
|
|
||||||
"response_type": "code",
|
|
||||||
"scope": "requests:read requests:write",
|
|
||||||
"state": _prepare_state(request, "zendesk")
|
|
||||||
})
|
|
||||||
|
|
||||||
return redirect(auth_url)
|
|
||||||
|
|
||||||
if "code" in request.GET:
|
|
||||||
code = _get_validated_code(request, "zendesk")
|
|
||||||
if code is None:
|
|
||||||
return HttpResponseBadRequest()
|
|
||||||
|
|
||||||
domain = request.session.pop("subdomain")
|
|
||||||
url = "https://%s.zendesk.com/oauth/tokens" % domain
|
|
||||||
|
|
||||||
redirect_uri = settings.SITE_ROOT + reverse("hc-add-zendesk")
|
|
||||||
result = requests.post(url, {
|
|
||||||
"client_id": settings.ZENDESK_CLIENT_ID,
|
|
||||||
"client_secret": settings.ZENDESK_CLIENT_SECRET,
|
|
||||||
"code": code,
|
|
||||||
"grant_type": "authorization_code",
|
|
||||||
"redirect_uri": redirect_uri,
|
|
||||||
"scope": "read"
|
|
||||||
})
|
|
||||||
|
|
||||||
doc = result.json()
|
|
||||||
if "access_token" in doc:
|
|
||||||
doc["subdomain"] = domain
|
|
||||||
|
|
||||||
channel = Channel(kind="zendesk")
|
|
||||||
channel.user = request.team.user
|
|
||||||
channel.value = json.dumps(doc)
|
|
||||||
channel.save()
|
|
||||||
channel.assign_all_checks()
|
|
||||||
messages.success(request,
|
|
||||||
"The Zendesk integration has been added!")
|
|
||||||
else:
|
|
||||||
messages.warning(request, "Something went wrong")
|
|
||||||
|
|
||||||
return redirect("hc-channels")
|
|
||||||
|
|
||||||
ctx = {"page": "channels"}
|
|
||||||
return render(request, "integrations/add_zendesk.html", ctx)
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def add_trello(request):
|
def add_trello(request):
|
||||||
if settings.TRELLO_APP_KEY is None:
|
if settings.TRELLO_APP_KEY is None:
|
||||||
|
@ -184,10 +184,6 @@ TWILIO_FROM = os.getenv("TWILIO_FROM")
|
|||||||
# PagerDuty
|
# PagerDuty
|
||||||
PD_VENDOR_KEY = os.getenv("PD_VENDOR_KEY")
|
PD_VENDOR_KEY = os.getenv("PD_VENDOR_KEY")
|
||||||
|
|
||||||
# Zendesk
|
|
||||||
ZENDESK_CLIENT_ID = os.getenv("ZENDESK_CLIENT_ID")
|
|
||||||
ZENDESK_CLIENT_SECRET = os.getenv("ZENDESK_CLIENT_ID")
|
|
||||||
|
|
||||||
# Trello
|
# Trello
|
||||||
TRELLO_APP_KEY = os.getenv("TRELLO_APP_KEY")
|
TRELLO_APP_KEY = os.getenv("TRELLO_APP_KEY")
|
||||||
|
|
||||||
|
@ -247,12 +247,3 @@ table.channels-table > tbody > tr > th {
|
|||||||
.webhook-header {
|
.webhook-header {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Add Zendesk */
|
|
||||||
.zendesk-subdomain {
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zendesk-subdomain input {
|
|
||||||
border-right: 0;
|
|
||||||
}
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 2.9 KiB |
@ -270,17 +270,6 @@
|
|||||||
|
|
||||||
<a href="{% url 'hc-add-opsgenie' %}" class="btn btn-primary">Add Integration</a>
|
<a href="{% url 'hc-add-opsgenie' %}" class="btn btn-primary">Add Integration</a>
|
||||||
</li>
|
</li>
|
||||||
{% if enable_zendesk %}
|
|
||||||
<li>
|
|
||||||
<img src="{% static 'img/integrations/zendesk.png' %}"
|
|
||||||
class="icon" alt="Zendesk icon" />
|
|
||||||
|
|
||||||
<h2>Zendesk Support</h2>
|
|
||||||
<p>Create a Zendesk support ticket when a check goes down.</p>
|
|
||||||
|
|
||||||
<a href="{% url 'hc-add-zendesk' %}" class="btn btn-primary">Add Integration</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{% if enable_trello %}
|
{% if enable_trello %}
|
||||||
<li>
|
<li>
|
||||||
<img src="{% static 'img/integrations/trello.png' %}"
|
<img src="{% static 'img/integrations/trello.png' %}"
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
{% extends "base.html" %}
|
|
||||||
{% load humanize static hc_extras %}
|
|
||||||
|
|
||||||
{% block title %}Add Zendesk - {% site_name %}{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-12">
|
|
||||||
<h1>Zendesk Support</h1>
|
|
||||||
|
|
||||||
<div class="jumbotron">
|
|
||||||
<p>
|
|
||||||
If your team uses <a href="http://zendesk.com/">Zendesk</a>,
|
|
||||||
you can set up {% site_name %} to create Zendesk support tickets
|
|
||||||
when checks go <strong>down</strong>, and comment on them when
|
|
||||||
checks go back <strong>up</strong>.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<form method="post">
|
|
||||||
{% csrf_token %}
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm-offset-3 col-sm-6">
|
|
||||||
<div class="input-group input-group-lg zendesk-subdomain">
|
|
||||||
<input
|
|
||||||
name="subdomain"
|
|
||||||
placeholder="Subdomain"
|
|
||||||
type="text"
|
|
||||||
class="form-control">
|
|
||||||
<span class="input-group-addon">.zendesk.com</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-lg btn-default btn-block">
|
|
||||||
<img class="ai-icon" src="{% static 'img/integrations/zendesk.png' %}" alt="Zendesk" />
|
|
||||||
Connect Zendesk Support
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
Loading…
x
Reference in New Issue
Block a user