FEATURE: Add support for user badge revocation webhook events (#21204)

Currently, only user badge grants emit webhook events. This change
extends the `user_badge` webhook to emit user badge revocation events.

A new `user_badge_revoked` event has been introduced instead of relying
on the existing `user_badge_removed` event. `user_badge_removed` emitted
just the `badge_id` and `user_id` which aren't helpful for generating a
meaningful webhook payload for revoked(deleted) user badges.

The new event emits  the user badge object.
This commit is contained in:
Selase Krakani 2023-04-24 20:36:40 +00:00 committed by GitHub
parent c03f83bbea
commit cdf1589a85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 64 additions and 8 deletions

View File

@ -48,7 +48,10 @@ class UserBadge < ActiveRecord::Base
Badge.decrement_counter "grant_count", self.badge_id Badge.decrement_counter "grant_count", self.badge_id
UserStat.update_distinct_badge_count self.user_id UserStat.update_distinct_badge_count self.user_id
UserBadge.update_featured_ranks! self.user_id UserBadge.update_featured_ranks! self.user_id
# TODO: Follow up with a deprecation notice for `user_badge_removed`
DiscourseEvent.trigger(:user_badge_removed, self.badge_id, self.user_id) DiscourseEvent.trigger(:user_badge_removed, self.badge_id, self.user_id)
DiscourseEvent.trigger(:user_badge_revoked, user_badge: self)
end end
def self.ensure_consistency! def self.ensure_consistency!

View File

@ -55,12 +55,18 @@ end
DiscourseEvent.on(event) { |tag| WebHook.enqueue_object_hooks(:tag, tag, event, TagSerializer) } DiscourseEvent.on(event) { |tag| WebHook.enqueue_object_hooks(:tag, tag, event, TagSerializer) }
end end
%i[user_badge_granted].each do |event| DiscourseEvent.on(:user_badge_granted) do |badge_id, user_id|
# user_badge_revoked ub = UserBadge.find_by(badge_id: badge_id, user_id: user_id)
DiscourseEvent.on(event) do |badge, user_id| WebHook.enqueue_object_hooks(:user_badge, ub, :user_badge_granted, UserBadgeSerializer)
ub = UserBadge.find_by(badge: badge, user_id: user_id) end
WebHook.enqueue_object_hooks(:user_badge, ub, event, UserBadgeSerializer)
end DiscourseEvent.on(:user_badge_revoked) do |args|
WebHook.enqueue_object_hooks(
:user_badge,
args[:user_badge],
:user_badge_revoked,
UserBadgeSerializer,
)
end end
%i[reviewable_created reviewable_score_updated].each do |event| %i[reviewable_created reviewable_score_updated].each do |event|

View File

@ -4867,8 +4867,8 @@ en:
name: "User Promoted Event" name: "User Promoted Event"
details: "When a user is promoted from one trust level to another." details: "When a user is promoted from one trust level to another."
user_badge_event: user_badge_event:
name: "Badge Grant Event" name: "Badge Event"
details: "When a user receives a badge." details: "When a badge is granted or revoked."
group_user_event: group_user_event:
name: "Group User Event" name: "Group User Event"
details: "When a user is added or removed in a group." details: "When a user is added or removed in a group."

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
RSpec.describe "Webhook event handlers" do
fab!(:user_badge) { Fabricate(:user_badge) }
fab!(:web_hook) { Fabricate(:user_badge_web_hook) }
fab!(:user) { Fabricate(:user) }
fab!(:badge) { Fabricate(:badge) }
fab!(:post) { Fabricate(:post) }
describe "user_badge events" do
it "enqueues user_badge_granted webhook event" do
expect do
BadgeGranter.grant(badge, user, granted_by: Discourse.system_user, post_id: post.id)
end.to change { Jobs::EmitWebHookEvent.jobs.size }.by(1)
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
expect(job_args["id"]).to eq(user.user_badges.last.id)
expect(job_args["event_name"]).to eq("user_badge_granted")
end
it "enqueues user_badge_revoked webhook event" do
expect { BadgeGranter.revoke(user_badge) }.to change { Jobs::EmitWebHookEvent.jobs.size }.by(
1,
)
job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first
expect(job_args["id"]).to eq(user_badge.id)
expect(job_args["event_name"]).to eq("user_badge_revoked")
end
end
end

View File

@ -31,6 +31,22 @@ RSpec.describe UserBadge do
end end
end end
describe "#destroy" do
it "triggers the 'user_badge_revoked' DiscourseEvent" do
user_badge =
UserBadge.create(
badge: badge,
user: user,
granted_at: Time.zone.now,
granted_by: Discourse.system_user,
)
event = DiscourseEvent.track(:user_badge_revoked) { user_badge.destroy! }
expect(event).to be_present
end
end
describe "featured rank" do describe "featured rank" do
fab!(:user) { Fabricate(:user) } fab!(:user) { Fabricate(:user) }
fab!(:user_badge_tl1) do fab!(:user_badge_tl1) do