spree_api 6.0.0.rc1 → 6.0.0.rc2

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 (370) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +0 -17
  3. data/app/controllers/concerns/spree/api/v3/admin/product_membership.rb +10 -0
  4. data/app/controllers/concerns/spree/api/v3/admin/stock_receipt_actions.rb +26 -4
  5. data/app/controllers/concerns/spree/api/v3/admin/subclassed_resource.rb +14 -28
  6. data/app/controllers/concerns/spree/api/v3/admin/validation_details.rb +1 -1
  7. data/app/controllers/concerns/spree/api/v3/cart_resolvable.rb +3 -2
  8. data/app/controllers/concerns/spree/api/v3/channel_resolution.rb +1 -1
  9. data/app/controllers/concerns/spree/api/v3/company_address_writes.rb +3 -1
  10. data/app/controllers/concerns/spree/api/v3/error_handler.rb +48 -2
  11. data/app/controllers/concerns/spree/api/v3/file_uploads.rb +35 -0
  12. data/app/controllers/concerns/spree/api/v3/money_params.rb +52 -0
  13. data/app/controllers/concerns/spree/api/v3/orders/claim_actions.rb +1 -5
  14. data/app/controllers/concerns/spree/api/v3/orders/delivery_actions.rb +6 -6
  15. data/app/controllers/concerns/spree/api/v3/orders/exchange_actions.rb +1 -4
  16. data/app/controllers/concerns/spree/api/v3/orders/fulfillment_actions.rb +1 -5
  17. data/app/controllers/concerns/spree/api/v3/orders/return_actions.rb +1 -4
  18. data/app/controllers/concerns/spree/api/v3/orders/shipping_label_actions.rb +5 -21
  19. data/app/controllers/concerns/spree/api/v3/params_normalizer.rb +5 -0
  20. data/app/controllers/concerns/spree/api/v3/purchase_serialization.rb +26 -0
  21. data/app/controllers/concerns/spree/api/v3/request_params.rb +65 -0
  22. data/app/controllers/concerns/spree/api/v3/strict_money_params.rb +94 -0
  23. data/app/controllers/spree/api/v3/admin/admin_users_controller.rb +10 -1
  24. data/app/controllers/spree/api/v3/admin/allowed_origins_controller.rb +8 -1
  25. data/app/controllers/spree/api/v3/admin/api_keys_controller.rb +15 -7
  26. data/app/controllers/spree/api/v3/admin/base_controller.rb +1 -0
  27. data/app/controllers/spree/api/v3/admin/catalogs/order_minimums_controller.rb +3 -1
  28. data/app/controllers/spree/api/v3/admin/catalogs/quantity_rules_controller.rb +7 -5
  29. data/app/controllers/spree/api/v3/admin/catalogs_controller.rb +60 -33
  30. data/app/controllers/spree/api/v3/admin/categories/products_controller.rb +4 -1
  31. data/app/controllers/spree/api/v3/admin/categories_controller.rb +40 -20
  32. data/app/controllers/spree/api/v3/admin/channels_controller.rb +39 -6
  33. data/app/controllers/spree/api/v3/admin/claim_reasons_controller.rb +5 -1
  34. data/app/controllers/spree/api/v3/admin/collections/products_controller.rb +4 -1
  35. data/app/controllers/spree/api/v3/admin/collections_controller.rb +44 -15
  36. data/app/controllers/spree/api/v3/admin/commission_rates_controller.rb +60 -12
  37. data/app/controllers/spree/api/v3/admin/companies/addresses_controller.rb +11 -0
  38. data/app/controllers/spree/api/v3/admin/companies/memberships_controller.rb +9 -1
  39. data/app/controllers/spree/api/v3/admin/companies/tax_exemption_certificates_controller.rb +19 -18
  40. data/app/controllers/spree/api/v3/admin/companies/tax_identifiers_controller.rb +6 -1
  41. data/app/controllers/spree/api/v3/admin/companies_controller.rb +17 -3
  42. data/app/controllers/spree/api/v3/admin/countries_controller.rb +5 -1
  43. data/app/controllers/spree/api/v3/admin/custom_field_definitions_controller.rb +21 -11
  44. data/app/controllers/spree/api/v3/admin/custom_fields_controller.rb +12 -5
  45. data/app/controllers/spree/api/v3/admin/customer_groups_controller.rb +9 -1
  46. data/app/controllers/spree/api/v3/admin/customers/addresses_controller.rb +12 -7
  47. data/app/controllers/spree/api/v3/admin/customers/store_credits_controller.rb +11 -9
  48. data/app/controllers/spree/api/v3/admin/customers/tax_identifiers_controller.rb +6 -1
  49. data/app/controllers/spree/api/v3/admin/customers_controller.rb +35 -7
  50. data/app/controllers/spree/api/v3/admin/data_requests_controller.rb +8 -0
  51. data/app/controllers/spree/api/v3/admin/delivery_method_rules_controller.rb +1 -1
  52. data/app/controllers/spree/api/v3/admin/delivery_methods/rules_controller.rb +15 -7
  53. data/app/controllers/spree/api/v3/admin/delivery_methods_controller.rb +92 -57
  54. data/app/controllers/spree/api/v3/admin/delivery_profiles/origin_groups_controller.rb +8 -1
  55. data/app/controllers/spree/api/v3/admin/delivery_profiles_controller.rb +13 -1
  56. data/app/controllers/spree/api/v3/admin/delivery_zones_controller.rb +21 -5
  57. data/app/controllers/spree/api/v3/admin/email_templates/drafts_controller.rb +14 -1
  58. data/app/controllers/spree/api/v3/admin/email_templates/previews_controller.rb +4 -0
  59. data/app/controllers/spree/api/v3/admin/email_templates/publications_controller.rb +7 -0
  60. data/app/controllers/spree/api/v3/admin/email_templates/restorations_controller.rb +8 -0
  61. data/app/controllers/spree/api/v3/admin/email_templates/test_emails_controller.rb +4 -0
  62. data/app/controllers/spree/api/v3/admin/exports_controller.rb +23 -6
  63. data/app/controllers/spree/api/v3/admin/files_controller.rb +29 -0
  64. data/app/controllers/spree/api/v3/admin/gift_card_batches_controller.rb +20 -1
  65. data/app/controllers/spree/api/v3/admin/gift_cards_controller.rb +19 -1
  66. data/app/controllers/spree/api/v3/admin/imports_controller.rb +36 -9
  67. data/app/controllers/spree/api/v3/admin/integrations_controller.rb +13 -1
  68. data/app/controllers/spree/api/v3/admin/invitation_acceptances_controller.rb +9 -2
  69. data/app/controllers/spree/api/v3/admin/invitations_controller.rb +8 -1
  70. data/app/controllers/spree/api/v3/admin/locales_controller.rb +1 -1
  71. data/app/controllers/spree/api/v3/admin/markets_controller.rb +30 -9
  72. data/app/controllers/spree/api/v3/admin/me_controller.rb +12 -3
  73. data/app/controllers/spree/api/v3/admin/media_controller.rb +22 -9
  74. data/app/controllers/spree/api/v3/admin/media_library_controller.rb +4 -0
  75. data/app/controllers/spree/api/v3/admin/option_types_controller.rb +24 -7
  76. data/app/controllers/spree/api/v3/admin/order_cancellation_reasons_controller.rb +5 -1
  77. data/app/controllers/spree/api/v3/admin/order_routing_rules_controller.rb +13 -1
  78. data/app/controllers/spree/api/v3/admin/orders/claims_controller.rb +39 -1
  79. data/app/controllers/spree/api/v3/admin/orders/deliveries_controller.rb +19 -1
  80. data/app/controllers/spree/api/v3/admin/orders/discount_codes_controller.rb +5 -1
  81. data/app/controllers/spree/api/v3/admin/orders/discounts_controller.rb +29 -5
  82. data/app/controllers/spree/api/v3/admin/orders/exchanges_controller.rb +36 -1
  83. data/app/controllers/spree/api/v3/admin/orders/fees_controller.rb +22 -1
  84. data/app/controllers/spree/api/v3/admin/orders/fulfillments_controller.rb +66 -7
  85. data/app/controllers/spree/api/v3/admin/orders/gift_cards_controller.rb +6 -2
  86. data/app/controllers/spree/api/v3/admin/orders/items_controller.rb +11 -1
  87. data/app/controllers/spree/api/v3/admin/orders/labels_controller.rb +35 -2
  88. data/app/controllers/spree/api/v3/admin/orders/payments_controller.rb +16 -5
  89. data/app/controllers/spree/api/v3/admin/orders/refunds_controller.rb +6 -0
  90. data/app/controllers/spree/api/v3/admin/orders/returns_controller.rb +37 -1
  91. data/app/controllers/spree/api/v3/admin/orders/store_credits_controller.rb +6 -3
  92. data/app/controllers/spree/api/v3/admin/orders_controller.rb +74 -60
  93. data/app/controllers/spree/api/v3/admin/package_types_controller.rb +30 -5
  94. data/app/controllers/spree/api/v3/admin/password_resets_controller.rb +17 -0
  95. data/app/controllers/spree/api/v3/admin/payment_methods_controller.rb +38 -22
  96. data/app/controllers/spree/api/v3/admin/policies_controller.rb +8 -4
  97. data/app/controllers/spree/api/v3/admin/price_lists_controller.rb +27 -15
  98. data/app/controllers/spree/api/v3/admin/prices_controller.rb +51 -4
  99. data/app/controllers/spree/api/v3/admin/product_types_controller.rb +25 -8
  100. data/app/controllers/spree/api/v3/admin/products/digital_assets_controller.rb +27 -10
  101. data/app/controllers/spree/api/v3/admin/products/variants_controller.rb +9 -14
  102. data/app/controllers/spree/api/v3/admin/products_controller.rb +121 -70
  103. data/app/controllers/spree/api/v3/admin/promotion_actions_controller.rb +11 -2
  104. data/app/controllers/spree/api/v3/admin/promotion_rules_controller.rb +10 -1
  105. data/app/controllers/spree/api/v3/admin/promotions_controller.rb +50 -14
  106. data/app/controllers/spree/api/v3/admin/purchase_orders_controller.rb +54 -7
  107. data/app/controllers/spree/api/v3/admin/refund_reasons_controller.rb +5 -1
  108. data/app/controllers/spree/api/v3/admin/reporting/saved_reports_controller.rb +9 -1
  109. data/app/controllers/spree/api/v3/admin/reporting_controller.rb +34 -0
  110. data/app/controllers/spree/api/v3/admin/resource_controller.rb +1 -0
  111. data/app/controllers/spree/api/v3/admin/return_reasons_controller.rb +5 -1
  112. data/app/controllers/spree/api/v3/admin/roles_controller.rb +13 -1
  113. data/app/controllers/spree/api/v3/admin/seller_payouts_controller.rb +6 -0
  114. data/app/controllers/spree/api/v3/admin/seller_requirement_submissions_controller.rb +15 -0
  115. data/app/controllers/spree/api/v3/admin/seller_requirements_controller.rb +32 -6
  116. data/app/controllers/spree/api/v3/admin/sellers_controller.rb +75 -22
  117. data/app/controllers/spree/api/v3/admin/setup_controller.rb +54 -15
  118. data/app/controllers/spree/api/v3/admin/stock_levels_controller.rb +38 -9
  119. data/app/controllers/spree/api/v3/admin/stock_locations_controller.rb +41 -9
  120. data/app/controllers/spree/api/v3/admin/stock_transfers_controller.rb +48 -7
  121. data/app/controllers/spree/api/v3/admin/store_controller.rb +122 -53
  122. data/app/controllers/spree/api/v3/admin/store_credits_controller.rb +1 -7
  123. data/app/controllers/spree/api/v3/admin/suppliers_controller.rb +16 -6
  124. data/app/controllers/spree/api/v3/admin/tax_categories_controller.rb +7 -4
  125. data/app/controllers/spree/api/v3/admin/tax_rates_controller.rb +21 -3
  126. data/app/controllers/spree/api/v3/admin/tracking_carriers_controller.rb +4 -0
  127. data/app/controllers/spree/api/v3/admin/translations/batches_controller.rb +17 -9
  128. data/app/controllers/spree/api/v3/admin/translations_controller.rb +4 -0
  129. data/app/controllers/spree/api/v3/admin/translations_coverage_controller.rb +5 -0
  130. data/app/controllers/spree/api/v3/admin/updates_controller.rb +39 -0
  131. data/app/controllers/spree/api/v3/admin/webhook_endpoints_controller.rb +23 -1
  132. data/app/controllers/spree/api/v3/base_controller.rb +2 -0
  133. data/app/controllers/spree/api/v3/resource_controller.rb +63 -10
  134. data/app/controllers/spree/api/v3/seller/auth_controller.rb +8 -0
  135. data/app/controllers/spree/api/v3/seller/base_controller.rb +1 -0
  136. data/app/controllers/spree/api/v3/seller/countries_controller.rb +2 -2
  137. data/app/controllers/spree/api/v3/seller/delivery_methods_controller.rb +49 -60
  138. data/app/controllers/spree/api/v3/seller/exports_controller.rb +18 -1
  139. data/app/controllers/spree/api/v3/seller/files_controller.rb +27 -0
  140. data/app/controllers/spree/api/v3/seller/imports_controller.rb +25 -9
  141. data/app/controllers/spree/api/v3/seller/invitation_acceptances_controller.rb +9 -1
  142. data/app/controllers/spree/api/v3/seller/me_controller.rb +14 -3
  143. data/app/controllers/spree/api/v3/seller/onboarding_controller.rb +11 -0
  144. data/app/controllers/spree/api/v3/seller/orders/claims_controller.rb +35 -0
  145. data/app/controllers/spree/api/v3/seller/orders/deliveries_controller.rb +12 -0
  146. data/app/controllers/spree/api/v3/seller/orders/exchanges_controller.rb +33 -0
  147. data/app/controllers/spree/api/v3/seller/orders/fulfillments_controller.rb +28 -2
  148. data/app/controllers/spree/api/v3/seller/orders/labels_controller.rb +22 -1
  149. data/app/controllers/spree/api/v3/seller/orders/notes_controller.rb +8 -1
  150. data/app/controllers/spree/api/v3/seller/orders/returns_controller.rb +32 -0
  151. data/app/controllers/spree/api/v3/seller/orders_controller.rb +35 -14
  152. data/app/controllers/spree/api/v3/seller/package_types_controller.rb +21 -9
  153. data/app/controllers/spree/api/v3/seller/password_resets_controller.rb +10 -0
  154. data/app/controllers/spree/api/v3/seller/policies_controller.rb +12 -4
  155. data/app/controllers/spree/api/v3/seller/products_controller.rb +114 -28
  156. data/app/controllers/spree/api/v3/seller/profile_controller.rb +33 -10
  157. data/app/controllers/spree/api/v3/seller/requirement_submissions_controller.rb +12 -5
  158. data/app/controllers/spree/api/v3/seller/resource_controller.rb +12 -0
  159. data/app/controllers/spree/api/v3/seller/stock_locations_controller.rb +44 -13
  160. data/app/controllers/spree/api/v3/seller/tax_identifiers_controller.rb +10 -1
  161. data/app/controllers/spree/api/v3/seller/team_controller.rb +4 -0
  162. data/app/controllers/spree/api/v3/store/auth_controller.rb +10 -0
  163. data/app/controllers/spree/api/v3/store/carts/discount_codes_controller.rb +8 -1
  164. data/app/controllers/spree/api/v3/store/carts/fulfillments_controller.rb +10 -2
  165. data/app/controllers/spree/api/v3/store/carts/gift_cards_controller.rb +8 -1
  166. data/app/controllers/spree/api/v3/store/carts/items_controller.rb +17 -5
  167. data/app/controllers/spree/api/v3/store/carts/payment_sessions_controller.rb +20 -6
  168. data/app/controllers/spree/api/v3/store/carts/payments_controller.rb +11 -0
  169. data/app/controllers/spree/api/v3/store/carts/po_documents_controller.rb +24 -26
  170. data/app/controllers/spree/api/v3/store/carts/store_credits_controller.rb +7 -1
  171. data/app/controllers/spree/api/v3/store/carts/tax_identifiers_controller.rb +5 -1
  172. data/app/controllers/spree/api/v3/store/carts_controller.rb +56 -47
  173. data/app/controllers/spree/api/v3/store/companies/addresses_controller.rb +12 -4
  174. data/app/controllers/spree/api/v3/store/companies/members_controller.rb +4 -0
  175. data/app/controllers/spree/api/v3/store/companies_controller.rb +5 -1
  176. data/app/controllers/spree/api/v3/store/company_invitations_controller.rb +9 -1
  177. data/app/controllers/spree/api/v3/store/customer/addresses_controller.rb +8 -7
  178. data/app/controllers/spree/api/v3/store/customer/data_requests_controller.rb +5 -0
  179. data/app/controllers/spree/api/v3/store/customer/password_resets_controller.rb +17 -0
  180. data/app/controllers/spree/api/v3/store/customer/payment_setup_sessions_controller.rb +10 -5
  181. data/app/controllers/spree/api/v3/store/customer/tax_identifiers_controller.rb +5 -1
  182. data/app/controllers/spree/api/v3/store/customers_controller.rb +28 -2
  183. data/app/controllers/spree/api/v3/store/newsletter_subscribers_controller.rb +22 -0
  184. data/app/controllers/spree/api/v3/store/orders/claims_controller.rb +11 -2
  185. data/app/controllers/spree/api/v3/store/orders/returns_controller.rb +10 -1
  186. data/app/controllers/spree/api/v3/store/orders_controller.rb +47 -10
  187. data/app/controllers/spree/api/v3/store/products_controller.rb +8 -0
  188. data/app/controllers/spree/api/v3/store/wishlist_items_controller.rb +10 -4
  189. data/app/controllers/spree/api/v3/store/wishlists_controller.rb +12 -4
  190. data/app/request_schemas/spree/api/v3/address_request_schema.rb +27 -0
  191. data/app/request_schemas/spree/api/v3/admin/address_request_schema.rb +13 -0
  192. data/app/request_schemas/spree/api/v3/admin/bulk_tags_request_schema.rb +17 -0
  193. data/app/request_schemas/spree/api/v3/admin/catalog_order_minimum_request_schema.rb +17 -0
  194. data/app/request_schemas/spree/api/v3/admin/email_template_language_request_schema.rb +17 -0
  195. data/app/request_schemas/spree/api/v3/admin/email_template_preview_request_schema.rb +32 -0
  196. data/app/request_schemas/spree/api/v3/admin/price_adjustment_tier_request_schema.rb +21 -0
  197. data/app/request_schemas/spree/api/v3/admin/price_list_price_request_schema.rb +21 -0
  198. data/app/request_schemas/spree/api/v3/admin/price_list_rule_request_schema.rb +19 -0
  199. data/app/request_schemas/spree/api/v3/admin/product_membership_reposition_request_schema.rb +17 -0
  200. data/app/request_schemas/spree/api/v3/admin/promotion_action_calculator_request_schema.rb +18 -0
  201. data/app/request_schemas/spree/api/v3/admin/promotion_action_line_item_request_schema.rb +15 -0
  202. data/app/request_schemas/spree/api/v3/admin/promotion_action_request_schema.rb +19 -0
  203. data/app/request_schemas/spree/api/v3/admin/promotion_rule_request_schema.rb +22 -0
  204. data/app/request_schemas/spree/api/v3/admin/reason_request_schema.rb +14 -0
  205. data/app/request_schemas/spree/api/v3/admin/tax_identifier_request_schema.rb +15 -0
  206. data/app/request_schemas/spree/api/v3/custom_field_value_request_schema.rb +15 -0
  207. data/app/request_schemas/spree/api/v3/file_upload_request_schema.rb +20 -0
  208. data/app/request_schemas/spree/api/v3/media_request_schema.rb +27 -0
  209. data/app/request_schemas/spree/api/v3/price_request_schema.rb +17 -0
  210. data/app/request_schemas/spree/api/v3/stock_level_request_schema.rb +18 -0
  211. data/app/request_schemas/spree/api/v3/store/address_request_schema.rb +29 -0
  212. data/app/request_schemas/spree/api/v3/tax_identifier_request_schema.rb +18 -0
  213. data/app/request_schemas/spree/api/v3/variant_option_request_schema.rb +13 -0
  214. data/app/request_schemas/spree/api/v3/variant_request_schema.rb +52 -0
  215. data/app/serializers/concerns/spree/api/v3/purchase_summary.rb +48 -0
  216. data/app/serializers/spree/api/v3/address_serializer.rb +4 -2
  217. data/app/serializers/spree/api/v3/admin/address_serializer.rb +3 -0
  218. data/app/serializers/spree/api/v3/admin/applied_promotion_serializer.rb +3 -1
  219. data/app/serializers/spree/api/v3/admin/cart_serializer.rb +3 -1
  220. data/app/serializers/spree/api/v3/admin/catalog_order_minimum_serializer.rb +3 -2
  221. data/app/serializers/spree/api/v3/admin/catalog_price_serializer.rb +3 -4
  222. data/app/serializers/spree/api/v3/admin/catalog_price_tier_serializer.rb +4 -3
  223. data/app/serializers/spree/api/v3/admin/channel_serializer.rb +5 -7
  224. data/app/serializers/spree/api/v3/admin/claim_line_item_serializer.rb +7 -0
  225. data/app/serializers/spree/api/v3/admin/claim_serializer.rb +5 -0
  226. data/app/serializers/spree/api/v3/admin/commission_line_serializer.rb +7 -18
  227. data/app/serializers/spree/api/v3/admin/commission_rate_serializer.rb +7 -11
  228. data/app/serializers/spree/api/v3/admin/commission_rule_serializer.rb +0 -2
  229. data/app/serializers/spree/api/v3/admin/country_serializer.rb +4 -0
  230. data/app/serializers/spree/api/v3/admin/customer_serializer.rb +3 -7
  231. data/app/serializers/spree/api/v3/admin/delivery_method_rule_serializer.rb +0 -2
  232. data/app/serializers/spree/api/v3/admin/delivery_method_serializer.rb +14 -10
  233. data/app/serializers/spree/api/v3/admin/delivery_method_service_serializer.rb +9 -1
  234. data/app/serializers/spree/api/v3/admin/delivery_rate_serializer.rb +8 -0
  235. data/app/serializers/spree/api/v3/admin/discount_serializer.rb +3 -1
  236. data/app/serializers/spree/api/v3/admin/exchange_line_item_serializer.rb +5 -0
  237. data/app/serializers/spree/api/v3/admin/exchange_serializer.rb +5 -0
  238. data/app/serializers/spree/api/v3/admin/fee_serializer.rb +3 -1
  239. data/app/serializers/spree/api/v3/admin/file_upload_serializer.rb +11 -0
  240. data/app/serializers/spree/api/v3/admin/file_upload_target_serializer.rb +10 -0
  241. data/app/serializers/spree/api/v3/admin/fulfillment_serializer.rb +13 -11
  242. data/app/serializers/spree/api/v3/admin/gift_card_serializer.rb +3 -3
  243. data/app/serializers/spree/api/v3/admin/import_serializer.rb +2 -2
  244. data/app/serializers/spree/api/v3/admin/integration_serializer.rb +1 -3
  245. data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +12 -12
  246. data/app/serializers/spree/api/v3/admin/locale_serializer.rb +13 -0
  247. data/app/serializers/spree/api/v3/admin/market_serializer.rb +3 -0
  248. data/app/serializers/spree/api/v3/admin/media_serializer.rb +3 -2
  249. data/app/serializers/spree/api/v3/admin/order_group_serializer.rb +2 -0
  250. data/app/serializers/spree/api/v3/admin/order_routing_rule_serializer.rb +1 -3
  251. data/app/serializers/spree/api/v3/admin/order_serializer.rb +33 -33
  252. data/app/serializers/spree/api/v3/admin/payment_method_serializer.rb +1 -4
  253. data/app/serializers/spree/api/v3/admin/payment_serializer.rb +6 -3
  254. data/app/serializers/spree/api/v3/admin/payment_split_serializer.rb +8 -9
  255. data/app/serializers/spree/api/v3/admin/price_adjustment_tier_serializer.rb +3 -6
  256. data/app/serializers/spree/api/v3/admin/price_history_serializer.rb +5 -2
  257. data/app/serializers/spree/api/v3/admin/price_list_serializer.rb +1 -5
  258. data/app/serializers/spree/api/v3/admin/price_rule_serializer.rb +1 -3
  259. data/app/serializers/spree/api/v3/admin/price_serializer.rb +2 -0
  260. data/app/serializers/spree/api/v3/admin/product_serializer.rb +2 -0
  261. data/app/serializers/spree/api/v3/admin/promotion_action_serializer.rb +4 -6
  262. data/app/serializers/spree/api/v3/admin/promotion_rule_serializer.rb +0 -2
  263. data/app/serializers/spree/api/v3/admin/purchase_order_item_serializer.rb +5 -11
  264. data/app/serializers/spree/api/v3/admin/purchase_order_serializer.rb +4 -4
  265. data/app/serializers/spree/api/v3/admin/refund_serializer.rb +7 -3
  266. data/app/serializers/spree/api/v3/admin/reporting_result_serializer.rb +9 -2
  267. data/app/serializers/spree/api/v3/admin/return_line_item_serializer.rb +8 -0
  268. data/app/serializers/spree/api/v3/admin/return_serializer.rb +8 -4
  269. data/app/serializers/spree/api/v3/admin/seller_balance_serializer.rb +15 -10
  270. data/app/serializers/spree/api/v3/admin/seller_payout_serializer.rb +3 -5
  271. data/app/serializers/spree/api/v3/admin/seller_serializer.rb +3 -1
  272. data/app/serializers/spree/api/v3/admin/seller_transfer_serializer.rb +7 -8
  273. data/app/serializers/spree/api/v3/admin/shipping_label_serializer.rb +4 -4
  274. data/app/serializers/spree/api/v3/admin/stock_location_serializer.rb +3 -0
  275. data/app/serializers/spree/api/v3/admin/stock_movement_serializer.rb +2 -6
  276. data/app/serializers/spree/api/v3/admin/store_credit_event_serializer.rb +5 -0
  277. data/app/serializers/spree/api/v3/admin/store_credit_serializer.rb +9 -4
  278. data/app/serializers/spree/api/v3/admin/store_serializer.rb +17 -98
  279. data/app/serializers/spree/api/v3/admin/supplier_serializer.rb +1 -1
  280. data/app/serializers/spree/api/v3/admin/tax_exemption_certificate_serializer.rb +3 -1
  281. data/app/serializers/spree/api/v3/admin/tax_line_serializer.rb +4 -2
  282. data/app/serializers/spree/api/v3/admin/tax_rate_serializer.rb +2 -12
  283. data/app/serializers/spree/api/v3/admin/update_status_serializer.rb +28 -0
  284. data/app/serializers/spree/api/v3/admin/variant_serializer.rb +7 -3
  285. data/app/serializers/spree/api/v3/base_serializer.rb +112 -7
  286. data/app/serializers/spree/api/v3/cart_serializer.rb +11 -29
  287. data/app/serializers/spree/api/v3/claim_line_item_serializer.rb +5 -13
  288. data/app/serializers/spree/api/v3/claim_serializer.rb +2 -3
  289. data/app/serializers/spree/api/v3/country_serializer.rb +2 -1
  290. data/app/serializers/spree/api/v3/currency_serializer.rb +9 -1
  291. data/app/serializers/spree/api/v3/customer_serializer.rb +3 -2
  292. data/app/serializers/spree/api/v3/delivery_rate_serializer.rb +8 -12
  293. data/app/serializers/spree/api/v3/delivery_zone_member_serializer.rb +1 -1
  294. data/app/serializers/spree/api/v3/discount_serializer.rb +4 -1
  295. data/app/serializers/spree/api/v3/exchange_line_item_serializer.rb +2 -8
  296. data/app/serializers/spree/api/v3/exchange_serializer.rb +2 -3
  297. data/app/serializers/spree/api/v3/file_upload_serializer.rb +28 -0
  298. data/app/serializers/spree/api/v3/file_upload_target_serializer.rb +17 -0
  299. data/app/serializers/spree/api/v3/gift_card_batch_serializer.rb +2 -4
  300. data/app/serializers/spree/api/v3/gift_card_serializer.rb +4 -2
  301. data/app/serializers/spree/api/v3/import_serializer.rb +0 -12
  302. data/app/serializers/spree/api/v3/line_item_serializer.rb +15 -5
  303. data/app/serializers/spree/api/v3/locale_serializer.rb +3 -1
  304. data/app/serializers/spree/api/v3/market_serializer.rb +5 -5
  305. data/app/serializers/spree/api/v3/media_serializer.rb +7 -2
  306. data/app/serializers/spree/api/v3/order_group_serializer.rb +12 -4
  307. data/app/serializers/spree/api/v3/order_serializer.rb +5 -23
  308. data/app/serializers/spree/api/v3/payment_session_serializer.rb +2 -4
  309. data/app/serializers/spree/api/v3/price_history_serializer.rb +5 -5
  310. data/app/serializers/spree/api/v3/price_serializer.rb +6 -9
  311. data/app/serializers/spree/api/v3/product_filter_price_range_serializer.rb +10 -4
  312. data/app/serializers/spree/api/v3/product_serializer.rb +3 -3
  313. data/app/serializers/spree/api/v3/refund_serializer.rb +8 -5
  314. data/app/serializers/spree/api/v3/return_line_item_serializer.rb +3 -11
  315. data/app/serializers/spree/api/v3/return_serializer.rb +3 -6
  316. data/app/serializers/spree/api/v3/seller/address_serializer.rb +12 -0
  317. data/app/serializers/spree/api/v3/seller/balance_serializer.rb +15 -10
  318. data/app/serializers/spree/api/v3/seller/claim_line_item_serializer.rb +7 -0
  319. data/app/serializers/spree/api/v3/seller/claim_serializer.rb +5 -0
  320. data/app/serializers/spree/api/v3/seller/country_serializer.rb +19 -0
  321. data/app/serializers/spree/api/v3/seller/delivery_method_rule_serializer.rb +1 -3
  322. data/app/serializers/spree/api/v3/seller/delivery_method_serializer.rb +4 -8
  323. data/app/serializers/spree/api/v3/seller/delivery_rate_serializer.rb +9 -8
  324. data/app/serializers/spree/api/v3/seller/exchange_line_item_serializer.rb +5 -0
  325. data/app/serializers/spree/api/v3/seller/exchange_serializer.rb +5 -0
  326. data/app/serializers/spree/api/v3/seller/file_upload_serializer.rb +11 -0
  327. data/app/serializers/spree/api/v3/seller/file_upload_target_serializer.rb +10 -0
  328. data/app/serializers/spree/api/v3/seller/import_serializer.rb +2 -2
  329. data/app/serializers/spree/api/v3/seller/media_serializer.rb +2 -5
  330. data/app/serializers/spree/api/v3/seller/order_line_item_serializer.rb +4 -3
  331. data/app/serializers/spree/api/v3/seller/order_serializer.rb +16 -16
  332. data/app/serializers/spree/api/v3/seller/payment_split_serializer.rb +8 -9
  333. data/app/serializers/spree/api/v3/seller/payout_serializer.rb +2 -4
  334. data/app/serializers/spree/api/v3/seller/price_history_serializer.rb +11 -0
  335. data/app/serializers/spree/api/v3/seller/price_serializer.rb +11 -0
  336. data/app/serializers/spree/api/v3/seller/product_serializer.rb +2 -0
  337. data/app/serializers/spree/api/v3/seller/profile_serializer.rb +7 -7
  338. data/app/serializers/spree/api/v3/seller/return_line_item_serializer.rb +8 -0
  339. data/app/serializers/spree/api/v3/seller/return_serializer.rb +8 -5
  340. data/app/serializers/spree/api/v3/seller/state_serializer.rb +10 -0
  341. data/app/serializers/spree/api/v3/seller/stock_location_serializer.rb +3 -0
  342. data/app/serializers/spree/api/v3/seller/transfer_serializer.rb +6 -7
  343. data/app/serializers/spree/api/v3/seller/variant_serializer.rb +8 -4
  344. data/app/serializers/spree/api/v3/stock_location_serializer.rb +8 -1
  345. data/app/serializers/spree/api/v3/store_credit_event_serializer.rb +1 -3
  346. data/app/serializers/spree/api/v3/store_credit_serializer.rb +3 -5
  347. data/app/serializers/spree/api/v3/tax_line_serializer.rb +2 -4
  348. data/app/serializers/spree/api/v3/variant_serializer.rb +3 -3
  349. data/app/services/spree/api/v3/filter_predicates.rb +29 -0
  350. data/app/services/spree/api/v3/filter_table.rb +337 -0
  351. data/app/services/spree/api/v3/filters_aggregator.rb +2 -2
  352. data/app/services/spree/api/v3/request_schema.rb +430 -0
  353. data/app/services/spree/webhooks/deliver_webhook.rb +7 -3
  354. data/config/initializers/typelizer.rb +1 -1
  355. data/config/routes.rb +5 -3
  356. data/lib/spree/api/configuration.rb +5 -0
  357. data/lib/spree/api/dependencies.rb +7 -6
  358. data/lib/spree/api/middleware/request_size_limit.rb +24 -3
  359. data/lib/spree/api/openapi/filter_documentation.rb +136 -0
  360. data/lib/spree/api/openapi/schema_helper.rb +126 -80
  361. data/lib/spree/api/preference_families.rb +46 -0
  362. data/lib/spree/api/preference_types.rb +138 -0
  363. data/lib/spree/api/typelizer_generation.rb +4 -0
  364. data/lib/tasks/filters.rake +12 -0
  365. data/lib/typelizer/open_enums.rb +10 -0
  366. metadata +60 -10
  367. data/app/controllers/concerns/spree/api/v3/address_params.rb +0 -17
  368. data/app/controllers/concerns/spree/api/v3/direct_uploads.rb +0 -35
  369. data/app/controllers/spree/api/v3/admin/direct_uploads_controller.rb +0 -54
  370. data/app/controllers/spree/api/v3/seller/direct_uploads_controller.rb +0 -21
