spree_api 5.5.2 → 5.5.4
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/spree/api/v3/store/carts_controller.rb +15 -2
- data/app/controllers/spree/api/v3/store/products/filters_controller.rb +7 -6
- data/app/jobs/spree/webhook_delivery_job.rb +6 -1
- data/app/serializers/spree/api/v3/admin/webhook_delivery_serializer.rb +6 -1
- data/app/serializers/spree/api/v3/product_filter_availability_option_serializer.rb +11 -0
- data/app/serializers/spree/api/v3/product_filter_availability_serializer.rb +15 -0
- data/app/serializers/spree/api/v3/product_filter_category_option_serializer.rb +14 -0
- data/app/serializers/spree/api/v3/product_filter_category_serializer.rb +15 -0
- data/app/serializers/spree/api/v3/product_filter_option_serializer.rb +18 -0
- data/app/serializers/spree/api/v3/product_filter_option_value_serializer.rb +17 -0
- data/app/serializers/spree/api/v3/product_filter_price_range_serializer.rb +15 -0
- data/app/serializers/spree/api/v3/product_filter_sort_option_serializer.rb +11 -0
- data/app/serializers/spree/api/v3/product_filters_serializer.rb +36 -0
- data/app/services/spree/webhooks/deliver_webhook.rb +6 -2
- data/app/subscribers/spree/webhook_event_subscriber.rb +5 -2
- data/lib/spree/api/dependencies.rb +9 -0
- metadata +15 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 25e42383bfb6759f7fd2f6831ab3d168ccfc049bae4a9c01e9dc8c63bb4a6bfc
|
|
4
|
+
data.tar.gz: a982087e479d23574fea03267796a054d79536fb7dc2fb7aa090452e7aab4f6e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7ee5ac82a202296ec6c83bf2d0a37b201f7905f6b7fa9caca1de5f8c4874717e44495cc37ef15bf5e545ae30a8464dccc8d9c70603836ab8a50e5752551e70f3
|
|
7
|
+
data.tar.gz: fa9e45fa9b4b269fb11143db92afe6786cfaa369d172ad8a7aae4e860f3efa48d5cde09785bc916cfb1fcfbed4eba38d1d5c74caa1cd7ff8cc962c2743dba367
|
|
@@ -83,10 +83,15 @@ module Spree
|
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
# PATCH /api/v3/store/carts/:id/associate
|
|
86
|
-
# Associates a guest cart with the currently authenticated user
|
|
87
|
-
# Requires: JWT authentication + cart ID in URL
|
|
86
|
+
# Associates a guest cart with the currently authenticated user.
|
|
87
|
+
# Requires: JWT authentication + cart ID in URL + possession of the
|
|
88
|
+
# cart's token (x-spree-token). The token is the credential that
|
|
89
|
+
# authorizes claiming the cart, so it is required even when the cart
|
|
90
|
+
# is already the caller's own.
|
|
88
91
|
def associate
|
|
89
92
|
@cart = find_cart_for_association
|
|
93
|
+
authorize!(:update, @cart, cart_token)
|
|
94
|
+
require_cart_token!
|
|
90
95
|
|
|
91
96
|
result = Spree.cart_associate_service.call(guest_order: @cart, user: current_user, guest_only: true)
|
|
92
97
|
|
|
@@ -173,6 +178,14 @@ module Spree
|
|
|
173
178
|
def find_cart_for_association
|
|
174
179
|
current_store.carts.where(user: [nil, current_user]).find_by_prefix_id!(params[:id])
|
|
175
180
|
end
|
|
181
|
+
|
|
182
|
+
# Claiming a cart requires presenting its token, even when the caller
|
|
183
|
+
# already owns it — the token, not ownership, is the claim credential.
|
|
184
|
+
def require_cart_token!
|
|
185
|
+
valid = cart_token.present? && cart_token == @cart.token
|
|
186
|
+
|
|
187
|
+
raise CanCan::AccessDenied.new(nil, :update, @cart) unless valid
|
|
188
|
+
end
|
|
176
189
|
end
|
|
177
190
|
end
|
|
178
191
|
end
|
|
@@ -14,17 +14,18 @@ module Spree
|
|
|
14
14
|
filters: (search_filters || {}).merge('_category' => category)
|
|
15
15
|
)
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
filters: result.filters,
|
|
19
|
-
sort_options: result.sort_options,
|
|
20
|
-
default_sort: result.default_sort,
|
|
21
|
-
total_count: result.total_count
|
|
22
|
-
}
|
|
17
|
+
serialize_resource(result)
|
|
23
18
|
end
|
|
24
19
|
|
|
25
20
|
render json: json
|
|
26
21
|
end
|
|
27
22
|
|
|
23
|
+
protected
|
|
24
|
+
|
|
25
|
+
def serializer_class
|
|
26
|
+
Spree.api.product_filters_serializer
|
|
27
|
+
end
|
|
28
|
+
|
|
28
29
|
private
|
|
29
30
|
|
|
30
31
|
def filters_cache_key
|
|
@@ -13,10 +13,15 @@ module Spree
|
|
|
13
13
|
|
|
14
14
|
# Accept optional second argument for backward compatibility with jobs
|
|
15
15
|
# enqueued before this change was deployed.
|
|
16
|
-
|
|
16
|
+
#
|
|
17
|
+
# `payload_secrets` carries credentials withheld from the persisted payload
|
|
18
|
+
# so the outgoing request body stays complete. See
|
|
19
|
+
# {Spree::WebhookPayloadRedaction}.
|
|
20
|
+
def perform(delivery_id, _deprecated_secret_key = nil, payload_secrets: nil)
|
|
17
21
|
delivery = Spree::WebhookDelivery.find_by(id: delivery_id)
|
|
18
22
|
return if delivery.nil?
|
|
19
23
|
|
|
24
|
+
delivery.payload_secrets = payload_secrets
|
|
20
25
|
secret_key = delivery.webhook_endpoint.secret_key
|
|
21
26
|
Spree::Webhooks::DeliverWebhook.call(delivery: delivery, secret_key: secret_key)
|
|
22
27
|
end
|
|
@@ -25,10 +25,15 @@ module Spree
|
|
|
25
25
|
|
|
26
26
|
attributes :event_name, :event_id, :response_code, :execution_time,
|
|
27
27
|
:error_type, :request_errors, :response_body, :success,
|
|
28
|
-
:payload,
|
|
29
28
|
created_at: :iso8601, updated_at: :iso8601,
|
|
30
29
|
delivered_at: :iso8601
|
|
31
30
|
|
|
31
|
+
# Redacted again on read: deliveries written before payload redaction
|
|
32
|
+
# shipped still hold live credentials in the column.
|
|
33
|
+
attribute :payload do |delivery|
|
|
34
|
+
Spree::WebhookPayloadRedaction.split(delivery.payload).first
|
|
35
|
+
end
|
|
36
|
+
|
|
32
37
|
attribute :webhook_endpoint_id do |delivery|
|
|
33
38
|
delivery.webhook_endpoint&.prefixed_id
|
|
34
39
|
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
class ProductFilterAvailabilitySerializer < BaseSerializer
|
|
5
|
+
typelize id: :string,
|
|
6
|
+
type: "'availability'",
|
|
7
|
+
options: [:ProductFilterAvailabilityOption, multi: true]
|
|
8
|
+
|
|
9
|
+
attributes :id, :type
|
|
10
|
+
|
|
11
|
+
many :options, resource: proc { Spree.api.product_filter_availability_option_serializer }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
class ProductFilterCategoryOptionSerializer < BaseSerializer
|
|
5
|
+
typelize id: :string,
|
|
6
|
+
name: :string,
|
|
7
|
+
permalink: :string,
|
|
8
|
+
count: :number
|
|
9
|
+
|
|
10
|
+
attributes :id, :name, :permalink, :count
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
class ProductFilterCategorySerializer < BaseSerializer
|
|
5
|
+
typelize id: :string,
|
|
6
|
+
type: "'category'",
|
|
7
|
+
options: [:ProductFilterCategoryOption, multi: true]
|
|
8
|
+
|
|
9
|
+
attributes :id, :type
|
|
10
|
+
|
|
11
|
+
many :options, resource: proc { Spree.api.product_filter_category_option_serializer }
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
class ProductFilterOptionSerializer < BaseSerializer
|
|
5
|
+
typelize id: :string,
|
|
6
|
+
type: "'option'",
|
|
7
|
+
name: :string,
|
|
8
|
+
label: :string,
|
|
9
|
+
kind: :string,
|
|
10
|
+
options: [:ProductFilterOptionValue, multi: true]
|
|
11
|
+
|
|
12
|
+
attributes :id, :type, :name, :label, :kind
|
|
13
|
+
|
|
14
|
+
many :options, resource: proc { Spree.api.product_filter_option_value_serializer }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
class ProductFilterOptionValueSerializer < BaseSerializer
|
|
5
|
+
typelize id: :string,
|
|
6
|
+
name: :string,
|
|
7
|
+
label: :string,
|
|
8
|
+
position: :number,
|
|
9
|
+
color_code: [:string, nullable: true],
|
|
10
|
+
image_url: [:string, nullable: true],
|
|
11
|
+
count: :number
|
|
12
|
+
|
|
13
|
+
attributes :id, :name, :label, :position, :color_code, :image_url, :count
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
class ProductFilterPriceRangeSerializer < BaseSerializer
|
|
5
|
+
typelize id: :string,
|
|
6
|
+
type: "'price_range'",
|
|
7
|
+
min: :number,
|
|
8
|
+
max: :number,
|
|
9
|
+
currency: :string
|
|
10
|
+
|
|
11
|
+
attributes :id, :type, :min, :max, :currency
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
class ProductFiltersSerializer < BaseSerializer
|
|
5
|
+
typelize default_sort: :string,
|
|
6
|
+
total_count: :number,
|
|
7
|
+
filters: 'Array<ProductFilterPriceRange | ProductFilterAvailability | ProductFilterOption | ProductFilterCategory>',
|
|
8
|
+
sort_options: [:ProductFilterSortOption, { multi: true }]
|
|
9
|
+
|
|
10
|
+
attributes :default_sort, :total_count
|
|
11
|
+
|
|
12
|
+
attribute :filters do |result|
|
|
13
|
+
result.filters.filter_map do |filter|
|
|
14
|
+
filter_type = filter[:type]
|
|
15
|
+
serializer_class = case filter_type
|
|
16
|
+
when 'price_range'
|
|
17
|
+
Spree.api.product_filter_price_range_serializer
|
|
18
|
+
when 'availability'
|
|
19
|
+
Spree.api.product_filter_availability_serializer
|
|
20
|
+
when 'option'
|
|
21
|
+
Spree.api.product_filter_option_serializer
|
|
22
|
+
when 'category'
|
|
23
|
+
Spree.api.product_filter_category_serializer
|
|
24
|
+
else
|
|
25
|
+
raise ArgumentError, "Unknown filter type: #{filter_type.inspect}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
serializer_class.new(filter, params: params).serializable_hash
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
many :sort_options, resource: proc { Spree.api.product_filter_sort_option_serializer }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -56,7 +56,7 @@ module Spree
|
|
|
56
56
|
'X-Spree-Webhook-Timestamp' => webhook_timestamp.to_s,
|
|
57
57
|
'X-Spree-Webhook-Event' => @delivery.event_name
|
|
58
58
|
}
|
|
59
|
-
body =
|
|
59
|
+
body = payload_json
|
|
60
60
|
http_options = { open_timeout: TIMEOUT, read_timeout: TIMEOUT, verify_mode: ssl_verify_mode }
|
|
61
61
|
|
|
62
62
|
# SSRF protection is disabled in development so webhooks can reach
|
|
@@ -77,10 +77,14 @@ module Spree
|
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def generate_signature
|
|
80
|
-
payload_json = @delivery.payload.to_json
|
|
81
80
|
OpenSSL::HMAC.hexdigest('SHA256', @secret_key, "#{webhook_timestamp}.#{payload_json}")
|
|
82
81
|
end
|
|
83
82
|
|
|
83
|
+
# Memoized so the signature is computed over exactly the bytes sent.
|
|
84
|
+
def payload_json
|
|
85
|
+
@payload_json ||= @delivery.deliverable_payload.to_json
|
|
86
|
+
end
|
|
87
|
+
|
|
84
88
|
def webhook_timestamp
|
|
85
89
|
@webhook_timestamp ||= Time.current.to_i
|
|
86
90
|
end
|
|
@@ -50,13 +50,16 @@ module Spree
|
|
|
50
50
|
)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
# Live credentials go over the wire but never into the delivery log.
|
|
54
|
+
persisted_payload, secrets = Spree::WebhookPayloadRedaction.split(payload)
|
|
55
|
+
|
|
53
56
|
delivery = endpoint.webhook_deliveries.create!(
|
|
54
57
|
event_name: event.name,
|
|
55
58
|
event_id: event.id,
|
|
56
|
-
payload:
|
|
59
|
+
payload: persisted_payload
|
|
57
60
|
)
|
|
58
61
|
|
|
59
|
-
Spree::WebhookDeliveryJob.perform_later(delivery.id)
|
|
62
|
+
Spree::WebhookDeliveryJob.perform_later(delivery.id, payload_secrets: secrets)
|
|
60
63
|
rescue ActiveRecord::RecordNotUnique
|
|
61
64
|
# Race condition: another thread already created this delivery — safe to ignore
|
|
62
65
|
rescue StandardError => e
|
|
@@ -136,6 +136,15 @@ module Spree
|
|
|
136
136
|
custom_field_serializer: 'Spree::Api::V3::CustomFieldSerializer',
|
|
137
137
|
shipping_category_serializer: 'Spree::Api::V3::ShippingCategorySerializer',
|
|
138
138
|
tax_category_serializer: 'Spree::Api::V3::TaxCategorySerializer',
|
|
139
|
+
product_filters_serializer: 'Spree::Api::V3::ProductFiltersSerializer',
|
|
140
|
+
product_filter_price_range_serializer: 'Spree::Api::V3::ProductFilterPriceRangeSerializer',
|
|
141
|
+
product_filter_availability_serializer: 'Spree::Api::V3::ProductFilterAvailabilitySerializer',
|
|
142
|
+
product_filter_availability_option_serializer: 'Spree::Api::V3::ProductFilterAvailabilityOptionSerializer',
|
|
143
|
+
product_filter_option_serializer: 'Spree::Api::V3::ProductFilterOptionSerializer',
|
|
144
|
+
product_filter_option_value_serializer: 'Spree::Api::V3::ProductFilterOptionValueSerializer',
|
|
145
|
+
product_filter_category_serializer: 'Spree::Api::V3::ProductFilterCategorySerializer',
|
|
146
|
+
product_filter_category_option_serializer: 'Spree::Api::V3::ProductFilterCategoryOptionSerializer',
|
|
147
|
+
product_filter_sort_option_serializer: 'Spree::Api::V3::ProductFilterSortOptionSerializer',
|
|
139
148
|
|
|
140
149
|
# v3 event serializers (for models without Store API endpoints yet)
|
|
141
150
|
asset_serializer: 'Spree::Api::V3::AssetSerializer',
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spree_api
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.5.
|
|
4
|
+
version: 5.5.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Vendo Connect Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rswag-specs
|
|
@@ -72,14 +72,14 @@ dependencies:
|
|
|
72
72
|
requirements:
|
|
73
73
|
- - '='
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: 5.5.
|
|
75
|
+
version: 5.5.4
|
|
76
76
|
type: :runtime
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
80
|
- - '='
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: 5.5.
|
|
82
|
+
version: 5.5.4
|
|
83
83
|
description: Spree's API
|
|
84
84
|
email:
|
|
85
85
|
- hello@spreecommerce.org
|
|
@@ -305,6 +305,15 @@ files:
|
|
|
305
305
|
- app/serializers/spree/api/v3/policy_serializer.rb
|
|
306
306
|
- app/serializers/spree/api/v3/price_history_serializer.rb
|
|
307
307
|
- app/serializers/spree/api/v3/price_serializer.rb
|
|
308
|
+
- app/serializers/spree/api/v3/product_filter_availability_option_serializer.rb
|
|
309
|
+
- app/serializers/spree/api/v3/product_filter_availability_serializer.rb
|
|
310
|
+
- app/serializers/spree/api/v3/product_filter_category_option_serializer.rb
|
|
311
|
+
- app/serializers/spree/api/v3/product_filter_category_serializer.rb
|
|
312
|
+
- app/serializers/spree/api/v3/product_filter_option_serializer.rb
|
|
313
|
+
- app/serializers/spree/api/v3/product_filter_option_value_serializer.rb
|
|
314
|
+
- app/serializers/spree/api/v3/product_filter_price_range_serializer.rb
|
|
315
|
+
- app/serializers/spree/api/v3/product_filter_sort_option_serializer.rb
|
|
316
|
+
- app/serializers/spree/api/v3/product_filters_serializer.rb
|
|
308
317
|
- app/serializers/spree/api/v3/product_publication_serializer.rb
|
|
309
318
|
- app/serializers/spree/api/v3/product_serializer.rb
|
|
310
319
|
- app/serializers/spree/api/v3/promotion_serializer.rb
|
|
@@ -355,9 +364,9 @@ licenses:
|
|
|
355
364
|
- BSD-3-Clause
|
|
356
365
|
metadata:
|
|
357
366
|
bug_tracker_uri: https://github.com/spree/spree/issues
|
|
358
|
-
changelog_uri: https://github.com/spree/spree/releases/tag/v5.5.
|
|
367
|
+
changelog_uri: https://github.com/spree/spree/releases/tag/v5.5.4
|
|
359
368
|
documentation_uri: https://docs.spreecommerce.org/
|
|
360
|
-
source_code_uri: https://github.com/spree/spree/tree/v5.5.
|
|
369
|
+
source_code_uri: https://github.com/spree/spree/tree/v5.5.4
|
|
361
370
|
post_install_message:
|
|
362
371
|
rdoc_options: []
|
|
363
372
|
require_paths:
|