spree_api 5.4.3 → 5.5.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +19 -0
  3. data/app/controllers/concerns/spree/api/v3/admin/auth_cookies.rb +62 -0
  4. data/app/controllers/concerns/spree/api/v3/admin/subclassed_resource.rb +149 -0
  5. data/app/controllers/concerns/spree/api/v3/admin_authentication.rb +54 -0
  6. data/app/controllers/concerns/spree/api/v3/bulk_operations.rb +103 -0
  7. data/app/controllers/concerns/spree/api/v3/channel_resolution.rb +60 -0
  8. data/app/controllers/concerns/spree/api/v3/error_handler.rb +4 -0
  9. data/app/controllers/concerns/spree/api/v3/params_normalizer.rb +84 -0
  10. data/app/controllers/concerns/spree/api/v3/scoped_authorization.rb +88 -0
  11. data/app/controllers/concerns/spree/api/v3/store/search_provider_support.rb +35 -1
  12. data/app/controllers/spree/api/v3/admin/admin_users_controller.rb +97 -0
  13. data/app/controllers/spree/api/v3/admin/allowed_origins_controller.rb +25 -0
  14. data/app/controllers/spree/api/v3/admin/api_keys_controller.rb +55 -0
  15. data/app/controllers/spree/api/v3/admin/auth_controller.rb +134 -0
  16. data/app/controllers/spree/api/v3/admin/base_controller.rb +3 -17
  17. data/app/controllers/spree/api/v3/admin/categories_controller.rb +25 -0
  18. data/app/controllers/spree/api/v3/admin/channels_controller.rb +65 -0
  19. data/app/controllers/spree/api/v3/admin/countries_controller.rb +38 -0
  20. data/app/controllers/spree/api/v3/admin/coupon_codes_controller.rb +33 -0
  21. data/app/controllers/spree/api/v3/admin/custom_field_definitions_controller.rb +34 -0
  22. data/app/controllers/spree/api/v3/admin/custom_fields_controller.rb +108 -0
  23. data/app/controllers/spree/api/v3/admin/customer_groups_controller.rb +31 -0
  24. data/app/controllers/spree/api/v3/admin/customers/addresses_controller.rb +88 -0
  25. data/app/controllers/spree/api/v3/admin/customers/credit_cards_controller.rb +31 -0
  26. data/app/controllers/spree/api/v3/admin/customers/store_credits_controller.rb +93 -0
  27. data/app/controllers/spree/api/v3/admin/customers_controller.rb +119 -0
  28. data/app/controllers/spree/api/v3/admin/dashboard_controller.rb +44 -0
  29. data/app/controllers/spree/api/v3/admin/direct_uploads_controller.rb +40 -0
  30. data/app/controllers/spree/api/v3/admin/exports_controller.rb +89 -0
  31. data/app/controllers/spree/api/v3/admin/gift_card_batches_controller.rb +31 -0
  32. data/app/controllers/spree/api/v3/admin/gift_cards_controller.rb +33 -0
  33. data/app/controllers/spree/api/v3/admin/invitation_acceptances_controller.rb +138 -0
  34. data/app/controllers/spree/api/v3/admin/invitations_controller.rb +70 -0
  35. data/app/controllers/spree/api/v3/admin/markets_controller.rb +42 -0
  36. data/app/controllers/spree/api/v3/admin/me_controller.rb +69 -0
  37. data/app/controllers/spree/api/v3/admin/media_controller.rb +119 -0
  38. data/app/controllers/spree/api/v3/admin/option_types_controller.rb +34 -0
  39. data/app/controllers/spree/api/v3/admin/orders/adjustments_controller.rb +27 -0
  40. data/app/controllers/spree/api/v3/admin/orders/base_controller.rb +26 -0
  41. data/app/controllers/spree/api/v3/admin/orders/fulfillments_controller.rb +104 -0
  42. data/app/controllers/spree/api/v3/admin/orders/gift_cards_controller.rb +79 -0
  43. data/app/controllers/spree/api/v3/admin/orders/items_controller.rb +92 -0
  44. data/app/controllers/spree/api/v3/admin/orders/payments_controller.rb +90 -0
  45. data/app/controllers/spree/api/v3/admin/orders/refunds_controller.rb +53 -0
  46. data/app/controllers/spree/api/v3/admin/orders/store_credits_controller.rb +59 -0
  47. data/app/controllers/spree/api/v3/admin/orders_controller.rb +190 -0
  48. data/app/controllers/spree/api/v3/admin/payment_methods_controller.rb +73 -0
  49. data/app/controllers/spree/api/v3/admin/price_lists_controller.rb +156 -0
  50. data/app/controllers/spree/api/v3/admin/prices_controller.rb +129 -0
  51. data/app/controllers/spree/api/v3/admin/products/variants_controller.rb +48 -0
  52. data/app/controllers/spree/api/v3/admin/products_controller.rb +237 -0
  53. data/app/controllers/spree/api/v3/admin/promotion_actions_controller.rb +78 -0
  54. data/app/controllers/spree/api/v3/admin/promotion_rules_controller.rb +56 -0
  55. data/app/controllers/spree/api/v3/admin/promotions_controller.rb +78 -0
  56. data/app/controllers/spree/api/v3/admin/resource_controller.rb +29 -11
  57. data/app/controllers/spree/api/v3/admin/roles_controller.rb +29 -0
  58. data/app/controllers/spree/api/v3/admin/stock_items_controller.rb +35 -0
  59. data/app/controllers/spree/api/v3/admin/stock_locations_controller.rb +36 -0
  60. data/app/controllers/spree/api/v3/admin/stock_reservations_controller.rb +29 -0
  61. data/app/controllers/spree/api/v3/admin/stock_transfers_controller.rb +75 -0
  62. data/app/controllers/spree/api/v3/admin/store_controller.rb +53 -0
  63. data/app/controllers/spree/api/v3/admin/store_credit_categories_controller.rb +21 -0
  64. data/app/controllers/spree/api/v3/admin/tags_controller.rb +51 -0
  65. data/app/controllers/spree/api/v3/admin/tax_categories_controller.rb +21 -0
  66. data/app/controllers/spree/api/v3/admin/variants_controller.rb +33 -0
  67. data/app/controllers/spree/api/v3/admin/webhook_deliveries_controller.rb +49 -0
  68. data/app/controllers/spree/api/v3/admin/webhook_endpoints_controller.rb +75 -0
  69. data/app/controllers/spree/api/v3/resource_controller.rb +90 -8
  70. data/app/controllers/spree/api/v3/store/auth_controller.rb +8 -28
  71. data/app/controllers/spree/api/v3/store/base_controller.rb +6 -0
  72. data/app/controllers/spree/api/v3/store/carts_controller.rb +1 -0
  73. data/app/controllers/spree/api/v3/store/customers_controller.rb +6 -0
  74. data/app/controllers/spree/api/v3/store/newsletter_subscribers_controller.rb +77 -0
  75. data/app/controllers/spree/api/v3/store/products/filters_controller.rb +2 -2
  76. data/app/controllers/spree/api/v3/store/products_controller.rb +3 -3
  77. data/app/controllers/spree/api/v3/store/resource_controller.rb +10 -2
  78. data/app/jobs/spree/webhook_delivery_job.rb +5 -0
  79. data/app/models/spree/api_key_ability.rb +16 -0
  80. data/app/serializers/spree/api/v3/admin/address_serializer.rb +2 -6
  81. data/app/serializers/spree/api/v3/admin/adjustment_serializer.rb +3 -15
  82. data/app/serializers/spree/api/v3/admin/admin_user_serializer.rb +19 -3
  83. data/app/serializers/spree/api/v3/admin/allowed_origin_serializer.rb +2 -6
  84. data/app/serializers/spree/api/v3/admin/api_key_serializer.rb +42 -0
  85. data/app/serializers/spree/api/v3/admin/category_serializer.rb +4 -3
  86. data/app/serializers/spree/api/v3/admin/channel_serializer.rb +15 -0
  87. data/app/serializers/spree/api/v3/admin/country_serializer.rb +1 -1
  88. data/app/serializers/spree/api/v3/admin/coupon_code_serializer.rb +30 -0
  89. data/app/serializers/spree/api/v3/admin/credit_card_serializer.rb +4 -2
  90. data/app/serializers/spree/api/v3/admin/custom_field_definition_serializer.rb +21 -0
  91. data/app/serializers/spree/api/v3/admin/custom_field_serializer.rb +8 -3
  92. data/app/serializers/spree/api/v3/admin/customer_group_serializer.rb +27 -0
  93. data/app/serializers/spree/api/v3/admin/customer_serializer.rb +58 -2
  94. data/app/serializers/spree/api/v3/admin/dashboard_analytics_serializer.rb +143 -0
  95. data/app/serializers/spree/api/v3/admin/export_serializer.rb +40 -0
  96. data/app/serializers/spree/api/v3/admin/fulfillment_serializer.rb +2 -6
  97. data/app/serializers/spree/api/v3/admin/{asset_serializer.rb → gift_card_batch_serializer.rb} +1 -1
  98. data/app/serializers/spree/api/v3/admin/gift_card_serializer.rb +39 -4
  99. data/app/serializers/spree/api/v3/admin/invitation_serializer.rb +64 -0
  100. data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +4 -16
  101. data/app/serializers/spree/api/v3/admin/media_serializer.rb +24 -2
  102. data/app/serializers/spree/api/v3/admin/option_type_serializer.rb +4 -1
  103. data/app/serializers/spree/api/v3/admin/option_value_serializer.rb +4 -1
  104. data/app/serializers/spree/api/v3/admin/order_serializer.rb +21 -6
  105. data/app/serializers/spree/api/v3/admin/payment_method_serializer.rb +11 -2
  106. data/app/serializers/spree/api/v3/admin/payment_serializer.rb +2 -6
  107. data/app/serializers/spree/api/v3/admin/payment_source_serializer.rb +4 -1
  108. data/app/serializers/spree/api/v3/admin/price_list_serializer.rb +51 -0
  109. data/app/serializers/spree/api/v3/admin/price_rule_serializer.rb +55 -0
  110. data/app/serializers/spree/api/v3/admin/price_serializer.rb +4 -0
  111. data/app/serializers/spree/api/v3/admin/product_publication_serializer.rb +11 -0
  112. data/app/serializers/spree/api/v3/admin/product_serializer.rb +34 -10
  113. data/app/serializers/spree/api/v3/admin/promotion_action_serializer.rb +71 -0
  114. data/app/serializers/spree/api/v3/admin/promotion_rule_serializer.rb +85 -0
  115. data/app/serializers/spree/api/v3/admin/promotion_serializer.rb +41 -0
  116. data/app/serializers/spree/api/v3/admin/refund_serializer.rb +4 -2
  117. data/app/serializers/spree/api/v3/admin/role_serializer.rb +17 -0
  118. data/app/serializers/spree/api/v3/admin/stock_item_serializer.rb +16 -1
  119. data/app/serializers/spree/api/v3/admin/stock_location_serializer.rb +11 -2
  120. data/app/serializers/spree/api/v3/admin/stock_reservation_serializer.rb +46 -0
  121. data/app/serializers/spree/api/v3/admin/stock_transfer_serializer.rb +37 -0
  122. data/app/serializers/spree/api/v3/admin/store_credit_category_serializer.rb +19 -0
  123. data/app/serializers/spree/api/v3/admin/store_credit_serializer.rb +11 -5
  124. data/app/serializers/spree/api/v3/admin/store_serializer.rb +55 -0
  125. data/app/serializers/spree/api/v3/admin/tax_category_serializer.rb +4 -2
  126. data/app/serializers/spree/api/v3/admin/variant_serializer.rb +37 -6
  127. data/app/serializers/spree/api/v3/admin/webhook_delivery_serializer.rb +45 -0
  128. data/app/serializers/spree/api/v3/admin/webhook_endpoint_serializer.rb +69 -0
  129. data/app/serializers/spree/api/v3/channel_serializer.rb +14 -0
  130. data/app/serializers/spree/api/v3/custom_field_serializer.rb +9 -10
  131. data/app/serializers/spree/api/v3/customer_serializer.rb +5 -0
  132. data/app/serializers/spree/api/v3/market_serializer.rb +2 -1
  133. data/app/serializers/spree/api/v3/media_serializer.rb +8 -6
  134. data/app/serializers/spree/api/v3/order_serializer.rb +6 -1
  135. data/app/serializers/spree/api/v3/payment_method_serializer.rb +11 -2
  136. data/app/serializers/spree/api/v3/product_publication_serializer.rb +22 -0
  137. data/app/serializers/spree/api/v3/product_serializer.rb +6 -1
  138. data/app/serializers/spree/api/v3/stock_reservation_serializer.rb +10 -0
  139. data/config/locales/en.yml +2 -0
  140. data/config/routes.rb +235 -1
  141. data/lib/spree/api/configuration.rb +2 -2
  142. data/lib/spree/api/dependencies.rb +25 -1
  143. data/lib/spree/api/openapi/path_sorter.rb +126 -0
  144. data/lib/spree/api/openapi/schema_helper.rb +185 -6
  145. metadata +96 -8
  146. data/app/serializers/spree/api/v3/admin/shipping_category_serializer.rb +0 -14
@@ -0,0 +1,56 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ # CRUD for `Spree::PromotionRule` STI subclasses. Same shape as
6
+ # PromotionActionsController — only the registry differs
7
+ # (`Spree.promotions.rules` instead of `Spree.promotions.actions`).
8
+ class PromotionRulesController < ResourceController
9
+ include Spree::Api::V3::Admin::SubclassedResource
10
+
11
+ scoped_resource :promotions
12
+
13
+ subclassed_via -> { Spree.promotions.rules },
14
+ unknown_type_error: 'unknown_promotion_rule_type'
15
+
16
+ def types
17
+ authorize! :read, model_class
18
+
19
+ render json: { data: model_class.subclasses_with_preference_schema }
20
+ end
21
+
22
+ protected
23
+
24
+ def model_class
25
+ Spree::PromotionRule
26
+ end
27
+
28
+ def serializer_class
29
+ Spree.api.admin_promotion_rule_serializer
30
+ end
31
+
32
+ def permitted_params
33
+ params.permit(:type, preferences: {})
34
+ end
35
+
36
+ def set_parent
37
+ return if action_name == 'types'
38
+
39
+ @parent = Spree::Promotion.accessible_by(current_ability, :show)
40
+ .find_by_prefix_id!(params[:promotion_id])
41
+ end
42
+
43
+ def parent_association
44
+ :promotion_rules
45
+ end
46
+
47
+ private
48
+
49
+ def build_subclassed_resource(klass, attrs)
50
+ klass.new(attrs.merge(promotion: @parent))
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,78 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ class PromotionsController < ResourceController
6
+ scoped_resource :promotions
7
+
8
+ protected
9
+
10
+ def model_class
11
+ Spree::Promotion
12
+ end
13
+
14
+ def serializer_class
15
+ Spree.api.admin_promotion_serializer
16
+ end
17
+
18
+ def collection_includes
19
+ promotion_includes
20
+ end
21
+
22
+ def scope_includes
23
+ promotion_includes
24
+ end
25
+
26
+ # A single POST/PATCH /promotions can ship rules and actions
27
+ # alongside the basics; `Promotion#rules=` / `actions=` reconcile
28
+ # to the desired set. The nested allowlist below is the union of
29
+ # every built-in rule/action's expected keys. Plugin-defined
30
+ # subclasses can add to it — see `additional_permitted_attributes`
31
+ # below.
32
+ def permitted_params
33
+ normalize_params(params.permit(*permitted_attributes))
34
+ end
35
+
36
+ def permitted_attributes
37
+ [
38
+ :name, :description, :code, :path,
39
+ :starts_at, :expires_at, :usage_limit, :match_policy,
40
+ :kind, :multi_codes, :number_of_codes, :code_prefix,
41
+ :promotion_category_id,
42
+ rules: rule_attributes,
43
+ actions: action_attributes
44
+ ]
45
+ end
46
+
47
+ def rule_attributes
48
+ [:id, :type, { preferences: {} }, *subclassed_collection_attributes(Spree.promotions.rules)]
49
+ end
50
+
51
+ def action_attributes
52
+ [
53
+ :id, :type,
54
+ { preferences: {} },
55
+ { calculator: [:type, { preferences: {} }] },
56
+ *subclassed_collection_attributes(Spree.promotions.actions)
57
+ ]
58
+ end
59
+
60
+ # Pulls in plugin-defined permitted attributes from every
61
+ # registered rule/action subclass. Subclasses declare these via
62
+ # `additional_permitted_attributes` (e.g. `[product_ids: []]`).
63
+ def subclassed_collection_attributes(registry)
64
+ registry.flat_map do |klass|
65
+ klass.respond_to?(:additional_permitted_attributes) ? klass.additional_permitted_attributes : []
66
+ end.uniq
67
+ end
68
+
69
+ private
70
+
71
+ def promotion_includes
72
+ [:stores, :promotion_actions, :promotion_rules]
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -2,24 +2,42 @@ module Spree
2
2
  module Api
3
3
  module V3
4
4
  module Admin
5
+ # Mirrors Admin::BaseController's concerns. Both classes anchor parallel
6
+ # inheritance branches (V3::BaseController vs V3::ResourceController);
7
+ # any concern added here MUST also be added to Admin::BaseController.
5
8
  class ResourceController < Spree::Api::V3::ResourceController
6
- # Require secret API key for all Admin API requests
7
- before_action :authenticate_secret_key!
8
-
9
- # Admin API responses must never be cached
10
- after_action :set_no_store_cache
9
+ include Spree::Api::V3::AdminAuthentication
10
+ include Spree::Api::V3::ScopedAuthorization
11
11
 
12
12
  protected
13
13
 
14
- # Override JWT audience to require admin tokens
15
- def expected_audience
16
- JWT_AUDIENCE_ADMIN
14
+ def authenticate_request!
15
+ authenticate_admin!
17
16
  end
18
17
 
19
- private
18
+ # Render error from ServiceModule::Result, extracting ActiveModel::Errors
19
+ # from the ResultError wrapper to get proper validation_error responses.
20
+ def render_result_error(result)
21
+ error = result.error
22
+ errors = error.respond_to?(:value) ? error.value : error
23
+
24
+ if errors.is_a?(ActiveModel::Errors)
25
+ render_validation_error(errors)
26
+ else
27
+ render_service_error(error)
28
+ end
29
+ end
30
+
31
+ def decode_ids(ids, klass)
32
+ Array(ids).map do |id|
33
+ Spree::PrefixedId.prefixed_id?(id) ? klass.find_by_param!(id).id : id
34
+ end
35
+ end
20
36
 
21
- def set_no_store_cache
22
- response.headers['Cache-Control'] = 'private, no-store'
37
+ def decode_prefixed_ids(ids)
38
+ Array(ids).map do |id|
39
+ Spree::PrefixedId.prefixed_id?(id) ? Spree::PrefixedId.decode_prefixed_id(id) : id
40
+ end
23
41
  end
24
42
  end
25
43
  end
@@ -0,0 +1,29 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ # Read-only list of roles available for staff role pickers (invite +
6
+ # edit forms). Roles are global, not per-store; CRUD is handled
7
+ # outside the SPA today and may grow into a richer permissions UI
8
+ # later. The controller ignores `current_store` for that reason.
9
+ class RolesController < ResourceController
10
+ scoped_resource :settings
11
+
12
+ protected
13
+
14
+ def model_class
15
+ Spree::Role
16
+ end
17
+
18
+ def serializer_class
19
+ Spree.api.admin_role_serializer
20
+ end
21
+
22
+ def scope
23
+ Spree::Role.accessible_by(current_ability, :show)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ # Stock items are auto-created when a variant lands at a stock
6
+ # location, so there's deliberately no `create` route — use the
7
+ # variants / stock-locations endpoints for that flow.
8
+ class StockItemsController < ResourceController
9
+ scoped_resource :settings
10
+
11
+ protected
12
+
13
+ def model_class
14
+ Spree::StockItem
15
+ end
16
+
17
+ def serializer_class
18
+ Spree.api.admin_stock_item_serializer
19
+ end
20
+
21
+ def collection_includes
22
+ [:stock_location, :variant]
23
+ end
24
+
25
+ # `StockItem.for_store` already applies its own `distinct`, and
26
+ # `id`-asc gives a stable order across edits (variant.position
27
+ # alone isn't unique — see git blame for the row-jumping bug).
28
+ def apply_collection_sort(collection)
29
+ collection.order(Spree::StockItem.arel_table[:id].asc)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,36 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ class StockLocationsController < ResourceController
6
+ scoped_resource :settings
7
+
8
+ protected
9
+
10
+ def model_class
11
+ Spree::StockLocation
12
+ end
13
+
14
+ def serializer_class
15
+ Spree.api.admin_stock_location_serializer
16
+ end
17
+
18
+ def scope
19
+ super.order_default
20
+ end
21
+
22
+ def permitted_params
23
+ params.permit(
24
+ :name, :admin_name, :active, :default,
25
+ :kind, :propagate_all_variants, :backorderable_default,
26
+ :address1, :address2, :city, :zipcode, :phone, :company,
27
+ :country_iso, :state_abbr, :state_name,
28
+ :pickup_enabled, :pickup_stock_policy,
29
+ :pickup_ready_in_minutes, :pickup_instructions
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ class StockReservationsController < ResourceController
6
+ scoped_resource :stock
7
+
8
+ protected
9
+
10
+ def model_class
11
+ Spree::StockReservation
12
+ end
13
+
14
+ def serializer_class
15
+ Spree.api.admin_stock_reservation_serializer
16
+ end
17
+
18
+ def scope
19
+ Spree::StockReservation.for_store(current_store)
20
+ end
21
+
22
+ def collection_includes
23
+ [{ stock_item: [:variant, :stock_location], line_item: [], order: [] }]
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,75 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ # Inventory movement between stock locations, or vendor → location
6
+ # for receives. Pass `source_location_id` for transfers; omit it to
7
+ # record an external receive.
8
+ class StockTransfersController < ResourceController
9
+ scoped_resource :settings
10
+
11
+ def create
12
+ authorize!(:create, model_class)
13
+
14
+ destination = Spree::StockLocation.find_by_prefix_id!(params[:destination_location_id])
15
+ source = params[:source_location_id].present? ?
16
+ Spree::StockLocation.find_by_prefix_id!(params[:source_location_id]) : nil
17
+
18
+ variants_map = build_variants_map
19
+ if variants_map.empty?
20
+ return render_error(
21
+ code: 'invalid_variants',
22
+ message: Spree.t('stock_transfer.errors.must_have_variant'),
23
+ status: :unprocessable_content
24
+ )
25
+ end
26
+
27
+ @resource = source ?
28
+ Spree::StockTransfer.new(reference: params[:reference]).tap { |t| t.transfer(source, destination, variants_map) } :
29
+ Spree::StockTransfer.new(reference: params[:reference]).tap { |t| t.receive(destination, variants_map) }
30
+
31
+ if @resource.persisted?
32
+ render json: serialize_resource(@resource), status: :created
33
+ else
34
+ render_validation_error(@resource.errors)
35
+ end
36
+ end
37
+
38
+ protected
39
+
40
+ def model_class
41
+ Spree::StockTransfer
42
+ end
43
+
44
+ def serializer_class
45
+ Spree.api.admin_stock_transfer_serializer
46
+ end
47
+
48
+ def collection_includes
49
+ [:source_location, :destination_location]
50
+ end
51
+
52
+ private
53
+
54
+ # Variants the merchant doesn't have access to are dropped silently;
55
+ # if the resulting map is empty the action surfaces a 422
56
+ # `invalid_variants` so callers can distinguish "nothing supplied"
57
+ # from "all variants were rejected." A single SELECT covers any
58
+ # number of variants instead of N round-trips.
59
+ def build_variants_map
60
+ entries = params.permit(variants: [:variant_id, :quantity]).fetch(:variants, [])
61
+ quantities_by_id = entries.each_with_object({}) do |entry, hash|
62
+ decoded = Spree::PrefixedId.decode_prefixed_id(entry[:variant_id])
63
+ hash[decoded.to_i] = entry[:quantity].to_i if decoded
64
+ end
65
+
66
+ Spree::Variant.where(id: quantities_by_id.keys).each_with_object({}) do |variant, acc|
67
+ quantity = quantities_by_id[variant.id]
68
+ acc[variant] = quantity if quantity&.positive?
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,53 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ class StoreController < Admin::BaseController
6
+ scoped_resource :settings
7
+
8
+ # GET /api/v3/admin/store
9
+ def show
10
+ authorize! :show, current_store
11
+ render json: serialize_store
12
+ end
13
+
14
+ # PATCH /api/v3/admin/store
15
+ def update
16
+ authorize! :update, current_store
17
+
18
+ if current_store.update(permitted_params)
19
+ render json: serialize_store
20
+ else
21
+ render_validation_error(current_store.errors)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def serialize_store
28
+ serializer_class.new(current_store, params: serializer_params).to_h
29
+ end
30
+
31
+ def serializer_class
32
+ Spree.api.admin_store_serializer
33
+ end
34
+
35
+ def permitted_params
36
+ params.permit(
37
+ :name,
38
+ :preferred_admin_locale,
39
+ :preferred_timezone,
40
+ :preferred_weight_unit,
41
+ :preferred_unit_system,
42
+ :mail_from_address,
43
+ :customer_support_email,
44
+ :new_order_notifications_email,
45
+ :preferred_send_consumer_transactional_emails,
46
+ :mailer_logo
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ class StoreCreditCategoriesController < ResourceController
6
+ scoped_resource :settings
7
+
8
+ protected
9
+
10
+ def model_class
11
+ Spree::StoreCreditCategory
12
+ end
13
+
14
+ def serializer_class
15
+ Spree.api.admin_store_credit_category_serializer
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,51 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ class TagsController < BaseController
6
+ skip_scope_check!
7
+
8
+ MAX_RESULTS = 50
9
+
10
+ def index
11
+ taggable_type = params[:taggable_type].to_s
12
+ unless allowed_taggable_types.include?(taggable_type)
13
+ render_error(
14
+ code: 'invalid_taggable_type',
15
+ message: "taggable_type must be one of #{allowed_taggable_types.join(', ')}",
16
+ status: :unprocessable_content
17
+ )
18
+ return
19
+ end
20
+
21
+ scope = ActsAsTaggableOn::Tag.
22
+ joins(:taggings).
23
+ where(ActsAsTaggableOn.taggings_table => { taggable_type: taggable_type, context: 'tags' }).
24
+ distinct.
25
+ order(:name).
26
+ limit(MAX_RESULTS)
27
+
28
+ if params[:q].present?
29
+ # Escape LIKE wildcards in user input so a query like "foo_" matches
30
+ # only the literal underscore, not any single character.
31
+ escaped = params[:q].to_s.downcase.gsub(/[\\%_]/) { |c| "\\#{c}" }
32
+ scope = scope.where('LOWER(name) LIKE ? ESCAPE ?', "%#{escaped}%", '\\')
33
+ end
34
+
35
+ render json: { data: scope.pluck(:name).map { |name| { name: name } } }
36
+ end
37
+
38
+ private
39
+
40
+ # Sourced from `Spree.taggable_types` (registered in
41
+ # `Spree::Core::Engine`'s after_initialize block). Apps extend the
42
+ # list in an initializer without overriding this controller:
43
+ # Spree.taggable_types << 'MyApp::Vendor'
44
+ def allowed_taggable_types
45
+ Spree.taggable_types
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,21 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ class TaxCategoriesController < ResourceController
6
+ scoped_resource :settings
7
+
8
+ protected
9
+
10
+ def model_class
11
+ Spree::TaxCategory
12
+ end
13
+
14
+ def serializer_class
15
+ Spree.api.admin_tax_category_serializer
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ class VariantsController < ResourceController
6
+ scoped_resource :products
7
+
8
+ protected
9
+
10
+ def model_class
11
+ Spree::Variant
12
+ end
13
+
14
+ def serializer_class
15
+ Spree.api.admin_variant_serializer
16
+ end
17
+
18
+ def scope
19
+ current_store.variants.eligible
20
+ end
21
+
22
+ def scope_includes
23
+ [
24
+ :prices, stock_items: :stock_location,
25
+ option_values: :option_type,
26
+ primary_media: [attachment_attachment: :blob]
27
+ ]
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,49 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ # Nested under WebhookEndpoint — deliveries are always read in the
6
+ # context of their endpoint (the delivery log on the endpoint detail
7
+ # page) and never accessed by ID at the top level.
8
+ class WebhookDeliveriesController < ResourceController
9
+ scoped_resource :settings
10
+
11
+ # POST /api/v3/admin/webhook_endpoints/:webhook_endpoint_id/deliveries/:id/redeliver
12
+ #
13
+ # Creates a new delivery row with the same payload + event_name and
14
+ # queues it. The original row is preserved for audit history.
15
+ #
16
+ # @return [Hash] the serialized newly-queued {Spree::WebhookDelivery},
17
+ # HTTP 201.
18
+ def redeliver
19
+ @resource = find_resource
20
+ authorize!(:update, webhook_endpoint)
21
+
22
+ new_delivery = @resource.redeliver!
23
+ render json: serialize_resource(new_delivery), status: :created
24
+ end
25
+
26
+ protected
27
+
28
+ def model_class
29
+ Spree::WebhookDelivery
30
+ end
31
+
32
+ def serializer_class
33
+ Spree.api.admin_webhook_delivery_serializer
34
+ end
35
+
36
+ def scope
37
+ webhook_endpoint.webhook_deliveries.recent
38
+ end
39
+
40
+ def webhook_endpoint
41
+ @webhook_endpoint ||= current_store.webhook_endpoints.find_by_prefix_id!(
42
+ params[:webhook_endpoint_id]
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end