spree_api 5.2.6 → 5.3.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/concerns/spree/api/v2/product_list_includes.rb +10 -22
- data/app/controllers/concerns/spree/api/v2/storefront/order_concern.rb +1 -1
- data/app/controllers/spree/api/v2/platform/classifications_controller.rb +1 -1
- data/app/controllers/spree/api/v2/resource_controller.rb +8 -5
- data/app/controllers/spree/api/v2/storefront/products_controller.rb +1 -4
- data/app/helpers/spree/api/v2/display_money_helper.rb +30 -10
- data/app/jobs/spree/webhook_delivery_job.rb +16 -0
- data/app/paginators/spree/api/paginate.rb +68 -0
- data/app/serializers/spree/api/v2/platform/product_serializer.rb +12 -4
- data/app/serializers/spree/api/v2/platform/variant_serializer.rb +12 -4
- data/app/serializers/spree/v2/storefront/product_serializer.rb +13 -5
- data/app/serializers/spree/v2/storefront/taxon_serializer.rb +1 -1
- data/app/serializers/spree/v2/storefront/variant_serializer.rb +12 -4
- data/app/services/spree/webhooks/deliver_webhook.rb +83 -0
- data/app/subscribers/spree/webhook_event_subscriber.rb +67 -0
- data/config/routes.rb +0 -2
- data/lib/generators/spree/api/install/install_generator.rb +24 -0
- data/lib/spree/api/configuration.rb +3 -0
- data/lib/spree/api/dependencies.rb +1 -1
- data/lib/spree/api/engine.rb +6 -0
- data/lib/spree/api/testing_support/matchers/webhooks.rb +1 -67
- data/lib/spree/api/testing_support/spree_webhooks.rb +1 -9
- data/lib/spree/api.rb +3 -0
- data/lib/spree_api.rb +0 -2
- metadata +25 -33
- data/app/controllers/spree/api/v2/platform/webhooks/events_controller.rb +0 -25
- data/app/controllers/spree/api/v2/platform/webhooks/subscribers_controller.rb +0 -25
- data/app/jobs/spree/webhooks/subscribers/make_request_job.rb +0 -17
- data/app/models/concerns/spree/webhooks/has_webhooks.rb +0 -95
- data/app/models/spree/order/webhooks.rb +0 -39
- data/app/models/spree/payment/webhooks.rb +0 -23
- data/app/models/spree/product/webhooks.rb +0 -42
- data/app/models/spree/shipment/webhooks.rb +0 -19
- data/app/models/spree/stock_item/webhooks.rb +0 -40
- data/app/models/spree/stock_movement/webhooks.rb +0 -49
- data/app/models/spree/variant/webhooks.rb +0 -25
- data/app/models/spree/webhooks/base.rb +0 -11
- data/app/models/spree/webhooks/event.rb +0 -20
- data/app/models/spree/webhooks/event_signature.rb +0 -33
- data/app/models/spree/webhooks/subscriber.rb +0 -88
- data/app/serializers/spree/api/v2/platform/webhooks/event_serializer.rb +0 -15
- data/app/serializers/spree/api/v2/platform/webhooks/subscriber_serializer.rb +0 -13
- data/app/services/spree/webhooks/subscribers/handle_request.rb +0 -77
- data/app/services/spree/webhooks/subscribers/make_request.rb +0 -83
- data/app/services/spree/webhooks/subscribers/queue_requests.rb +0 -27
- data/db/migrate/20210902162826_create_spree_webhooks_tables.rb +0 -16
- data/db/migrate/20211025162826_create_spree_webhooks_events.rb +0 -14
- data/db/migrate/20221221122100_add_secret_key_to_spree_webhooks_subscribers.rb +0 -5
- data/db/migrate/20230116204600_backfill_secret_key_for_spree_webhooks_subscribers.rb +0 -5
- data/db/migrate/20230116205000_change_secret_key_to_non_null_column.rb +0 -5
- data/lib/spree/api/testing_support/factories/webhook_event_factory.rb +0 -27
- data/lib/spree/api/testing_support/factories/webhook_subscriber_factory.rb +0 -13
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
require 'base64'
|
|
2
|
-
require 'openssl'
|
|
3
|
-
|
|
4
|
-
module Spree
|
|
5
|
-
module Webhooks
|
|
6
|
-
class EventSignature
|
|
7
|
-
def initialize(event, payload)
|
|
8
|
-
@event = event
|
|
9
|
-
@payload = payload
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Generates a base64-encoded HMAC SHA256 signature for the payload of the event.
|
|
13
|
-
#
|
|
14
|
-
# By using the stringified payload, the signature is made tamper-proof as any
|
|
15
|
-
# alterations of the data during transit will lead to an incorrect signature
|
|
16
|
-
# comparison by the client.
|
|
17
|
-
#
|
|
18
|
-
# @return [String] The computed signature
|
|
19
|
-
def computed_signature
|
|
20
|
-
@computed_signature ||=
|
|
21
|
-
Base64.strict_encode64(
|
|
22
|
-
OpenSSL::HMAC.digest('sha256', @event.subscriber.secret_key, payload)
|
|
23
|
-
)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
private
|
|
27
|
-
|
|
28
|
-
def payload
|
|
29
|
-
@payload.is_a?(String) ? @payload : @payload.to_json
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
module Webhooks
|
|
3
|
-
class Subscriber < Spree::Webhooks::Base
|
|
4
|
-
if defined?(Spree::VendorConcern)
|
|
5
|
-
include Spree::VendorConcern
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
if Rails::VERSION::STRING >= '7.1.0'
|
|
9
|
-
has_secure_token :secret_key, on: :save
|
|
10
|
-
else
|
|
11
|
-
has_secure_token :secret_key
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
has_many :events, inverse_of: :subscriber
|
|
15
|
-
|
|
16
|
-
validates :url, 'spree/url': true, presence: true
|
|
17
|
-
validate :check_uri_path
|
|
18
|
-
|
|
19
|
-
self.whitelisted_ransackable_attributes = %w[active subscriptions url]
|
|
20
|
-
self.whitelisted_ransackable_associations = %w[event]
|
|
21
|
-
|
|
22
|
-
scope :active, -> { where(active: true) }
|
|
23
|
-
scope :inactive, -> { where(active: false) }
|
|
24
|
-
|
|
25
|
-
before_save :parse_subscriptions
|
|
26
|
-
|
|
27
|
-
def latest_event_at
|
|
28
|
-
events.order(:created_at).last&.created_at
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Returns true if the subscriber supports the given event
|
|
32
|
-
#
|
|
33
|
-
# @param event [String] The event to check, e.g. 'product.create'
|
|
34
|
-
# @return [Boolean]
|
|
35
|
-
def supports_event?(event)
|
|
36
|
-
subscriptions.include?(event) || subscriptions.include?('*')
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def self.with_urls_for(event)
|
|
40
|
-
where(
|
|
41
|
-
case ActiveRecord::Base.connection.adapter_name
|
|
42
|
-
when 'Mysql2'
|
|
43
|
-
["('*' MEMBER OF(subscriptions) OR ? MEMBER OF(subscriptions))", event]
|
|
44
|
-
when 'PostgreSQL'
|
|
45
|
-
["subscriptions @> '[\"*\"]' OR subscriptions @> ?", [event].to_json]
|
|
46
|
-
when 'SQLite'
|
|
47
|
-
["subscriptions LIKE '%\"*\"%' OR subscriptions LIKE ?", "%#{event}%"]
|
|
48
|
-
end
|
|
49
|
-
)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def self.supported_events
|
|
53
|
-
@supported_events ||= begin
|
|
54
|
-
Rails.application.eager_load! if Rails.env.development?
|
|
55
|
-
Spree::Base.descendants.
|
|
56
|
-
select { |model| model.included_modules.include? Spree::Webhooks::HasWebhooks }.
|
|
57
|
-
sort_by { |model| model.name.demodulize.underscore }.
|
|
58
|
-
to_h do |model|
|
|
59
|
-
model_name = model.name.demodulize.underscore.to_sym
|
|
60
|
-
[model_name, model.supported_webhook_events]
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def name
|
|
66
|
-
url
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
private
|
|
70
|
-
|
|
71
|
-
def check_uri_path
|
|
72
|
-
uri = begin
|
|
73
|
-
URI.parse(url)
|
|
74
|
-
rescue URI::InvalidURIError
|
|
75
|
-
return false
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
errors.add(:url, 'the URL must have a path') if uri.blank? || uri.path.blank?
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def parse_subscriptions
|
|
82
|
-
return if subscriptions.blank? || subscriptions.is_a?(Array)
|
|
83
|
-
|
|
84
|
-
self.subscriptions = JSON.parse(subscriptions)
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
end
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Spree
|
|
4
|
-
module Webhooks
|
|
5
|
-
module Subscribers
|
|
6
|
-
class HandleRequest
|
|
7
|
-
def initialize(event_name:, subscriber:, webhook_payload_body:)
|
|
8
|
-
@event_name = event_name
|
|
9
|
-
@subscriber = subscriber
|
|
10
|
-
@webhook_payload_body = JSON.parse(webhook_payload_body)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def call
|
|
14
|
-
Rails.logger.debug(msg("sending to '#{url}'"))
|
|
15
|
-
Rails.logger.debug(msg("webhook_payload_body: #{body_with_event_metadata}"))
|
|
16
|
-
|
|
17
|
-
if request.unprocessable_uri?
|
|
18
|
-
return process(:warn, msg("can not make a request to '#{url}'"))
|
|
19
|
-
end
|
|
20
|
-
return process(:warn, msg("failed for '#{url}'")) if request.failed_request?
|
|
21
|
-
|
|
22
|
-
process
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
private
|
|
26
|
-
|
|
27
|
-
attr_reader :webhook_payload_body, :event_name, :subscriber
|
|
28
|
-
|
|
29
|
-
delegate :execution_time, :failed_request?, :response_code, :success?, :unprocessable_uri?, to: :request
|
|
30
|
-
delegate :id, :url, to: :subscriber
|
|
31
|
-
delegate :created_at, :id, to: :event, prefix: true
|
|
32
|
-
|
|
33
|
-
def process(log_level = nil, msg = nil)
|
|
34
|
-
Rails.logger.public_send(log_level, msg) if msg.present? && log_level.present?
|
|
35
|
-
make_request
|
|
36
|
-
update_event(msg)
|
|
37
|
-
nil
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def request
|
|
41
|
-
@request ||=
|
|
42
|
-
Spree::Webhooks::Subscribers::MakeRequest.new(
|
|
43
|
-
signature: event.signature_for(body_with_event_metadata),
|
|
44
|
-
url: url,
|
|
45
|
-
webhook_payload_body: body_with_event_metadata
|
|
46
|
-
)
|
|
47
|
-
end
|
|
48
|
-
alias make_request request
|
|
49
|
-
|
|
50
|
-
def body_with_event_metadata
|
|
51
|
-
webhook_payload_body.
|
|
52
|
-
merge(event_created_at: event_created_at, event_id: event_id, event_type: event.name).
|
|
53
|
-
to_json
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def event
|
|
57
|
-
@event ||= Spree::Webhooks::Event.create!(
|
|
58
|
-
name: event_name, subscriber_id: subscriber.id, url: url
|
|
59
|
-
)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def update_event(msg = nil)
|
|
63
|
-
event.update(
|
|
64
|
-
execution_time: execution_time,
|
|
65
|
-
request_errors: msg,
|
|
66
|
-
response_code: response_code,
|
|
67
|
-
success: success?
|
|
68
|
-
)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def msg(msg)
|
|
72
|
-
"[SPREE WEBHOOKS] '#{event_name}' #{msg}"
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Spree
|
|
4
|
-
module Webhooks
|
|
5
|
-
module Subscribers
|
|
6
|
-
class MakeRequest
|
|
7
|
-
def initialize(signature:, url:, webhook_payload_body:)
|
|
8
|
-
@execution_time_in_milliseconds = 0
|
|
9
|
-
@signature = signature
|
|
10
|
-
@url = url
|
|
11
|
-
@webhook_payload_body = webhook_payload_body
|
|
12
|
-
@webhooks_timeout = ENV['SPREE_WEBHOOKS_TIMEOUT']
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def execution_time
|
|
16
|
-
request
|
|
17
|
-
@execution_time_in_milliseconds
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def failed_request?
|
|
21
|
-
(200...300).exclude?(response_code)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def response_code
|
|
25
|
-
request.code.to_i
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def success?
|
|
29
|
-
!unprocessable_uri? && !failed_request?
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def unprocessable_uri?
|
|
33
|
-
uri_path == '' && uri_host.nil? && uri_port.nil?
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
private
|
|
37
|
-
|
|
38
|
-
attr_reader :execution_time_in_milliseconds, :url, :webhook_payload_body, :webhooks_timeout
|
|
39
|
-
|
|
40
|
-
HEADERS = { 'Content-Type' => 'application/json' }.freeze
|
|
41
|
-
private_constant :HEADERS
|
|
42
|
-
|
|
43
|
-
delegate :host, :path, :port, to: :uri, prefix: true
|
|
44
|
-
|
|
45
|
-
def http
|
|
46
|
-
http = Net::HTTP.new(uri_host, uri_port)
|
|
47
|
-
http.read_timeout = webhooks_timeout.to_i if custom_read_timeout?
|
|
48
|
-
http.use_ssl = use_ssl?
|
|
49
|
-
http
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def request
|
|
53
|
-
req = Net::HTTP::Post.new(uri_path, HEADERS.merge('X-Spree-Hmac-SHA256' => @signature))
|
|
54
|
-
req.body = webhook_payload_body
|
|
55
|
-
@request ||= begin
|
|
56
|
-
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
57
|
-
request_result = http.request(req)
|
|
58
|
-
@execution_time_in_milliseconds = (Process.clock_gettime(Process::CLOCK_MONOTONIC) - start_time).in_milliseconds
|
|
59
|
-
request_result
|
|
60
|
-
end
|
|
61
|
-
rescue Errno::ECONNREFUSED, Net::ReadTimeout, SocketError
|
|
62
|
-
Class.new do
|
|
63
|
-
def self.code
|
|
64
|
-
'0'
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def custom_read_timeout?
|
|
70
|
-
webhooks_timeout.present?
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def use_ssl?
|
|
74
|
-
uri.scheme == 'https'
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def uri
|
|
78
|
-
URI(url)
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
module Webhooks
|
|
3
|
-
module Subscribers
|
|
4
|
-
class QueueRequests
|
|
5
|
-
prepend Spree::ServiceModule::Base
|
|
6
|
-
|
|
7
|
-
def call(event_name:, webhook_payload_body:, record: nil, **options)
|
|
8
|
-
filtered_subscribers(event_name, webhook_payload_body, record, options).each do |subscriber|
|
|
9
|
-
Spree::Webhooks::Subscribers::MakeRequestJob.perform_later(
|
|
10
|
-
webhook_payload_body, event_name, subscriber
|
|
11
|
-
)
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
private
|
|
16
|
-
|
|
17
|
-
def filtered_subscribers(event_name, webhook_payload_body, record, options)
|
|
18
|
-
Spree::Current.webhooks_subscribers.map do |subscriber|
|
|
19
|
-
if subscriber.supports_event?(event_name)
|
|
20
|
-
subscriber
|
|
21
|
-
end
|
|
22
|
-
end.compact
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
class CreateSpreeWebhooksTables < ActiveRecord::Migration[5.2]
|
|
2
|
-
def change
|
|
3
|
-
create_table :spree_webhooks_subscribers do |t|
|
|
4
|
-
t.string :url, null: false
|
|
5
|
-
t.boolean :active, default: false, index: true
|
|
6
|
-
|
|
7
|
-
if t.respond_to? :jsonb
|
|
8
|
-
t.jsonb :subscriptions
|
|
9
|
-
else
|
|
10
|
-
t.json :subscriptions
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
t.timestamps
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
class CreateSpreeWebhooksEvents < ActiveRecord::Migration[5.2]
|
|
2
|
-
def change
|
|
3
|
-
create_table :spree_webhooks_events do |t|
|
|
4
|
-
t.integer "execution_time"
|
|
5
|
-
t.string "name", null: false
|
|
6
|
-
t.string "request_errors"
|
|
7
|
-
t.string "response_code", index: true
|
|
8
|
-
t.belongs_to "subscriber", null: false, index: true
|
|
9
|
-
t.boolean "success", index: true
|
|
10
|
-
t.string "url", null: false
|
|
11
|
-
t.timestamps
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
FactoryBot.define do
|
|
2
|
-
factory :webhook_event, aliases: [:event], class: Spree::Webhooks::Event do
|
|
3
|
-
subscriber
|
|
4
|
-
|
|
5
|
-
execution_time { rand(1..99_999) }
|
|
6
|
-
name { 'order.canceled' }
|
|
7
|
-
request_errors { '' }
|
|
8
|
-
sequence(:url) { |n| "https://www.url#{n}.com/" }
|
|
9
|
-
|
|
10
|
-
trait :failed do
|
|
11
|
-
response_code { '500' }
|
|
12
|
-
success { false }
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
trait :successful do
|
|
16
|
-
response_code { '200' }
|
|
17
|
-
success { true }
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
trait :blank do
|
|
21
|
-
execution_time { nil }
|
|
22
|
-
request_errors { nil }
|
|
23
|
-
subscriber_id { nil }
|
|
24
|
-
url { nil }
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
FactoryBot.define do
|
|
2
|
-
factory :webhook_subscriber, aliases: [:subscriber, :webhooks_subscriber], class: Spree::Webhooks::Subscriber do
|
|
3
|
-
sequence(:url) { |n| "https://www.url#{n}.com/" }
|
|
4
|
-
|
|
5
|
-
trait :active do
|
|
6
|
-
active { true }
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
trait :inactive do
|
|
10
|
-
active { false }
|
|
11
|
-
end
|
|
12
|
-
end
|
|
13
|
-
end
|