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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/spree/api/v2/product_list_includes.rb +10 -22
  3. data/app/controllers/concerns/spree/api/v2/storefront/order_concern.rb +1 -1
  4. data/app/controllers/spree/api/v2/platform/classifications_controller.rb +1 -1
  5. data/app/controllers/spree/api/v2/resource_controller.rb +8 -5
  6. data/app/controllers/spree/api/v2/storefront/products_controller.rb +1 -4
  7. data/app/helpers/spree/api/v2/display_money_helper.rb +30 -10
  8. data/app/jobs/spree/webhook_delivery_job.rb +16 -0
  9. data/app/paginators/spree/api/paginate.rb +68 -0
  10. data/app/serializers/spree/api/v2/platform/product_serializer.rb +12 -4
  11. data/app/serializers/spree/api/v2/platform/variant_serializer.rb +12 -4
  12. data/app/serializers/spree/v2/storefront/product_serializer.rb +13 -5
  13. data/app/serializers/spree/v2/storefront/taxon_serializer.rb +1 -1
  14. data/app/serializers/spree/v2/storefront/variant_serializer.rb +12 -4
  15. data/app/services/spree/webhooks/deliver_webhook.rb +83 -0
  16. data/app/subscribers/spree/webhook_event_subscriber.rb +67 -0
  17. data/config/routes.rb +0 -2
  18. data/lib/generators/spree/api/install/install_generator.rb +24 -0
  19. data/lib/spree/api/configuration.rb +3 -0
  20. data/lib/spree/api/dependencies.rb +1 -1
  21. data/lib/spree/api/engine.rb +6 -0
  22. data/lib/spree/api/testing_support/matchers/webhooks.rb +1 -67
  23. data/lib/spree/api/testing_support/spree_webhooks.rb +1 -9
  24. data/lib/spree/api.rb +3 -0
  25. data/lib/spree_api.rb +0 -2
  26. metadata +25 -33
  27. data/app/controllers/spree/api/v2/platform/webhooks/events_controller.rb +0 -25
  28. data/app/controllers/spree/api/v2/platform/webhooks/subscribers_controller.rb +0 -25
  29. data/app/jobs/spree/webhooks/subscribers/make_request_job.rb +0 -17
  30. data/app/models/concerns/spree/webhooks/has_webhooks.rb +0 -95
  31. data/app/models/spree/order/webhooks.rb +0 -39
  32. data/app/models/spree/payment/webhooks.rb +0 -23
  33. data/app/models/spree/product/webhooks.rb +0 -42
  34. data/app/models/spree/shipment/webhooks.rb +0 -19
  35. data/app/models/spree/stock_item/webhooks.rb +0 -40
  36. data/app/models/spree/stock_movement/webhooks.rb +0 -49
  37. data/app/models/spree/variant/webhooks.rb +0 -25
  38. data/app/models/spree/webhooks/base.rb +0 -11
  39. data/app/models/spree/webhooks/event.rb +0 -20
  40. data/app/models/spree/webhooks/event_signature.rb +0 -33
  41. data/app/models/spree/webhooks/subscriber.rb +0 -88
  42. data/app/serializers/spree/api/v2/platform/webhooks/event_serializer.rb +0 -15
  43. data/app/serializers/spree/api/v2/platform/webhooks/subscriber_serializer.rb +0 -13
  44. data/app/services/spree/webhooks/subscribers/handle_request.rb +0 -77
  45. data/app/services/spree/webhooks/subscribers/make_request.rb +0 -83
  46. data/app/services/spree/webhooks/subscribers/queue_requests.rb +0 -27
  47. data/db/migrate/20210902162826_create_spree_webhooks_tables.rb +0 -16
  48. data/db/migrate/20211025162826_create_spree_webhooks_events.rb +0 -14
  49. data/db/migrate/20221221122100_add_secret_key_to_spree_webhooks_subscribers.rb +0 -5
  50. data/db/migrate/20230116204600_backfill_secret_key_for_spree_webhooks_subscribers.rb +0 -5
  51. data/db/migrate/20230116205000_change_secret_key_to_non_null_column.rb +0 -5
  52. data/lib/spree/api/testing_support/factories/webhook_event_factory.rb +0 -27
  53. 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,15 +0,0 @@
1
- module Spree
2
- module Api
3
- module V2
4
- module Platform
5
- module Webhooks
6
- class EventSerializer < BaseSerializer
7
- include ResourceSerializerConcern
8
-
9
- belongs_to :subscriber
10
- end
11
- end
12
- end
13
- end
14
- end
15
- end
@@ -1,13 +0,0 @@
1
- module Spree
2
- module Api
3
- module V2
4
- module Platform
5
- module Webhooks
6
- class SubscriberSerializer < BaseSerializer
7
- include ResourceSerializerConcern
8
- end
9
- end
10
- end
11
- end
12
- end
13
- 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,5 +0,0 @@
1
- class AddSecretKeyToSpreeWebhooksSubscribers < ActiveRecord::Migration[6.1]
2
- def change
3
- add_column :spree_webhooks_subscribers, :secret_key, :string, null: true
4
- end
5
- end
@@ -1,5 +0,0 @@
1
- class BackfillSecretKeyForSpreeWebhooksSubscribers < ActiveRecord::Migration[6.1]
2
- def change
3
- Spree::Webhooks::Subscriber.where(secret_key: nil).find_each(&:regenerate_secret_key)
4
- end
5
- end
@@ -1,5 +0,0 @@
1
- class ChangeSecretKeyToNonNullColumn < ActiveRecord::Migration[6.1]
2
- def change
3
- change_column_null :spree_webhooks_subscribers, :secret_key, false
4
- end
5
- 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