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 163bf4d1..00000000 Binary files a/static/img/integrations/zendesk.png and /dev/null differ 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 %}