Class: Munster::BaseHandler
- Inherits:
-
Object
- Object
- Munster::BaseHandler
- Defined in:
- lib/munster/base_handler.rb
Instance Method Summary collapse
-
#active? ⇒ Boolean
Tells the controller whether this handler is active or not.
-
#enqueue(webhook) ⇒ void
Enqueues the processing job to process webhook asynchronously.
-
#expose_errors_to_sender? ⇒ Boolean
Webhook senders have varying retry behaviors, and often you want to “pretend” everything is fine even though there is an error so that they keep sending you data and do not disable your endpoint forcibly.
-
#extract_event_id_from_request(action_dispatch_request) ⇒ String
Default implementation just generates a random UUID, but if the webhook sender sends us an event ID we use it for deduplication.
-
#handle(action_dispatch_request) ⇒ void
‘handle` accepts the ActionDispatch HTTP request and saves the webhook for later processing.
-
#process(received_webhook) ⇒ void
This is the heart of your webhook processing.
-
#valid?(action_dispatch_request) ⇒ Boolean
This method verifies that request is not malformed and actually comes from the webhook sender: signature validation, HTTP authentication, IP whitelisting and the like.
Instance Method Details
#active? ⇒ Boolean
Tells the controller whether this handler is active or not. This can be used to deactivate a particular handler via feature flags for example, or use other logic to determine whether the handler may be used to create new received webhooks in the system. This is primarily needed for load shedding.
96 97 98 |
# File 'lib/munster/base_handler.rb', line 96 def active? true end |
#enqueue(webhook) ⇒ void
This method returns an undefined value.
Enqueues the processing job to process webhook asynchronously. The job class could be configured.
28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/munster/base_handler.rb', line 28 def enqueue(webhook) # The configured job class can be a class name or a module, to support lazy loading job_class_or_module_name = Munster.configuration.processing_job_class job_class = if job_class_or_module_name.respond_to?(:perform_later) job_class_or_module_name else job_class_or_module_name.constantize end job_class.perform_later(webhook) end |
#expose_errors_to_sender? ⇒ Boolean
Webhook senders have varying retry behaviors, and often you want to “pretend” everything is fine even though there is an error so that they keep sending you data and do not disable your endpoint forcibly. We allow this to be configured on a per-handler basis - a better webhooks sender will be able to make out some sense of the errors.
86 87 88 |
# File 'lib/munster/base_handler.rb', line 86 def expose_errors_to_sender? true end |
#extract_event_id_from_request(action_dispatch_request) ⇒ String
Default implementation just generates a random UUID, but if the webhook sender sends us an event ID we use it for deduplication. A duplicate webhook is not going to be stored in the database if it is already present there.
75 76 77 |
# File 'lib/munster/base_handler.rb', line 75 def extract_event_id_from_request(action_dispatch_request) SecureRandom.uuid end |
#handle(action_dispatch_request) ⇒ void
This method returns an undefined value.
‘handle` accepts the ActionDispatch HTTP request and saves the webhook for later processing. It then enqueues an ActiveJob which will perform the processing using `process`.
12 13 14 15 16 17 18 19 20 21 22 |
# File 'lib/munster/base_handler.rb', line 12 def handle(action_dispatch_request) handler_module_name = is_a?(Munster::BaseHandler) ? self.class.name : to_s handler_event_id = extract_event_id_from_request(action_dispatch_request) webhook = Munster::ReceivedWebhook.new(request: action_dispatch_request, handler_event_id: handler_event_id, handler_module_name: handler_module_name) webhook.save! enqueue(webhook) rescue ActiveRecord::RecordNotUnique # Webhook deduplicated Rails.logger.info { "#{inspect} Webhook #{handler_event_id} is a duplicate delivery and will not be stored." } end |
#process(received_webhook) ⇒ void
This method returns an undefined value.
This is the heart of your webhook processing. Override this method and define your processing inside of it. The ‘received_webhook` will provide access to the `ReceivedWebhook` model, which contains the received body of the webhook request, but also the full (as-full-as-possible) clone of the original ActionDispatch::Request that you can use.
47 48 |
# File 'lib/munster/base_handler.rb', line 47 def process(received_webhook) end |
#valid?(action_dispatch_request) ⇒ Boolean
This method verifies that request is not malformed and actually comes from the webhook sender: signature validation, HTTP authentication, IP whitelisting and the like. There is a difference depending on whether you validate sync (in the receiving controller) or async (in the processing job): Validation is async - it takes place in the background job that gets enqueued to process the webhook. The ‘action_dispatch_request` will be reconstructed from the `ReceivedWebhook` data. Background validation is used because the most common misconfiguration that may occur is usually forgetting or misidentifying the signature for signed webhooks. If such a misconfiguration has taken place, the background validation (instead of rejecting the webhook at input) permits you to still process the webhook once the secrets have been configured correctly.
If this method returns ‘false`, the webhook will be marked as `failed_validation` in the database. If this method returns `true`, the `process` method of the handler is going to be called.
66 67 68 |
# File 'lib/munster/base_handler.rb', line 66 def valid?(action_dispatch_request) true end |