spree_api 4.5.5 → 4.6.0

Sign up to get free protection for your applications and to get access to all the features.
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: