From 2bd9c8cb42a4d7d77df6a9fdba039db5a052023b Mon Sep 17 00:00:00 2001 From: "Hemanth V. Alluri" Date: Thu, 16 May 2019 12:53:15 +0530 Subject: [PATCH] devtools: Add custom HTTP headers support to the integrations dev panel. This commit introduces a simple field where the user can now specify custom HTTP headers. This commit does not introduce an improved system for storing HTTP headers as fixtures - such a change would modify both the existing unit tests as well as this devtool. --- .../integrations/integrations_dev_panel.png | Bin 20963 -> 31174 bytes static/js/integrations_dev_panel.js | 21 ++++++++++--- static/styles/integrations_dev_panel.css | 5 ++++ .../api/incoming-webhooks-walkthrough.md | 12 ++++---- .../integrations/development/dev_panel.html | 9 ++++++ zerver/tests/test_integrations_dev_panel.py | 28 ++++++++++++++++-- zerver/views/development/integrations.py | 14 +++++++-- 7 files changed, 76 insertions(+), 13 deletions(-) diff --git a/static/images/integrations/integrations_dev_panel.png b/static/images/integrations/integrations_dev_panel.png index 462757ae5ef6fe0f7f289b96d6aa6d214146ba03..99d2878dc4e275612ee3cf5c96f9bface2ccd4bd 100644 GIT binary patch literal 31174 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 literal 20963 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 gpZ}KWb)ylv1p^F`;{X5v diff --git a/static/js/integrations_dev_panel.js b/static/js/integrations_dev_panel.js index 10014753b9..f8220fa0d9 100644 --- a/static/js/integrations_dev_panel.js +++ b/static/js/integrations_dev_panel.js @@ -17,6 +17,7 @@ var clear_handlers = { integration_name: function () { $('#integration_name').children()[0].selected = true; }, fixture_name: function () { $('#fixture_name').empty(); }, fixture_body: function () { $("#fixture_body")[0].value = ""; }, + custom_http_headers: function () { $("#custom_http_headers")[0].value = ""; }, }; function clear_elements(elements) { @@ -141,7 +142,7 @@ function get_fixtures(integration_name) { /* Request fixtures from the backend for any integrations that we don't already have fixtures for (which would be stored in the JS variable called "loaded_fixtures"). */ if (integration_name === "") { - clear_elements(["fixture_body", "fixture_name", "URL", "message"]); + clear_elements(["custom_http_headers", "fixture_body", "fixture_name", "URL", "message"]); return; } @@ -190,9 +191,21 @@ function send_webhook_fixture_message() { return; } + var custom_headers = $("#custom_http_headers").val(); + if (custom_headers !== "") { + // JSON.parse("") would trigger an error, as empty strings do not qualify as JSON. + try { + // Let JavaScript validate the JSON for us. + custom_headers = JSON.stringify(JSON.parse(custom_headers)); + } catch (err) { + set_message("Custom HTTP headers are not in a valid JSON format.", "warning"); + return; + } + } + channel.post({ url: "/devtools/integrations/check_send_webhook_fixture_message", - data: {url: url, body: body}, + data: {url: url, body: body, custom_headers: custom_headers}, beforeSend: function (xhr) {xhr.setRequestHeader('X-CSRFToken', csrftoken);}, success: function () { // If the previous fixture body was sent successfully, then we should change the success @@ -214,7 +227,7 @@ function send_webhook_fixture_message() { // Initialization $(function () { clear_elements(["stream_name", "topic_name", "URL", "bot_name", "integration_name", - "fixture_name", "fixture_body", "message"]); + "fixture_name", "custom_http_headers", "fixture_body", "message"]); var potential_default_bot = $("#bot_name")[0][1]; if (potential_default_bot !== undefined) { @@ -222,7 +235,7 @@ $(function () { } $('#integration_name').change(function () { - clear_elements(["fixture_body", "fixture_name", "message"]); + clear_elements(["custom_http_headers", "fixture_body", "fixture_name", "message"]); var integration_name = $(this).children("option:selected").val(); get_fixtures(integration_name); update_url(); diff --git a/static/styles/integrations_dev_panel.css b/static/styles/integrations_dev_panel.css index e38f68a074..bb36c4cd96 100644 --- a/static/styles/integrations_dev_panel.css +++ b/static/styles/integrations_dev_panel.css @@ -7,6 +7,11 @@ padding: 10px; } +#custom_http_headers { + height: 200px; + width: 700px; +} + #fixture_body { height: 500px; width: 700px; diff --git a/templates/zerver/api/incoming-webhooks-walkthrough.md b/templates/zerver/api/incoming-webhooks-walkthrough.md index f6ff4b1d31..b62b8bc604 100644 --- a/templates/zerver/api/incoming-webhooks-walkthrough.md +++ b/templates/zerver/api/incoming-webhooks-walkthrough.md @@ -237,17 +237,19 @@ This is the GUI tool. 1. Run `./tools/run-dev.py` then go to http://localhost:9991/devtools/integrations/. -2. Set the following mandatory fields: -- **Bot** - Any incoming webhook bot. -- **Integration** - One of the integrations. -- **Fixture** - Though not mandatory, it's recommended that you select one and then tweak it if necessary. +2. Set the following mandatory fields: +**Bot** - Any incoming webhook bot. +**Integration** - One of the integrations. +**Fixture** - Though not mandatory, it's recommended that you select one and then tweak it if necessary. The remaining fields are optional, and the URL will automatically be generated. 3. Click **Send**! -By opening Zulip in one tab and this tool in another, you can quickly tweak +By opening Zulip in one tab and then this tool in another, you can quickly tweak your code and send sample messages for many different test fixtures. +Note: Custom HTTP Headers must be entered as a JSON dictionary, if you want to use any in the first place that is. +Feel free to use 4-spaces as tabs for indentation if you'd like! Your sample notification may look like: diff --git a/templates/zerver/integrations/development/dev_panel.html b/templates/zerver/integrations/development/dev_panel.html index 18084fca7c..0f9e5236c4 100644 --- a/templates/zerver/integrations/development/dev_panel.html +++ b/templates/zerver/integrations/development/dev_panel.html @@ -65,6 +65,15 @@ + + Custom HTTP Headers + + + + + + + JSON Body diff --git a/zerver/tests/test_integrations_dev_panel.py b/zerver/tests/test_integrations_dev_panel.py index 7f1d317932..c4aae28f5f 100644 --- a/zerver/tests/test_integrations_dev_panel.py +++ b/zerver/tests/test_integrations_dev_panel.py @@ -15,7 +15,8 @@ class TestIntegrationsDevPanel(ZulipTestCase): data = { "url": url, - "body": body + "body": body, + "custom_headers": "", } response = self.client_post(target_url, data) @@ -24,7 +25,7 @@ class TestIntegrationsDevPanel(ZulipTestCase): expected_response = {"result": "error", "msg": "Internal server error"} self.assertEqual(ujson.loads(response.content), expected_response) - def test_check_send_webhook_fixture_message_for_success(self) -> None: + def test_check_send_webhook_fixture_message_for_success_without_headers(self) -> None: bot = get_user('webhook-bot@zulip.com', self.zulip_realm) url = "/api/v1/external/airbrake?api_key={key}&stream=Denmark&topic=Airbrake Notifications".format(key=bot.api_key) target_url = "/devtools/integrations/check_send_webhook_fixture_message" @@ -34,6 +35,7 @@ class TestIntegrationsDevPanel(ZulipTestCase): data = { "url": url, "body": body, + "custom_headers": "", } response = self.client_post(target_url, data) @@ -45,6 +47,28 @@ class TestIntegrationsDevPanel(ZulipTestCase): self.assertEqual(Stream.objects.get(id=latest_msg.recipient.type_id).name, "Denmark") self.assertEqual(latest_msg.topic_name(), "Airbrake Notifications") + def test_check_send_webhook_fixture_message_for_success_with_headers(self) -> None: + bot = get_user('webhook-bot@zulip.com', self.zulip_realm) + url = "/api/v1/external/github?api_key={key}&stream=Denmark&topic=GitHub Notifications".format(key=bot.api_key) + target_url = "/devtools/integrations/check_send_webhook_fixture_message" + with open("zerver/webhooks/github/fixtures/ping_organization.json", "r") as f: + body = f.read() + + data = { + "url": url, + "body": body, + "custom_headers": ujson.dumps({"X_GITHUB_EVENT": "ping"}), + } + + response = self.client_post(target_url, data) + self.assertEqual(response.status_code, 200) + + latest_msg = Message.objects.latest('id') + expected_message = "GitHub webhook has been successfully configured by eeshangarg." + self.assertEqual(latest_msg.content, expected_message) + self.assertEqual(Stream.objects.get(id=latest_msg.recipient.type_id).name, "Denmark") + self.assertEqual(latest_msg.topic_name(), "GitHub Notifications") + def test_get_fixtures_for_nonexistant_integration(self) -> None: target_url = "/devtools/integrations/somerandomnonexistantintegration/fixtures" response = self.client_get(target_url) diff --git a/zerver/views/development/integrations.py b/zerver/views/development/integrations.py index d0f28e019c..264f2fdb93 100644 --- a/zerver/views/development/integrations.py +++ b/zerver/views/development/integrations.py @@ -10,6 +10,7 @@ from zerver.lib.integrations import WEBHOOK_INTEGRATIONS from zerver.lib.request import has_request_variables, REQ from zerver.lib.response import json_success, json_error from zerver.models import UserProfile, get_realm +from zerver.management.commands.send_webhook_fixture_message import parse_headers ZULIP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../../') @@ -58,10 +59,19 @@ def get_fixtures(request: HttpResponse, @has_request_variables def check_send_webhook_fixture_message(request: HttpRequest, url: str=REQ(), - body: str=REQ()) -> HttpResponse: + body: str=REQ(), + custom_headers: str=REQ()) -> HttpResponse: client = Client() realm = get_realm("zulip") - response = client.post(url, body, content_type="application/json", HTTP_HOST=realm.host) + try: + headers = parse_headers(custom_headers) + except ValueError as ve: + return json_error("Custom HTTP headers are not in a valid JSON format. {}".format(ve)) # nolint + if not headers: + headers = {} + http_host = headers.pop("HTTP_HOST", realm.host) + content_type = headers.pop("HTTP_CONTENT_TYPE", "application/json") + response = client.post(url, body, content_type=content_type, HTTP_HOST=http_host, **headers) if response.status_code == 200: return json_success() else: