spree_api 6.0.0.beta1 → 6.0.0.beta2
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/v3/admin/stock_receipt_actions.rb +1 -1
- data/app/controllers/concerns/spree/api/v3/admin_authentication.rb +11 -0
- data/app/controllers/concerns/spree/api/v3/orders/claim_actions.rb +2 -2
- data/app/controllers/concerns/spree/api/v3/orders/exchange_actions.rb +3 -3
- data/app/controllers/concerns/spree/api/v3/orders/post_sale_actions.rb +1 -1
- data/app/controllers/concerns/spree/api/v3/orders/return_actions.rb +3 -3
- data/app/controllers/spree/api/v3/admin/api_keys_controller.rb +2 -2
- data/app/controllers/spree/api/v3/admin/orders/refunds_controller.rb +1 -1
- data/app/controllers/spree/api/v3/admin/orders_controller.rb +14 -8
- data/app/controllers/spree/api/v3/admin/setup_controller.rb +9 -1
- data/app/controllers/spree/api/v3/admin/store_controller.rb +1 -0
- data/app/controllers/spree/api/v3/base_controller.rb +15 -0
- data/app/controllers/spree/api/v3/resource_controller.rb +16 -1
- data/app/controllers/spree/api/v3/seller/orders_controller.rb +1 -1
- data/app/serializers/spree/api/v3/admin/actor_serializer.rb +28 -0
- data/app/serializers/spree/api/v3/admin/api_key_serializer.rb +14 -1
- data/app/serializers/spree/api/v3/admin/claim_serializer.rb +3 -4
- data/app/serializers/spree/api/v3/admin/exchange_serializer.rb +3 -4
- data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +28 -1
- data/app/serializers/spree/api/v3/admin/order_serializer.rb +7 -24
- data/app/serializers/spree/api/v3/admin/refund_serializer.rb +6 -0
- data/app/serializers/spree/api/v3/admin/return_serializer.rb +2 -3
- data/app/serializers/spree/api/v3/admin/saved_report_serializer.rb +3 -4
- data/app/serializers/spree/api/v3/admin/stock_receipt_serializer.rb +2 -3
- data/app/serializers/spree/api/v3/admin/store_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/base_serializer.rb +22 -0
- data/lib/spree/api/configuration.rb +19 -14
- data/lib/spree/api/dependencies.rb +1 -0
- data/lib/spree/api/engine.rb +6 -0
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 66c29980b5060309f11b480faec57266dc0bf8423b03e820a365e346a7f5f8a7
|
|
4
|
+
data.tar.gz: 0b3f632bfaa9cb6158694e492c3c8266a7b4e5c05c942abad15ce45a99662aea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bde5239802c9065c5b69fd3473b424dc2245c446035ff5ee1dc9cc475ea1de6cfcfbeffd65d11659427df2de94883bed3ae671916605e30155eb7ae9291e7ffe
|
|
7
|
+
data.tar.gz: ad098d05870290fae2ac19c12cf4ed92d91584466723f7992f8ac45beba9669ee1ad1c4f2a3a61c1a292e66b6b2568e6b027b42ba959efcb8f7662e4a5774c8f
|
|
@@ -15,6 +15,17 @@ module Spree
|
|
|
15
15
|
Spree::Api::V3::JwtAuthentication::JWT_AUDIENCE_ADMIN
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# A key-authenticated write records the key: it is the thing that gets
|
|
19
|
+
# named, scoped and revoked, so it is what an order's `canceler` or a
|
|
20
|
+
# receipt's `received_by` should point at. A JWT request records the
|
|
21
|
+
# admin. When both credentials are present the JWT user wins here too,
|
|
22
|
+
# matching how permissions resolve below.
|
|
23
|
+
#
|
|
24
|
+
# @return [Object, nil]
|
|
25
|
+
def current_actor
|
|
26
|
+
try_spree_current_user || @current_api_key
|
|
27
|
+
end
|
|
28
|
+
|
|
18
29
|
# API-key-only requests bypass CanCanCan: the ScopedAuthorization
|
|
19
30
|
# concern is the authoritative gate (read_/write_ scopes per resource).
|
|
20
31
|
# JWT admin users keep CanCanCan abilities; if both credentials are
|
|
@@ -19,7 +19,7 @@ module Spree
|
|
|
19
19
|
|
|
20
20
|
# PATCH .../claims/:id/approve
|
|
21
21
|
def approve
|
|
22
|
-
run_workflow(Spree.claim_approve_workflow, approver:
|
|
22
|
+
run_workflow(Spree.claim_approve_workflow, approver: current_actor)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
# PATCH .../claims/:id/resolve — a refund, a replacement, or both.
|
|
@@ -29,7 +29,7 @@ module Spree
|
|
|
29
29
|
refund_method: params[:refund_method] || 'store_credit',
|
|
30
30
|
amount: params[:amount],
|
|
31
31
|
replacement_line_item_ids: replacement_line_item_ids,
|
|
32
|
-
resolver:
|
|
32
|
+
resolver: current_actor)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
# PATCH .../claims/:id/deny
|
|
@@ -21,14 +21,14 @@ module Spree
|
|
|
21
21
|
|
|
22
22
|
# PATCH .../exchanges/:id/approve
|
|
23
23
|
def approve
|
|
24
|
-
run_workflow(Spree.exchange_approve_workflow, approver:
|
|
24
|
+
run_workflow(Spree.exchange_approve_workflow, approver: current_actor)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# PATCH .../exchanges/:id/receive
|
|
28
28
|
def receive
|
|
29
29
|
run_workflow(Spree.exchange_receive_workflow,
|
|
30
30
|
items: items_for_receive,
|
|
31
|
-
received_by:
|
|
31
|
+
received_by: current_actor)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# PATCH .../exchanges/:id/fulfill — sends the replacement, settling
|
|
@@ -36,7 +36,7 @@ module Spree
|
|
|
36
36
|
def fulfill
|
|
37
37
|
run_workflow(Spree.exchange_fulfill_workflow,
|
|
38
38
|
refund_method: params[:refund_method] || 'store_credit',
|
|
39
|
-
refunder:
|
|
39
|
+
refunder: current_actor)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# PATCH .../exchanges/:id/cancel
|
|
@@ -21,14 +21,14 @@ module Spree
|
|
|
21
21
|
|
|
22
22
|
# PATCH .../returns/:id/approve
|
|
23
23
|
def approve
|
|
24
|
-
run_workflow(Spree.return_approve_workflow, approver:
|
|
24
|
+
run_workflow(Spree.return_approve_workflow, approver: current_actor)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# PATCH .../returns/:id/receive — what the warehouse actually counted.
|
|
28
28
|
def receive
|
|
29
29
|
run_workflow(Spree.return_receive_workflow,
|
|
30
30
|
items: items_for_receive,
|
|
31
|
-
received_by:
|
|
31
|
+
received_by: current_actor)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# PATCH .../returns/:id/refund
|
|
@@ -36,7 +36,7 @@ module Spree
|
|
|
36
36
|
run_workflow(Spree.return_refund_workflow,
|
|
37
37
|
amount: params[:amount],
|
|
38
38
|
refund_method: params[:refund_method] || 'original_payment',
|
|
39
|
-
refunder:
|
|
39
|
+
refunder: current_actor)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# PATCH .../returns/:id/cancel
|
|
@@ -47,7 +47,7 @@ module Spree
|
|
|
47
47
|
@resource = find_resource
|
|
48
48
|
authorize!(:update, @resource)
|
|
49
49
|
|
|
50
|
-
@resource.revoke!(
|
|
50
|
+
@resource.revoke!(current_actor)
|
|
51
51
|
render json: serialize_resource(@resource)
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -90,7 +90,7 @@ module Spree
|
|
|
90
90
|
# happens in `before_validation :generate_token` on the model.
|
|
91
91
|
def build_resource
|
|
92
92
|
scope.new(permitted_params).tap do |key|
|
|
93
|
-
key.created_by =
|
|
93
|
+
key.created_by = current_actor
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
|
|
@@ -26,7 +26,7 @@ module Spree
|
|
|
26
26
|
payment: payment,
|
|
27
27
|
amount: params[:amount],
|
|
28
28
|
reason: reason,
|
|
29
|
-
refunder:
|
|
29
|
+
refunder: current_actor,
|
|
30
30
|
# Names which order is being refunded. Only matters when the
|
|
31
31
|
# payment is shared by a split checkout, where it covers several
|
|
32
32
|
# and the payment cannot say which one this is for.
|
|
@@ -23,7 +23,7 @@ module Spree
|
|
|
23
23
|
result = Spree.order_create_service.call(
|
|
24
24
|
store: current_store,
|
|
25
25
|
customer: resolve_customer,
|
|
26
|
-
created_by:
|
|
26
|
+
created_by: current_actor,
|
|
27
27
|
params: order_create_params
|
|
28
28
|
)
|
|
29
29
|
|
|
@@ -73,7 +73,7 @@ module Spree
|
|
|
73
73
|
with_order_lock do
|
|
74
74
|
result = Spree.order_cancel_workflow.call(
|
|
75
75
|
order: @resource,
|
|
76
|
-
canceler:
|
|
76
|
+
canceler: current_actor,
|
|
77
77
|
reason: cancel_reason,
|
|
78
78
|
note: params[:cancel_note].presence,
|
|
79
79
|
refund_payments: params[:refund_payments].to_b,
|
|
@@ -92,7 +92,7 @@ module Spree
|
|
|
92
92
|
# PATCH /api/v3/admin/orders/:id/approve
|
|
93
93
|
def approve
|
|
94
94
|
with_order_lock do
|
|
95
|
-
@resource.approved_by(
|
|
95
|
+
@resource.approved_by(current_actor)
|
|
96
96
|
render json: serialize_resource(@resource.reload)
|
|
97
97
|
end
|
|
98
98
|
end
|
|
@@ -150,11 +150,13 @@ module Spree
|
|
|
150
150
|
end
|
|
151
151
|
|
|
152
152
|
# Override scope — Order uses SingleStoreResource (for_store).
|
|
153
|
-
# Variant prices are
|
|
154
|
-
# which this override bypasses; the
|
|
153
|
+
# Variant prices and each line's price list are named here rather
|
|
154
|
+
# than via scope_includes, which this override bypasses; the
|
|
155
|
+
# serializer reads both per row.
|
|
155
156
|
def scope
|
|
156
157
|
base = current_store.orders.accessible_by(current_ability, :show).
|
|
157
|
-
includes(line_items: { variant: :prices }).
|
|
158
|
+
includes(line_items: [{ variant: :prices }, { price_list: :catalog }]).
|
|
159
|
+
preload_associations_lazily
|
|
158
160
|
|
|
159
161
|
# Transient completion drafts (status draft + cart_id set) belong
|
|
160
162
|
# to in-flight checkouts, never to the admin. Admin drafts are the
|
|
@@ -190,7 +192,10 @@ module Spree
|
|
|
190
192
|
# only its id is reported and that comes off the order's own column.
|
|
191
193
|
# Variant prices ride along because the admin line-item serializer
|
|
192
194
|
# reads the base catalog price for every row (the negotiated-price
|
|
193
|
-
# comparison); without it each line costs its own price query.
|
|
195
|
+
# comparison); without it each line costs its own price query. Each
|
|
196
|
+
# line's price list and its catalog are named for the same reason —
|
|
197
|
+
# stated rather than left to ar_lazy_preload, which happens to cover
|
|
198
|
+
# them today.
|
|
194
199
|
def collection_includes
|
|
195
200
|
# `market` is the withdrawal deadline's other input. Fulfillments
|
|
196
201
|
# are loaded with their selected rate because the freight summary
|
|
@@ -198,7 +203,8 @@ module Spree
|
|
|
198
203
|
# fulfillment for a field that is nil on every parcel order.
|
|
199
204
|
[:customer, :channel, :seller, :external_references, :cancel_reason,
|
|
200
205
|
:market, { fulfillments: :selected_delivery_rate },
|
|
201
|
-
{ line_items: { variant: :prices }
|
|
206
|
+
{ line_items: [{ variant: :prices }, { price_list: :catalog }] },
|
|
207
|
+
{ po_document_attachment: :blob }]
|
|
202
208
|
end
|
|
203
209
|
|
|
204
210
|
# Read through the store's own vocabulary, so a reason belonging to
|
|
@@ -41,7 +41,7 @@ module Spree
|
|
|
41
41
|
# POST /api/v3/admin/auth/setup
|
|
42
42
|
# Body: { setup_token, email, password, password_confirmation?,
|
|
43
43
|
# first_name?, last_name?, store_name, country_code,
|
|
44
|
-
# locale?, currency? }
|
|
44
|
+
# locale?, currency?, sample_data? }
|
|
45
45
|
# Currency defaults to the country's own; an unknown code is refused
|
|
46
46
|
# rather than ignored, since the token is spent in the same request.
|
|
47
47
|
def create
|
|
@@ -63,6 +63,10 @@ module Spree
|
|
|
63
63
|
adopt_default_store(user, store)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
# Outside the lock: the loader needs the admin committed, and it
|
|
67
|
+
# runs for minutes, so it is queued rather than awaited.
|
|
68
|
+
Spree::SampleData::LoadJob.perform_later if sample_data_requested?
|
|
69
|
+
|
|
66
70
|
refresh_token = Spree::RefreshToken.create_for(user, audience: JWT_AUDIENCE_ADMIN, request_env: request_env_for_token)
|
|
67
71
|
set_refresh_cookie(refresh_token)
|
|
68
72
|
render json: auth_response(user)
|
|
@@ -167,6 +171,10 @@ module Spree
|
|
|
167
171
|
)
|
|
168
172
|
end
|
|
169
173
|
|
|
174
|
+
def sample_data_requested?
|
|
175
|
+
ActiveModel::Type::Boolean.new.cast(params[:sample_data]) == true
|
|
176
|
+
end
|
|
177
|
+
|
|
170
178
|
def admin_user_params
|
|
171
179
|
params.permit(:email, :password, :password_confirmation, :first_name, :last_name)
|
|
172
180
|
end
|
|
@@ -81,6 +81,7 @@ module Spree
|
|
|
81
81
|
:preferred_storefront_access,
|
|
82
82
|
:preferred_storefront_url,
|
|
83
83
|
:preferred_guest_checkout,
|
|
84
|
+
:preferred_always_include_confirm_step,
|
|
84
85
|
:preferred_company_field_enabled,
|
|
85
86
|
:preferred_address_requires_company,
|
|
86
87
|
:preferred_address_requires_phone,
|
|
@@ -93,6 +93,21 @@ module Spree
|
|
|
93
93
|
|
|
94
94
|
alias try_spree_current_user spree_current_user
|
|
95
95
|
|
|
96
|
+
# Who performed this request, for the associations that record it —
|
|
97
|
+
# `canceler`, `approver`, `created_by` and their siblings. Answers the
|
|
98
|
+
# signed-in user here; the Admin branch prefers the authenticating
|
|
99
|
+
# secret key, because a key-made write must record the key rather than
|
|
100
|
+
# nobody (see docs/plans/6.0-action-actors.md).
|
|
101
|
+
#
|
|
102
|
+
# Deliberately not `current_api_key || current_user` at this level: on
|
|
103
|
+
# a Store API request the key is the storefront's publishable one, and
|
|
104
|
+
# a customer's own action is not the storefront's.
|
|
105
|
+
#
|
|
106
|
+
# @return [Object, nil] a member of Spree.actor_classes
|
|
107
|
+
def current_actor
|
|
108
|
+
try_spree_current_user
|
|
109
|
+
end
|
|
110
|
+
|
|
96
111
|
# CanCanCan ability
|
|
97
112
|
# @return [Spree::Ability]
|
|
98
113
|
def current_ability
|
|
@@ -176,10 +176,25 @@ module Spree
|
|
|
176
176
|
def build_resource
|
|
177
177
|
resource = resource_scope.new
|
|
178
178
|
resource.assign_attributes(permitted_params) if create_workflow.nil?
|
|
179
|
-
resource.created_by =
|
|
179
|
+
resource.created_by = creator_for(resource) if resource.respond_to?(:created_by_id)
|
|
180
180
|
resource
|
|
181
181
|
end
|
|
182
182
|
|
|
183
|
+
# The actor to stamp on a new record, or nil when this model cannot
|
|
184
|
+
# hold it. `created_by` is polymorphic on the order operations and
|
|
185
|
+
# still admin-user-only elsewhere until 6.1, so a key-authenticated
|
|
186
|
+
# create of a gift card records nobody rather than raising — the same
|
|
187
|
+
# thing it recorded before keys became actors.
|
|
188
|
+
def creator_for(resource)
|
|
189
|
+
actor = current_actor
|
|
190
|
+
return actor if actor.nil?
|
|
191
|
+
|
|
192
|
+
association = resource.class.reflect_on_association(:created_by)
|
|
193
|
+
return actor if association.nil? || association.polymorphic?
|
|
194
|
+
|
|
195
|
+
actor.is_a?(association.klass) ? actor : nil
|
|
196
|
+
end
|
|
197
|
+
|
|
183
198
|
# The relation a new record is built on: a nested resource's parent
|
|
184
199
|
# association, the store's own association for this model, or the
|
|
185
200
|
# class itself for genuinely global data (countries, roles).
|
|
@@ -43,7 +43,7 @@ module Spree
|
|
|
43
43
|
with_order_lock do
|
|
44
44
|
result = Spree.order_cancel_workflow.call(
|
|
45
45
|
order: @resource,
|
|
46
|
-
canceler:
|
|
46
|
+
canceler: current_actor,
|
|
47
47
|
reason: cancel_reason,
|
|
48
48
|
note: params[:cancel_note].presence,
|
|
49
49
|
refund_payments: params[:refund_payments].to_b,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Admin
|
|
5
|
+
# Whoever performed an action — an admin user, an API key, or a class
|
|
6
|
+
# an extension registered in `Spree.actor_classes`.
|
|
7
|
+
#
|
|
8
|
+
# One shape for every kind, so a client renders "cancelled by" the same
|
|
9
|
+
# way whether a person clicked the button or a warehouse connector
|
|
10
|
+
# called the endpoint. See docs/plans/6.0-action-actors.md.
|
|
11
|
+
class ActorSerializer < V3::BaseSerializer
|
|
12
|
+
typelize type: [:string, enum: Spree::Actor::BUILT_IN_KINDS, enum_type_name: 'ActorKind'],
|
|
13
|
+
label: [:string, nullable: true]
|
|
14
|
+
|
|
15
|
+
# `admin_user` / `api_key`, never the Ruby class name.
|
|
16
|
+
attribute :type do |actor|
|
|
17
|
+
actor.actor_kind
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# What a timeline shows: a person's name or email, a key's name.
|
|
21
|
+
attribute :label do |actor|
|
|
22
|
+
actor.actor_label
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -19,6 +19,8 @@ module Spree
|
|
|
19
19
|
revoked_at: [:string, nullable: true],
|
|
20
20
|
last_used_at: [:string, nullable: true],
|
|
21
21
|
created_by_email: [:string, nullable: true],
|
|
22
|
+
created_by_type: [:string, nullable: true, enum: Spree::Actor::BUILT_IN_KINDS, enum_type_name: 'ActorKind'],
|
|
23
|
+
created_by_label: [:string, nullable: true],
|
|
22
24
|
channel_id: [:string, nullable: true]
|
|
23
25
|
|
|
24
26
|
attributes :name, :key_type, :token_prefix, :scopes,
|
|
@@ -37,8 +39,19 @@ module Spree
|
|
|
37
39
|
key.plaintext_token
|
|
38
40
|
end
|
|
39
41
|
|
|
42
|
+
# A key can be minted by another key, which has no email — so the
|
|
43
|
+
# address is answered only for the actors that have one, and
|
|
44
|
+
# `created_by_label` is what a list column should render.
|
|
40
45
|
attribute :created_by_email do |key|
|
|
41
|
-
key.created_by
|
|
46
|
+
key.created_by.try(:email)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
attribute :created_by_type do |key|
|
|
50
|
+
Spree::Base.polymorphic_api_type(key.created_by_type)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
attribute :created_by_label do |key|
|
|
54
|
+
key.created_by.try(:actor_label)
|
|
42
55
|
end
|
|
43
56
|
end
|
|
44
57
|
end
|
|
@@ -7,13 +7,12 @@ module Spree
|
|
|
7
7
|
class ClaimSerializer < V3::ClaimSerializer
|
|
8
8
|
typelize memo: [:string, nullable: true],
|
|
9
9
|
metadata: 'Record<string, unknown>',
|
|
10
|
-
created_by_id: [:string, nullable: true]
|
|
10
|
+
created_by_id: [:string, nullable: true],
|
|
11
|
+
created_by_type: [:string, nullable: true, enum: Spree::Actor::BUILT_IN_KINDS, enum_type_name: 'ActorKind']
|
|
11
12
|
|
|
12
13
|
attributes :memo, :metadata, created_at: :iso8601, updated_at: :iso8601
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
claim.created_by&.prefixed_id
|
|
16
|
-
end
|
|
15
|
+
actor_attributes :created_by
|
|
17
16
|
|
|
18
17
|
many :claim_line_items,
|
|
19
18
|
resource: proc { Spree.api.admin_claim_line_item_serializer },
|
|
@@ -8,7 +8,8 @@ module Spree
|
|
|
8
8
|
typelize memo: [:string, nullable: true],
|
|
9
9
|
metadata: 'Record<string, unknown>',
|
|
10
10
|
stock_location_id: [:string, nullable: true],
|
|
11
|
-
created_by_id: [:string, nullable: true]
|
|
11
|
+
created_by_id: [:string, nullable: true],
|
|
12
|
+
created_by_type: [:string, nullable: true, enum: Spree::Actor::BUILT_IN_KINDS, enum_type_name: 'ActorKind']
|
|
12
13
|
|
|
13
14
|
attributes :memo, :metadata, created_at: :iso8601, updated_at: :iso8601
|
|
14
15
|
|
|
@@ -16,9 +17,7 @@ module Spree
|
|
|
16
17
|
exchange.stock_location&.prefixed_id
|
|
17
18
|
end
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
exchange.created_by&.prefixed_id
|
|
21
|
-
end
|
|
20
|
+
actor_attributes :created_by
|
|
22
21
|
|
|
23
22
|
many :exchange_line_items,
|
|
24
23
|
resource: proc { Spree.api.admin_exchange_line_item_serializer },
|
|
@@ -20,7 +20,11 @@ module Spree
|
|
|
20
20
|
cost_price: [:string, nullable: true],
|
|
21
21
|
tax_category_id: [:string, nullable: true],
|
|
22
22
|
price_source: [:string, nullable: true],
|
|
23
|
-
catalog_price: [:string, nullable: true]
|
|
23
|
+
catalog_price: [:string, nullable: true],
|
|
24
|
+
price_list_id: [:string, nullable: true],
|
|
25
|
+
price_list_name: [:string, nullable: true],
|
|
26
|
+
catalog_id: [:string, nullable: true],
|
|
27
|
+
catalog_name: [:string, nullable: true]
|
|
24
28
|
|
|
25
29
|
# price_source is operational provenance — admin-only, never on the
|
|
26
30
|
# store serializer.
|
|
@@ -33,6 +37,29 @@ module Spree
|
|
|
33
37
|
line_item.variant&.amount_in(line_item.currency)&.to_s
|
|
34
38
|
end
|
|
35
39
|
|
|
40
|
+
# Which agreement priced this line. The list id is stamped on the
|
|
41
|
+
# row and never re-resolved (the same reason `catalog_price` above
|
|
42
|
+
# is a base price); the catalog is whichever one owns that list
|
|
43
|
+
# now, so moving a list between catalogs re-labels past orders.
|
|
44
|
+
# Empty on a shop-price or hand-negotiated line. Names ride along
|
|
45
|
+
# beside the ids so a page of lines does not cost a request each to
|
|
46
|
+
# label, as `company_name` already does.
|
|
47
|
+
attribute :price_list_id do |line_item|
|
|
48
|
+
line_item.price_list&.prefixed_id
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
attribute :price_list_name do |line_item|
|
|
52
|
+
line_item.price_list&.name
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
attribute :catalog_id do |line_item|
|
|
56
|
+
line_item.price_list&.catalog&.prefixed_id
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
attribute :catalog_name do |line_item|
|
|
60
|
+
line_item.price_list&.catalog&.name
|
|
61
|
+
end
|
|
62
|
+
|
|
36
63
|
attribute :cost_price do |line_item|
|
|
37
64
|
line_item.cost_price&.to_s
|
|
38
65
|
end
|
|
@@ -49,7 +49,10 @@ module Spree
|
|
|
49
49
|
store_owner_notification_delivered: :boolean,
|
|
50
50
|
internal_note: [:string, nullable: true], internal_note_html: [:string, nullable: true],
|
|
51
51
|
approver_id: [:string, nullable: true],
|
|
52
|
+
approver_type: [:string, nullable: true, enum: Spree::Actor::BUILT_IN_KINDS, enum_type_name: 'ActorKind'],
|
|
52
53
|
canceler_id: [:string, nullable: true], created_by_id: [:string, nullable: true],
|
|
54
|
+
canceler_type: [:string, nullable: true, enum: Spree::Actor::BUILT_IN_KINDS, enum_type_name: 'ActorKind'],
|
|
55
|
+
created_by_type: [:string, nullable: true, enum: Spree::Actor::BUILT_IN_KINDS, enum_type_name: 'ActorKind'],
|
|
53
56
|
customer_id: [:string, nullable: true],
|
|
54
57
|
preferred_stock_location_id: [:string, nullable: true],
|
|
55
58
|
canceled_at: [:string, nullable: true], approved_at: [:string, nullable: true],
|
|
@@ -128,13 +131,10 @@ module Spree
|
|
|
128
131
|
order.internal_note.presence
|
|
129
132
|
end
|
|
130
133
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
attribute :canceler_id do |order|
|
|
136
|
-
order.canceler&.prefixed_id
|
|
137
|
-
end
|
|
134
|
+
# Who approved, cancelled and opened it — a member of staff, or the
|
|
135
|
+
# API key an integration called with. An order cancelled through a
|
|
136
|
+
# secret key names the key.
|
|
137
|
+
actor_attributes :approver, :canceler, :created_by
|
|
138
138
|
|
|
139
139
|
attribute :cancel_reason_id do |order|
|
|
140
140
|
order.cancel_reason&.prefixed_id
|
|
@@ -147,10 +147,6 @@ module Spree
|
|
|
147
147
|
order.cancel_reason&.name
|
|
148
148
|
end
|
|
149
149
|
|
|
150
|
-
attribute :created_by_id do |order|
|
|
151
|
-
order.created_by&.prefixed_id
|
|
152
|
-
end
|
|
153
|
-
|
|
154
150
|
attribute :customer_id do |order|
|
|
155
151
|
order.customer&.prefixed_id
|
|
156
152
|
end
|
|
@@ -189,23 +185,10 @@ module Spree
|
|
|
189
185
|
resource: proc { Spree.api.admin_customer_serializer },
|
|
190
186
|
if: proc { expand?('customer') }
|
|
191
187
|
|
|
192
|
-
# Staff actors, not customers — these point at Spree.admin_user_class.
|
|
193
|
-
one :approver,
|
|
194
|
-
resource: proc { Spree.api.admin_admin_user_serializer },
|
|
195
|
-
if: proc { expand?('approver') }
|
|
196
|
-
|
|
197
|
-
one :canceler,
|
|
198
|
-
resource: proc { Spree.api.admin_admin_user_serializer },
|
|
199
|
-
if: proc { expand?('canceler') }
|
|
200
|
-
|
|
201
188
|
one :cancel_reason,
|
|
202
189
|
resource: proc { Spree.api.admin_order_cancellation_reason_serializer },
|
|
203
190
|
if: proc { expand?('cancel_reason') }
|
|
204
191
|
|
|
205
|
-
one :created_by,
|
|
206
|
-
resource: proc { Spree.api.admin_admin_user_serializer },
|
|
207
|
-
if: proc { expand?('created_by') }
|
|
208
|
-
|
|
209
192
|
|
|
210
193
|
many :returns,
|
|
211
194
|
resource: proc { Spree.api.admin_return_serializer },
|
|
@@ -5,11 +5,17 @@ module Spree
|
|
|
5
5
|
class RefundSerializer < V3::RefundSerializer
|
|
6
6
|
typelize payment_id: [:string, nullable: true],
|
|
7
7
|
refund_reason_id: [:string, nullable: true],
|
|
8
|
+
refunder_id: [:string, nullable: true],
|
|
9
|
+
refunder_type: [:string, nullable: true, enum: Spree::Actor::BUILT_IN_KINDS, enum_type_name: 'ActorKind'],
|
|
8
10
|
metadata: 'Record<string, unknown>'
|
|
9
11
|
|
|
10
12
|
attributes :metadata,
|
|
11
13
|
created_at: :iso8601, updated_at: :iso8601
|
|
12
14
|
|
|
15
|
+
# Who issued it — an admin user, or the API key an integration
|
|
16
|
+
# refunded through.
|
|
17
|
+
actor_attributes :refunder
|
|
18
|
+
|
|
13
19
|
one :payment,
|
|
14
20
|
resource: proc { Spree.api.admin_payment_serializer },
|
|
15
21
|
if: proc { expand?('payment') }
|
|
@@ -10,6 +10,7 @@ module Spree
|
|
|
10
10
|
documents: "Array<{ kind: string; url: string }>",
|
|
11
11
|
stock_location_id: [:string, nullable: true],
|
|
12
12
|
created_by_id: [:string, nullable: true],
|
|
13
|
+
created_by_type: [:string, nullable: true, enum: Spree::Actor::BUILT_IN_KINDS, enum_type_name: 'ActorKind'],
|
|
13
14
|
refunded_total: :string,
|
|
14
15
|
refundable_total: :string
|
|
15
16
|
|
|
@@ -19,9 +20,7 @@ module Spree
|
|
|
19
20
|
return_record.stock_location&.prefixed_id
|
|
20
21
|
end
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
return_record.created_by&.prefixed_id
|
|
24
|
-
end
|
|
23
|
+
actor_attributes :created_by
|
|
25
24
|
|
|
26
25
|
attribute :refunded_total do |return_record|
|
|
27
26
|
return_record.refunded_total.to_s
|
|
@@ -20,11 +20,10 @@ module Spree
|
|
|
20
20
|
report.user&.prefixed_id
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
+
# The same "name, else email" rule every actor answers, so a report's
|
|
24
|
+
# author and an order's canceler read alike.
|
|
23
25
|
attribute :author_name do |report|
|
|
24
|
-
|
|
25
|
-
next nil unless user
|
|
26
|
-
|
|
27
|
-
user.full_name.presence || user.email
|
|
26
|
+
report.user&.actor_label
|
|
28
27
|
end
|
|
29
28
|
end
|
|
30
29
|
end
|
|
@@ -12,6 +12,7 @@ module Spree
|
|
|
12
12
|
receivable_type: [:string, enum: %w[purchase_order stock_transfer]],
|
|
13
13
|
receivable_id: :string,
|
|
14
14
|
received_by_id: 'string | null',
|
|
15
|
+
received_by_type: [:string, nullable: true, enum: Spree::Actor::BUILT_IN_KINDS, enum_type_name: 'ActorKind'],
|
|
15
16
|
items_count: :number,
|
|
16
17
|
quantity_accepted_total: :number,
|
|
17
18
|
quantity_rejected_total: :number,
|
|
@@ -31,9 +32,7 @@ module Spree
|
|
|
31
32
|
receipt.receivable&.prefixed_id
|
|
32
33
|
end
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
receipt.received_by.try(:prefixed_id)
|
|
36
|
-
end
|
|
35
|
+
actor_attributes :received_by
|
|
37
36
|
|
|
38
37
|
attribute :items_count do |receipt|
|
|
39
38
|
receipt.items.size
|
|
@@ -22,6 +22,7 @@ module Spree
|
|
|
22
22
|
preferred_unit_system: :string,
|
|
23
23
|
preferred_storefront_access: :string,
|
|
24
24
|
preferred_guest_checkout: :boolean,
|
|
25
|
+
preferred_always_include_confirm_step: :boolean,
|
|
25
26
|
preferred_company_field_enabled: :boolean,
|
|
26
27
|
preferred_address_requires_company: :boolean,
|
|
27
28
|
preferred_address_requires_phone: :boolean,
|
|
@@ -69,6 +70,7 @@ module Spree
|
|
|
69
70
|
:preferred_unit_system,
|
|
70
71
|
:preferred_storefront_access,
|
|
71
72
|
:preferred_guest_checkout,
|
|
73
|
+
:preferred_always_include_confirm_step,
|
|
72
74
|
:preferred_company_field_enabled,
|
|
73
75
|
:preferred_address_requires_company,
|
|
74
76
|
:preferred_address_requires_phone,
|
|
@@ -27,6 +27,28 @@ module Spree
|
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
# Declares the wire form of an `acted_by` association: the actor's
|
|
31
|
+
# prefixed id, the kind of actor it is, and the expansion. Both
|
|
32
|
+
# halves read off the columns, so naming an actor costs no query and
|
|
33
|
+
# the id and the kind always describe the same row (see
|
|
34
|
+
# docs/plans/6.0-action-actors.md).
|
|
35
|
+
#
|
|
36
|
+
# Declare the matching `typelize` entries alongside, as with any
|
|
37
|
+
# other attribute.
|
|
38
|
+
def self.actor_attributes(*names)
|
|
39
|
+
names.each do |name|
|
|
40
|
+
attribute(:"#{name}_id") { |object| object.acted_by_prefixed_id(name) }
|
|
41
|
+
|
|
42
|
+
attribute(:"#{name}_type") do |object|
|
|
43
|
+
Spree::Base.polymorphic_api_type(object.acted_by_type(name))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
one name,
|
|
47
|
+
resource: proc { Spree.api.admin_actor_serializer },
|
|
48
|
+
if: proc { expand?(name.to_s) }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
30
52
|
# Plain decimal notation. BigDecimal renders 0.06 as "0.6e-1", which
|
|
31
53
|
# is what a client would otherwise print beside a unit.
|
|
32
54
|
#
|
|
@@ -3,25 +3,30 @@ require 'spree/core/preferences/runtime_configuration'
|
|
|
3
3
|
module Spree
|
|
4
4
|
module Api
|
|
5
5
|
class Configuration < Spree::Preferences::RuntimeConfiguration
|
|
6
|
-
preference :jwt_expiration, :integer, default: 3600 # 1 hour in seconds (customer/store JWT default)
|
|
7
|
-
preference :admin_jwt_expiration, :integer, default: 300 # 5 minutes — admin tokens have higher blast radius
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
preference :jwt_expiration, :integer, default: 3600, env: 'SPREE_JWT_EXPIRATION' # 1 hour in seconds (customer/store JWT default)
|
|
7
|
+
preference :admin_jwt_expiration, :integer, default: 300, env: 'SPREE_ADMIN_JWT_EXPIRATION' # 5 minutes — admin tokens have higher blast radius
|
|
8
|
+
# SPREE_JWT_SECRET_KEY resolves inside the preference, so it sits ahead of
|
|
9
|
+
# the Rails credentials fallback in Spree::Api::V3::JwtAuthentication —
|
|
10
|
+
# an operator who sets it means it. The older, unprefixed JWT_SECRET_KEY
|
|
11
|
+
# keeps its place behind credentials so existing deployments are
|
|
12
|
+
# unaffected.
|
|
13
|
+
preference :jwt_secret_key, :string, default: nil, env: 'SPREE_JWT_SECRET_KEY'
|
|
14
|
+
preference :refresh_token_expiry, :integer, default: 2_592_000, env: 'SPREE_REFRESH_TOKEN_EXPIRY' # 30 days in seconds
|
|
10
15
|
|
|
11
16
|
# Rate limiting
|
|
12
|
-
preference :rate_limit_per_key, :integer, default: 300 # per publishable API key + client IP (per visitor)
|
|
13
|
-
preference :rate_limit_per_secret_key, :integer, default: 600 # per secret API key, across the whole API
|
|
14
|
-
preference :rate_limit_window, :integer, default: 60 # window in seconds
|
|
15
|
-
preference :rate_limit_login, :integer, default: 5 # per IP
|
|
16
|
-
preference :rate_limit_register, :integer, default: 3 # per IP
|
|
17
|
-
preference :rate_limit_refresh, :integer, default: 10 # per IP
|
|
18
|
-
preference :rate_limit_password_reset, :integer, default: 3 # per IP
|
|
17
|
+
preference :rate_limit_per_key, :integer, default: 300, env: 'SPREE_RATE_LIMIT_PER_KEY' # per publishable API key + client IP (per visitor)
|
|
18
|
+
preference :rate_limit_per_secret_key, :integer, default: 600, env: 'SPREE_RATE_LIMIT_PER_SECRET_KEY' # per secret API key, across the whole API
|
|
19
|
+
preference :rate_limit_window, :integer, default: 60, env: 'SPREE_RATE_LIMIT_WINDOW' # window in seconds
|
|
20
|
+
preference :rate_limit_login, :integer, default: 5, env: 'SPREE_RATE_LIMIT_LOGIN' # per IP
|
|
21
|
+
preference :rate_limit_register, :integer, default: 3, env: 'SPREE_RATE_LIMIT_REGISTER' # per IP
|
|
22
|
+
preference :rate_limit_refresh, :integer, default: 10, env: 'SPREE_RATE_LIMIT_REFRESH' # per IP
|
|
23
|
+
preference :rate_limit_password_reset, :integer, default: 3, env: 'SPREE_RATE_LIMIT_PASSWORD_RESET' # per IP
|
|
19
24
|
|
|
20
25
|
# Request body size limit in bytes
|
|
21
|
-
preference :max_request_body_size, :integer, default: 102_400 # 100KB
|
|
26
|
+
preference :max_request_body_size, :integer, default: 102_400, env: 'SPREE_MAX_REQUEST_BODY_SIZE' # 100KB
|
|
22
27
|
|
|
23
|
-
preference :webhooks_enabled, :boolean, default: true
|
|
24
|
-
preference :webhooks_verify_ssl, :boolean, default: !Rails.env.development
|
|
28
|
+
preference :webhooks_enabled, :boolean, default: true, env: 'SPREE_WEBHOOKS_ENABLED'
|
|
29
|
+
preference :webhooks_verify_ssl, :boolean, default: !Rails.env.development?, env: 'SPREE_WEBHOOKS_VERIFY_SSL'
|
|
25
30
|
end
|
|
26
31
|
end
|
|
27
32
|
end
|
|
@@ -181,6 +181,7 @@ module Spree
|
|
|
181
181
|
admin_catalog_order_minimum_serializer: 'Spree::Api::V3::Admin::CatalogOrderMinimumSerializer',
|
|
182
182
|
admin_tax_exemption_certificate_serializer: 'Spree::Api::V3::Admin::TaxExemptionCertificateSerializer',
|
|
183
183
|
admin_admin_user_serializer: 'Spree::Api::V3::Admin::AdminUserSerializer',
|
|
184
|
+
admin_actor_serializer: 'Spree::Api::V3::Admin::ActorSerializer',
|
|
184
185
|
admin_seller_team_member_serializer: 'Spree::Api::V3::Admin::SellerTeamMemberSerializer',
|
|
185
186
|
seller_serializer: 'Spree::Api::V3::SellerSerializer',
|
|
186
187
|
seller_profile_serializer: 'Spree::Api::V3::Seller::ProfileSerializer',
|
data/lib/spree/api/engine.rb
CHANGED
|
@@ -24,6 +24,12 @@ module Spree
|
|
|
24
24
|
Spree.subscribers << Spree::WebhookEventSubscriber
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# Runs after initializers so an explicitly assigned preference — which
|
|
28
|
+
# wins over the environment — is never rejected for a stale env var.
|
|
29
|
+
config.after_initialize do
|
|
30
|
+
Spree::Api::Configuration.validate_env!(Spree::Api::Config)
|
|
31
|
+
end
|
|
32
|
+
|
|
27
33
|
# Warn in production if no dedicated JWT secret key is configured
|
|
28
34
|
config.after_initialize do
|
|
29
35
|
next unless Rails.env.production?
|
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: 6.0.0.
|
|
4
|
+
version: 6.0.0.beta2
|
|
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-09-
|
|
11
|
+
date: 2026-09-17 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: 6.0.0.
|
|
75
|
+
version: 6.0.0.beta2
|
|
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: 6.0.0.
|
|
82
|
+
version: 6.0.0.beta2
|
|
83
83
|
description: Spree Commerce Store / Admin API and HTTP webhooks
|
|
84
84
|
email:
|
|
85
85
|
- hello@spreecommerce.org
|
|
@@ -393,6 +393,7 @@ files:
|
|
|
393
393
|
- app/models/spree/api_key_ability.rb
|
|
394
394
|
- app/serializers/concerns/spree/api/v3/admin/translatable.rb
|
|
395
395
|
- app/serializers/spree/api/v3/address_serializer.rb
|
|
396
|
+
- app/serializers/spree/api/v3/admin/actor_serializer.rb
|
|
396
397
|
- app/serializers/spree/api/v3/admin/address_serializer.rb
|
|
397
398
|
- app/serializers/spree/api/v3/admin/admin_user_serializer.rb
|
|
398
399
|
- app/serializers/spree/api/v3/admin/allowed_origin_serializer.rb
|
|
@@ -698,9 +699,9 @@ licenses:
|
|
|
698
699
|
- BSD-3-Clause
|
|
699
700
|
metadata:
|
|
700
701
|
bug_tracker_uri: https://github.com/spree/spree/issues
|
|
701
|
-
changelog_uri: https://github.com/spree/spree/releases/tag/v6.0.0.
|
|
702
|
+
changelog_uri: https://github.com/spree/spree/releases/tag/v6.0.0.beta2
|
|
702
703
|
documentation_uri: https://docs.spreecommerce.org/
|
|
703
|
-
source_code_uri: https://github.com/spree/spree/tree/v6.0.0.
|
|
704
|
+
source_code_uri: https://github.com/spree/spree/tree/v6.0.0.beta2
|
|
704
705
|
post_install_message:
|
|
705
706
|
rdoc_options: []
|
|
706
707
|
require_paths:
|