2019-04-14 15:28:19 +02:00
|
|
|
(function () {
|
|
|
|
|
|
|
|
|
|
|
|
// Data Segment: We lazy load the requested fixtures from the backend as and when required
|
|
|
|
// and then keep them here.
|
|
|
|
var loaded_fixtures = {};
|
|
|
|
var url_base = "/api/v1/external/";
|
|
|
|
|
|
|
|
|
|
|
|
// Resetting/Clearing: a method and a map for clearing certain UI elements.
|
|
|
|
var clear_handlers = {
|
|
|
|
stream_name: "#stream_name",
|
|
|
|
topic_name: "#topic_name",
|
|
|
|
URL: "#URL",
|
|
|
|
message: "#message",
|
|
|
|
bot_name: function () { $('#bot_name').children()[0].selected = true; },
|
|
|
|
integration_name: function () { $('#integration_name').children()[0].selected = true; },
|
|
|
|
fixture_name: function () { $('#fixture_name').empty(); },
|
|
|
|
fixture_body: function () { $("#fixture_body")[0].value = ""; },
|
2019-05-16 09:23:15 +02:00
|
|
|
custom_http_headers: function () { $("#custom_http_headers")[0].value = ""; },
|
2019-04-14 15:28:19 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
function clear_elements(elements) {
|
|
|
|
/* Clear UI elements by specifying which ones to clear as an array of strings. */
|
|
|
|
elements.forEach(function (element_name) {
|
|
|
|
var handler = clear_handlers[element_name];
|
|
|
|
if (typeof handler === "string") {
|
|
|
|
// Handle clearing text input fields or the message field.
|
|
|
|
var element_object = $(handler)[0];
|
|
|
|
element_object.value = "";
|
|
|
|
element_object.innerHTML = "";
|
|
|
|
} else {
|
|
|
|
// Use the function returned by the map directly.
|
|
|
|
handler();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Message handlers: The message is a small paragraph at the bottom of the page where
|
|
|
|
// we let the user know what happened - e.g. success, invalid JSON, etc.
|
|
|
|
var message_level_to_color_map = {
|
|
|
|
warning: "#be1931",
|
|
|
|
success: "#085d44",
|
|
|
|
};
|
|
|
|
|
|
|
|
function set_message(msg, level) {
|
|
|
|
var message_field = $("#message")[0];
|
|
|
|
message_field.innerHTML = msg;
|
|
|
|
message_field.style.color = message_level_to_color_map[level];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Helper methods
|
|
|
|
function get_api_key_from_selected_bot() {
|
|
|
|
return $("#bot_name").children("option:selected").val();
|
|
|
|
}
|
|
|
|
|
|
|
|
function get_selected_integration_name() {
|
|
|
|
return $("#integration_name").children("option:selected").val();
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
var element = loaded_fixtures[integration_name][fixture_name];
|
|
|
|
var fixture_body = JSON.stringify(element, null, 4); // 4 is for the pretty print indent factor.
|
|
|
|
|
|
|
|
if (fixture_body === undefined) {
|
|
|
|
set_message("Fixture does not have a body.", "warning");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$("#fixture_body")[0].value = fixture_body;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
function load_fixture_options(integration_name) {
|
|
|
|
/* Using the integration name and loaded_fixtures object to set the fixture options for the
|
|
|
|
fixture_names dropdown and also set the fixture body to the first fixture by default. */
|
|
|
|
var fixtures_options_dropdown = $("#fixture_name")[0];
|
|
|
|
var fixtures_names = Object.keys(loaded_fixtures[integration_name]);
|
|
|
|
|
|
|
|
fixtures_names.forEach(function (fixture_name) {
|
|
|
|
var new_dropdown_option = document.createElement("option");
|
|
|
|
new_dropdown_option.value = fixture_name;
|
|
|
|
new_dropdown_option.innerHTML = fixture_name;
|
|
|
|
fixtures_options_dropdown.add(new_dropdown_option);
|
|
|
|
});
|
|
|
|
load_fixture_body(fixtures_names[0]);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
function update_url() {
|
|
|
|
/* Automatically build the URL that the webhook should be targeting. To generate this URL, we
|
|
|
|
would need at least the bot's API Key and the integration name. The stream and topic are both
|
|
|
|
optional, and for the sake of completeness, it should be noted that the topic is irrelavent
|
|
|
|
without specifying the stream.*/
|
|
|
|
var url_field = $("#URL")[0];
|
|
|
|
|
|
|
|
var integration_name = get_selected_integration_name();
|
|
|
|
var api_key = get_api_key_from_selected_bot();
|
|
|
|
|
|
|
|
if (integration_name === "" || api_key === "") {
|
|
|
|
clear_elements(["URL"]);
|
|
|
|
} else {
|
|
|
|
var url = url_base + integration_name + "?api_key=" + api_key;
|
|
|
|
var stream_name = $("#stream_name").val();
|
|
|
|
if (stream_name !== "") {
|
|
|
|
url += "&stream=" + stream_name;
|
|
|
|
var topic_name = $("#topic_name").val();
|
|
|
|
if (topic_name !== "") {
|
|
|
|
url += "&topic=" + topic_name;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
url_field.value = url;
|
|
|
|
url_field.innerHTML = url;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// API Callers: These methods handle communicating with the Python backend API.
|
|
|
|
function handle_unsuccessful_response(response) {
|
|
|
|
try {
|
|
|
|
var status_code = response.statusCode().status;
|
|
|
|
response = JSON.parse(response.responseText);
|
|
|
|
set_message("Result: " + "(" + status_code + ") " + response.msg, "warning");
|
|
|
|
} catch (err) {
|
|
|
|
// If the response is not a JSON response then it would be Django sending a HTML response
|
|
|
|
// containing a stack trace and useful debugging information regarding the backend code.
|
|
|
|
document.write(response.responseText);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 === "") {
|
2019-05-16 09:23:15 +02:00
|
|
|
clear_elements(["custom_http_headers", "fixture_body", "fixture_name", "URL", "message"]);
|
2019-04-14 15:28:19 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (loaded_fixtures[integration_name] !== undefined) {
|
|
|
|
load_fixture_options(integration_name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We don't have the fixutures for this integration; fetch them using Zulip's channel library.
|
|
|
|
// Relative url pattern: /devtools/integrations/(?P<integration_name>.+)/fixtures
|
|
|
|
channel.get({
|
|
|
|
url: "/devtools/integrations/" + integration_name + "/fixtures",
|
|
|
|
idempotent: false, // Since the user may add or modify fixtures while testing.
|
|
|
|
success: function (response) {
|
|
|
|
loaded_fixtures[integration_name] = response.fixtures;
|
|
|
|
load_fixture_options(integration_name);
|
|
|
|
return;
|
|
|
|
},
|
|
|
|
error: handle_unsuccessful_response,
|
|
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
function send_webhook_fixture_message() {
|
|
|
|
/* Make sure that the user is sending valid JSON in the fixture body and that the URL is not
|
|
|
|
empty. Then simply send the fixture body to the specified URL. */
|
|
|
|
|
|
|
|
// Note: If the user has just logged in using a seperate tab while the integrations dev panel is
|
|
|
|
// open, then the csrf token that we have stored in the hidden input element would be obsoleted
|
|
|
|
// leading to an error message when the user tries to send the fixture body.
|
|
|
|
var csrftoken = $("#csrftoken").val();
|
|
|
|
|
|
|
|
var url = $("#URL").val();
|
|
|
|
if (url === "") {
|
|
|
|
set_message("URL can't be empty.", "warning");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var body = $("#fixture_body").val();
|
|
|
|
try {
|
|
|
|
// Let JavaScript validate the JSON for us.
|
|
|
|
body = JSON.stringify(JSON.parse(body));
|
|
|
|
} catch (err) {
|
|
|
|
set_message("Invalid JSON in fixture body.", "warning");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-05-16 09:23:15 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-14 15:28:19 +02:00
|
|
|
channel.post({
|
|
|
|
url: "/devtools/integrations/check_send_webhook_fixture_message",
|
2019-05-16 09:23:15 +02:00
|
|
|
data: {url: url, body: body, custom_headers: custom_headers},
|
2019-04-14 15:28:19 +02:00
|
|
|
beforeSend: function (xhr) {xhr.setRequestHeader('X-CSRFToken', csrftoken);},
|
|
|
|
success: function () {
|
|
|
|
// 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.
|
|
|
|
if ($("#message")[0].innerHTML === "Success!") {
|
|
|
|
set_message("Success!!!", "success");
|
|
|
|
} else {
|
|
|
|
set_message("Success!", "success");
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
},
|
|
|
|
error: handle_unsuccessful_response,
|
|
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialization
|
|
|
|
$(function () {
|
|
|
|
clear_elements(["stream_name", "topic_name", "URL", "bot_name", "integration_name",
|
2019-05-16 09:23:15 +02:00
|
|
|
"fixture_name", "custom_http_headers", "fixture_body", "message"]);
|
2019-04-14 15:28:19 +02:00
|
|
|
|
|
|
|
var potential_default_bot = $("#bot_name")[0][1];
|
|
|
|
if (potential_default_bot !== undefined) {
|
|
|
|
potential_default_bot.selected = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$('#integration_name').change(function () {
|
2019-05-16 09:23:15 +02:00
|
|
|
clear_elements(["custom_http_headers", "fixture_body", "fixture_name", "message"]);
|
2019-04-14 15:28:19 +02:00
|
|
|
var integration_name = $(this).children("option:selected").val();
|
|
|
|
get_fixtures(integration_name);
|
|
|
|
update_url();
|
|
|
|
return;
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#fixture_name').change(function () {
|
|
|
|
clear_elements(["fixture_body", "message"]);
|
|
|
|
var fixture_name = $(this).children("option:selected").val();
|
|
|
|
load_fixture_body(fixture_name);
|
|
|
|
return;
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#send_fixture_button').click(function () {
|
|
|
|
send_webhook_fixture_message();
|
|
|
|
return;
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#bot_name").change(update_url);
|
|
|
|
|
|
|
|
$("#stream_name").change(update_url);
|
|
|
|
|
|
|
|
$("#topic_name").change(update_url);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}());
|
|
|
|
|
|
|
|
/*
|
|
|
|
Development Notes:
|
|
|
|
- We currently don't support non-json fixtures.
|
|
|
|
|
|
|
|
Possible Improvements:
|
|
|
|
- Add support for extra keys, headers, etc.
|
|
|
|
*/
|