spree_api 6.0.0.rc4 → 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/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/scoped_authorization.rb +6 -2
- data/app/controllers/spree/api/v3/admin/companies/addresses_controller.rb +7 -1
- 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/gift_cards_controller.rb +1 -8
- data/app/controllers/spree/api/v3/admin/orders_controller.rb +11 -5
- 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/store_controller.rb +1 -1
- data/app/controllers/spree/api/v3/admin/variants_controller.rb +96 -1
- data/app/controllers/spree/api/v3/oauth/authorizations_controller.rb +17 -6
- data/app/controllers/spree/api/v3/resource_controller.rb +35 -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 +3 -3
- 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/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/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/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/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_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/variant_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/base_serializer.rb +27 -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 +19 -11
- data/lib/spree/api/agent_write_schemas.rb +15 -3
- data/lib/spree/api/dependencies.rb +2 -2
- data/lib/spree/api/enum_types.rb +118 -0
- data/lib/spree/api/openapi/filter_documentation.rb +8 -1
- data/lib/spree/api/typelizer_generation.rb +5 -0
- data/lib/typelizer/open_enums.rb +40 -16
- metadata +17 -6
|
@@ -41,19 +41,6 @@ module Spree
|
|
|
41
41
|
'archived' => -> { Spree.product_archive_workflow }
|
|
42
42
|
}.freeze
|
|
43
43
|
|
|
44
|
-
# The operator's price shape, except `currency` may be left out: the
|
|
45
|
-
# store's currency is assumed (see `default_price_currencies`).
|
|
46
|
-
PRICE = Spree::Api::V3::RequestSchema.new do |schema|
|
|
47
|
-
schema.attribute :currency, :currency,
|
|
48
|
-
example: 'USD',
|
|
49
|
-
description: "Omit to price in the store's currency. A seller has no currency of their own."
|
|
50
|
-
schema.attribute :amount, :decimal,
|
|
51
|
-
required: true,
|
|
52
|
-
example: '24.00',
|
|
53
|
-
description: 'A canonical decimal string (`"1234.56"`: period decimal, no grouping). The API does not parse localized input.'
|
|
54
|
-
schema.attribute :compare_at_amount, :decimal, nullable: true, example: '30.00'
|
|
55
|
-
end
|
|
56
|
-
|
|
57
44
|
scoped_resource :products
|
|
58
45
|
|
|
59
46
|
request_params :product do |body|
|
|
@@ -69,7 +56,7 @@ module Spree
|
|
|
69
56
|
description: "The marketplace's product type this product is listed against, from the product types endpoint. Null detaches it. Saving with a type seeds its option types onto the product."
|
|
70
57
|
body.attribute :delivery_profile_id, :id,
|
|
71
58
|
description: "Which of the marketplace's delivery profiles the product ships under. A new product sent without one lands on the profile marked default."
|
|
72
|
-
body.attribute :prices, :array, of:
|
|
59
|
+
body.attribute :prices, :array, of: Spree::Api::V3::Seller::PriceRequestSchema,
|
|
73
60
|
description: "Shorthand for a simple (no-options) product: per-currency prices that forward to the product's sole variant."
|
|
74
61
|
# `external_url` stays out for the same reason it does on the
|
|
75
62
|
# operator's endpoint: it would make the server fetch a URL the
|
|
@@ -83,56 +70,11 @@ module Spree
|
|
|
83
70
|
media_item.attribute :variant_ids, :array, of: :id,
|
|
84
71
|
description: 'Replaces the variants this media is linked to. An empty list clears them.'
|
|
85
72
|
end
|
|
86
|
-
# Narrower than the operator's variant set: `seller_id` would let a
|
|
87
|
-
# payload hand a variant to somebody else, and
|
|
88
|
-
# `tax_category_id`/`delivery_profile_id` are marketplace
|
|
89
|
-
# configuration.
|
|
90
73
|
body.attribute :variants, :array,
|
|
91
|
-
as: '
|
|
74
|
+
as: 'ProductVariantParams',
|
|
92
75
|
description: 'The variants, as a whole: one absent from the list is removed. Pass `id` to update an existing variant.' do |variant|
|
|
76
|
+
variant.extends Spree::Api::V3::Seller::VariantRequestSchema
|
|
93
77
|
variant.attribute :id, :id
|
|
94
|
-
variant.attribute :sku, :string
|
|
95
|
-
variant.attribute :barcode, :string
|
|
96
|
-
variant.attribute :cost_price, :decimal, nullable: true
|
|
97
|
-
variant.attribute :cost_currency, :currency
|
|
98
|
-
variant.attribute :weight, :number, nullable: true
|
|
99
|
-
variant.attribute :height, :number, nullable: true
|
|
100
|
-
variant.attribute :width, :number, nullable: true
|
|
101
|
-
variant.attribute :depth, :number, nullable: true
|
|
102
|
-
variant.attribute :weight_unit, :string, choices: Spree::Variant::WEIGHT_UNITS
|
|
103
|
-
variant.attribute :dimensions_unit, :string, choices: Spree::Variant::DIMENSION_UNITS
|
|
104
|
-
variant.attribute :hs_code, :string
|
|
105
|
-
variant.attribute :customs_description, :string
|
|
106
|
-
variant.attribute :country_of_origin, :country
|
|
107
|
-
variant.attribute :minimum_order_quantity, :integer, nullable: true
|
|
108
|
-
variant.attribute :order_multiple, :integer, nullable: true
|
|
109
|
-
variant.attribute :purchase_unit, :string
|
|
110
|
-
variant.attribute :units_per_carton, :integer,
|
|
111
|
-
nullable: true,
|
|
112
|
-
description: 'How many units fill one carton.'
|
|
113
|
-
variant.attribute :carton_package_type_id, :id,
|
|
114
|
-
nullable: true,
|
|
115
|
-
description: 'The carton this variant is packed into: a package type of kind `carton`.'
|
|
116
|
-
variant.attribute :carton_weight, :number,
|
|
117
|
-
nullable: true,
|
|
118
|
-
description: "What one packed carton weighs, in the variant's weight unit."
|
|
119
|
-
variant.attribute :cartons_per_pallet, :integer,
|
|
120
|
-
nullable: true,
|
|
121
|
-
description: 'How many cartons stack on one pallet.'
|
|
122
|
-
variant.attribute :track_inventory, :boolean
|
|
123
|
-
variant.attribute :preorderable, :boolean
|
|
124
|
-
variant.attribute :preorder_ships_at, :datetime,
|
|
125
|
-
nullable: true,
|
|
126
|
-
description: 'The "ships by" date shown to customers while the variant is a pre-order. Null clears it.'
|
|
127
|
-
variant.attribute :backorder_limit, :integer,
|
|
128
|
-
nullable: true,
|
|
129
|
-
description: 'Maximum units sellable beyond available stock, capping backorders and pre-orders. Null is unlimited.'
|
|
130
|
-
variant.attribute :position, :integer
|
|
131
|
-
variant.attribute :options, :array, of: Spree::Api::V3::VariantOptionRequestSchema,
|
|
132
|
-
description: 'One pair per option type the variant takes part in (size, color).'
|
|
133
|
-
variant.attribute :prices, :array, of: PRICE, description: 'Per-currency prices. Upserted by currency.'
|
|
134
|
-
variant.attribute :stock_levels, :array, of: Spree::Api::V3::StockLevelRequestSchema,
|
|
135
|
-
description: "Stock per location, at this seller's own locations only: a location belonging to anyone else is ignored."
|
|
136
78
|
end
|
|
137
79
|
end
|
|
138
80
|
|
|
@@ -270,6 +212,10 @@ module Spree
|
|
|
270
212
|
Spree.product_update_workflow
|
|
271
213
|
end
|
|
272
214
|
|
|
215
|
+
def nested_permitted_models
|
|
216
|
+
{ media: Spree::Media, variants: Spree::Variant }
|
|
217
|
+
end
|
|
218
|
+
|
|
273
219
|
# The workflow builds its own product, which would leave a seller's
|
|
274
220
|
# listing unowned. Handing it one built through `current_seller.products`
|
|
275
221
|
# keeps the seller on the record, whatever the payload claims.
|
|
@@ -69,12 +69,8 @@ module Spree
|
|
|
69
69
|
# What extensions opened to sellers, never the operator-level list:
|
|
70
70
|
# an attribute opened to operators must not become seller-writable
|
|
71
71
|
# by accident.
|
|
72
|
-
def
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
Array(model_class.try(:additional_seller_permitted_attributes))
|
|
76
|
-
rescue NotImplementedError
|
|
77
|
-
[]
|
|
72
|
+
def additional_permitted_attributes_for(model)
|
|
73
|
+
Array(model.try(:additional_seller_permitted_attributes))
|
|
78
74
|
end
|
|
79
75
|
|
|
80
76
|
def authorize_resource!(resource = @resource, action = action_name.to_sym)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Store
|
|
5
|
+
module Carts
|
|
6
|
+
# Every address this cart may be shipped or billed to, in one call:
|
|
7
|
+
# the buyer's own book, plus the book of the company the cart names
|
|
8
|
+
# — that node's entries and the ones it inherits from the nodes above
|
|
9
|
+
# it — limited to the countries the cart's market sells to.
|
|
10
|
+
#
|
|
11
|
+
# A checkout address step would otherwise merge two books and filter
|
|
12
|
+
# them by the market itself. Asking the cart keeps the picker and the
|
|
13
|
+
# checkout from ever disagreeing about what is usable.
|
|
14
|
+
#
|
|
15
|
+
# Signed in only, unlike the rest of the cart surface. What comes
|
|
16
|
+
# back is not the cart's own data but the books behind it — every
|
|
17
|
+
# address the buyer has saved, and their company's sites. A guest
|
|
18
|
+
# token says the caller holds this cart, which it may well do
|
|
19
|
+
# because the account holder adopted a cart the attacker started;
|
|
20
|
+
# it says nothing about the account. A genuine guest cart has
|
|
21
|
+
# neither book, so requiring the account costs nothing but the 401.
|
|
22
|
+
class AddressesController < Store::ResourceController
|
|
23
|
+
include Spree::Api::V3::CartResolvable
|
|
24
|
+
|
|
25
|
+
prepend_before_action :require_authentication!
|
|
26
|
+
|
|
27
|
+
protected
|
|
28
|
+
|
|
29
|
+
def set_parent
|
|
30
|
+
@parent = @cart = find_cart(token: nil)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def model_class
|
|
34
|
+
Spree::Address
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def serializer_class
|
|
38
|
+
Spree.api.address_serializer
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def collection_includes
|
|
42
|
+
[:owner]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def parent_association
|
|
46
|
+
:selectable_addresses
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -13,7 +13,7 @@ module Spree
|
|
|
13
13
|
|
|
14
14
|
CART_ADDRESS = Spree::Api::V3::RequestSchema.shape('CartAddressParams') do |schema|
|
|
15
15
|
schema.attribute :id, :id,
|
|
16
|
-
description:
|
|
16
|
+
description: "An existing address from the buyer's address book, or from the book of the company the cart is for. The other fields are then ignored."
|
|
17
17
|
schema.include_attributes Spree::Api::V3::Store::AddressRequestSchema
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -25,10 +25,10 @@ module Spree
|
|
|
25
25
|
schema.attribute :currency, :currency
|
|
26
26
|
schema.attribute :locale, :locale
|
|
27
27
|
schema.attribute :shipping_address_id, :id,
|
|
28
|
-
description:
|
|
28
|
+
description: "An existing address to ship to, from the buyer's address book or from the book of the company the cart is for. List both with `GET /store/carts/:id/addresses`.",
|
|
29
29
|
example: 'addr_abc123'
|
|
30
30
|
schema.attribute :billing_address_id, :id,
|
|
31
|
-
description:
|
|
31
|
+
description: "An existing address to bill, from the buyer's address book or from the book of the company the cart is for. List both with `GET /store/carts/:id/addresses`.",
|
|
32
32
|
example: 'addr_def456'
|
|
33
33
|
schema.attribute :preferred_stock_location_id, :id
|
|
34
34
|
schema.attribute :company_id, :id, nullable: true, description: <<~TEXT.squish
|
|
@@ -69,6 +69,10 @@ module Spree
|
|
|
69
69
|
Spree.api.address_serializer
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
def collection_includes
|
|
73
|
+
[:owner]
|
|
74
|
+
end
|
|
75
|
+
|
|
72
76
|
# Reading a book and keeping one are different rights: the listing
|
|
73
77
|
# includes what this node inherits, writes stay with the node that
|
|
74
78
|
# owns the row. The Store API authorizes purely by what this scope
|
|
@@ -10,14 +10,30 @@ module Spree
|
|
|
10
10
|
# irreversible, and an unattended session should not be enough to
|
|
11
11
|
# trigger it — the same bar the account already applies to changing
|
|
12
12
|
# an email address.
|
|
13
|
+
#
|
|
14
|
+
# Nothing happens until the request is confirmed from the link emailed
|
|
15
|
+
# to the account's address (see DataRequestConfirmationsController),
|
|
16
|
+
# and the export link is never shown to this session.
|
|
13
17
|
class DataRequestsController < ResourceController
|
|
14
18
|
include Spree::Api::V3::CurrentPasswordConfirmation
|
|
19
|
+
include Spree::Api::V3::AllowedRedirectUrl
|
|
15
20
|
|
|
16
21
|
prepend_before_action :require_authentication!
|
|
17
22
|
|
|
23
|
+
# Each request while one waits sends the confirmation email again.
|
|
24
|
+
rate_limit to: Spree::Api::Config[:rate_limit_password_reset],
|
|
25
|
+
within: Spree::Api::Config[:rate_limit_window].seconds,
|
|
26
|
+
store: Rails.cache,
|
|
27
|
+
only: :create,
|
|
28
|
+
with: RATE_LIMIT_RESPONSE
|
|
29
|
+
|
|
18
30
|
request_params 'CreateDataRequestParams', action: :create do |body|
|
|
19
31
|
body.attribute :kind, :string, choices: Spree::DataRequest::KINDS, description: 'Defaults to `access`.'
|
|
20
32
|
body.attribute :current_password, :string, description: 'Required for `erasure`.'
|
|
33
|
+
body.attribute :redirect_url, :string,
|
|
34
|
+
format: :uri,
|
|
35
|
+
example: 'https://myshop.com/account/data-requests/confirm',
|
|
36
|
+
description: "The storefront page the confirmation link opens, with the token appended. Ignored unless it matches one of the store's allowed origins."
|
|
21
37
|
end
|
|
22
38
|
|
|
23
39
|
# POST /api/v3/store/customers/me/data_requests
|
|
@@ -27,7 +43,9 @@ module Spree
|
|
|
27
43
|
result = Spree::DataRequests::Create.call(
|
|
28
44
|
store: current_store,
|
|
29
45
|
customer: current_user,
|
|
30
|
-
kind: requested_kind
|
|
46
|
+
kind: requested_kind,
|
|
47
|
+
confirm_by_email: true,
|
|
48
|
+
redirect_url: validated_redirect_url
|
|
31
49
|
)
|
|
32
50
|
|
|
33
51
|
return render_result_error(result) unless result.success?
|
|
@@ -6,6 +6,8 @@ module Spree
|
|
|
6
6
|
module Store
|
|
7
7
|
module Customer
|
|
8
8
|
class PasswordResetsController < Store::BaseController
|
|
9
|
+
include Spree::Api::V3::AllowedRedirectUrl
|
|
10
|
+
|
|
9
11
|
allow_guest_storefront_access!
|
|
10
12
|
rate_limit to: Spree::Api::Config[:rate_limit_password_reset],
|
|
11
13
|
within: Spree::Api::Config[:rate_limit_window].seconds,
|
|
@@ -33,26 +35,13 @@ module Spree
|
|
|
33
35
|
|
|
34
36
|
# POST /api/v3/store/password_resets
|
|
35
37
|
def create
|
|
36
|
-
redirect_url = params[:redirect_url]
|
|
37
|
-
|
|
38
|
-
# Validate redirect_url against allowed origins (secure by default).
|
|
39
|
-
# If no allowed origins are configured, redirect_url is silently ignored
|
|
40
|
-
# to prevent open redirect / token exfiltration attacks.
|
|
41
|
-
if redirect_url.present?
|
|
42
|
-
if current_store.allowed_origins.exists? && current_store.allowed_origin?(redirect_url)
|
|
43
|
-
# redirect_url is valid — keep it
|
|
44
|
-
else
|
|
45
|
-
redirect_url = nil
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
38
|
user = Spree.customer_class.find_by(email: params[:email])
|
|
50
39
|
|
|
51
40
|
if user
|
|
52
41
|
event_name = 'customer.password_reset_requested'
|
|
53
42
|
payload = user.event_payload_for(
|
|
54
43
|
event_name,
|
|
55
|
-
reset_token: user.generate_token_for(:password_reset), store: current_store, redirect_url:
|
|
44
|
+
reset_token: user.generate_token_for(:password_reset), store: current_store, redirect_url: validated_redirect_url
|
|
56
45
|
)
|
|
57
46
|
user.publish_event(event_name, payload)
|
|
58
47
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Store
|
|
5
|
+
# Confirms a request a customer raised, from the page the emailed link
|
|
6
|
+
# opens. The page calls this when the person presses its button, never
|
|
7
|
+
# on load: some mail scanners open links and run the page's scripts.
|
|
8
|
+
class DataRequestConfirmationsController < DataRequestsController
|
|
9
|
+
# POST /api/v3/store/data_requests/:token/confirmation
|
|
10
|
+
def create
|
|
11
|
+
return render_confirmation_invalid(status: :unprocessable_content) unless data_request
|
|
12
|
+
|
|
13
|
+
result = Spree::DataRequests::Confirm.call(data_request: data_request)
|
|
14
|
+
|
|
15
|
+
return render_confirmation_invalid(status: :unprocessable_content) unless result.success?
|
|
16
|
+
|
|
17
|
+
render json: serialize_resource(result.value)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Store
|
|
5
|
+
# A data request as seen by whoever holds its confirmation token: the
|
|
6
|
+
# person reading mail at the address the request names.
|
|
7
|
+
#
|
|
8
|
+
# Token-addressed rather than session-authenticated, because the link
|
|
9
|
+
# arrives by email and may be opened on a device that was never signed
|
|
10
|
+
# in. Besides the export email, it is the only place the export link is
|
|
11
|
+
# shown, since the token is what reached the address the request names.
|
|
12
|
+
class DataRequestsController < Store::BaseController
|
|
13
|
+
allow_guest_storefront_access!
|
|
14
|
+
|
|
15
|
+
# The response can carry a live link to a person's whole export, the
|
|
16
|
+
# same credential the download itself keeps out of shared caches.
|
|
17
|
+
before_action { response.headers['Cache-Control'] = 'no-store' }
|
|
18
|
+
|
|
19
|
+
# GET /api/v3/store/data_requests/:token
|
|
20
|
+
def show
|
|
21
|
+
return render_confirmation_invalid(status: :not_found) unless data_request
|
|
22
|
+
|
|
23
|
+
render json: serialize_resource(data_request)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
protected
|
|
27
|
+
|
|
28
|
+
def serializer_class
|
|
29
|
+
Spree.api.data_request_serializer
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def serializer_params
|
|
33
|
+
super.merge(download_url_visible: true)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def data_request
|
|
39
|
+
return @data_request if defined?(@data_request)
|
|
40
|
+
|
|
41
|
+
token = params[:token].presence
|
|
42
|
+
@data_request = token && Spree::DataRequest.for_store(current_store).find_by_token_for(:confirmation, token)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def render_confirmation_invalid(status:)
|
|
46
|
+
render_error(
|
|
47
|
+
code: ERROR_CODES[:invalid_token],
|
|
48
|
+
message: I18n.t('spree.data_request_errors.confirmation_invalid'),
|
|
49
|
+
status: status
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -3,6 +3,8 @@ module Spree
|
|
|
3
3
|
module V3
|
|
4
4
|
module Store
|
|
5
5
|
class NewsletterSubscribersController < Store::BaseController
|
|
6
|
+
include Spree::Api::V3::AllowedRedirectUrl
|
|
7
|
+
|
|
6
8
|
allow_guest_storefront_access!
|
|
7
9
|
rate_limit to: Spree::Api::Config[:rate_limit_register],
|
|
8
10
|
within: Spree::Api::Config[:rate_limit_window].seconds,
|
|
@@ -139,17 +141,6 @@ module Spree
|
|
|
139
141
|
subscriber = Spree::NewsletterSubscriber.for_store(current_store).find_by_token_for(:unsubscribe, params[:token])
|
|
140
142
|
subscriber if subscriber&.prefixed_id == params[:id]
|
|
141
143
|
end
|
|
142
|
-
|
|
143
|
-
# Drop redirect_url when it isn't in the store's allow-list — secure-by-default,
|
|
144
|
-
# mirrors password_resets. Returning nil omits it from the webhook payload rather
|
|
145
|
-
# than rejecting the request, so callers can't probe the allow-list via 4xx errors.
|
|
146
|
-
def validated_redirect_url
|
|
147
|
-
redirect_url = params[:redirect_url]
|
|
148
|
-
return nil if redirect_url.blank?
|
|
149
|
-
return nil unless current_store.allowed_origin?(redirect_url)
|
|
150
|
-
|
|
151
|
-
redirect_url
|
|
152
|
-
end
|
|
153
144
|
end
|
|
154
145
|
end
|
|
155
146
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Admin
|
|
5
|
+
# The shared variant attributes, plus the marketplace configuration
|
|
6
|
+
# only an operator sets.
|
|
7
|
+
class VariantRequestSchema < Spree::Api::V3::VariantRequestSchema
|
|
8
|
+
publishes 'VariantParams'
|
|
9
|
+
|
|
10
|
+
attribute :seller_id, :id
|
|
11
|
+
attribute :delivery_profile_id, :id, nullable: true
|
|
12
|
+
attribute :tax_category_id, :id
|
|
13
|
+
attribute :prices, :array, of: Spree::Api::V3::PriceRequestSchema,
|
|
14
|
+
description: 'Per-currency prices. Upserted by currency.'
|
|
15
|
+
attribute :stock_levels, :array, of: Spree::Api::V3::StockLevelRequestSchema,
|
|
16
|
+
description: 'Stock per location. Upserted by location; omitted locations are removed.'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Seller
|
|
5
|
+
# The operator's price shape, except `currency` may be left out: the
|
|
6
|
+
# store's currency is assumed (see `default_price_currencies`).
|
|
7
|
+
class PriceRequestSchema < Spree::Api::V3::RequestSchema
|
|
8
|
+
attribute :currency, :currency,
|
|
9
|
+
example: 'USD',
|
|
10
|
+
description: "Omit to price in the store's currency. A seller has no currency of their own."
|
|
11
|
+
attribute :amount, :decimal,
|
|
12
|
+
required: true,
|
|
13
|
+
example: '24.00',
|
|
14
|
+
description: 'A canonical decimal string (`"1234.56"`: period decimal, no grouping). The API does not parse localized input.'
|
|
15
|
+
attribute :compare_at_amount, :decimal, nullable: true, example: '30.00'
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Seller
|
|
5
|
+
# The shared variant attributes. Narrower than the operator's set:
|
|
6
|
+
# `seller_id` would let a payload hand a variant to somebody else, and
|
|
7
|
+
# `tax_category_id`/`delivery_profile_id` are marketplace configuration.
|
|
8
|
+
class VariantRequestSchema < Spree::Api::V3::VariantRequestSchema
|
|
9
|
+
publishes 'VariantParams'
|
|
10
|
+
|
|
11
|
+
attribute :prices, :array, of: Spree::Api::V3::Seller::PriceRequestSchema,
|
|
12
|
+
description: 'Per-currency prices. Upserted by currency.'
|
|
13
|
+
attribute :stock_levels, :array, of: Spree::Api::V3::StockLevelRequestSchema,
|
|
14
|
+
description: "Stock per location, at this seller's own locations only: a location belonging to anyone else is ignored."
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
module Api
|
|
3
3
|
module V3
|
|
4
|
-
# A variant's own attributes,
|
|
5
|
-
#
|
|
6
|
-
#
|
|
4
|
+
# A variant's own attributes, the ones a seller may write too. Each API
|
|
5
|
+
# publishes its own `VariantParams` from a subclass, which the variant
|
|
6
|
+
# endpoints and the variants nested in a product payload both extend.
|
|
7
7
|
class VariantRequestSchema < Spree::Api::V3::RequestSchema
|
|
8
8
|
attribute :sku, :string
|
|
9
9
|
attribute :barcode, :string
|
|
@@ -13,14 +13,14 @@ module Spree
|
|
|
13
13
|
attribute :height, :number, nullable: true
|
|
14
14
|
attribute :width, :number, nullable: true
|
|
15
15
|
attribute :depth, :number, nullable: true
|
|
16
|
-
attribute :weight_unit, :
|
|
17
|
-
attribute :dimensions_unit, :
|
|
16
|
+
attribute :weight_unit, :weight_unit, nullable: true, description: "Null falls back to the store's weight unit."
|
|
17
|
+
attribute :dimensions_unit, :dimension_unit, nullable: true, description: "Null falls back to the store's unit system."
|
|
18
18
|
attribute :hs_code, :string
|
|
19
19
|
attribute :customs_description, :string
|
|
20
20
|
attribute :country_of_origin, :country
|
|
21
21
|
attribute :minimum_order_quantity, :integer, nullable: true
|
|
22
22
|
attribute :order_multiple, :integer, nullable: true
|
|
23
|
-
attribute :purchase_unit, :string
|
|
23
|
+
attribute :purchase_unit, :string, nullable: true, choices: Spree::Variant::PURCHASE_UNITS
|
|
24
24
|
attribute :units_per_carton, :integer, nullable: true, description: 'How many units fill one carton.'
|
|
25
25
|
attribute :carton_package_type_id, :id,
|
|
26
26
|
nullable: true,
|
|
@@ -29,8 +29,6 @@ module Spree
|
|
|
29
29
|
nullable: true,
|
|
30
30
|
description: "What one packed carton weighs, in the variant's weight unit."
|
|
31
31
|
attribute :cartons_per_pallet, :integer, nullable: true, description: 'How many cartons stack on one pallet.'
|
|
32
|
-
attribute :seller_id, :id
|
|
33
|
-
attribute :delivery_profile_id, :id, nullable: true
|
|
34
32
|
attribute :track_inventory, :boolean
|
|
35
33
|
attribute :preorderable, :boolean,
|
|
36
34
|
description: "Sell this variant before it's in stock. Adds the pre-order label and ships-by promise; the cap comes from `backorder_limit`, an incoming stock count, or backorder."
|
|
@@ -40,12 +38,9 @@ module Spree
|
|
|
40
38
|
attribute :backorder_limit, :integer,
|
|
41
39
|
nullable: true,
|
|
42
40
|
description: 'Maximum units sellable beyond available stock, capping backorders and pre-orders. Null is unlimited.'
|
|
43
|
-
attribute :tax_category_id, :id
|
|
44
41
|
attribute :position, :integer
|
|
45
|
-
attribute :
|
|
46
|
-
description: '
|
|
47
|
-
attribute :stock_levels, :array, of: Spree::Api::V3::StockLevelRequestSchema,
|
|
48
|
-
description: 'Stock per location. Upserted by location; omitted locations are removed.'
|
|
42
|
+
attribute :options, :array, of: Spree::Api::V3::VariantOptionRequestSchema,
|
|
43
|
+
description: 'One pair per option type the variant takes part in (size, color). Option types and values are created when missing.'
|
|
49
44
|
end
|
|
50
45
|
end
|
|
51
46
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
# Which address book an entry came out of. A checkout offering the
|
|
5
|
+
# buyer's own addresses beside their company's sites has to label the
|
|
6
|
+
# two, and the owner is the only thing that tells a warehouse from
|
|
7
|
+
# somebody's flat.
|
|
8
|
+
class AddressOwnerSerializer < BaseSerializer
|
|
9
|
+
typelize type: [:string, enum: %w[customer company seller], enum_type_name: 'AddressOwnerType'],
|
|
10
|
+
name: [:string, nullable: true]
|
|
11
|
+
|
|
12
|
+
attribute :type do |owner|
|
|
13
|
+
Spree::Base.polymorphic_api_type(owner.class.to_s)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# What to call the book. A company node answers with its own name, so
|
|
17
|
+
# a division's own sites and the headquarters sites it inherits are
|
|
18
|
+
# told apart rather than filed under one heading. A customer's book is
|
|
19
|
+
# the caller's own and needs no name — "your addresses" is the client's
|
|
20
|
+
# word for it, in the shopper's language.
|
|
21
|
+
attribute :name do |owner|
|
|
22
|
+
owner.name if owner.is_a?(Spree::Company)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -12,7 +12,7 @@ module Spree
|
|
|
12
12
|
country_iso: [:string, deprecated: 'Use `country_code`. Removed in 6.1.'],
|
|
13
13
|
country_name: :string,
|
|
14
14
|
quick_checkout: :boolean, is_default_billing: :boolean, is_default_shipping: :boolean,
|
|
15
|
-
label: [:string, nullable: true]
|
|
15
|
+
label: [:string, nullable: true], owner: { nullable: true }
|
|
16
16
|
|
|
17
17
|
# What the address is filed under in whichever book owns it — a
|
|
18
18
|
# customer's ("Home") or a company node's ("Northern Warehouse").
|
|
@@ -31,6 +31,10 @@ module Spree
|
|
|
31
31
|
attribute :state_name do |address|
|
|
32
32
|
address.state_name.presence || address.state&.name
|
|
33
33
|
end
|
|
34
|
+
|
|
35
|
+
# The address book the entry belongs to. Null for an address in no
|
|
36
|
+
# book: one a guest typed in, or the copy a placed order keeps.
|
|
37
|
+
one :owner, resource: proc { Spree.api.address_owner_serializer }
|
|
34
38
|
end
|
|
35
39
|
end
|
|
36
40
|
end
|
|
@@ -7,8 +7,6 @@ module Spree
|
|
|
7
7
|
%i[state_abbr country_iso].each { |key| _attributes.delete(key) }
|
|
8
8
|
|
|
9
9
|
typelize customer_id: [:string, nullable: true],
|
|
10
|
-
owner_id: [:string, nullable: true],
|
|
11
|
-
owner_type: [:string, nullable: true],
|
|
12
10
|
latitude: [:number, nullable: true],
|
|
13
11
|
longitude: [:number, nullable: true],
|
|
14
12
|
metadata: 'Record<string, unknown>'
|
|
@@ -29,14 +27,7 @@ module Spree
|
|
|
29
27
|
|
|
30
28
|
prefixed_id_attributes customer_id: :customer_owner
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
# seller's billing address. `customer_id` above stays as the
|
|
34
|
-
# customer-only shorthand the customers surface reads.
|
|
35
|
-
prefixed_id_attributes :owner
|
|
36
|
-
|
|
37
|
-
attribute :owner_type do |address|
|
|
38
|
-
Spree::Base.polymorphic_api_type(address.owner_type)
|
|
39
|
-
end
|
|
30
|
+
one :owner, resource: proc { Spree.api.admin_address_owner_serializer }
|
|
40
31
|
end
|
|
41
32
|
end
|
|
42
33
|
end
|
|
@@ -12,7 +12,7 @@ module Spree
|
|
|
12
12
|
# via {#plaintext_token} below — it is `nil` everywhere else.
|
|
13
13
|
class ApiKeySerializer < V3::BaseSerializer
|
|
14
14
|
typelize name: :string,
|
|
15
|
-
key_type: [:string, enum: Spree::ApiKey::KEY_TYPES],
|
|
15
|
+
key_type: [:string, enum: Spree::ApiKey::KEY_TYPES, enum_type_name: 'ApiKeyType'],
|
|
16
16
|
token_prefix: [:string, nullable: true],
|
|
17
17
|
plaintext_token: [:string, nullable: true],
|
|
18
18
|
scopes: [:string, multi: true],
|
|
@@ -3,7 +3,7 @@ module Spree
|
|
|
3
3
|
module V3
|
|
4
4
|
module Admin
|
|
5
5
|
class CatalogAssignmentSerializer < V3::BaseSerializer
|
|
6
|
-
typelize catalog_id: :string, assignable_type: :string, assignable_id: :string,
|
|
6
|
+
typelize catalog_id: :string, assignable_type: [:string, enum: Spree::CatalogAssignment::ASSIGNABLE_TYPES.map { |type| Spree::Base.polymorphic_api_type(type) }, enum_type_name: 'CatalogAssignableType'], assignable_id: :string,
|
|
7
7
|
assignable_name: [:string, nullable: true]
|
|
8
8
|
|
|
9
9
|
attributes created_at: :iso8601
|
|
@@ -17,7 +17,7 @@ module Spree
|
|
|
17
17
|
_attributes.delete(:id)
|
|
18
18
|
|
|
19
19
|
typelize id: 'string | null', label: 'string | null', sku: 'string | null',
|
|
20
|
-
currency: [:string, enum: Spree::Money::Rounding::CURRENCY_CODES, enum_type_name: 'CurrencyCode'], source: [:string, enum: Spree::CatalogPrice::SOURCES], break_count: :number
|
|
20
|
+
currency: [:string, enum: Spree::Money::Rounding::CURRENCY_CODES, enum_type_name: 'CurrencyCode'], source: [:string, enum: Spree::CatalogPrice::SOURCES, enum_type_name: 'CatalogPriceSource'], break_count: :number
|
|
21
21
|
|
|
22
22
|
# The variant this prices, so a row can name what it is showing —
|
|
23
23
|
# a product's variants can be priced differently, and one figure
|
|
@@ -8,7 +8,7 @@ module Spree
|
|
|
8
8
|
# wire shorthand (e.g. "tag"), matching every other typed-rule
|
|
9
9
|
# serializer and what the writer accepts.
|
|
10
10
|
class CollectionRuleSerializer < BaseSerializer
|
|
11
|
-
typelize type: [:string, comment: 'Rule type. Built-in: tag, available_on, sale. Extensions may register more.'], value: [:string, nullable: true], match_policy: [:string, enum: Spree::CollectionRule::MATCH_POLICIES]
|
|
11
|
+
typelize type: [:string, comment: 'Rule type. Built-in: tag, available_on, sale. Extensions may register more.'], value: [:string, nullable: true], match_policy: [:string, enum: Spree::CollectionRule::MATCH_POLICIES, enum_type_name: 'CollectionRuleMatchPolicy']
|
|
12
12
|
|
|
13
13
|
attributes :value, :match_policy
|
|
14
14
|
|