Ever wonder how to add new webhook types? Or how to reduce the payload? Here is the tutorial for the plugin authors. It demos how to add the session and user notification event types as well as customization to the payload. You can also check the plugin on GitHub while reading.
If you’d like to have a new webhook type supported by the team, bring up a feature request instead.
Before started, make sure you already understand webhook guide.
New webhook event type
A new webhook event type is defined in the database so that Ember client and webhook can find relevant information.
1.Seeding
Add a seed file in the following path of the plugin db/fixtures/001_custom_web_hook.rb
.
WebHookEventType.seed do |b|
b.id = 100 # start with a relative large number so it doesn't conflict with the core type
b.name = "notification"
end
WebHookEventType.seed do |b|
b.id = 101
b.name = "session"
end
Then putting SeedFu.fixture_paths << Rails.root.join("plugins", "discourse-webhooks-example", "db", "fixtures").to_s
into the plugin.
Admin dashboard needs the text to display the new event type. Adding them in config/locales/client.<language-code>.yml
:
en:
admin_js:
admin:
web_hooks:
notification_event:
name: "Notification Event"
details: "When there is a new notification."
session_event:
name: "Session Event"
details: "When there is a login or logout."
2. Connect with the internal DiscourseEvent
or hook on your own
add_model_callback(:notification, :after_commit, on: :create) do
# you can enqueue web hooks anywhere outside the AR transaction
# provided that web hook event type exists
WebHook.enqueue_hooks(:notification, # event type name
notification_id: self.id, # pass the relevant record id
# event name appears in the header of webhook payload
event_name: "notification_#{Notification.types[self.notification_type]}_created")
end
%i(user_logged_in user_logged_out).each do |event|
DiscourseEvent.on(event) do |user|
WebHook.enqueue_hooks(:session, user_id: user.id, event_name: event.to_s)
end
end
3. Final step: Sidekiq Jobs
Adding a new method to the Jobs::EmitWebHookEvent
:
Jobs::EmitWebHookEvent.class_eval do
# the method name should always be setup_<event type name>(args)
def setup_notification(args)
notification = Notification.find_by(id: args[:notification_id])
return if notification.blank? # or raise an exception if you like
# here you can define the serializer, you can also create a new serializer to prune the payload
# See also: `WebHookPostSerializer`, `WebHookTopicViewSerializer`
args[:payload] = NotificationSerializer.new(notification, scope: guardian, root: false).as_json
end
def setup_session(args)
user = User.find_by(id: args[:user_id])
return if user.blank?
args[:payload] = UserSerializer.new(user, scope: guardian, root: false).as_json
end
end
An aside note, the payload is sent as if you are an administrator browsing a Discourse site. Be careful for what you sent.
Payload customization
There are two ways to reduce the payload size.
- Define a custom serializer.
- Uses plugin filter.
The first one is explicit to do. The second one involves a plugin API where you have the power to modify the payload. This enables the possibility to slice the JSON, i.e. @Lapinot suggested.
Plugin::Filter.register(:after_build_web_hook_body) do |instance, body|
if body[:session]
body[:user_session] = body.delete :session
end
body # remember to return the object, otherwise the payload would be empty
end
Final aside note, a {{plugin-outlet name="web-hook-fields"}}
is now available in the web hook configuration page.
The plugin code is available under GitHub - erickguan/discourse-webhooks-example.
Thanks @erlend_sh to encourage me writing this tutorial and @tgxworld for sorting out the internals.