spree_api 6.0.0.rc3 → 6.0.0.rc5
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/email_template_lookup.rb +6 -1
- data/app/controllers/concerns/spree/api/v3/admin/reporting_authorization.rb +17 -8
- data/app/controllers/concerns/spree/api/v3/admin/store_context.rb +7 -0
- data/app/controllers/concerns/spree/api/v3/admin_authentication.rb +61 -0
- data/app/controllers/concerns/spree/api/v3/allowed_redirect_url.rb +27 -0
- data/app/controllers/concerns/spree/api/v3/cart_resolvable.rb +8 -2
- data/app/controllers/concerns/spree/api/v3/error_handler.rb +49 -0
- data/app/controllers/concerns/spree/api/v3/params_normalizer.rb +76 -7
- data/app/controllers/concerns/spree/api/v3/scoped_authorization.rb +66 -5
- data/app/controllers/spree/api/v3/admin/companies/addresses_controller.rb +7 -1
- data/app/controllers/spree/api/v3/admin/companies/tax_exemption_certificates_controller.rb +11 -5
- data/app/controllers/spree/api/v3/admin/concerns/external_references.rb +3 -0
- data/app/controllers/spree/api/v3/admin/custom_fields_controller.rb +30 -8
- data/app/controllers/spree/api/v3/admin/customers/addresses_controller.rb +4 -0
- data/app/controllers/spree/api/v3/admin/customers_controller.rb +1 -6
- data/app/controllers/spree/api/v3/admin/exports_controller.rb +15 -3
- data/app/controllers/spree/api/v3/admin/gift_cards_controller.rb +1 -8
- data/app/controllers/spree/api/v3/admin/imports_controller.rb +12 -3
- data/app/controllers/spree/api/v3/admin/markets_controller.rb +3 -1
- data/app/controllers/spree/api/v3/admin/me_controller.rb +21 -1
- data/app/controllers/spree/api/v3/admin/oauth/application_tokens_controller.rb +56 -0
- data/app/controllers/spree/api/v3/admin/oauth/applications_controller.rb +55 -0
- data/app/controllers/spree/api/v3/admin/oauth/authorizations_controller.rb +294 -0
- data/app/controllers/spree/api/v3/admin/order_groups_controller.rb +1 -1
- data/app/controllers/spree/api/v3/admin/orders/fulfillments_controller.rb +8 -0
- data/app/controllers/spree/api/v3/admin/orders/items_controller.rb +1 -0
- data/app/controllers/spree/api/v3/admin/orders/returns_controller.rb +7 -0
- data/app/controllers/spree/api/v3/admin/orders_controller.rb +20 -7
- data/app/controllers/spree/api/v3/admin/package_types_controller.rb +2 -3
- data/app/controllers/spree/api/v3/admin/products/variants_controller.rb +3 -4
- data/app/controllers/spree/api/v3/admin/products_controller.rb +5 -3
- data/app/controllers/spree/api/v3/admin/purchase_orders_controller.rb +1 -0
- data/app/controllers/spree/api/v3/admin/reporting_controller.rb +20 -14
- data/app/controllers/spree/api/v3/admin/stock_levels_controller.rb +1 -1
- data/app/controllers/spree/api/v3/admin/store_controller.rb +1 -1
- data/app/controllers/spree/api/v3/admin/tags_controller.rb +8 -5
- data/app/controllers/spree/api/v3/admin/tax_exemption_reasons_controller.rb +58 -0
- data/app/controllers/spree/api/v3/admin/tax_rates_controller.rb +0 -1
- data/app/controllers/spree/api/v3/admin/translations/batches_controller.rb +17 -11
- data/app/controllers/spree/api/v3/admin/variants_controller.rb +96 -1
- data/app/controllers/spree/api/v3/oauth/authorizations_controller.rb +72 -0
- data/app/controllers/spree/api/v3/oauth/metadata_controller.rb +57 -0
- data/app/controllers/spree/api/v3/oauth/server_metadata_controller.rb +57 -0
- data/app/controllers/spree/api/v3/resource_controller.rb +36 -5
- data/app/controllers/spree/api/v3/seller/package_types_controller.rb +2 -2
- data/app/controllers/spree/api/v3/seller/products_controller.rb +7 -61
- data/app/controllers/spree/api/v3/seller/resource_controller.rb +2 -6
- data/app/controllers/spree/api/v3/store/carts/addresses_controller.rb +53 -0
- data/app/controllers/spree/api/v3/store/carts_controller.rb +7 -8
- data/app/controllers/spree/api/v3/store/companies/addresses_controller.rb +4 -0
- data/app/controllers/spree/api/v3/store/customer/addresses_controller.rb +4 -0
- data/app/controllers/spree/api/v3/store/customer/data_requests_controller.rb +19 -1
- data/app/controllers/spree/api/v3/store/customer/password_resets_controller.rb +3 -14
- data/app/controllers/spree/api/v3/store/data_request_confirmations_controller.rb +23 -0
- data/app/controllers/spree/api/v3/store/data_requests_controller.rb +56 -0
- data/app/controllers/spree/api/v3/store/newsletter_subscribers_controller.rb +2 -11
- data/app/request_schemas/spree/api/v3/admin/email_template_preview_request_schema.rb +4 -0
- data/app/request_schemas/spree/api/v3/admin/variant_request_schema.rb +21 -0
- data/app/request_schemas/spree/api/v3/seller/price_request_schema.rb +20 -0
- data/app/request_schemas/spree/api/v3/seller/variant_request_schema.rb +19 -0
- data/app/request_schemas/spree/api/v3/variant_request_schema.rb +8 -13
- data/app/serializers/spree/api/v3/address_owner_serializer.rb +27 -0
- data/app/serializers/spree/api/v3/address_serializer.rb +5 -1
- data/app/serializers/spree/api/v3/admin/address_owner_serializer.rb +10 -0
- data/app/serializers/spree/api/v3/admin/address_serializer.rb +1 -10
- data/app/serializers/spree/api/v3/admin/api_key_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/catalog_assignment_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/catalog_price_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/collection_rule_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/collection_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/commission_line_serializer.rb +2 -2
- data/app/serializers/spree/api/v3/admin/commission_rate_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/consent_record_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/email_template_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/fulfillment_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/oauth_access_token_serializer.rb +62 -0
- data/app/serializers/spree/api/v3/admin/oauth_application_serializer.rb +56 -0
- data/app/serializers/spree/api/v3/admin/oauth_consent_request_serializer.rb +25 -0
- data/app/serializers/spree/api/v3/admin/oauth_protected_resource_serializer.rb +27 -0
- data/app/serializers/spree/api/v3/admin/oauth_redirect_serializer.rb +17 -0
- data/app/serializers/spree/api/v3/admin/order_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/package_type_serializer.rb +3 -3
- data/app/serializers/spree/api/v3/admin/payment_capture_event_serializer.rb +16 -0
- data/app/serializers/spree/api/v3/admin/payment_serializer.rb +12 -1
- data/app/serializers/spree/api/v3/admin/permission_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/price_list_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/purchase_order_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/return_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/seller_requirement_status_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/seller_serializer.rb +2 -2
- data/app/serializers/spree/api/v3/admin/seller_transfer_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/shipping_label_serializer.rb +3 -3
- data/app/serializers/spree/api/v3/admin/stock_location_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/stock_receipt_item_serializer.rb +2 -2
- data/app/serializers/spree/api/v3/admin/stock_receipt_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/stock_transfer_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/store_serializer.rb +1 -0
- data/app/serializers/spree/api/v3/admin/tax_exemption_certificate_serializer.rb +7 -3
- data/app/serializers/spree/api/v3/admin/tax_exemption_reason_serializer.rb +20 -0
- data/app/serializers/spree/api/v3/admin/tax_identifier_serializer.rb +2 -2
- data/app/serializers/spree/api/v3/admin/tax_line_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/tax_rate_serializer.rb +1 -2
- data/app/serializers/spree/api/v3/admin/variant_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/webhook_delivery_serializer.rb +4 -1
- data/app/serializers/spree/api/v3/base_serializer.rb +41 -5
- data/app/serializers/spree/api/v3/claim_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/collection_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/company_invitation_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/company_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/data_request_confirmation_requested_event_serializer.rb +28 -0
- data/app/serializers/spree/api/v3/data_request_event_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/data_request_serializer.rb +8 -4
- data/app/serializers/spree/api/v3/delivery_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/delivery_zone_member_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/discount_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/file_upload_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/fulfillment_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/media_event_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/media_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/option_type_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/order_group_serializer.rb +2 -2
- data/app/serializers/spree/api/v3/order_serializer.rb +4 -2
- data/app/serializers/spree/api/v3/payment_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/product_filter_availability_option_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/product_filter_option_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/seller/address_serializer.rb +3 -2
- data/app/serializers/spree/api/v3/seller/order_serializer.rb +3 -3
- data/app/serializers/spree/api/v3/seller/package_type_serializer.rb +3 -3
- data/app/serializers/spree/api/v3/seller/profile_serializer.rb +2 -2
- data/app/serializers/spree/api/v3/seller/requirement_status_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/seller/tax_identifier_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/seller/variant_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/shipping_label_event_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/stock_movement_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/stock_receipt_event_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/variant_serializer.rb +2 -2
- data/app/services/spree/api/v3/filter_table.rb +22 -3
- data/app/services/spree/api/v3/request_schema.rb +64 -17
- data/config/locales/en.yml +1 -0
- data/config/routes.rb +80 -12
- data/lib/spree/api/agent_membership_map.rb +73 -0
- data/lib/spree/api/agent_resource_map.rb +350 -0
- data/lib/spree/api/agent_workflow_endpoints.rb +113 -0
- data/lib/spree/api/agent_write_schemas.rb +131 -0
- data/lib/spree/api/dependencies.rb +5 -4
- data/lib/spree/api/engine.rb +40 -0
- data/lib/spree/api/enum_types.rb +118 -0
- data/lib/spree/api/extension_fields.rb +167 -0
- data/lib/spree/api/oauth.rb +243 -0
- data/lib/spree/api/openapi/filter_documentation.rb +8 -1
- data/lib/spree/api/typelizer_generation.rb +5 -0
- data/lib/spree/api.rb +8 -0
- data/lib/typelizer/open_enums.rb +48 -10
- metadata +37 -6
|
@@ -25,6 +25,17 @@ module Spree
|
|
|
25
25
|
boolean: 'boolean'
|
|
26
26
|
}.freeze
|
|
27
27
|
|
|
28
|
+
# Columns holding a value from one of the closed code lists the API
|
|
29
|
+
# publishes as a shared type, matched by name (`locale`,
|
|
30
|
+
# `default_locale`, `cost_currency`), so clients filter with that type,
|
|
31
|
+
# not free text.
|
|
32
|
+
CODE_TYPES = {
|
|
33
|
+
'locale' => 'LocaleCode',
|
|
34
|
+
'currency' => 'CurrencyCode',
|
|
35
|
+
'country_code' => 'CountryCode',
|
|
36
|
+
'country_of_origin' => 'CountryCode'
|
|
37
|
+
}.freeze
|
|
38
|
+
|
|
28
39
|
# Raised when an endpoint's table cannot be described — a column of a
|
|
29
40
|
# kind the contract has no predicates for, or two models sharing a name.
|
|
30
41
|
class Error < StandardError; end
|
|
@@ -124,8 +135,9 @@ module Spree
|
|
|
124
135
|
self.class.name_for(model)
|
|
125
136
|
end
|
|
126
137
|
|
|
127
|
-
# @return [Hash{String => String, Hash}] attribute => value kind,
|
|
128
|
-
# `{ 'enum' => [values] }` for a status
|
|
138
|
+
# @return [Hash{String => String, Hash}] attribute => value kind,
|
|
139
|
+
# `{ 'enum' => [values] }` for a status, or `{ 'code' => type }` for
|
|
140
|
+
# a value from a shared code list
|
|
129
141
|
def attributes
|
|
130
142
|
@attributes ||= kinds.reject { |_, kind| kind == :unclassified }.compact
|
|
131
143
|
end
|
|
@@ -210,6 +222,10 @@ module Spree
|
|
|
210
222
|
# attribute the model does not have (`position` on a table without
|
|
211
223
|
# one), `:unclassified` for one the contract cannot describe
|
|
212
224
|
def attribute_kind(attribute)
|
|
225
|
+
if (code_type = code_type(attribute))
|
|
226
|
+
return { 'code' => code_type }
|
|
227
|
+
end
|
|
228
|
+
|
|
213
229
|
aliased = model._ransack_aliases[attribute] || model.attribute_aliases[attribute]
|
|
214
230
|
return attribute_kind(aliased) if aliased
|
|
215
231
|
return ransacker_kind(model._ransackers[attribute]) if model._ransackers.key?(attribute)
|
|
@@ -220,7 +236,6 @@ module Spree
|
|
|
220
236
|
if (values = inclusion_values(attribute))
|
|
221
237
|
return { 'enum' => values }
|
|
222
238
|
end
|
|
223
|
-
|
|
224
239
|
column = model.columns_hash[attribute]
|
|
225
240
|
return COLUMN_KINDS.fetch(column.type, :unclassified) if column
|
|
226
241
|
return 'text' if translated_attribute?(attribute)
|
|
@@ -229,6 +244,10 @@ module Spree
|
|
|
229
244
|
:unclassified
|
|
230
245
|
end
|
|
231
246
|
|
|
247
|
+
def code_type(attribute)
|
|
248
|
+
CODE_TYPES.find { |name, _| attribute == name || attribute.end_with?("_#{name}") }&.last
|
|
249
|
+
end
|
|
250
|
+
|
|
232
251
|
# The fixed list an inclusion validation allows, for a column that
|
|
233
252
|
# holds one of a known set of values (`payment_status`, `kind`).
|
|
234
253
|
def inclusion_values(attribute)
|
|
@@ -40,20 +40,24 @@ module Spree
|
|
|
40
40
|
datetime: { type: :string, format: :'date-time' },
|
|
41
41
|
id: { type: :string },
|
|
42
42
|
any: {},
|
|
43
|
-
#
|
|
43
|
+
# Codes from the closed lists below.
|
|
44
44
|
country: { type: :string },
|
|
45
45
|
currency: { type: :string },
|
|
46
|
-
locale: { type: :string }
|
|
46
|
+
locale: { type: :string },
|
|
47
|
+
weight_unit: { type: :string },
|
|
48
|
+
dimension_unit: { type: :string }
|
|
47
49
|
}.freeze
|
|
48
50
|
|
|
49
|
-
# The
|
|
50
|
-
#
|
|
51
|
-
# `
|
|
52
|
-
#
|
|
51
|
+
# The closed lists a code field takes, published once per API as a
|
|
52
|
+
# named component — the same `CountryCode`, `CurrencyCode` and
|
|
53
|
+
# `WeightUnit` types the serializers declare, so a value read from a
|
|
54
|
+
# response is the type a request takes.
|
|
53
55
|
CODE_LISTS = {
|
|
54
56
|
country: ['CountryCode', -> { Spree::IsoData.country_codes }],
|
|
55
57
|
currency: ['CurrencyCode', -> { Spree::Money::Rounding::CURRENCY_CODES }],
|
|
56
|
-
locale: ['LocaleCode', -> { Spree::Locales::ALL }]
|
|
58
|
+
locale: ['LocaleCode', -> { Spree::Locales::ALL }],
|
|
59
|
+
weight_unit: ['WeightUnit', -> { Spree::Variant::WEIGHT_UNITS }],
|
|
60
|
+
dimension_unit: ['DimensionUnit', -> { Spree::Variant::DIMENSION_UNITS }]
|
|
57
61
|
}.freeze
|
|
58
62
|
|
|
59
63
|
ACTIONS = %i[create update].freeze
|
|
@@ -119,7 +123,7 @@ module Spree
|
|
|
119
123
|
end
|
|
120
124
|
end
|
|
121
125
|
|
|
122
|
-
attr_reader :name, :fields
|
|
126
|
+
attr_reader :name, :fields, :base
|
|
123
127
|
|
|
124
128
|
# The types an attribute takes: the scalars above, plus `:hash` (any
|
|
125
129
|
# keys), `:object` (a block or a `shape:`) and `:array` (`of:` a type
|
|
@@ -172,7 +176,7 @@ module Spree
|
|
|
172
176
|
value.is_a?(Class) ? value.definition : value
|
|
173
177
|
end
|
|
174
178
|
|
|
175
|
-
%i[attribute include_attributes require_attributes external_references].each do |method|
|
|
179
|
+
%i[attribute extends include_attributes require_attributes external_references].each do |method|
|
|
176
180
|
define_method(method) { |*args, **options, &block| definition.public_send(method, *args, **options, &block) }
|
|
177
181
|
end
|
|
178
182
|
end
|
|
@@ -229,6 +233,19 @@ module Spree
|
|
|
229
233
|
TEXT
|
|
230
234
|
end
|
|
231
235
|
|
|
236
|
+
# Builds on a published shape: the body accepts all of its attributes,
|
|
237
|
+
# and the published type is that shape's type plus what this schema
|
|
238
|
+
# adds, so clients write one type wherever the shape is accepted.
|
|
239
|
+
#
|
|
240
|
+
# @param other [RequestSchema, Class] a shape with a published name
|
|
241
|
+
def extends(other)
|
|
242
|
+
parent = RequestSchema.resolve(other)
|
|
243
|
+
raise ArgumentError, "#{parent.class.name} has no published name to extend" unless shape?(parent)
|
|
244
|
+
|
|
245
|
+
include_attributes(parent)
|
|
246
|
+
@base = parent
|
|
247
|
+
end
|
|
248
|
+
|
|
232
249
|
# Adds the attributes of another schema, for endpoints that extend a
|
|
233
250
|
# shared set.
|
|
234
251
|
#
|
|
@@ -253,6 +270,19 @@ module Spree
|
|
|
253
270
|
@permit_lists = nil
|
|
254
271
|
end
|
|
255
272
|
|
|
273
|
+
# Replaces the description of an attribute this schema took from
|
|
274
|
+
# another, for an endpoint where it behaves differently.
|
|
275
|
+
#
|
|
276
|
+
# @param name [Symbol]
|
|
277
|
+
# @param description [String]
|
|
278
|
+
# @return [void]
|
|
279
|
+
def describe_attribute(name, description)
|
|
280
|
+
field = fields.fetch(name) { raise ArgumentError, "#{name} is not an attribute of this schema" }
|
|
281
|
+
copy = field.dup
|
|
282
|
+
copy.instance_variable_set(:@description, description)
|
|
283
|
+
fields[name] = copy
|
|
284
|
+
end
|
|
285
|
+
|
|
256
286
|
# The `params.permit` list for an action.
|
|
257
287
|
#
|
|
258
288
|
# @param action [Symbol, String, nil] `create` or `update`; nil
|
|
@@ -269,12 +299,21 @@ module Spree
|
|
|
269
299
|
# @return [Hash]
|
|
270
300
|
def json_schema(action = nil)
|
|
271
301
|
action = action&.to_sym
|
|
272
|
-
|
|
273
|
-
|
|
302
|
+
# A base field this action does not take would still reach clients
|
|
303
|
+
# through the base's type, so such a body is published in full.
|
|
304
|
+
unless base && base.fields.each_value.all? { |field| field.accepted_on?(action) }
|
|
305
|
+
return object_schema(fields_for(action), action)
|
|
306
|
+
end
|
|
274
307
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
308
|
+
# Attributes the base describes the same way for this action are
|
|
309
|
+
# left to its type.
|
|
310
|
+
own = fields_for(action).reject do |field|
|
|
311
|
+
inherited = base.fields[field.name]
|
|
312
|
+
inherited && inherited.required_on?(action) == field.required_on?(action) &&
|
|
313
|
+
property_schema(inherited) == property_schema(field)
|
|
314
|
+
end
|
|
315
|
+
reference = { '$ref': "#/components/schemas/#{base.name}" }
|
|
316
|
+
own.any? ? { allOf: [reference, object_schema(own, action)] } : reference
|
|
278
317
|
end
|
|
279
318
|
|
|
280
319
|
# The component name published for an action.
|
|
@@ -291,15 +330,15 @@ module Spree
|
|
|
291
330
|
#
|
|
292
331
|
# @return [Array<RequestSchema>]
|
|
293
332
|
def shapes
|
|
294
|
-
fields.each_value.flat_map do |field|
|
|
333
|
+
(base ? [base, *base.shapes] : []).concat(fields.each_value.flat_map do |field|
|
|
295
334
|
next [] unless field.schema
|
|
296
335
|
|
|
297
336
|
nested = field.schema.shapes
|
|
298
337
|
shape?(field.schema) ? [field.schema, *nested] : nested
|
|
299
|
-
end.uniq
|
|
338
|
+
end).uniq
|
|
300
339
|
end
|
|
301
340
|
|
|
302
|
-
# The
|
|
341
|
+
# The code lists this schema's fields reach, nested ones included,
|
|
303
342
|
# as the components they publish.
|
|
304
343
|
#
|
|
305
344
|
# @return [Hash{String => Hash}]
|
|
@@ -374,6 +413,14 @@ module Spree
|
|
|
374
413
|
end
|
|
375
414
|
|
|
376
415
|
|
|
416
|
+
def object_schema(fields, action)
|
|
417
|
+
required = fields.select { |field| field.required_on?(action) }.map { |field| field.name.to_s }
|
|
418
|
+
|
|
419
|
+
schema = { type: :object, properties: fields.to_h { |field| [field.name, property_schema(field)] } }
|
|
420
|
+
schema[:required] = required if required.any?
|
|
421
|
+
schema
|
|
422
|
+
end
|
|
423
|
+
|
|
377
424
|
def fields_for(action)
|
|
378
425
|
fields.values.select { |field| field.accepted_on?(action) }
|
|
379
426
|
end
|
data/config/locales/en.yml
CHANGED
|
@@ -14,6 +14,7 @@ en:
|
|
|
14
14
|
media_in_use: This file is still in use (%{places}). Pass detach=true to remove it everywhere and delete it, or clear those places first.
|
|
15
15
|
media_not_deleted: The file could not be deleted. Nothing was changed.
|
|
16
16
|
media_url_import_requires_product: Importing media from a URL needs a product to import it into. Upload the file instead, or post it to a product's media.
|
|
17
|
+
order_sales_context_exclusive: An order already fixes its sales context, so order_id cannot be combined with %{params}.
|
|
17
18
|
store_mismatch: The requested store does not match the store this API key belongs to.
|
|
18
19
|
storefront_login_required: Authentication required to access this store.
|
|
19
20
|
unsupported_export_type: 'Unsupported export type. Supported types: %{types}'
|
data/config/routes.rb
CHANGED
|
@@ -43,6 +43,7 @@ Spree::Core::Engine.add_routes do
|
|
|
43
43
|
patch :associate
|
|
44
44
|
post :complete
|
|
45
45
|
end
|
|
46
|
+
resources :addresses, only: [:index], controller: 'carts/addresses'
|
|
46
47
|
resources :items, only: [:create, :update, :destroy], controller: 'carts/items'
|
|
47
48
|
resources :discount_codes, only: [:create, :destroy], controller: 'carts/discount_codes'
|
|
48
49
|
resources :gift_cards, only: [:create, :destroy], controller: 'carts/gift_cards'
|
|
@@ -154,6 +155,10 @@ Spree::Core::Engine.add_routes do
|
|
|
154
155
|
# Token-addressed so the emailed link works from a device that was
|
|
155
156
|
# never signed in.
|
|
156
157
|
get 'data_requests/:token/download', to: 'data_request_downloads#show', as: :data_request_download
|
|
158
|
+
# Addressed by the confirmation token emailed to the address a
|
|
159
|
+
# customer's own request names, for the same reason.
|
|
160
|
+
get 'data_requests/:token', to: 'data_requests#show', as: :data_request
|
|
161
|
+
post 'data_requests/:token/confirmation', to: 'data_request_confirmations#create', as: :data_request_confirmation
|
|
157
162
|
# Legacy path — emailed and bookmarked URLs outlive the rename. Removed in 6.1.
|
|
158
163
|
get 'digitals/:token', to: 'digital_links#show', as: :digital_download
|
|
159
164
|
|
|
@@ -238,6 +243,27 @@ Spree::Core::Engine.add_routes do
|
|
|
238
243
|
post 'auth/password_resets', to: 'password_resets#create'
|
|
239
244
|
patch 'auth/password_resets/:id', to: 'password_resets#update'
|
|
240
245
|
|
|
246
|
+
# OAuth 2.1 consent, read and decided by the dashboard. The admin's
|
|
247
|
+
# own session authorizes it: a secret key may authenticate here but
|
|
248
|
+
# can never grant authority on a person's behalf.
|
|
249
|
+
namespace :oauth do
|
|
250
|
+
get 'authorize', to: 'authorizations#show'
|
|
251
|
+
post 'authorize', to: 'authorizations#create'
|
|
252
|
+
delete 'authorize', to: 'authorizations#destroy'
|
|
253
|
+
|
|
254
|
+
# The OAuth clients registered against this store. Deleting one
|
|
255
|
+
# removes the registration; revoking a connection is the nested
|
|
256
|
+
# `tokens` resource, because that is what revoking deletes.
|
|
257
|
+
#
|
|
258
|
+
# Tokens are plural and addressable: a store registers a client once
|
|
259
|
+
# and every staff member connects to that same registration, so the
|
|
260
|
+
# live tokens are who is connected. Revoking one has to cut off that
|
|
261
|
+
# person and leave their colleagues working.
|
|
262
|
+
resources :applications, except: :show do
|
|
263
|
+
resources :tokens, only: %i[index destroy], controller: 'application_tokens'
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
241
267
|
# Semantic reporting (docs/plans/6.0-analytics-semantic-layer.md)
|
|
242
268
|
namespace :reporting do
|
|
243
269
|
post :query
|
|
@@ -261,6 +287,9 @@ Spree::Core::Engine.add_routes do
|
|
|
261
287
|
resource :store, only: [:show, :update], controller: 'store' do
|
|
262
288
|
get :data_sources
|
|
263
289
|
end
|
|
290
|
+
# The store's own custom fields, addressed by its id like every other
|
|
291
|
+
# parent. The controller only ever resolves the current store.
|
|
292
|
+
resources :stores, only: [], concerns: :custom_fieldable
|
|
264
293
|
|
|
265
294
|
# Staff & access (invitations, admin users, roles, API keys)
|
|
266
295
|
resources :admin_users, only: [:index, :show, :update, :destroy]
|
|
@@ -324,7 +353,7 @@ Spree::Core::Engine.add_routes do
|
|
|
324
353
|
end
|
|
325
354
|
|
|
326
355
|
# Products
|
|
327
|
-
resources :product_types do
|
|
356
|
+
resources :product_types, concerns: :custom_fieldable do
|
|
328
357
|
post :apply_to_products, on: :member
|
|
329
358
|
end
|
|
330
359
|
resources :products, concerns: [:custom_fieldable, :translatable] do
|
|
@@ -366,7 +395,7 @@ Spree::Core::Engine.add_routes do
|
|
|
366
395
|
# Media library — every file in the store, placed or not. Files are put
|
|
367
396
|
# ON a product through the nested media routes above; this is where they
|
|
368
397
|
# are uploaded, browsed and deleted.
|
|
369
|
-
resources :media, controller: 'media_library', only: [:index, :show, :create, :update, :destroy] do
|
|
398
|
+
resources :media, controller: 'media_library', only: [:index, :show, :create, :update, :destroy], concerns: :custom_fieldable do
|
|
370
399
|
get :usage, on: :member
|
|
371
400
|
end
|
|
372
401
|
|
|
@@ -401,7 +430,7 @@ Spree::Core::Engine.add_routes do
|
|
|
401
430
|
resources :tax_categories
|
|
402
431
|
|
|
403
432
|
# Tax Rates — the internal tax provider's configuration
|
|
404
|
-
resources :tax_rates
|
|
433
|
+
resources :tax_rates, concerns: :custom_fieldable
|
|
405
434
|
|
|
406
435
|
# Selectable tax engines and their declared limits (discovery only)
|
|
407
436
|
resources :tax_providers, only: [:index]
|
|
@@ -409,11 +438,12 @@ Spree::Core::Engine.add_routes do
|
|
|
409
438
|
# preference, so there is nothing here to create.
|
|
410
439
|
resources :payout_providers, only: [:index]
|
|
411
440
|
|
|
412
|
-
# Return / claim / refund / cancellation reasons (dropdowns + settings management)
|
|
441
|
+
# Return / claim / refund / cancellation / tax exemption reasons (dropdowns + settings management)
|
|
413
442
|
resources :return_reasons
|
|
414
443
|
resources :claim_reasons
|
|
415
444
|
resources :refund_reasons
|
|
416
445
|
resources :order_cancellation_reasons
|
|
446
|
+
resources :tax_exemption_reasons
|
|
417
447
|
|
|
418
448
|
# Markets
|
|
419
449
|
resources :markets
|
|
@@ -425,7 +455,7 @@ Spree::Core::Engine.add_routes do
|
|
|
425
455
|
post :bulk_upsert, on: :collection
|
|
426
456
|
end
|
|
427
457
|
resources :stock_movements, only: [:index, :show]
|
|
428
|
-
resources :stock_transfers do
|
|
458
|
+
resources :stock_transfers, concerns: :custom_fieldable do
|
|
429
459
|
member do
|
|
430
460
|
patch :mark_ready
|
|
431
461
|
patch :mark_in_transit
|
|
@@ -438,7 +468,7 @@ Spree::Core::Engine.add_routes do
|
|
|
438
468
|
|
|
439
469
|
# Purchasing
|
|
440
470
|
resources :suppliers
|
|
441
|
-
resources :purchase_orders do
|
|
471
|
+
resources :purchase_orders, concerns: :custom_fieldable do
|
|
442
472
|
member do
|
|
443
473
|
patch :mark_ordered
|
|
444
474
|
patch :mark_draft
|
|
@@ -449,7 +479,7 @@ Spree::Core::Engine.add_routes do
|
|
|
449
479
|
end
|
|
450
480
|
|
|
451
481
|
# Payment Methods
|
|
452
|
-
resources :delivery_methods do
|
|
482
|
+
resources :delivery_methods, concerns: :custom_fieldable do
|
|
453
483
|
collection do
|
|
454
484
|
get :calculators
|
|
455
485
|
get :fulfillment_providers
|
|
@@ -470,7 +500,7 @@ Spree::Core::Engine.add_routes do
|
|
|
470
500
|
only: [:index, :show, :create, :update, :destroy]
|
|
471
501
|
end
|
|
472
502
|
|
|
473
|
-
resources :payment_methods do
|
|
503
|
+
resources :payment_methods, concerns: :custom_fieldable do
|
|
474
504
|
get :types, on: :collection
|
|
475
505
|
end
|
|
476
506
|
|
|
@@ -480,7 +510,7 @@ Spree::Core::Engine.add_routes do
|
|
|
480
510
|
end
|
|
481
511
|
|
|
482
512
|
# Promotions, with nested actions/rules/coupon codes.
|
|
483
|
-
resources :promotions do
|
|
513
|
+
resources :promotions, concerns: :custom_fieldable do
|
|
484
514
|
resources :promotion_actions, only: [:index, :show, :create, :update, :destroy]
|
|
485
515
|
resources :promotion_rules, only: [:index, :show, :create, :update, :destroy]
|
|
486
516
|
resources :coupon_codes, only: [:index, :show]
|
|
@@ -613,7 +643,7 @@ Spree::Core::Engine.add_routes do
|
|
|
613
643
|
# Business customers — a tree of company nodes. Nested collections are
|
|
614
644
|
# listed and created under their node and then addressed directly, so
|
|
615
645
|
# a caller holding an entry id does not have to know its company.
|
|
616
|
-
resources :companies do
|
|
646
|
+
resources :companies, concerns: :custom_fieldable do
|
|
617
647
|
# The node's address book.
|
|
618
648
|
resources :addresses, controller: 'companies/addresses'
|
|
619
649
|
|
|
@@ -698,12 +728,12 @@ Spree::Core::Engine.add_routes do
|
|
|
698
728
|
end
|
|
699
729
|
|
|
700
730
|
# Gift cards
|
|
701
|
-
resources :gift_cards
|
|
731
|
+
resources :gift_cards, concerns: :custom_fieldable
|
|
702
732
|
resources :gift_card_batches, only: [:index, :show, :create]
|
|
703
733
|
|
|
704
734
|
# Store credits across all customers. Read-only: issuing, editing and
|
|
705
735
|
# deleting a credit stays nested under the customer that holds it.
|
|
706
|
-
resources :store_credits, only: [:index, :show] do
|
|
736
|
+
resources :store_credits, only: [:index, :show], concerns: :custom_fieldable do
|
|
707
737
|
resources :events, only: [:index], controller: 'store_credits/events'
|
|
708
738
|
end
|
|
709
739
|
|
|
@@ -1052,6 +1082,14 @@ Spree::Core::Engine.add_routes do
|
|
|
1052
1082
|
end
|
|
1053
1083
|
end
|
|
1054
1084
|
|
|
1085
|
+
# RFC 9728 Protected Resource Metadata. One document per protected
|
|
1086
|
+
# resource, named by the surface that registered it, so a client
|
|
1087
|
+
# discovers which authorization server guards the URL it was given.
|
|
1088
|
+
# Unauthenticated: a client has no credential at the point it asks.
|
|
1089
|
+
get 'oauth/protected-resource/:resource_key',
|
|
1090
|
+
to: 'oauth/metadata#show',
|
|
1091
|
+
as: :oauth_protected_resource
|
|
1092
|
+
|
|
1055
1093
|
# Webhooks (outside of store namespace — no API key authentication)
|
|
1056
1094
|
namespace :webhooks do
|
|
1057
1095
|
post 'payments/:payment_method_id', to: 'payments#create', as: :payment_webhook
|
|
@@ -1066,4 +1104,34 @@ Spree::Core::Engine.add_routes do
|
|
|
1066
1104
|
end
|
|
1067
1105
|
end
|
|
1068
1106
|
end
|
|
1107
|
+
|
|
1108
|
+
# The authorization server's own endpoints: /oauth/authorize, /oauth/token,
|
|
1109
|
+
# /oauth/revoke and RFC 8414 metadata. Outside the versioned API namespace —
|
|
1110
|
+
# the OAuth protocol is not a Spree API resource and its paths are fixed by
|
|
1111
|
+
# the spec and by what clients expect to find.
|
|
1112
|
+
#
|
|
1113
|
+
# `authorizations` is NOT skipped. Skipping it would leave the metadata
|
|
1114
|
+
# document without an `authorization_endpoint`, which is required for the
|
|
1115
|
+
# code flow, so a consumer client would have nowhere to send the browser.
|
|
1116
|
+
# The controller is Spree's own and redirects to the dashboard's consent
|
|
1117
|
+
# screen instead of rendering Doorkeeper's HTML.
|
|
1118
|
+
#
|
|
1119
|
+
# The application-management controllers stay skipped: connected apps are
|
|
1120
|
+
# managed through /api/v3/admin/oauth/applications, under the Admin API's
|
|
1121
|
+
# own authentication.
|
|
1122
|
+
# `controllers` is a block method, not an option to `use_doorkeeper`.
|
|
1123
|
+
#
|
|
1124
|
+
# Doorkeeper's own controllers need a leading slash because these routes are
|
|
1125
|
+
# drawn inside Spree::Core::Engine, which isolates the namespace — a bare
|
|
1126
|
+
# `doorkeeper/tokens` resolves as `Spree::Doorkeeper::Tokens` and fails on
|
|
1127
|
+
# the first request. Spree's own authorizations controller is named from the
|
|
1128
|
+
# application root WITHOUT a slash, because the metadata document builds its
|
|
1129
|
+
# URL with `url_for(controller: "/#{name}")` and adds the slash itself.
|
|
1130
|
+
use_doorkeeper scope: 'oauth' do
|
|
1131
|
+
controllers authorizations: 'api/v3/oauth/authorizations',
|
|
1132
|
+
tokens: '/doorkeeper/tokens',
|
|
1133
|
+
token_info: '/doorkeeper/token_info',
|
|
1134
|
+
metadata: '/spree/api/v3/oauth/server_metadata'
|
|
1135
|
+
skip_controllers :applications, :authorized_applications
|
|
1136
|
+
end
|
|
1069
1137
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
# Which parents an agent may curate products into, derived from the
|
|
4
|
+
# controllers that adopt `Spree::Api::V3::Admin::ProductMembership`.
|
|
5
|
+
#
|
|
6
|
+
# Read off the controllers for the same reason {AgentResourceMap} is: a
|
|
7
|
+
# parent that adopts the concern later is curated without anyone editing
|
|
8
|
+
# this file, and one that drops it stops being offered.
|
|
9
|
+
module AgentMembershipMap
|
|
10
|
+
class << self
|
|
11
|
+
# @param map [Module]
|
|
12
|
+
# @return [Integer] how many parents were registered
|
|
13
|
+
def install(map: Spree::AgentTools::MembershipMap)
|
|
14
|
+
map.reset!
|
|
15
|
+
|
|
16
|
+
curating_controllers.each do |controller|
|
|
17
|
+
target = target_for(controller)
|
|
18
|
+
next if target.blank?
|
|
19
|
+
|
|
20
|
+
# Addressing a parent an agent cannot otherwise read would hand it
|
|
21
|
+
# a write with no matching read, and no way to name what it changed.
|
|
22
|
+
entry = Spree::AgentTools::ResourceMap.find(target.pluralize)
|
|
23
|
+
next if entry.nil?
|
|
24
|
+
|
|
25
|
+
map.register(
|
|
26
|
+
target: target,
|
|
27
|
+
permission: write_permission_for(entry),
|
|
28
|
+
api_path: membership_path_for(controller)
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
map.all.size
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# Where this parent's products are added and removed, read from the
|
|
38
|
+
# routes. The nested path carries the parent's id, which a tool fills
|
|
39
|
+
# in from the parent it was given.
|
|
40
|
+
def membership_path_for(controller)
|
|
41
|
+
route = Spree::Core::Engine.routes.routes.find do |candidate|
|
|
42
|
+
candidate.defaults[:controller] == controller.controller_path &&
|
|
43
|
+
candidate.defaults[:action] == 'create'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
route&.path&.spec.to_s.sub('(.:format)', '').presence
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def curating_controllers
|
|
50
|
+
Rails.application.eager_load! unless Rails.application.config.eager_load
|
|
51
|
+
|
|
52
|
+
Spree::Api::V3::Admin::ResourceController.descendants.
|
|
53
|
+
select { |controller| controller.include?(Spree::Api::V3::Admin::ProductMembership) }.
|
|
54
|
+
sort_by(&:name)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Curating is a write to the parent, so it is gated on the parent's
|
|
58
|
+
# own write scope — a price list rides `write_products`, a category
|
|
59
|
+
# `write_categories`, exactly as their controllers do.
|
|
60
|
+
def write_permission_for(entry)
|
|
61
|
+
entry.write_permission.presence || "write_#{entry.permission.to_s.sub(/\Aread_/, '')}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# `Admin::Categories::ProductsController` curates a category. The
|
|
65
|
+
# parent is the namespace the nested controller sits in, which is how
|
|
66
|
+
# the routes name it too.
|
|
67
|
+
def target_for(controller)
|
|
68
|
+
controller.name.to_s.split('::')[-2]&.underscore&.singularize
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|