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.
This commit is contained in:
Hemanth V. Alluri 2019-05-16 12:53:15 +05:30 committed by Tim Abbott
parent 64b4fd5923
commit 2bd9c8cb42
7 changed files with 76 additions and 13 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -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();

View File

@ -7,6 +7,11 @@
padding: 10px;
}
#custom_http_headers {
height: 200px;
width: 700px;
}
#fixture_body {
height: 500px;
width: 700px;

View File

@ -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:

View File

@ -65,6 +65,15 @@
</td>
</tr>
<tr>
<td colspan="2" class="center-text pad-top"><b>Custom HTTP Headers</b></td>
</tr>
<tr>
<td colspan="2" class="form-group">
<textarea id="custom_http_headers" class="form-control"></textarea>
</td>
</tr>
<tr>
<td colspan="2" class="center-text pad-top"><b>JSON Body</b></td>
</tr>

View File

@ -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)

View File

@ -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: