Skip to content

Commit

Permalink
close #766 add webhook subscriber rule
Browse files Browse the repository at this point in the history
  • Loading branch information
theachoem committed Nov 2, 2023
1 parent 9939c5e commit d396322
Show file tree
Hide file tree
Showing 37 changed files with 866 additions and 2 deletions.
21 changes: 21 additions & 0 deletions app/controllers/spree/admin/webhooks_events_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module Spree
module Admin
class WebhooksEventsController < Spree::Admin::ResourceController
# @overrided
def collection
return @collection if @collection.present?

@q = Spree::Webhooks::Event.ransack(params[:q])
@collection = @q.result
.includes(:subscriber)
.page(params[:page])
.per(params[:per_page] || 24)
end

# @overrided
def model_class
Spree::Webhooks::Event
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module Spree
module Admin
class WebhooksSubscriberRulesController < Spree::Admin::ResourceController
before_action :load_webhooks_subscriber

def load_webhooks_subscriber
@webhooks_subscriber = Spree::Webhooks::Subscriber.find(params[:webhooks_subscriber_id])
end

def scope
load_webhooks_subscriber

@webhooks_subscriber.rules
end

# @overrided
def collection
scope
end

# @overrided
def load_resource_instance
return scope.new if new_actions.include?(action)

scope.find(params[:id])
end

def collection_url(options = {})
edit_admin_webhooks_subscriber_url(params[:webhooks_subscriber_id], options)
end

# @overrided
def model_class
SpreeCmCommissioner::Webhooks::SubscriberRule
end

# @overrided
# depend on type of rule eg. spree_cm_commissioner_webhooks_rules_vendors
def object_name
@object.class.to_s.underscore.tr('/', '_')
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ def self.prepended(base)
edit update cancel resume approve resend open_adjustments
close_adjustments cart channel set_channel
accept_all reject_all alert_request_to_vendor
notifications fire_notification
notifications fire_notification queue_webhooks_requests
]

base.before_action :initialize_notification_methods, only: %i[notifications fire_notification]
base.before_action :initialize_notification_methods, only: %i[
notifications
fire_notification
queue_webhooks_requests
]
end

def accept_all
Expand Down Expand Up @@ -46,6 +50,23 @@ def fire_notification
redirect_back fallback_location: notifications_admin_order_url(@order)
end

def queue_webhooks_requests
event = @webhook_events.find { |e| e == params['event'] }

if event.present?
Spree::Webhooks::Subscribers::QueueRequests.call(
event_name: event,
webhook_payload_body: @order.send(:webhook_payload_body),
**@order.send(:webhooks_request_options)
)
flash[:success] = Spree.t(:sent)
else
flash[:error] = Spree.t(:send_failed_or_method_not_support)
end

redirect_back fallback_location: notifications_admin_order_url(@order)
end

def notifications; end

# override
Expand All @@ -62,6 +83,16 @@ def initialize_notification_methods
send_order_accepted_telegram_alert_to_store
send_order_rejected_telegram_alert_to_store
]

@webhook_events = [
'order.create',
'order.delete',
'order.update',
'order.canceled',
'order.placed',
'order.resumed',
'order.shipped'
]
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
module SpreeCmCommissioner
module Webhooks
module SubscriberRulable
extend ActiveSupport::Concern

MATCH_POLICIES = %i[all any].freeze

SUPPORTED_RULE_TYPES = [
SpreeCmCommissioner::Webhooks::Rules::OrderStates,
SpreeCmCommissioner::Webhooks::Rules::OrderVendors
].map(&:to_s)

included do
enum match_policy: MATCH_POLICIES, _prefix: true

has_many :rules, autosave: true, dependent: :destroy, class_name: 'SpreeCmCommissioner::Webhooks::SubscriberRule'
end

def available_rule_types
existing = rules.pluck(:type)

SUPPORTED_RULE_TYPES.reject { |r| existing.include? r }
end

def match_all?
match_policy == 'all'
end

def match_any?
match_policy == 'any'
end

def matches?(event, webhook_payload_body, options = {})
# Subscriber without rules are always match by default.
return true if rules.none?

# Reject if event is not supported by rules
supported_rules = rules.select { |rule| rule.supported?(event) }
return false if supported_rules.none?

if match_all?
supported_rules.all? do |rule|
rule.matches?(event, webhook_payload_body, options)
end
elsif match_any?
supported_rules.any? do |rule|
rule.matches?(event, webhook_payload_body, options)
end
end
end
end
end
end
6 changes: 6 additions & 0 deletions app/models/spree_cm_commissioner/line_item_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ def self.prepended(base)
base.before_create :add_due_date, if: :subscription?

base.whitelisted_ransackable_attributes |= %w[to_date from_date]

def base.json_api_columns
json_api_columns = column_names.reject { |c| c.match(/_id$|id|preferences|(.*)password|(.*)token|(.*)api_key/) }
json_api_columns << :options_text
json_api_columns << :vendor_id
end
end

def reservation?
Expand Down
7 changes: 7 additions & 0 deletions app/models/spree_cm_commissioner/webhooks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module SpreeCmCommissioner
module Webhooks
def self.table_name_prefix
'cm_webhooks_'
end
end
end
43 changes: 43 additions & 0 deletions app/models/spree_cm_commissioner/webhooks/rules/order_states.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module SpreeCmCommissioner
module Webhooks
module Rules
class OrderStates < SubscriberRule
SUPPORTED_EVENTS = [
'order.create',
'order.delete',
'order.update',
'order.canceled',
'order.placed',
'order.resumed',
'order.shipped'
].freeze

DEFAULT_STATES = %w[
cart
address
payment
complete
delivery
awaiting_return
canceled
returned
resumed
].freeze

preference :states, :array, default: DEFAULT_STATES

def supported?(event)
SUPPORTED_EVENTS.include?(event)
end

def matches?(_event, webhook_payload_body, _options = {})
payload_body = JSON.parse(webhook_payload_body)

state = payload_body['data']['attributes']['state']

preferred_states.include?(state)
end
end
end
end
end
41 changes: 41 additions & 0 deletions app/models/spree_cm_commissioner/webhooks/rules/order_vendors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module SpreeCmCommissioner
module Webhooks
module Rules
class OrderVendors < SubscriberRule
MATCH_POLICIES = %w[any all].freeze

SUPPORTED_EVENTS = [
'order.create',
'order.delete',
'order.update',
'order.canceled',
'order.placed',
'order.resumed',
'order.shipped'
].freeze

preference :match_policy, :string, default: MATCH_POLICIES.first
preference :vendors, :array

def supported?(event)
SUPPORTED_EVENTS.include?(event)
end

def matches?(_event, webhook_payload_body, _options = {})
payload_body = JSON.parse(webhook_payload_body)

vendor_ids = payload_body['included'].filter_map do |include|
include['attributes']['vendor_id'].to_s if include['type'] == 'line_item'
end

case preferred_match_policy
when 'any'
preferred_vendors.any? { |vendor_id| vendor_ids.include?(vendor_id) }
when 'all'
preferred_vendors.all? { |vendor_id| vendor_ids.include?(vendor_id) }
end
end
end
end
end
end
13 changes: 13 additions & 0 deletions app/models/spree_cm_commissioner/webhooks/subscriber_decorator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module SpreeCmCommissioner
module Webhooks
module SubscriberDecorator
def self.prepended(base)
base.include SpreeCmCommissioner::Webhooks::SubscriberRulable
end
end
end
end

unless Spree::Webhooks::Subscriber.included_modules.include?(SpreeCmCommissioner::Webhooks::SubscriberDecorator)
Spree::Webhooks::Subscriber.prepend(SpreeCmCommissioner::Webhooks::SubscriberDecorator)
end
11 changes: 11 additions & 0 deletions app/models/spree_cm_commissioner/webhooks/subscriber_rule.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module SpreeCmCommissioner
module Webhooks
class SubscriberRule < Base
belongs_to :subscriber, class_name: 'Spree::Webhooks::Subscriber', inverse_of: :rules

def matches?(_event, _webhook_payload_body, _options = {})
raise 'matches? should be implemented in a sub-class of SpreeCmCommissioner::Webhooks::SubscriberRule'
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!-- insert_top "[data-hook='admin_webhooks_subscriber_form_fields']" -->

<%= f.field_container :api_key, class: ['form-group'] do %>
<%= f.label :api_key, '3rd API key' %>
<%= f.text_field :api_key, class: 'form-control', placeholder: 'Headers : X-Api-key' %>
<%= f.error_message_on :api_key %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!-- insert_top "[data-hook='admin_webhooks_subscriber_form_fields']" -->

<%= f.field_container :name, class: ['form-group'] do %>
<%= f.label :name, Spree.t(:name) %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.error_message_on :name %>
<% end %>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!-- insert_bottom "[data-hook='admin_webhooks_subscriber_form_fields']" -->

<div class="mb-4">
<div class="mb-2 d-flex justify-content-between align-items-center">
<div>Rules</div>
<%= button_link_to Spree.t(:new), new_admin_webhooks_subscriber_rule_path(@webhooks_subscriber), class: "btn-light", icon: 'add.svg' %>
</div>

<% if @webhooks_subscriber.rules.any? %>
<table class="table border" id="admin_webhooks_subscriber_rules">
<thead class="text-muted">
<tr data-hook="admin_webhooks_subscriber_rules_index_headers">
<th><%= Spree.t(:id) %></th>
<th><%= Spree.t(:rule) %></th>
<th><%= Spree.t(:preferences) %></th>
<th><%= Spree.t(:supported_events) %></th>
<th><%= Spree.t(:created_at) %></th>
<th><%= Spree.t(:updated_at) %></th>
</tr>
</thead>
<tbody>
<% @webhooks_subscriber.rules.each do |rule| %>
<tr id="<%= spree_dom_id rule %>" data-hook="admin_webhooks_subscriber_rules_index_rows">
<td><%= rule.id %></td>
<td><%= rule.class.name.demodulize %></td>
<td><%= rule.preferences %></td>
<td><%= rule.class::SUPPORTED_EVENTS.to_sentence %></td>
<td><%= rule.created_at %></td>
<td><%= rule.updated_at %></td>
<td data-hook="admin_webhooks_subscriber_rules_index_row_actions" class="actions">
<span class="d-flex justify-content-end">
<%= link_to_edit rule, url: edit_admin_webhooks_subscriber_rule_path(@webhooks_subscriber, rule), no_text: true if can?(:edit, rule) %>
<%= link_to_delete rule, url: admin_webhooks_subscriber_rule_path(@webhooks_subscriber, rule), no_text: true if can?(:delete, rule) %>
</span>
</td>
</tr>
<% end %>
</tbody>
</table>

<%= f.field_container :match_policy, class: ['form-group mt-3'] do %>
<%= f.label :match_policy, Spree.t(:match_policy) %></span>
<%= f.select :match_policy, @object.class::MATCH_POLICIES, {}, :class => "fullwidth select2" %>
<%= f.error_message_on :match_policy %>
<% end %>

<% else %>
<small class="form-text text-muted">
<%= raw I18n.t('webhooks_subscriber_rules.empty_info') %>
</small>
<% end %>
</div>
Loading

0 comments on commit d396322

Please sign in to comment.