mirror of https://github.com/zulip/zulip.git
Add outgoing webhook interface for Slack.
This commit is contained in:
parent
e5be29bc3c
commit
4c50c4fc76
|
@ -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):
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
|
@ -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')
|
||||
|
|
Loading…
Reference in New Issue