From 7077b1743b67d2dd6a3e6a21376a7566132c77ad Mon Sep 17 00:00:00 2001 From: Piotr Banasik <piotr.banasik@gmail.com> Date: Wed, 20 Jul 2016 11:35:59 -0700 Subject: [PATCH 1/3] Fixing issue with abstract errors --- lib/webhook_system/base_event.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/webhook_system/base_event.rb b/lib/webhook_system/base_event.rb index c371355..06b9e28 100644 --- a/lib/webhook_system/base_event.rb +++ b/lib/webhook_system/base_event.rb @@ -13,12 +13,12 @@ def initialize(*args, &block) def event_name mesg = "class #{self.class.name} must implement abstract method `#{self.class.name}#event_name()'." - raise RuntimeError.new(mesg).tap { |err| err.backtrace = caller } + raise with_caller_backtrace(RuntimeError.new(mesg), 2) end def payload_attributes mesg = "class #{self.class.name} must implement abstract method `#{self.class.name}#payload_attributes()'." - raise RuntimeError.new(mesg).tap { |err| err.backtrace = caller } + raise with_caller_backtrace(RuntimeError.new(mesg), 2) end def as_json @@ -39,6 +39,11 @@ def self.key_is_reserved?(key) private + def with_caller_backtrace(exception, backtrack=2) + exception.set_backtrace(caller[backtrack..-1]) + exception + end + def validate_attribute_name(key) if self.class.key_is_reserved?(key) message = "#{self.class.name} should not be defining an attribute named #{key} since its reserved" From 9652279bbd8f57f2f2b03c43bc903e35fefa4664 Mon Sep 17 00:00:00 2001 From: Piotr Banasik <piotr.banasik@gmail.com> Date: Wed, 20 Jul 2016 11:08:43 -0700 Subject: [PATCH 2/3] Changing the main dispatch interface to call of a relation, enabling filtering of the subscriptions being considered --- README.md | 36 ++++++++++++++++++- lib/webhook_system.rb | 4 --- lib/webhook_system/dispatcher.rb | 16 --------- lib/webhook_system/subscription.rb | 10 ++++++ ...tch_spec.rb => dispatching_events_spec.rb} | 8 ++--- spec/integration_spec.rb | 2 +- 6 files changed, 50 insertions(+), 26 deletions(-) delete mode 100644 lib/webhook_system/dispatcher.rb rename spec/{dispatch_spec.rb => dispatching_events_spec.rb} (93%) diff --git a/README.md b/README.md index 5580881..9890752 100644 --- a/README.md +++ b/README.md @@ -203,12 +203,46 @@ more suitable for the actual notification payload. The general API for this is via: ```ruby -WebhookSystem.dispatch(event_object) +WebhookSystem::Subscription.dispatch(event_object) ``` This is meant to be fairly fire and forget. Internally this will create an ActiveJob for each subscription interested in the event. +### Dispatching to Selected Subscriptions + +There may be scenarios where you extended the Subscription model, and may need to only dispatch to a subset of subs. +For example, if you attached a relation to say Account. The `dispatch` method is actually defined to work with any +subscription relation. eg: + +```ruby +account = Account.find(1) # assume we have some model called Account +subs = account.webhook_subscriptions # and we added a column to webhook_subscriptions to accomodate this extra relation +subs.dispatch(some_event) # you can dispatch to just those subscriptions (it will filter for the specific ones) + # that are actually interested in the event +``` + +### Checking if any sub is interested + +There may scenarios, where you really don't want to do some additional work unless you really have an event to dispatch. +You can check pretty quickly if there is any topics interested liks so: + +```ruby +if WebhookSystem::Subscription.interested_in_topic('some_topic').present? + # do some stuff +end +``` + +This also works with selected subscriptions like in the example above: + +```ruby +account = Account.find(1) # assume we have some model called Account +subs = account.webhook_subscriptions # and we added a column to webhook_subscriptions to accomodate this extra relation +if subs.interested_in_topic('some_topic').present? + subs.dispatch(SomeEvent.build(some_expensive_function())) +end +``` + # Payload Format Payloads can either be plain json or encrypted. On top of that, they're also signed. The format for the signature diff --git a/lib/webhook_system.rb b/lib/webhook_system.rb index 219f5f4..5e563b5 100644 --- a/lib/webhook_system.rb +++ b/lib/webhook_system.rb @@ -20,8 +20,4 @@ module WebhookSystem # Error raised when there is an issue with decoding the payload class DecodingError < RuntimeError end - - class << self - delegate :dispatch, to: :'WebhookSystem::Dispatcher' - end end diff --git a/lib/webhook_system/dispatcher.rb b/lib/webhook_system/dispatcher.rb deleted file mode 100644 index b989a1b..0000000 --- a/lib/webhook_system/dispatcher.rb +++ /dev/null @@ -1,16 +0,0 @@ -module WebhookSystem - - # Main code that handles dispatching of events out to subscribers - class Dispatcher - class << self - - # @param [WebhookSystem::BaseEvent] event The Event Object - def dispatch(event) - WebhookSystem::Subscription.interested_in_topic(event.event_name).each do |subscription| - WebhookSystem::Job.perform_later subscription, event.as_json - end - end - - end - end -end diff --git a/lib/webhook_system/subscription.rb b/lib/webhook_system/subscription.rb index ec1a419..45d822b 100644 --- a/lib/webhook_system/subscription.rb +++ b/lib/webhook_system/subscription.rb @@ -19,6 +19,16 @@ class Subscription < ActiveRecord::Base scope :interested_in_topic, -> (topic) { active.for_topic(topic) } + # Main invocation point for dispatching events, can either be called on the class + # or on a relation (ie a scoped down list of subs), will find applicable subs and dispatch to them + # + # @param [WebhookSystem::BaseEvent] event The Event Object + def self.dispatch(event) + interested_in_topic(event.event_name).each do |subscription| + WebhookSystem::Job.perform_later subscription, event.as_json + end + end + # Just a helper to get a nice representation of the subscription def url_domain URI.parse(url).host diff --git a/spec/dispatch_spec.rb b/spec/dispatching_events_spec.rb similarity index 93% rename from spec/dispatch_spec.rb rename to spec/dispatching_events_spec.rb index 4621db7..4cdd6ab 100644 --- a/spec/dispatch_spec.rb +++ b/spec/dispatching_events_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe WebhookSystem, aggregate_failures: true, db: true do +describe 'dispatching events', aggregate_failures: true, db: true do describe 'dispatching' do let!(:subscription1) do create(:webhook_subscription, :active, :encrypted, :with_topics, url: 'http://lvh.me/hook1', topics: ['other_event']) @@ -45,7 +45,7 @@ def payload_attributes expect { perform_enqueued_jobs do - described_class.dispatch event + WebhookSystem::Subscription.dispatch event end }.to change { subscription1.event_logs.count }.by(1) @@ -68,7 +68,7 @@ def payload_attributes expect { expect { perform_enqueued_jobs do - described_class.dispatch event + WebhookSystem::Subscription.dispatch event end }.to change { subscription1.event_logs.count }.by(1) }.to raise_exception(WebhookSystem::Job::RequestFailed, 'request failed with code: 400') @@ -90,7 +90,7 @@ def payload_attributes expect { expect { perform_enqueued_jobs do - described_class.dispatch event + WebhookSystem::Subscription.dispatch event end }.to change { subscription1.event_logs.count }.by(1) }.to raise_exception(WebhookSystem::Job::RequestFailed, 'request failed with code: 0') diff --git a/spec/integration_spec.rb b/spec/integration_spec.rb index 89ac0e2..c2472d2 100644 --- a/spec/integration_spec.rb +++ b/spec/integration_spec.rb @@ -70,7 +70,7 @@ def handle_webhook(to:) end perform_enqueued_jobs do - WebhookSystem.dispatch event + WebhookSystem::Subscription.dispatch event end expect(hooks_called).to match_array([:hook1, :hook2]) From 8d5843fdbecff57ae764631804bd910a4985218a Mon Sep 17 00:00:00 2001 From: Piotr Banasik <piotr.banasik@gmail.com> Date: Wed, 20 Jul 2016 11:14:48 -0700 Subject: [PATCH 3/3] Bump to 2.1.0 --- lib/webhook_system/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/webhook_system/version.rb b/lib/webhook_system/version.rb index 7591714..19a1b3e 100644 --- a/lib/webhook_system/version.rb +++ b/lib/webhook_system/version.rb @@ -1,3 +1,3 @@ module WebhookSystem - VERSION = '2.0.0' + VERSION = '2.1.0' end