From 4e51a86ea4f845b0eef83687fdf4eafea8d372f5 Mon Sep 17 00:00:00 2001 From: Tomasz Kolek Date: Wed, 25 May 2016 20:42:28 +0200 Subject: [PATCH] Add updownio integration. --- static/images/integrations/logos/updown.png | Bin 0 -> 3835 bytes static/images/integrations/updown/001.png | Bin 0 -> 23315 bytes templates/zerver/integrations.html | 32 ++++++ .../updown/updown_check_down_one_event.json | 27 +++++ .../updown/updown_check_multiple_events.json | 56 +++++++++++ .../updown_check_up_again_one_event.json | 27 +++++ .../updown/updown_check_up_first_time.json | 27 +++++ zerver/lib/test_helpers.py | 3 + zerver/tests/test_hooks.py | 36 +++++++ zerver/views/webhooks/updown.py | 94 ++++++++++++++++++ zproject/urls.py | 1 + 11 files changed, 303 insertions(+) create mode 100644 static/images/integrations/logos/updown.png create mode 100644 static/images/integrations/updown/001.png create mode 100644 zerver/fixtures/updown/updown_check_down_one_event.json create mode 100644 zerver/fixtures/updown/updown_check_multiple_events.json create mode 100644 zerver/fixtures/updown/updown_check_up_again_one_event.json create mode 100644 zerver/fixtures/updown/updown_check_up_first_time.json create mode 100644 zerver/views/webhooks/updown.py diff --git a/static/images/integrations/logos/updown.png b/static/images/integrations/logos/updown.png new file mode 100644 index 0000000000000000000000000000000000000000..72a999a0a25d66ae7eb896673dabe528b2fcbbcd GIT binary patch literal 3835 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 z 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 literal 0 HcmV?d00001 diff --git a/templates/zerver/integrations.html b/templates/zerver/integrations.html index 1b5bf69ee5..d176c500b8 100644 --- a/templates/zerver/integrations.html +++ b/templates/zerver/integrations.html @@ -280,6 +280,12 @@ Twitter + +
+ + +

See Updown reports in Zulip! This is great to be up to date with + downtime in the services you monitor with Updown! +

+ +

First, create the stream you'd like to use for updown + notifications, and subscribe all interested parties to this + stream. We recommend the name updown.

+ +

Go to your Updown + settings page and in WEBHOOKS section, enter + the following as the URL:

+ +

{{ external_api_uri }}/v1/external/updown?api_key=abcdefgh&stream=updown

+ + +

Congratulations! You're done!
+ Now you'll receive Updown notifications for your service in Zulip. +

+ + + +
+

See your Yo App notifications in Zulip!

