mirror of
https://github.com/discourse/discourse.git
synced 2024-11-26 22:03:45 +08:00
FIX: Payload for webhooks should be current as of the time the event was triggered.
https://meta.discourse.org/t/group-category-tag-user-deleted-webhooks-not-firing/87752
This commit is contained in:
parent
cba3942850
commit
bf84037f79
|
@ -2,8 +2,14 @@ require 'excon'
|
||||||
|
|
||||||
module Jobs
|
module Jobs
|
||||||
class EmitWebHookEvent < Jobs::Base
|
class EmitWebHookEvent < Jobs::Base
|
||||||
|
PING_EVENT = 'ping'.freeze
|
||||||
|
|
||||||
def execute(args)
|
def execute(args)
|
||||||
[:web_hook_id, :event_type].each do |key|
|
%i{
|
||||||
|
web_hook_id
|
||||||
|
event_type
|
||||||
|
payload
|
||||||
|
}.each do |key|
|
||||||
raise Discourse::InvalidParameters.new(key) unless args[key].present?
|
raise Discourse::InvalidParameters.new(key) unless args[key].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -19,8 +25,7 @@ module Jobs
|
||||||
return if web_hook.category_ids.present? && (!args[:category_id].present? ||
|
return if web_hook.category_ids.present? && (!args[:category_id].present? ||
|
||||||
!web_hook.category_ids.include?(args[:category_id]))
|
!web_hook.category_ids.include?(args[:category_id]))
|
||||||
|
|
||||||
event_type = args[:event_type].to_s
|
args[:payload] = JSON.parse(args[:payload])
|
||||||
return unless self.send("setup_#{event_type}", args)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
web_hook_request(args, web_hook)
|
web_hook_request(args, web_hook)
|
||||||
|
@ -32,50 +37,8 @@ module Jobs
|
||||||
Guardian.new(Discourse.system_user)
|
Guardian.new(Discourse.system_user)
|
||||||
end
|
end
|
||||||
|
|
||||||
def setup_post(args)
|
|
||||||
post = Post.with_deleted.find_by(id: args[:post_id])
|
|
||||||
return if post.blank?
|
|
||||||
args[:payload] = WebHookPostSerializer.new(post, scope: guardian, root: false).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup_topic(args)
|
|
||||||
topic_view = TopicView.new(args[:topic_id], Discourse.system_user)
|
|
||||||
return if topic_view.blank?
|
|
||||||
args[:payload] = WebHookTopicViewSerializer.new(topic_view, scope: guardian, root: false).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup_user(args)
|
|
||||||
user = User.find_by(id: args[:user_id])
|
|
||||||
return if user.blank?
|
|
||||||
args[:payload] = WebHookUserSerializer.new(user, scope: guardian, root: false).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup_group(args)
|
|
||||||
group = Group.find_by(id: args[:group_id])
|
|
||||||
return if group.blank?
|
|
||||||
args[:payload] = WebHookGroupSerializer.new(group, scope: guardian, root: false).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup_category(args)
|
|
||||||
category = Category.find_by(id: args[:category_id])
|
|
||||||
return if category.blank?
|
|
||||||
args[:payload] = WebHookCategorySerializer.new(category, scope: guardian, root: false).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup_tag(args)
|
|
||||||
tag = Tag.find_by(id: args[:tag_id])
|
|
||||||
return if tag.blank?
|
|
||||||
args[:payload] = TagSerializer.new(tag, scope: guardian, root: false).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def setup_flag(args)
|
|
||||||
flag = PostAction.find_by(id: args[:flag_id])
|
|
||||||
return if flag.blank?
|
|
||||||
args[:payload] = WebHookFlagSerializer.new(flag, scope: guardian, root: false).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def ping_event?(event_type)
|
def ping_event?(event_type)
|
||||||
event_type.to_s == 'ping'.freeze
|
PING_EVENT == event_type.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_web_hook_body(args, web_hook)
|
def build_web_hook_body(args, web_hook)
|
||||||
|
@ -89,7 +52,6 @@ module Jobs
|
||||||
end
|
end
|
||||||
|
|
||||||
new_body = Plugin::Filter.apply(:after_build_web_hook_body, self, body)
|
new_body = Plugin::Filter.apply(:after_build_web_hook_body, self, body)
|
||||||
|
|
||||||
MultiJson.dump(new_body)
|
MultiJson.dump(new_body)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -120,7 +82,7 @@ module Jobs
|
||||||
'Content-Length' => body.bytesize,
|
'Content-Length' => body.bytesize,
|
||||||
'Content-Type' => content_type,
|
'Content-Type' => content_type,
|
||||||
'Host' => uri.host,
|
'Host' => uri.host,
|
||||||
'User-Agent' => "Discourse/" + Discourse::VERSION::STRING,
|
'User-Agent' => "Discourse/#{Discourse::VERSION::STRING}",
|
||||||
'X-Discourse-Instance' => Discourse.base_url,
|
'X-Discourse-Instance' => Discourse.base_url,
|
||||||
'X-Discourse-Event-Id' => web_hook_event.id,
|
'X-Discourse-Event-Id' => web_hook_event.id,
|
||||||
'X-Discourse-Event-Type' => args[:event_type]
|
'X-Discourse-Event-Type' => args[:event_type]
|
||||||
|
@ -129,7 +91,7 @@ module Jobs
|
||||||
headers['X-Discourse-Event'] = args[:event_name].to_s if args[:event_name].present?
|
headers['X-Discourse-Event'] = args[:event_name].to_s if args[:event_name].present?
|
||||||
|
|
||||||
if web_hook.secret.present?
|
if web_hook.secret.present?
|
||||||
headers['X-Discourse-Event-Signature'] = "sha256=" + OpenSSL::HMAC.hexdigest("sha256", web_hook.secret, body)
|
headers['X-Discourse-Event-Signature'] = "sha256=#{OpenSSL::HMAC.hexdigest("sha256", web_hook.secret, body)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
now = Time.zone.now
|
now = Time.zone.now
|
||||||
|
|
|
@ -30,30 +30,79 @@ class WebHook < ActiveRecord::Base
|
||||||
[WebHookEventType.find(WebHookEventType::POST)]
|
[WebHookEventType.find(WebHookEventType::POST)]
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.find_by_type(type)
|
def strip_url
|
||||||
|
self.payload_url = (payload_url || "").strip.presence
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.active_web_hooks(type)
|
||||||
WebHook.where(active: true)
|
WebHook.where(active: true)
|
||||||
.joins(:web_hook_event_types)
|
.joins(:web_hook_event_types)
|
||||||
.where("web_hooks.wildcard_web_hook = ? OR web_hook_event_types.name = ?", true, type.to_s)
|
.where("web_hooks.wildcard_web_hook = ? OR web_hook_event_types.name = ?", true, type.to_s)
|
||||||
.uniq
|
.uniq
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.enqueue_hooks(type, opts = {})
|
def self.enqueue_hooks(type, opts = {}, web_hooks = nil)
|
||||||
find_by_type(type).each do |w|
|
(web_hooks || active_web_hooks(type)).each do |web_hook|
|
||||||
Jobs.enqueue(:emit_web_hook_event, opts.merge(web_hook_id: w.id, event_type: type.to_s))
|
Jobs.enqueue(:emit_web_hook_event, opts.merge(
|
||||||
|
web_hook_id: web_hook.id, event_type: type.to_s
|
||||||
|
))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.enqueue_topic_hooks(event, topic, user = nil)
|
def self.enqueue_object_hooks(type, object, event, serializer = nil)
|
||||||
WebHook.enqueue_hooks(:topic, topic_id: topic.id, category_id: topic&.category_id, event_name: event.to_s)
|
Scheduler::Defer.later("Enqueue User Webhook") do
|
||||||
|
web_hooks = active_web_hooks(type)
|
||||||
|
return if web_hooks.empty?
|
||||||
|
serializer ||= "WebHook#{type.capitalize}Serializer".constantize
|
||||||
|
|
||||||
|
WebHook.enqueue_hooks(type, {
|
||||||
|
event_name: event.to_s,
|
||||||
|
payload: serializer.new(object,
|
||||||
|
scope: self.guardian,
|
||||||
|
root: false
|
||||||
|
).to_json
|
||||||
|
}, web_hooks)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.enqueue_post_hooks(event, post, user = nil)
|
def self.enqueue_topic_hooks(event, topic)
|
||||||
WebHook.enqueue_hooks(:post, post_id: post.id, category_id: post&.topic&.category_id, event_name: event.to_s)
|
Scheduler::Defer.later("Enqueue Topic Webhook") do
|
||||||
|
web_hooks = active_web_hooks('topic')
|
||||||
|
return if web_hooks.empty?
|
||||||
|
topic_view = TopicView.new(topic.id, Discourse.system_user)
|
||||||
|
|
||||||
|
WebHook.enqueue_hooks(:topic, {
|
||||||
|
category_id: topic&.category_id,
|
||||||
|
event_name: event.to_s,
|
||||||
|
payload: WebHookTopicViewSerializer.new(topic_view,
|
||||||
|
scope: self.guardian,
|
||||||
|
root: false
|
||||||
|
).to_json
|
||||||
|
}, web_hooks)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def strip_url
|
def self.enqueue_post_hooks(event, post)
|
||||||
self.payload_url = (payload_url || "").strip.presence
|
Scheduler::Defer.later("Enqueue Post Webhook") do
|
||||||
|
web_hooks = active_web_hooks('post')
|
||||||
|
return if web_hooks.empty?
|
||||||
|
|
||||||
|
WebHook.enqueue_hooks(:post, {
|
||||||
|
category_id: post&.topic&.category_id,
|
||||||
|
event_name: event.to_s,
|
||||||
|
payload: WebHookPostSerializer.new(post,
|
||||||
|
scope: self.guardian,
|
||||||
|
root: false
|
||||||
|
).to_json
|
||||||
|
}, web_hooks)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def self.guardian
|
||||||
|
@guardian ||= Guardian.new(Discourse.system_user)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# == Schema Information
|
# == Schema Information
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
%i(topic_destroyed topic_recovered).each do |event|
|
%i(
|
||||||
DiscourseEvent.on(event) do |topic, user|
|
topic_destroyed
|
||||||
WebHook.enqueue_topic_hooks(event, topic, user)
|
topic_recovered
|
||||||
|
).each do |event|
|
||||||
|
DiscourseEvent.on(event) do |topic, _|
|
||||||
|
WebHook.enqueue_topic_hooks(event, topic)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -8,16 +11,17 @@ DiscourseEvent.on(:topic_status_updated) do |topic, status|
|
||||||
WebHook.enqueue_topic_hooks("topic_#{status}_status_updated", topic)
|
WebHook.enqueue_topic_hooks("topic_#{status}_status_updated", topic)
|
||||||
end
|
end
|
||||||
|
|
||||||
DiscourseEvent.on(:topic_created) do |topic, _, user|
|
DiscourseEvent.on(:topic_created) do |topic, _, _|
|
||||||
WebHook.enqueue_topic_hooks(:topic_created, topic, user)
|
WebHook.enqueue_topic_hooks(:topic_created, topic)
|
||||||
end
|
end
|
||||||
|
|
||||||
%i(post_created
|
%i(
|
||||||
post_destroyed
|
post_created
|
||||||
post_recovered).each do |event|
|
post_destroyed
|
||||||
|
post_recovered
|
||||||
DiscourseEvent.on(event) do |post, _, user|
|
).each do |event|
|
||||||
WebHook.enqueue_post_hooks(event, post, user)
|
DiscourseEvent.on(event) do |post, _, _|
|
||||||
|
WebHook.enqueue_post_hooks(event, post)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -39,7 +43,7 @@ end
|
||||||
user_updated
|
user_updated
|
||||||
).each do |event|
|
).each do |event|
|
||||||
DiscourseEvent.on(event) do |user|
|
DiscourseEvent.on(event) do |user|
|
||||||
WebHook.enqueue_hooks(:user, user_id: user.id, event_name: event.to_s)
|
WebHook.enqueue_object_hooks(:user, user, event)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -49,7 +53,7 @@ end
|
||||||
group_destroyed
|
group_destroyed
|
||||||
).each do |event|
|
).each do |event|
|
||||||
DiscourseEvent.on(event) do |group|
|
DiscourseEvent.on(event) do |group|
|
||||||
WebHook.enqueue_hooks(:group, group_id: group.id, event_name: event.to_s)
|
WebHook.enqueue_object_hooks(:group, group, event)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -59,7 +63,7 @@ end
|
||||||
category_destroyed
|
category_destroyed
|
||||||
).each do |event|
|
).each do |event|
|
||||||
DiscourseEvent.on(event) do |category|
|
DiscourseEvent.on(event) do |category|
|
||||||
WebHook.enqueue_hooks(:category, category_id: category.id, event_name: event.to_s)
|
WebHook.enqueue_object_hooks(:category, category, event)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -69,7 +73,7 @@ end
|
||||||
tag_destroyed
|
tag_destroyed
|
||||||
).each do |event|
|
).each do |event|
|
||||||
DiscourseEvent.on(event) do |tag|
|
DiscourseEvent.on(event) do |tag|
|
||||||
WebHook.enqueue_hooks(:tag, tag_id: tag.id, event_name: event.to_s)
|
WebHook.enqueue_object_hooks(:tag, tag, event)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -80,6 +84,6 @@ end
|
||||||
flag_deferred
|
flag_deferred
|
||||||
).each do |event|
|
).each do |event|
|
||||||
DiscourseEvent.on(event) do |flag|
|
DiscourseEvent.on(event) do |flag|
|
||||||
WebHook.enqueue_hooks(:flag, flag_id: flag.id, event_name: event.to_s)
|
WebHook.enqueue_object_hooks(:flag, flag, event, WebHookFlagSerializer)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,21 +7,41 @@ describe Jobs::EmitWebHookEvent do
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
it 'raises an error when there is no web hook record' do
|
it 'raises an error when there is no web hook record' do
|
||||||
expect { subject.execute(event_type: 'post') }.to raise_error(Discourse::InvalidParameters)
|
expect do
|
||||||
|
subject.execute(event_type: 'post', payload: {})
|
||||||
|
end.to raise_error(Discourse::InvalidParameters)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an error when there is no event type' do
|
it 'raises an error when there is no event type' do
|
||||||
expect { subject.execute(web_hook_id: 1) }.to raise_error(Discourse::InvalidParameters)
|
expect do
|
||||||
|
subject.execute(web_hook_id: 1, payload: {})
|
||||||
|
end.to raise_error(Discourse::InvalidParameters)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises an error when there is no payload' do
|
||||||
|
expect do
|
||||||
|
subject.execute(web_hook_id: 1, event_type: 'post')
|
||||||
|
end.to raise_error(Discourse::InvalidParameters)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "doesn't emit when the hook is inactive" do
|
it "doesn't emit when the hook is inactive" do
|
||||||
Jobs::EmitWebHookEvent.any_instance.expects(:web_hook_request).never
|
subject.execute(
|
||||||
subject.execute(web_hook_id: inactive_hook.id, event_type: 'post', post_id: post.id)
|
web_hook_id: inactive_hook.id,
|
||||||
|
event_type: 'post',
|
||||||
|
payload: { test: "some payload" }.to_json
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'emits normally with sufficient arguments' do
|
it 'emits normally with sufficient arguments' do
|
||||||
Jobs::EmitWebHookEvent.any_instance.expects(:web_hook_request).once
|
stub_request(:post, "https://meta.discourse.org/webhook_listener")
|
||||||
subject.execute(web_hook_id: post_hook.id, event_type: 'post', post_id: post.id)
|
.with(body: "{\"post\":{\"test\":\"some payload\"}}")
|
||||||
|
.to_return(body: 'OK', status: 200)
|
||||||
|
|
||||||
|
subject.execute(
|
||||||
|
web_hook_id: post_hook.id,
|
||||||
|
event_type: 'post',
|
||||||
|
payload: { test: "some payload" }.to_json
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'with category filters' do
|
context 'with category filters' do
|
||||||
|
@ -31,69 +51,64 @@ describe Jobs::EmitWebHookEvent do
|
||||||
let(:topic_hook) { Fabricate(:topic_web_hook, categories: [category]) }
|
let(:topic_hook) { Fabricate(:topic_web_hook, categories: [category]) }
|
||||||
|
|
||||||
it "doesn't emit when event is not related with defined categories" do
|
it "doesn't emit when event is not related with defined categories" do
|
||||||
Jobs::EmitWebHookEvent.any_instance.expects(:web_hook_request).never
|
subject.execute(
|
||||||
|
web_hook_id: topic_hook.id,
|
||||||
subject.execute(web_hook_id: topic_hook.id,
|
event_type: 'topic',
|
||||||
event_type: 'topic',
|
category_id: topic.category.id,
|
||||||
topic_id: topic.id,
|
payload: { test: "some payload" }.to_json
|
||||||
user_id: user.id,
|
)
|
||||||
category_id: topic.category.id)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'emit when event is related with defined categories' do
|
it 'emit when event is related with defined categories' do
|
||||||
Jobs::EmitWebHookEvent.any_instance.expects(:web_hook_request).once
|
stub_request(:post, "https://meta.discourse.org/webhook_listener")
|
||||||
|
.with(body: "{\"topic\":{\"test\":\"some payload\"}}")
|
||||||
|
.to_return(body: 'OK', status: 200)
|
||||||
|
|
||||||
subject.execute(web_hook_id: topic_hook.id,
|
subject.execute(
|
||||||
event_type: 'topic',
|
web_hook_id: topic_hook.id,
|
||||||
topic_id: topic_with_category.id,
|
event_type: 'topic',
|
||||||
user_id: user.id,
|
category_id: topic_with_category.category.id,
|
||||||
category_id: topic_with_category.category.id)
|
payload: { test: "some payload" }.to_json
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '.web_hook_request' do
|
describe '#web_hook_request' do
|
||||||
it 'creates delivery event record' do
|
it 'creates delivery event record' do
|
||||||
stub_request(:post, "https://meta.discourse.org/webhook_listener")
|
stub_request(:post, "https://meta.discourse.org/webhook_listener")
|
||||||
.to_return(body: 'OK', status: 200)
|
.to_return(body: 'OK', status: 200)
|
||||||
|
|
||||||
WebHookEventType.all.pluck(:name).each do |name|
|
WebHookEventType.all.pluck(:name).each do |name|
|
||||||
web_hook_id = Fabricate("#{name}_web_hook").id
|
web_hook_id = Fabricate("#{name}_web_hook").id
|
||||||
object_id = Fabricate(name).id
|
|
||||||
|
|
||||||
expect do
|
expect do
|
||||||
subject.execute(web_hook_id: web_hook_id, event_type: name, "#{name}_id": object_id)
|
subject.execute(
|
||||||
|
web_hook_id: web_hook_id,
|
||||||
|
event_type: name,
|
||||||
|
payload: { test: "some payload" }.to_json
|
||||||
|
)
|
||||||
end.to change(WebHookEvent, :count).by(1)
|
end.to change(WebHookEvent, :count).by(1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'skips silently on missing post' do
|
|
||||||
expect do
|
|
||||||
subject.execute(web_hook_id: post_hook.id, event_type: 'post', post_id: (Post.maximum(:id).to_i + 1))
|
|
||||||
end.not_to raise_error
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'should not skip trashed post' do
|
|
||||||
stub_request(:post, "https://meta.discourse.org/webhook_listener")
|
|
||||||
.to_return(body: 'OK', status: 200)
|
|
||||||
|
|
||||||
expect do
|
|
||||||
post.trash!
|
|
||||||
subject.execute(web_hook_id: post_hook.id, event_type: 'post', post_id: post.id)
|
|
||||||
end.to change(WebHookEvent, :count).by(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'sets up proper request headers' do
|
it 'sets up proper request headers' do
|
||||||
stub_request(:post, "https://meta.discourse.org/webhook_listener")
|
stub_request(:post, "https://meta.discourse.org/webhook_listener")
|
||||||
.to_return(headers: { test: 'string' }, body: 'OK', status: 200)
|
.to_return(headers: { test: 'string' }, body: 'OK', status: 200)
|
||||||
|
|
||||||
subject.execute(web_hook_id: post_hook.id, event_type: 'ping', event_name: 'ping')
|
subject.execute(
|
||||||
|
web_hook_id: post_hook.id,
|
||||||
|
event_type: described_class::PING_EVENT,
|
||||||
|
event_name: described_class::PING_EVENT,
|
||||||
|
payload: { test: "some payload" }.to_json
|
||||||
|
)
|
||||||
|
|
||||||
event = WebHookEvent.last
|
event = WebHookEvent.last
|
||||||
headers = MultiJson.load(event.headers)
|
headers = MultiJson.load(event.headers)
|
||||||
expect(headers['Content-Length']).to eq(13)
|
expect(headers['Content-Length']).to eq(13)
|
||||||
expect(headers['Host']).to eq("meta.discourse.org")
|
expect(headers['Host']).to eq("meta.discourse.org")
|
||||||
expect(headers['X-Discourse-Event-Id']).to eq(event.id)
|
expect(headers['X-Discourse-Event-Id']).to eq(event.id)
|
||||||
expect(headers['X-Discourse-Event-Type']).to eq('ping')
|
expect(headers['X-Discourse-Event-Type']).to eq(described_class::PING_EVENT)
|
||||||
expect(headers['X-Discourse-Event']).to eq('ping')
|
expect(headers['X-Discourse-Event']).to eq(described_class::PING_EVENT)
|
||||||
expect(headers['X-Discourse-Event-Signature']).to eq('sha256=162f107f6b5022353274eb1a7197885cfd35744d8d08e5bcea025d309386b7d6')
|
expect(headers['X-Discourse-Event-Signature']).to eq('sha256=162f107f6b5022353274eb1a7197885cfd35744d8d08e5bcea025d309386b7d6')
|
||||||
expect(event.payload).to eq(MultiJson.dump(ping: 'OK'))
|
expect(event.payload).to eq(MultiJson.dump(ping: 'OK'))
|
||||||
expect(event.status).to eq(200)
|
expect(event.status).to eq(200)
|
||||||
|
|
|
@ -42,69 +42,76 @@ describe WebHook do
|
||||||
expect(post_hook.payload_url).to eq("https://example.com")
|
expect(post_hook.payload_url).to eq("https://example.com")
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#find_by_type' do
|
describe '#active_web_hooks' do
|
||||||
it "returns unique hooks" do
|
it "returns unique hooks" do
|
||||||
post_hook.web_hook_event_types << WebHookEventType.find_by(name: 'topic')
|
post_hook.web_hook_event_types << WebHookEventType.find_by(name: 'topic')
|
||||||
post_hook.update!(wildcard_web_hook: true)
|
post_hook.update!(wildcard_web_hook: true)
|
||||||
|
|
||||||
expect(WebHook.find_by_type(:post)).to eq([post_hook])
|
expect(WebHook.active_web_hooks(:post)).to eq([post_hook])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'find relevant hooks' do
|
it 'find relevant hooks' do
|
||||||
expect(WebHook.find_by_type(:post)).to eq([post_hook])
|
expect(WebHook.active_web_hooks(:post)).to eq([post_hook])
|
||||||
expect(WebHook.find_by_type(:topic)).to eq([topic_hook])
|
expect(WebHook.active_web_hooks(:topic)).to eq([topic_hook])
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'excludes inactive hooks' do
|
it 'excludes inactive hooks' do
|
||||||
post_hook.update_attributes!(active: false)
|
post_hook.update!(active: false)
|
||||||
|
|
||||||
expect(WebHook.find_by_type(:post)).to eq([])
|
expect(WebHook.active_web_hooks(:post)).to eq([])
|
||||||
expect(WebHook.find_by_type(:topic)).to eq([topic_hook])
|
expect(WebHook.active_web_hooks(:topic)).to eq([topic_hook])
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'wildcard web hooks' do
|
||||||
|
let!(:wildcard_hook) { Fabricate(:wildcard_web_hook) }
|
||||||
|
|
||||||
|
it 'should include wildcard hooks' do
|
||||||
|
expect(WebHook.active_web_hooks(:wildcard)).to eq([wildcard_hook])
|
||||||
|
|
||||||
|
expect(WebHook.active_web_hooks(:post)).to contain_exactly(
|
||||||
|
post_hook, wildcard_hook
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(WebHook.active_web_hooks(:topic)).to contain_exactly(
|
||||||
|
topic_hook, wildcard_hook
|
||||||
|
)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#enqueue_hooks' do
|
describe '#enqueue_hooks' do
|
||||||
it 'enqueues hooks with id and name' do
|
before do
|
||||||
Jobs.expects(:enqueue).with(:emit_web_hook_event, web_hook_id: post_hook.id, event_type: 'post')
|
SiteSetting.queue_jobs = true
|
||||||
|
|
||||||
WebHook.enqueue_hooks(:post)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'accepts additional parameters' do
|
it 'accepts additional parameters' do
|
||||||
Jobs.expects(:enqueue).with(:emit_web_hook_event, web_hook_id: post_hook.id, post_id: 1, event_type: 'post')
|
payload = { test: 'some payload' }.to_json
|
||||||
|
WebHook.enqueue_hooks(:post, payload: payload)
|
||||||
|
|
||||||
WebHook.enqueue_hooks(:post, post_id: 1)
|
job_args = Jobs::EmitWebHookEvent.jobs.first["args"].first
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'includes wildcard hooks' do
|
expect(job_args["web_hook_id"]).to eq(post_hook.id)
|
||||||
let!(:wildcard_hook) { Fabricate(:wildcard_web_hook) }
|
expect(job_args["event_type"]).to eq('post')
|
||||||
|
expect(job_args["payload"]).to eq(payload)
|
||||||
describe '#find_by_type' do
|
|
||||||
it 'can find wildcard hooks' do
|
|
||||||
expect(WebHook.find_by_type(:wildcard)).to eq([wildcard_hook])
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'can include wildcard hooks' do
|
|
||||||
expect(WebHook.find_by_type(:post).sort_by(&:id)).to eq([post_hook, wildcard_hook])
|
|
||||||
expect(WebHook.find_by_type(:topic).sort_by(&:id)).to eq([topic_hook, wildcard_hook])
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#enqueue_hooks' do
|
context 'includes wildcard hooks' do
|
||||||
it 'enqueues hooks with ids' do
|
let!(:wildcard_hook) { Fabricate(:wildcard_web_hook) }
|
||||||
Jobs.expects(:enqueue).with(:emit_web_hook_event, web_hook_id: post_hook.id, event_type: 'post')
|
|
||||||
Jobs.expects(:enqueue).with(:emit_web_hook_event, web_hook_id: wildcard_hook.id, event_type: 'post')
|
|
||||||
|
|
||||||
WebHook.enqueue_hooks(:post)
|
describe '#enqueue_hooks' do
|
||||||
end
|
it 'enqueues hooks with ids' do
|
||||||
|
WebHook.enqueue_hooks(:post)
|
||||||
|
|
||||||
it 'accepts additional parameters' do
|
job_args = Jobs::EmitWebHookEvent.jobs.first["args"].first
|
||||||
Jobs.expects(:enqueue).with(:emit_web_hook_event, web_hook_id: post_hook.id, post_id: 1, event_type: 'post')
|
|
||||||
Jobs.expects(:enqueue).with(:emit_web_hook_event, web_hook_id: wildcard_hook.id, post_id: 1, event_type: 'post')
|
|
||||||
|
|
||||||
WebHook.enqueue_hooks(:post, post_id: 1)
|
expect(job_args["web_hook_id"]).to eq(post_hook.id)
|
||||||
|
expect(job_args["event_type"]).to eq('post')
|
||||||
|
|
||||||
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
|
|
||||||
|
expect(job_args["web_hook_id"]).to eq(wildcard_hook.id)
|
||||||
|
expect(job_args["event_type"]).to eq('post')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -115,39 +122,51 @@ describe WebHook do
|
||||||
let(:admin) { Fabricate(:admin) }
|
let(:admin) { Fabricate(:admin) }
|
||||||
let(:topic) { Fabricate(:topic, user: user) }
|
let(:topic) { Fabricate(:topic, user: user) }
|
||||||
let(:post) { Fabricate(:post, topic: topic, user: user) }
|
let(:post) { Fabricate(:post, topic: topic, user: user) }
|
||||||
|
let(:topic_web_hook) { Fabricate(:topic_web_hook) }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
SiteSetting.queue_jobs = true
|
SiteSetting.queue_jobs = true
|
||||||
|
topic_web_hook
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when there are no active hooks' do
|
||||||
|
it 'should not enqueue anything' do
|
||||||
|
topic_web_hook.destroy!
|
||||||
|
post = PostCreator.create(user, raw: 'post', title: 'topic', skip_validations: true)
|
||||||
|
expect(Jobs::EmitWebHookEvent.jobs.length).to eq(0)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should enqueue the right hooks for topic events' do
|
it 'should enqueue the right hooks for topic events' do
|
||||||
Fabricate(:topic_web_hook)
|
|
||||||
|
|
||||||
post = PostCreator.create(user, raw: 'post', title: 'topic', skip_validations: true)
|
post = PostCreator.create(user, raw: 'post', title: 'topic', skip_validations: true)
|
||||||
topic_id = post.topic_id
|
topic_id = post.topic.id
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("topic_created")
|
expect(job_args["event_name"]).to eq("topic_created")
|
||||||
expect(job_args["topic_id"]).to eq(topic_id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(topic_id)
|
||||||
|
|
||||||
PostDestroyer.new(user, post).destroy
|
PostDestroyer.new(user, post).destroy
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("topic_destroyed")
|
expect(job_args["event_name"]).to eq("topic_destroyed")
|
||||||
expect(job_args["topic_id"]).to eq(topic_id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(topic_id)
|
||||||
|
|
||||||
PostDestroyer.new(user, post).recover
|
PostDestroyer.new(user, post).recover
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("topic_recovered")
|
expect(job_args["event_name"]).to eq("topic_recovered")
|
||||||
expect(job_args["topic_id"]).to eq(topic_id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(topic_id)
|
||||||
|
|
||||||
%w{archived closed visible}.each do |status|
|
%w{archived closed visible}.each do |status|
|
||||||
post.topic.update_status(status, true, topic.user)
|
post.topic.update_status(status, true, topic.user)
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("topic_#{status}_status_updated")
|
expect(job_args["event_name"]).to eq("topic_#{status}_status_updated")
|
||||||
expect(job_args["topic_id"]).to eq(topic_id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(topic_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -172,10 +191,7 @@ describe WebHook do
|
||||||
it 'should enqueue the right hooks for post events' do
|
it 'should enqueue the right hooks for post events' do
|
||||||
Fabricate(:web_hook)
|
Fabricate(:web_hook)
|
||||||
|
|
||||||
user
|
post = PostCreator.create!(user,
|
||||||
topic
|
|
||||||
|
|
||||||
post = PostCreator.create(user,
|
|
||||||
raw: 'post',
|
raw: 'post',
|
||||||
topic_id: topic.id,
|
topic_id: topic.id,
|
||||||
reply_to_post_number: 1,
|
reply_to_post_number: 1,
|
||||||
|
@ -183,30 +199,57 @@ describe WebHook do
|
||||||
)
|
)
|
||||||
|
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
Sidekiq::Worker.clear_all
|
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("post_created")
|
expect(job_args["event_name"]).to eq("post_created")
|
||||||
expect(job_args["post_id"]).to eq(post.id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(post.id)
|
||||||
|
|
||||||
|
Jobs::EmitWebHookEvent.jobs.clear
|
||||||
|
|
||||||
# post destroy or recover triggers a moderator post
|
# post destroy or recover triggers a moderator post
|
||||||
expect { PostDestroyer.new(user, post).destroy }
|
expect { PostDestroyer.new(user, post).destroy }
|
||||||
.to change { Jobs::EmitWebHookEvent.jobs.count }.by(2)
|
.to change { Jobs::EmitWebHookEvent.jobs.count }.by(3)
|
||||||
|
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.first["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs[0]["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("post_edited")
|
expect(job_args["event_name"]).to eq("post_edited")
|
||||||
expect(job_args["post_id"]).to eq(post.id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(post.id)
|
||||||
|
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs[1]["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("post_destroyed")
|
expect(job_args["event_name"]).to eq("post_destroyed")
|
||||||
expect(job_args["post_id"]).to eq(post.id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(post.id)
|
||||||
|
|
||||||
PostDestroyer.new(user, post).recover
|
job_args = Jobs::EmitWebHookEvent.jobs[2]["args"].first
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
|
||||||
|
expect(job_args["event_name"]).to eq("topic_destroyed")
|
||||||
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(post.topic.id)
|
||||||
|
|
||||||
|
Jobs::EmitWebHookEvent.jobs.clear
|
||||||
|
|
||||||
|
expect { PostDestroyer.new(user, post).recover }
|
||||||
|
.to change { Jobs::EmitWebHookEvent.jobs.count }.by(3)
|
||||||
|
|
||||||
|
job_args = Jobs::EmitWebHookEvent.jobs[0]["args"].first
|
||||||
|
|
||||||
|
expect(job_args["event_name"]).to eq("post_edited")
|
||||||
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(post.id)
|
||||||
|
|
||||||
|
job_args = Jobs::EmitWebHookEvent.jobs[1]["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("post_recovered")
|
expect(job_args["event_name"]).to eq("post_recovered")
|
||||||
expect(job_args["post_id"]).to eq(post.id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(post.id)
|
||||||
|
|
||||||
|
job_args = Jobs::EmitWebHookEvent.jobs[2]["args"].first
|
||||||
|
|
||||||
|
expect(job_args["event_name"]).to eq("topic_recovered")
|
||||||
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(post.topic.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'should enqueue the right hooks for user events' do
|
it 'should enqueue the right hooks for user events' do
|
||||||
|
@ -216,37 +259,43 @@ describe WebHook do
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("user_created")
|
expect(job_args["event_name"]).to eq("user_created")
|
||||||
expect(job_args["user_id"]).to eq(user.id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(user.id)
|
||||||
|
|
||||||
admin
|
admin
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("user_created")
|
expect(job_args["event_name"]).to eq("user_created")
|
||||||
expect(job_args["user_id"]).to eq(admin.id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(admin.id)
|
||||||
|
|
||||||
user.approve(admin)
|
user.approve(admin)
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("user_approved")
|
expect(job_args["event_name"]).to eq("user_approved")
|
||||||
expect(job_args["user_id"]).to eq(user.id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(user.id)
|
||||||
|
|
||||||
UserUpdater.new(admin, user).update(username: 'testing123')
|
UserUpdater.new(admin, user).update(username: 'testing123')
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("user_updated")
|
expect(job_args["event_name"]).to eq("user_updated")
|
||||||
expect(job_args["user_id"]).to eq(user.id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(user.id)
|
||||||
|
|
||||||
user.logged_out
|
user.logged_out
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("user_logged_out")
|
expect(job_args["event_name"]).to eq("user_logged_out")
|
||||||
expect(job_args["user_id"]).to eq(user.id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(user.id)
|
||||||
|
|
||||||
user.logged_in
|
user.logged_in
|
||||||
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
|
||||||
|
|
||||||
expect(job_args["event_name"]).to eq("user_logged_in")
|
expect(job_args["event_name"]).to eq("user_logged_in")
|
||||||
expect(job_args["user_id"]).to eq(user.id)
|
payload = JSON.parse(job_args["payload"])
|
||||||
|
expect(payload["id"]).to eq(user.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
28
spec/services/web_hook_enqueuer_spec.rb
Normal file
28
spec/services/web_hook_enqueuer_spec.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe WebHookEnqueuer do
|
||||||
|
describe '#find_by_type' do
|
||||||
|
let(:enqueuer) { WebHookEnqueuer.new }
|
||||||
|
let!(:post_hook) { Fabricate(:web_hook, payload_url: " https://example.com ") }
|
||||||
|
let!(:topic_hook) { Fabricate(:topic_web_hook) }
|
||||||
|
|
||||||
|
it "returns unique hooks" do
|
||||||
|
post_hook.web_hook_event_types << WebHookEventType.find_by(name: 'topic')
|
||||||
|
post_hook.update!(wildcard_web_hook: true)
|
||||||
|
|
||||||
|
expect(enqueuer.find_by_type(:post)).to eq([post_hook])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'find relevant hooks' do
|
||||||
|
expect(enqueuer.find_by_type(:post)).to eq([post_hook])
|
||||||
|
expect(enqueuer.find_by_type(:topic)).to eq([topic_hook])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'excludes inactive hooks' do
|
||||||
|
post_hook.update!(active: false)
|
||||||
|
|
||||||
|
expect(enqueuer.find_by_type(:post)).to eq([])
|
||||||
|
expect(enqueuer.find_by_type(:topic)).to eq([topic_hook])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user