spree_api 4.5.5 → 4.6.0

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/spree/api/v2/product_list_includes.rb +2 -1
  3. data/app/controllers/spree/api/v2/base_controller.rb +2 -0
  4. data/app/controllers/spree/api/v2/data_feeds/google_controller.rb +24 -0
  5. data/app/controllers/spree/api/v2/platform/data_feeds_controller.rb +15 -0
  6. data/app/controllers/spree/api/v2/resource_controller.rb +1 -1
  7. data/app/controllers/spree/api/v2/storefront/products_controller.rb +1 -1
  8. data/app/controllers/spree/api/v2/storefront/taxons_controller.rb +4 -3
  9. data/app/helpers/spree/api/v2/collection_options_helpers.rb +1 -1
  10. data/app/models/spree/webhooks/event.rb +8 -0
  11. data/app/models/spree/webhooks/event_signature.rb +33 -0
  12. data/app/models/spree/webhooks/subscriber.rb +2 -1
  13. data/app/serializers/spree/api/v2/platform/data_feed_serializer.rb +13 -0
  14. data/app/serializers/spree/api/v2/platform/user_serializer.rb +1 -1
  15. data/app/serializers/spree/v2/storefront/product_serializer.rb +4 -0
  16. data/app/serializers/spree/v2/storefront/taxon_serializer.rb +4 -0
  17. data/app/serializers/spree/v2/storefront/user_serializer.rb +1 -1
  18. data/app/services/spree/webhooks/subscribers/handle_request.rb +5 -1
  19. data/app/services/spree/webhooks/subscribers/make_request.rb +3 -2
  20. data/brakeman.ignore +7 -0
  21. data/config/routes.rb +8 -0
  22. data/db/migrate/20221221122100_add_secret_key_to_spree_webhooks_subscribers.rb +5 -0
  23. data/db/migrate/20230116204600_backfill_secret_key_for_spree_webhooks_subscribers.rb +5 -0
  24. data/db/migrate/20230116205000_change_secret_key_to_non_null_column.rb +5 -0
  25. data/docs/oauth/index.yml +5 -5
  26. data/docs/v2/platform/index.yaml +329 -1
  27. data/docs/v2/storefront/index.yaml +129 -105
  28. data/lib/spree/api/dependencies.rb +86 -129
  29. metadata +15 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b4744bbb79cc84cafabdf5daa80d40600a688ba99fe5c4a6b6fc0f07ea00351
4
- data.tar.gz: 765911a0d3a372569fa939902d201a5317606af2aae1cb4cafb9abed76c61be1
3
+ metadata.gz: 6324feb2fed1bf0f72361c80626006ad1de52f6516c0799993ab83ae61251166
4
+ data.tar.gz: c0006992c93b9bdafcc04cd0ac4934f92b08d0c5056b5981e54cbe847cb0a969
5
5
  SHA512:
6
- metadata.gz: f701f555fbdd2c77bbd701284fef558b049f9ac2b030735c2745086be486984d1d131d00816390f70016a6135bfcb045835123d4261836b2301a896f95492f06
7
- data.tar.gz: f612d41f16fde6b35ccb7b270eed3485de6032010f04579376178fcc3eec916bd6b2f587e67d71a27146048090b4a10c8b9256ccbcb761efe40900477df6b1e7
6
+ metadata.gz: b4e71a7c23dc66e6a93df0fea81e8882bf2934de213ecc0d4eee6a260622dbdd82a71c46d8bddcb71147b8b8c3395786ea06dbd3b2f8f23353adf5c8a2139238
7
+ data.tar.gz: cc8b2ecef98e57554dce21a75608562389d65c4a9a8b049ea26cdf8e38d1729ddf30fde834b1cf4c928b1ae970597a2039a303ef2fd7a5cd665eadafc9f4fc75
@@ -8,7 +8,8 @@ module Spree
8
8
  option_types: [],
9
9
  variant_images: [],
10
10
  master: product_variant_includes,
11
- variants: product_variant_includes
11
+ variants: product_variant_includes,
12
+ translations: []
12
13
  }
13
14
  end
14
15
 
@@ -82,6 +82,8 @@ module Spree
82
82
  @spree_current_user ||= doorkeeper_token.resource_owner
83
83
  end
84
84
 
85
+ alias try_spree_current_user spree_current_user # for compatibility with spree_legacy_frontend
86
+
85
87
  def spree_authorize!(action, subject, *args)
86
88
  authorize!(action, subject, *args)
87
89
  end
@@ -0,0 +1,24 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module DataFeeds
5
+ class GoogleController < ::Spree::Api::V2::BaseController
6
+ def rss_feed
7
+ send_data data_feeds_google_rss_service.value[:file], filename: 'products.rss', type: 'text/xml'
8
+ end
9
+
10
+ private
11
+
12
+ def settings
13
+ @settings ||= Spree::DataFeed::Google.find_by!(store: current_store, slug: params[:slug], active: true)
14
+ end
15
+
16
+ def data_feeds_google_rss_service
17
+ Spree::Dependencies.data_feeds_google_rss_service.constantize.new.call(settings)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,15 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Platform
5
+ class DataFeedsController < ResourceController
6
+ private
7
+
8
+ def model_class
9
+ Spree::DataFeed
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -35,7 +35,7 @@ module Spree
35
35
  base_scope = model_class.for_store(current_store)
36
36
  base_scope = base_scope.accessible_by(current_ability, :show) unless skip_cancancan
37
37
  base_scope = base_scope.includes(scope_includes) if scope_includes.any? && action_name == 'index'
38
- base_scope
38
+ model_class.include?(TranslatableResource) ? base_scope.i18n : base_scope
39
39
  end
40
40
 
41
41
  def scope_includes
@@ -16,7 +16,7 @@ module Spree
16
16
  end
17
17
 
18
18
  def resource
19
- @resource ||= scope.find_by(slug: params[:id]) || scope.find(params[:id])
19
+ @resource ||= find_with_fallback_default_locale { scope.find_by(slug: params[:id]) } || scope.find(params[:id])
20
20
  end
21
21
 
22
22
  def collection_sorter
@@ -22,7 +22,7 @@ module Spree
22
22
  end
23
23
 
24
24
  def resource
25
- @resource ||= scope.find_by(permalink: params[:id]) || scope.find(params[:id])
25
+ @resource ||= find_with_fallback_default_locale { scope.find_by(permalink: params[:id]) } || scope.find(params[:id])
26
26
  end
27
27
 
28
28
  def model_class
@@ -30,13 +30,14 @@ module Spree
30
30
  end
31
31
 
32
32
  def scope_includes
33
- node_includes = %i[icon parent taxonomy]
33
+ node_includes = %i[icon parent taxonomy translations]
34
34
 
35
35
  {
36
36
  parent: node_includes,
37
37
  children: node_includes,
38
38
  taxonomy: [root: node_includes],
39
- icon: [attachment_attachment: :blob]
39
+ icon: [attachment_attachment: :blob],
40
+ translations: []
40
41
  }
41
42
  end
42
43
 
@@ -23,7 +23,7 @@ module Spree
23
23
  # leaving this method in public scope so it's still possible to modify
24
24
  # those params to support non-standard non-JSON API parameters
25
25
  def collection_permitted_params
26
- params.permit(:format, :page, :per_page, :sort, :include, fields: {}, filter: {})
26
+ params.permit(:format, :page, :per_page, :sort, :include, :locale, fields: {}, filter: {})
27
27
  end
28
28
 
29
29
  private
@@ -7,6 +7,14 @@ module Spree
7
7
 
8
8
  self.whitelisted_ransackable_associations = %w[subscriber]
9
9
  self.whitelisted_ransackable_attributes = %w[name request_errors response_code success url]
10
+
11
+ # Computes the base64-encoded HMAC SHA256 signature of the event for the given payload.
12
+ #
13
+ # @param payload [Hash, String] The payload for to the webhook subscriber.
14
+ # @return [String]
15
+ def signature_for(payload)
16
+ EventSignature.new(self, payload).computed_signature
17
+ end
10
18
  end
11
19
  end
12
20
  end
@@ -0,0 +1,33 @@
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
@@ -5,10 +5,11 @@ module Spree
5
5
  include Spree::VendorConcern
6
6
  end
7
7
 
8
+ has_secure_token :secret_key
9
+
8
10
  has_many :events, inverse_of: :subscriber
9
11
 
10
12
  validates :url, 'spree/url': true, presence: true
11
-
12
13
  validate :check_uri_path
13
14
 
14
15
  self.whitelisted_ransackable_attributes = %w[active subscriptions url]
@@ -0,0 +1,13 @@
1
+ module Spree
2
+ module Api
3
+ module V2
4
+ module Platform
5
+ class DataFeedSerializer < BaseSerializer
6
+ set_type :data_feed
7
+
8
+ attributes :name, :type, :slug, :active
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -5,7 +5,7 @@ module Spree
5
5
  class UserSerializer < BaseSerializer
6
6
  set_type :user
7
7
 
8
- attributes :email, :first_name, :last_name, :created_at, :updated_at, :public_metadata, :private_metadata
8
+ attributes :email, :first_name, :last_name, :created_at, :updated_at, :public_metadata, :private_metadata, :selected_locale
9
9
 
10
10
  attribute :average_order_value do |user, params|
11
11
  price_stats(user.report_values_for(:average_order_value, params[:store]))
@@ -44,6 +44,10 @@ module Spree
44
44
  display_compare_at_price(product, params[:currency])
45
45
  end
46
46
 
47
+ attribute :localized_slugs do |product, params|
48
+ product.localized_slugs_for_store(params[:store])
49
+ end
50
+
47
51
  has_many :variants
48
52
  has_many :option_types
49
53
  has_many :product_properties
@@ -19,6 +19,10 @@ module Spree
19
19
  taxon.leaf?
20
20
  end
21
21
 
22
+ attribute :localized_slugs do |taxon, params|
23
+ taxon.localized_slugs_for_store(params[:store])
24
+ end
25
+
22
26
  belongs_to :parent, record_type: :taxon, serializer: :taxon
23
27
  belongs_to :taxonomy, record_type: :taxonomy
24
28
 
@@ -4,7 +4,7 @@ module Spree
4
4
  class UserSerializer < BaseSerializer
5
5
  set_type :user
6
6
 
7
- attributes :email, :first_name, :last_name, :public_metadata
7
+ attributes :email, :first_name, :selected_locale, :last_name, :public_metadata
8
8
 
9
9
  attribute :store_credits do |user|
10
10
  user.total_available_store_credit
@@ -39,7 +39,11 @@ module Spree
39
39
 
40
40
  def request
41
41
  @request ||=
42
- Spree::Webhooks::Subscribers::MakeRequest.new(webhook_payload_body: body_with_event_metadata, url: url)
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
+ )
43
47
  end
44
48
  alias make_request request
45
49
 
@@ -4,8 +4,9 @@ module Spree
4
4
  module Webhooks
5
5
  module Subscribers
6
6
  class MakeRequest
7
- def initialize(url:, webhook_payload_body:)
7
+ def initialize(signature:, url:, webhook_payload_body:)
8
8
  @execution_time_in_milliseconds = 0
9
+ @signature = signature
9
10
  @url = url
10
11
  @webhook_payload_body = webhook_payload_body
11
12
  @webhooks_timeout = ENV['SPREE_WEBHOOKS_TIMEOUT']
@@ -49,7 +50,7 @@ module Spree
49
50
  end
50
51
 
51
52
  def request
52
- req = Net::HTTP::Post.new(uri_path, HEADERS)
53
+ req = Net::HTTP::Post.new(uri_path, HEADERS.merge('X-Spree-Hmac-SHA256' => @signature))
53
54
  req.body = webhook_payload_body
54
55
  @request ||= begin
55
56
  start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
data/brakeman.ignore ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "ignored_warnings": [
3
+
4
+ ],
5
+ "updated": "2022-12-29 09:29:46 +0100",
6
+ "brakeman_version": "5.4.0"
7
+ }
data/config/routes.rb CHANGED
@@ -196,6 +196,9 @@ Spree::Core::Engine.add_routes do
196
196
  # Store API
197
197
  resources :stores
198
198
 
199
+ # Data Feeds API
200
+ resources :data_feeds
201
+
199
202
  # Configurations API
200
203
  resources :payment_methods
201
204
  resources :shipping_categories
@@ -207,6 +210,11 @@ Spree::Core::Engine.add_routes do
207
210
  resources :subscribers
208
211
  end
209
212
  end
213
+
214
+ namespace :data_feeds do
215
+ # google data feed API
216
+ get '/google/:slug', to: 'google#rss_feed'
217
+ end
210
218
  end
211
219
  end
212
220
  end
@@ -0,0 +1,5 @@
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
@@ -0,0 +1,5 @@
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
@@ -0,0 +1,5 @@
1
+ class ChangeSecretKeyToNonNullColumn < ActiveRecord::Migration[6.1]
2
+ def change
3
+ change_column_null :spree_webhooks_subscribers, :secret_key, false
4
+ end
5
+ end
data/docs/oauth/index.yml CHANGED
@@ -2,7 +2,7 @@ openapi: 3.0.3
2
2
  servers:
3
3
  - url: 'https://demo.spreecommerce.org'
4
4
  description: demo
5
- - url: 'http://localhost:3000'
5
+ - url: 'http://localhost:4000'
6
6
  description: localhost
7
7
  info:
8
8
  version: 1.0.0
@@ -139,7 +139,7 @@ components:
139
139
  - expires_in
140
140
  - refresh_token
141
141
  - created_at
142
- x-internal: true
142
+ x-internal: false
143
143
  CreateTokenBody:
144
144
  type: object
145
145
  x-examples:
@@ -152,7 +152,7 @@ components:
152
152
  username: spree@example.com
153
153
  password: spree123
154
154
  scope: admin
155
- x-internal: true
155
+ x-internal: false
156
156
  title: 'Create a new token (grant_type: password)'
157
157
  description: ''
158
158
  properties:
@@ -186,7 +186,7 @@ components:
186
186
  example-1:
187
187
  grant_type: refresh_token
188
188
  refresh_token: SqJDIwX00fehqHxS6xmb-kzqAlrYe_0EHgekMexVT8k
189
- x-internal: true
189
+ x-internal: false
190
190
  title: 'Create a new token (grant_type: client_credentials)'
191
191
  description: ''
192
192
  properties:
@@ -213,7 +213,7 @@ components:
213
213
  example-1:
214
214
  grant_type: refresh_token
215
215
  refresh_token: SqJDIwX00fehqHxS6xmb-kzqAlrYe_0EHgekMexVT8k
216
- x-internal: true
216
+ x-internal: false
217
217
  title: 'Refresh an existing token (grant_type: refresh_token)'
218
218
  description: ''
219
219
  properties: