devtools: Completely overhaul the frontend for the integrations devtool.

This commit also adds a small functionality change where the results of
each webhook fixture message sent is now displayed to the user.

With a small tweak by tabbott to fix a styling bug.

Fixes #12122.
This commit is contained in:
Hemanth V. Alluri 2019-05-19 00:29:37 +05:30 committed by Tim Abbott
parent ef98211f68
commit 1a12e112d9
7 changed files with 217 additions and 115 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

View File

@ -18,6 +18,7 @@ var clear_handlers = {
fixture_name: function () { $('#fixture_name').empty(); },
fixture_body: function () { $("#fixture_body")[0].value = ""; },
custom_http_headers: function () { $("#custom_http_headers")[0].value = ""; },
results: function () { $("#idp-results")[0].value = ""; },
};
function clear_elements(elements) {
@ -82,6 +83,27 @@ function get_custom_http_headers() {
return custom_headers;
}
function set_results(response) {
/* After sending a webhook fixture message, the backend will let
us know the responses information for each of the requests sent
(which is just 1 for send, but usually is several for send all).
The following is a bit messy, but it's a devtool, so ultimately OK */
var responses = response.responses;
var data = "Results:\n\n";
responses.forEach(function (response) {
if (response.fixture_name !== undefined) {
data += "Fixture: " + response.fixture_name;
data += "\nStatus Code: " + response.status_code;
} else {
data += "Status Code: " + response.status_code;
}
data += "\nResponse: " + response.message + "\n\n";
});
$("#idp-results")[0].value = data;
}
function load_fixture_body(fixture_name) {
/* Given a fixture name, use the loaded_fixtures dictionary to set the fixture body field. */
var integration_name = get_selected_integration_name();
@ -223,10 +245,11 @@ function send_webhook_fixture_message() {
url: "/devtools/integrations/check_send_webhook_fixture_message",
data: {url: url, body: body, custom_headers: custom_headers, is_json: is_json},
beforeSend: function (xhr) {xhr.setRequestHeader('X-CSRFToken', csrftoken);},
success: function () {
success: function (response) {
// If the previous fixture body was sent successfully, then we should change the success
// message up a bit to let the user easily know that this fixture body was also sent
// successfully.
set_results(response);
if ($("#message")[0].innerHTML === "Success!") {
set_message("Success!!!", "success");
} else {
@ -256,16 +279,7 @@ function send_all_fixture_messages() {
url: "/devtools/integrations/send_all_webhook_fixture_messages",
data: {url: url, custom_headers: custom_headers, integration_name: integration},
beforeSend: function (xhr) {xhr.setRequestHeader('X-CSRFToken', csrftoken);},
success: function (response) {
// This is a somewhat sloppy construction, but ultimately, it's a devtool.
var responses = response.responses;
var data = "Results:\n\n";
responses.forEach(function (response) {
data += "Fixture: " + response.fixture_name + "\nStatus Code: ";
data += response.status_code + "\nResponse: " + response.message + "\n\n";
});
$("#fixture_body")[0].value = data;
},
success: function (response) {set_results(response);},
error: handle_unsuccessful_response,
});
@ -275,7 +289,8 @@ function send_all_fixture_messages() {
// Initialization
$(function () {
clear_elements(["stream_name", "topic_name", "URL", "bot_name", "integration_name",
"fixture_name", "custom_http_headers", "fixture_body", "message"]);
"fixture_name", "custom_http_headers", "fixture_body", "message",
"results"]);
$("#stream_name")[0].value = "Denmark";
$("#topic_name")[0].value = "Integrations Testing";
@ -306,6 +321,7 @@ $(function () {
});
$('#send_all_fixtures_button').click(function () {
clear_elements(["message"]);
send_all_fixture_messages();
return;
});
@ -319,11 +335,3 @@ $(function () {
});
}());
/*
Development Notes:
- We currently don't support non-json fixtures.
Possible Improvements:
- Add support for extra keys, headers, etc.
*/

View File

@ -1,34 +1,114 @@
#panel_div {
margin: auto;
width: 720px;
margin-top: 50px;
border: 3px solid;
border-color: rgb(0, 128, 0);
padding: 10px;
circle {
fill: rgb(0, 0, 0);
stroke-width: 1.93936479;
stroke: transparent;
}
path {
fill: rgb(82, 194, 175);
stroke: rgb(82, 194, 175);
}
button {
background-color: rgb(220, 244, 233);
color: rgb(46, 139, 87);
}
#custom_http_headers {
height: 200px;
width: 700px;
width: 100%;
height: 60px;
resize: vertical;
}
#dev-panel {
margin-left: 3%;
margin-right: 3%;
padding-bottom: 15px;
padding-top: 20px;
padding-left: 10px;
padding-right: 10px;
background-color: rgb(247, 255, 254);
box-shadow: 8px 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
#fixture_body {
width: 90%;
height: 500px;
width: 700px;
resize: vertical;
}
#send_fixture_button {
float: right;
#idp-results {
width: 100%;
height: 500px;
resize: vertical;
background-color: rgb(256, 256, 256);
}
#top-navbar {
display: grid;
grid-template-columns: 1fr 5fr 1fr;
}
#URL {
width: 90%;
width: 100%;
}
.center-text {
.buttons {
display: flex;
justify-content: space-between;
width: 92%;
margin-right: 2%;
}
.centerpiece {
font-size: 30px;
text-align: center;
}
.pad-top {
padding-top: 30px;
.inline {
display: flex;
flex-wrap: nowrap;
justify-content: space-between;
margin-right: 10%;
}
.optional {
color: rgb(128, 128, 128);
}
.pad-small {
height: 100px;
width: 100%;
}
.row1 {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-left: 2%;
margin-right: 2%;
}
.row2 {
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
margin-left: 2%;
margin-right: 2%;
}
.row3 {
display: flex;
flex-wrap: wrap;
flex-direction: column;
justify-content: flex-start;
margin-left: 2%;
margin-right: 2%;
}
.row4 {
display: grid;
grid-template-columns: 1fr 1fr;
margin-left: 2%;
margin-right: 2%;
}

View File

@ -233,8 +233,6 @@ approach shown above.
### Integrations Dev Panel
This is the GUI tool.
<img class="screenshot" src="/static/images/integrations/integrations_dev_panel.png" />
1. Run `./tools/run-dev.py` then go to http://localhost:9991/devtools/integrations/.
2. Set the following mandatory fields:

View File

@ -1,105 +1,115 @@
{% extends "zerver/base.html" %}
{% extends "zerver/portico.html" %}
{% block customhead %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{ render_bundle('integrations-dev-panel') }}
{{ render_bundle('portico') }}
{% endblock %}
{% block content %}
<div class="header portico-header">
<div class="header-main" id="top-navbar">
<div>
<a class="brand logo">
<svg class="brand-logo" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 40 40" version="1.1">
<g transform="translate(-297.14285,-466.64792)">
<circle cx="317.14285" cy="486.64792" r="19.030317"></circle>
<path d="m309.24286 477.14791 14.2 0 1.6 3.9-11.2 11.9 9.6 0 1.6 3.2-14.2 0-1.6-3.9 11.2-11.9-9.6 0z"></path>
</g>
</svg>
<span>Zulip</span>
</a>
</div>
<input id="csrftoken" type="hidden" value="{{ csrf_token }}">
<div class="centerpiece">Integrations Developer Panel</div>
<div id="panel_div">
<div class="top-links">
<a href="/">Go to Zulip</a>
</div>
<h1 class="center-text"> Integrations Developer Panel </h1>
</div>
</div>
<table>
<div class="pad-small"></div>
<tr>
<td>
<label><b>Stream</b></label>
<input id="stream_name" type="text" />
</td>
<td>
<label><b>Topic</b></label>
<input id="topic_name" type="text" />
</td>
</tr>
<div id="dev-panel">
<div class="row1">
<div>
<label for="bot_name"><b>Bot</b></label>
<select id="bot_name">
<option value=""></option>
{% for bot in bots %}
<option value="{{ bot.api_key }}"> {{ bot.full_name }} </option>
{% endfor %}
</select>
</div>
<tr>
<td class="pad-top"><b>Bot</b></td>
<td class="pad-top">
<select id="bot_name">
<option value=""></option>
{% for bot in bots %}
<option value="{{ bot.api_key }}"> {{ bot.full_name }} </option>
{% endfor %}
</select>
</td>
</tr>
<div>
<label><b>Integration</b></label>
<select id="integration_name">
<option value=""></option>
{% for integration in integrations %}
<option value="{{ integration }}"> {{ integration }} </option>
{% endfor %}
</select>
</div>
<tr>
<td><b>Integration</b></td>
<td>
<select id="integration_name">
<option value=""></option>
{% for integration in integrations %}
<option value="{{ integration }}"> {{ integration }} </option>
{% endfor %}
</select>
</td>
</tr>
<div>
<label for="fixture_name"><b>Fixture</b></label>
<select id="fixture_name"></select>
</div>
<tr>
<td><b>Fixture</b></td>
<td><select id="fixture_name"></select></td>
</tr>
<div>
<label class="optional"><b>Stream</b></label>
<input id="stream_name" type="text" />
</div>
<tr>
<td colspan="2" class="pad-top">
<label for="URL"><b>URL</b></label>
<input id="URL" type="text" />
</td>
</tr>
<div>
<label class="optional"><b>Topic</b></label>
<input id="topic_name" type="text" />
</div>
</div>
<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>
<br>
<tr>
<td colspan="2" class="center-text pad-top"><b>Fixture Body</b></td>
</tr>
<tr>
<td colspan="2" class="form-group">
<textarea id="fixture_body" class="form-control"></textarea>
</td>
</tr>
<div class="row2">
<label for="URL"><b>URL</b> (Automatically Generated)</label>
<input id="URL" type="text" />
</div>
<tr>
<td colspan="2"> <p id="message"></p> </td>
</tr>
<br>
<tr>
<td>
<button id="send_all_fixtures_button" class="btn-success">Send All</button>
</td>
<td>
<button id="send_fixture_button" class="btn-success">Send!</button>
</td>
</tr>
<div class="row3">
<label for="custom_http_headers"><b>Custom HTTP Headers</b></label>
<textarea id="custom_http_headers"></textarea>
</div>
</table>
<br>
<div class="row4">
<div class="col1">
<div class="inline">
<label for="fixture_body"><b>Fixture Body</b></label>
<span id="message"></span>
</div>
<textarea id="fixture_body"></textarea>
<div class="buttons">
<button id="send_all_fixtures_button">Send All</button>
<button id="send_fixture_button">Send!</button>
</div>
</div>
<div class="col1">
<label for="idp-results"><b>Results</b></label>
<textarea id="idp-results" readonly></textarea>
</div>
</div>
</div>
<br> <br> <br>
<div class="pad-small"></div>
<input id="csrftoken" type="hidden" value="{{ csrf_token }}">
{% endblock %}

View File

@ -41,7 +41,11 @@ class TestIntegrationsDevPanel(ZulipTestCase):
}
response = self.client_post(target_url, data)
expected_response = {'responses': [{'status_code': 200, 'message': {"result": "success", "msg": ""}}], 'result': 'success', 'msg': ''}
response_content = ujson.loads(response.content)
response_content["responses"][0]["message"] = ujson.loads(response_content["responses"][0]["message"])
self.assertEqual(response.status_code, 200)
self.assertEqual(response_content, expected_response)
latest_msg = Message.objects.latest('id')
expected_message = "[ZeroDivisionError](https://zulip.airbrake.io/projects/125209/groups/1705190192091077626): \"Error message from logger\" occurred."

View File

@ -81,7 +81,9 @@ def check_send_webhook_fixture_message(request: HttpRequest,
custom_headers: str=REQ()) -> HttpResponse:
response = send_webhook_fixture_message(url, body, is_json, custom_headers)
if response.status_code == 200:
return json_success()
responses = [{"status_code": response.status_code,
"message": response.content}]
return json_success({"responses": responses})
else:
return response