spree_api 5.4.3 → 5.5.0.rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +36 -0
- data/app/controllers/concerns/spree/api/v3/admin/auth_cookies.rb +62 -0
- data/app/controllers/concerns/spree/api/v3/admin/role_grant_guard.rb +52 -0
- data/app/controllers/concerns/spree/api/v3/admin/subclassed_resource.rb +149 -0
- data/app/controllers/concerns/spree/api/v3/admin_authentication.rb +54 -0
- data/app/controllers/concerns/spree/api/v3/bulk_operations.rb +103 -0
- data/app/controllers/concerns/spree/api/v3/channel_resolution.rb +60 -0
- data/app/controllers/concerns/spree/api/v3/error_handler.rb +4 -0
- data/app/controllers/concerns/spree/api/v3/params_normalizer.rb +84 -0
- data/app/controllers/concerns/spree/api/v3/scoped_authorization.rb +104 -0
- data/app/controllers/concerns/spree/api/v3/store/search_provider_support.rb +35 -1
- data/app/controllers/spree/api/v3/admin/admin_users_controller.rb +109 -0
- data/app/controllers/spree/api/v3/admin/allowed_origins_controller.rb +25 -0
- data/app/controllers/spree/api/v3/admin/api_keys_controller.rb +84 -0
- data/app/controllers/spree/api/v3/admin/auth_controller.rb +134 -0
- data/app/controllers/spree/api/v3/admin/base_controller.rb +3 -17
- data/app/controllers/spree/api/v3/admin/categories_controller.rb +25 -0
- data/app/controllers/spree/api/v3/admin/channels_controller.rb +65 -0
- data/app/controllers/spree/api/v3/admin/countries_controller.rb +38 -0
- data/app/controllers/spree/api/v3/admin/coupon_codes_controller.rb +33 -0
- data/app/controllers/spree/api/v3/admin/custom_field_definitions_controller.rb +34 -0
- data/app/controllers/spree/api/v3/admin/custom_fields_controller.rb +129 -0
- data/app/controllers/spree/api/v3/admin/customer_groups_controller.rb +31 -0
- data/app/controllers/spree/api/v3/admin/customers/addresses_controller.rb +83 -0
- data/app/controllers/spree/api/v3/admin/customers/base_controller.rb +33 -0
- data/app/controllers/spree/api/v3/admin/customers/credit_cards_controller.rb +25 -0
- data/app/controllers/spree/api/v3/admin/customers/store_credits_controller.rb +92 -0
- data/app/controllers/spree/api/v3/admin/customers_controller.rb +119 -0
- data/app/controllers/spree/api/v3/admin/dashboard_controller.rb +44 -0
- data/app/controllers/spree/api/v3/admin/direct_uploads_controller.rb +40 -0
- data/app/controllers/spree/api/v3/admin/exports_controller.rb +136 -0
- data/app/controllers/spree/api/v3/admin/gift_card_batches_controller.rb +31 -0
- data/app/controllers/spree/api/v3/admin/gift_cards_controller.rb +33 -0
- data/app/controllers/spree/api/v3/admin/invitation_acceptances_controller.rb +138 -0
- data/app/controllers/spree/api/v3/admin/invitations_controller.rb +81 -0
- data/app/controllers/spree/api/v3/admin/markets_controller.rb +42 -0
- data/app/controllers/spree/api/v3/admin/me_controller.rb +69 -0
- data/app/controllers/spree/api/v3/admin/media_controller.rb +119 -0
- data/app/controllers/spree/api/v3/admin/option_types_controller.rb +34 -0
- data/app/controllers/spree/api/v3/admin/orders/adjustments_controller.rb +27 -0
- data/app/controllers/spree/api/v3/admin/orders/base_controller.rb +31 -0
- data/app/controllers/spree/api/v3/admin/orders/fulfillments_controller.rb +104 -0
- data/app/controllers/spree/api/v3/admin/orders/gift_cards_controller.rb +79 -0
- data/app/controllers/spree/api/v3/admin/orders/items_controller.rb +92 -0
- data/app/controllers/spree/api/v3/admin/orders/payments_controller.rb +90 -0
- data/app/controllers/spree/api/v3/admin/orders/refunds_controller.rb +53 -0
- data/app/controllers/spree/api/v3/admin/orders/store_credits_controller.rb +59 -0
- data/app/controllers/spree/api/v3/admin/orders_controller.rb +190 -0
- data/app/controllers/spree/api/v3/admin/payment_methods_controller.rb +73 -0
- data/app/controllers/spree/api/v3/admin/price_lists_controller.rb +179 -0
- data/app/controllers/spree/api/v3/admin/prices_controller.rb +157 -0
- data/app/controllers/spree/api/v3/admin/products/variants_controller.rb +48 -0
- data/app/controllers/spree/api/v3/admin/products_controller.rb +237 -0
- data/app/controllers/spree/api/v3/admin/promotion_actions_controller.rb +78 -0
- data/app/controllers/spree/api/v3/admin/promotion_rules_controller.rb +56 -0
- data/app/controllers/spree/api/v3/admin/promotions_controller.rb +78 -0
- data/app/controllers/spree/api/v3/admin/resource_controller.rb +29 -11
- data/app/controllers/spree/api/v3/admin/roles_controller.rb +29 -0
- data/app/controllers/spree/api/v3/admin/stock_items_controller.rb +35 -0
- data/app/controllers/spree/api/v3/admin/stock_locations_controller.rb +36 -0
- data/app/controllers/spree/api/v3/admin/stock_reservations_controller.rb +29 -0
- data/app/controllers/spree/api/v3/admin/stock_transfers_controller.rb +75 -0
- data/app/controllers/spree/api/v3/admin/store_controller.rb +53 -0
- data/app/controllers/spree/api/v3/admin/store_credit_categories_controller.rb +21 -0
- data/app/controllers/spree/api/v3/admin/tags_controller.rb +51 -0
- data/app/controllers/spree/api/v3/admin/tax_categories_controller.rb +21 -0
- data/app/controllers/spree/api/v3/admin/variants_controller.rb +33 -0
- data/app/controllers/spree/api/v3/admin/webhook_deliveries_controller.rb +49 -0
- data/app/controllers/spree/api/v3/admin/webhook_endpoints_controller.rb +75 -0
- data/app/controllers/spree/api/v3/resource_controller.rb +117 -8
- data/app/controllers/spree/api/v3/store/auth_controller.rb +8 -28
- data/app/controllers/spree/api/v3/store/base_controller.rb +6 -0
- data/app/controllers/spree/api/v3/store/carts_controller.rb +1 -0
- data/app/controllers/spree/api/v3/store/customers_controller.rb +6 -0
- data/app/controllers/spree/api/v3/store/newsletter_subscribers_controller.rb +77 -0
- data/app/controllers/spree/api/v3/store/products/filters_controller.rb +2 -2
- data/app/controllers/spree/api/v3/store/products_controller.rb +4 -3
- data/app/controllers/spree/api/v3/store/resource_controller.rb +10 -2
- data/app/jobs/spree/webhook_delivery_job.rb +5 -0
- data/app/models/spree/api_key_ability.rb +16 -0
- data/app/serializers/spree/api/v3/admin/address_serializer.rb +2 -6
- data/app/serializers/spree/api/v3/admin/adjustment_serializer.rb +3 -15
- data/app/serializers/spree/api/v3/admin/admin_user_serializer.rb +19 -3
- data/app/serializers/spree/api/v3/admin/allowed_origin_serializer.rb +2 -6
- data/app/serializers/spree/api/v3/admin/api_key_serializer.rb +42 -0
- data/app/serializers/spree/api/v3/admin/category_serializer.rb +4 -3
- data/app/serializers/spree/api/v3/admin/channel_serializer.rb +15 -0
- data/app/serializers/spree/api/v3/admin/country_serializer.rb +1 -1
- data/app/serializers/spree/api/v3/admin/coupon_code_serializer.rb +30 -0
- data/app/serializers/spree/api/v3/admin/credit_card_serializer.rb +4 -2
- data/app/serializers/spree/api/v3/admin/custom_field_definition_serializer.rb +21 -0
- data/app/serializers/spree/api/v3/admin/custom_field_serializer.rb +8 -3
- data/app/serializers/spree/api/v3/admin/customer_group_serializer.rb +27 -0
- data/app/serializers/spree/api/v3/admin/customer_serializer.rb +58 -2
- data/app/serializers/spree/api/v3/admin/dashboard_analytics_serializer.rb +143 -0
- data/app/serializers/spree/api/v3/admin/export_serializer.rb +40 -0
- data/app/serializers/spree/api/v3/admin/fulfillment_serializer.rb +2 -6
- data/app/serializers/spree/api/v3/admin/{asset_serializer.rb → gift_card_batch_serializer.rb} +1 -1
- data/app/serializers/spree/api/v3/admin/gift_card_serializer.rb +39 -4
- data/app/serializers/spree/api/v3/admin/invitation_serializer.rb +64 -0
- data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +4 -16
- data/app/serializers/spree/api/v3/admin/media_serializer.rb +24 -2
- data/app/serializers/spree/api/v3/admin/option_type_serializer.rb +4 -1
- data/app/serializers/spree/api/v3/admin/option_value_serializer.rb +4 -1
- data/app/serializers/spree/api/v3/admin/order_serializer.rb +21 -6
- data/app/serializers/spree/api/v3/admin/payment_method_serializer.rb +11 -2
- data/app/serializers/spree/api/v3/admin/payment_serializer.rb +2 -6
- data/app/serializers/spree/api/v3/admin/payment_source_serializer.rb +4 -1
- data/app/serializers/spree/api/v3/admin/price_list_serializer.rb +51 -0
- data/app/serializers/spree/api/v3/admin/price_rule_serializer.rb +55 -0
- data/app/serializers/spree/api/v3/admin/price_serializer.rb +4 -0
- data/app/serializers/spree/api/v3/admin/product_publication_serializer.rb +11 -0
- data/app/serializers/spree/api/v3/admin/product_serializer.rb +34 -10
- data/app/serializers/spree/api/v3/admin/promotion_action_serializer.rb +71 -0
- data/app/serializers/spree/api/v3/admin/promotion_rule_serializer.rb +85 -0
- data/app/serializers/spree/api/v3/admin/promotion_serializer.rb +41 -0
- data/app/serializers/spree/api/v3/admin/refund_serializer.rb +4 -2
- data/app/serializers/spree/api/v3/admin/role_serializer.rb +17 -0
- data/app/serializers/spree/api/v3/admin/stock_item_serializer.rb +16 -1
- data/app/serializers/spree/api/v3/admin/stock_location_serializer.rb +11 -2
- data/app/serializers/spree/api/v3/admin/stock_reservation_serializer.rb +46 -0
- data/app/serializers/spree/api/v3/admin/stock_transfer_serializer.rb +37 -0
- data/app/serializers/spree/api/v3/admin/store_credit_category_serializer.rb +19 -0
- data/app/serializers/spree/api/v3/admin/store_credit_serializer.rb +11 -5
- data/app/serializers/spree/api/v3/admin/store_serializer.rb +55 -0
- data/app/serializers/spree/api/v3/admin/tax_category_serializer.rb +4 -2
- data/app/serializers/spree/api/v3/admin/variant_serializer.rb +37 -6
- data/app/serializers/spree/api/v3/admin/webhook_delivery_serializer.rb +45 -0
- data/app/serializers/spree/api/v3/admin/webhook_endpoint_serializer.rb +69 -0
- data/app/serializers/spree/api/v3/channel_serializer.rb +14 -0
- data/app/serializers/spree/api/v3/custom_field_serializer.rb +9 -10
- data/app/serializers/spree/api/v3/customer_serializer.rb +5 -0
- data/app/serializers/spree/api/v3/market_serializer.rb +2 -1
- data/app/serializers/spree/api/v3/media_serializer.rb +8 -6
- data/app/serializers/spree/api/v3/order_serializer.rb +6 -1
- data/app/serializers/spree/api/v3/payment_method_serializer.rb +11 -2
- data/app/serializers/spree/api/v3/product_publication_serializer.rb +22 -0
- data/app/serializers/spree/api/v3/product_serializer.rb +6 -1
- data/app/serializers/spree/api/v3/stock_reservation_serializer.rb +10 -0
- data/config/locales/en.yml +2 -0
- data/config/routes.rb +235 -1
- data/lib/spree/api/configuration.rb +2 -2
- data/lib/spree/api/dependencies.rb +25 -1
- data/lib/spree/api/openapi/path_sorter.rb +126 -0
- data/lib/spree/api/openapi/schema_helper.rb +185 -6
- data/lib/spree/api/testing_support/v3/base.rb +28 -0
- metadata +98 -8
- data/app/serializers/spree/api/v3/admin/shipping_category_serializer.rb +0 -14
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Store
|
|
5
|
+
class NewsletterSubscribersController < Store::BaseController
|
|
6
|
+
rate_limit to: Spree::Api::Config[:rate_limit_register],
|
|
7
|
+
within: Spree::Api::Config[:rate_limit_window].seconds,
|
|
8
|
+
store: Rails.cache,
|
|
9
|
+
only: [:create, :verify],
|
|
10
|
+
with: RATE_LIMIT_RESPONSE
|
|
11
|
+
|
|
12
|
+
# POST /api/v3/store/newsletter_subscribers
|
|
13
|
+
def create
|
|
14
|
+
subscriber = Spree::NewsletterSubscriber.subscribe(
|
|
15
|
+
email: params[:email],
|
|
16
|
+
user: current_user,
|
|
17
|
+
store: current_store,
|
|
18
|
+
redirect_url: validated_redirect_url
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if subscriber.errors.any?
|
|
22
|
+
render_errors(subscriber.errors)
|
|
23
|
+
else
|
|
24
|
+
render json: serialize_resource(subscriber), status: :created
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# POST /api/v3/store/newsletter_subscribers/verify
|
|
29
|
+
def verify
|
|
30
|
+
token = params[:token]
|
|
31
|
+
|
|
32
|
+
if token.blank?
|
|
33
|
+
return render_error(
|
|
34
|
+
code: ERROR_CODES[:parameter_missing],
|
|
35
|
+
message: 'token is required',
|
|
36
|
+
status: :unprocessable_content
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
subscriber = Spree::NewsletterSubscriber.for_store(current_store).unverified.find_by(verification_token: token)
|
|
41
|
+
|
|
42
|
+
unless subscriber
|
|
43
|
+
return render_error(
|
|
44
|
+
code: ERROR_CODES[:invalid_token],
|
|
45
|
+
message: Spree.t(:newsletter_verification_token_invalid, scope: :api),
|
|
46
|
+
status: :unprocessable_content
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
Spree::Newsletter::Verify.new(subscriber: subscriber).call
|
|
51
|
+
|
|
52
|
+
render json: serialize_resource(subscriber)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
protected
|
|
56
|
+
|
|
57
|
+
def serializer_class
|
|
58
|
+
Spree::Api::V3::NewsletterSubscriberSerializer
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
# Drop redirect_url when it isn't in the store's allow-list — secure-by-default,
|
|
64
|
+
# mirrors password_resets. Returning nil omits it from the webhook payload rather
|
|
65
|
+
# than rejecting the request, so callers can't probe the allow-list via 4xx errors.
|
|
66
|
+
def validated_redirect_url
|
|
67
|
+
redirect_url = params[:redirect_url]
|
|
68
|
+
return nil if redirect_url.blank?
|
|
69
|
+
return nil unless current_store.allowed_origin?(redirect_url)
|
|
70
|
+
|
|
71
|
+
redirect_url
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -29,7 +29,7 @@ module Spree
|
|
|
29
29
|
|
|
30
30
|
def filters_cache_key
|
|
31
31
|
products_table = Spree::Product.table_name
|
|
32
|
-
stats = current_store.products.
|
|
32
|
+
stats = current_store.products.available(Time.current, current_currency)
|
|
33
33
|
.pick(Arel.sql("MAX(#{products_table}.updated_at)"), Arel.sql("COUNT(DISTINCT #{products_table}.id)"))
|
|
34
34
|
max_updated = stats&.first&.to_i
|
|
35
35
|
product_count = stats&.last || 0
|
|
@@ -50,7 +50,7 @@ module Spree
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def filters_scope
|
|
53
|
-
scope = current_store.products.
|
|
53
|
+
scope = current_store.products.available(Time.current, current_currency)
|
|
54
54
|
scope = scope.in_category(category) if category.present?
|
|
55
55
|
scope.accessible_by(current_ability, :show)
|
|
56
56
|
end
|
|
@@ -29,15 +29,16 @@ module Spree
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def scope
|
|
32
|
-
super.
|
|
32
|
+
super.available(Time.current, Spree::Current.currency)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
# these scopes are not automatically picked by ar_lazy_preload gem and we need to explicitly include them
|
|
36
36
|
def scope_includes
|
|
37
37
|
[
|
|
38
|
+
product_publications: [],
|
|
38
39
|
primary_media: [attachment_attachment: :blob],
|
|
39
|
-
master: [:prices, stock_items: :stock_location],
|
|
40
|
-
variants: [:prices, stock_items: :stock_location]
|
|
40
|
+
master: [:prices, stock_items: [:stock_location, :active_stock_reservations]],
|
|
41
|
+
variants: [:prices, stock_items: [:stock_location, :active_stock_reservations]]
|
|
41
42
|
]
|
|
42
43
|
end
|
|
43
44
|
|
|
@@ -2,9 +2,17 @@ module Spree
|
|
|
2
2
|
module Api
|
|
3
3
|
module V3
|
|
4
4
|
module Store
|
|
5
|
+
# Mirrors Store::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 Store::BaseController.
|
|
5
8
|
class ResourceController < Spree::Api::V3::ResourceController
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
include Spree::Api::V3::ChannelResolution
|
|
10
|
+
|
|
11
|
+
protected
|
|
12
|
+
|
|
13
|
+
def authenticate_request!
|
|
14
|
+
authenticate_api_key!
|
|
15
|
+
end
|
|
8
16
|
end
|
|
9
17
|
end
|
|
10
18
|
end
|
|
@@ -4,7 +4,12 @@ module Spree
|
|
|
4
4
|
class WebhookDeliveryJob < Spree::BaseJob
|
|
5
5
|
queue_as Spree.queues.webhooks
|
|
6
6
|
|
|
7
|
+
# Webhook delivery hits external endpoints; broad retry covers network timeouts,
|
|
8
|
+
# 5xx, DNS failures, etc.
|
|
7
9
|
retry_on StandardError, wait: :polynomially_longer, attempts: 5
|
|
10
|
+
# Must come after `retry_on StandardError` so DeserializationError lands in discard
|
|
11
|
+
# (ActiveJob handler lookup is reverse-declaration-order).
|
|
12
|
+
discard_on ActiveJob::DeserializationError
|
|
8
13
|
|
|
9
14
|
# Accept optional second argument for backward compatibility with jobs
|
|
10
15
|
# enqueued before this change was deployed.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# CanCanCan ability used for API-key-authenticated admin requests.
|
|
3
|
+
# Grants full access — authorization happens at the scope-check layer
|
|
4
|
+
# (Spree::Api::V3::ScopedAuthorization), not at the per-record CanCanCan
|
|
5
|
+
# layer. This exists so that `accessible_by(current_ability, :show)` in
|
|
6
|
+
# admin controllers returns the unrestricted scope (it would otherwise
|
|
7
|
+
# require a real Spree::Ability with role lookups, which doesn't apply
|
|
8
|
+
# to API key principals).
|
|
9
|
+
class ApiKeyAbility
|
|
10
|
+
include CanCan::Ability
|
|
11
|
+
|
|
12
|
+
def initialize(_options = {})
|
|
13
|
+
can :manage, :all
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -5,18 +5,14 @@ module Spree
|
|
|
5
5
|
class AddressSerializer < V3::AddressSerializer
|
|
6
6
|
typelize label: [:string, nullable: true],
|
|
7
7
|
customer_id: [:string, nullable: true],
|
|
8
|
-
metadata: 'Record<string, unknown>
|
|
8
|
+
metadata: 'Record<string, unknown>'
|
|
9
9
|
|
|
10
|
-
attributes :label,
|
|
10
|
+
attributes :label, :metadata,
|
|
11
11
|
created_at: :iso8601, updated_at: :iso8601
|
|
12
12
|
|
|
13
13
|
attribute :customer_id do |address|
|
|
14
14
|
address.user&.prefixed_id
|
|
15
15
|
end
|
|
16
|
-
|
|
17
|
-
attribute :metadata do |address|
|
|
18
|
-
address.metadata.presence
|
|
19
|
-
end
|
|
20
16
|
end
|
|
21
17
|
end
|
|
22
18
|
end
|
|
@@ -4,31 +4,19 @@ module Spree
|
|
|
4
4
|
module Admin
|
|
5
5
|
class AdjustmentSerializer < V3::BaseSerializer
|
|
6
6
|
typelize label: :string, amount: :string, display_amount: :string,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
adjustable_type: :string, adjustable_id: :string,
|
|
10
|
-
order_id: [:string, nullable: true],
|
|
11
|
-
source_id: [:string, nullable: true]
|
|
7
|
+
included: :boolean,
|
|
8
|
+
order_id: [:string, nullable: true]
|
|
12
9
|
|
|
13
|
-
attributes :label, :display_amount, :
|
|
14
|
-
:source_type, :adjustable_type,
|
|
10
|
+
attributes :label, :display_amount, :included,
|
|
15
11
|
created_at: :iso8601, updated_at: :iso8601
|
|
16
12
|
|
|
17
13
|
attribute :amount do |adjustment|
|
|
18
14
|
adjustment.amount.to_s
|
|
19
15
|
end
|
|
20
16
|
|
|
21
|
-
attribute :adjustable_id do |adjustment|
|
|
22
|
-
adjustment.adjustable&.prefixed_id
|
|
23
|
-
end
|
|
24
|
-
|
|
25
17
|
attribute :order_id do |adjustment|
|
|
26
18
|
adjustment.order&.prefixed_id
|
|
27
19
|
end
|
|
28
|
-
|
|
29
|
-
attribute :source_id do |adjustment|
|
|
30
|
-
adjustment.source&.prefixed_id
|
|
31
|
-
end
|
|
32
20
|
end
|
|
33
21
|
end
|
|
34
22
|
end
|
|
@@ -3,11 +3,27 @@ module Spree
|
|
|
3
3
|
module V3
|
|
4
4
|
module Admin
|
|
5
5
|
class AdminUserSerializer < V3::BaseSerializer
|
|
6
|
-
typelize email: :string,
|
|
7
|
-
|
|
6
|
+
typelize email: :string,
|
|
7
|
+
first_name: [:string, nullable: true],
|
|
8
|
+
last_name: [:string, nullable: true],
|
|
9
|
+
full_name: [:string, nullable: true],
|
|
10
|
+
roles: 'Array<{ id: string; name: string }>'
|
|
8
11
|
|
|
9
|
-
attributes :email, :first_name, :last_name,
|
|
12
|
+
attributes :email, :first_name, :last_name, :full_name,
|
|
10
13
|
created_at: :iso8601, updated_at: :iso8601
|
|
14
|
+
|
|
15
|
+
# Roles assigned to this user *for the current store*. Each store
|
|
16
|
+
# gets its own role set via `Spree::RoleUser`, so this attribute is
|
|
17
|
+
# scoped against `current_store` rather than returning every role
|
|
18
|
+
# the user might have on other stores. Block receives `params`
|
|
19
|
+
# only when Alba passes it through the `serializer_params` hash —
|
|
20
|
+
# we fall back to `Spree::Current.store` if not.
|
|
21
|
+
attribute :roles do |user, params|
|
|
22
|
+
store = params&.dig(:store) || Spree::Current.store
|
|
23
|
+
scope = user.role_users
|
|
24
|
+
scope = scope.where(resource: store) if store
|
|
25
|
+
scope.includes(:role).map { |ru| { id: ru.role.prefixed_id, name: ru.role.name } }
|
|
26
|
+
end
|
|
11
27
|
end
|
|
12
28
|
end
|
|
13
29
|
end
|
|
@@ -5,13 +5,9 @@ module Spree
|
|
|
5
5
|
module V3
|
|
6
6
|
module Admin
|
|
7
7
|
class AllowedOriginSerializer < V3::BaseSerializer
|
|
8
|
-
typelize
|
|
8
|
+
typelize origin: :string
|
|
9
9
|
|
|
10
|
-
attributes :
|
|
11
|
-
|
|
12
|
-
attribute :store_id do |allowed_origin|
|
|
13
|
-
allowed_origin.store&.prefixed_id
|
|
14
|
-
end
|
|
10
|
+
attributes :origin, created_at: :iso8601, updated_at: :iso8601
|
|
15
11
|
end
|
|
16
12
|
end
|
|
17
13
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Admin
|
|
5
|
+
# Admin API serializer for {Spree::ApiKey}.
|
|
6
|
+
#
|
|
7
|
+
# Never exposes `token` or `token_digest` — only the 12-char
|
|
8
|
+
# `token_prefix` (e.g. `sk_abc123def`) so existing keys can be
|
|
9
|
+
# identified in the UI without leaking material that would let an
|
|
10
|
+
# attacker make requests. The full plaintext token is delivered
|
|
11
|
+
# exactly once, as the response body of `POST /api/v3/admin/api_keys`,
|
|
12
|
+
# via {#plaintext_token} below — it is `nil` everywhere else.
|
|
13
|
+
class ApiKeySerializer < V3::BaseSerializer
|
|
14
|
+
typelize name: :string,
|
|
15
|
+
key_type: :string,
|
|
16
|
+
token_prefix: [:string, nullable: true],
|
|
17
|
+
plaintext_token: [:string, nullable: true],
|
|
18
|
+
scopes: [:string, multi: true],
|
|
19
|
+
revoked_at: [:string, nullable: true],
|
|
20
|
+
last_used_at: [:string, nullable: true],
|
|
21
|
+
created_by_email: [:string, nullable: true]
|
|
22
|
+
|
|
23
|
+
attributes :name, :key_type, :token_prefix, :scopes,
|
|
24
|
+
created_at: :iso8601, updated_at: :iso8601,
|
|
25
|
+
revoked_at: :iso8601, last_used_at: :iso8601
|
|
26
|
+
|
|
27
|
+
# Returned only on the create response — `plaintext_token` is held in
|
|
28
|
+
# memory on the model after `generate_token` and is never persisted
|
|
29
|
+
# for secret keys, so we serialize it whenever it's available rather
|
|
30
|
+
# than gating on the action.
|
|
31
|
+
attribute :plaintext_token do |key|
|
|
32
|
+
key.plaintext_token
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
attribute :created_by_email do |key|
|
|
36
|
+
key.created_by&.email
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -5,10 +5,11 @@ module Spree
|
|
|
5
5
|
# Admin API Category Serializer
|
|
6
6
|
# Full category data including admin-only fields
|
|
7
7
|
class CategorySerializer < V3::CategorySerializer
|
|
8
|
-
typelize lft: :number, rgt: :number
|
|
8
|
+
typelize pretty_name: :string, lft: :number, rgt: :number, sort_order: :string,
|
|
9
|
+
metadata: 'Record<string, unknown>'
|
|
9
10
|
|
|
10
|
-
#
|
|
11
|
-
attributes :lft, :rgt, created_at: :iso8601, updated_at: :iso8601
|
|
11
|
+
# Admin-only attributes
|
|
12
|
+
attributes :metadata, :pretty_name, :lft, :rgt, created_at: :iso8601, updated_at: :iso8601
|
|
12
13
|
|
|
13
14
|
# Override inherited associations to use admin serializers
|
|
14
15
|
one :parent,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Admin
|
|
5
|
+
class ChannelSerializer < V3::ChannelSerializer
|
|
6
|
+
typelize store_id: :string,
|
|
7
|
+
preferred_order_routing_strategy: [:string, nullable: true]
|
|
8
|
+
|
|
9
|
+
attributes :preferred_order_routing_strategy,
|
|
10
|
+
created_at: :iso8601, updated_at: :iso8601
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
module Api
|
|
5
|
+
module V3
|
|
6
|
+
module Admin
|
|
7
|
+
# Coupon codes belong to multi-code promotions. Read-only here:
|
|
8
|
+
# codes are generated server-side based on the promotion's
|
|
9
|
+
# `code_prefix` + `number_of_codes`.
|
|
10
|
+
class CouponCodeSerializer < BaseSerializer
|
|
11
|
+
typelize code: :string,
|
|
12
|
+
state: [:string, nullable: true],
|
|
13
|
+
promotion_id: :string,
|
|
14
|
+
order_id: [:string, nullable: true]
|
|
15
|
+
|
|
16
|
+
attributes :code, :state,
|
|
17
|
+
created_at: :iso8601, updated_at: :iso8601
|
|
18
|
+
|
|
19
|
+
attribute :promotion_id do |coupon|
|
|
20
|
+
coupon.promotion&.prefixed_id
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
attribute :order_id do |coupon|
|
|
24
|
+
coupon.order&.prefixed_id
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -4,7 +4,8 @@ module Spree
|
|
|
4
4
|
module Admin
|
|
5
5
|
class CreditCardSerializer < V3::CreditCardSerializer
|
|
6
6
|
typelize customer_id: [:string, nullable: true],
|
|
7
|
-
payment_method_id: [:string, nullable: true]
|
|
7
|
+
payment_method_id: [:string, nullable: true],
|
|
8
|
+
metadata: 'Record<string, unknown>'
|
|
8
9
|
|
|
9
10
|
attribute :customer_id do |credit_card|
|
|
10
11
|
credit_card.user&.prefixed_id
|
|
@@ -14,7 +15,8 @@ module Spree
|
|
|
14
15
|
credit_card.payment_method&.prefixed_id
|
|
15
16
|
end
|
|
16
17
|
|
|
17
|
-
attributes
|
|
18
|
+
attributes :metadata,
|
|
19
|
+
created_at: :iso8601, updated_at: :iso8601
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Admin
|
|
5
|
+
# Admin API Custom Field Definition Serializer
|
|
6
|
+
# Schema-side metadata for custom fields (per resource type).
|
|
7
|
+
class CustomFieldDefinitionSerializer < BaseSerializer
|
|
8
|
+
typelize namespace: :string,
|
|
9
|
+
key: :string,
|
|
10
|
+
label: :string,
|
|
11
|
+
field_type: Spree::Metafield::FIELD_TYPE_TOKENS,
|
|
12
|
+
resource_type: :string,
|
|
13
|
+
storefront_visible: :boolean
|
|
14
|
+
|
|
15
|
+
attributes :namespace, :key, :label, :field_type, :resource_type, :storefront_visible,
|
|
16
|
+
created_at: :iso8601, updated_at: :iso8601
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -5,12 +5,17 @@ module Spree
|
|
|
5
5
|
# Admin API Custom Field Serializer
|
|
6
6
|
# Full custom field data including admin-only fields
|
|
7
7
|
class CustomFieldSerializer < V3::CustomFieldSerializer
|
|
8
|
-
typelize storefront_visible: :boolean
|
|
8
|
+
typelize storefront_visible: :boolean,
|
|
9
|
+
custom_field_definition_id: :string
|
|
9
10
|
|
|
10
11
|
attributes created_at: :iso8601, updated_at: :iso8601
|
|
11
12
|
|
|
12
|
-
attribute :storefront_visible do |
|
|
13
|
-
|
|
13
|
+
attribute :storefront_visible do |custom_field|
|
|
14
|
+
custom_field.metafield_definition.available_on_front_end?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attribute :custom_field_definition_id do |custom_field|
|
|
18
|
+
custom_field.metafield_definition.prefixed_id
|
|
14
19
|
end
|
|
15
20
|
end
|
|
16
21
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Api
|
|
3
|
+
module V3
|
|
4
|
+
module Admin
|
|
5
|
+
# Serializes Spree::CustomerGroup for the admin pickers
|
|
6
|
+
# (e.g. promotion rule, customer-group filters). Surfaces only
|
|
7
|
+
# what the admin UI needs to display + select rows.
|
|
8
|
+
class CustomerGroupSerializer < V3::BaseSerializer
|
|
9
|
+
typelize name: :string,
|
|
10
|
+
description: 'string | null',
|
|
11
|
+
customers_count: :number
|
|
12
|
+
|
|
13
|
+
attributes :name, :description, :customers_count,
|
|
14
|
+
created_at: :iso8601, updated_at: :iso8601
|
|
15
|
+
|
|
16
|
+
# Members are paginated separately via `/customers?customer_group_id_in=…`
|
|
17
|
+
# because a group can hold tens of thousands of users — embedding the
|
|
18
|
+
# whole list on every group fetch would explode the index payload.
|
|
19
|
+
# Pass `expand=customers` when you need them inline (single-record reads only).
|
|
20
|
+
many :customers,
|
|
21
|
+
resource: Spree.api.admin_customer_serializer,
|
|
22
|
+
if: proc { expand?('customers') }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -8,10 +8,19 @@ module Spree
|
|
|
8
8
|
typelize login: [:string, nullable: true],
|
|
9
9
|
last_sign_in_at: [:string, nullable: true], current_sign_in_at: [:string, nullable: true],
|
|
10
10
|
sign_in_count: :number, failed_attempts: :number,
|
|
11
|
-
last_sign_in_ip: [:string, nullable: true], current_sign_in_ip: [:string, nullable: true]
|
|
11
|
+
last_sign_in_ip: [:string, nullable: true], current_sign_in_ip: [:string, nullable: true],
|
|
12
|
+
tags: [:string, multi: true],
|
|
13
|
+
internal_note_html: [:string, nullable: true],
|
|
14
|
+
metadata: 'Record<string, unknown>',
|
|
15
|
+
orders_count: :number,
|
|
16
|
+
total_spent: :string,
|
|
17
|
+
display_total_spent: :string,
|
|
18
|
+
last_order_completed_at: [:string, nullable: true],
|
|
19
|
+
default_billing_address_id: [:string, nullable: true],
|
|
20
|
+
default_shipping_address_id: [:string, nullable: true]
|
|
12
21
|
|
|
13
22
|
# Admin-only attributes
|
|
14
|
-
attributes :login,
|
|
23
|
+
attributes :login, :metadata,
|
|
15
24
|
last_sign_in_at: :iso8601, current_sign_in_at: :iso8601,
|
|
16
25
|
created_at: :iso8601, updated_at: :iso8601
|
|
17
26
|
|
|
@@ -31,6 +40,45 @@ module Spree
|
|
|
31
40
|
user.current_sign_in_ip
|
|
32
41
|
end
|
|
33
42
|
|
|
43
|
+
attribute :tags do |user|
|
|
44
|
+
user.tag_list.to_a
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
attribute :internal_note_html do |user|
|
|
48
|
+
user.respond_to?(:internal_note) ? user.internal_note&.body&.to_s.presence : nil
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
attribute :default_billing_address_id do |user|
|
|
52
|
+
user.bill_address&.prefixed_id
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
attribute :default_shipping_address_id do |user|
|
|
56
|
+
user.ship_address&.prefixed_id
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Order aggregates: prefer attributes precomputed on the scope (see
|
|
60
|
+
# CustomersController#scope) to avoid N+1 on list endpoints. Fall
|
|
61
|
+
# back to per-user queries when not preloaded (e.g. show endpoint
|
|
62
|
+
# for a freshly loaded record).
|
|
63
|
+
attribute :orders_count do |user|
|
|
64
|
+
user.attributes['orders_count']&.to_i || user.orders.complete.count
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
attribute :total_spent do |user|
|
|
68
|
+
(user.attributes['total_spent'] || user.orders.complete.sum(:total)).to_s
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
attribute :display_total_spent do |user|
|
|
72
|
+
amount = user.attributes['total_spent'] || user.orders.complete.sum(:total)
|
|
73
|
+
currency = Spree::Current.currency || Spree::Current.store&.default_currency || Spree::Config[:currency]
|
|
74
|
+
Spree::Money.new(amount, currency: currency).to_s
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
attribute :last_order_completed_at do |user|
|
|
78
|
+
value = user.attributes.key?('last_order_completed_at') ? user.attributes['last_order_completed_at'] : user.orders.complete.maximum(:completed_at)
|
|
79
|
+
value.respond_to?(:iso8601) ? value.iso8601 : value
|
|
80
|
+
end
|
|
81
|
+
|
|
34
82
|
# Override inherited associations to use admin serializers
|
|
35
83
|
many :addresses, resource: Spree.api.admin_address_serializer, if: proc { expand?('addresses') }
|
|
36
84
|
one :bill_address, key: :default_billing_address, resource: Spree.api.admin_address_serializer, if: proc { expand?('default_billing_address') }
|
|
@@ -39,6 +87,14 @@ module Spree
|
|
|
39
87
|
many :orders,
|
|
40
88
|
resource: Spree.api.admin_order_serializer,
|
|
41
89
|
if: proc { expand?('orders') }
|
|
90
|
+
|
|
91
|
+
many :store_credits,
|
|
92
|
+
resource: Spree.api.admin_store_credit_serializer,
|
|
93
|
+
if: proc { expand?('store_credits') }
|
|
94
|
+
|
|
95
|
+
many :customer_groups,
|
|
96
|
+
resource: Spree.api.admin_customer_group_serializer,
|
|
97
|
+
if: proc { expand?('customer_groups') }
|
|
42
98
|
end
|
|
43
99
|
end
|
|
44
100
|
end
|