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();
|
||||
}
|
||||
|
||||
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) {
|
||||
/* Given a fixture name, use the loaded_fixtures dictionary to set the fixture body field. */
|
||||
var integration_name = get_selected_integration_name();
|
||||
|
@ -191,17 +206,7 @@ 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;
|
||||
}
|
||||
}
|
||||
var custom_headers = get_custom_http_headers();
|
||||
|
||||
channel.post({
|
||||
url: "/devtools/integrations/check_send_webhook_fixture_message",
|
||||
|
@ -224,6 +229,38 @@ function send_webhook_fixture_message() {
|
|||
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
|
||||
$(function () {
|
||||
clear_elements(["stream_name", "topic_name", "URL", "bot_name", "integration_name",
|
||||
|
@ -257,6 +294,11 @@ $(function () {
|
|||
return;
|
||||
});
|
||||
|
||||
$('#send_all_fixtures_button').click(function () {
|
||||
send_all_fixture_messages();
|
||||
return;
|
||||
});
|
||||
|
||||
$("#bot_name").change(update_url);
|
||||
|
||||
$("#stream_name").change(update_url);
|
||||
|
|
|
@ -84,8 +84,14 @@
|
|||
</tr>
|
||||
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<p id="message"></p>
|
||||
<td colspan="2"> <p id="message"></p> </td>
|
||||
</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>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -103,3 +103,75 @@ class TestIntegrationsDevPanel(ZulipTestCase):
|
|||
target_url = "/devtools/integrations/"
|
||||
response = self.client_get(target_url)
|
||||
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)
|
||||
|
||||
|
||||
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
|
||||
def get_fixtures(request: HttpResponse,
|
||||
integration_name: str=REQ()) -> HttpResponse:
|
||||
|
@ -61,18 +77,37 @@ def check_send_webhook_fixture_message(request: HttpRequest,
|
|||
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")
|
||||
response = client.post(url, body, content_type=content_type, HTTP_HOST=http_host, **headers)
|
||||
response = send_webhook_fixture_message(url, body, custom_headers)
|
||||
if response.status_code == 200:
|
||||
return json_success()
|
||||
else:
|
||||
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/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$',
|
||||
zerver.views.development.integrations.get_fixtures),
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue