-
-
Save benedikt/fa3488d968385e553884f61676a69f13 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/models/webhook/delivery.rb | |
module Webhook | |
module Delivery | |
extend ActiveSupport::Concern | |
def webhook_payload | |
{} | |
end | |
def webhook_scope | |
raise NotImplementedError | |
end | |
def deliver_webhook(action) | |
event_name = "#{self.class.name.underscore}_#{action}" | |
deliver_webhook_event(event_name, webhook_payload) | |
end | |
def deliver_webhook_event(event_name, payload) | |
event = Webhook::Event.new(event_name, payload || {}) | |
webhook_scope.webhook_endpoints.for_event(event_name).each do |endpoint| | |
endpoint.deliver(event) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/workers/webhook/delivery_worker.rb | |
require 'net/http' | |
module Webhook | |
class DeliveryWorker | |
include Sidekiq::Worker | |
def perform(endpoint_id, payload) | |
return unless endpoint = Webhook::Endpoint.find(endpoint_id) | |
response = request(endpoint.target_url, payload) | |
case response.code | |
when 410 | |
endpoint.destroy | |
when 400..599 | |
raise response.to_s | |
end | |
end | |
private | |
def request(endpoint, payload) | |
uri = URI.parse(endpoint) | |
request = Net::HTTP::Post.new(uri.request_uri) | |
request['Content-Type'] = 'application/json' | |
request.body = payload | |
http = Net::HTTP.new(uri.host, uri.port) | |
http.use_ssl = (uri.scheme == 'https') | |
http.request(request) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/models/webhook/endpoint.rb | |
module Webhook | |
class Endpoint < ApplicationRecord | |
def self.table_name_prefix | |
'webhook_' | |
end | |
attribute :events, :string, array: true, default: [] | |
belongs_to :account | |
validates :target_url, | |
presence: true, | |
format: URI.regexp(%w(http https)) | |
validates :events, | |
presence: true | |
def self.for_event(events) | |
where('events @> ARRAY[?]::varchar[]', Array(events)) | |
end | |
def events=(events) | |
events = Array(events).map { |event| event.to_s.underscore } | |
super(Webhook::Event::EVENT_TYPES & events) | |
end | |
def deliver(event) | |
Webhook::DeliveryWorker.perform_async(id, event.to_json) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/models/webhook/event.rb | |
module Webhook | |
class Event | |
EVENT_TYPES = %w( | |
project_created | |
project_updated | |
project_destroyed | |
).freeze | |
attr_reader :event_name, :payload | |
def initialize(event_name, payload = {}) | |
@event_name = event_name | |
@payload = payload | |
end | |
def as_json(*args) | |
hash = payload.transform_values do |value| | |
serialize_resource(value).as_json(*args) | |
end | |
hash[:event_name] = event_name | |
hash | |
end | |
private | |
def serialize_resource(resource) | |
ActiveModelSerializers::SerializableResource.new(resource, {}) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/models/webhook/observable.rb | |
module Webhook | |
module Observable | |
extend ActiveSupport::Concern | |
include Webhook::Delivery | |
included do | |
after_commit on: :create do | |
deliver_webhook(:created) | |
end | |
after_commit on: :update do | |
deliver_webhook(:updated) | |
end | |
after_commit on: :destroy do | |
deliver_webhook(:destroyed) | |
end | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/models/project.rb | |
class Project < ApplicationRecord | |
include Webhook::Observable | |
belongs_to :account | |
private | |
def webhook_scope | |
account | |
end | |
def webhook_payload | |
{ project: self } | |
end | |
end |
Real helpful! Thank you for the effort. 👍
How to implement the same with version-based?
Api (v1) -> Webhook (v1)
Api (v2) -> Webhook (v2)
@ddlogesh You could add a api_version column to the webhook endpoint and dynamically switch the serializer based on that.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for nice solution.
One thing:
webhook_scope.webhook_endpoints.for_event(event_name)
->event_name
is passed as string while you're expecting list of events in method definition: