Add threading to slack notification module (#47333)
* Add threading to slack notification module * Fix sanity check * Change thread_ts to thread_id
This commit is contained in:
parent
9949629e5a
commit
43bbc505a1
2 changed files with 100 additions and 3 deletions
|
@ -55,6 +55,10 @@ options:
|
|||
channel:
|
||||
description:
|
||||
- Channel to send the message to. If absent, the message goes to the channel selected for the I(token).
|
||||
thread_id:
|
||||
version_added: 2.8
|
||||
description:
|
||||
- Optional. Timestamp of message to thread this message to as a float. https://api.slack.com/docs/message-threading
|
||||
username:
|
||||
description:
|
||||
- This is the sender of the message.
|
||||
|
@ -113,6 +117,7 @@ EXAMPLES = """
|
|||
token: thetoken/generatedby/slack
|
||||
msg: '{{ inventory_hostname }} completed'
|
||||
channel: '#ansible'
|
||||
thread_id: 1539917263.000100
|
||||
username: 'Ansible on {{ inventory_hostname }}'
|
||||
icon_url: http://www.example.com/some-image-file.png
|
||||
link_names: 0
|
||||
|
@ -173,7 +178,8 @@ def escape_quotes(text):
|
|||
return "".join(escape_table.get(c, c) for c in text)
|
||||
|
||||
|
||||
def build_payload_for_slack(module, text, channel, username, icon_url, icon_emoji, link_names, parse, color, attachments):
|
||||
def build_payload_for_slack(module, text, channel, thread_id, username, icon_url, icon_emoji, link_names,
|
||||
parse, color, attachments):
|
||||
payload = {}
|
||||
if color == "normal" and text is not None:
|
||||
payload = dict(text=escape_quotes(text))
|
||||
|
@ -185,6 +191,8 @@ def build_payload_for_slack(module, text, channel, username, icon_url, icon_emoj
|
|||
payload['channel'] = channel
|
||||
else:
|
||||
payload['channel'] = '#' + channel
|
||||
if thread_id is not None:
|
||||
payload['thread_ts'] = thread_id
|
||||
if username is not None:
|
||||
payload['username'] = username
|
||||
if icon_emoji is not None:
|
||||
|
@ -228,7 +236,8 @@ def do_notify_slack(module, domain, token, payload):
|
|||
slack_incoming_webhook = SLACK_INCOMING_WEBHOOK % (token)
|
||||
else:
|
||||
if not domain:
|
||||
module.fail_json(msg="Slack has updated its webhook API. You need to specify a token of the form XXXX/YYYY/ZZZZ in your playbook")
|
||||
module.fail_json(msg="Slack has updated its webhook API. You need to specify a token of the form "
|
||||
"XXXX/YYYY/ZZZZ in your playbook")
|
||||
slack_incoming_webhook = OLD_SLACK_INCOMING_WEBHOOK % (domain, token)
|
||||
|
||||
headers = {
|
||||
|
@ -249,6 +258,7 @@ def main():
|
|||
token=dict(type='str', required=True, no_log=True),
|
||||
msg=dict(type='str', required=False, default=None),
|
||||
channel=dict(type='str', default=None),
|
||||
thread_id=dict(type='float', default=None),
|
||||
username=dict(type='str', default='Ansible'),
|
||||
icon_url=dict(type='str', default='https://www.ansible.com/favicon.ico'),
|
||||
icon_emoji=dict(type='str', default=None),
|
||||
|
@ -264,6 +274,7 @@ def main():
|
|||
token = module.params['token']
|
||||
text = module.params['msg']
|
||||
channel = module.params['channel']
|
||||
thread_id = module.params['thread_id']
|
||||
username = module.params['username']
|
||||
icon_url = module.params['icon_url']
|
||||
icon_emoji = module.params['icon_emoji']
|
||||
|
@ -272,7 +283,8 @@ def main():
|
|||
color = module.params['color']
|
||||
attachments = module.params['attachments']
|
||||
|
||||
payload = build_payload_for_slack(module, text, channel, username, icon_url, icon_emoji, link_names, parse, color, attachments)
|
||||
payload = build_payload_for_slack(module, text, channel, thread_id, username, icon_url, icon_emoji, link_names,
|
||||
parse, color, attachments)
|
||||
do_notify_slack(module, domain, token, payload)
|
||||
|
||||
module.exit_json(msg="OK")
|
||||
|
|
85
test/units/modules/notification/test_slack.py
Normal file
85
test/units/modules/notification/test_slack.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
import json
|
||||
import pytest
|
||||
from units.compat.mock import patch
|
||||
from ansible.modules.notification import slack
|
||||
from units.modules.utils import AnsibleExitJson, AnsibleFailJson, ModuleTestCase, set_module_args
|
||||
from ansible import module_utils
|
||||
|
||||
|
||||
class TestSlackModule(ModuleTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestSlackModule, self).setUp()
|
||||
self.module = slack
|
||||
|
||||
def tearDown(self):
|
||||
super(TestSlackModule, self).tearDown()
|
||||
|
||||
@pytest.fixture
|
||||
def fetch_url_mock(self, mocker):
|
||||
return mocker.patch('ansible.module_utils.notification.slack.fetch_url')
|
||||
|
||||
def test_without_required_parameters(self):
|
||||
"""Failure must occurs when all parameters are missing"""
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
set_module_args({})
|
||||
self.module.main()
|
||||
|
||||
def test_invalid_old_token(self):
|
||||
"""Failure if there is an old style token"""
|
||||
set_module_args({
|
||||
'token': 'test',
|
||||
})
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
self.module.main()
|
||||
|
||||
def test_sucessful_message(self):
|
||||
"""tests sending a message. This is example 1 from the docs"""
|
||||
set_module_args({
|
||||
'token': 'XXXX/YYYY/ZZZZ',
|
||||
'msg': 'test'
|
||||
})
|
||||
|
||||
with patch.object(slack, "fetch_url") as fetch_url_mock:
|
||||
fetch_url_mock.return_value = (None, {"status": 200})
|
||||
with self.assertRaises(AnsibleExitJson):
|
||||
self.module.main()
|
||||
|
||||
self.assertTrue(fetch_url_mock.call_count, 1)
|
||||
call_data = json.loads(fetch_url_mock.call_args[1]['data'])
|
||||
assert call_data['username'] == "Ansible"
|
||||
assert call_data['text'] == "test"
|
||||
assert fetch_url_mock.call_args[1]['url'] == "https://hooks.slack.com/services/XXXX/YYYY/ZZZZ"
|
||||
|
||||
def test_failed_message(self):
|
||||
"""tests failing to send a message"""
|
||||
|
||||
set_module_args({
|
||||
'token': 'XXXX/YYYY/ZZZZ',
|
||||
'msg': 'test'
|
||||
})
|
||||
|
||||
with patch.object(slack, "fetch_url") as fetch_url_mock:
|
||||
fetch_url_mock.return_value = (None, {"status": 404, 'msg': 'test'})
|
||||
with self.assertRaises(AnsibleFailJson):
|
||||
self.module.main()
|
||||
|
||||
def test_message_with_thread(self):
|
||||
"""tests sending a message with a thread"""
|
||||
set_module_args({
|
||||
'token': 'XXXX/YYYY/ZZZZ',
|
||||
'msg': 'test',
|
||||
'thread_id': 100.00
|
||||
})
|
||||
|
||||
with patch.object(slack, "fetch_url") as fetch_url_mock:
|
||||
fetch_url_mock.return_value = (None, {"status": 200})
|
||||
with self.assertRaises(AnsibleExitJson):
|
||||
self.module.main()
|
||||
|
||||
self.assertTrue(fetch_url_mock.call_count, 1)
|
||||
call_data = json.loads(fetch_url_mock.call_args[1]['data'])
|
||||
assert call_data['username'] == "Ansible"
|
||||
assert call_data['text'] == "test"
|
||||
assert call_data['thread_ts'] == 100.00
|
||||
assert fetch_url_mock.call_args[1]['url'] == "https://hooks.slack.com/services/XXXX/YYYY/ZZZZ"
|
Loading…
Reference in a new issue