From b9a24a21e7dd07ac27e755f9a131e51236da77d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C4=93teris=20Caune?= Date: Fri, 30 Nov 2018 22:07:37 +0200 Subject: [PATCH] Remove the Zendesk integration (unfinished, could not get it to work acceptably) --- hc/api/models.py | 14 ----- hc/api/tests/test_notify.py | 64 ---------------------- hc/api/transports.py | 45 ---------------- hc/front/tests/test_add_zendesk.py | 68 ------------------------ hc/front/urls.py | 1 - hc/front/views.py | 59 -------------------- hc/settings.py | 4 -- static/css/channels.css | 9 ---- static/img/integrations/zendesk.png | Bin 2942 -> 0 bytes templates/front/channels.html | 11 ---- templates/integrations/add_zendesk.html | 43 --------------- 11 files changed, 318 deletions(-) delete mode 100644 hc/front/tests/test_add_zendesk.py delete mode 100644 static/img/integrations/zendesk.png delete mode 100644 templates/integrations/add_zendesk.html diff --git a/hc/api/models.py b/hc/api/models.py index f87ac01d..82963536 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -309,8 +309,6 @@ class Channel(models.Model): return transports.Telegram(self) elif self.kind == "sms": return transports.Sms(self) - elif self.kind == "zendesk": - return transports.Zendesk(self) elif self.kind == "trello": return transports.Trello(self) else: @@ -488,18 +486,6 @@ class Channel(models.Model): doc = json.loads(self.value) 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): return Notification.objects.filter(channel=self).latest() diff --git a/hc/api/tests/test_notify.py b/hc/api/tests/test_notify.py index b92ba911..c9bc90fa 100644 --- a/hc/api/tests/test_notify.py +++ b/hc/api/tests/test_notify.py @@ -534,67 +534,3 @@ class NotifyTestCase(BaseTestCase): self.channel.notify(self.check) 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) diff --git a/hc/api/transports.py b/hc/api/transports.py index 6e67546a..95834303 100644 --- a/hc/api/transports.py +++ b/hc/api/transports.py @@ -392,51 +392,6 @@ class Sms(HttpTransport): 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): URL = 'https://api.trello.com/1/cards' diff --git a/hc/front/tests/test_add_zendesk.py b/hc/front/tests/test_add_zendesk.py deleted file mode 100644 index d4dda826..00000000 --- a/hc/front/tests/test_add_zendesk.py +++ /dev/null @@ -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) diff --git a/hc/front/urls.py b/hc/front/urls.py index 09670b1d..6e57b5c7 100644 --- a/hc/front/urls.py +++ b/hc/front/urls.py @@ -34,7 +34,6 @@ channel_urls = [ path('telegram/bot/', views.telegram_bot, name="hc-telegram-webhook"), path('add_telegram/', views.add_telegram, name="hc-add-telegram"), 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/settings/', views.trello_settings, name="hc-trello-settings"), path('/checks/', views.channel_checks, name="hc-channel-checks"), diff --git a/hc/front/views.py b/hc/front/views.py index bc956ba4..2ff7edf5 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -481,7 +481,6 @@ def channels(request): "enable_telegram": settings.TELEGRAM_TOKEN is not None, "enable_sms": settings.TWILIO_AUTH 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, "use_payments": settings.USE_PAYMENTS } @@ -1049,64 +1048,6 @@ def add_sms(request): 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 def add_trello(request): if settings.TRELLO_APP_KEY is None: diff --git a/hc/settings.py b/hc/settings.py index 0950a68f..3e00d328 100644 --- a/hc/settings.py +++ b/hc/settings.py @@ -184,10 +184,6 @@ TWILIO_FROM = os.getenv("TWILIO_FROM") # PagerDuty 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_APP_KEY = os.getenv("TRELLO_APP_KEY") diff --git a/static/css/channels.css b/static/css/channels.css index 0bd53d47..6e7bb82e 100644 --- a/static/css/channels.css +++ b/static/css/channels.css @@ -247,12 +247,3 @@ table.channels-table > tbody > tr > th { .webhook-header { margin-bottom: 4px; } - -/* Add Zendesk */ -.zendesk-subdomain { - margin-bottom: 8px; -} - -.zendesk-subdomain input { - border-right: 0; -} diff --git a/static/img/integrations/zendesk.png b/static/img/integrations/zendesk.png deleted file mode 100644 index 163bf4d1ba58c71927d70f5587a11fbef51689a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2942 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 diff --git a/templates/front/channels.html b/templates/front/channels.html index 8f8e9371..1839c203 100644 --- a/templates/front/channels.html +++ b/templates/front/channels.html @@ -270,17 +270,6 @@ Add Integration - {% if enable_zendesk %} -
  • - Zendesk icon - -

    Zendesk Support

    -

    Create a Zendesk support ticket when a check goes down.

    - - Add Integration -
  • - {% endif %} {% if enable_trello %}
  • -
    -

    Zendesk Support

    - -
    -

    - If your team uses Zendesk, - you can set up {% site_name %} to create Zendesk support tickets - when checks go down, and comment on them when - checks go back up. -

    - -
    - {% csrf_token %} -
    -
    -
    - - .zendesk.com -
    - - -
    -
    -
    -
    -
    - -{% endblock %}