@@ -0,0 +1,430 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ # What one write endpoint accepts, declared once. The controller permits
5
+ # from it, the API reference documents it, and the SDK request types are
6
+ # generated from the reference, so the three cannot disagree.
7
+ #
8
+ # request_params :product do |body|
9
+ # body.attribute :name, :string, required: true, on: :create
10
+ # body.attribute :status, :string, choices: Spree::Product::STATUSES, open: true
11
+ # body.attribute :category_ids, :array, of: :id
12
+ # body.attribute :metadata, :hash
13
+ # body.attribute :prices, :array, of: Spree::Api::V3::PriceRequestSchema
14
+ # body.attribute :options, :array do |option|
15
+ # option.attribute :name, :string, required: true
16
+ # end
17
+ # end
18
+ #
19
+ # The vocabulary is the `preference` one: a type symbol, `of:` for what
20
+ # an `:array` holds, `choices:` for a closed list (`open: true` when
21
+ # extensions may extend it), `:hash` for any keys. `required: true` is
22
+ # required on both actions; add `on: :create` to require it when
23
+ # creating only. `only: :create` (or `:update`) accepts the field on one
24
+ # action alone. `permit: false` documents a field the controller reads
25
+ # from the raw params itself. `format:` documents a string's format
26
+ # (`email`, `uri`). `deprecated: 'Use country_code. Removed in 6.1.'`
27
+ # keeps a field working while the API reference and the SDK types flag it.
28
+ class RequestSchema
29
+ KINDS = {
30
+ string: { type: :string },
31
+ integer: { type: :integer },
32
+ # Measurements a client sends as JSON numbers (weights, dimensions,
33
+ # coordinates). Money is never a number: use `decimal`.
34
+ number: { type: :number },
35
+ # Amounts, rates and quantities with decimals travel as canonical
36
+ # decimal strings, never JSON numbers (see Spree::Money::Rounding).
37
+ decimal: { type: :string, format: :decimal, pattern: '^-?\d+(\.\d+)?$' },
38
+ boolean: { type: :boolean },
39
+ date: { type: :string, format: :date },
40
+ datetime: { type: :string, format: :'date-time' },
41
+ id: { type: :string },
42
+ any: {},
43
+ # ISO codes, from the closed lists below.
44
+ country: { type: :string },
45
+ currency: { type: :string },
46
+ locale: { type: :string }
47
+ }.freeze
48
+
49
+ # The ISO lists a `country` or `currency` field takes, published once
50
+ # per API as a named component — the same `CountryCode` and
51
+ # `CurrencyCode` types the serializers declare, so a value read from
52
+ # a response is the type a request takes.
53
+ CODE_LISTS = {
54
+ country: ['CountryCode', -> { Spree::IsoData.country_codes }],
55
+ currency: ['CurrencyCode', -> { Spree::Money::Rounding::CURRENCY_CODES }],
56
+ locale: ['LocaleCode', -> { Spree::Locales::ALL }]
57
+ }.freeze
58
+
59
+ ACTIONS = %i[create update].freeze
60
+
61
+ OPEN_LIST_NOTE = 'The values listed are the built-in ones; extensions may add more.'.freeze
62
+
63
+ class Field
64
+ attr_reader :name, :kind, :array, :required, :nullable, :description, :example, :enum, :open, :only, :schema,
65
+ :permit, :format, :deprecated
66
+
67
+ def initialize(name, kind, array: false, required: false, nullable: false, description: nil,
68
+ example: nil, enum: nil, open: false, only: nil, schema: nil, permit: true, format: nil,
69
+ deprecated: nil)
70
+ raise ArgumentError, "deprecated: says what to use instead (#{name})" if deprecated == true
71
+
72
+ raise ArgumentError, "open: needs an enum: list of the built-in values (#{name})" if open && enum.nil?
73
+
74
+ unless required.in?([true, false, :create])
75
+ raise ArgumentError, "required: must be true, false or :create (#{name})"
76
+ end
77
+ raise ArgumentError, "only: must be :create or :update (#{name})" if only && !only.in?(ACTIONS)
78
+
79
+ @name = name.to_sym
80
+ @kind = kind
81
+ @array = array
82
+ @required = required
83
+ @nullable = nullable
84
+ @description = description
85
+ @example = example
86
+ @enum = enum&.map(&:to_s)
87
+ @open = open
88
+ @only = only
89
+ @schema = schema
90
+ @permit = permit
91
+ @format = format
92
+ @deprecated = deprecated
93
+ end
94
+
95
+ # @param action [Symbol, nil]
96
+ def accepted_on?(action)
97
+ only.nil? || action.nil? || only == action
98
+ end
99
+
100
+ # @param action [Symbol, nil]
101
+ def required_on?(action)
102
+ required == true || (required == :create && action == :create)
103
+ end
104
+
105
+ # This field, required on `on` (`:create`) or always. A field
106
+ # already required always stays required always.
107
+ #
108
+ # @param on [Symbol, nil] `:create`, or nil for every action
109
+ # @return [Field]
110
+ def required_copy(on)
111
+ copy = dup
112
+ copy.instance_variable_set(:@required, required == true ? true : on || true)
113
+ copy
114
+ end
115
+
116
+ # A hash of any keys, such as `metadata` or `provider_settings`.
117
+ def free_object?
118
+ kind == :object && schema.nil?
119
+ end
120
+ end
121
+
122
+ attr_reader :name, :fields
123
+
124
+ # The types an attribute takes: the scalars above, plus `:hash` (any
125
+ # keys), `:object` (a block or a `shape:`) and `:array` (`of:` a type
126
+ # or a shape, or a block of fields).
127
+ TYPES = (KINDS.keys + %i[hash object array]).freeze
128
+
129
+ # A shape shared between several endpoints (a price, an address),
130
+ # published as its own component so each SDK has one type for it.
131
+ #
132
+ # @param name [String] the component name, such as `PriceParams`
133
+ # @return [RequestSchema]
134
+ def self.shape(name, &block)
135
+ raise ArgumentError, "a shape's name ends in Params (#{name})" unless name.to_s.end_with?('Params')
136
+
137
+ new(name, &block)
138
+ end
139
+
140
+ # A shape several endpoints share is a class in app/request_schemas,
141
+ # declared the way a model declares its attributes:
142
+ #
143
+ # class Spree::Api::V3::PriceRequestSchema < Spree::Api::V3::RequestSchema
144
+ # publishes 'PriceParams'
145
+ #
146
+ # attribute :currency, :currency, required: true
147
+ # end
148
+ #
149
+ # A subclass starts from its parent's attributes, so the Admin API's
150
+ # address extends the shared one. Without `publishes` the class is a
151
+ # set of attributes other declarations include rather than a type of
152
+ # its own.
153
+ class << self
154
+ # @param name [String] the component name, ending in `Params`
155
+ def publishes(name)
156
+ raise ArgumentError, "a shape's name ends in Params (#{name})" unless name.to_s.end_with?('Params')
157
+ raise ArgumentError, "#{self.name}: `publishes` goes before the attributes" if @definition
158
+
159
+ @published_name = name.to_s
160
+ end
161
+
162
+ # @return [RequestSchema] what the class declares
163
+ def definition
164
+ @definition ||= new(@published_name).tap do |schema|
165
+ schema.include_attributes(superclass) if superclass < RequestSchema
166
+ end
167
+ end
168
+
169
+ # @param value [RequestSchema, Class] an instance or a declared class
170
+ # @return [RequestSchema, nil]
171
+ def resolve(value)
172
+ value.is_a?(Class) ? value.definition : value
173
+ end
174
+
175
+ %i[attribute include_attributes require_attributes external_references].each do |method|
176
+ define_method(method) { |*args, **options, &block| definition.public_send(method, *args, **options, &block) }
177
+ end
178
+ end
179
+
180
+ # @param name [String, Symbol, nil] the resource, `product` publishing
181
+ # `ProductCreateParams` and `ProductUpdateParams`; or a name ending
182
+ # in `Params`, published as given for every action
183
+ # @yieldparam schema [RequestSchema] the schema, to declare attributes on
184
+ def initialize(name = nil)
185
+ @name = name&.to_s
186
+ @fields = {}
187
+ yield self if block_given?
188
+ end
189
+
190
+ # Declares one attribute of the body.
191
+ #
192
+ # @param name [Symbol]
193
+ # @param type [Symbol] one of TYPES
194
+ # @param of [Symbol, Class, RequestSchema, nil] what an `:array` holds:
195
+ # a scalar type, or a shared shape
196
+ # @param shape [Class, RequestSchema, nil] what an `:object` holds
197
+ # @param as [String, nil] publishes a block's object (or the objects
198
+ # of an `:array`) as a component of this name
199
+ # @param choices [Array, nil] the values the attribute takes
200
+ # @param required [Boolean]
201
+ # @param on [Symbol, nil] `:create` to require it only when creating
202
+ # @yieldparam schema [RequestSchema] a nested object's attributes
203
+ def attribute(name, type, of: nil, shape: nil, as: nil, choices: nil, required: false, on: nil, **options, &block)
204
+ raise ArgumentError, "#{name}: unknown type #{type.inspect}; use one of #{TYPES.join(', ')}" unless TYPES.include?(type)
205
+ raise ArgumentError, "#{name}: `on:` scopes `required: true` and is `:create`" if on && (!required || on != :create)
206
+
207
+ options = options.merge(required: on || required, enum: choices)
208
+ nested = nested_schema(type, of: of, shape: shape, as: as, &block)
209
+
210
+ case type
211
+ when :array then add_array(name, of, nested, options)
212
+ when :object
213
+ raise ArgumentError, "#{name}: an :object needs a block or `shape:`; use :hash for any keys" unless nested
214
+
215
+ add(Field.new(name, :object, schema: nested, **options))
216
+ when :hash then add(Field.new(name, :object, **options))
217
+ else add(Field.new(name, type, **options))
218
+ end
219
+ end
220
+
221
+ # The identities a record has in external systems, which the Admin API
222
+ # reads from the raw params (Admin::Concerns::ExternalReferences).
223
+ def external_references
224
+ attribute :external_references, :hash, permit: false, description: <<~TEXT.squish
225
+ Identities the record has in external systems, keyed by system: the same
226
+ `{ system: external_id }` map the Admin API renders, so what you read can be
227
+ sent straight back. Systems you do not name keep the reference they have.
228
+ A list of `{ system, external_id }` entries is accepted too.
229
+ TEXT
230
+ end
231
+
232
+ # Adds the attributes of another schema, for endpoints that extend a
233
+ # shared set.
234
+ #
235
+ # @param other [RequestSchema, Class]
236
+ def include_attributes(other)
237
+ RequestSchema.resolve(other).fields.each_value { |field| add(field) }
238
+ end
239
+
240
+ # Marks attributes this schema took from another as required, for an
241
+ # endpoint whose model needs what the shared set leaves optional.
242
+ #
243
+ # @param names [Array<Symbol>]
244
+ # @param on [Symbol, nil] `:create` to require them only when creating
245
+ # @return [void]
246
+ def require_attributes(*names, on: nil)
247
+ raise ArgumentError, '`on:` is :create' if on && on != :create
248
+
249
+ names.each do |name|
250
+ field = fields.fetch(name) { raise ArgumentError, "#{name} is not an attribute of this schema" }
251
+ fields[name] = field.required_copy(on)
252
+ end
253
+ @permit_lists = nil
254
+ end
255
+
256
+ # The `params.permit` list for an action.
257
+ #
258
+ # @param action [Symbol, String, nil] `create` or `update`; nil
259
+ # accepts every field
260
+ # @return [Array<Symbol, Hash>]
261
+ def permit_list(action = nil)
262
+ action = action&.to_sym
263
+ (@permit_lists ||= {})[action] ||= build_permit_list(action).freeze
264
+ end
265
+
266
+ # The JSON Schema of the request body for an action.
267
+ #
268
+ # @param action [Symbol, String, nil]
269
+ # @return [Hash]
270
+ def json_schema(action = nil)
271
+ action = action&.to_sym
272
+ fields = fields_for(action)
273
+ required = fields.select { |field| field.required_on?(action) }.map { |field| field.name.to_s }
274
+
275
+ schema = { type: :object, properties: fields.to_h { |field| [field.name, property_schema(field)] } }
276
+ schema[:required] = required if required.any?
277
+ schema
278
+ end
279
+
280
+ # The component name published for an action.
281
+ #
282
+ # @param action [Symbol, String]
283
+ # @return [String]
284
+ def component_name(action)
285
+ return name if name.end_with?('Params')
286
+
287
+ "#{name.camelize}#{action.to_s.camelize}Params"
288
+ end
289
+
290
+ # Every named shape this schema reaches, nested ones included.
291
+ #
292
+ # @return [Array<RequestSchema>]
293
+ def shapes
294
+ fields.each_value.flat_map do |field|
295
+ next [] unless field.schema
296
+
297
+ nested = field.schema.shapes
298
+ shape?(field.schema) ? [field.schema, *nested] : nested
299
+ end.uniq
300
+ end
301
+
302
+ # The ISO code lists this schema's fields reach, nested ones included,
303
+ # as the components they publish.
304
+ #
305
+ # @return [Hash{String => Hash}]
306
+ def code_lists
307
+ fields.each_value.each_with_object({}) do |field, lists|
308
+ lists.merge!(field.schema.code_lists) if field.schema
309
+ next unless CODE_LISTS.key?(field.kind)
310
+
311
+ name, values = CODE_LISTS[field.kind]
312
+ lists[name] = { type: :string, enum: values.call }
313
+ end
314
+ end
315
+
316
+ def shape?(schema)
317
+ schema.name.present?
318
+ end
319
+
320
+ private
321
+
322
+ def nested_schema(type, of:, shape:, as:, &block)
323
+ return RequestSchema.resolve(shape) if shape
324
+ return RequestSchema.resolve(of) if of.is_a?(Class) || of.is_a?(RequestSchema)
325
+ return unless block
326
+
327
+ as ? RequestSchema.shape(as, &block) : RequestSchema.new(&block)
328
+ end
329
+
330
+ def add_array(name, of, nested, options)
331
+ return add(Field.new(name, :object, schema: nested, array: true, **options)) if nested
332
+
333
+ unless of.is_a?(Symbol) && KINDS.key?(of)
334
+ raise ArgumentError, "#{name}: an :array needs `of:` a scalar type or a shape, or a block of attributes"
335
+ end
336
+ # Rails permits a list of scalars, never a list of any-key hashes.
337
+ if of == :any && options.fetch(:permit, true)
338
+ raise ArgumentError, "#{name}: a list of :any values cannot be permitted; declare its items or use `permit: false`"
339
+ end
340
+
341
+ add(Field.new(name, of, array: true, **options))
342
+ end
343
+
344
+ def add(field)
345
+ raise ArgumentError, "#{field.name} is declared twice" if fields.key?(field.name)
346
+
347
+ @permit_lists = nil
348
+ fields[field.name] = field
349
+ end
350
+
351
+ def build_permit_list(action)
352
+ scalars = []
353
+ nested = []
354
+
355
+ fields_for(action).each do |field|
356
+ next unless field.permit
357
+
358
+ if field.schema
359
+ nested << { field.name => field.schema.permit_list }
360
+ elsif field.free_object?
361
+ nested << { field.name => {} }
362
+ elsif field.kind == :any
363
+ # A value of any JSON type: a scalar, a list or a hash.
364
+ scalars << field.name
365
+ nested << { field.name => [] } << { field.name => {} }
366
+ elsif field.array
367
+ nested << { field.name => [] }
368
+ else
369
+ scalars << field.name
370
+ end
371
+ end
372
+
373
+ scalars + nested
374
+ end
375
+
376
+
377
+ def fields_for(action)
378
+ fields.values.select { |field| field.accepted_on?(action) }
379
+ end
380
+
381
+ def property_schema(field)
382
+ item = item_schema(field)
383
+ description = [field.description, (OPEN_LIST_NOTE if field.open), deprecation_note(field)].compact.join(' ').presence
384
+ documentation = { description: description, example: field.example }.compact
385
+ documentation[:deprecated] = true if field.deprecated
386
+
387
+ if field.array
388
+ schema = { type: :array, items: item }.merge(documentation)
389
+ schema[:nullable] = true if field.nullable
390
+ return schema
391
+ end
392
+
393
+ # A $ref ignores the keys beside it, so a described or nullable
394
+ # reference wraps it.
395
+ if item.key?(:$ref) && (documentation.any? || field.nullable)
396
+ item = { allOf: [item] }
397
+ end
398
+
399
+ item = item.merge(documentation)
400
+ item[:nullable] = true if field.nullable
401
+ item
402
+ end
403
+
404
+ def deprecation_note(field)
405
+ "Deprecated: #{field.deprecated}" if field.deprecated
406
+ end
407
+
408
+ def item_schema(field)
409
+ return { '$ref': "#/components/schemas/#{CODE_LISTS[field.kind].first}" } if CODE_LISTS.key?(field.kind)
410
+
411
+ if field.schema
412
+ return { '$ref': "#/components/schemas/#{field.schema.name}" } if shape?(field.schema)
413
+
414
+ return field.schema.json_schema
415
+ end
416
+ return { type: :object, additionalProperties: true } if field.free_object?
417
+
418
+ schema = KINDS.fetch(field.kind).dup
419
+ schema[:format] = field.format if field.format
420
+ return schema unless field.enum
421
+ return schema.merge(enum: field.enum) unless field.open
422
+
423
+ # The same open list the serializers publish (`enum_type_name`):
424
+ # clients autocomplete the built-in values and still accept any other.
425
+ { anyOf: [schema.merge(enum: field.enum), schema] }
426
+ end
427
+ end
428
+ end
429
+ end
430
+ end
@@ -90,10 +90,14 @@ module Spree
90
90
  http_options = { open_timeout: TIMEOUT, read_timeout: TIMEOUT, verify_mode: ssl_verify_mode }
91
91
 
92
92
  # SSRF protection is disabled in development so webhooks can reach
93
- # localhost / host.docker.internal (the storefront running on the host).
94
- if Rails.env.development?
93
+ # localhost / host.docker.internal (the storefront running on the host),
94
+ # and for hosts the operator explicitly trusts
95
+ # (Spree::Api::Config.webhooks_allowed_internal_hosts).
96
+ if Rails.env.development? || Spree::WebhookEndpoint.allowed_internal_host?(@delivery.url)
95
97
  uri = URI.parse(@delivery.url)
96
- http = Net::HTTP.new(uri.host, uri.port)
98
+ # No proxy, as SsrfFilter: an egress proxy from the environment would
99
+ # carry this in-cluster (or localhost) request out of the network.
100
+ http = Net::HTTP.new(uri.hostname, uri.port, nil)
97
101
  http.use_ssl = uri.scheme == 'https'
98
102
  http_options.each { |k, v| http.send(:"#{k}=", v) }
99
103
 
@@ -75,7 +75,7 @@ Rails.application.config.after_initialize do
75
75
  # serializer this branch inherits references them, so their types still
76
76
  # have to exist in the package.
77
77
  store_nested_for_seller = %w[
78
- Address Category CustomField OptionType OptionValue Price PriceHistory Seller
78
+ Category CustomField OptionType OptionValue Seller
79
79
  ].to_set
80
80
 
81
81
  config.writer(:seller) do |c|
data/config/routes.rb CHANGED
@@ -255,6 +255,8 @@ Spree::Core::Engine.add_routes do
255
255
  # Self-service profile updates (e.g. the admin's own UI language)
256
256
  patch 'me', to: 'me#update'
257
257
 
258
+ resource :updates, only: :show
259
+
258
260
  # Store Settings
259
261
  resource :store, only: [:show, :update], controller: 'store' do
260
262
  get :data_sources
@@ -299,8 +301,8 @@ Spree::Core::Engine.add_routes do
299
301
  resources :roles, only: [:index, :show, :create, :update, :destroy]
300
302
  resources :permissions, only: [:index]
301
303
 
302
- # Direct Uploads (Active Storage)
303
- resources :direct_uploads, only: [:create]
304
+ # File uploads — see docs/plans/6.0-uploads-and-file-ownership.md
305
+ resources :files, only: [:create]
304
306
 
305
307
  # CSV Exports — see docs/plans/5.5-admin-spa-csv-export.md
306
308
  resources :exports, only: [:index, :show, :create, :destroy] do
@@ -961,7 +963,7 @@ Spree::Core::Engine.add_routes do
961
963
  # Registry data — which carriers a tracking number can be pinned to.
962
964
  resources :tracking_carriers, only: [:index]
963
965
 
964
- resources :direct_uploads, only: [:create]
966
+ resources :files, only: [:create]
965
967
 
966
968
  # CSV of what this seller sold, or of their catalogue. Created, polled
967
969
  # and downloaded — no index, since a seller has no history page to
@@ -27,6 +27,11 @@ module Spree
27
27
 
28
28
  preference :webhooks_enabled, :boolean, default: true, env: 'SPREE_WEBHOOKS_ENABLED'
29
29
  preference :webhooks_verify_ssl, :boolean, default: !Rails.env.development?, env: 'SPREE_WEBHOOKS_VERIFY_SSL'
30
+ # Hosts a webhook may reach even though they resolve to a private
31
+ # address (the SSRF guard otherwise refuses them). "host" matches that
32
+ # host; ".suffix" matches any host under it. See
33
+ # Spree::WebhookEndpoint.allowed_internal_host?.
34
+ preference :webhooks_allowed_internal_hosts, :array, default: [], env: 'SPREE_WEBHOOKS_ALLOWED_INTERNAL_HOSTS'
30
35
  end
31
36
  end
32
37
  end
@@ -18,10 +18,10 @@ 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].freeze
21
+ stock_transfer company company_membership company_invitation seller file_upload file_upload_target].freeze
22
22
 
23
23
  ADMIN_SERIALIZERS =
24
- %i[country state applied_promotion discount tax_line cart delivery_zone delivery_profile
24
+ %i[country state locale applied_promotion discount tax_line cart delivery_zone delivery_profile
25
25
  delivery_origin_group integration delivery_method_rule delivery_zone_member fee consent_record customer
26
26
  data_request newsletter_subscriber order order_group product product_type
27
27
  product_type_custom_field_definition variant price price_history custom_field custom_field_definition
@@ -38,19 +38,20 @@ module Spree
38
38
  channel order_routing_rule product_publication market delivery_method delivery_method_service
39
39
  stock_location stock_reservation delivery_rate freight_summary freight_summary_line payment_method
40
40
  credit_card store_credit store_credit_event customer_group payment_source digital_asset digital_link
41
- store setup_task api_key allowed_origin webhook_endpoint webhook_event webhook_delivery invitation
41
+ store setup_task update_status api_key allowed_origin webhook_endpoint webhook_event webhook_delivery invitation
42
42
  invitation_acceptance_link role permission export saved_report import import_row import_mapping promotion
43
43
  promotion_action promotion_rule coupon_code price_adjustment_tier price_list price_rule
44
44
  resource_translations email_template email_template_draft email_template_revision email_template_preview
45
- email_template_sample_record].freeze
45
+ email_template_sample_record file_upload file_upload_target].freeze
46
46
 
47
47
  SELLER_SERIALIZERS =
48
- %i[profile policy team_member account invitation invitation_acceptance_link product product_type
48
+ %i[address country state profile policy team_member account invitation invitation_acceptance_link product product_type
49
49
  delivery_profile delivery_method delivery_method_rule delivery_zone variant media stock_level order
50
50
  order_line_item fulfillment fulfillment_item delivery delivery_rate shipping_label return
51
51
  return_line_item exchange exchange_line_item claim claim_line_item reason package_type stock_location
52
52
  requirement_custom_field tax_identifier requirement_status import import_row import_mapping
53
- requirement_submission export transfer payout balance payment_split].freeze
53
+ requirement_submission export transfer payout balance payment_split price price_history file_upload
54
+ file_upload_target].freeze
54
55
 
55
56
  INJECTION_POINTS_WITH_DEFAULTS = {
56
57
  **STORE_SERIALIZERS.to_h { |name| [:"#{name}_serializer", "Spree::Api::V3::#{name.to_s.camelize}Serializer"] },
@@ -2,13 +2,24 @@ module Spree
2
2
  module Api
3
3
  module Middleware
4
4
  class RequestSizeLimit
5
+ # `POST /files` takes a file's bytes in the request itself, so it is
6
+ # held to the multipart upload limit instead, plus room for the other
7
+ # form fields.
8
+ FILE_UPLOAD_PATH = %r{\A/api/v3/(admin|seller)/files/?\z}
9
+ MULTIPART_ENVELOPE = 64.kilobytes
10
+
5
11
  def initialize(app, limit: nil)
6
12
  @app = app
7
13
  @limit = limit
8
14
  end
9
15
 
10
16
  def call(env)
11
- if api_request?(env) && content_length_exceeded?(env)
17
+ if file_upload?(env) && chunked?(env)
18
+ # A chunked body has no length to check here, and these bytes pass
19
+ # through the application.
20
+ body = { error: { code: 'request_too_large', message: 'Send the file with a Content-Length header' } }
21
+ [411, { 'Content-Type' => 'application/json' }, [body.to_json]]
22
+ elsif api_request?(env) && content_length_exceeded?(env)
12
23
  body = { error: { code: 'request_too_large', message: 'Request body too large' } }
13
24
  [413, { 'Content-Type' => 'application/json' }, [body.to_json]]
14
25
  else
@@ -24,12 +35,22 @@ module Spree
24
35
 
25
36
  def content_length_exceeded?(env)
26
37
  content_length = env['CONTENT_LENGTH'].to_i
27
- content_length > max_body_size
38
+ content_length > max_body_size(env)
28
39
  end
29
40
 
30
- def max_body_size
41
+ def max_body_size(env)
42
+ return Spree::Config[:max_multipart_upload_size] + MULTIPART_ENVELOPE if file_upload?(env)
43
+
31
44
  @limit || Spree::Api::Config[:max_request_body_size]
32
45
  end
46
+
47
+ def chunked?(env)
48
+ env['CONTENT_LENGTH'].nil? && env['HTTP_TRANSFER_ENCODING'].to_s.include?('chunked')
49
+ end
50
+
51
+ def file_upload?(env)
52
+ env['REQUEST_METHOD'] == 'POST' && FILE_UPLOAD_PATH.match?(env['PATH_INFO'])
53
+ end
33
54
  end
34
55
  end
35
56
  end