Add outgoing webhook interface for Slack.

This commit is contained in:
vaibhav 2017-07-01 18:12:34 +05:30 committed by Tim Abbott
parent e5be29bc3c
commit 4c50c4fc76
4 changed files with 127 additions and 4 deletions

View File

@ -1787,6 +1787,7 @@ class CustomProfileFieldValue(models.Model):
# They provide additional functionality like parsing message to obtain query url, data to be sent to url,
# and parsing the response.
GENERIC_INTERFACE = u'GenericService'
SLACK_INTERFACE = u'SlackOutgoingWebhookService'
# A Service corresponds to either an outgoing webhook bot or an embedded bot.
# The type of Service is determined by the bot_type field of the referenced
@ -1813,15 +1814,18 @@ class Service(models.Model):
# Interface / API version of the service.
interface = models.PositiveSmallIntegerField(default=1) # type: int
# Valid interfaces are {generic}
# Valid interfaces are {generic, zulip_bot_service, slack}
GENERIC = 1
SLACK = 2
ALLOWED_INTERFACE_TYPES = [
GENERIC,
SLACK,
]
# N.B. If we used Django's choice=... we would get this for free (kinda)
_interfaces = {
GENERIC: GENERIC_INTERFACE,
SLACK: SLACK_INTERFACE,
} # type: Dict[int, Text]
def interface_name(self):

View File

@ -1,11 +1,13 @@
from __future__ import absolute_import
from typing import Any, Dict, Text
from zerver.models import GENERIC_INTERFACE, Service, SLACK_INTERFACE
from zerver.outgoing_webhooks.generic import GenericOutgoingWebhookService
from zerver.models import GENERIC_INTERFACE, Service
from zerver.outgoing_webhooks.slack import SlackOutgoingWebhookService
AVAILABLE_OUTGOING_WEBHOOK_INTERFACES = {
GENERIC_INTERFACE: GenericOutgoingWebhookService
GENERIC_INTERFACE: GenericOutgoingWebhookService,
SLACK_INTERFACE: SlackOutgoingWebhookService,
} # type: Dict[Text, Any]
def get_service_interface_class(interface):

View File

@ -0,0 +1,51 @@
from __future__ import absolute_import
from typing import Any, Dict, Text, Tuple, Callable, Mapping, Optional
import json
from requests import Response
from zerver.models import UserProfile, email_to_domain, get_service_profile
from zerver.lib.outgoing_webhook import OutgoingWebhookServiceInterface
class SlackOutgoingWebhookService(OutgoingWebhookServiceInterface):
def __init__(self, base_url, token, user_profile, service_name):
# type: (Text, Text, UserProfile, Text) -> None
super(SlackOutgoingWebhookService, self).__init__(base_url, token, user_profile, service_name)
def process_event(self, event):
# type: (Dict[Text, Any]) -> Tuple[Dict[str, Any], Any]
rest_operation = {'method': 'POST',
'relative_url_path': '',
'base_url': self.base_url,
'request_kwargs': {}}
if event['message']['type'] == 'private':
raise NotImplementedError("Private messaging service not supported.")
service = get_service_profile(event['user_profile_id'], str(self.service_name))
request_data = [("token", self.token),
("team_id", event['message']['sender_realm_str']),
("team_domain", email_to_domain(event['message']['sender_email'])),
("channel_id", event['message']['stream_id']),
("channel_name", event['message']['display_recipient']),
("timestamp", event['message']['timestamp']),
("user_id", event['message']['sender_id']),
("user_name", event['message']['sender_full_name']),
("text", event['command']),
("trigger_word", event['trigger']),
("service_id", service.id),
]
return rest_operation, request_data
def process_success(self, response, event):
# type: (Response, Dict[Text, Any]) -> Optional[str]
response_json = json.loads(response.text)
response_text = ""
if "text" in response_json:
response_text = response_json["text"]
return response_text
def process_failure(self, response, event):
# type: (Response, Dict[Text, Any]) -> Optional[str]
return str(response.text)

View File

@ -8,7 +8,9 @@ import json
from requests.models import Response
from zerver.lib.test_classes import ZulipTestCase
from zerver.models import Service
from zerver.outgoing_webhooks.generic import GenericOutgoingWebhookService
from zerver.outgoing_webhooks.slack import SlackOutgoingWebhookService
class Test_GenericOutgoingWebhookService(ZulipTestCase):
@ -37,7 +39,6 @@ class Test_GenericOutgoingWebhookService(ZulipTestCase):
def test_process_success(self):
# type: () -> None
response = mock.Mock(spec=Response)
response.text = json.dumps({"response_not_required": True})
success_response = self.handler.process_success(response, self.event)
@ -57,3 +58,68 @@ class Test_GenericOutgoingWebhookService(ZulipTestCase):
response.text = 'test_content'
success_response = self.handler.process_failure(response, self.event)
self.assertEqual(success_response, 'test_content')
mock_service = Service()
class Test_SlackOutgoingWebhookService(ZulipTestCase):
def setUp(self):
# type: () -> None
self.event = {
u'command': '@**test**',
u'user_profile_id': 12,
u'service_name': 'test-service',
u'trigger': 'mention',
u'message': {
'content': 'test_content',
'type': 'stream',
'sender_realm_str': 'zulip',
'sender_email': 'sampleuser@zulip.com',
'stream_id': '123',
'display_recipient': 'integrations',
'timestamp': 123456,
'sender_id': 21,
'sender_full_name': 'Sample User',
}
}
self.handler = SlackOutgoingWebhookService(base_url='http://example.domain.com',
token="abcdef",
user_profile=None,
service_name='test-service')
@mock.patch('zerver.outgoing_webhooks.slack.get_service_profile', return_value=mock_service)
def test_process_event(self, mock_get_service_profile):
# type: (mock.Mock) -> None
rest_operation, request_data = self.handler.process_event(self.event)
self.assertEqual(rest_operation['base_url'], 'http://example.domain.com')
self.assertEqual(rest_operation['method'], 'POST')
self.assertEqual(request_data[0][1], "abcdef") # token
self.assertEqual(request_data[1][1], "zulip") # team_id
self.assertEqual(request_data[2][1], "zulip.com") # team_domain
self.assertEqual(request_data[3][1], "123") # channel_id
self.assertEqual(request_data[4][1], "integrations") # channel_name
self.assertEqual(request_data[5][1], 123456) # timestamp
self.assertEqual(request_data[6][1], 21) # user_id
self.assertEqual(request_data[7][1], "Sample User") # user_name
self.assertEqual(request_data[8][1], "@**test**") # text
self.assertEqual(request_data[9][1], "mention") # trigger_word
self.assertEqual(request_data[10][1], mock_service.id) # service_id
def test_process_success(self):
# type: () -> None
response = mock.Mock(spec=Response)
response.text = json.dumps({"response_not_required": True})
success_response = self.handler.process_success(response, self.event)
self.assertEqual(success_response, "")
response.text = json.dumps({"text": 'test_content'})
success_response = self.handler.process_success(response, self.event)
self.assertEqual(success_response, 'test_content')
def test_process_failure(self):
# type: () -> None
response = mock.Mock(spec=Response)
response.text = 'test_content'
success_response = self.handler.process_failure(response, self.event)
self.assertEqual(success_response, 'test_content')