diff --git a/zerver/fixtures/updown/updown_check_down_one_event.json b/zerver/fixtures/updown/updown_check_down_one_event.json new file mode 100644 index 0000000000..cf823770a1 --- /dev/null +++ b/zerver/fixtures/updown/updown_check_down_one_event.json @@ -0,0 +1,27 @@ +[{ + "event": "check.down", + "check": { + "token": "ngg8", + "url": "https://updown.io", + "alias": "", + "last_status": 500, + "uptime": 100.0, + "down": true, + "down_since": "2016-02-07T13:11:43Z", + "error": "500", + "period": 30, + "apdex_t": 0.25, + "string_match": "", + "enabled": true, + "published": true, + "last_check_at": "2016-02-07T13:12:13Z", + "next_check_at": "2016-02-07T13:12:43Z", + "favicon_url": "https://updown.io/favicon.png" + }, + "downtime": { + "error": "500", + "started_at": "2016-02-07T13:11:43Z", + "ended_at": null, + "duration": null + } +}] diff --git a/zerver/fixtures/updown/updown_check_multiple_events.json b/zerver/fixtures/updown/updown_check_multiple_events.json new file mode 100644 index 0000000000..88ab9260be --- /dev/null +++ b/zerver/fixtures/updown/updown_check_multiple_events.json @@ -0,0 +1,56 @@ +[ + { + "event": "check.down", + "check": { + "token": "ngg8", + "url": "https://updown.io", + "alias": "", + "last_status": 200, + "uptime": 99.954, + "down": false, + "down_since": null, + "error": null, + "period": 30, + "apdex_t": 0.25, + "string_match": "", + "enabled": true, + "published": true, + "last_check_at": "2016-02-07T13:16:07Z", + "next_check_at": "2016-02-07T13:16:37Z", + "favicon_url": "https://updown.io/favicon.png" + }, + "downtime": { + "error": "500", + "started_at": "2016-02-07T13:11:43Z", + "ended_at": null, + "duration": null + } + }, + { + "event": "check.up", + "check": { + "token": "ngg8", + "url": "https://updown.io", + "alias": "", + "last_status": 200, + "uptime": 99.954, + "down": false, + "down_since": null, + "error": null, + "period": 30, + "apdex_t": 0.25, + "string_match": "", + "enabled": true, + "published": true, + "last_check_at": "2016-02-07T13:16:07Z", + "next_check_at": "2016-02-07T13:16:37Z", + "favicon_url": "https://updown2.io/favicon.png" + }, + "downtime": { + "error": "500", + "started_at": "2016-02-07T13:11:43Z", + "ended_at": "2016-02-07T13:11:44Z", + "duration": 1 + } + } +] diff --git a/zerver/fixtures/updown/updown_check_up_again_one_event.json b/zerver/fixtures/updown/updown_check_up_again_one_event.json new file mode 100644 index 0000000000..22aff8ff80 --- /dev/null +++ b/zerver/fixtures/updown/updown_check_up_again_one_event.json @@ -0,0 +1,27 @@ +[{ + "event": "check.up", + "check": { + "token": "ngg8", + "url": "https://updown.io", + "alias": "", + "last_status": 200, + "uptime": 99.954, + "down": false, + "down_since": null, + "error": null, + "period": 30, + "apdex_t": 0.25, + "string_match": "", + "enabled": true, + "published": true, + "last_check_at": "2016-02-07T13:16:07Z", + "next_check_at": "2016-02-07T13:16:37Z", + "favicon_url": "https://updown.io/favicon.png" + }, + "downtime": { + "error": "500", + "started_at": "2016-02-07T13:11:43Z", + "ended_at": "2016-02-07T13:16:07Z", + "duration": 265 + } +}] diff --git a/zerver/fixtures/updown/updown_check_up_first_time.json b/zerver/fixtures/updown/updown_check_up_first_time.json new file mode 100644 index 0000000000..a7a9f97d18 --- /dev/null +++ b/zerver/fixtures/updown/updown_check_up_first_time.json @@ -0,0 +1,27 @@ +[{ + "event": "check.up", + "check": { + "token": "ngg8", + "url": "https://updown.io", + "alias": "", + "last_status": 200, + "uptime": 99.954, + "down": false, + "down_since": null, + "error": null, + "period": 30, + "apdex_t": 0.25, + "string_match": "", + "enabled": true, + "published": true, + "last_check_at": "2016-02-07T13:16:07Z", + "next_check_at": "2016-02-07T13:16:37Z", + "favicon_url": "https://updown.io/favicon.png" + }, + "downtime": { + "error": null, + "started_at": null, + "ended_at": null, + "duration": null + } +}] diff --git a/zerver/lib/test_helpers.py b/zerver/lib/test_helpers.py index 412bee1a75..5cbbe4eb8c 100644 --- a/zerver/lib/test_helpers.py +++ b/zerver/lib/test_helpers.py @@ -416,6 +416,9 @@ class AuthedTestCase(TestCase): # type: () -> Message return Message.objects.latest('id') + def get_second_to_last_message(self): + return Message.objects.all().order_by('-id')[1] + def get_all_templates(): # type: () -> List[str] templates = [] diff --git a/zerver/tests/test_hooks.py b/zerver/tests/test_hooks.py index dae9096391..678de0a881 100644 --- a/zerver/tests/test_hooks.py +++ b/zerver/tests/test_hooks.py @@ -1244,3 +1244,39 @@ class AirbrakeHookTests(WebhookTestCase): expected_subject = u"ZulipIntegrationTest" expected_message = u"[ZeroDivisionError](https://zulip.airbrake.io/projects/125209/groups/1705190192091077626): \"Error message from logger\" occurred." self.send_and_test_stream_message('error_message', expected_subject, expected_message) + +class UpdownHookTests(WebhookTestCase): + STREAM_NAME = 'updown' + URL_TEMPLATE = "/api/v1/external/updown?stream={stream}&api_key={api_key}" + FIXTURE_DIR_NAME = 'updown' + + def test_updown_check_down_event(self): + expected_subject = u"https://updown.io" + expected_message = u"Service is `down`. It returned \"500\" error at 07-02-2016 13:11." + self.send_and_test_stream_message('check_down_one_event', expected_subject, expected_message) + + def test_updown_check_up_again_event(self): + expected_subject = u"https://updown.io" + expected_message = u"Service is `up` again after 4 minutes 25 seconds." + self.send_and_test_stream_message('check_up_again_one_event', expected_subject, expected_message) + + def test_updown_check_up_event(self): + expected_subject = u"https://updown.io" + expected_message = u"Service is `up`." + self.send_and_test_stream_message('check_up_first_time', expected_subject, expected_message) + + def test_updown_check_up_multiple_events(self): + first_message_expected_subject = u"https://updown.io" + first_message_expected_message = u"Service is `up` again after 1 second." + + second_message_expected_subject = u"https://updown.io" + second_message_expected_message = u"Service is `down`. It returned \"500\" error at 07-02-2016 13:11." + + self.send_and_test_stream_message('check_multiple_events') + last_message = self.get_last_message() + self.do_test_subject(last_message, first_message_expected_subject) + self.do_test_message(last_message, first_message_expected_message) + + second_to_last_message = self.get_second_to_last_message() + self.do_test_subject(second_to_last_message, second_message_expected_subject) + self.do_test_message(second_to_last_message, second_message_expected_message) diff --git a/zerver/views/webhooks/updown.py b/zerver/views/webhooks/updown.py new file mode 100644 index 0000000000..003aba0452 --- /dev/null +++ b/zerver/views/webhooks/updown.py @@ -0,0 +1,94 @@ +# Webhooks for external integrations. +from __future__ import absolute_import +import re +from datetime import datetime +from typing import Any, Dict +from django.http import HttpRequest, HttpResponse +from django.utils.translation import ugettext as _ +from zerver.lib.actions import check_send_message +from zerver.lib.response import json_success, json_error +from zerver.lib.request import JsonableError +from zerver.decorator import REQ, has_request_variables, api_key_only_webhook_view +from zerver.models import UserProfile, Client + + +SUBJECT_TEMPLATE = "{service_url}" + + +class UnsupportedUpdownEventType(JsonableError): + pass + +def send_message_for_event(event, user_profile, client, stream): + # type: (Dict[str, Any], UserProfile, Client, str) -> None + try: + event_type = get_event_type(event) + subject = SUBJECT_TEMPLATE.format(service_url=event['check']['url']) + body = EVENT_TYPE_BODY_MAPPER[event_type](event) + except KeyError as e: + return json_error(_("Missing key {} in JSON").format(e.message)) + check_send_message(user_profile, client, 'stream', [stream], subject, body) + +def get_body_for_up_event(event): + # type: (Dict[str, Any]) -> str + body = "Service is `up`" + event_downtime = event['downtime'] + if event_downtime['started_at']: + body = "{} again".format(body) + string_date = get_time_string_based_on_duration(event_downtime['duration']) + if string_date: + body = "{} after {}".format(body, string_date) + return "{}.".format(body) + +def get_time_string_based_on_duration(duration): + # type: (int) -> str + days, reminder = divmod(duration, 86400) + hours, reminder = divmod(reminder, 3600) + minutes, seconds = divmod(reminder, 60) + + string_date = '' + string_date += add_time_part_to_string_date_if_needed(days, 'day') + string_date += add_time_part_to_string_date_if_needed(hours, 'hour') + string_date += add_time_part_to_string_date_if_needed(minutes, 'minute') + string_date += add_time_part_to_string_date_if_needed(seconds, 'second') + return string_date.rstrip() + +def add_time_part_to_string_date_if_needed(value, text_name): + # type: (int, str) -> str + if value == 1: + return "1 {} ".format(text_name) + if value > 1: + return "{} {}s ".format(value, text_name) + return '' + +def get_body_for_down_event(event): + # type: (Dict[str, Any]) -> str + event_downtime = event['downtime'] + started_at = datetime.strptime(event_downtime['started_at'], "%Y-%m-%dT%H:%M:%SZ") + return "Service is `down`. It returned \"{}\" error at {}.".format( + event_downtime['error'], + started_at.strftime("%d-%m-%Y %H:%M") + ) + +@api_key_only_webhook_view('Updown') +@has_request_variables +def api_updown_webhook(request, user_profile, client, + payload=REQ(argument_type='body'), + stream=REQ(default='updown')): + # type: (HttpRequest, UserProfile, Client, List[Dict[str, Any]], str) -> HttpResponse + for event in payload: + send_message_for_event(event, user_profile, client, stream) + return json_success() + +EVENT_TYPE_BODY_MAPPER = { + 'up': get_body_for_up_event, + 'down': get_body_for_down_event +} + +def get_event_type(event): + # type: (Dict[str, Any]) -> str + event_type_match = re.match('check.(.*)', event['event']) + if event_type_match: + event_type = event_type_match.group(1) + if event_type in EVENT_TYPE_BODY_MAPPER: + return event_type + raise UnsupportedUpdownEventType(event['event']) diff --git a/zproject/urls.py b/zproject/urls.py index 3d63ce51dc..c947b9973d 100644 --- a/zproject/urls.py +++ b/zproject/urls.py @@ -163,6 +163,7 @@ urlpatterns += patterns('zerver.views', url(r'^api/v1/external/teamcity$', 'webhooks.teamcity.api_teamcity_webhook'), url(r'^api/v1/external/transifex$', 'webhooks.transifex.api_transifex_webhook'), url(r'^api/v1/external/travis$', 'webhooks.travis.api_travis_webhook'), + url(r'^api/v1/external/updown$', 'webhooks.updown.api_updown_webhook'), url(r'^api/v1/external/yo$', 'webhooks.yo.api_yo_app_webhook'), url(r'^api/v1/external/zendesk$', 'webhooks.zendesk.api_zendesk_webhook'),