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
@@ -0,0 +1,118 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module JwtAuthentication
5
+ extend ActiveSupport::Concern
6
+
7
+ include Spree::Api::V3::ErrorHandler
8
+
9
+ USER_TYPE_CUSTOMER = 'customer'.freeze
10
+ USER_TYPE_ADMIN = 'admin'.freeze
11
+
12
+ JWT_AUDIENCE_STORE = 'store_api'.freeze
13
+ JWT_AUDIENCE_ADMIN = 'admin_api'.freeze
14
+ JWT_ISSUER = 'spree'.freeze
15
+
16
+ included do
17
+ attr_reader :current_user
18
+ end
19
+
20
+ # Optional authentication - doesn't fail if no token
21
+ def authenticate_user
22
+ token = extract_token
23
+ return unless token.present?
24
+
25
+ payload = decode_jwt(token)
26
+ @current_user = find_user_from_payload(payload)
27
+ rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::InvalidIssuerError,
28
+ JWT::InvalidAudError, ActiveRecord::RecordNotFound => e
29
+ Rails.logger.debug { "JWT authentication failed: #{e.message}" }
30
+ @current_user = nil
31
+ end
32
+
33
+ # Required authentication - fails if no valid token
34
+ # Returns true if authenticated, false otherwise (also renders error and halts)
35
+ def require_authentication!
36
+ authenticate_user
37
+
38
+ return true if current_user
39
+
40
+ render_error(code: ErrorHandler::ERROR_CODES[:authentication_required], message: 'Authentication required', status: :unauthorized)
41
+ false
42
+ end
43
+
44
+ protected
45
+
46
+ # Generate a JWT token for a user
47
+ # @param user [Object] The user to generate a token for
48
+ # @param expiration [Integer] Time in seconds until expiration (default from config, 1 hour)
49
+ # @param audience [String] The audience claim (default: store_api)
50
+ # @return [String] The JWT token
51
+ def generate_jwt(user, expiration: jwt_expiration, audience: JWT_AUDIENCE_STORE)
52
+ payload = {
53
+ user_id: user.id,
54
+ user_type: determine_user_type(user),
55
+ jti: SecureRandom.uuid,
56
+ iss: JWT_ISSUER,
57
+ aud: audience,
58
+ exp: Time.current.to_i + expiration
59
+ }
60
+ JWT.encode(payload, jwt_secret, 'HS256')
61
+ end
62
+
63
+ private
64
+
65
+ def extract_token
66
+ # Check Authorization header first
67
+ header = request.headers['Authorization']
68
+ return header.split(' ').last if header.present? && header.start_with?('Bearer ')
69
+
70
+ # Restricted fallback: only for digital download endpoints
71
+ params[:token] if controller_name == 'digitals'
72
+ end
73
+
74
+ def decode_jwt(token)
75
+ JWT.decode(token, jwt_secret, true,
76
+ algorithm: 'HS256',
77
+ iss: JWT_ISSUER,
78
+ aud: expected_audience,
79
+ verify_iss: true,
80
+ verify_aud: true
81
+ ).first
82
+ end
83
+
84
+ def jwt_secret
85
+ Rails.application.credentials.jwt_secret_key || ENV['JWT_SECRET_KEY'] || Rails.application.secret_key_base
86
+ end
87
+
88
+ def jwt_expiration
89
+ Spree::Api::Config[:jwt_expiration]
90
+ end
91
+
92
+ def expected_audience
93
+ JWT_AUDIENCE_STORE
94
+ end
95
+
96
+ def find_user_from_payload(payload)
97
+ user_id = payload['user_id']
98
+ user_type = payload['user_type'] || USER_TYPE_CUSTOMER
99
+
100
+ case user_type
101
+ when USER_TYPE_ADMIN
102
+ Spree.admin_user_class.find(user_id)
103
+ else
104
+ Spree.user_class.find(user_id)
105
+ end
106
+ end
107
+
108
+ def determine_user_type(user)
109
+ if user.is_a?(Spree.admin_user_class)
110
+ USER_TYPE_ADMIN
111
+ else
112
+ USER_TYPE_CUSTOMER
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,185 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ # Handles locale, currency, and market resolution for API v3 controllers.
5
+ #
6
+ # This concern is fully self-contained and does not depend on
7
+ # +Spree::Core::ControllerHelpers::Locale+ or +Spree::Core::ControllerHelpers::Currency+.
8
+ #
9
+ # Resolution order:
10
+ # 1. Market is resolved from +x-spree-country+ header (sets +Spree::Current.market+)
11
+ # 2. Locale is resolved: +x-spree-locale+ header > +params[:locale]+ > +Spree::Current.locale+ (market -> store fallback)
12
+ # 3. Currency is resolved: +x-spree-currency+ header > +params[:currency]+ > +Spree::Current.currency+ (market -> store fallback)
13
+ # 4. Mobility fallback locale is configured for the current store
14
+ module LocaleAndCurrency
15
+ extend ActiveSupport::Concern
16
+
17
+ included do
18
+ before_action :set_market_from_country
19
+ before_action :set_locale
20
+ before_action :set_currency
21
+ before_action :set_fallback_locale
22
+ end
23
+
24
+ protected
25
+
26
+ # Returns the current locale for this request.
27
+ #
28
+ # Priority: x-spree-locale header > params[:locale] > Spree::Current.locale (market -> store fallback)
29
+ #
30
+ # @return [String] the locale code, e.g. +"en"+, +"fr"+
31
+ def current_locale
32
+ @current_locale ||= begin
33
+ locale = locale_from_header || locale_from_params
34
+ locale.to_s if locale.present? && supported_locale?(locale)
35
+ end || Spree::Current.locale
36
+ end
37
+
38
+ # Returns the current currency for this request.
39
+ #
40
+ # Priority: x-spree-currency header > params[:currency] > Spree::Current.currency (market -> store fallback)
41
+ #
42
+ # @return [String] the currency ISO code, e.g. +"USD"+, +"EUR"+
43
+ def current_currency
44
+ @current_currency ||= begin
45
+ currency = currency_from_header || currency_from_params
46
+ currency = currency&.upcase
47
+ currency if currency.present? && supported_currency?(currency)
48
+ end || Spree::Current.currency
49
+ end
50
+
51
+ # Returns the default locale, delegating to +Spree::Current.locale+
52
+ # which falls back through market -> store.
53
+ #
54
+ # @return [String] the default locale code
55
+ def default_locale
56
+ Spree::Current.locale
57
+ end
58
+
59
+ # Returns the list of supported locale codes for the current store.
60
+ #
61
+ # When markets are configured, this aggregates locales from all markets.
62
+ #
63
+ # @return [Array<String>] supported locale codes
64
+ def supported_locales
65
+ @supported_locales ||= current_store&.supported_locales_list
66
+ end
67
+
68
+ # Checks if the given locale is supported by the current store.
69
+ #
70
+ # @param locale_code [String, nil] the locale code to check
71
+ # @return [Boolean]
72
+ def supported_locale?(locale_code)
73
+ return false if supported_locales.nil?
74
+
75
+ supported_locales.include?(locale_code&.to_s)
76
+ end
77
+
78
+ # Returns the list of supported currencies for the current store.
79
+ #
80
+ # When markets are configured, this aggregates currencies from all markets.
81
+ #
82
+ # @return [Array<Money::Currency>] supported currencies
83
+ def supported_currencies
84
+ @supported_currencies ||= current_store&.supported_currencies_list
85
+ end
86
+
87
+ # Checks if the given currency ISO code is supported by the current store.
88
+ #
89
+ # @param currency_iso_code [String, nil] the currency ISO code to check, e.g. +"USD"+
90
+ # @return [Boolean]
91
+ def supported_currency?(currency_iso_code)
92
+ return false if supported_currencies.nil?
93
+
94
+ supported_currencies.map(&:iso_code).include?(currency_iso_code&.upcase)
95
+ end
96
+
97
+ # Finds a record using the given block, falling back to the store's default locale
98
+ # if the record is not found in the current locale.
99
+ #
100
+ # Used for slug/permalink lookups where translated slugs may not exist in all locales.
101
+ #
102
+ # @yield the block that performs the lookup
103
+ # @return [ActiveRecord::Base] the found record
104
+ # @raise [ActiveRecord::RecordNotFound] if not found in any locale
105
+ def find_with_fallback_default_locale(&block)
106
+ result = begin
107
+ block.call
108
+ rescue ActiveRecord::RecordNotFound => _e
109
+ nil
110
+ end
111
+
112
+ result || Mobility.with_locale(current_store.default_locale) { block.call }
113
+ end
114
+
115
+ private
116
+
117
+ # Sets +I18n.locale+ and +Spree::Current.locale+ from the resolved locale.
118
+ def set_locale
119
+ Spree::Current.locale = current_locale
120
+ I18n.locale = current_locale
121
+ end
122
+
123
+ # Sets +Spree::Current.currency+ from the resolved currency.
124
+ def set_currency
125
+ Spree::Current.currency = current_currency
126
+ end
127
+
128
+ # Configures Mobility fallback locales for the current store.
129
+ #
130
+ # This runs after market resolution so fallbacks are aware of the store's
131
+ # full locale configuration.
132
+ def set_fallback_locale
133
+ return unless current_store.present?
134
+
135
+ Spree::Locales::SetFallbackLocaleForStore.new.call(store: current_store)
136
+ end
137
+
138
+ # Reads the locale from the +x-spree-locale+ request header.
139
+ #
140
+ # @return [String, nil]
141
+ def locale_from_header
142
+ request.headers['x-spree-locale'].presence
143
+ end
144
+
145
+ # Reads the currency from the +x-spree-currency+ request header.
146
+ #
147
+ # @return [String, nil]
148
+ def currency_from_header
149
+ request.headers['x-spree-currency'].presence
150
+ end
151
+
152
+ # Reads the locale from request params.
153
+ #
154
+ # @return [String, nil]
155
+ def locale_from_params
156
+ params[:locale].presence
157
+ end
158
+
159
+ # Reads the currency from request params.
160
+ #
161
+ # @return [String, nil]
162
+ def currency_from_params
163
+ params[:currency].presence
164
+ end
165
+
166
+ # Resolves the market from the +x-spree-country+ header or +params[:country]+.
167
+ #
168
+ # When a matching market is found, it is set on +Spree::Current.market+,
169
+ # which influences the default locale and currency fallbacks.
170
+ def set_market_from_country
171
+ country_iso = request.headers['x-spree-country'].presence || params[:country].presence
172
+ return unless country_iso
173
+
174
+ country = Spree::Country.find_by(iso: country_iso.upcase)
175
+ return unless country
176
+
177
+ market = current_store&.market_for_country(country)
178
+ return unless market
179
+
180
+ Spree::Current.market = market
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,51 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module OrderConcern
5
+ extend ActiveSupport::Concern
6
+
7
+ # Allow access to order via order token for guests or authenticated users
8
+ # Expects @parent to be set to the order
9
+ def authorize_order_access!
10
+ authorize!(:update, @parent, order_token)
11
+ end
12
+
13
+ def set_parent
14
+ return if params[:order_id].blank?
15
+
16
+ @parent = order_scope.find_by_prefix_id!(params[:order_id])
17
+ end
18
+
19
+ def order_scope
20
+ base = current_store.orders
21
+ base = if current_user
22
+ base.where(user: current_user)
23
+ elsif order_token.present?
24
+ base.where(token: order_token)
25
+ else
26
+ base.none
27
+ end
28
+ base.preload_associations_lazily
29
+ end
30
+
31
+ protected
32
+
33
+ # Render the parent order as JSON using the order serializer.
34
+ # Use this when sub-resource mutations should return the updated order
35
+ # (e.g., line items, coupon codes, store credits).
36
+ def render_order(status: :ok)
37
+ render json: Spree.api.order_serializer.new(@parent.reload, params: serializer_params).to_h, status: status
38
+ end
39
+
40
+ def order_token
41
+ # Check x-spree-order-token header first (lowercase for consistency)
42
+ header = request.headers['x-spree-order-token']
43
+ return header if header.present?
44
+
45
+ # Fallback to query params (support both token and order_token)
46
+ params[:order_token].presence || params[:token]
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,41 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module ResourceSerializer
5
+ extend ActiveSupport::Concern
6
+
7
+ protected
8
+
9
+ # Serialize a single resource
10
+ def serialize_resource(resource)
11
+ serializer_class.new(resource, params: serializer_params).to_h
12
+ end
13
+
14
+ # Serialize a collection of resources
15
+ def serialize_collection(collection)
16
+ collection.map { |item| serializer_class.new(item, params: serializer_params).to_h }
17
+ end
18
+
19
+ # Params passed to serializers
20
+ def serializer_params
21
+ {
22
+ currency: current_currency,
23
+ store: current_store,
24
+ user: current_user,
25
+ locale: current_locale,
26
+ includes: include_list
27
+ }
28
+ end
29
+
30
+ # Parse include parameter into list
31
+ # Supports: ?include=variants,images or ?includes=variants,images
32
+ def include_list
33
+ include_param = params[:include].presence || params[:includes].presence
34
+ return [] unless include_param
35
+
36
+ include_param.to_s.split(',').map(&:strip)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module SecurityHeaders
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ after_action :set_security_headers
9
+ end
10
+
11
+ private
12
+
13
+ def set_security_headers
14
+ response.headers['X-Content-Type-Options'] = 'nosniff'
15
+ response.headers['X-Frame-Options'] = 'DENY'
16
+ response.headers.delete('X-Powered-By')
17
+ response.headers.delete('Server')
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,45 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ class BaseController < ActionController::API
5
+ include ActiveStorage::SetCurrent
6
+ include CanCan::ControllerAdditions
7
+ include Spree::Core::ControllerHelpers::StrongParameters
8
+ include Spree::Core::ControllerHelpers::Store
9
+ include Spree::Api::V3::LocaleAndCurrency
10
+ include Spree::Api::V3::JwtAuthentication
11
+ include Spree::Api::V3::ApiKeyAuthentication
12
+ include Spree::Api::V3::ErrorHandler
13
+ include Spree::Api::V3::HttpCaching
14
+ include Spree::Api::V3::SecurityHeaders
15
+ include Spree::Api::V3::ResourceSerializer
16
+ include Pagy::Method
17
+
18
+ # Optional JWT authentication by default
19
+ before_action :authenticate_user
20
+
21
+ protected
22
+
23
+ # Override to use current_user from JWT authentication
24
+ # @return [Spree.user_class]
25
+ def spree_current_user
26
+ current_user
27
+ end
28
+
29
+ alias try_spree_current_user spree_current_user
30
+
31
+ # CanCanCan ability
32
+ # @return [Spree::Ability]
33
+ def current_ability
34
+ @current_ability ||= Spree::Ability.new(current_user, ability_options)
35
+ end
36
+
37
+ # Options passed to the CanCanCan ability
38
+ # @return [Hash]
39
+ def ability_options
40
+ { store: current_store }
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,210 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ class ResourceController < BaseController
5
+ before_action :set_parent
6
+ before_action :set_resource, only: [:show, :update, :destroy]
7
+
8
+ # GET /api/v3/resource
9
+ def index
10
+ @collection = collection
11
+
12
+ # Apply HTTP caching for guests
13
+ return unless cache_collection(@collection)
14
+
15
+ render json: {
16
+ data: serialize_collection(@collection),
17
+ meta: collection_meta(@collection)
18
+ }
19
+ end
20
+
21
+ # GET /api/v3/resource/:id
22
+ def show
23
+ # Apply HTTP caching for guests
24
+ return unless cache_resource(@resource)
25
+
26
+ render json: serialize_resource(@resource)
27
+ end
28
+
29
+ # POST /api/v3/resource
30
+ def create
31
+ @resource = build_resource
32
+ authorize_resource!(@resource, :create)
33
+
34
+ if @resource.save
35
+ render json: serialize_resource(@resource), status: :created
36
+ else
37
+ render_errors(@resource.errors)
38
+ end
39
+ end
40
+
41
+ # PATCH /api/v3/resource/:id
42
+ def update
43
+ if @resource.update(permitted_params)
44
+ render json: serialize_resource(@resource)
45
+ else
46
+ render_errors(@resource.errors)
47
+ end
48
+ end
49
+
50
+ # DELETE /api/v3/resource/:id
51
+ def destroy
52
+ @resource.destroy
53
+ head :no_content
54
+ end
55
+
56
+ protected
57
+
58
+ # Override in subclass to set parent resource (e.g., @wishlist, @order)
59
+ # This runs before set_resource, allowing scope to use the parent
60
+ def set_parent
61
+ # No-op by default, override in nested resource controllers
62
+ end
63
+
64
+ # Sets the resource for show, update, destroy actions
65
+ # Always uses scope to respect controller's custom scoping
66
+ def set_resource
67
+ @resource = find_resource
68
+ authorize_resource!(@resource)
69
+ end
70
+
71
+ # Builds a new resource, using parent association when @parent is set
72
+ def build_resource
73
+ if @parent.present?
74
+ @parent.send(parent_association).build(permitted_params)
75
+ else
76
+ model_class.new(permitted_params)
77
+ end
78
+ end
79
+
80
+ # Finds a single resource within scope using prefixed ID
81
+ def find_resource
82
+ scope.find_by_prefix_id!(params[:id])
83
+ end
84
+
85
+ # Authorize resource with CanCanCan
86
+ def authorize_resource!(resource = @resource, action = action_name.to_sym)
87
+ authorize!(action, resource)
88
+ end
89
+
90
+ # Returns ransack-filtered, sorted and paginated collection
91
+ # ar_lazy_preload handles automatic association preloading
92
+ # @return [ActiveRecord::Relation]
93
+ def collection
94
+ return @collection if @collection.present?
95
+
96
+ @search = scope.includes(collection_includes).
97
+ preload_associations_lazily.
98
+ ransack(ransack_params)
99
+ result = @search.result(distinct: collection_distinct?)
100
+ result = apply_collection_sort(result)
101
+ @pagy, @collection = pagy(result, limit: limit, page: page)
102
+ @collection
103
+ end
104
+
105
+ # Override in subclass to disable distinct (e.g., for custom sorting with computed columns)
106
+ def collection_distinct?
107
+ true
108
+ end
109
+
110
+ # Override in subclass to apply custom sorting
111
+ def apply_collection_sort(collection)
112
+ collection
113
+ end
114
+
115
+ def collection_includes
116
+ []
117
+ end
118
+
119
+ # Ransack query parameters
120
+ def ransack_params
121
+ params[:q] || {}
122
+ end
123
+
124
+ # Pagination parameters
125
+ def page
126
+ params[:page]&.to_i || 1
127
+ end
128
+
129
+ def limit
130
+ limit_param = params[:per_page]&.to_i || params[:limit]&.to_i || 25
131
+ [limit_param, 100].min # Max 100 per page
132
+ end
133
+
134
+ # Metadata for collection responses
135
+ def collection_meta(_collection)
136
+ return {} unless @pagy
137
+
138
+ {
139
+ page: @pagy.page,
140
+ limit: @pagy.limit,
141
+ count: @pagy.count,
142
+ pages: @pagy.pages,
143
+ from: @pagy.from,
144
+ to: @pagy.to,
145
+ in: @pagy.in,
146
+ previous: @pagy.previous,
147
+ next: @pagy.next
148
+ }
149
+ end
150
+
151
+ # Base scope with store and ability
152
+ # When @parent is set (nested resources), uses parent association instead
153
+ def scope
154
+ base_scope = if @parent.present?
155
+ @parent.send(parent_association)
156
+ else
157
+ model_class.for_store(current_store)
158
+ end
159
+ base_scope = base_scope.accessible_by(current_ability, :show) unless @parent.present?
160
+ base_scope = base_scope.includes(scope_includes) if scope_includes.any?
161
+ base_scope = base_scope.preload_associations_lazily
162
+ model_class.include?(Spree::TranslatableResource) ? base_scope.i18n : base_scope
163
+ end
164
+
165
+ # Override to specify the association name on @parent
166
+ # Defaults to controller_name (e.g., 'wished_items' for WishlistItemsController)
167
+ def parent_association
168
+ controller_name
169
+ end
170
+
171
+ # Override in subclass to eager load associations that don't work well
172
+ # with ar_lazy_preload (e.g., prices, stock_items)
173
+ def scope_includes
174
+ []
175
+ end
176
+
177
+ # Override in subclass to define the model
178
+ def model_class
179
+ raise NotImplementedError, 'Subclass must implement model_class'
180
+ end
181
+
182
+ # Override in subclass to define the serializer class
183
+ def serializer_class
184
+ raise NotImplementedError, 'Subclass must implement serializer_class'
185
+ end
186
+
187
+ # Permit flat parameters based on model class
188
+ # Automatically infers attribute list from Spree::PermittedAttributes
189
+ # e.g., ProductsController -> Spree::PermittedAttributes.product_attributes
190
+ #
191
+ # Override in subclass for custom parameter handling
192
+ def permitted_params
193
+ params.permit(permitted_attributes)
194
+ end
195
+
196
+ # Returns the permitted attributes list for the model
197
+ # Override in subclass for custom attributes
198
+ def permitted_attributes
199
+ Spree::PermittedAttributes.public_send(permitted_attributes_key)
200
+ end
201
+
202
+ # Infers the PermittedAttributes key from model class
203
+ # e.g., Spree::Product -> :product_attributes
204
+ def permitted_attributes_key
205
+ :"#{model_class.model_name.element}_attributes"
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end