spree_api 5.5.3 → 5.6.0.rc1

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.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/spree/api/v3/admin/role_grant_guard.rb +3 -1
  3. data/app/controllers/concerns/spree/api/v3/admin_authentication.rb +1 -1
  4. data/app/controllers/concerns/spree/api/v3/channel_resolution.rb +4 -2
  5. data/app/controllers/concerns/spree/api/v3/locale_and_currency.rb +4 -1
  6. data/app/controllers/concerns/spree/api/v3/storefront_gating.rb +65 -0
  7. data/app/controllers/spree/api/v3/admin/admin_users_controller.rb +1 -1
  8. data/app/controllers/spree/api/v3/admin/categories/products_controller.rb +98 -0
  9. data/app/controllers/spree/api/v3/admin/categories_controller.rb +102 -2
  10. data/app/controllers/spree/api/v3/admin/customers_controller.rb +8 -2
  11. data/app/controllers/spree/api/v3/admin/exports_controller.rb +3 -1
  12. data/app/controllers/spree/api/v3/admin/import_rows_controller.rb +49 -0
  13. data/app/controllers/spree/api/v3/admin/imports_controller.rb +274 -0
  14. data/app/controllers/spree/api/v3/admin/locales_controller.rb +29 -0
  15. data/app/controllers/spree/api/v3/admin/me_controller.rb +39 -9
  16. data/app/controllers/spree/api/v3/admin/orders/fulfillments_controller.rb +62 -2
  17. data/app/controllers/spree/api/v3/admin/password_resets_controller.rb +105 -0
  18. data/app/controllers/spree/api/v3/admin/payment_methods_controller.rb +1 -8
  19. data/app/controllers/spree/api/v3/admin/products/variants_controller.rb +1 -1
  20. data/app/controllers/spree/api/v3/admin/products_controller.rb +8 -7
  21. data/app/controllers/spree/api/v3/admin/promotions_controller.rb +1 -1
  22. data/app/controllers/spree/api/v3/admin/resource_controller.rb +31 -0
  23. data/app/controllers/spree/api/v3/admin/store_controller.rb +3 -0
  24. data/app/controllers/spree/api/v3/admin/translatable_resources_controller.rb +38 -0
  25. data/app/controllers/spree/api/v3/admin/translations/batches_controller.rb +113 -0
  26. data/app/controllers/spree/api/v3/admin/translations_controller.rb +106 -0
  27. data/app/controllers/spree/api/v3/resource_controller.rb +0 -2
  28. data/app/controllers/spree/api/v3/store/auth_controller.rb +3 -1
  29. data/app/controllers/spree/api/v3/store/base_controller.rb +5 -0
  30. data/app/controllers/spree/api/v3/store/carts/fulfillments_controller.rb +1 -3
  31. data/app/controllers/spree/api/v3/store/carts_controller.rb +4 -0
  32. data/app/controllers/spree/api/v3/store/countries_controller.rb +1 -0
  33. data/app/controllers/spree/api/v3/store/currencies_controller.rb +1 -0
  34. data/app/controllers/spree/api/v3/store/customer/password_resets_controller.rb +5 -1
  35. data/app/controllers/spree/api/v3/store/customers_controller.rb +3 -1
  36. data/app/controllers/spree/api/v3/store/digitals_controller.rb +1 -0
  37. data/app/controllers/spree/api/v3/store/locales_controller.rb +3 -8
  38. data/app/controllers/spree/api/v3/store/markets/countries_controller.rb +1 -0
  39. data/app/controllers/spree/api/v3/store/markets_controller.rb +1 -0
  40. data/app/controllers/spree/api/v3/store/newsletter_subscribers_controller.rb +1 -0
  41. data/app/controllers/spree/api/v3/store/products/filters_controller.rb +5 -2
  42. data/app/controllers/spree/api/v3/store/products_controller.rb +1 -1
  43. data/app/controllers/spree/api/v3/store/resource_controller.rb +1 -0
  44. data/app/serializers/concerns/spree/api/v3/admin/translatable.rb +38 -0
  45. data/app/serializers/spree/api/v3/admin/admin_user_serializer.rb +7 -1
  46. data/app/serializers/spree/api/v3/admin/category_serializer.rb +5 -3
  47. data/app/serializers/spree/api/v3/admin/channel_serializer.rb +5 -1
  48. data/app/serializers/spree/api/v3/admin/customer_serializer.rb +7 -2
  49. data/app/serializers/spree/api/v3/admin/discount_serializer.rb +4 -0
  50. data/app/serializers/spree/api/v3/admin/fulfillment_serializer.rb +9 -0
  51. data/app/serializers/spree/api/v3/admin/gift_card_serializer.rb +7 -0
  52. data/app/serializers/spree/api/v3/admin/import_mapping_serializer.rb +20 -0
  53. data/app/serializers/spree/api/v3/admin/import_row_serializer.rb +17 -0
  54. data/app/serializers/spree/api/v3/admin/import_serializer.rb +70 -0
  55. data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +11 -0
  56. data/app/serializers/spree/api/v3/admin/option_type_serializer.rb +2 -0
  57. data/app/serializers/spree/api/v3/admin/order_serializer.rb +15 -1
  58. data/app/serializers/spree/api/v3/admin/payment_serializer.rb +4 -0
  59. data/app/serializers/spree/api/v3/admin/product_serializer.rb +1 -0
  60. data/app/serializers/spree/api/v3/admin/promotion_rule_serializer.rb +8 -0
  61. data/app/serializers/spree/api/v3/admin/resource_translations_serializer.rb +76 -0
  62. data/app/serializers/spree/api/v3/admin/setup_task_serializer.rb +18 -0
  63. data/app/serializers/spree/api/v3/admin/store_serializer.rb +27 -1
  64. data/app/serializers/spree/api/v3/admin/variant_serializer.rb +8 -1
  65. data/app/serializers/spree/api/v3/base_serializer.rb +13 -0
  66. data/app/serializers/spree/api/v3/cart_serializer.rb +25 -22
  67. data/app/serializers/spree/api/v3/discount_serializer.rb +6 -2
  68. data/app/serializers/spree/api/v3/fulfillment_serializer.rb +16 -13
  69. data/app/serializers/spree/api/v3/gift_card_serializer.rb +15 -11
  70. data/app/serializers/spree/api/v3/import_row_serializer.rb +1 -1
  71. data/app/serializers/spree/api/v3/line_item_serializer.rb +35 -17
  72. data/app/serializers/spree/api/v3/locale_serializer.rb +6 -1
  73. data/app/serializers/spree/api/v3/order_serializer.rb +25 -21
  74. data/app/serializers/spree/api/v3/payment_serializer.rb +6 -2
  75. data/app/serializers/spree/api/v3/product_serializer.rb +9 -1
  76. data/app/serializers/spree/api/v3/variant_serializer.rb +12 -1
  77. data/app/services/spree/api/v3/filters_aggregator.rb +4 -4
  78. data/app/subscribers/spree/webhook_event_subscriber.rb +6 -0
  79. data/config/initializers/typelizer.rb +10 -0
  80. data/config/locales/en.yml +4 -0
  81. data/config/routes.rb +54 -4
  82. data/lib/spree/api/dependencies.rb +5 -0
  83. data/lib/spree/api/openapi/schema_helper.rb +29 -0
  84. metadata +21 -6
@@ -4,32 +4,50 @@ module Spree
4
4
  class LineItemSerializer < BaseSerializer
5
5
  typelize variant_id: :string, quantity: :number, currency: :string,
6
6
  name: :string, slug: :string, options_text: :string,
7
- price: :string, display_price: :string, total: :string, display_total: :string,
8
- adjustment_total: :string, display_adjustment_total: :string,
9
- additional_tax_total: :string, display_additional_tax_total: :string,
10
- included_tax_total: :string, display_included_tax_total: :string,
11
- discount_total: :string, display_discount_total: :string,
12
- pre_tax_amount: :string, display_pre_tax_amount: :string,
13
- discounted_amount: :string, display_discounted_amount: :string,
7
+ price: [:string, nullable: true], display_price: [:string, nullable: true],
8
+ total: [:string, nullable: true], display_total: [:string, nullable: true],
9
+ adjustment_total: [:string, nullable: true], display_adjustment_total: [:string, nullable: true],
10
+ additional_tax_total: [:string, nullable: true], display_additional_tax_total: [:string, nullable: true],
11
+ included_tax_total: [:string, nullable: true], display_included_tax_total: [:string, nullable: true],
12
+ discount_total: [:string, nullable: true], display_discount_total: [:string, nullable: true],
13
+ pre_tax_amount: [:string, nullable: true], display_pre_tax_amount: [:string, nullable: true],
14
+ discounted_amount: [:string, nullable: true], display_discounted_amount: [:string, nullable: true],
14
15
  compare_at_amount: [:string, nullable: true], display_compare_at_amount: [:string, nullable: true],
15
- thumbnail_url: [:string, nullable: true]
16
+ thumbnail_url: [:string, nullable: true],
17
+ preorder: :boolean, preorder_ships_at: [:string, nullable: true]
16
18
 
17
19
  attribute :variant_id do |line_item|
18
20
  line_item.variant&.prefixed_id
19
21
  end
20
22
 
21
- attributes :quantity, :currency, :name, :slug, :options_text,
22
- :price, :display_price, :total, :display_total,
23
- :adjustment_total, :display_adjustment_total,
24
- :additional_tax_total, :display_additional_tax_total,
25
- :included_tax_total, :display_included_tax_total,
26
- :discount_total, :display_discount_total,
27
- :pre_tax_amount, :display_pre_tax_amount,
28
- :discounted_amount, :display_discounted_amount,
29
- :display_compare_at_amount
23
+ # True when the line item's variant is currently a pre-order, so the
24
+ # cart/checkout can flag it as shipping later.
25
+ attribute :preorder do |line_item|
26
+ line_item.variant&.preorder? || false
27
+ end
28
+
29
+ # The variant's "ships by" promise while it's a pre-order.
30
+ attribute :preorder_ships_at do |line_item|
31
+ line_item.variant&.preorder_ships_at&.iso8601 if line_item.variant&.preorder?
32
+ end
33
+
34
+ attributes :quantity, :currency, :name, :slug, :options_text
35
+
36
+ # Nulled for gated (prices_hidden) guests so the cart's line items can't
37
+ # leak the prices that product/variant serializers already withhold.
38
+ money_attributes :price, :display_price, :total, :display_total,
39
+ :adjustment_total, :display_adjustment_total,
40
+ :additional_tax_total, :display_additional_tax_total,
41
+ :included_tax_total, :display_included_tax_total,
42
+ :discount_total, :display_discount_total,
43
+ :pre_tax_amount, :display_pre_tax_amount,
44
+ :discounted_amount, :display_discounted_amount,
45
+ :display_compare_at_amount
30
46
 
31
47
  # Return compare_at_amount as string, nil if zero
32
48
  attribute :compare_at_amount do |line_item|
49
+ next nil if params[:hide_prices]
50
+
33
51
  amount = line_item.compare_at_amount
34
52
  amount.present? && amount.positive? ? amount.to_s : nil
35
53
  end
@@ -1,13 +1,18 @@
1
1
  module Spree
2
2
  module Api
3
3
  module V3
4
+ # Serializes a Spree::Locale. The display name, default flag, and RTL
5
+ # direction all live on the model — this is a thin projection of it.
4
6
  class LocaleSerializer
5
7
  include Alba::Resource
6
8
  include Typelizer::DSL
7
9
 
8
- typelize code: :string, name: :string
10
+ typelize code: :string, name: :string, default: :boolean, rtl: :boolean
9
11
 
10
12
  attributes :code, :name
13
+
14
+ attribute(:default, &:default?)
15
+ attribute(:rtl, &:rtl?)
11
16
  end
12
17
  end
13
18
  end
@@ -9,18 +9,18 @@ module Spree
9
9
  market_id: [:string, nullable: true], channel_id: [:string, nullable: true],
10
10
  currency: :string, locale: [:string, nullable: true], total_quantity: :number,
11
11
  fulfillment_status: [:string, nullable: true], payment_status: [:string, nullable: true],
12
- item_total: :string, display_item_total: :string,
13
- delivery_total: :string, display_delivery_total: :string,
14
- adjustment_total: :string, display_adjustment_total: :string,
15
- discount_total: :string, display_discount_total: :string,
16
- tax_total: :string, display_tax_total: :string,
17
- included_tax_total: :string, display_included_tax_total: :string,
18
- additional_tax_total: :string, display_additional_tax_total: :string,
19
- store_credit_total: :string, display_store_credit_total: :string,
20
- gift_card_total: :string, display_gift_card_total: :string,
12
+ item_total: [:string, nullable: true], display_item_total: [:string, nullable: true],
13
+ delivery_total: [:string, nullable: true], display_delivery_total: [:string, nullable: true],
14
+ adjustment_total: [:string, nullable: true], display_adjustment_total: [:string, nullable: true],
15
+ discount_total: [:string, nullable: true], display_discount_total: [:string, nullable: true],
16
+ tax_total: [:string, nullable: true], display_tax_total: [:string, nullable: true],
17
+ included_tax_total: [:string, nullable: true], display_included_tax_total: [:string, nullable: true],
18
+ additional_tax_total: [:string, nullable: true], display_additional_tax_total: [:string, nullable: true],
19
+ store_credit_total: [:string, nullable: true], display_store_credit_total: [:string, nullable: true],
20
+ gift_card_total: [:string, nullable: true], display_gift_card_total: [:string, nullable: true],
21
21
  covered_by_store_credit: :boolean,
22
- total: :string, display_total: :string,
23
- amount_due: :string, display_amount_due: :string,
22
+ total: [:string, nullable: true], display_total: [:string, nullable: true],
23
+ amount_due: [:string, nullable: true], display_amount_due: [:string, nullable: true],
24
24
  completed_at: [:string, nullable: true],
25
25
  billing_address: { nullable: true }, shipping_address: { nullable: true },
26
26
  gift_card: { nullable: true }, market: { nullable: true }
@@ -35,22 +35,26 @@ module Spree
35
35
 
36
36
  attributes :number, :email, :customer_note,
37
37
  :currency, :locale, :total_quantity,
38
- :item_total, :display_item_total,
39
- :adjustment_total, :display_adjustment_total,
40
- :discount_total, :display_discount_total,
41
- :tax_total, :display_tax_total, :included_tax_total, :display_included_tax_total,
42
- :additional_tax_total, :display_additional_tax_total, :total, :display_total,
43
- :gift_card_total, :display_gift_card_total,
44
- :amount_due, :display_amount_due,
45
- :delivery_total, :display_delivery_total, :fulfillment_status, :payment_status,
38
+ :fulfillment_status, :payment_status,
46
39
  completed_at: :iso8601
47
40
 
41
+ # Nulled for gated (prices_hidden) guests, consistent with cart and
42
+ # catalog price hiding.
43
+ money_attributes :item_total, :display_item_total,
44
+ :adjustment_total, :display_adjustment_total,
45
+ :discount_total, :display_discount_total,
46
+ :tax_total, :display_tax_total, :included_tax_total, :display_included_tax_total,
47
+ :additional_tax_total, :display_additional_tax_total, :total, :display_total,
48
+ :gift_card_total, :display_gift_card_total,
49
+ :amount_due, :display_amount_due,
50
+ :delivery_total, :display_delivery_total
51
+
48
52
  attribute :store_credit_total do |order|
49
- order.total_applied_store_credit.to_s
53
+ order.total_applied_store_credit.to_s unless params[:hide_prices]
50
54
  end
51
55
 
52
56
  attribute :display_store_credit_total do |order|
53
- order.display_total_applied_store_credit.to_s
57
+ order.display_total_applied_store_credit.to_s unless params[:hide_prices]
54
58
  end
55
59
 
56
60
  attribute :covered_by_store_credit do |order|
@@ -3,7 +3,7 @@ module Spree
3
3
  module V3
4
4
  class PaymentSerializer < BaseSerializer
5
5
  typelize status: :string, payment_method_id: :string, response_code: [:string, nullable: true],
6
- number: :string, amount: :string, display_amount: :string,
6
+ number: :string, amount: [:string, nullable: true], display_amount: [:string, nullable: true],
7
7
  source_type: [:string, nullable: true, enum: %w[credit_card store_credit payment_source]],
8
8
  source_id: [:string, nullable: true],
9
9
  source: 'CreditCard | StoreCredit | PaymentSource | null'
@@ -12,7 +12,11 @@ module Spree
12
12
  payment.payment_method&.prefixed_id
13
13
  end
14
14
 
15
- attributes :response_code, :number, :amount, :display_amount
15
+ attributes :response_code, :number
16
+
17
+ # Nulled for gated (prices_hidden) guests so a payment can't leak the
18
+ # amount the cart/order totals already withhold.
19
+ money_attributes :amount, :display_amount
16
20
 
17
21
  attribute :status do |payment|
18
22
  payment.state
@@ -11,7 +11,9 @@ module Spree
11
11
  default_variant_id: :string,
12
12
  thumbnail_url: [:string, nullable: true],
13
13
  available_on: [:string, nullable: true],
14
+ preorder_ships_at: [:string, nullable: true],
14
15
  purchasable: :boolean, in_stock: :boolean, backorderable: :boolean, available: :boolean,
16
+ preorder: :boolean,
15
17
  price: 'Price',
16
18
  original_price: ['Price', nullable: true],
17
19
  tags: [:string, multi: true]
@@ -19,12 +21,18 @@ module Spree
19
21
  attributes :name, :slug,
20
22
  :meta_title, :meta_description, :meta_keywords,
21
23
  :variant_count,
22
- available_on: :iso8601
24
+ available_on: :iso8601, preorder_ships_at: :iso8601
23
25
 
24
26
  attribute :purchasable do |product|
25
27
  product.purchasable?
26
28
  end
27
29
 
30
+ # True when this product is currently offered as a pre-order on the
31
+ # requesting channel.
32
+ attribute :preorder do |product|
33
+ product.preorder?
34
+ end
35
+
28
36
  attribute :in_stock do |product|
29
37
  product.in_stock?
30
38
  end
@@ -7,7 +7,8 @@ module Spree
7
7
  typelize product_id: :string, sku: [:string, nullable: true],
8
8
  options_text: :string, track_inventory: :boolean, media_count: :number,
9
9
  thumbnail_url: [:string, nullable: true],
10
- purchasable: :boolean, in_stock: :boolean, backorderable: :boolean,
10
+ purchasable: :boolean, in_stock: :boolean, backorderable: :boolean, preorder: :boolean,
11
+ preorder_ships_at: [:string, nullable: true],
11
12
  weight: [:number, nullable: true], height: [:number, nullable: true], width: [:number, nullable: true], depth: [:number, nullable: true],
12
13
  price: 'Price',
13
14
  original_price: ['Price', nullable: true]
@@ -18,6 +19,12 @@ module Spree
18
19
 
19
20
  attributes :sku, :options_text, :track_inventory, :media_count
20
21
 
22
+ # Customer-facing "ships by" promise — only while the variant is a
23
+ # pre-order (nil otherwise, even if a stale date lingers on the column).
24
+ attribute :preorder_ships_at do |variant|
25
+ variant.preorder_ships_at&.iso8601 if variant.preorder?
26
+ end
27
+
21
28
  # Main variant image URL for listings (cached primary_media)
22
29
  attribute :thumbnail_url do |variant|
23
30
  image_url_for(variant.primary_media)
@@ -35,6 +42,10 @@ module Spree
35
42
  variant.backorderable?
36
43
  end
37
44
 
45
+ attribute :preorder do |variant|
46
+ variant.preorder?
47
+ end
48
+
38
49
  attribute :weight do |variant|
39
50
  variant.weight&.to_f
40
51
  end
@@ -87,9 +87,9 @@ module Spree
87
87
  type_ids = option_types.map(&:id)
