Neue Webhooks hinzufügen und Webhook-Payloads anpassen

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.

  1. Define a custom serializer.
  2. 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.

26 „Gefällt mir“

@fantasticfears Note that it isn’t recommended to run migrations in plugins. Instead I’ve updated the howto to create a fixture file that will automatically be run when db:migrate is invoked.

7 „Gefällt mir“

Fixture is better than migration but it doesn’t work with the plugin now. The plugin system includes only db/migrate but not fixture.

Ah ha I forgot to include the secret sauce.

https://github.com/discourse/discourse-narrative-bot/blob/5ae7c8c7495bc5af667a6125b790e656b83d9b10/plugin.rb#L19

10 „Gefällt mir“

I didn’t know there’s a new API register_seedfu_fixtures! Updated.

8 „Gefällt mir“

what do i have to do to make it just notification webhook and remove the session event ?

Can someone offer a hint here?

I need to submit this JSON payload when a new user is created:

{"email_address":"address of new user", "status": "subscribed"}

I guess I don’t know how to create a serializer.

This is easy.

class MySerializer < ApplicationSerializer
  attributes :email_address, :status
end

my_object = MyClass.new(email_address: "address of new user", status: "subscribed")
MySerializer.new(my_object).as_json
4 „Gefällt mir“

@fantasticfears Thanks for the writeup!

It needs a bit of tweaking to take account of the changes introduced in this commit.

  • You no longer need a setup_ method in Jobs::EmitWebHookEvent.

  • There’s now a WebHook a class method for formatting custom payloads via a serializer:

    WebHook.enqueue_object_hooks(type, object, event, Serializer) 
    

Also, I’m wondering about the thinking behind namespacing the payload with the event type? This makes it a little harder to use endpoints you don’t fully control, which expect certain attributes in the body.

def build_web_hook_body(args, web_hook)
   ....
   if ping_event?(event_type)
     body[:ping] = 'OK'
   else
     body[event_type] = args[:payload]
   end
  ...
end
2 „Gefällt mir“

Can you help me update the guide?

This is mainly for possible extension. Additional information can be serialized to other keys. But we relies on serializers now so it’s more or less useless.

2 „Gefällt mir“

@fantasticfears
Hallo.

Ich versuche, Discourse mit APP über Webhooks zu verbinden.

Ich habe die meisten Dinge erledigt, aber nur noch eine Sache ist übrig.

Wenn Sie ein neues Thema in Discourse erstellen, senden Sie eine Nutzlast, die viele Parameter an APP enthält.

Slack erhält jedoch nur den Parameter „text“. Daher möchte ich die Nutzlast bei Verwendung von Webhooks anpassen.

Wie ändere ich die Nutzlast bei Verwendung von Webhooks in Discourse?

AS-IS

{
  "post": {
    "id": 19,
    "name": "user",
    "username": "user",
    "avatar_template": "/letter_avatar_proxy/v2/letter/u/c0e974/{size}.png",
    "created_at": "2018-07-20T06:24:33.205Z",
    "cooked": "<p>Cool, now i have you, haha</p>",
    "post_number": 6,
    "post_type": 1,
    "updated_at": "2018-07-20T06:24:33.205Z",
    "reply_count": 0,
    "reply_to_post_number": null,
    "quote_count": 0,
    "avg_time": null,
    "incoming_link_count": 0,
    "reads": 0,
    "score": 0,
    "topic_id": 11,
    "topic_slug": "this-is-new-topic",
    "topic_title": "This is new topic",
    "display_username": "user",
    "primary_group_name": null,
    "version": 1,
    "user_title": null,
    "moderator": false,
    "admin": true,
    "staff": true,
    "user_id": 1,
    "hidden": false,
    "trust_level": 1,
    "deleted_at": null,
    "user_deleted": false,
    "edit_reason": null,
    "wiki": false,
    "topic_posts_count": 6
  }
}

TO-BE

{
   "topic_title": "This is new topic"
}

Mit freundlichen Grüßen.

Hallo, ich versuche, einen benutzerdefinierten Webhook gemäß dieser Anleitung und diesem Beispiel-Plugin zu erstellen, aber meine neuen benutzerdefinierten Ereignistypen werden nicht in der Webhook-Benutzeroberfläche angezeigt. Ich habe versucht, das in dieser Anleitung verlinkte Plugin auf meiner lokalen Entwicklungsinstanz zu installieren, und die Ereignisse in diesem Beispiel-Plugin werden ebenfalls nicht angezeigt. Ich vermute, dass sich zwischen 2017 und jetzt etwas geändert hat, das dies beeinflusst?

Weiß jemand hier, wie man das Beispiel-Plugin in diesem Thema zum Laufen bringt? Ich vermute, dass das Problem bei dem Seeding-Code für die Datenbank liegen könnte, da die neuen Einträge nicht in der Antwort von /admin/api/web_hooks angezeigt werden. So wie es derzeit aussieht, ist diese Anleitung nicht funktionsfähig. Danke!

Hallo! Ich glaube, ich brauche etwas Hilfe

Ich versuche, einen benutzerdefinierten Webhook hinzuzufügen, was mir gelungen ist und er sendet die Nutzlast korrekt an die Webhook-URL. Das Einzige, was mir nicht gelingt, ist, das Ereignis bei der Konfiguration des Webhooks im Adminbereich zu aktivieren. Ich habe dem obigen Tutorial zum Registrieren der Fixture befolgt, aber sie erscheint nicht im Adminbereich.

Ich habe es auf verschiedene Arten versucht:

SeedFu.fixture_paths << Rails.root.join("plugins", "<plugin_name>", "db", "fixtures").to_s
register_seedfu_fixtures(Rails.root.join("plugins", "<plugin_name>", "db", "fixtures").to_s)

Fixture:

WebHookEventType.seed do |b|
    b.id = 101
    b.name = "remove_like"
end

Ich habe auch Folgendes zu Locale hinzugefügt:

en:
  js:
    plugin_name:
      placeholder: placeholder
    admin:
      web_hooks:
        remove_like_event:
          name: "Remove Like Event"
          details: "When a user marks a post as the accepted or unaccepted answer."

Fehlt mir etwas? Oder soll ich es auf eine andere Weise machen?

Okay, es scheint ein Cache-Problem oder etwas Ähnliches gewesen zu sein, als ich es in einer anderen Instanz von Discourse installiert habe, erschien es.