mirror of https://github.com/zulip/zulip.git
devtools: Add support for send_all in the integrations dev panel.
Using this feature a reviewer can easily send and view all fixture messages for any given integration - with only JSON fixtures that is.
This commit is contained in:
parent
d17fb622c9
commit
8214d65336
|
@ -62,6 +62,21 @@ function get_selected_integration_name() {
|
||||||
return $("#integration_name").children("option:selected").val();
|
return $("#integration_name").children("option:selected").val();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function get_custom_http_headers() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return custom_headers;
|
||||||
|
}
|
||||||
|
|
||||||
function load_fixture_body(fixture_name) {
|
function load_fixture_body(fixture_name) {
|
||||||
/* Given a fixture name, use the loaded_fixtures dictionary to set the fixture body field. */
|
/* Given a fixture name, use the loaded_fixtures dictionary to set the fixture body field. */
|
||||||
var integration_name = get_selected_integration_name();
|
var integration_name = get_selected_integration_name();
|
||||||
|
@ -191,17 +206,7 @@ function send_webhook_fixture_message() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var custom_headers = $("#custom_http_headers").val();
|
var custom_headers = get_custom_http_headers();
|
||||||
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({
|
channel.post({
|
||||||
url: "/devtools/integrations/check_send_webhook_fixture_message",
|
url: "/devtools/integrations/check_send_webhook_fixture_message",
|
||||||
|
@ -224,6 +229,38 @@ function send_webhook_fixture_message() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function send_all_fixture_messages() {
|
||||||
|
/* Send all fixture messages for a given integration. */
|
||||||
|
var url = $("#URL").val();
|
||||||
|
var integration = get_selected_integration_name();
|
||||||
|
if (integration === "") {
|
||||||
|
set_message("You have to select an integration first.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var custom_headers = get_custom_http_headers();
|
||||||
|
|
||||||
|
var csrftoken = $("#csrftoken").val();
|
||||||
|
channel.post({
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
error: handle_unsuccessful_response,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
$(function () {
|
$(function () {
|
||||||
clear_elements(["stream_name", "topic_name", "URL", "bot_name", "integration_name",
|
clear_elements(["stream_name", "topic_name", "URL", "bot_name", "integration_name",
|
||||||
|
@ -257,6 +294,11 @@ $(function () {
|
||||||
return;
|
return;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#send_all_fixtures_button').click(function () {
|
||||||
|
send_all_fixture_messages();
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
$("#bot_name").change(update_url);
|
$("#bot_name").change(update_url);
|
||||||
|
|
||||||
$("#stream_name").change(update_url);
|
$("#stream_name").change(update_url);
|
||||||
|
|
|
@ -84,8 +84,14 @@
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2"> <p id="message"></p> </td>
|
||||||
<p id="message"></p>
|
</tr>
|
||||||
|
|
||||||
|
<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>
|
<button id="send_fixture_button" class="btn-success">Send!</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -103,3 +103,75 @@ class TestIntegrationsDevPanel(ZulipTestCase):
|
||||||
target_url = "/devtools/integrations/"
|
target_url = "/devtools/integrations/"
|
||||||
response = self.client_get(target_url)
|
response = self.client_get(target_url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_send_all_webhook_fixture_messages_for_success(self) -> None:
|
||||||
|
bot = get_user('webhook-bot@zulip.com', self.zulip_realm)
|
||||||
|
url = "/api/v1/external/appfollow?api_key={key}&stream=Denmark&topic=Appfollow Bulk Notifications".format(key=bot.api_key)
|
||||||
|
target_url = "/devtools/integrations/send_all_webhook_fixture_messages"
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"url": url,
|
||||||
|
"custom_headers": "",
|
||||||
|
"integration_name": "appfollow",
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client_post(target_url, data)
|
||||||
|
expected_responses = [
|
||||||
|
{
|
||||||
|
"fixture_name": "sample.json",
|
||||||
|
"status_code": 200,
|
||||||
|
"message": {"msg": "", "result": "success"}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fixture_name": "review.json",
|
||||||
|
"status_code": 200,
|
||||||
|
"message": {"msg": "", "result": "success"}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
responses = ujson.loads(response.content)["responses"]
|
||||||
|
for r in responses:
|
||||||
|
r["message"] = ujson.loads(r["message"])
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
for r in responses:
|
||||||
|
# We have to use this roundabout manner since the order may vary each time. This is not
|
||||||
|
# an issue.
|
||||||
|
self.assertTrue(r in expected_responses)
|
||||||
|
expected_responses.remove(r)
|
||||||
|
|
||||||
|
new_messages = Message.objects.order_by('-id')[0:2]
|
||||||
|
expected_messages = ["Webhook integration was successful.\nTest User / Acme (Google Play)", "Acme - Group chat\nApp Store, Acme Technologies, Inc.\n★★★★★ United States\n**Great for Information Management**\nAcme enables me to manage the flow of information quite well. I only wish I could create and edit my Acme Post files in the iOS app.\n*by* **Mr RESOLUTIONARY** *for v3.9*\n[Permalink](http://appfollow.io/permalink) · [Add tag](http://watch.appfollow.io/add_tag)"]
|
||||||
|
for msg in new_messages:
|
||||||
|
# new_messages -> expected_messages or expected_messages -> new_messages shouldn't make
|
||||||
|
# a difference since equality is commutative.
|
||||||
|
self.assertTrue(msg.content in expected_messages)
|
||||||
|
expected_messages.remove(msg.content)
|
||||||
|
self.assertEqual(Stream.objects.get(id=msg.recipient.type_id).name, "Denmark")
|
||||||
|
self.assertEqual(msg.topic_name(), "Appfollow Bulk Notifications")
|
||||||
|
|
||||||
|
@patch("zerver.views.development.integrations.os.path.exists")
|
||||||
|
def test_send_all_webhook_fixture_messages_for_missing_fixtures(self, os_path_exists_mock: MagicMock) -> None:
|
||||||
|
os_path_exists_mock.return_value = False
|
||||||
|
bot = get_user('webhook-bot@zulip.com', self.zulip_realm)
|
||||||
|
url = "/api/v1/external/appfollow?api_key={key}&stream=Denmark&topic=Appfollow Bulk Notifications".format(key=bot.api_key)
|
||||||
|
data = {
|
||||||
|
"url": url,
|
||||||
|
"custom_headers": "",
|
||||||
|
"integration_name": "appfollow",
|
||||||
|
}
|
||||||
|
response = self.client_post("/devtools/integrations/send_all_webhook_fixture_messages", data)
|
||||||
|
expected_response = {'msg': 'The integration "appfollow" does not have fixtures.', 'result': 'error'}
|
||||||
|
self.assertEqual(response.status_code, 404)
|
||||||
|
self.assertEqual(ujson.loads(response.content), expected_response)
|
||||||
|
|
||||||
|
def test_send_all_webhook_fixture_messages_for_non_json_fixtures(self) -> None:
|
||||||
|
bot = get_user('webhook-bot@zulip.com', self.zulip_realm)
|
||||||
|
url = "/api/v1/external/appfollow?api_key={key}&stream=Denmark&topic=Desktdotcom Bulk Notifications".format(key=bot.api_key)
|
||||||
|
data = {
|
||||||
|
"url": url,
|
||||||
|
"custom_headers": "",
|
||||||
|
"integration_name": "deskdotcom",
|
||||||
|
}
|
||||||
|
response = self.client_post("/devtools/integrations/send_all_webhook_fixture_messages", data)
|
||||||
|
expected_response = {'msg': 'The integration "deskdotcom" has non-JSON fixtures.', 'result': 'error'}
|
||||||
|
self.assertEqual(response.status_code, 400)
|
||||||
|
self.assertEqual(ujson.loads(response.content), expected_response)
|
||||||
|
|
|
@ -27,6 +27,22 @@ def dev_panel(request: HttpRequest) -> HttpResponse:
|
||||||
return render(request, "zerver/integrations/development/dev_panel.html", context)
|
return render(request, "zerver/integrations/development/dev_panel.html", context)
|
||||||
|
|
||||||
|
|
||||||
|
def send_webhook_fixture_message(url: str=REQ(),
|
||||||
|
body: str=REQ(),
|
||||||
|
custom_headers: str=REQ()) -> HttpResponse:
|
||||||
|
client = Client()
|
||||||
|
realm = get_realm("zulip")
|
||||||
|
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")
|
||||||
|
return client.post(url, body, content_type=content_type, HTTP_HOST=http_host, **headers)
|
||||||
|
|
||||||
|
|
||||||
@has_request_variables
|
@has_request_variables
|
||||||
def get_fixtures(request: HttpResponse,
|
def get_fixtures(request: HttpResponse,
|
||||||
integration_name: str=REQ()) -> HttpResponse:
|
integration_name: str=REQ()) -> HttpResponse:
|
||||||
|
@ -61,18 +77,37 @@ def check_send_webhook_fixture_message(request: HttpRequest,
|
||||||
url: str=REQ(),
|
url: str=REQ(),
|
||||||
body: str=REQ(),
|
body: str=REQ(),
|
||||||
custom_headers: str=REQ()) -> HttpResponse:
|
custom_headers: str=REQ()) -> HttpResponse:
|
||||||
client = Client()
|
response = send_webhook_fixture_message(url, body, custom_headers)
|
||||||
realm = get_realm("zulip")
|
|
||||||
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:
|
if response.status_code == 200:
|
||||||
return json_success()
|
return json_success()
|
||||||
else:
|
else:
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@has_request_variables
|
||||||
|
def send_all_webhook_fixture_messages(request: HttpRequest,
|
||||||
|
url: str=REQ(),
|
||||||
|
custom_headers: str=REQ(),
|
||||||
|
integration_name: str=REQ()) -> HttpResponse:
|
||||||
|
fixtures_dir = os.path.join(ZULIP_PATH, "zerver/webhooks/{integration_name}/fixtures".format(
|
||||||
|
integration_name=integration_name))
|
||||||
|
if not os.path.exists(fixtures_dir):
|
||||||
|
msg = ("The integration \"{integration_name}\" does not have fixtures.").format(
|
||||||
|
integration_name=integration_name)
|
||||||
|
return json_error(msg, status=404)
|
||||||
|
|
||||||
|
responses = []
|
||||||
|
for fixture in os.listdir(fixtures_dir):
|
||||||
|
fixture_path = os.path.join(fixtures_dir, fixture)
|
||||||
|
try:
|
||||||
|
# Just a quick check to make sure that the fixtures are of a valid JSON format.
|
||||||
|
json_data = ujson.dumps(ujson.loads(open(fixture_path).read()))
|
||||||
|
except ValueError:
|
||||||
|
msg = ("The integration \"{integration_name}\" has non-JSON fixtures.").format(
|
||||||
|
integration_name=integration_name)
|
||||||
|
return json_error(msg)
|
||||||
|
response = send_webhook_fixture_message(url, json_data, custom_headers)
|
||||||
|
responses.append({"status_code": response.status_code,
|
||||||
|
"fixture_name": fixture,
|
||||||
|
"message": response.content})
|
||||||
|
return json_success({"responses": responses})
|
||||||
|
|
|
@ -54,6 +54,8 @@ urls = [
|
||||||
url(r'^devtools/integrations/$', zerver.views.development.integrations.dev_panel),
|
url(r'^devtools/integrations/$', zerver.views.development.integrations.dev_panel),
|
||||||
url(r'^devtools/integrations/check_send_webhook_fixture_message$',
|
url(r'^devtools/integrations/check_send_webhook_fixture_message$',
|
||||||
zerver.views.development.integrations.check_send_webhook_fixture_message),
|
zerver.views.development.integrations.check_send_webhook_fixture_message),
|
||||||
|
url(r'^devtools/integrations/send_all_webhook_fixture_messages$',
|
||||||
|
zerver.views.development.integrations.send_all_webhook_fixture_messages),
|
||||||
url(r'^devtools/integrations/(?P<integration_name>.+)/fixtures$',
|
url(r'^devtools/integrations/(?P<integration_name>.+)/fixtures$',
|
||||||
zerver.views.development.integrations.get_fixtures),
|
zerver.views.development.integrations.get_fixtures),
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in New Issue