spree_api 6.0.0 → 6.0.2
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/README.md +3 -2
- data/app/controllers/concerns/spree/api/v3/admin/embed_permissions.rb +6 -1
- data/app/controllers/spree/api/v3/admin/admin_users_controller.rb +10 -0
- data/app/controllers/spree/api/v3/admin/invitation_acceptances_controller.rb +8 -4
- data/app/controllers/spree/api/v3/admin/sellers_controller.rb +1 -1
- data/app/controllers/spree/api/v3/admin/store_controller.rb +23 -7
- data/app/controllers/spree/api/v3/seller/invitation_acceptances_controller.rb +15 -5
- data/app/controllers/spree/api/v3/store/customers_controller.rb +1 -0
- data/app/controllers/spree/api/v3/store/store_controller.rb +25 -0
- data/app/serializers/spree/api/v3/address_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/address_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/admin_user_serializer.rb +20 -2
- data/app/serializers/spree/api/v3/admin/category_serializer.rb +1 -3
- data/app/serializers/spree/api/v3/admin/claim_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/collection_serializer.rb +1 -4
- data/app/serializers/spree/api/v3/admin/company_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/credit_card_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/customer_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/delivery_method_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/fulfillment_serializer.rb +1 -0
- data/app/serializers/spree/api/v3/admin/gift_card_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/invitation_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +1 -0
- data/app/serializers/spree/api/v3/admin/media_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/newsletter_subscriber_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/option_type_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/option_value_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/order_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/payment_method_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/payment_serializer.rb +8 -2
- data/app/serializers/spree/api/v3/admin/payment_source_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/product_serializer.rb +1 -3
- data/app/serializers/spree/api/v3/admin/return_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/seller_payout_serializer.rb +2 -1
- data/app/serializers/spree/api/v3/admin/seller_serializer.rb +4 -0
- data/app/serializers/spree/api/v3/admin/session_serializer.rb +17 -0
- data/app/serializers/spree/api/v3/admin/store_credit_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/admin/store_serializer.rb +3 -4
- data/app/serializers/spree/api/v3/admin/variant_serializer.rb +1 -3
- data/app/serializers/spree/api/v3/base_serializer.rb +28 -0
- data/app/serializers/spree/api/v3/category_serializer.rb +1 -4
- data/app/serializers/spree/api/v3/claim_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/collection_serializer.rb +1 -4
- data/app/serializers/spree/api/v3/company_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/credit_card_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/customer_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/delivery_method_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/fulfillment_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/gift_card_serializer.rb +1 -0
- data/app/serializers/spree/api/v3/line_item_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/media_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/newsletter_subscriber_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/option_type_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/option_value_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/order_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/payment_method_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/payment_serializer.rb +3 -1
- data/app/serializers/spree/api/v3/payment_source_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/product_serializer.rb +1 -4
- data/app/serializers/spree/api/v3/return_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/seller/invitation_serializer.rb +22 -1
- data/app/serializers/spree/api/v3/seller/profile_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/seller_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/store_credit_serializer.rb +2 -0
- data/app/serializers/spree/api/v3/store_serializer.rb +16 -0
- data/app/serializers/spree/api/v3/variant_serializer.rb +1 -4
- data/app/services/spree/api/v3/request_schema.rb +15 -7
- data/config/routes.rb +5 -2
- data/lib/spree/api/agent_workflow_endpoints.rb +2 -2
- data/lib/spree/api/agent_write_schemas.rb +6 -6
- data/lib/spree/api/dependencies.rb +2 -2
- data/lib/spree/api/enum_types.rb +2 -1
- data/lib/spree/api/oauth.rb +40 -20
- data/lib/typelizer/open_enums.rb +22 -3
- data/openapi/admin.yaml +87631 -0
- data/openapi/store.yaml +25412 -0
- metadata +12 -7
|
@@ -152,6 +152,12 @@ module Spree
|
|
|
152
152
|
}.freeze
|
|
153
153
|
private_constant :PREFERENCE_LIST_NAMES
|
|
154
154
|
|
|
155
|
+
# Settings declared with a code format share the code list request
|
|
156
|
+
# bodies take, so a value read back is the type a write takes.
|
|
157
|
+
FORMAT_CODE_LISTS = { 'iso-country' => :country, 'currency' => :currency, 'locale' => :locale,
|
|
158
|
+
'timezone' => :time_zone }.freeze
|
|
159
|
+
private_constant :FORMAT_CODE_LISTS
|
|
160
|
+
|
|
155
161
|
def self.preference_type_hint(schema, name)
|
|
156
162
|
types = Array(schema['type'])
|
|
157
163
|
options = {}
|
|
@@ -165,6 +171,9 @@ module Spree
|
|
|
165
171
|
if schema['enum']
|
|
166
172
|
options[:enum] = schema['enum'].compact
|
|
167
173
|
options[:enum_type_name] = PREFERENCE_LIST_NAMES[name.to_sym] if PREFERENCE_LIST_NAMES.key?(name.to_sym)
|
|
174
|
+
elsif (code_list = FORMAT_CODE_LISTS[(schema['items'] || schema)['format']])
|
|
175
|
+
options[:enum_type_name], values = Spree::Api::V3::RequestSchema::CODE_LISTS.fetch(code_list)
|
|
176
|
+
options[:enum] = values.call
|
|
168
177
|
end
|
|
169
178
|
options.empty? ? hint : [hint, options]
|
|
170
179
|
end
|
|
@@ -192,6 +201,25 @@ module Spree
|
|
|
192
201
|
end
|
|
193
202
|
end
|
|
194
203
|
|
|
204
|
+
# Declares the opt-in `custom_fields` expand. The Store API sends only
|
|
205
|
+
# values whose definition is storefront visible; Admin API serializers
|
|
206
|
+
# pass `admin: true` to send every value.
|
|
207
|
+
#
|
|
208
|
+
# @param admin [Boolean] whether to send every value rather than only storefront visible ones
|
|
209
|
+
# @return [void]
|
|
210
|
+
def self.expandable_custom_fields(admin: false)
|
|
211
|
+
if admin
|
|
212
|
+
many :custom_fields,
|
|
213
|
+
resource: proc { Spree.api.admin_custom_field_serializer },
|
|
214
|
+
if: proc { expand?('custom_fields') }
|
|
215
|
+
else
|
|
216
|
+
many :storefront_custom_fields,
|
|
217
|
+
key: :custom_fields,
|
|
218
|
+
resource: proc { Spree.api.custom_field_serializer },
|
|
219
|
+
if: proc { expand?('custom_fields') }
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
195
223
|
# Plain decimal notation. BigDecimal renders 0.06 as "0.6e-1", which
|
|
196
224
|
# is what a client would otherwise print beside a unit.
|
|
197
225
|
#
|
|
@@ -50,10 +50,7 @@ module Spree
|
|
|
50
50
|
resource: proc { Spree.api.category_serializer },
|
|
51
51
|
if: proc { expand?('ancestors') }
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
key: :custom_fields,
|
|
55
|
-
resource: proc { Spree.api.custom_field_serializer },
|
|
56
|
-
if: proc { expand?('custom_fields') }
|
|
53
|
+
expandable_custom_fields
|
|
57
54
|
end
|
|
58
55
|
end
|
|
59
56
|
end
|
|
@@ -29,10 +29,7 @@ module Spree
|
|
|
29
29
|
image_url_for(collection.square_image)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
key: :custom_fields,
|
|
34
|
-
resource: proc { Spree.api.custom_field_serializer },
|
|
35
|
-
if: proc { expand?('custom_fields') }
|
|
32
|
+
expandable_custom_fields
|
|
36
33
|
end
|
|
37
34
|
end
|
|
38
35
|
end
|
|
@@ -59,6 +59,8 @@ module Spree
|
|
|
59
59
|
one :stock_location, resource: proc { Spree.api.stock_location_serializer }
|
|
60
60
|
many :delivery_rates, resource: proc { Spree.api.delivery_rate_serializer }
|
|
61
61
|
many :tax_lines, resource: proc { Spree.api.tax_line_serializer }, if: proc { expand?('tax_lines') }
|
|
62
|
+
|
|
63
|
+
expandable_custom_fields
|
|
62
64
|
end
|
|
63
65
|
end
|
|
64
66
|
end
|
|
@@ -73,6 +73,8 @@ module Spree
|
|
|
73
73
|
# exactly-one-adjustable rule makes the nested view complete, and the
|
|
74
74
|
# owner already carries the tax totals.
|
|
75
75
|
many :tax_lines, resource: proc { Spree.api.tax_line_serializer }, if: proc { expand?('tax_lines') }
|
|
76
|
+
|
|
77
|
+
expandable_custom_fields
|
|
76
78
|
end
|
|
77
79
|
end
|
|
78
80
|
end
|
|
@@ -79,6 +79,8 @@ module Spree
|
|
|
79
79
|
one :shipping_address, resource: proc { Spree.api.address_serializer }
|
|
80
80
|
one :gift_card, resource: proc { Spree.api.gift_card_serializer }
|
|
81
81
|
one :market, resource: proc { Spree.api.market_serializer }
|
|
82
|
+
|
|
83
|
+
expandable_custom_fields
|
|
82
84
|
end
|
|
83
85
|
end
|
|
84
86
|
end
|
|
@@ -42,10 +42,12 @@ module Spree
|
|
|
42
42
|
Spree.api.payment_source_serializer
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
serializer.new(payment.source).to_h
|
|
45
|
+
serializer.new(payment.source, params: nested_params('source')).to_h
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
one :payment_method, resource: proc { Spree.api.payment_method_serializer }
|
|
49
|
+
|
|
50
|
+
expandable_custom_fields
|
|
49
51
|
end
|
|
50
52
|
end
|
|
51
53
|
end
|
|
@@ -137,10 +137,7 @@ module Spree
|
|
|
137
137
|
resource: proc { Spree.api.category_serializer },
|
|
138
138
|
if: proc { expand?('categories') }
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
key: :custom_fields,
|
|
142
|
-
resource: proc { Spree.api.custom_field_serializer },
|
|
143
|
-
if: proc { expand?('custom_fields') }
|
|
140
|
+
expandable_custom_fields
|
|
144
141
|
|
|
145
142
|
typelize prior_price: ['PriceHistory', nullable: true],
|
|
146
143
|
seller_id: [:string, nullable: true]
|
|
@@ -5,12 +5,33 @@ module Spree
|
|
|
5
5
|
class InvitationSerializer < V3::BaseSerializer
|
|
6
6
|
typelize email: :string, status: [:string, enum: Spree::Invitation.statuses, enum_type_name: 'InvitationStatus'],
|
|
7
7
|
expires_at: [:string, nullable: true],
|
|
8
|
-
accepted_at: [:string, nullable: true]
|
|
8
|
+
accepted_at: [:string, nullable: true],
|
|
9
|
+
inviter_email: :string,
|
|
10
|
+
invitee_exists: [:boolean, optional: true],
|
|
11
|
+
seller: '{ id: string; name: string }'
|
|
9
12
|
|
|
10
13
|
attributes :email,
|
|
11
14
|
created_at: :iso8601, expires_at: :iso8601, accepted_at: :iso8601
|
|
12
15
|
|
|
13
16
|
attributes status: :string
|
|
17
|
+
|
|
18
|
+
attribute :inviter_email do |invitation|
|
|
19
|
+
invitation.inviter.email
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Drives the acceptance page's sign-in vs sign-up branch, so only the
|
|
23
|
+
# public lookup carries it: whoever holds the emailed link already
|
|
24
|
+
# knows the address. A seller's team screen does not get to learn
|
|
25
|
+
# which addresses hold accounts elsewhere on the marketplace.
|
|
26
|
+
attribute :invitee_exists, if: proc { params[:acceptance] } do |invitation|
|
|
27
|
+
invitation.invitee_account.present?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The smallest shape the acceptance page's title ("Join <seller>")
|
|
31
|
+
# needs; the page is public, so nothing more of the seller goes out.
|
|
32
|
+
attribute :seller do |invitation|
|
|
33
|
+
{ id: invitation.resource.prefixed_id, name: invitation.resource.name }
|
|
34
|
+
end
|
|
14
35
|
end
|
|
15
36
|
end
|
|
16
37
|
end
|
|
@@ -17,7 +17,7 @@ module Spree
|
|
|
17
17
|
products_count: :number,
|
|
18
18
|
default_currency: [:string, enum: Spree::Money::Rounding::CURRENCY_CODES, enum_type_name: 'CurrencyCode'],
|
|
19
19
|
supported_currencies: [:string, multi: true, enum: Spree::Money::Rounding::CURRENCY_CODES, enum_type_name: 'CurrencyCode'],
|
|
20
|
-
timezone: [:string, nullable: true]
|
|
20
|
+
timezone: [:string, nullable: true, enum: Spree::TimeZones::SUGGESTED, enum_type_name: 'TimeZone']
|
|
21
21
|
|
|
22
22
|
attributes :timezone,
|
|
23
23
|
:status, :legal_name, :registration_number,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
# The current store's public identity and default SEO metadata, in the
|
|
5
|
+
# request's locale. Missing translations fall back to the store's default
|
|
6
|
+
# locale.
|
|
7
|
+
class StoreSerializer < BaseSerializer
|
|
8
|
+
typelize name: :string,
|
|
9
|
+
seo_title: [:string, nullable: true],
|
|
10
|
+
meta_description: [:string, nullable: true]
|
|
11
|
+
|
|
12
|
+
attributes :name, :seo_title, :meta_description
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -127,10 +127,7 @@ module Spree
|
|
|
127
127
|
|
|
128
128
|
many :option_values, resource: proc { Spree.api.option_value_serializer }
|
|
129
129
|
|
|
130
|
-
|
|
131
|
-
key: :custom_fields,
|
|
132
|
-
resource: proc { Spree.api.custom_field_serializer },
|
|
133
|
-
if: proc { expand?('custom_fields') }
|
|
130
|
+
expandable_custom_fields
|
|
134
131
|
|
|
135
132
|
typelize prior_price: ['PriceHistory', nullable: true]
|
|
136
133
|
|
|
@@ -40,22 +40,25 @@ module Spree
|
|
|
40
40
|
datetime: { type: :string, format: :'date-time' },
|
|
41
41
|
id: { type: :string },
|
|
42
42
|
any: {},
|
|
43
|
-
# Codes from the
|
|
43
|
+
# Codes from the lists below.
|
|
44
44
|
country: { type: :string },
|
|
45
45
|
currency: { type: :string },
|
|
46
46
|
locale: { type: :string },
|
|
47
|
+
time_zone: { type: :string },
|
|
47
48
|
weight_unit: { type: :string },
|
|
48
49
|
dimension_unit: { type: :string }
|
|
49
50
|
}.freeze
|
|
50
51
|
|
|
51
|
-
# The
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
52
|
+
# The lists a code field takes, published once per API as a named
|
|
53
|
+
# component — the same `CountryCode`, `CurrencyCode` and `WeightUnit`
|
|
54
|
+
# types the serializers declare, so a value read from a response is the
|
|
55
|
+
# type a request takes. A list `Typelizer::OpenEnums` does not close
|
|
56
|
+
# only suggests its values.
|
|
55
57
|
CODE_LISTS = {
|
|
56
58
|
country: ['CountryCode', -> { Spree::IsoData.country_codes }],
|
|
57
59
|
currency: ['CurrencyCode', -> { Spree::Money::Rounding::CURRENCY_CODES }],
|
|
58
60
|
locale: ['LocaleCode', -> { Spree::Locales::ALL }],
|
|
61
|
+
time_zone: ['TimeZone', -> { Spree::TimeZones::SUGGESTED }],
|
|
59
62
|
weight_unit: ['WeightUnit', -> { Spree::Variant::WEIGHT_UNITS }],
|
|
60
63
|
dimension_unit: ['DimensionUnit', -> { Spree::Variant::DIMENSION_UNITS }]
|
|
61
64
|
}.freeze
|
|
@@ -348,7 +351,12 @@ module Spree
|
|
|
348
351
|
next unless CODE_LISTS.key?(field.kind)
|
|
349
352
|
|
|
350
353
|
name, values = CODE_LISTS[field.kind]
|
|
351
|
-
lists[name] =
|
|
354
|
+
lists[name] = if Typelizer::OpenEnums.closed?(name)
|
|
355
|
+
{ type: :string, enum: values.call }
|
|
356
|
+
else
|
|
357
|
+
Typelizer::OpenEnums.open_schema({ type: :string }, values.call)
|
|
358
|
+
.merge(description: Typelizer::OpenEnums.description(name)).compact
|
|
359
|
+
end
|
|
352
360
|
end
|
|
353
361
|
end
|
|
354
362
|
|
|
@@ -469,7 +477,7 @@ module Spree
|
|
|
469
477
|
|
|
470
478
|
# The same open list the serializers publish (`enum_type_name`):
|
|
471
479
|
# clients autocomplete the built-in values and still accept any other.
|
|
472
|
-
|
|
480
|
+
Typelizer::OpenEnums.open_schema(schema, field.enum)
|
|
473
481
|
end
|
|
474
482
|
end
|
|
475
483
|
end
|
data/config/routes.rb
CHANGED
|
@@ -22,6 +22,9 @@ Spree::Core::Engine.add_routes do
|
|
|
22
22
|
# Current channel context (never a list — see Store::ChannelController)
|
|
23
23
|
resource :channel, only: [:show], controller: 'channel'
|
|
24
24
|
|
|
25
|
+
# Current store context (name and default SEO metadata, localized)
|
|
26
|
+
resource :store, only: [:show], controller: 'store'
|
|
27
|
+
|
|
25
28
|
# Catalog
|
|
26
29
|
resources :products, only: [:index, :show] do
|
|
27
30
|
collection do
|
|
@@ -287,9 +290,9 @@ Spree::Core::Engine.add_routes do
|
|
|
287
290
|
resource :store, only: [:show, :update], controller: 'store' do
|
|
288
291
|
get :data_sources
|
|
289
292
|
end
|
|
290
|
-
# The store's own custom fields, addressed by its id like every other
|
|
293
|
+
# The store's own custom fields and translations, addressed by its id like every other
|
|
291
294
|
# parent. The controller only ever resolves the current store.
|
|
292
|
-
resources :stores, only: [], concerns: :custom_fieldable
|
|
295
|
+
resources :stores, only: [], concerns: [:custom_fieldable, :translatable]
|
|
293
296
|
|
|
294
297
|
# Staff & access (invitations, admin users, roles, API keys)
|
|
295
298
|
resources :admin_users, only: [:index, :show, :update, :destroy]
|
|
@@ -95,8 +95,8 @@ module Spree
|
|
|
95
95
|
path.gsub(/:([a-z_]+)/) { "{#{Regexp.last_match(1)}}" }
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
# A
|
|
99
|
-
#
|
|
98
|
+
# A body the document does not describe is simply passed through
|
|
99
|
+
# unchecked.
|
|
100
100
|
#
|
|
101
101
|
# `AgentWriteSchemas` already holds this file parsed, so it is read
|
|
102
102
|
# through there rather than a second time: the document is nearly a
|
|
@@ -33,12 +33,14 @@ module Spree
|
|
|
33
33
|
self.for(key).keys
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
# Where the generated document lives
|
|
37
|
-
#
|
|
36
|
+
# Where the generated document lives: `docs/` in a source checkout,
|
|
37
|
+
# or the copy the release packs into the gem. Resolved lazily: the
|
|
38
|
+
# engine class does not exist yet while this file loads.
|
|
38
39
|
#
|
|
39
40
|
# @return [Pathname]
|
|
40
41
|
def spec_path
|
|
41
|
-
Spree::Api::Engine.root.join('..', '..', 'docs', 'api-reference', 'admin.yaml')
|
|
42
|
+
source = Spree::Api::Engine.root.join('..', '..', 'docs', 'api-reference', 'admin.yaml')
|
|
43
|
+
source.exist? ? source : Spree::Api::Engine.root.join('openapi', 'admin.yaml')
|
|
42
44
|
end
|
|
43
45
|
|
|
44
46
|
# The parsed document, for a reader that needs more of it than the
|
|
@@ -63,9 +65,7 @@ module Spree
|
|
|
63
65
|
end
|
|
64
66
|
|
|
65
67
|
# A missing or unparseable document is not a boot failure: the generic
|
|
66
|
-
# write tools simply offer nothing, which is the safe direction.
|
|
67
|
-
# installation running from the packaged gems has no docs/ directory at
|
|
68
|
-
# all.
|
|
68
|
+
# write tools simply offer nothing, which is the safe direction.
|
|
69
69
|
def load_schemas
|
|
70
70
|
path = spec_path
|
|
71
71
|
return {} unless File.exist?(path)
|
|
@@ -18,7 +18,7 @@ module Spree
|
|
|
18
18
|
product_filter_sort_option media_event return return_line_item exchange exchange_line_item claim
|
|
19
19
|
claim_line_item return_reason claim_reason digital_asset export gift_card_batch import import_row
|
|
20
20
|
invitation newsletter_subscriber promotion refund stock_level stock_movement stock_reservation
|
|
21
|
-
stock_transfer company company_membership company_invitation seller file_upload file_upload_target].freeze
|
|
21
|
+
stock_transfer company company_membership company_invitation seller file_upload file_upload_target store].freeze
|
|
22
22
|
|
|
23
23
|
ADMIN_SERIALIZERS =
|
|
24
24
|
%i[country state locale applied_promotion discount tax_line cart delivery_zone delivery_profile
|
|
@@ -32,7 +32,7 @@ module Spree
|
|
|
32
32
|
exchange exchange_line_item claim claim_line_item package_type policy tax_category tax_rate
|
|
33
33
|
tax_identifier company company_membership company_invitation catalog catalog_price catalog_price_tier
|
|
34
34
|
catalog_product price_list_product catalog_assignment catalog_quantity_rule catalog_product_term
|
|
35
|
-
catalog_order_minimum tax_exemption_certificate tax_exemption_reason admin_user actor seller_team_member product_submission
|
|
35
|
+
catalog_order_minimum tax_exemption_certificate tax_exemption_reason admin_user actor session seller_team_member product_submission
|
|
36
36
|
seller seller_requirement seller_requirement_submission seller_requirement_status commission_rate
|
|
37
37
|
commission_rule commission_line seller_transfer seller_payout seller_balance payment_split payment_capture_event address address_owner
|
|
38
38
|
channel order_routing_rule product_publication market delivery_method delivery_method_service
|
data/lib/spree/api/enum_types.rb
CHANGED
|
@@ -110,7 +110,8 @@ module Spree
|
|
|
110
110
|
type = "export type #{name} = (typeof #{constant})[number] | (string & {})"
|
|
111
111
|
type = "export type #{name} =\n | (typeof #{constant})[number]\n | (string & {})" if type.length > LINE_WIDTH
|
|
112
112
|
|
|
113
|
-
"
|
|
113
|
+
summary = Typelizer::OpenEnums.description(name) || "The built-in values of `#{name}`. Extensions may add more."
|
|
114
|
+
"\n/** #{summary} */\n#{line}\n#{type}\n"
|
|
114
115
|
end
|
|
115
116
|
end
|
|
116
117
|
end
|
data/lib/spree/api/oauth.rb
CHANGED
|
@@ -15,23 +15,11 @@ module Spree
|
|
|
15
15
|
# which is why this lives in `spree_api` rather than in any one gem that
|
|
16
16
|
# happens to need it.
|
|
17
17
|
module Oauth
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
# Spree::Api::Oauth.register_resource(:admin, '/api/v3/admin')
|
|
22
|
-
#
|
|
23
|
-
# A path rather than a full URL because the identifier must equal the
|
|
24
|
-
# URL the client was actually pointed at: the origin comes from the
|
|
25
|
-
# request where there is one, and from the application's configured
|
|
26
|
-
# URL options otherwise. A store's own `url` column is deliberately not
|
|
27
|
-
# consulted — it describes the storefront, which is frequently neither
|
|
28
|
-
# the host nor the scheme the Admin API answers on.
|
|
29
|
-
# Whether a redirect URI points at the machine the client runs on.
|
|
18
|
+
# Whether a redirect URI points at the machine the client runs on, by IP
|
|
19
|
+
# literal (`127.0.0.1`, `[::1]`).
|
|
30
20
|
#
|
|
31
21
|
# Decided by parsing the host as an address, the same way Doorkeeper
|
|
32
|
-
# does when it ignores the port of a loopback redirect
|
|
33
|
-
# is deliberately not loopback here either, and a registration must use
|
|
34
|
-
# the IP literal for an ephemeral port to match.
|
|
22
|
+
# does when it ignores the port of a loopback redirect.
|
|
35
23
|
#
|
|
36
24
|
# @param uri [URI, String]
|
|
37
25
|
# @return [Boolean]
|
|
@@ -44,6 +32,37 @@ module Spree
|
|
|
44
32
|
false
|
|
45
33
|
end
|
|
46
34
|
|
|
35
|
+
# Whether a redirect URI may use plain http: an IP-literal loopback, or
|
|
36
|
+
# `localhost` with an explicit port.
|
|
37
|
+
#
|
|
38
|
+
# Claude Code only ever calls back on `http://localhost:<port>`. Doorkeeper
|
|
39
|
+
# compares a `localhost` callback exactly, so the host must be lowercase
|
|
40
|
+
# and the port explicit: anything else could never match an
|
|
41
|
+
# authorization.
|
|
42
|
+
#
|
|
43
|
+
# @param uri [URI, String]
|
|
44
|
+
# @return [Boolean]
|
|
45
|
+
def self.local_redirect_uri?(uri)
|
|
46
|
+
uri = URI.parse(uri.to_s) unless uri.respond_to?(:host)
|
|
47
|
+
return true if loopback_uri?(uri)
|
|
48
|
+
|
|
49
|
+
uri.host == 'localhost' &&
|
|
50
|
+
(1..65_535).cover?(uri.port) && uri.port != uri.default_port
|
|
51
|
+
rescue StandardError
|
|
52
|
+
false
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Protected resources, each registered by the surface that owns it as a
|
|
56
|
+
# path relative to this application's own origin.
|
|
57
|
+
#
|
|
58
|
+
# Spree::Api::Oauth.register_resource(:admin, '/api/v3/admin')
|
|
59
|
+
#
|
|
60
|
+
# A path rather than a full URL because the identifier must equal the
|
|
61
|
+
# URL the client was actually pointed at: the origin comes from the
|
|
62
|
+
# request where there is one, and from the application's configured
|
|
63
|
+
# URL options otherwise. A store's own `url` column is deliberately not
|
|
64
|
+
# consulted — it describes the storefront, which is frequently neither
|
|
65
|
+
# the host nor the scheme the Admin API answers on.
|
|
47
66
|
mattr_accessor :resources, default: {}
|
|
48
67
|
|
|
49
68
|
class << self
|
|
@@ -191,17 +210,18 @@ module Spree
|
|
|
191
210
|
|
|
192
211
|
# A redirect URI is where authorization codes are delivered, so a
|
|
193
212
|
# plain-http one hands them to anyone on the path — except a
|
|
194
|
-
# loopback one, which never leaves the machine. A
|
|
195
|
-
# listens on
|
|
196
|
-
# it
|
|
197
|
-
# refusing it would lock
|
|
213
|
+
# loopback or `localhost` one, which never leaves the machine. A
|
|
214
|
+
# terminal client listens on a local port and has no certificate
|
|
215
|
+
# for it. RFC 8252 §7.3 recommends the IP literal, but Claude Code
|
|
216
|
+
# only sends `localhost`, so refusing it would lock Claude Code
|
|
217
|
+
# out entirely.
|
|
198
218
|
#
|
|
199
219
|
# Passed as a callable so the exemption is per URI rather than
|
|
200
220
|
# per environment: everything else still has to be https.
|
|
201
221
|
force_ssl_in_redirect_uri do |uri|
|
|
202
222
|
next false if Rails.env.development?
|
|
203
223
|
|
|
204
|
-
!Spree::Api::Oauth.
|
|
224
|
+
!Spree::Api::Oauth.local_redirect_uri?(uri)
|
|
205
225
|
end
|
|
206
226
|
|
|
207
227
|
# RFC 8707. Registering the validator is what binds an audience
|