spree_api 5.3.4 → 5.4.0.beta2

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 (335) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +71 -33
  3. data/Rakefile +25 -0
  4. data/app/controllers/concerns/spree/api/v3/api_key_authentication.rb +76 -0
  5. data/app/controllers/concerns/spree/api/v3/error_handler.rb +261 -0
  6. data/app/controllers/concerns/spree/api/v3/http_caching.rb +90 -0
  7. data/app/controllers/concerns/spree/api/v3/jwt_authentication.rb +118 -0
  8. data/app/controllers/concerns/spree/api/v3/locale_and_currency.rb +185 -0
  9. data/app/controllers/concerns/spree/api/v3/order_concern.rb +51 -0
  10. data/app/controllers/concerns/spree/api/v3/resource_serializer.rb +41 -0
  11. data/app/controllers/concerns/spree/api/v3/security_headers.rb +22 -0
  12. data/app/controllers/spree/api/v3/base_controller.rb +45 -0
  13. data/app/controllers/spree/api/v3/resource_controller.rb +210 -0
  14. data/app/controllers/spree/api/v3/store/auth_controller.rb +146 -0
  15. data/app/controllers/spree/api/v3/store/base_controller.rb +23 -0
  16. data/app/controllers/spree/api/v3/store/cart_controller.rb +93 -0
  17. data/app/controllers/spree/api/v3/store/countries_controller.rb +31 -0
  18. data/app/controllers/spree/api/v3/store/currencies_controller.rb +18 -0
  19. data/app/controllers/spree/api/v3/store/customer/account_controller.rb +38 -0
  20. data/app/controllers/spree/api/v3/store/customer/addresses_controller.rb +85 -0
  21. data/app/controllers/spree/api/v3/store/customer/credit_cards_controller.rb +31 -0
  22. data/app/controllers/spree/api/v3/store/customer/gift_cards_controller.rb +36 -0
  23. data/app/controllers/spree/api/v3/store/customer/orders_controller.rb +35 -0
  24. data/app/controllers/spree/api/v3/store/customer/payment_setup_sessions_controller.rb +83 -0
  25. data/app/controllers/spree/api/v3/store/digitals_controller.rb +37 -0
  26. data/app/controllers/spree/api/v3/store/locales_controller.rb +24 -0
  27. data/app/controllers/spree/api/v3/store/orders/coupon_codes_controller.rb +68 -0
  28. data/app/controllers/spree/api/v3/store/orders/line_items_controller.rb +93 -0
  29. data/app/controllers/spree/api/v3/store/orders/payment_methods_controller.rb +43 -0
  30. data/app/controllers/spree/api/v3/store/orders/payment_sessions_controller.rb +96 -0
  31. data/app/controllers/spree/api/v3/store/orders/payments_controller.rb +45 -0
  32. data/app/controllers/spree/api/v3/store/orders/shipments_controller.rb +53 -0
  33. data/app/controllers/spree/api/v3/store/orders/store_credits_controller.rb +42 -0
  34. data/app/controllers/spree/api/v3/store/orders_controller.rb +127 -0
  35. data/app/controllers/spree/api/v3/store/products/filters_controller.rb +38 -0
  36. data/app/controllers/spree/api/v3/store/products_controller.rb +74 -0
  37. data/app/controllers/spree/api/v3/store/resource_controller.rb +12 -0
  38. data/app/controllers/spree/api/v3/store/stores_controller.rb +26 -0
  39. data/app/controllers/spree/api/v3/store/taxonomies_controller.rb +19 -0
  40. data/app/controllers/spree/api/v3/store/taxons/products_controller.rb +37 -0
  41. data/app/controllers/spree/api/v3/store/taxons_controller.rb +34 -0
  42. data/app/controllers/spree/api/v3/store/wishlist_items_controller.rb +33 -0
  43. data/app/controllers/spree/api/v3/store/wishlists_controller.rb +41 -0
  44. data/app/jobs/spree/api_keys/mark_as_used.rb +15 -0
  45. data/app/serializers/spree/api/v3/address_serializer.rb +22 -0
  46. data/app/serializers/spree/api/v3/admin/customer_serializer.rb +43 -0
  47. data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +17 -0
  48. data/app/serializers/spree/api/v3/admin/metafield_serializer.rb +15 -0
  49. data/app/serializers/spree/api/v3/admin/order_serializer.rb +46 -0
  50. data/app/serializers/spree/api/v3/admin/price_serializer.rb +21 -0
  51. data/app/serializers/spree/api/v3/admin/product_serializer.rb +47 -0
  52. data/app/serializers/spree/api/v3/admin/taxon_serializer.rb +20 -0
  53. data/app/serializers/spree/api/v3/admin/taxonomy_serializer.rb +15 -0
  54. data/app/serializers/spree/api/v3/admin/variant_serializer.rb +44 -0
  55. data/app/serializers/spree/api/v3/asset_serializer.rb +20 -0
  56. data/app/serializers/spree/api/v3/base_serializer.rb +98 -0
  57. data/app/serializers/spree/api/v3/country_serializer.rb +35 -0
  58. data/app/serializers/spree/api/v3/credit_card_serializer.rb +12 -0
  59. data/app/serializers/spree/api/v3/currency_serializer.rb +14 -0
  60. data/app/serializers/spree/api/v3/customer_return_serializer.rb +17 -0
  61. data/app/serializers/spree/api/v3/customer_serializer.rb +19 -0
  62. data/app/serializers/spree/api/v3/digital_link_serializer.rb +30 -0
  63. data/app/serializers/spree/api/v3/digital_serializer.rb +17 -0
  64. data/app/serializers/spree/api/v3/export_serializer.rb +19 -0
  65. data/app/serializers/spree/api/v3/gift_card_batch_serializer.rb +28 -0
  66. data/app/serializers/spree/api/v3/gift_card_serializer.rb +78 -0
  67. data/app/serializers/spree/api/v3/image_serializer.rb +43 -0
  68. data/app/serializers/spree/api/v3/import_row_serializer.rb +24 -0
  69. data/app/serializers/spree/api/v3/import_serializer.rb +32 -0
  70. data/app/serializers/spree/api/v3/invitation_serializer.rb +47 -0
  71. data/app/serializers/spree/api/v3/line_item_serializer.rb +48 -0
  72. data/app/serializers/spree/api/v3/locale_serializer.rb +14 -0
  73. data/app/serializers/spree/{v2/storefront → api/v3}/metafield_serializer.rb +5 -3
  74. data/app/serializers/spree/api/v3/newsletter_subscriber_serializer.rb +27 -0
  75. data/app/serializers/spree/api/v3/option_type_serializer.rb +11 -0
  76. data/app/serializers/spree/api/v3/option_value_serializer.rb +24 -0
  77. data/app/serializers/spree/api/v3/order_promotion_serializer.rb +16 -0
  78. data/app/serializers/spree/api/v3/order_serializer.rb +39 -0
  79. data/app/serializers/spree/api/v3/payment_method_serializer.rb +15 -0
  80. data/app/serializers/spree/api/v3/payment_serializer.rb +54 -0
  81. data/app/serializers/spree/api/v3/payment_session_serializer.rb +32 -0
  82. data/app/serializers/spree/api/v3/payment_setup_session_serializer.rb +33 -0
  83. data/app/serializers/spree/api/v3/payment_source_serializer.rb +13 -0
  84. data/app/serializers/spree/api/v3/price_serializer.rb +34 -0
  85. data/app/serializers/spree/api/v3/product_serializer.rb +108 -0
  86. data/app/serializers/spree/api/v3/promotion_serializer.rb +28 -0
  87. data/app/serializers/spree/api/v3/refund_serializer.rb +31 -0
  88. data/app/serializers/spree/api/v3/reimbursement_serializer.rb +28 -0
  89. data/app/serializers/spree/api/v3/report_serializer.rb +29 -0
  90. data/app/serializers/spree/api/v3/return_authorization_serializer.rb +31 -0
  91. data/app/serializers/spree/api/v3/return_item_serializer.rb +53 -0
  92. data/app/serializers/spree/api/v3/shipment_serializer.rb +19 -0
  93. data/app/serializers/spree/api/v3/shipping_method_serializer.rb +11 -0
  94. data/app/serializers/spree/api/v3/shipping_rate_serializer.rb +26 -0
  95. data/app/serializers/spree/api/v3/state_serializer.rb +14 -0
  96. data/app/serializers/spree/api/v3/stock_item_serializer.rb +23 -0
  97. data/app/serializers/spree/api/v3/stock_location_serializer.rb +18 -0
  98. data/app/serializers/spree/api/v3/stock_movement_serializer.rb +24 -0
  99. data/app/serializers/spree/api/v3/stock_transfer_serializer.rb +25 -0
  100. data/app/serializers/spree/api/v3/store_credit_serializer.rb +37 -0
  101. data/app/serializers/spree/api/v3/store_serializer.rb +38 -0
  102. data/app/serializers/spree/api/v3/taxon_serializer.rb +78 -0
  103. data/app/serializers/spree/api/v3/taxonomy_serializer.rb +33 -0
  104. data/app/serializers/spree/api/v3/variant_serializer.rb +86 -0
  105. data/app/serializers/spree/api/v3/wished_item_serializer.rb +22 -0
  106. data/app/serializers/spree/api/v3/wishlist_serializer.rb +25 -0
  107. data/app/services/spree/api/v3/filters_aggregator.rb +156 -0
  108. data/app/services/spree/api/v3/orders/update.rb +105 -0
  109. data/config/initializers/alba.rb +5 -0
  110. data/config/initializers/pagy.rb +10 -0
  111. data/config/initializers/typelizer.rb +25 -0
  112. data/config/locales/en.yml +1 -0
  113. data/config/routes.rb +63 -195
  114. data/lib/spree/api/configuration.rb +12 -0
  115. data/lib/spree/api/dependencies.rb +70 -7
  116. data/lib/spree/api/engine.rb +3 -3
  117. data/lib/spree/api/middleware/request_size_limit.rb +36 -0
  118. data/lib/spree/api/openapi/schema_helper.rb +121 -0
  119. data/lib/spree/api/testing_support/factories.rb +1 -3
  120. data/lib/spree/api/testing_support/v3/base.rb +121 -0
  121. data/lib/spree/api.rb +7 -4
  122. metadata +133 -264
  123. data/LICENSE.md +0 -57
  124. data/app/controllers/concerns/spree/api/v2/caching.rb +0 -40
  125. data/app/controllers/concerns/spree/api/v2/coupon_codes_helper.rb +0 -29
  126. data/app/controllers/concerns/spree/api/v2/number_resource.rb +0 -11
  127. data/app/controllers/concerns/spree/api/v2/platform/nested_set_reposition_concern.rb +0 -37
  128. data/app/controllers/concerns/spree/api/v2/platform/promotion_calculator_params.rb +0 -17
  129. data/app/controllers/concerns/spree/api/v2/platform/promotion_rule_params.rb +0 -16
  130. data/app/controllers/concerns/spree/api/v2/product_list_includes.rb +0 -21
  131. data/app/controllers/concerns/spree/api/v2/storefront/metadata_controller_concern.rb +0 -18
  132. data/app/controllers/concerns/spree/api/v2/storefront/order_concern.rb +0 -49
  133. data/app/controllers/spree/api/v2/base_controller.rb +0 -233
  134. data/app/controllers/spree/api/v2/data_feeds/google_controller.rb +0 -24
  135. data/app/controllers/spree/api/v2/platform/addresses_controller.rb +0 -23
  136. data/app/controllers/spree/api/v2/platform/adjustments_controller.rb +0 -23
  137. data/app/controllers/spree/api/v2/platform/classifications_controller.rb +0 -26
  138. data/app/controllers/spree/api/v2/platform/countries_controller.rb +0 -23
  139. data/app/controllers/spree/api/v2/platform/data_feeds_controller.rb +0 -19
  140. data/app/controllers/spree/api/v2/platform/digital_links_controller.rb +0 -29
  141. data/app/controllers/spree/api/v2/platform/digitals_controller.rb +0 -23
  142. data/app/controllers/spree/api/v2/platform/gift_cards_controller.rb +0 -23
  143. data/app/controllers/spree/api/v2/platform/line_items_controller.rb +0 -63
  144. data/app/controllers/spree/api/v2/platform/option_types_controller.rb +0 -19
  145. data/app/controllers/spree/api/v2/platform/option_values_controller.rb +0 -23
  146. data/app/controllers/spree/api/v2/platform/orders_controller.rb +0 -167
  147. data/app/controllers/spree/api/v2/platform/payment_methods_controller.rb +0 -31
  148. data/app/controllers/spree/api/v2/platform/payments_controller.rb +0 -21
  149. data/app/controllers/spree/api/v2/platform/products_controller.rb +0 -41
  150. data/app/controllers/spree/api/v2/platform/promotion_actions_controller.rb +0 -34
  151. data/app/controllers/spree/api/v2/platform/promotion_categories_controller.rb +0 -23
  152. data/app/controllers/spree/api/v2/platform/promotion_rules_controller.rb +0 -29
  153. data/app/controllers/spree/api/v2/platform/promotions_controller.rb +0 -35
  154. data/app/controllers/spree/api/v2/platform/resource_controller.rb +0 -154
  155. data/app/controllers/spree/api/v2/platform/roles_controller.rb +0 -19
  156. data/app/controllers/spree/api/v2/platform/shipments_controller.rb +0 -147
  157. data/app/controllers/spree/api/v2/platform/shipping_categories_controller.rb +0 -19
  158. data/app/controllers/spree/api/v2/platform/shipping_methods_controller.rb +0 -28
  159. data/app/controllers/spree/api/v2/platform/states_controller.rb +0 -23
  160. data/app/controllers/spree/api/v2/platform/stock_items_controller.rb +0 -23
  161. data/app/controllers/spree/api/v2/platform/stock_locations_controller.rb +0 -23
  162. data/app/controllers/spree/api/v2/platform/store_credit_categories_controller.rb +0 -19
  163. data/app/controllers/spree/api/v2/platform/store_credit_types_controller.rb +0 -19
  164. data/app/controllers/spree/api/v2/platform/store_credits_controller.rb +0 -23
  165. data/app/controllers/spree/api/v2/platform/tax_categories_controller.rb +0 -23
  166. data/app/controllers/spree/api/v2/platform/tax_rates_controller.rb +0 -27
  167. data/app/controllers/spree/api/v2/platform/taxonomies_controller.rb +0 -23
  168. data/app/controllers/spree/api/v2/platform/taxons_controller.rb +0 -59
  169. data/app/controllers/spree/api/v2/platform/users_controller.rb +0 -37
  170. data/app/controllers/spree/api/v2/platform/variants_controller.rb +0 -23
  171. data/app/controllers/spree/api/v2/platform/wished_items_controller.rb +0 -23
  172. data/app/controllers/spree/api/v2/platform/wishlists_controller.rb +0 -23
  173. data/app/controllers/spree/api/v2/platform/zones_controller.rb +0 -23
  174. data/app/controllers/spree/api/v2/resource_controller.rb +0 -79
  175. data/app/controllers/spree/api/v2/storefront/account/addresses_controller.rb +0 -77
  176. data/app/controllers/spree/api/v2/storefront/account/credit_cards_controller.rb +0 -52
  177. data/app/controllers/spree/api/v2/storefront/account/orders_controller.rb +0 -50
  178. data/app/controllers/spree/api/v2/storefront/account_controller.rb +0 -52
  179. data/app/controllers/spree/api/v2/storefront/cart_controller.rb +0 -246
  180. data/app/controllers/spree/api/v2/storefront/checkout_controller.rb +0 -183
  181. data/app/controllers/spree/api/v2/storefront/countries_controller.rb +0 -57
  182. data/app/controllers/spree/api/v2/storefront/digitals_controller.rb +0 -58
  183. data/app/controllers/spree/api/v2/storefront/order_status_controller.rb +0 -34
  184. data/app/controllers/spree/api/v2/storefront/policies_controller.rb +0 -31
  185. data/app/controllers/spree/api/v2/storefront/post_categories_controller.rb +0 -35
  186. data/app/controllers/spree/api/v2/storefront/posts_controller.rb +0 -51
  187. data/app/controllers/spree/api/v2/storefront/products_controller.rb +0 -66
  188. data/app/controllers/spree/api/v2/storefront/stores_controller.rb +0 -27
  189. data/app/controllers/spree/api/v2/storefront/taxons_controller.rb +0 -51
  190. data/app/controllers/spree/api/v2/storefront/variants_controller.rb +0 -41
  191. data/app/controllers/spree/api/v2/storefront/wishlists_controller.rb +0 -214
  192. data/app/helpers/spree/api/v2/collection_options_helpers.rb +0 -46
  193. data/app/helpers/spree/api/v2/display_money_helper.rb +0 -63
  194. data/app/helpers/spree/api/v2/store_media_serializer_images_concern.rb +0 -30
  195. data/app/models/concerns/spree/user_api_authentication.rb +0 -19
  196. data/app/models/concerns/spree/user_api_methods.rb +0 -7
  197. data/app/models/spree/oauth_access_grant.rb +0 -7
  198. data/app/models/spree/oauth_access_token.rb +0 -7
  199. data/app/models/spree/oauth_application.rb +0 -22
  200. data/app/paginators/spree/api/paginate.rb +0 -68
  201. data/app/presenters/spree/api/products/filters_presenter.rb +0 -39
  202. data/app/serializers/concerns/spree/api/v2/image_transformation_concern.rb +0 -15
  203. data/app/serializers/concerns/spree/api/v2/public_metafields_concern.rb +0 -15
  204. data/app/serializers/concerns/spree/api/v2/resource_serializer_concern.rb +0 -42
  205. data/app/serializers/concerns/spree/api/v2/taxon_image_transformation_concern.rb +0 -15
  206. data/app/serializers/spree/api/v2/base_serializer.rb +0 -43
  207. data/app/serializers/spree/api/v2/platform/address_serializer.rb +0 -15
  208. data/app/serializers/spree/api/v2/platform/adjustment_serializer.rb +0 -20
  209. data/app/serializers/spree/api/v2/platform/admin_user_serializer.rb +0 -11
  210. data/app/serializers/spree/api/v2/platform/asset_serializer.rb +0 -13
  211. data/app/serializers/spree/api/v2/platform/base_serializer.rb +0 -10
  212. data/app/serializers/spree/api/v2/platform/calculator_serializer.rb +0 -17
  213. data/app/serializers/spree/api/v2/platform/classification_serializer.rb +0 -14
  214. data/app/serializers/spree/api/v2/platform/country_serializer.rb +0 -13
  215. data/app/serializers/spree/api/v2/platform/credit_card_serializer.rb +0 -14
  216. data/app/serializers/spree/api/v2/platform/customer_return_serializer.rb +0 -17
  217. data/app/serializers/spree/api/v2/platform/data_feed_serializer.rb +0 -13
  218. data/app/serializers/spree/api/v2/platform/digital_link_serializer.rb +0 -16
  219. data/app/serializers/spree/api/v2/platform/digital_serializer.rb +0 -29
  220. data/app/serializers/spree/api/v2/platform/gift_card_serializer.rb +0 -17
  221. data/app/serializers/spree/api/v2/platform/image_serializer.rb +0 -17
  222. data/app/serializers/spree/api/v2/platform/inventory_unit_serializer.rb +0 -19
  223. data/app/serializers/spree/api/v2/platform/line_item_serializer.rb +0 -19
  224. data/app/serializers/spree/api/v2/platform/log_entry_serializer.rb +0 -13
  225. data/app/serializers/spree/api/v2/platform/metafield_serializer.rb +0 -21
  226. data/app/serializers/spree/api/v2/platform/option_type_serializer.rb +0 -13
  227. data/app/serializers/spree/api/v2/platform/option_value_serializer.rb +0 -13
  228. data/app/serializers/spree/api/v2/platform/order_promotion_serializer.rb +0 -14
  229. data/app/serializers/spree/api/v2/platform/order_serializer.rb +0 -31
  230. data/app/serializers/spree/api/v2/platform/payment_capture_event_serializer.rb +0 -13
  231. data/app/serializers/spree/api/v2/platform/payment_method_serializer.rb +0 -18
  232. data/app/serializers/spree/api/v2/platform/payment_serializer.rb +0 -22
  233. data/app/serializers/spree/api/v2/platform/payment_source_serializer.rb +0 -12
  234. data/app/serializers/spree/api/v2/platform/price_serializer.rb +0 -19
  235. data/app/serializers/spree/api/v2/platform/product_property_serializer.rb +0 -11
  236. data/app/serializers/spree/api/v2/platform/product_serializer.rb +0 -90
  237. data/app/serializers/spree/api/v2/platform/promotion_action_line_item_serializer.rb +0 -14
  238. data/app/serializers/spree/api/v2/platform/promotion_action_serializer.rb +0 -19
  239. data/app/serializers/spree/api/v2/platform/promotion_category_serializer.rb +0 -13
  240. data/app/serializers/spree/api/v2/platform/promotion_rule_serializer.rb +0 -21
  241. data/app/serializers/spree/api/v2/platform/promotion_serializer.rb +0 -17
  242. data/app/serializers/spree/api/v2/platform/property_serializer.rb +0 -11
  243. data/app/serializers/spree/api/v2/platform/prototype_serializer.rb +0 -15
  244. data/app/serializers/spree/api/v2/platform/refund_reason_serializer.rb +0 -11
  245. data/app/serializers/spree/api/v2/platform/refund_serializer.rb +0 -17
  246. data/app/serializers/spree/api/v2/platform/reimbursement_credit_serializer.rb +0 -10
  247. data/app/serializers/spree/api/v2/platform/reimbursement_serializer.rb +0 -19
  248. data/app/serializers/spree/api/v2/platform/reimbursement_type_serializer.rb +0 -11
  249. data/app/serializers/spree/api/v2/platform/return_authorization_reason_serializer.rb +0 -11
  250. data/app/serializers/spree/api/v2/platform/return_authorization_serializer.rb +0 -17
  251. data/app/serializers/spree/api/v2/platform/return_item_serializer.rb +0 -16
  252. data/app/serializers/spree/api/v2/platform/role_serializer.rb +0 -11
  253. data/app/serializers/spree/api/v2/platform/shipment_serializer.rb +0 -22
  254. data/app/serializers/spree/api/v2/platform/shipping_category_serializer.rb +0 -11
  255. data/app/serializers/spree/api/v2/platform/shipping_method_serializer.rb +0 -16
  256. data/app/serializers/spree/api/v2/platform/shipping_rate_serializer.rb +0 -15
  257. data/app/serializers/spree/api/v2/platform/state_change_serializer.rb +0 -13
  258. data/app/serializers/spree/api/v2/platform/state_serializer.rb +0 -13
  259. data/app/serializers/spree/api/v2/platform/stock_item_serializer.rb +0 -18
  260. data/app/serializers/spree/api/v2/platform/stock_location_serializer.rb +0 -14
  261. data/app/serializers/spree/api/v2/platform/stock_movement_serializer.rb +0 -11
  262. data/app/serializers/spree/api/v2/platform/stock_transfer_serializer.rb +0 -15
  263. data/app/serializers/spree/api/v2/platform/store_credit_category_serializer.rb +0 -12
  264. data/app/serializers/spree/api/v2/platform/store_credit_event_serializer.rb +0 -14
  265. data/app/serializers/spree/api/v2/platform/store_credit_serializer.rb +0 -18
  266. data/app/serializers/spree/api/v2/platform/store_credit_type_serializer.rb +0 -12
  267. data/app/serializers/spree/api/v2/platform/store_serializer.rb +0 -14
  268. data/app/serializers/spree/api/v2/platform/tax_category_serializer.rb +0 -13
  269. data/app/serializers/spree/api/v2/platform/tax_rate_serializer.rb +0 -14
  270. data/app/serializers/spree/api/v2/platform/taxon_image_serializer.rb +0 -15
  271. data/app/serializers/spree/api/v2/platform/taxon_serializer.rb +0 -47
  272. data/app/serializers/spree/api/v2/platform/taxonomy_serializer.rb +0 -14
  273. data/app/serializers/spree/api/v2/platform/user_serializer.rb +0 -37
  274. data/app/serializers/spree/api/v2/platform/variant_serializer.rb +0 -66
  275. data/app/serializers/spree/api/v2/platform/wished_item_serializer.rb +0 -29
  276. data/app/serializers/spree/api/v2/platform/wishlist_serializer.rb +0 -19
  277. data/app/serializers/spree/api/v2/platform/zone_member_serializer.rb +0 -13
  278. data/app/serializers/spree/api/v2/platform/zone_serializer.rb +0 -13
  279. data/app/serializers/spree/v2/storefront/address_serializer.rb +0 -22
  280. data/app/serializers/spree/v2/storefront/base_serializer.rb +0 -10
  281. data/app/serializers/spree/v2/storefront/cart_serializer.rb +0 -67
  282. data/app/serializers/spree/v2/storefront/country_serializer.rb +0 -24
  283. data/app/serializers/spree/v2/storefront/credit_card_serializer.rb +0 -15
  284. data/app/serializers/spree/v2/storefront/digital_link_serializer.rb +0 -11
  285. data/app/serializers/spree/v2/storefront/estimated_shipping_rate_serializer.rb +0 -35
  286. data/app/serializers/spree/v2/storefront/gift_card_serializer.rb +0 -16
  287. data/app/serializers/spree/v2/storefront/image_serializer.rb +0 -13
  288. data/app/serializers/spree/v2/storefront/line_item_serializer.rb +0 -23
  289. data/app/serializers/spree/v2/storefront/option_type_serializer.rb +0 -15
  290. data/app/serializers/spree/v2/storefront/option_value_serializer.rb +0 -13
  291. data/app/serializers/spree/v2/storefront/order_promotion_serializer.rb +0 -14
  292. data/app/serializers/spree/v2/storefront/order_serializer.rb +0 -11
  293. data/app/serializers/spree/v2/storefront/payment_method_serializer.rb +0 -17
  294. data/app/serializers/spree/v2/storefront/payment_serializer.rb +0 -20
  295. data/app/serializers/spree/v2/storefront/payment_source_serializer.rb +0 -12
  296. data/app/serializers/spree/v2/storefront/policy_serializer.rb +0 -19
  297. data/app/serializers/spree/v2/storefront/post_category_serializer.rb +0 -23
  298. data/app/serializers/spree/v2/storefront/post_serializer.rb +0 -51
  299. data/app/serializers/spree/v2/storefront/product_property_serializer.rb +0 -19
  300. data/app/serializers/spree/v2/storefront/product_serializer.rb +0 -92
  301. data/app/serializers/spree/v2/storefront/shipment_serializer.rb +0 -26
  302. data/app/serializers/spree/v2/storefront/shipping_category_serializer.rb +0 -10
  303. data/app/serializers/spree/v2/storefront/shipping_method_serializer.rb +0 -12
  304. data/app/serializers/spree/v2/storefront/shipping_rate_serializer.rb +0 -26
  305. data/app/serializers/spree/v2/storefront/state_serializer.rb +0 -11
  306. data/app/serializers/spree/v2/storefront/stock_location_serializer.rb +0 -11
  307. data/app/serializers/spree/v2/storefront/store_credit_category_serializer.rb +0 -11
  308. data/app/serializers/spree/v2/storefront/store_credit_event_serializer.rb +0 -15
  309. data/app/serializers/spree/v2/storefront/store_credit_serializer.rb +0 -19
  310. data/app/serializers/spree/v2/storefront/store_credit_type_serializer.rb +0 -11
  311. data/app/serializers/spree/v2/storefront/store_serializer.rb +0 -18
  312. data/app/serializers/spree/v2/storefront/taxon_image_serializer.rb +0 -13
  313. data/app/serializers/spree/v2/storefront/taxon_serializer.rb +0 -55
  314. data/app/serializers/spree/v2/storefront/taxonomy_serializer.rb +0 -13
  315. data/app/serializers/spree/v2/storefront/user_serializer.rb +0 -33
  316. data/app/serializers/spree/v2/storefront/variant_serializer.rb +0 -58
  317. data/app/serializers/spree/v2/storefront/wished_item_serializer.rb +0 -29
  318. data/app/serializers/spree/v2/storefront/wishlist_serializer.rb +0 -17
  319. data/config/initializers/doorkeeper.rb +0 -50
  320. data/config/initializers/json_api_mime_types.rb +0 -8
  321. data/config/initializers/user_class_extensions.rb +0 -7
  322. data/db/migrate/20100107141738_add_api_key_to_spree_users.rb +0 -7
  323. data/db/migrate/20120411123334_resize_api_key_field.rb +0 -7
  324. data/db/migrate/20120530054546_rename_api_key_to_spree_api_key.rb +0 -7
  325. data/db/migrate/20131017162334_add_index_to_user_spree_api_key.rb +0 -7
  326. data/db/migrate/20180320110726_create_doorkeeper_tables.rb +0 -69
  327. data/db/migrate/20210727102516_change_integer_id_columns_type.rb +0 -9
  328. data/db/migrate/20210919183228_enable_polymorphic_resource_owner.rb +0 -21
  329. data/lib/generators/spree/api/install/install_generator.rb +0 -24
  330. data/lib/spree/api/testing_support/factories/oauth_application_factory.rb +0 -6
  331. data/lib/spree/api/testing_support/serializers.rb +0 -15
  332. data/lib/spree/api/testing_support/v2/base.rb +0 -13
  333. data/lib/spree/api/testing_support/v2/current_order.rb +0 -116
  334. data/lib/spree/api/testing_support/v2/platform_contexts.rb +0 -272
  335. data/lib/spree/api/testing_support/v2/serializers_params.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8103cf9477d203a505f580a267a96ad940babc92b4dd09814d86dba03e330300
4
- data.tar.gz: 98f44fdc93e9c4cad74c52ccb58b2b82d62064c53dc9f7fa9d963ca78ed37b41
3
+ metadata.gz: 5e2afbf0958129824507229d5994a8cf37076727f77f7c8a9cebade0a59c237b
4
+ data.tar.gz: ca64a94e5e43b4bbc4e0bbc02123102c0e1c49f03381f41d9021596e4b2fe013
5
5
  SHA512:
6
- metadata.gz: 8b63700e0591ed1aa8f55837aec0f9492016f7cd35ccf3b202e8dcb764a35d82188cb7fc2cac64ff99c1f0a07b05142b59c48be48b01629810dfe4a2bd0cecaa
7
- data.tar.gz: 5d8809fcfe387e374449bb285df5c10df363a2e972e8b3c0fba5c2d7644841e437e70b166bd981765801390c706361bdcb2423c2922a0c7e588735a5d9adf2f9
6
+ metadata.gz: 56393e0ac09195448b830d59283804af1634734c656961fbe07ec21b1ae7f752cdc2a6c0cabd19f5a48a26a548d6ae616cfc5744b823734f3928369d4e201588
7
+ data.tar.gz: 21477917637463a37f0c3085c497974adc26aa3228b7b5665cc0f3da5051f459c5681b853e739fe44f6e4171d0be71a702baadf141dd450a437ffe1ce15cc6fd
data/README.md CHANGED
@@ -8,55 +8,76 @@ Spree API provides RESTful API endpoints for building custom storefronts, mobile
8
8
 
9
9
  This gem includes:
10
10
 
11
- - **Storefront API** - Customer-facing endpoints for cart, checkout, products, and accounts
12
- - **Platform API** - Administrative endpoints for managing orders, products, and store settings
11
+ - **Store API v3** - Customer-facing endpoints for cart, checkout, products, and accounts
12
+ - **Admin API v3** - Administrative endpoints for managing orders, products, and store settings
13
13
  - **Webhooks** - Event-driven notifications to external systems
14
- - **OAuth2 Authentication** - Token-based authentication via Doorkeeper
15
- - **JSONAPI Serializers** - Standardized API responses
14
+ - **API Key Authentication** - Secure token-based authentication with scopes
15
+ - **Alba Serializers** - Fast, flexible JSON serialization with TypeScript type generation
16
16
 
17
17
  ## Installation
18
18
 
19
19
  This gem is included in every Spree installation. No additional steps are required.
20
20
 
21
- ## API Endpoints
21
+ ## API v3 Endpoints
22
22
 
23
- ### Storefront API (v2)
23
+ ### Store API
24
24
 
25
- The Storefront API is designed for building custom frontends:
25
+ The Store API is designed for building custom storefronts:
26
26
 
27
27
  ```
28
- GET /api/v2/storefront/products
29
- GET /api/v2/storefront/products/:id
30
- GET /api/v2/storefront/taxons
31
- POST /api/v2/storefront/cart
32
- PATCH /api/v2/storefront/cart/add_item
33
- PATCH /api/v2/storefront/checkout
34
- POST /api/v2/storefront/account
28
+ GET /api/v3/products
29
+ GET /api/v3/products/:id
30
+ GET /api/v3/taxons
31
+ GET /api/v3/taxonomies
32
+ POST /api/v3/cart
33
+ PATCH /api/v3/cart/add_item
34
+ PATCH /api/v3/checkout
35
+ GET /api/v3/account
35
36
  ```
36
37
 
37
- ### Platform API
38
+ ### Admin API
38
39
 
39
- The Platform API provides administrative access:
40
+ The Admin API provides full administrative access:
40
41
 
41
42
  ```
42
- GET /api/v2/platform/orders
43
- POST /api/v2/platform/products
44
- PATCH /api/v2/platform/variants/:id
45
- DELETE /api/v2/platform/line_items/:id
43
+ GET /api/v3/admin/orders
44
+ POST /api/v3/admin/products
45
+ PATCH /api/v3/admin/variants/:id
46
+ DELETE /api/v3/admin/line_items/:id
46
47
  ```
47
48
 
48
49
  ## Authentication
49
50
 
50
- ### OAuth2 Token Authentication
51
+ ### API Key Authentication
52
+
53
+ Create API keys with appropriate scopes:
54
+
55
+ ```ruby
56
+ # Store API key (customer-facing)
57
+ api_key = Spree::ApiKey.create!(
58
+ name: 'My Storefront',
59
+ scope: 'store',
60
+ store: current_store
61
+ )
62
+
63
+ # Admin API key (full access)
64
+ admin_key = Spree::ApiKey.create!(
65
+ name: 'Admin Integration',
66
+ scope: 'admin',
67
+ store: current_store
68
+ )
69
+ ```
70
+
71
+ Use the API key in requests:
51
72
 
52
73
  ```bash
53
- # Request access token
54
- curl -X POST https://your-store.com/spree_oauth/token \
55
- -d "grant_type=password&username=user@example.com&password=secret"
74
+ # Store API
75
+ curl -H "Authorization: Bearer spree_pk_xxx" \
76
+ https://your-store.com/api/v3/products
56
77
 
57
- # Use token in requests
58
- curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
59
- https://your-store.com/api/v2/storefront/account
78
+ # Admin API
79
+ curl -H "Authorization: Bearer spree_sk_xxx" \
80
+ https://your-store.com/api/v3/admin/orders
60
81
  ```
61
82
 
62
83
  ### Guest Cart Token
@@ -65,25 +86,43 @@ For guest checkout, use the `X-Spree-Order-Token` header:
65
86
 
66
87
  ```bash
67
88
  curl -H "X-Spree-Order-Token: ORDER_TOKEN" \
68
- https://your-store.com/api/v2/storefront/cart
89
+ https://your-store.com/api/v3/cart
69
90
  ```
70
91
 
71
92
  ## Serializers
72
93
 
73
- API responses use JSONAPI format. Customize serializers by creating your own:
94
+ API v3 uses Alba serializers for fast JSON serialization. Serializers are organized by scope:
95
+
96
+ - `app/serializers/spree/api/v3/` - Store API serializers
97
+ - `app/serializers/spree/api/v3/admin/` - Admin API serializers
98
+
99
+ Customize serializers by creating your own:
74
100
 
75
101
  ```ruby
76
102
  # app/serializers/my_app/product_serializer.rb
77
103
  module MyApp
78
- class ProductSerializer < Spree::V2::Storefront::ProductSerializer
104
+ class ProductSerializer < Spree::Api::V3::ProductSerializer
79
105
  attribute :custom_field
80
106
  end
81
107
  end
82
108
 
83
109
  # Configure in initializer
84
- Spree.api.storefront_product_serializer = 'MyApp::ProductSerializer'
110
+ Spree.api.product_serializer = 'MyApp::ProductSerializer'
85
111
  ```
86
112
 
113
+ ## TypeScript Types
114
+
115
+ TypeScript types are automatically generated from serializers using [typelizer](https://github.com/skryukov/typelizer):
116
+
117
+ ```bash
118
+ # Generate TypeScript types
119
+ bundle exec rake typelizer:generate
120
+ ```
121
+
122
+ Types are output to `sdk/src/types/generated/` with naming:
123
+ - Store types: `StoreProduct`, `StoreOrder`, etc.
124
+ - Admin types: `AdminProduct`, `AdminOrder`, etc.
125
+
87
126
  ## Testing
88
127
 
89
128
  ```bash
@@ -94,6 +133,5 @@ bundle exec rspec
94
133
 
95
134
  ## Documentation
96
135
 
97
- - [Storefront API Reference](https://docs.spreecommerce.org/api/storefront)
98
- - [Platform API Reference](https://docs.spreecommerce.org/api/platform)
136
+ - [API Reference](https://docs.spreecommerce.org/api)
99
137
  - [Authentication Guide](https://docs.spreecommerce.org/developer/api/authentication)
data/Rakefile CHANGED
@@ -23,7 +23,32 @@ namespace :rswag do
23
23
  'spec/integration/**/*_spec.rb'
24
24
  )
25
25
 
26
+ # OPENAPI env var prevents spec_helper from overriding --order defined with :random
27
+ ENV['OPENAPI'] = 'true'
26
28
  t.rspec_opts = ['--format Rswag::Specs::SwaggerFormatter', '--order defined']
27
29
  end
28
30
  end
29
31
  end
32
+
33
+ namespace :typelizer do
34
+ desc 'Generate TypeScript types from Alba serializers'
35
+ task :generate do
36
+ ENV['RAILS_ENV'] ||= 'test'
37
+ ENV['DISABLE_TYPELIZER'] = 'false'
38
+
39
+ require File.expand_path('spec/dummy/config/environment', __dir__)
40
+
41
+ # Eager load serializers so typelizer can find them
42
+ serializers_path = File.expand_path('app/serializers/spree/api/v3', __dir__)
43
+ Rails.autoloaders.main.eager_load_dir(serializers_path)
44
+
45
+ require 'typelizer/generator'
46
+ serializers = Typelizer::Generator.call(force: true)
47
+
48
+ puts "Generated TypeScript types for #{serializers.size} serializers"
49
+ puts "Output: #{Typelizer.configuration.writer_config.output_dir}"
50
+ end
51
+
52
+ desc 'Clean and regenerate TypeScript types'
53
+ task refresh: :generate
54
+ end
@@ -0,0 +1,76 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module ApiKeyAuthentication
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ # @!attribute [r] current_api_key
9
+ # The authenticated API key for the current request.
10
+ # @return [Spree::ApiKey, nil]
11
+ attr_reader :current_api_key
12
+ end
13
+
14
+ # Authenticates a publishable API key (pk_*) for Store API requests.
15
+ # Looks up the key by plaintext token scoped to the current store.
16
+ #
17
+ # @return [Boolean] true if authentication succeeded, false otherwise
18
+ def authenticate_api_key!
19
+ @current_api_key = current_store.api_keys.active.publishable.find_by(token: extract_api_key)
20
+
21
+ unless @current_api_key
22
+ render_error(
23
+ code: ErrorHandler::ERROR_CODES[:invalid_token],
24
+ message: 'Valid API key required',
25
+ status: :unauthorized
26
+ )
27
+ return false
28
+ end
29
+
30
+ touch_api_key_if_needed(@current_api_key)
31
+ true
32
+ end
33
+
34
+ # Authenticates a secret API key (sk_*) for Admin API requests.
35
+ # Computes the HMAC-SHA256 digest of the provided token and looks up
36
+ # by +token_digest+, then verifies it belongs to the current store.
37
+ #
38
+ # @return [Boolean] true if authentication succeeded, false otherwise
39
+ def authenticate_secret_key!
40
+ @current_api_key = Spree::ApiKey.find_by_secret_token(extract_api_key)
41
+ @current_api_key = nil if @current_api_key && @current_api_key.store_id != current_store.id
42
+
43
+ unless @current_api_key
44
+ render_error(
45
+ code: ErrorHandler::ERROR_CODES[:invalid_token],
46
+ message: 'Valid secret API key required',
47
+ status: :unauthorized
48
+ )
49
+ return false
50
+ end
51
+
52
+ touch_api_key_if_needed(@current_api_key)
53
+ true
54
+ end
55
+
56
+ private
57
+
58
+ # Marks the API key as used at most once per hour
59
+ # to avoid unnecessary DB writes and job queue pressure on every request.
60
+ # This follows the same approach as GitHub's personal access tokens.
61
+ def touch_api_key_if_needed(api_key)
62
+ return if api_key.last_used_at.present? && api_key.last_used_at > 1.hour.ago
63
+
64
+ Spree::ApiKeys::MarkAsUsed.perform_later(api_key.id, Time.current)
65
+ end
66
+
67
+ # Extracts the API key from the request headers or params.
68
+ #
69
+ # @return [String, nil] the API key token
70
+ def extract_api_key
71
+ request.headers['X-Spree-Api-Key'].presence || params[:api_key]
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,261 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module ErrorHandler
5
+ extend ActiveSupport::Concern
6
+
7
+ # Stripe-style error codes for consistent API error responses
8
+ ERROR_CODES = {
9
+ # Authentication & authorization errors
10
+ authentication_required: 'authentication_required',
11
+ authentication_failed: 'authentication_failed',
12
+ access_denied: 'access_denied',
13
+ invalid_token: 'invalid_token',
14
+ invalid_provider: 'invalid_provider',
15
+
16
+ # Resource errors
17
+ record_not_found: 'record_not_found',
18
+ resource_invalid: 'resource_invalid',
19
+
20
+ # Order errors
21
+ order_not_found: 'order_not_found',
22
+ order_already_completed: 'order_already_completed',
23
+ order_cannot_transition: 'order_cannot_transition',
24
+ order_empty: 'order_empty',
25
+ order_invalid_state: 'order_invalid_state',
26
+
27
+ # Line item errors
28
+ line_item_not_found: 'line_item_not_found',
29
+ variant_not_found: 'variant_not_found',
30
+ insufficient_stock: 'insufficient_stock',
31
+ invalid_quantity: 'invalid_quantity',
32
+
33
+ # Validation errors
34
+ validation_error: 'validation_error',
35
+ parameter_missing: 'parameter_missing',
36
+ parameter_invalid: 'parameter_invalid',
37
+
38
+ # Payment errors
39
+ payment_failed: 'payment_failed',
40
+ payment_processing_error: 'payment_processing_error',
41
+ gateway_error: 'gateway_error',
42
+
43
+ # Digital download errors
44
+ attachment_missing: 'attachment_missing',
45
+ download_unauthorized: 'download_unauthorized',
46
+ digital_link_expired: 'digital_link_expired',
47
+ download_limit_exceeded: 'download_limit_exceeded',
48
+
49
+ # Rate limiting errors
50
+ rate_limit_exceeded: 'rate_limit_exceeded',
51
+
52
+ # Request errors
53
+ request_too_large: 'request_too_large',
54
+
55
+ # General errors
56
+ processing_error: 'processing_error',
57
+ invalid_request: 'invalid_request'
58
+ }.freeze
59
+
60
+ included do
61
+ # Override base controller error handlers
62
+ rescue_from ActiveRecord::RecordNotFound, with: :handle_record_not_found
63
+ rescue_from CanCan::AccessDenied, with: :handle_access_denied
64
+ rescue_from Spree::Core::GatewayError, with: :handle_gateway_error
65
+ rescue_from ActionController::ParameterMissing, with: :handle_parameter_missing
66
+ rescue_from ActiveRecord::RecordInvalid, with: :handle_record_invalid
67
+ rescue_from ArgumentError, with: :handle_argument_error
68
+ rescue_from ActionDispatch::Http::Parameters::ParseError, with: :handle_parse_error
69
+ rescue_from StateMachines::InvalidTransition, with: :handle_invalid_transition
70
+ end
71
+
72
+ protected
73
+
74
+ # Main error rendering method with Stripe-style structure
75
+ def render_error(code:, message:, status:, details: nil)
76
+ error_response = {
77
+ error: {
78
+ code: code,
79
+ message: message
80
+ }
81
+ }
82
+
83
+ error_response[:error][:details] = details if details.present?
84
+
85
+ render json: error_response, status: status
86
+ end
87
+
88
+ # Convenience method for validation errors with ActiveModel::Errors
89
+ def render_validation_error(errors, code: ERROR_CODES[:validation_error])
90
+ details = errors.is_a?(ActiveModel::Errors) ? format_validation_details(errors) : nil
91
+ message = errors.is_a?(ActiveModel::Errors) ? errors.full_messages.to_sentence : errors.to_s
92
+
93
+ render_error(
94
+ code: code,
95
+ message: message,
96
+ status: :unprocessable_content,
97
+ details: details
98
+ )
99
+ end
100
+
101
+ # Convenience method for service result errors
102
+ def render_service_error(error, code: ERROR_CODES[:processing_error], status: :unprocessable_content)
103
+ if error.is_a?(ActiveModel::Errors)
104
+ render_validation_error(error, code: code)
105
+ elsif error.is_a?(String)
106
+ render_error(code: code, message: error, status: status)
107
+ else
108
+ render_error(code: code, message: error.to_s, status: status)
109
+ end
110
+ end
111
+
112
+ # Legacy support - redirect to new error handling
113
+ def render_errors(errors, status = :unprocessable_content)
114
+ code = infer_error_code(errors, status)
115
+
116
+ if errors.is_a?(ActiveModel::Errors)
117
+ render_validation_error(errors, code: code)
118
+ else
119
+ message = errors.is_a?(String) ? errors : errors.to_s
120
+ render_error(code: code, message: message, status: status)
121
+ end
122
+ end
123
+
124
+ # Exception handlers
125
+ def handle_record_not_found(exception)
126
+ code = determine_not_found_code(exception)
127
+ message = generate_not_found_message(exception)
128
+
129
+ render_error(
130
+ code: code,
131
+ message: message,
132
+ status: :not_found
133
+ )
134
+ end
135
+
136
+ def handle_access_denied(exception)
137
+ render_error(
138
+ code: ERROR_CODES[:access_denied],
139
+ message: exception.message,
140
+ status: :forbidden
141
+ )
142
+ end
143
+
144
+ def handle_gateway_error(exception)
145
+ Rails.error.report(exception, context: error_context, source: 'spree.api.v3')
146
+ render_error(
147
+ code: ERROR_CODES[:gateway_error],
148
+ message: exception.message,
149
+ status: :unprocessable_content
150
+ )
151
+ end
152
+
153
+ def handle_parameter_missing(exception)
154
+ Rails.error.report(exception, context: error_context, source: 'spree.api.v3')
155
+ render_error(
156
+ code: ERROR_CODES[:parameter_missing],
157
+ message: exception.message,
158
+ status: :bad_request
159
+ )
160
+ end
161
+
162
+ def handle_record_invalid(exception)
163
+ Rails.error.report(exception, context: error_context, source: 'spree.api.v3')
164
+ render_validation_error(exception.record.errors)
165
+ end
166
+
167
+ def handle_argument_error(exception)
168
+ Rails.error.report(exception, context: error_context, source: 'spree.api.v3')
169
+ render_error(
170
+ code: ERROR_CODES[:invalid_request],
171
+ message: exception.message,
172
+ status: :bad_request
173
+ )
174
+ end
175
+
176
+ def handle_parse_error(exception)
177
+ Rails.error.report(exception, context: error_context, source: 'spree.api.v3')
178
+ message = exception.respond_to?(:original_message) ? exception.original_message : exception.message
179
+ render_error(
180
+ code: ERROR_CODES[:invalid_request],
181
+ message: message,
182
+ status: :bad_request
183
+ )
184
+ end
185
+
186
+ def handle_invalid_transition(exception)
187
+ Rails.error.report(exception, context: error_context, source: 'spree.api.v3')
188
+ render_error(
189
+ code: ERROR_CODES[:order_cannot_transition],
190
+ message: exception.message,
191
+ status: :unprocessable_content
192
+ )
193
+ end
194
+
195
+ private
196
+
197
+ # Format validation errors for details field
198
+ def format_validation_details(errors)
199
+ errors.messages.transform_values do |messages|
200
+ messages.map { |msg| msg }
201
+ end
202
+ end
203
+
204
+ # Infer error code from context
205
+ def infer_error_code(errors, status)
206
+ case status
207
+ when :not_found
208
+ ERROR_CODES[:record_not_found]
209
+ when :forbidden
210
+ ERROR_CODES[:access_denied]
211
+ when :bad_request
212
+ ERROR_CODES[:invalid_request]
213
+ when :unprocessable_content
214
+ errors.is_a?(ActiveModel::Errors) ? ERROR_CODES[:validation_error] : ERROR_CODES[:processing_error]
215
+ else
216
+ ERROR_CODES[:processing_error]
217
+ end
218
+ end
219
+
220
+ # Determine specific not found code based on model
221
+ def determine_not_found_code(exception)
222
+ model_name = extract_model_name(exception)
223
+
224
+ case model_name
225
+ when 'order'
226
+ ERROR_CODES[:order_not_found]
227
+ when 'line_item'
228
+ ERROR_CODES[:line_item_not_found]
229
+ when 'variant'
230
+ ERROR_CODES[:variant_not_found]
231
+ else
232
+ ERROR_CODES[:record_not_found]
233
+ end
234
+ end
235
+
236
+ # Generate human-readable not found message
237
+ def generate_not_found_message(exception)
238
+ model_name = extract_model_name(exception)
239
+ Spree.t(:record_not_found, scope: 'api', model: model_name&.humanize || 'record')
240
+ end
241
+
242
+ # Extract clean model name from exception
243
+ def extract_model_name(exception)
244
+ return nil unless exception.model
245
+
246
+ # Remove Spree:: namespace and convert to underscore
247
+ exception.model.to_s.demodulize.underscore
248
+ end
249
+
250
+ # Error reporting context
251
+ def error_context
252
+ {
253
+ user_id: current_user&.id,
254
+ store_id: current_store&.id,
255
+ request_id: request.request_id
256
+ }
257
+ end
258
+ end
259
+ end
260
+ end
261
+ end
@@ -0,0 +1,90 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ # Provides HTTP caching support for API v3 controllers
5
+ #
6
+ # Strategy:
7
+ # - Guest users: Public HTTP caching with CDN support (5-15 min TTL)
8
+ # - Authenticated users: Private, no-store (no caching)
9
+ #
10
+ # Uses ETag and Last-Modified headers for cache validation.
11
+ module HttpCaching
12
+ extend ActiveSupport::Concern
13
+
14
+ included do
15
+ before_action :set_vary_headers
16
+ end
17
+
18
+ protected
19
+
20
+ # Check if the current user is a guest (no authentication)
21
+ def guest_user?
22
+ current_user.nil?
23
+ end
24
+
25
+ # Set Vary headers to ensure proper CDN caching by currency/locale
26
+ def set_vary_headers
27
+ if guest_user?
28
+ response.headers['Vary'] = 'Accept, x-spree-currency, x-spree-locale'
29
+ else
30
+ response.headers['Cache-Control'] = 'private, no-store'
31
+ end
32
+ end
33
+
34
+ # Apply HTTP caching for a collection (index actions)
35
+ # Only caches for guest users
36
+ #
37
+ # @param collection [ActiveRecord::Relation] The collection to cache
38
+ # @param expires_in [ActiveSupport::Duration] Cache TTL (default: 5 minutes)
39
+ # @param stale_while_revalidate [ActiveSupport::Duration] Allow stale responses while revalidating
40
+ # @return [Boolean] true if response should be rendered, false if 304 Not Modified
41
+ def cache_collection(collection, expires_in: 5.minutes, stale_while_revalidate: 30.seconds)
42
+ return true unless guest_user?
43
+
44
+ expires_in expires_in, public: true, stale_while_revalidate: stale_while_revalidate
45
+
46
+ # Use collection's cache key for ETag
47
+ cache_key = collection_cache_key(collection)
48
+ response.headers['ETag'] = %("#{Digest::MD5.hexdigest(cache_key)}")
49
+
50
+ # Return false if client has fresh cache (304 Not Modified)
51
+ !request.fresh?(response)
52
+ end
53
+
54
+ # Apply HTTP caching for a single resource (show actions)
55
+ # Only caches for guest users
56
+ #
57
+ # @param resource [ActiveRecord::Base] The resource to cache
58
+ # @param expires_in [ActiveSupport::Duration] Cache TTL (default: 5 minutes)
59
+ # @return [Boolean] true if response should be rendered, false if 304 Not Modified
60
+ def cache_resource(resource, expires_in: 5.minutes)
61
+ return true unless guest_user?
62
+
63
+ expires_in expires_in, public: true
64
+
65
+ # Use Rails' stale? which handles ETag and Last-Modified
66
+ stale?(resource, public: true)
67
+ end
68
+
69
+ private
70
+
71
+ # Build a cache key for a collection
72
+ # Includes: query params, pagination, includes, currency, locale
73
+ # Strips order to avoid PostgreSQL errors with DISTINCT + subquery ORDER BY expressions
74
+ def collection_cache_key(collection)
75
+ parts = [
76
+ collection.reorder(nil).cache_key_with_version,
77
+ params[:include],
78
+ params[:q],
79
+ params[:page],
80
+ params[:per_page],
81
+ current_currency,
82
+ current_locale
83
+ ]
84
+
85
+ parts.compact.join('/')
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end