diff --git a/hc/api/models.py b/hc/api/models.py index 05ae3ac6..44cc2143 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -311,8 +311,8 @@ class Channel(models.Model): return parts[0] doc = json.loads(self.value) - return doc["url_down"] - + return doc.get("url_down") + @property def url_up(self): @@ -322,7 +322,7 @@ class Channel(models.Model): return parts[1] if len(parts) > 1 else "" doc = json.loads(self.value) - return doc["url_up"] + return doc.get("url_up") @property def post_data(self): @@ -332,16 +332,16 @@ class Channel(models.Model): return parts[2] if len(parts) > 2 else "" doc = json.loads(self.value) - return doc["post_data"] + return doc.get("post_data") @property def headers(self): assert self.kind == "webhook" if not self.value.startswith("{"): - return "" + return {} doc = json.loads(self.value) - return doc["headers"] + return doc.get("headers", {}) @property def slack_team(self): diff --git a/hc/api/tests/test_notify.py b/hc/api/tests/test_notify.py index 10265e25..2438c941 100644 --- a/hc/api/tests/test_notify.py +++ b/hc/api/tests/test_notify.py @@ -147,43 +147,73 @@ class NotifyTestCase(BaseTestCase): @patch("hc.api.transports.requests.request") def test_webhooks_handle_json_value(self, mock_request): - self._setup_data("webhook", '{"url_down": "http://foo.com", ' - '"url_up": "", "post_data": "", "headers": ""}') + definition = {"url_down": "http://foo.com"} + self._setup_data("webhook", json.dumps(definition)) self.channel.notify(self.check) - headers = { - "User-Agent": "healthchecks.io" - } + headers = {"User-Agent": "healthchecks.io"} mock_request.assert_called_with( - "get", "http://foo.com", headers=headers, - timeout=5) + "get", "http://foo.com", headers=headers, timeout=5) @patch("hc.api.transports.requests.request") def test_webhooks_handle_json_up_event(self, mock_request): - self._setup_data("webhook", '{"url_down": "", ' - '"url_up": "http://bar", "post_data": "", "headers": ""}', status="up") + definition = {"url_up": "http://bar"} + + self._setup_data("webhook", json.dumps(definition), status="up") + self.channel.notify(self.check) + + headers = {"User-Agent": "healthchecks.io"} + mock_request.assert_called_with( + "get", "http://bar", headers=headers, timeout=5) + + @patch("hc.api.transports.requests.request") + def test_webhooks_handle_post_headers(self, mock_request): + definition = { + "url_down": "http://foo.com", + "post_data": "data", + "headers": {"Content-Type": "application/json"} + } + + self._setup_data("webhook", json.dumps(definition)) self.channel.notify(self.check) headers = { - "User-Agent": "healthchecks.io" + "User-Agent": "healthchecks.io", + "Content-Type": "application/json" } mock_request.assert_called_with( - "get", "http://bar", headers=headers, - timeout=5) + "post", "http://foo.com", data=b"data", headers=headers, timeout=5) @patch("hc.api.transports.requests.request") - def test_webhooks_handle_headers(self, mock_request): - self._setup_data("webhook", '{"url_down": "http://foo.com", ' - '"url_up": "", "post_data": "data", ' - '"headers": {"Content-Type": "application/json"}}') + def test_webhooks_handle_get_headers(self, mock_request): + definition = { + "url_down": "http://foo.com", + "headers": {"Content-Type": "application/json"} + } + + self._setup_data("webhook", json.dumps(definition)) self.channel.notify(self.check) headers = { - "User-Agent": "healthchecks.io", + "User-Agent": "healthchecks.io", "Content-Type": "application/json" } mock_request.assert_called_with( - "post", "http://foo.com", data=b"data", headers=headers, timeout=5) + "get", "http://foo.com", headers=headers, timeout=5) + + @patch("hc.api.transports.requests.request") + def test_webhooks_allow_user_agent_override(self, mock_request): + definition = { + "url_down": "http://foo.com", + "headers": {"User-Agent": "My-Agent"} + } + + self._setup_data("webhook", json.dumps(definition)) + self.channel.notify(self.check) + + headers = {"User-Agent": "My-Agent"} + mock_request.assert_called_with( + "get", "http://foo.com", headers=headers, timeout=5) def test_email(self): self._setup_data("email", "alice@example.org") diff --git a/hc/api/transports.py b/hc/api/transports.py index 325a0899..6d1a0559 100644 --- a/hc/api/transports.py +++ b/hc/api/transports.py @@ -80,11 +80,11 @@ class HttpTransport(Transport): def _request(cls, method, url, **kwargs): try: options = dict(kwargs) + options["timeout"] = 5 if "headers" not in options: options["headers"] = {} - - options["timeout"] = 5 - options["headers"]["User-Agent"] = "healthchecks.io" + if "User-Agent" not in options["headers"]: + options["headers"]["User-Agent"] = "healthchecks.io" r = requests.request(method, url, **options) if r.status_code not in (200, 201, 204): @@ -96,10 +96,10 @@ class HttpTransport(Transport): return "Connection failed" @classmethod - def get(cls, url): + def get(cls, url, **kwargs): # Make 3 attempts-- for x in range(0, 3): - error = cls._request("get", url) + error = cls._request("get", url, **kwargs) if error is None: break @@ -166,14 +166,12 @@ class Webhook(HttpTransport): assert url url = self.prepare(url, check, urlencode=True) + headers = self.channel.headers if self.channel.post_data: payload = self.prepare(self.channel.post_data, check) - headers = {} - if self.channel.headers: - headers = self.channel.headers return self.post(url, data=payload.encode("utf-8"), headers=headers) else: - return self.get(url) + return self.get(url, headers=headers) class Slack(HttpTransport):