spree_api 6.0.3 → 6.0.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/concerns/spree/api/v3/idempotent.rb +10 -1
- data/app/controllers/concerns/spree/api/v3/security_headers.rb +3 -0
- data/app/controllers/spree/api/v3/admin/base_controller.rb +31 -0
- data/app/controllers/spree/api/v3/admin/companies/memberships_controller.rb +21 -4
- data/app/controllers/spree/api/v3/admin/diagnostics_controller.rb +46 -0
- data/app/controllers/spree/api/v3/admin/me_controller.rb +0 -13
- data/app/controllers/spree/api/v3/admin/media_controller.rb +10 -0
- data/app/controllers/spree/api/v3/admin/orders/items_controller.rb +6 -2
- data/app/controllers/spree/api/v3/admin/sellers/invitations_controller.rb +39 -3
- data/app/controllers/spree/api/v3/admin/sellers/team_controller.rb +1 -3
- data/app/controllers/spree/api/v3/admin/sellers_controller.rb +24 -1
- data/app/controllers/spree/api/v3/admin/stock_levels_controller.rb +15 -1
- data/app/controllers/spree/api/v3/admin/updates_controller.rb +4 -15
- data/app/controllers/spree/api/v3/seller/products/media_controller.rb +46 -0
- data/app/controllers/spree/api/v3/seller/products_controller.rb +4 -2
- data/app/controllers/spree/api/v3/seller/profile_controller.rb +13 -5
- data/app/controllers/spree/api/v3/store/carts_controller.rb +10 -7
- data/app/serializers/spree/api/v3/admin/company_membership_serializer.rb +4 -0
- data/app/serializers/spree/api/v3/admin/diagnostics_serializer.rb +30 -0
- data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +6 -1
- data/app/serializers/spree/api/v3/admin/seller_serializer.rb +10 -0
- data/app/serializers/spree/api/v3/product_category_serializer.rb +26 -0
- data/app/serializers/spree/api/v3/product_collection_serializer.rb +26 -0
- data/app/serializers/spree/api/v3/seller/order_line_item_serializer.rb +2 -2
- data/config/routes.rb +3 -1
- data/lib/spree/api/agent_resource_map.rb +42 -7
- data/lib/spree/api/dependencies.rb +1 -1
- data/lib/spree/api/testing_support/openapi.rb +2 -1
- data/lib/spree/api/testing_support/sdk_method.rb +34 -0
- data/openapi/admin.yaml +695 -279
- data/openapi/store.yaml +125 -125
- metadata +12 -6
|
@@ -55,6 +55,11 @@ module Spree
|
|
|
55
55
|
# so the minimum-products requirement asks the same question free.
|
|
56
56
|
attributes :products_count
|
|
57
57
|
|
|
58
|
+
# What is live and what waits on the operator's review, read in the
|
|
59
|
+
# same query as the total above.
|
|
60
|
+
typelize(Spree::Product::ALL_STATUSES.to_h { |status| [status.to_sym, :number] })
|
|
61
|
+
attribute :products_count_by_status, &:products_count_by_status
|
|
62
|
+
|
|
58
63
|
attributes :users_count
|
|
59
64
|
|
|
60
65
|
# How far through the marketplace's checklist this seller is — what
|
|
@@ -78,6 +83,11 @@ module Spree
|
|
|
78
83
|
resource: proc { Spree.api.admin_address_serializer },
|
|
79
84
|
if: proc { |seller| seller.returns_address.present? }
|
|
80
85
|
|
|
86
|
+
# The stock location the returns address is read from. The address
|
|
87
|
+
# is not the seller's own to write — it is edited on this location.
|
|
88
|
+
typelize returns_location_id: [:string, nullable: true]
|
|
89
|
+
prefixed_id_attributes :returns_location
|
|
90
|
+
|
|
81
91
|
expandable_custom_fields admin: true
|
|
82
92
|
end
|
|
83
93
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
class ProductCategorySerializer < BaseSerializer
|
|
5
|
+
typelize product_id: [:string, nullable: true],
|
|
6
|
+
category_id: [:string, nullable: true]
|
|
7
|
+
|
|
8
|
+
attribute :product_id do |product_category|
|
|
9
|
+
prefixed_id_for(Spree::Product, product_category.product_id)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attribute :category_id do |product_category|
|
|
13
|
+
prefixed_id_for(Spree::Category, product_category.category_id)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def prefixed_id_for(klass, id)
|
|
19
|
+
return nil if id.nil?
|
|
20
|
+
|
|
21
|
+
"#{klass._prefix_id_prefix}_#{Spree::PrefixedId::SQIDS.encode([id])}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
class ProductCollectionSerializer < BaseSerializer
|
|
5
|
+
typelize product_id: [:string, nullable: true],
|
|
6
|
+
collection_id: [:string, nullable: true]
|
|
7
|
+
|
|
8
|
+
attribute :product_id do |product_collection|
|
|
9
|
+
prefixed_id_for(Spree::Product, product_collection.product_id)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attribute :collection_id do |product_collection|
|
|
13
|
+
prefixed_id_for(Spree::Collection, product_collection.collection_id)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def prefixed_id_for(klass, id)
|
|
19
|
+
return nil if id.nil?
|
|
20
|
+
|
|
21
|
+
"#{klass._prefix_id_prefix}_#{Spree::PrefixedId::SQIDS.encode([id])}"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -13,7 +13,7 @@ module Spree
|
|
|
13
13
|
class OrderLineItemSerializer < V3::BaseSerializer
|
|
14
14
|
typelize name: :string, sku: [:string, nullable: true], options_text: [:string, nullable: true],
|
|
15
15
|
quantity: :number, currency: [:string, enum: Spree::Money::Rounding::CURRENCY_CODES, enum_type_name: 'CurrencyCode'], variant_id: [:string, nullable: true],
|
|
16
|
-
thumbnail_url: [:string, nullable: true]
|
|
16
|
+
thumbnail_url: [:string, nullable: true], product_id: [:string, nullable: true]
|
|
17
17
|
|
|
18
18
|
attributes :name, :options_text, :quantity, :currency
|
|
19
19
|
|
|
@@ -22,7 +22,7 @@ module Spree
|
|
|
22
22
|
money_attributes :discounted_amount,
|
|
23
23
|
:additional_tax_total, :total
|
|
24
24
|
|
|
25
|
-
prefixed_id_attributes :variant
|
|
25
|
+
prefixed_id_attributes :variant, :product
|
|
26
26
|
|
|
27
27
|
# How a seller finds the item on their own shelf.
|
|
28
28
|
attribute :sku do |line_item|
|
data/config/routes.rb
CHANGED
|
@@ -285,6 +285,7 @@ Spree::Core::Engine.add_routes do
|
|
|
285
285
|
patch 'me', to: 'me#update'
|
|
286
286
|
|
|
287
287
|
resource :updates, only: :show
|
|
288
|
+
resource :diagnostics, only: :show
|
|
288
289
|
|
|
289
290
|
# Store Settings
|
|
290
291
|
resource :store, only: [:show, :update], controller: 'store' do
|
|
@@ -610,7 +611,7 @@ Spree::Core::Engine.add_routes do
|
|
|
610
611
|
# The operator is the only one who can repair a seller whose team
|
|
611
612
|
# has locked itself out, which the seller's own panel cannot do.
|
|
612
613
|
resources :team, only: [:index, :destroy], controller: 'sellers/team'
|
|
613
|
-
resources :invitations, only: [:index, :destroy], controller: 'sellers/invitations' do
|
|
614
|
+
resources :invitations, only: [:index, :create, :destroy], controller: 'sellers/invitations' do
|
|
614
615
|
patch :resend, on: :member
|
|
615
616
|
resource :acceptance_link, only: [:show], controller: 'sellers/invitations/acceptance_links'
|
|
616
617
|
end
|
|
@@ -930,6 +931,7 @@ Spree::Core::Engine.add_routes do
|
|
|
930
931
|
|
|
931
932
|
resources :digital_assets, controller: 'products/digital_assets',
|
|
932
933
|
only: [:index, :create, :show, :update, :destroy]
|
|
934
|
+
resources :media, controller: 'products/media', only: [:destroy]
|
|
933
935
|
end
|
|
934
936
|
|
|
935
937
|
|
|
@@ -71,16 +71,23 @@ module Spree
|
|
|
71
71
|
# one, because the scope belongs to the resource being imported or
|
|
72
72
|
# exported — creating a product import is a product write, and reading
|
|
73
73
|
# its rows exposes the uploaded data. A single static key cannot say
|
|
74
|
-
# that, so these
|
|
75
|
-
#
|
|
74
|
+
# that, so these open to a caller holding the scope of any one type,
|
|
75
|
+
# in the kind the controller asks for: `read_` for an export, `write_`
|
|
76
|
+
# for an import. The endpoint then keeps to the types that key covers.
|
|
77
|
+
#
|
|
78
|
+
# Creating one stays with create_import and create_export; reading,
|
|
79
|
+
# listing and deleting go to the endpoint.
|
|
76
80
|
DYNAMIC_SCOPE_RESOURCES = {
|
|
77
|
-
'imports' => { model_name: 'Spree::Import',
|
|
81
|
+
'imports' => { model_name: 'Spree::Import', access_kind: 'write',
|
|
78
82
|
serializer_name: 'Spree::Api::V3::Admin::ImportSerializer',
|
|
79
83
|
dashboard_path: '/settings/imports' },
|
|
80
|
-
'exports' => { model_name: 'Spree::Export',
|
|
84
|
+
'exports' => { model_name: 'Spree::Export', access_kind: 'read',
|
|
81
85
|
serializer_name: 'Spree::Api::V3::Admin::ExportSerializer' }
|
|
82
86
|
}.freeze
|
|
83
87
|
|
|
88
|
+
# The operations the generic tools reach on those resources.
|
|
89
|
+
DYNAMIC_SCOPE_ACTIONS = %i[index show destroy].freeze
|
|
90
|
+
|
|
84
91
|
class << self
|
|
85
92
|
# Registers this walk as how the core map fills itself, and marks the
|
|
86
93
|
# map stale so the next reader gets a fresh one. Called on every code
|
|
@@ -102,7 +109,11 @@ module Spree
|
|
|
102
109
|
registered = resolve_collisions(controllers.filter_map { |controller| derive(controller) })
|
|
103
110
|
registered.each { |entry| map.register(**entry) }
|
|
104
111
|
|
|
105
|
-
DYNAMIC_SCOPE_RESOURCES.each
|
|
112
|
+
DYNAMIC_SCOPE_RESOURCES.each do |key, attributes|
|
|
113
|
+
map.register(key: key, permission: nil, **attributes.except(:access_kind),
|
|
114
|
+
api_paths: dynamic_scope_paths(attributes[:model_name]),
|
|
115
|
+
access_permissions: type_permissions(attributes[:model_name], attributes[:access_kind]))
|
|
116
|
+
end
|
|
106
117
|
|
|
107
118
|
registered.size + DYNAMIC_SCOPE_RESOURCES.size
|
|
108
119
|
end
|
|
@@ -183,6 +194,7 @@ module Spree
|
|
|
183
194
|
create_workflow = workflow_key(instance, :create_workflow)
|
|
184
195
|
update_workflow = workflow_key(instance, :update_workflow)
|
|
185
196
|
writable = AgentWriteSchemas.attribute_names(key)
|
|
197
|
+
api_paths = api_paths_for(controller)
|
|
186
198
|
|
|
187
199
|
{
|
|
188
200
|
key: key,
|
|
@@ -190,7 +202,8 @@ module Spree
|
|
|
190
202
|
permission: "read_#{scope}",
|
|
191
203
|
serializer_name: serializer.name,
|
|
192
204
|
dashboard_path: DASHBOARD_PATHS[key],
|
|
193
|
-
api_paths:
|
|
205
|
+
api_paths: api_paths,
|
|
206
|
+
index_paths: Array(api_paths[:index]),
|
|
194
207
|
write_permission: write_permission_for(model, scope, create_workflow, update_workflow, writable, controller),
|
|
195
208
|
writable_attributes: writable,
|
|
196
209
|
create_workflow_key: create_workflow,
|
|
@@ -219,6 +232,25 @@ module Spree
|
|
|
219
232
|
|
|
220
233
|
private
|
|
221
234
|
|
|
235
|
+
# The read and delete paths of the controller serving an import or
|
|
236
|
+
# export. Without them the generic tools refused every one, though
|
|
237
|
+
# create_export tells the agent to read the export with get_resource.
|
|
238
|
+
def dynamic_scope_paths(model_name)
|
|
239
|
+
controller = controllers.find { |candidate| model_for(candidate)&.name == model_name }
|
|
240
|
+
return {} if controller.nil?
|
|
241
|
+
|
|
242
|
+
api_paths_for(controller).slice(*DYNAMIC_SCOPE_ACTIONS)
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# The catalog keys that open an import or export of each registered
|
|
246
|
+
# type. A type with no scope of its own needs `read_all` or
|
|
247
|
+
# `write_all`, which no catalog key stands for, so it adds none.
|
|
248
|
+
def type_permissions(model_name, kind)
|
|
249
|
+
model_name.constantize.available_types.
|
|
250
|
+
filter_map { |type| type.required_scope.presence && "#{kind}_#{type.required_scope}" }.
|
|
251
|
+
uniq.select { |key| Spree.permissions.key?(key) }
|
|
252
|
+
end
|
|
253
|
+
|
|
222
254
|
# What the controller does with its own collection. A controller that
|
|
223
255
|
# disables DISTINCT has a reason — Postgres refuses it beside an
|
|
224
256
|
# ORDER BY the select list does not carry — and a search tool adding
|
|
@@ -273,7 +305,10 @@ module Spree
|
|
|
273
305
|
# nothing about depth — so a resource served both at the top
|
|
274
306
|
# level and under a parent kept whichever won on scope, and a
|
|
275
307
|
# tool was handed a path with an unfillable `:parent_id` in it.
|
|
276
|
-
api_paths: shallowest_paths(candidates)
|
|
308
|
+
api_paths: shallowest_paths(candidates),
|
|
309
|
+
# Every collection path, not only the shallowest, so a search
|
|
310
|
+
# under a parent reaches the controller that lists its records.
|
|
311
|
+
index_paths: candidates.flat_map { |entry| entry[:index_paths] }.uniq
|
|
277
312
|
)
|
|
278
313
|
end
|
|
279
314
|
end
|
|
@@ -38,7 +38,7 @@ module Spree
|
|
|
38
38
|
channel order_routing_rule product_publication market delivery_method delivery_method_service
|
|
39
39
|
stock_location stock_reservation delivery_rate freight_summary freight_summary_line payment_method
|
|
40
40
|
credit_card store_credit store_credit_event customer_group payment_source digital_asset digital_link
|
|
41
|
-
store setup_task update_status api_key oauth_application oauth_access_token oauth_consent_request oauth_redirect
|
|
41
|
+
store setup_task update_status diagnostics api_key oauth_application oauth_access_token oauth_consent_request oauth_redirect
|
|
42
42
|
oauth_protected_resource allowed_origin webhook_endpoint webhook_event webhook_delivery invitation
|
|
43
43
|
invitation_acceptance_link role permission export saved_report import import_row import_mapping promotion
|
|
44
44
|
maintenance_task_run
|
|
@@ -2,6 +2,7 @@ require 'spree/api/testing_support/v3/base'
|
|
|
2
2
|
require 'spree/api/testing_support/agent_tools'
|
|
3
3
|
require 'spree/api/testing_support/deterministic_openapi'
|
|
4
4
|
require 'spree/api/testing_support/filter_parameters'
|
|
5
|
+
require 'spree/api/testing_support/sdk_method'
|
|
5
6
|
|
|
6
7
|
module Spree
|
|
7
8
|
module Api
|
|
@@ -165,7 +166,7 @@ module Spree
|
|
|
165
166
|
# and in its Rakefile, `require 'spree/api/testing_support/openapi_tasks'`
|
|
166
167
|
# for `rake spree:openapi:generate`.
|
|
167
168
|
module Openapi
|
|
168
|
-
HELPERS = [RequestBody, FilterParameters, AdminScope, AdminOperation, AgentTools].freeze
|
|
169
|
+
HELPERS = [RequestBody, FilterParameters, AdminScope, AdminOperation, AgentTools, SdkMethod].freeze
|
|
169
170
|
|
|
170
171
|
class << self
|
|
171
172
|
# @param config [RSpec::Core::Configuration]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module TestingSupport
|
|
4
|
+
# rswag helper for what an operation is called in the SDK client
|
|
5
|
+
# `spree sdk generate` writes for an extension. By default a method is
|
|
6
|
+
# named after the path (`PATCH /quotes/{id}/send` → `quotes.send`), and
|
|
7
|
+
# an `operationId` renames it, but the `operationId` also names the
|
|
8
|
+
# operation's agent tool. This names the SDK method alone:
|
|
9
|
+
#
|
|
10
|
+
# delete 'Remove a file from a quote' do
|
|
11
|
+
# sdk_method :remove_attachment # quotes.removeAttachment
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# post 'Upload a file for a quote request' do
|
|
15
|
+
# sdk_method :presign_attachment, namespace: :quotes # not carts.…
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# See docs/plans/6.0-sdk-extensions.md.
|
|
19
|
+
module SdkMethod
|
|
20
|
+
METHOD_KEY = :'x-spree-sdk-method'
|
|
21
|
+
NAMESPACE_KEY = :'x-spree-sdk-namespace'
|
|
22
|
+
|
|
23
|
+
# @param name [Symbol, String] the method, snake_case or camelCase
|
|
24
|
+
# @param namespace [Symbol, String, nil] the client namespace it goes in,
|
|
25
|
+
# instead of the path's resource
|
|
26
|
+
# @return [void]
|
|
27
|
+
def sdk_method(name, namespace: nil)
|
|
28
|
+
metadata[:operation][METHOD_KEY] = name.to_s
|
|
29
|
+
metadata[:operation][NAMESPACE_KEY] = namespace.to_s if namespace
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|