From 138d4aebde37f2f4cd2bb3efe8828f38680098da Mon Sep 17 00:00:00 2001 From: Kane York Date: Wed, 8 Apr 2020 11:34:35 -0700 Subject: [PATCH] FEATURE: Webhooks and Event for user being granted a badge Adding a webhook for badge revocation is left for future work as it's relatively rare. --- app/models/web_hook_event_type.rb | 1 + app/services/badge_granter.rb | 6 +++++- config/initializers/012-web_hook_events.rb | 10 +++++++++ config/locales/client.en.yml | 3 +++ db/fixtures/007_web_hook_event_types.rb | 5 +++++ spec/fabricators/web_hook_fabricator.rb | 8 +++++++ spec/models/web_hook_spec.rb | 25 ++++++++++++++++++++++ 7 files changed, 57 insertions(+), 1 deletion(-) diff --git a/app/models/web_hook_event_type.rb b/app/models/web_hook_event_type.rb index d3822d1ede4..952c1c2062c 100644 --- a/app/models/web_hook_event_type.rb +++ b/app/models/web_hook_event_type.rb @@ -13,6 +13,7 @@ class WebHookEventType < ActiveRecord::Base NOTIFICATION = 10 SOLVED = 11 ASSIGN = 12 + USER_BADGE = 13 has_and_belongs_to_many :web_hooks diff --git a/app/services/badge_granter.rb b/app/services/badge_granter.rb index 2249b33692a..c6dfd821700 100644 --- a/app/services/badge_granter.rb +++ b/app/services/badge_granter.rb @@ -410,7 +410,7 @@ class BadgeGranter end def self.send_notification(user_id, username, locale, badge) - I18n.with_locale(notification_locale(locale)) do + notification = I18n.with_locale(notification_locale(locale)) do Notification.create!( user_id: user_id, notification_type: Notification.types[:granted_badge], @@ -423,6 +423,10 @@ class BadgeGranter }.to_json ) end + + DiscourseEvent.trigger(:user_badge_granted, badge, user_id) + + notification end end diff --git a/config/initializers/012-web_hook_events.rb b/config/initializers/012-web_hook_events.rb index 6ce6501ff32..727119a6276 100644 --- a/config/initializers/012-web_hook_events.rb +++ b/config/initializers/012-web_hook_events.rb @@ -74,6 +74,16 @@ end end end +%i( + user_badge_granted +).each do |event| + # user_badge_revoked + DiscourseEvent.on(event) do |badge, user_id| + ub = UserBadge.find_by(badge: badge, user_id: user_id) + WebHook.enqueue_object_hooks(:user_badge, ub, event, UserBadgeSerializer) + end +end + DiscourseEvent.on(:reviewable_created) do |reviewable| WebHook.enqueue_object_hooks(:reviewable, reviewable, :reviewable_created, reviewable.serializer) end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 9345ba52723..f395e62717c 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -3562,6 +3562,9 @@ en: notification_event: name: "Notification Event" details: "When a user receives a notification in their feed." + user_badge_event: + name: "Badge Grant Event" + details: "When a user receives a badge." delivery_status: title: "Delivery Status" inactive: "Inactive" diff --git a/db/fixtures/007_web_hook_event_types.rb b/db/fixtures/007_web_hook_event_types.rb index 5cbcd3edc2a..8bf8e938055 100644 --- a/db/fixtures/007_web_hook_event_types.rb +++ b/db/fixtures/007_web_hook_event_types.rb @@ -59,3 +59,8 @@ WebHookEventType.seed do |b| b.id = WebHookEventType::ASSIGN b.name = "assign" end + +WebHookEventType.seed do |b| + b.id = WebHookEventType::USER_BADGE + b.name = "user_badge" +end diff --git a/spec/fabricators/web_hook_fabricator.rb b/spec/fabricators/web_hook_fabricator.rb index 6d03c7b47f7..46472510808 100644 --- a/spec/fabricators/web_hook_fabricator.rb +++ b/spec/fabricators/web_hook_fabricator.rb @@ -102,3 +102,11 @@ Fabricator(:notification_web_hook, from: :web_hook) do web_hook.web_hook_event_types = [transients[:notification_hook]] end end + +Fabricator(:user_badge_web_hook, from: :web_hook) do + transient user_badge_hook: WebHookEventType.find_by(name: 'user_badge') + + after_build do |web_hook, transients| + web_hook.web_hook_event_types = [transients[:user_badge_hook]] + end +end diff --git a/spec/models/web_hook_spec.rb b/spec/models/web_hook_spec.rb index 47b4c20bb72..586458a6149 100644 --- a/spec/models/web_hook_spec.rb +++ b/spec/models/web_hook_spec.rb @@ -486,5 +486,30 @@ describe WebHook do payload = JSON.parse(job_args["payload"]) expect(payload["id"]).to eq(reviewable.id) end + + it 'should enqueue the right hooks for badge grants' do + Fabricate(:user_badge_web_hook) + badge = Fabricate(:badge) + badge.multiple_grant = true + badge.show_posts = true + badge.save + + now = Time.now + freeze_time now + + BadgeGranter.grant(badge, user, granted_by: admin, post_id: post.id) + + job_args = Jobs::EmitWebHookEvent.jobs.last["args"].first + expect(job_args["event_name"]).to eq("user_badge_granted") + payload = JSON.parse(job_args["payload"]) + expect(payload["badge_id"]).to eq(badge.id) + expect(payload["user_id"]).to eq(user.id) + expect(payload["granted_by_id"]).to eq(admin.id) + # be_within required because rounding occurs + expect(Time.zone.parse(payload["granted_at"]).to_f).to be_within(0.001).of(now.to_f) + expect(payload["post_id"]).to eq(post.id) + + # Future work: revoke badge hook + end end end