88
88
 
89
89
  # Pluck only the columns we need — avoids instantiating thousands of AR models.
90
- # Force default locale so Mobility returns column values (not translations);
90
+ # Force the content locale so Mobility returns column values (not translations);
91
91
  # translated labels are overlaid separately via load_option_value_translations.
92
- ov_rows = Mobility.with_locale(I18n.default_locale) do
92
+ ov_rows = Mobility.with_locale(Spree::Current.content_locale) do
93
93
  Spree::OptionValue
94
94
  .where(option_type_id: type_ids)
95
95
  .order(:position)
@@ -144,10 +144,10 @@ module Spree
144
144
  end
145
145
 
146
146
  # Load translated presentations for option values.
147
- # Returns { option_value_id => translated_presentation } or empty hash when using the default locale.
147
+ # Returns { option_value_id => translated_presentation } or empty hash when using the content locale.
148
148
  def load_option_value_translations(ov_ids)
149
149
  locale = Spree::Current.locale || I18n.locale.to_s
150
- return {} if locale.to_s == I18n.default_locale.to_s
150
+ return {} if locale.to_s == Spree::Current.content_locale
151
151
 
152
152
  Spree::OptionValue::Translation
153
153
  .where(spree_option_value_id: ov_ids, locale: locale)
@@ -17,9 +17,15 @@ module Spree
17
17
  class WebhookEventSubscriber < Spree::Subscriber
18
18
  subscribes_to '*'
19
19
 
20
+ # Admin auth events carry live credentials (password reset tokens) and have
21
+ # no legitimate external consumer — core delivers those emails itself.
22
+ # Never forward them to webhook endpoints, including '*' subscriptions.
23
+ NON_DELIVERABLE_EVENTS = %w[admin_user.password_reset_requested].freeze
24
+
20
25
  def handle(event)
21
26
  return unless Spree::Api::Config.webhooks_enabled
22
27
  return if event.store_id.blank?
28
+ return if NON_DELIVERABLE_EVENTS.include?(event.name)
23
29
 
24
30
  # Only load the columns we need for matching and delivery
25
31
  endpoints = Spree::WebhookEndpoint
@@ -10,6 +10,16 @@ Rails.application.config.after_initialize do
10
10
  config.comments = true
11
11
  config.listen = false
12
12
 
13
+ # Teach typelizer's Alba plugin about custom Alba types (see alba.rb);
14
+ # unmapped typed attributes crash generation.
15
+ config.plugin_configs = {
16
+ alba: {
17
+ ts_mapper: Typelizer::SerializerPlugins::Alba::ALBA_TS_MAPPER.merge(
18
+ 'iso8601' => { type: :string }
19
+ )
20
+ }
21
+ }
22
+
13
23
  # Serializers that exist only for Admin API or events — no Store API controller
14
24
  store_excluded = %w[
15
25
  Asset CartPromotion OrderPromotion
@@ -9,6 +9,9 @@ en:
9
9
  invalid_resource: Invalid resource. Please fix errors and try again.
10
10
  invalid_taxonomy_id: Invalid taxonomy id.
11
11
  must_specify_api_key: You must specify an API key.
12
+ errors:
13
+ guest_checkout_not_allowed: You must be signed in to complete checkout.
14
+ storefront_login_required: Authentication required to access this store.
12
15
  negative_quantity: quantity is negative
13
16
  newsletter_verification_token_invalid: Newsletter verification token is invalid or has expired.
14
17
  order:
@@ -20,6 +23,7 @@ en:
20
23
  payment:
21
24
  credit_over_limit: This payment can only be credited up to %{limit}. Please specify an amount less than or equal to this number.
22
25
  update_forbidden: This payment cannot be updated because it is %{state}.
26
+ invalid_position: new_position must be an integer
23
27
  record_not_found: "%{model} not found"
24
28
  redirect_url_not_allowed: The redirect URL is not from an allowed origin for this store.
25
29
  resource_not_found: The resource you were looking for could not be found.
data/config/routes.rb CHANGED
@@ -103,9 +103,26 @@ Spree::Core::Engine.add_routes do
103
103
  resources :custom_fields
104
104
  end
105
105
 
106
+ # Mounts a read-only nested `translations` matrix on parents in the
107
+ # Spree.translatable_resources registry. Writes go through the batch
108
+ # endpoint (POST /translations/batch). One generic controller serves
109
+ # every translatable model. See docs/plans/5.5-6.0-resource-translations-api.md.
110
+ concern :translatable do
111
+ resources :translations, only: [:index], controller: 'translations'
112
+ end
113
+
106
114
  # Definitions are per resource type, not per instance.
107
115
  resources :custom_field_definitions
108
116
 
117
+ # Translation discovery: the translatable-resources registry and the
118
+ # store's supported locales. See docs/plans/5.5-6.0-resource-translations-api.md.
119
+ resources :translatable_resources, only: [:index]
120
+ resources :locales, only: [:index]
121
+
122
+ # Atomic multi-record translation upsert (e.g. an option type + all its
123
+ # option values in one save). Flat list of independent registry writes.
124
+ post 'translations/batch', to: 'translations/batches#create'
125
+
109
126
  # Authentication
110
127
  post 'auth/login', to: 'auth#create'
111
128
  post 'auth/refresh', to: 'auth#refresh'
@@ -117,6 +134,12 @@ Spree::Core::Engine.add_routes do
117
134
  get 'auth/invitations/:id/lookup', to: 'invitation_acceptances#lookup'
118
135
  post 'auth/invitations/:id/accept', to: 'invitation_acceptances#accept'
119
136
 
137
+ # Public password reset — unauthenticated; the token is the credential.
138
+ # Mounted under `auth/` so the refresh-token cookie issued on success
139
+ # shares its path with `/auth/refresh`.
140
+ post 'auth/password_resets', to: 'password_resets#create'
141
+ patch 'auth/password_resets/:id', to: 'password_resets#update'
142
+
120
143
  # Dashboard
121
144
  namespace :dashboard do
122
145
  get :analytics
@@ -124,6 +147,8 @@ Spree::Core::Engine.add_routes do
124
147
 
125
148
  # Current admin user + permissions (for UI permission checks)
126
149
  get 'me', to: 'me#show'
150
+ # Self-service profile updates (e.g. the admin's own UI language)
151
+ patch 'me', to: 'me#update'
127
152
 
128
153
  # Store Settings
129
154
  resource :store, only: [:show, :update], controller: 'store'
@@ -168,8 +193,21 @@ Spree::Core::Engine.add_routes do
168
193
  end
169
194
  end
170
195
 
196
+ # CSV Imports — see docs/plans/5.6-admin-spa-csv-import.md
197
+ resources :imports, only: [:index, :show, :create, :destroy] do
198
+ collection do
199
+ get :template
200
+ end
201
+ member do
202
+ patch :complete_mapping
203
+ patch :retry_failed_rows
204
+ get :download
205
+ end
206
+ resources :rows, only: [:index], controller: 'import_rows'
207
+ end
208
+
171
209
  # Products
172
- resources :products, concerns: :custom_fieldable do
210
+ resources :products, concerns: [:custom_fieldable, :translatable] do
173
211
  member do
174
212
  post :clone
175
213
  end
@@ -190,10 +228,22 @@ Spree::Core::Engine.add_routes do
190
228
  end
191
229
 
192
230
  # Categories
193
- resources :categories, only: [:index, :show], concerns: :custom_fieldable
231
+ resources :categories, concerns: [:custom_fieldable, :translatable] do
232
+ member do
233
+ patch :reposition
234
+ end
235
+
236
+ # Manual product membership + ordering within the category.
237
+ # (Bulk add/remove reuse POST /products/bulk_{add,remove}_*_categories.)
238
+ resources :products, controller: 'categories/products', only: [:index, :create, :destroy] do
239
+ member do
240
+ patch :reposition
241
+ end
242
+ end
243
+ end
194
244
 
195
245
  # Option Types (with nested option_values in payload)
196
- resources :option_types, concerns: :custom_fieldable
246
+ resources :option_types, concerns: [:custom_fieldable, :translatable]
197
247
 
198
248
  # Tax Categories
199
249
  resources :tax_categories
@@ -306,7 +356,7 @@ Spree::Core::Engine.add_routes do
306
356
  end
307
357
 
308
358
  resources :items, only: [:index, :show, :create, :update, :destroy], controller: 'orders/items'
309
- resources :fulfillments, controller: 'orders/fulfillments', only: [:index, :show, :update] do
359
+ resources :fulfillments, controller: 'orders/fulfillments', only: [:index, :show, :create, :update] do
310
360
  member do
311
361
  patch :fulfill
312
362
  patch :cancel
@@ -216,6 +216,7 @@ module Spree
216
216
  admin_payment_source_serializer: 'Spree::Api::V3::Admin::PaymentSourceSerializer',
217
217
  admin_digital_link_serializer: 'Spree::Api::V3::Admin::DigitalLinkSerializer',
218
218
  admin_store_serializer: 'Spree::Api::V3::Admin::StoreSerializer',
219
+ admin_setup_task_serializer: 'Spree::Api::V3::Admin::SetupTaskSerializer',
219
220
  admin_api_key_serializer: 'Spree::Api::V3::Admin::ApiKeySerializer',
220
221
  admin_allowed_origin_serializer: 'Spree::Api::V3::Admin::AllowedOriginSerializer',
221
222
  admin_webhook_endpoint_serializer: 'Spree::Api::V3::Admin::WebhookEndpointSerializer',
@@ -223,12 +224,16 @@ module Spree
223
224
  admin_invitation_serializer: 'Spree::Api::V3::Admin::InvitationSerializer',
224
225
  admin_role_serializer: 'Spree::Api::V3::Admin::RoleSerializer',
225
226
  admin_export_serializer: 'Spree::Api::V3::Admin::ExportSerializer',
227
+ admin_import_serializer: 'Spree::Api::V3::Admin::ImportSerializer',
228
+ admin_import_row_serializer: 'Spree::Api::V3::Admin::ImportRowSerializer',
229
+ admin_import_mapping_serializer: 'Spree::Api::V3::Admin::ImportMappingSerializer',
226
230
  admin_promotion_serializer: 'Spree::Api::V3::Admin::PromotionSerializer',
227
231
  admin_promotion_action_serializer: 'Spree::Api::V3::Admin::PromotionActionSerializer',
228
232
  admin_promotion_rule_serializer: 'Spree::Api::V3::Admin::PromotionRuleSerializer',
229
233
  admin_coupon_code_serializer: 'Spree::Api::V3::Admin::CouponCodeSerializer',
230
234
  admin_price_list_serializer: 'Spree::Api::V3::Admin::PriceListSerializer',
231
235
  admin_price_rule_serializer: 'Spree::Api::V3::Admin::PriceRuleSerializer',
236
+ admin_resource_translations_serializer: 'Spree::Api::V3::Admin::ResourceTranslationsSerializer',
232
237
 
233
238
  # platform serializers
234
239
  platform_metafield_serializer: 'Spree::Api::V2::Platform::MetafieldSerializer',
@@ -106,6 +106,16 @@ module Spree
106
106
  },
