Added Zendesk webhook

Zendesk works a lot like desk.com, it has triggers which use targets.
The triggers have a user defined template. Targets can also have place
holders that are posted, we add the ticket id and title here so we can
always construct the message subject.

(imported from commit 04e8e5c7c0fc5568201f252546f6ed42f282fd00)
This commit is contained in:
Jason Michalski 2014-01-10 17:55:27 -05:00
parent 3679889be2
commit 4b86ef59f1
3 changed files with 61 additions and 4 deletions

View File

@ -5372,6 +5372,41 @@ Priority: **High** => **Low**""")
self.assertEqual(msg.subject, u"#12: Not enough ☃ guinea pigs") self.assertEqual(msg.subject, u"#12: Not enough ☃ guinea pigs")
self.assertIn("[guinea_pig.png](http://cdn.freshdesk.com/data/helpdesk/attachments/production/12744808/original/guinea_pig.png)", msg.content) self.assertIn("[guinea_pig.png](http://cdn.freshdesk.com/data/helpdesk/attachments/production/12744808/original/guinea_pig.png)", msg.content)
class ZenDeskHookTests(AuthedTestCase):
def generate_webhook_response(self, ticket_title='User can\'t login',
ticket_id=54, message='Message',
stream_name='zendesk'):
data = {
'ticket_title': ticket_title,
'ticket_id': ticket_id,
'message': message,
'stream': stream_name,
}
email = 'hamlet@zulip.com'
self.subscribe_to_stream(email, stream_name)
result = self.client.post('/api/v1/external/zendesk', data,
**self.api_auth(email))
self.assert_json_success(result)
# Check the correct message was sent
msg = Message.objects.filter().order_by('-id')[0]
self.assertEqual(msg.sender.email, email)
return msg
def test_subject(self):
msg = self.generate_webhook_response(ticket_id=4, ticket_title="Test ticket")
self.assertEqual(msg.subject, '#4: Test ticket')
def test_long_subject(self):
msg = self.generate_webhook_response(ticket_id=4, ticket_title="Test ticket" + '!' * 80)
self.assertEqual(msg.subject, '#4: Test ticket' + '!' * 42 + '...')
def test_content(self):
msg = self.generate_webhook_response(message='New comment:\n> It is better\n* here')
self.assertEqual(msg.content, 'New comment:\n> It is better\n* here')
class RateLimitTests(AuthedTestCase): class RateLimitTests(AuthedTestCase):
def setUp(self): def setUp(self):

View File

@ -3,7 +3,6 @@
from __future__ import absolute_import from __future__ import absolute_import
from django.conf import settings from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
from zerver.models import UserProfile, get_client, get_user_profile_by_email from zerver.models import UserProfile, get_client, get_user_profile_by_email
from zerver.lib.actions import check_send_message, convert_html_to_markdown from zerver.lib.actions import check_send_message, convert_html_to_markdown
from zerver.lib.response import json_success, json_error from zerver.lib.response import json_success, json_error
@ -568,7 +567,6 @@ def api_beanstalk_webhook(request, user_profile,
# There's no raw JSON for us to work from. Thus, it makes sense to just write # There's no raw JSON for us to work from. Thus, it makes sense to just write
# a template Zulip message within Desk.com and have the webhook extract that # a template Zulip message within Desk.com and have the webhook extract that
# from the "data" param and post it, which this does. # from the "data" param and post it, which this does.
@csrf_exempt
@authenticated_rest_api_view @authenticated_rest_api_view
@has_request_variables @has_request_variables
def api_deskdotcom_webhook(request, user_profile, data=REQ(), def api_deskdotcom_webhook(request, user_profile, data=REQ(),
@ -633,7 +631,6 @@ def api_bitbucket_webhook(request, user_profile, payload=REQ(converter=json_to_d
[stream], subject, content) [stream], subject, content)
return json_success() return json_success()
@csrf_exempt
@authenticated_rest_api_view @authenticated_rest_api_view
@has_request_variables @has_request_variables
def api_stash_webhook(request, user_profile, stream=REQ(default='')): def api_stash_webhook(request, user_profile, stream=REQ(default='')):
@ -751,7 +748,6 @@ def format_freshdesk_ticket_creation_message(ticket):
return content return content
@csrf_exempt
@authenticated_rest_api_view @authenticated_rest_api_view
@has_request_variables @has_request_variables
def api_freshdesk_webhook(request, user_profile, stream=REQ(default='')): def api_freshdesk_webhook(request, user_profile, stream=REQ(default='')):
@ -800,3 +796,28 @@ def api_freshdesk_webhook(request, user_profile, stream=REQ(default='')):
check_send_message(user_profile, get_client("ZulipFreshdeskWebhook"), "stream", check_send_message(user_profile, get_client("ZulipFreshdeskWebhook"), "stream",
[stream], subject, content) [stream], subject, content)
return json_success() return json_success()
def truncate(string, length):
if len(string) > length:
string = string[:length-3] + '...'
return string
@authenticated_rest_api_view
def api_zendesk_webhook(request, user_profile):
"""
Zendesk uses trigers with message templates. This webhook uses the
ticket_id and ticket_title to create a subject. And passes with zendesk
user's configured message to zulip.
"""
try:
ticket_title = request.POST['ticket_title']
ticket_id = request.POST['ticket_id']
message = request.POST['message']
stream = request.POST.get('stream', 'zendesk')
except KeyError as e:
return json_error('Missing post parameter %s' % (e.message,))
subject = truncate('#%s: %s' % (ticket_id, ticket_title), 60)
check_send_message(user_profile, get_client('ZulipZenDeskWebhook'), 'stream',
[stream], subject, message)
return json_success()

View File

@ -160,6 +160,7 @@ urlpatterns += patterns('zerver.views',
url(r'^api/v1/external/desk$', 'webhooks.api_deskdotcom_webhook'), url(r'^api/v1/external/desk$', 'webhooks.api_deskdotcom_webhook'),
url(r'^api/v1/external/stash$', 'webhooks.api_stash_webhook'), url(r'^api/v1/external/stash$', 'webhooks.api_stash_webhook'),
url(r'^api/v1/external/freshdesk$', 'webhooks.api_freshdesk_webhook'), url(r'^api/v1/external/freshdesk$', 'webhooks.api_freshdesk_webhook'),
url(r'^api/v1/external/zendesk$', 'webhooks.api_zendesk_webhook'),
url(r'^user_uploads/(?P<realm_id>\d*)/(?P<filename>.*)', 'rest_dispatch', url(r'^user_uploads/(?P<realm_id>\d*)/(?P<filename>.*)', 'rest_dispatch',
{'GET': 'get_uploaded_file'}), {'GET': 'get_uploaded_file'}),