FEATURE: Enable optional support for threading slack posts (#38)
When creating a new Discourse post from slack with the `post` feature, record the slack `ts` thread ID for the resulting topic post using an HTML comment to pass the `ts` through. When notifying slack of new Discourse posts, record the slack `ts` thread ID in the post's topic if it has not yet been recorded. (Normally, this will be done for the topic post, except where notifications are being posted for old topics before this feature was created.) Add a new rule filter `thread` which posts threaded responses to slack if there is a `ts` recorded for the post topic. Modify the `trigger_notifications` interface to enable other integrations to implement similar functionality. Present the `thread` rule in the help text and admin UI only for the slack providers. https://meta.discourse.org/t/optionally-threading-posts-to-parent-topic-in-slack-integration/150759
This commit is contained in:
@@ -34,7 +34,7 @@ class DiscourseChat::ChatController < ApplicationController
|
||||
|
||||
post = Topic.find(topic_id.to_i).posts.first
|
||||
|
||||
provider.trigger_notification(post, channel)
|
||||
provider.trigger_notification(post, channel, nil)
|
||||
|
||||
render json: success_json
|
||||
rescue Discourse::InvalidParameters, ActiveRecord::RecordNotFound => e
|
||||
|
||||
@@ -13,7 +13,7 @@ module DiscourseChat
|
||||
error_text = I18n.t("chat_integration.provider.#{provider}.parse_error")
|
||||
|
||||
case cmd
|
||||
when "watch", "follow", "mute"
|
||||
when "thread", "watch", "follow", "mute"
|
||||
return error_text if tokens.empty?
|
||||
# If the first token in the command is a tag, this rule applies to all categories
|
||||
category_name = tokens[0].start_with?('tag:') ? nil : tokens.shift
|
||||
|
||||
+4
-3
@@ -31,15 +31,16 @@ class DiscourseChat::Rule < DiscourseChat::PluginModel
|
||||
"
|
||||
CASE
|
||||
WHEN value::json->>'filter' = 'mute' THEN 1
|
||||
WHEN value::json->>'filter' = 'watch' THEN 2
|
||||
WHEN value::json->>'filter' = 'follow' THEN 3
|
||||
WHEN value::json->>'filter' = 'thread' THEN 2
|
||||
WHEN value::json->>'filter' = 'watch' THEN 3
|
||||
WHEN value::json->>'filter' = 'follow' THEN 4
|
||||
END
|
||||
")
|
||||
}
|
||||
|
||||
after_initialize :init_filter
|
||||
|
||||
validates :filter, inclusion: { in: %w(watch follow mute),
|
||||
validates :filter, inclusion: { in: %w(thread watch follow mute),
|
||||
message: "%{value} is not a valid filter" }
|
||||
|
||||
validates :type, inclusion: { in: %w(normal group_message group_mention),
|
||||
|
||||
@@ -52,7 +52,7 @@ module DiscourseChat
|
||||
|
||||
# Sort by order of precedence
|
||||
t_prec = { 'group_message' => 0, 'group_mention' => 1, 'normal' => 2 } # Group things win
|
||||
f_prec = { 'mute' => 0, 'watch' => 1, 'follow' => 2 } #(mute always wins; watch beats follow)
|
||||
f_prec = { 'mute' => 0, 'thread' => 1, 'watch' => 2, 'follow' => 3 } #(mute always wins; thread beats watch beats follow)
|
||||
sort_func = proc { |a, b| [t_prec[a.type], f_prec[a.filter]] <=> [t_prec[b.type], f_prec[b.filter]] }
|
||||
matching_rules = matching_rules.sort(&sort_func)
|
||||
|
||||
@@ -80,7 +80,7 @@ module DiscourseChat
|
||||
next unless is_enabled = ::DiscourseChat::Provider.is_enabled(provider)
|
||||
|
||||
begin
|
||||
provider.trigger_notification(post, channel)
|
||||
provider.trigger_notification(post, channel, rule)
|
||||
channel.update_attribute('error_key', nil) if channel.error_key
|
||||
rescue => e
|
||||
if e.class == (DiscourseChat::ProviderError) && e.info.key?(:error_key) && !e.info[:error_key].nil?
|
||||
|
||||
@@ -6,23 +6,38 @@ import {
|
||||
} from "discourse-common/utils/decorators";
|
||||
|
||||
export default RestModel.extend({
|
||||
available_filters: [
|
||||
{
|
||||
id: "watch",
|
||||
name: I18n.t("chat_integration.filter.watch"),
|
||||
icon: "exclamation-circle"
|
||||
},
|
||||
{
|
||||
id: "follow",
|
||||
name: I18n.t("chat_integration.filter.follow"),
|
||||
icon: "circle"
|
||||
},
|
||||
{
|
||||
id: "mute",
|
||||
name: I18n.t("chat_integration.filter.mute"),
|
||||
icon: "times-circle"
|
||||
@computed("channel.provider")
|
||||
available_filters(provider) {
|
||||
const available = [];
|
||||
|
||||
if (provider === "slack") {
|
||||
available.push({
|
||||
id: "thread",
|
||||
name: I18n.t("chat_integration.filter.thread"),
|
||||
icon: "chevron-right"
|
||||
});
|
||||
}
|
||||
],
|
||||
|
||||
available.push(
|
||||
{
|
||||
id: "watch",
|
||||
name: I18n.t("chat_integration.filter.watch"),
|
||||
icon: "exclamation-circle"
|
||||
},
|
||||
{
|
||||
id: "follow",
|
||||
name: I18n.t("chat_integration.filter.follow"),
|
||||
icon: "circle"
|
||||
},
|
||||
{
|
||||
id: "mute",
|
||||
name: I18n.t("chat_integration.filter.mute"),
|
||||
icon: "times-circle"
|
||||
}
|
||||
);
|
||||
|
||||
return available;
|
||||
},
|
||||
|
||||
available_types: [
|
||||
{ id: "normal", name: I18n.t("chat_integration.type.normal") },
|
||||
|
||||
@@ -18,7 +18,7 @@ export default DiscourseRoute.extend({
|
||||
"rules",
|
||||
channel.rules.map(rule => {
|
||||
rule = this.store.createRecord("rule", rule);
|
||||
rule.channel = channel;
|
||||
rule.set("channel", channel);
|
||||
return rule;
|
||||
})
|
||||
);
|
||||
|
||||
@@ -36,6 +36,7 @@ en:
|
||||
mute: 'Mute'
|
||||
follow: 'First post only'
|
||||
watch: 'All posts and replies'
|
||||
thread: 'All posts with threaded replies'
|
||||
rule_table:
|
||||
filter: "Filter"
|
||||
category: "Category"
|
||||
|
||||
@@ -122,8 +122,9 @@ en:
|
||||
tag: "The *%{name}* tag cannot be found."
|
||||
category: "The *%{name}* category cannot be found. Available categories: *%{list}*"
|
||||
help: |
|
||||
*New rule:* `/discourse [watch|follow|mute] [category] [tag:name]`
|
||||
*New rule:* `/discourse [thread|watch|follow|mute] [category] [tag:name]`
|
||||
(you must specify a rule type and at least one category or tag)
|
||||
- *thread* – notify this channel for new topics, thread replies if possible
|
||||
- *watch* – notify this channel for new topics and new replies
|
||||
- *follow* – notify this channel for new topics
|
||||
- *mute* – block notifications to this channel
|
||||
|
||||
@@ -63,7 +63,7 @@ module DiscourseChat
|
||||
message
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
# Adding ?wait=true means that we actually get a success/failure response, rather than returning asynchronously
|
||||
webhook_url = "#{channel.data['webhook_url']}?wait=true"
|
||||
message = generate_discord_message(post)
|
||||
|
||||
@@ -48,7 +48,7 @@ module DiscourseChat::Provider::FlowdockProvider
|
||||
message
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
flow_token = channel.data["flow_token"]
|
||||
message = generate_flowdock_message(post, flow_token)
|
||||
response = send_message("https://api.flowdock.com/messages", message)
|
||||
|
||||
@@ -10,7 +10,7 @@ module DiscourseChat
|
||||
{ key: "webhook_url", regex: '^https://webhooks\.gitter\.im/e/\S+$', unique: true, hidden: true }
|
||||
]
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
message = gitter_message(post)
|
||||
response = Net::HTTP.post_form(URI(channel.data['webhook_url']), message: message)
|
||||
unless response.kind_of? Net::HTTPSuccess
|
||||
|
||||
@@ -71,7 +71,7 @@ module DiscourseChat::Provider::GroupmeProvider
|
||||
end
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
data_package = generate_groupme_message(post)
|
||||
self.send_via_webhook(data_package, channel)
|
||||
end
|
||||
|
||||
@@ -56,7 +56,7 @@ module DiscourseChat
|
||||
message
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
message = generate_matrix_message(post)
|
||||
|
||||
response = send_message(channel.data['room_id'], message)
|
||||
|
||||
@@ -75,7 +75,7 @@ module DiscourseChat
|
||||
message
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
channel_id = channel.data['identifier']
|
||||
message = mattermost_message(post, channel_id)
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ module DiscourseChat::Provider::RocketchatProvider
|
||||
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
channel_id = channel.data['identifier']
|
||||
message = rocketchat_message(post, channel_id)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
module DiscourseChat::Provider::SlackProvider
|
||||
PROVIDER_NAME = "slack".freeze
|
||||
THREAD = "thread".freeze
|
||||
|
||||
PROVIDER_ENABLED_SETTING = :chat_integration_slack_enabled
|
||||
|
||||
@@ -9,6 +10,18 @@ module DiscourseChat::Provider::SlackProvider
|
||||
{ key: "identifier", regex: '^[@#]?\S*$', unique: true }
|
||||
]
|
||||
|
||||
require_dependency 'topic'
|
||||
::Topic.register_custom_field_type(DiscourseChat::Provider::SlackProvider::THREAD, :text)
|
||||
|
||||
class ::Topic
|
||||
def slack_thread_id=(ts)
|
||||
self.custom_fields[DiscourseChat::Provider::SlackProvider::THREAD] = ts
|
||||
end
|
||||
def slack_thread_id
|
||||
self.custom_fields[DiscourseChat::Provider::SlackProvider::THREAD]
|
||||
end
|
||||
end
|
||||
|
||||
def self.excerpt(post, max_length = SiteSetting.chat_integration_slack_excerpt_length)
|
||||
doc = Nokogiri::HTML.fragment(post.excerpt(max_length,
|
||||
remap_emoji: true,
|
||||
@@ -18,7 +31,7 @@ module DiscourseChat::Provider::SlackProvider
|
||||
SlackMessageFormatter.format(doc.to_html)
|
||||
end
|
||||
|
||||
def self.slack_message(post, channel)
|
||||
def self.slack_message(post, channel, filter)
|
||||
display_name = "@#{post.user.username}"
|
||||
full_name = post.user.name || ""
|
||||
|
||||
@@ -56,6 +69,10 @@ module DiscourseChat::Provider::SlackProvider
|
||||
attachments: []
|
||||
}
|
||||
|
||||
if filter == "thread" && thread_ts = topic.slack_thread_id
|
||||
message[:thread_ts] = thread_ts if not thread_ts.nil?
|
||||
end
|
||||
|
||||
summary = {
|
||||
fallback: "#{topic.title} - #{display_name}",
|
||||
author_name: display_name,
|
||||
@@ -79,11 +96,9 @@ module DiscourseChat::Provider::SlackProvider
|
||||
|
||||
response = nil
|
||||
uri = ""
|
||||
record = DiscourseChat.pstore_get("slack_topic_#{post.topic.id}_#{channel}")
|
||||
|
||||
data = {
|
||||
token: SiteSetting.chat_integration_slack_access_token,
|
||||
}
|
||||
# <!--SLACK_CHANNEL_ID=#{@channel_id};SLACK_TS=#{@requested_thread_ts}-->
|
||||
slack_thread_regex = /<!--SLACK_CHANNEL_ID=(\w+);SLACK_TS=([0-9]{10}.[0-9]{6})-->/
|
||||
|
||||
req = Net::HTTP::Post.new(URI('https://slack.com/api/chat.postMessage'))
|
||||
|
||||
@@ -94,6 +109,12 @@ module DiscourseChat::Provider::SlackProvider
|
||||
channel: message[:channel].gsub('#', ''),
|
||||
attachments: message[:attachments].to_json
|
||||
}
|
||||
if message.key?(:thread_ts)
|
||||
data[:thread_ts] = message[:thread_ts]
|
||||
elsif match = slack_thread_regex.match(post.raw)
|
||||
post.topic.slack_thread_id = match.captures[1]
|
||||
post.topic.save_custom_fields
|
||||
end
|
||||
|
||||
req.set_form_data(data)
|
||||
|
||||
@@ -114,6 +135,12 @@ module DiscourseChat::Provider::SlackProvider
|
||||
raise ::DiscourseChat::ProviderError.new info: { error_key: error_key, request: uri, response_code: response.code, response_body: response.body }
|
||||
end
|
||||
|
||||
ts = json["ts"]
|
||||
if !ts.nil? && post.topic.slack_thread_id.nil?
|
||||
post.topic.slack_thread_id = ts
|
||||
post.topic.save_custom_fields
|
||||
end
|
||||
|
||||
response
|
||||
end
|
||||
|
||||
@@ -137,9 +164,10 @@ module DiscourseChat::Provider::SlackProvider
|
||||
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
channel_id = channel.data['identifier']
|
||||
message = slack_message(post, channel_id)
|
||||
filter = rule.nil? ? "" : rule.filter
|
||||
message = slack_message(post, channel_id, filter)
|
||||
|
||||
if SiteSetting.chat_integration_slack_access_token.empty?
|
||||
self.send_via_webhook(message)
|
||||
|
||||
@@ -118,6 +118,10 @@ module DiscourseChat::Provider::SlackProvider
|
||||
post_content << "[#{username}]: #{url}\n"
|
||||
end
|
||||
|
||||
if not @requested_thread_ts.nil?
|
||||
post_content << "<!--SLACK_CHANNEL_ID=#{@channel_id};SLACK_TS=#{@requested_thread_ts}-->"
|
||||
end
|
||||
|
||||
post_content
|
||||
end
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ module DiscourseChat
|
||||
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
chat_id = channel.data['chat_id']
|
||||
|
||||
message = {
|
||||
|
||||
@@ -46,7 +46,7 @@ module DiscourseChat
|
||||
}
|
||||
end
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
|
||||
stream = channel.data['stream']
|
||||
subject = channel.data['subject']
|
||||
|
||||
@@ -11,6 +11,7 @@ enabled_site_setting :chat_integration_enabled
|
||||
register_asset "stylesheets/chat-integration-admin.scss"
|
||||
|
||||
register_svg_icon "rocket" if respond_to?(:register_svg_icon)
|
||||
register_svg_icon "fa-arrow-circle-o-right" if respond_to?(:register_svg_icon)
|
||||
|
||||
# Site setting validators must be loaded before initialize
|
||||
require_relative "lib/discourse_chat/provider/slack/slack_enabled_setting_validator"
|
||||
|
||||
@@ -10,7 +10,7 @@ RSpec.shared_context "dummy provider" do
|
||||
@@sent_messages = []
|
||||
@@raise_exception = nil
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
if @@raise_exception
|
||||
raise @@raise_exception
|
||||
end
|
||||
@@ -50,7 +50,7 @@ RSpec.shared_context "validated dummy provider" do
|
||||
|
||||
@@sent_messages = []
|
||||
|
||||
def self.trigger_notification(post, channel)
|
||||
def self.trigger_notification(post, channel, rule)
|
||||
@@sent_messages.push(post: post.id, channel: channel)
|
||||
end
|
||||
|
||||
|
||||
@@ -32,7 +32,12 @@ RSpec.describe DiscourseChat::Manager do
|
||||
expect(rule.tags).to eq(nil)
|
||||
end
|
||||
|
||||
it 'should work with all three filter types' do
|
||||
it 'should work with all four filter types' do
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['thread', category.slug])
|
||||
|
||||
rule = DiscourseChat::Rule.all.first
|
||||
expect(rule.filter).to eq('thread')
|
||||
|
||||
response = DiscourseChat::Helper.process_command(chan1, ['watch', category.slug])
|
||||
|
||||
rule = DiscourseChat::Rule.all.first
|
||||
|
||||
@@ -14,7 +14,7 @@ RSpec.describe DiscourseChat::Provider::DiscordProvider do
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, 'https://discordapp.com/api/webhooks/1234/abcd?wait=true').to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
@@ -22,14 +22,14 @@ RSpec.describe DiscourseChat::Provider::DiscordProvider do
|
||||
stub1 = stub_request(:post, 'https://discordapp.com/api/webhooks/1234/abcd?wait=true')
|
||||
.with(body: hash_including(embeds: [hash_including(author: hash_including(url: /^https?:\/\//))]))
|
||||
.to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, "https://discordapp.com/api/webhooks/1234/abcd?wait=true").to_return(status: 400)
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
|
||||
@@ -14,14 +14,14 @@ RSpec.describe DiscourseChat::Provider::FlowdockProvider do
|
||||
|
||||
it 'sends a request' do
|
||||
stub1 = stub_request(:post, "https://api.flowdock.com/messages").to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, "https://api.flowdock.com/messages").to_return(status: 404, body: "{ \"error\": \"Not Found\"}")
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,14 +14,14 @@ RSpec.describe DiscourseChat::Provider::GitterProvider do
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(body: "OK")
|
||||
described_class.trigger_notification(post, chan1)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, chan1.data['webhook_url']).to_return(status: 404, body: "{ \"error\": \"Not Found\"}")
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,14 +15,14 @@ RSpec.describe DiscourseChat::Provider::GroupmeProvider do
|
||||
|
||||
it 'sends a request' do
|
||||
stub1 = stub_request(:post, "https://api.groupme.com/v3/bots/post").to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, "https://api.groupme.com/v3/bots/post").to_return(status: 404, body: "{ \"error\": \"Not Found\"}")
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,14 +15,14 @@ RSpec.describe DiscourseChat::Provider::MatrixProvider do
|
||||
|
||||
it 'sends the message' do
|
||||
stub1 = stub_request(:put, %r{https://matrix.org/_matrix/client/r0/rooms/!blah:matrix.org/send/m.room.message/*}).to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:put, %r{https://matrix.org/_matrix/client/r0/rooms/!blah:matrix.org/send/m.room.message/*}).to_return(status: 400, body: '{"errmsg":"M_UNKNOWN"}')
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ RSpec.describe DiscourseChat::Provider::MattermostProvider do
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, 'https://mattermost.blah/hook/abcd').to_return(status: 200)
|
||||
described_class.trigger_notification(post, chan1)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
@@ -40,7 +40,7 @@ RSpec.describe DiscourseChat::Provider::MattermostProvider do
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, "https://mattermost.blah/hook/abcd").to_return(status: 500, body: "error")
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
|
||||
@@ -15,14 +15,14 @@ RSpec.describe DiscourseChat::Provider::RocketchatProvider do
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, 'https://example.com/abcd').to_return(body: "{\"success\":true}")
|
||||
described_class.trigger_notification(post, chan1)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, 'https://example.com/abcd').to_return(status: 400, body: "{}")
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
|
||||
@@ -75,36 +75,65 @@ RSpec.describe DiscourseChat::Provider::SlackProvider do
|
||||
|
||||
it 'sends a webhook request' do
|
||||
stub1 = stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(body: "success")
|
||||
described_class.trigger_notification(post, chan1)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
stub1 = stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(status: 400, body: "error")
|
||||
expect(stub1).to have_been_requested.times(0)
|
||||
expect { described_class.trigger_notification(post, chan1) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect(stub1).to have_been_requested.once
|
||||
end
|
||||
|
||||
describe 'with api token' do
|
||||
before do
|
||||
SiteSetting.chat_integration_slack_access_token = "magic"
|
||||
@ts = "#{Time.now.to_i}.012345"
|
||||
@stub1 = stub_request(:post, SiteSetting.chat_integration_slack_outbound_webhook_url).to_return(body: "success")
|
||||
@stub2 = stub_request(:post, %r{https://slack.com/api/chat.postMessage}).to_return(body: "{\"ok\":true, \"ts\": \"#{Time.now.to_i}.012345\", \"message\": {\"attachments\": [], \"username\":\"blah\", \"text\":\"blah2\"} }", headers: { 'Content-Type' => 'application/json' })
|
||||
@stub3 = stub_request(:post, %r{https://slack.com/api/chat.update}).to_return(body: '{"ok":true, "ts": "some_message_id"}', headers: { 'Content-Type' => 'application/json' })
|
||||
@thread_stub = stub_request(:post, %r{https://slack.com/api/chat.postMessage}).with(body: hash_including("thread_ts" => @ts)).to_return(body: "{\"ok\":true, \"ts\": \"12345.67890\", \"message\": {\"attachments\": [], \"username\":\"blah\", \"text\":\"blah2\"} }", headers: { 'Content-Type' => 'application/json' })
|
||||
@stub2 = stub_request(:post, %r{https://slack.com/api/chat.postMessage}).to_return(body: "{\"ok\":true, \"ts\": \"#{@ts}\", \"message\": {\"attachments\": [], \"username\":\"blah\", \"text\":\"blah2\"} }", headers: { 'Content-Type' => 'application/json' })
|
||||
@channel = DiscourseChat::Channel.create(provider: 'dummy')
|
||||
end
|
||||
|
||||
it 'sends an api request' do
|
||||
expect(@stub2).to have_been_requested.times(0)
|
||||
expect(@thread_stub).to have_been_requested.times(0)
|
||||
|
||||
described_class.trigger_notification(post, chan1)
|
||||
described_class.trigger_notification(post, chan1, nil)
|
||||
expect(@stub1).to have_been_requested.times(0)
|
||||
expect(@stub2).to have_been_requested.once
|
||||
expect(post.topic.slack_thread_id).to eq(@ts)
|
||||
expect(@thread_stub).to have_been_requested.times(0)
|
||||
end
|
||||
|
||||
it 'sends thread id for thread' do
|
||||
expect(@thread_stub).to have_been_requested.times(0)
|
||||
|
||||
rule = DiscourseChat::Rule.create(channel: @channel, filter: "thread")
|
||||
post.topic.slack_thread_id = @ts
|
||||
|
||||
described_class.trigger_notification(post, chan1, rule)
|
||||
expect(@thread_stub).to have_been_requested.once
|
||||
end
|
||||
|
||||
it 'recognizes slack thread ts in comment' do
|
||||
post.update!(cooked: "cooked", raw: <<~RAW
|
||||
My fingers are typing words that improve `raw_quality`
|
||||
<!--SLACK_CHANNEL_ID=UIGNOREFORNOW;SLACK_TS=1501801629.052212-->
|
||||
RAW
|
||||
)
|
||||
|
||||
rule = DiscourseChat::Rule.create(channel: @channel, filter: "thread")
|
||||
post.topic.slack_thread_id = nil
|
||||
|
||||
described_class.trigger_notification(post, chan1, rule)
|
||||
expect(post.topic.slack_thread_id).to eq('1501801629.052212')
|
||||
end
|
||||
|
||||
it 'handles errors correctly' do
|
||||
@stub2 = stub_request(:post, %r{https://slack.com/api/chat.postMessage}).to_return(body: "{\"ok\":false }", headers: { 'Content-Type' => 'application/json' })
|
||||
expect { described_class.trigger_notification(post, chan1) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect { described_class.trigger_notification(post, chan1, nil) }.to raise_exception(::DiscourseChat::ProviderError)
|
||||
expect(@stub2).to have_been_requested.once
|
||||
end
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user