107
107
  required: %w[user permissions]
108
108
  },
109
+ ImportSchemaField: {
110
+ type: :object,
111
+ description: 'A canonical column of an import type, including per-store custom field columns',
112
+ properties: {
113
+ name: { type: :string, description: 'Canonical field name used in mappings', example: 'slug' },
114
+ label: { type: :string, description: 'Human-readable label', example: 'Slug' },
115
+ required: { type: :boolean, description: 'Whether the field must be mapped before processing' }
116
+ },
117
+ required: %w[name label required]
118
+ },
109
119
  CheckoutRequirement: {
110
120
  type: :object,
111
121
  properties: {
@@ -230,10 +240,29 @@ module Spree
230
240
  patch_promotion_rule_schema(schemas)
231
241
  patch_promotion_action_schema(schemas)
232
242
  patch_price_rule_schema(schemas)
243
+ patch_import_schema(schemas)
233
244
  schemas
234
245
  end
235
246
  end
236
247
 
248
+ # Same Array<{...}> issue as cart/fulfillment — patch Import#schema_fields
249
+ # to reference the ImportSchemaField component schema.
250
+ def patch_import_schema(schemas)
251
+ import = schemas['Import'] || schemas[:Import]
252
+ return unless import
253
+
254
+ props = import[:properties]
255
+ return unless props
256
+
257
+ fields_key = props.key?('schema_fields') ? 'schema_fields' : :schema_fields
258
+ if props[fields_key]
259
+ props[fields_key] = {
260
+ type: :array,
261
+ items: { '$ref' => '#/components/schemas/ImportSchemaField' }
262
+ }
263
+ end
264
+ end
265
+
237
266
  # Typelizer cannot represent Array<{...}> inline object types in OpenAPI,
238
267
  # so we patch them to reference manually-defined component schemas.
239
268
  def patch_cart_schema(schemas)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.5.3
4
+ version: 5.6.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vendo Connect Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-10 00:00:00.000000000 Z
11
+ date: 2026-07-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rswag-specs
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - '='
74
74
  - !ruby/object:Gem::Version
75
- version: 5.5.3
75
+ version: 5.6.0.rc1
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '='
81
81
  - !ruby/object:Gem::Version
82
- version: 5.5.3
82
+ version: 5.6.0.rc1
83
83
  description: Spree's API
84
84
  email:
85
85
  - hello@spreecommerce.org
@@ -109,11 +109,13 @@ files:
109
109
  - app/controllers/concerns/spree/api/v3/scoped_authorization.rb
110
110
  - app/controllers/concerns/spree/api/v3/security_headers.rb
111
111
  - app/controllers/concerns/spree/api/v3/store/search_provider_support.rb
112
+ - app/controllers/concerns/spree/api/v3/storefront_gating.rb
112
113
  - app/controllers/spree/api/v3/admin/admin_users_controller.rb
113
114
  - app/controllers/spree/api/v3/admin/allowed_origins_controller.rb
114
115
  - app/controllers/spree/api/v3/admin/api_keys_controller.rb
115
116
  - app/controllers/spree/api/v3/admin/auth_controller.rb
116
117
  - app/controllers/spree/api/v3/admin/base_controller.rb
118
+ - app/controllers/spree/api/v3/admin/categories/products_controller.rb
117
119
  - app/controllers/spree/api/v3/admin/categories_controller.rb
118
120
  - app/controllers/spree/api/v3/admin/channels_controller.rb
119
121
  - app/controllers/spree/api/v3/admin/countries_controller.rb
@@ -131,8 +133,11 @@ files:
131
133
  - app/controllers/spree/api/v3/admin/exports_controller.rb
132
134
  - app/controllers/spree/api/v3/admin/gift_card_batches_controller.rb
133
135
  - app/controllers/spree/api/v3/admin/gift_cards_controller.rb
136
+ - app/controllers/spree/api/v3/admin/import_rows_controller.rb
137
+ - app/controllers/spree/api/v3/admin/imports_controller.rb
134
138
  - app/controllers/spree/api/v3/admin/invitation_acceptances_controller.rb
135
139
  - app/controllers/spree/api/v3/admin/invitations_controller.rb
140
+ - app/controllers/spree/api/v3/admin/locales_controller.rb
136
141
  - app/controllers/spree/api/v3/admin/markets_controller.rb
137
142
  - app/controllers/spree/api/v3/admin/me_controller.rb
138
143
  - app/controllers/spree/api/v3/admin/media_controller.rb
@@ -146,6 +151,7 @@ files:
146
151
  - app/controllers/spree/api/v3/admin/orders/refunds_controller.rb
147
152
  - app/controllers/spree/api/v3/admin/orders/store_credits_controller.rb
148
153
  - app/controllers/spree/api/v3/admin/orders_controller.rb
154
+ - app/controllers/spree/api/v3/admin/password_resets_controller.rb
149
155
  - app/controllers/spree/api/v3/admin/payment_methods_controller.rb
150
156
  - app/controllers/spree/api/v3/admin/price_lists_controller.rb
151
157
  - app/controllers/spree/api/v3/admin/prices_controller.rb
@@ -164,6 +170,9 @@ files:
164
170
  - app/controllers/spree/api/v3/admin/store_credit_categories_controller.rb
165
171
  - app/controllers/spree/api/v3/admin/tags_controller.rb
166
172
  - app/controllers/spree/api/v3/admin/tax_categories_controller.rb
173
+ - app/controllers/spree/api/v3/admin/translatable_resources_controller.rb
174
+ - app/controllers/spree/api/v3/admin/translations/batches_controller.rb
175
+ - app/controllers/spree/api/v3/admin/translations_controller.rb
167
176
  - app/controllers/spree/api/v3/admin/variants_controller.rb
168
177
  - app/controllers/spree/api/v3/admin/webhook_deliveries_controller.rb
169
178
  - app/controllers/spree/api/v3/admin/webhook_endpoints_controller.rb
@@ -207,6 +216,7 @@ files:
207
216
  - app/jobs/spree/api_keys/mark_as_used.rb
208
217
  - app/jobs/spree/webhook_delivery_job.rb
209
218
  - app/models/spree/api_key_ability.rb
219
+ - app/serializers/concerns/spree/api/v3/admin/translatable.rb
210
220
  - app/serializers/spree/api/v3/address_serializer.rb
211
221
  - app/serializers/spree/api/v3/admin/address_serializer.rb
212
222
  - app/serializers/spree/api/v3/admin/adjustment_serializer.rb
@@ -231,6 +241,9 @@ files:
231
241
  - app/serializers/spree/api/v3/admin/fulfillment_serializer.rb
232
242
  - app/serializers/spree/api/v3/admin/gift_card_batch_serializer.rb
233
243
  - app/serializers/spree/api/v3/admin/gift_card_serializer.rb
244
+ - app/serializers/spree/api/v3/admin/import_mapping_serializer.rb
245
+ - app/serializers/spree/api/v3/admin/import_row_serializer.rb
246
+ - app/serializers/spree/api/v3/admin/import_serializer.rb
234
247
  - app/serializers/spree/api/v3/admin/invitation_serializer.rb
235
248
  - app/serializers/spree/api/v3/admin/line_item_serializer.rb
236
249
  - app/serializers/spree/api/v3/admin/market_serializer.rb
@@ -252,8 +265,10 @@ files:
252
265
  - app/serializers/spree/api/v3/admin/promotion_serializer.rb
253
266
  - app/serializers/spree/api/v3/admin/refund_serializer.rb
254
267
  - app/serializers/spree/api/v3/admin/reimbursement_serializer.rb
268
+ - app/serializers/spree/api/v3/admin/resource_translations_serializer.rb
255
269
  - app/serializers/spree/api/v3/admin/return_authorization_serializer.rb
256
270
  - app/serializers/spree/api/v3/admin/role_serializer.rb
271
+ - app/serializers/spree/api/v3/admin/setup_task_serializer.rb
257
272
  - app/serializers/spree/api/v3/admin/state_serializer.rb
258
273
  - app/serializers/spree/api/v3/admin/stock_item_serializer.rb
259
274
  - app/serializers/spree/api/v3/admin/stock_location_serializer.rb
@@ -364,9 +379,9 @@ licenses:
364
379
  - BSD-3-Clause
365
380
  metadata:
366
381
  bug_tracker_uri: https://github.com/spree/spree/issues
367
- changelog_uri: https://github.com/spree/spree/releases/tag/v5.5.3
382
+ changelog_uri: https://github.com/spree/spree/releases/tag/v5.6.0.rc1
368
383
  documentation_uri: https://docs.spreecommerce.org/
369
- source_code_uri: https://github.com/spree/spree/tree/v5.5.3
384
+ source_code_uri: https://github.com/spree/spree/tree/v5.6.0.rc1
370
385
  post_install_message:
371
386
  rdoc_options: []
372
387
  require_paths: