spree_api 6.0.2 → 6.0.4

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/spree/api/v3/admin/embed_permissions.rb +5 -2
  3. data/app/controllers/concerns/spree/api/v3/idempotent.rb +10 -1
  4. data/app/controllers/concerns/spree/api/v3/security_headers.rb +3 -0
  5. data/app/controllers/spree/api/v3/admin/base_controller.rb +31 -0
  6. data/app/controllers/spree/api/v3/admin/companies/memberships_controller.rb +21 -4
  7. data/app/controllers/spree/api/v3/admin/diagnostics_controller.rb +46 -0
  8. data/app/controllers/spree/api/v3/admin/me_controller.rb +0 -13
  9. data/app/controllers/spree/api/v3/admin/media_controller.rb +10 -0
  10. data/app/controllers/spree/api/v3/admin/orders/items_controller.rb +6 -2
  11. data/app/controllers/spree/api/v3/admin/products_controller.rb +56 -0
  12. data/app/controllers/spree/api/v3/admin/sellers/invitations_controller.rb +39 -3
  13. data/app/controllers/spree/api/v3/admin/sellers/team_controller.rb +1 -3
  14. data/app/controllers/spree/api/v3/admin/sellers_controller.rb +24 -1
  15. data/app/controllers/spree/api/v3/admin/stock_levels_controller.rb +15 -1
  16. data/app/controllers/spree/api/v3/admin/updates_controller.rb +4 -15
  17. data/app/controllers/spree/api/v3/seller/products/media_controller.rb +46 -0
  18. data/app/controllers/spree/api/v3/seller/products_controller.rb +4 -2
  19. data/app/controllers/spree/api/v3/seller/profile_controller.rb +13 -5
  20. data/app/controllers/spree/api/v3/store/carts_controller.rb +10 -7
  21. data/app/serializers/spree/api/v3/admin/admin_user_serializer.rb +2 -2
  22. data/app/serializers/spree/api/v3/admin/company_membership_serializer.rb +4 -0
  23. data/app/serializers/spree/api/v3/admin/delivery_profile_serializer.rb +2 -1
  24. data/app/serializers/spree/api/v3/admin/diagnostics_serializer.rb +30 -0
  25. data/app/serializers/spree/api/v3/admin/fulfillment_serializer.rb +1 -1
  26. data/app/serializers/spree/api/v3/admin/import_serializer.rb +1 -1
  27. data/app/serializers/spree/api/v3/admin/invitation_serializer.rb +2 -2
  28. data/app/serializers/spree/api/v3/admin/line_item_serializer.rb +6 -1
  29. data/app/serializers/spree/api/v3/admin/price_rule_serializer.rb +3 -1
  30. data/app/serializers/spree/api/v3/admin/product_serializer.rb +8 -0
  31. data/app/serializers/spree/api/v3/admin/promotion_action_serializer.rb +2 -2
  32. data/app/serializers/spree/api/v3/admin/promotion_rule_serializer.rb +4 -1
  33. data/app/serializers/spree/api/v3/admin/promotion_serializer.rb +13 -0
  34. data/app/serializers/spree/api/v3/admin/return_serializer.rb +1 -1
  35. data/app/serializers/spree/api/v3/admin/seller_requirement_status_serializer.rb +3 -2
  36. data/app/serializers/spree/api/v3/admin/seller_serializer.rb +12 -2
  37. data/app/serializers/spree/api/v3/fulfillment_serializer.rb +1 -1
  38. data/app/serializers/spree/api/v3/product_category_serializer.rb +26 -0
  39. data/app/serializers/spree/api/v3/product_collection_serializer.rb +26 -0
  40. data/app/serializers/spree/api/v3/seller/import_serializer.rb +1 -1
  41. data/app/serializers/spree/api/v3/seller/invitation_serializer.rb +2 -2
  42. data/app/serializers/spree/api/v3/seller/order_line_item_serializer.rb +2 -2
  43. data/app/serializers/spree/api/v3/seller/product_serializer.rb +8 -0
  44. data/app/serializers/spree/api/v3/seller/requirement_status_serializer.rb +3 -2
  45. data/config/initializers/typelizer.rb +8 -19
  46. data/config/routes.rb +8 -1
  47. data/lib/spree/api/admin_document.rb +284 -0
  48. data/lib/spree/api/agent_operations.rb +300 -0
  49. data/lib/spree/api/agent_resource_map.rb +74 -14
  50. data/lib/spree/api/agent_write_schemas.rb +15 -51
  51. data/lib/spree/api/dependencies.rb +1 -1
  52. data/lib/spree/api/engine.rb +14 -1
  53. data/lib/spree/api/enum_types.rb +4 -1
  54. data/lib/spree/api/openapi/schema_helper.rb +3 -3
  55. data/lib/spree/api/sdk_types.rb +190 -0
  56. data/lib/spree/api/sdk_types_task.rb +68 -0
  57. data/lib/spree/api/testing_support/agent_tools.rb +35 -0
  58. data/lib/spree/api/testing_support/agent_tools_contract.rb +110 -0
  59. data/lib/spree/api/testing_support/deterministic_openapi.rb +147 -0
  60. data/lib/spree/api/testing_support/filter_parameters.rb +99 -0
  61. data/lib/spree/api/testing_support/openapi.rb +233 -0
  62. data/lib/spree/api/testing_support/openapi_tasks.rb +21 -0
  63. data/lib/spree/api/testing_support/sdk_method.rb +34 -0
  64. data/lib/spree/api/typelizer_generation.rb +39 -11
  65. data/lib/tasks/openapi.rake +5 -0
  66. data/lib/tasks/sdk_types.rake +6 -0
  67. data/lib/typelizer/open_enums.rb +13 -1
  68. data/openapi/admin.yaml +2241 -318
  69. data/openapi/store.yaml +125 -125
  70. metadata +26 -9
  71. data/lib/spree/api/agent_workflow_endpoints.rb +0 -113
@@ -129,17 +129,20 @@ module Spree
129
129
  end
130
130
  end
131
131
 
132
- # DELETE /api/v3/store/carts/:id
133
- # Deletes/abandons the cart
132
+ # Abandons a cart after checking its completion claim under the row lock.
133
+ #
134
+ # @return [void]
134
135
  def destroy
135
136
  find_cart!
136
137
 
137
- result = Spree.cart_destroy_service.call(cart: @cart)
138
+ with_order_lock do
139
+ result = Spree.cart_destroy_service.call(cart: @cart)
138
140
 
139
- if result.success?
140
- head :no_content
141
- else
142
- render_service_error(result.error.to_s)
141
+ if result.success?
142
+ head :no_content
143
+ else
144
+ render_service_error(result.error.to_s)
145
+ end
143
146
  end
144
147
  end
145
148
 
@@ -9,8 +9,6 @@ module Spree
9
9
  full_name: [:string, nullable: true],
10
10
  selected_locale: [:string, nullable: true],
11
11
  avatar_url: [:string, nullable: true],
12
- roles: 'Array<{ id: string; name: string }>',
13
- stores: 'Array<{ id: string; name: string; code: string }>',
14
12
  status: [:string, enum: Spree::AdminUserMethods::STATUSES, enum_type_name: 'AdminUserStatus'],
15
13
  last_active_at: [:string, nullable: true],
16
14
  inviter: ['Actor', nullable: true]
@@ -45,6 +43,7 @@ module Spree
45
43
  # we fall back to `Spree::Current.store` if not.
46
44
  # Filtered in Ruby rather than with `for_resource`, so the staff
47
45
  # index's preloaded roles are used instead of a query per user.
46
+ typelize({ id: :string, name: :string }, multi: true)
48
47
  attribute :roles do |user, params|
49
48
  store = params&.dig(:store) || Spree::Current.store
50
49
  roles = user.spree_roles.to_a
@@ -57,6 +56,7 @@ module Spree
57
56
  # store switcher can offer all accessible stores. Read off the same
58
57
  # preloaded roles, and deduped/sorted in Ruby, so the staff index
59
58
  # spends no extra query per user.
59
+ typelize({ id: :string, name: :string, code: :string }, multi: true)
60
60
  attribute :stores do |user|
61
61
  stores = user.spree_roles.filter_map { |role| role.resource if role.resource.is_a?(Spree::Store) }
62
62
 
@@ -4,6 +4,10 @@ module Spree
4
4
  module Admin
5
5
  class CompanyMembershipSerializer < V3::CompanyMembershipSerializer
6
6
  attributes created_at: :iso8601, updated_at: :iso8601
7
+
8
+ # The member as a customer, so a list can show their name, not only
9
+ # the email.
10
+ one :customer, resource: proc { Spree.api.admin_customer_serializer }, if: proc { expand?('customer') }
7
11
  end
8
12
  end
9
13
  end
@@ -8,7 +8,6 @@ module Spree
8
8
  digital: :boolean,
9
9
  offers_pickup: :boolean, offers_shipping: :boolean,
10
10
  stock_location_ids: [:string, multi: true],
11
- origin_groups: ['Array<{ id: string; name: string | null; position: number | null; stock_location_ids: Array<string> }>'],
12
11
  products_count: :number, delivery_methods_count: :number,
13
12
  delivery_zones_count: :number
14
13
 
@@ -36,6 +35,8 @@ module Spree
36
35
 
37
36
  # Origin groups partition the profile's fulfillment origins; zones
38
37
  # and methods reference one. A null name renders as "All locations".
38
+ typelize({ id: :string, name: [:string, { nullable: true }], position: [:number, { nullable: true }],
39
+ stock_location_ids: 'string[]' }, multi: true)
39
40
  attribute :origin_groups do |record|
40
41
  record.delivery_origin_groups.map do |group|
41
42
  {
@@ -0,0 +1,30 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ # The diagnostics report (Spree::Diagnostics::Report). A computed
6
+ # value rather than a record, so it carries no id. `errors[].error_class`
7
+ # is an exception's class name on purpose: it is what a maintainer
8
+ # reads in a bug report, not a value any client branches on.
9
+ class DiagnosticsSerializer < V3::BaseSerializer
10
+ typelize generated_at: :string,
11
+ spree_version: :string,
12
+ markdown: :string,
13
+ issue_url: :string,
14
+ issue_prefilled: :boolean,
15
+ sections: 'Array<{ key: string; title: string; rows: Array<{ label: string; value: string }> }>',
16
+ errors: 'Array<{ error_class: string; source: string | null; frames: string[]; ' \
17
+ 'request_id: string | null; count: number; first_seen_at: string; last_seen_at: string }>'
18
+
19
+ _attributes.delete(:id)
20
+
21
+ attributes :spree_version, :sections, :errors, generated_at: :iso8601
22
+
23
+ attribute(:markdown, &:to_markdown)
24
+ attribute(:issue_url, &:issue_url)
25
+ attribute(:issue_prefilled, &:issue_prefilled?)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -17,7 +17,6 @@ module Spree
17
17
  tax_total: [:string, nullable: false]
18
18
 
19
19
  typelize metadata: 'Record<string, unknown>',
20
- documents: "Array<{ kind: string; url: string }>",
21
20
  provider_generates_labels: :boolean,
22
21
  order_id: [:string, nullable: true],
23
22
  stock_location_id: [:string, nullable: true]
@@ -31,6 +30,7 @@ module Spree
31
30
 
32
31
  # Customs forms and other paperwork the provider produced beside the
33
32
  # label; the labels themselves are listed under +labels+.
33
+ typelize({ kind: :string, url: :string }, multi: true)
34
34
  attribute :documents do |fulfillment|
35
35
  fulfillment.provider.documents(fulfillment).map(&:as_json)
36
36
  end
@@ -10,7 +10,6 @@ module Spree
10
10
  failed_rows_count: :number,
11
11
  processing_errors: [:string, nullable: true],
12
12
  delimiter: :string,
13
- schema_fields: 'Array<{ name: string; label: string; required: boolean }>',
14
13
  csv_headers: [:string, multi: true],
15
14
  sample_row: 'Record<string, string | null>',
16
15
  original_filename: [:string, nullable: true],
@@ -58,6 +57,7 @@ module Spree
58
57
  import.rows_status_counts['failed'] || 0
59
58
  end
60
59
 
60
+ typelize({ name: :string, label: :string, required: :boolean }, multi: true)
61
61
  attribute :schema_fields do |import|
62
62
  import.schema_fields.map do |field|
63
63
  { name: field[:name], label: field[:label], required: field[:required].present? }
@@ -13,8 +13,7 @@ module Spree
13
13
  role_name: :string,
14
14
  inviter_email: :string,
15
15
  expires_at: :string,
16
- invitee_exists: :boolean,
17
- store: '{ id: string; name: string }'
16
+ invitee_exists: :boolean
18
17
 
19
18
  attributes :email, :status,
20
19
  created_at: :iso8601, updated_at: :iso8601, expires_at: :iso8601
@@ -43,6 +42,7 @@ module Spree
43
42
  # Minimal store identity for the unauthenticated acceptance page's
44
43
  # title ("Join <store>"). Full Store would over-expose internals to
45
44
  # a public landing page; this is the smallest shape that renders.
45
+ typelize({ id: :string, name: :string })
46
46
  attribute :store do |invitation|
47
47
  { id: invitation.store.prefixed_id, name: invitation.store.name }
48
48
  end
@@ -28,7 +28,8 @@ module Spree
28
28
  price_list_id: [:string, nullable: true],
29
29
  price_list_name: [:string, nullable: true],
30
30
  catalog_id: [:string, nullable: true],
31
- catalog_name: [:string, nullable: true]
31
+ catalog_name: [:string, nullable: true],
32
+ product_id: [:string, nullable: true]
32
33
 
33
34
  # price_source is operational provenance — admin-only, never on the
34
35
  # store serializer.
@@ -66,6 +67,10 @@ module Spree
66
67
 
67
68
  prefixed_id_attributes :tax_category
68
69
 
70
+ # So an order page can link each line to its product without
71
+ # expanding the variant.
72
+ prefixed_id_attributes :product
73
+
69
74
  # Override inherited associations to use admin serializers
70
75
  many :option_values, resource: proc { Spree.api.admin_option_value_serializer }
71
76
  many :digital_links, resource: proc { Spree.api.admin_digital_link_serializer }
@@ -34,10 +34,12 @@ module Spree
34
34
  resource: proc { Spree.api.admin_channel_serializer },
35
35
  if: proc { |rule| rule.respond_to?(:channels) }
36
36
 
37
+ # Only for a caller who may read customers; see the promotion rule
38
+ # serializer.
37
39
  many :users,
38
40
  key: :customers,
39
41
  resource: proc { Spree.api.admin_customer_serializer },
40
- if: proc { |rule| rule.respond_to?(:users) }
42
+ if: proc { |rule| rule.respond_to?(:users) && params[:customer_records] }
41
43
  end
42
44
  end
43
45
  end
@@ -17,6 +17,7 @@ module Spree
17
17
  product_type_id: [:string, nullable: true],
18
18
  delivery_profile_id: [:string, nullable: true],
19
19
  seller_name: [:string, nullable: true],
20
+ total_on_hand: [:number, nullable: true],
20
21
  price: ['Price', nullable: true],
21
22
  deleted_at: [:string, nullable: true],
22
23
  metadata: 'Record<string, unknown>',
@@ -49,6 +50,13 @@ module Spree
49
50
 
50
51
  prefixed_id_attributes :product_type, :tax_category
51
52
 
53
+ # Null when any variant is not tracked: its stock is unlimited, which
54
+ # is not a number to send.
55
+ attribute :total_on_hand do |product|
56
+ total = product.total_on_hand
57
+ total.to_i unless total.infinite?
58
+ end
59
+
52
60
  attribute :price do |product|
53
61
  price = price_for(product.default_variant)
54
62
  Spree.api.admin_price_serializer.new(price, params: params).to_h if price&.persisted?
@@ -13,8 +13,7 @@ module Spree
13
13
  typelize type: [:string, comment: 'Action type. Built-in: create_adjustment, create_item_adjustments, create_line_items, free_shipping. Extensions may register more.'],
14
14
  promotion_id: :string,
15
15
  preferences: 'Record<string, unknown>',
16
- calculator: 'TypedPromotionCalculator | null',
17
- line_items: 'Array<{ variant_id: string; quantity: number }> | null'
16
+ calculator: 'TypedPromotionCalculator | null'
18
17
 
19
18
  attributes created_at: :iso8601, updated_at: :iso8601
20
19
 
@@ -44,6 +43,7 @@ module Spree
44
43
  # Line items the action will add for CreateLineItems. variant_id
45
44
  # is prefixed for consistency with the rest of the API. Null on
46
45
  # actions that don't have the association.
46
+ typelize({ variant_id: :string, quantity: :number }, multi: true, nullable: true)
47
47
  attribute :line_items do |action|
48
48
  next nil unless action.respond_to?(:promotion_action_line_items)
49
49
 
@@ -61,9 +61,12 @@ module Spree
61
61
  resource: proc { Spree.api.admin_category_serializer },
62
62
  if: proc { |rule| rule.respond_to?(:categories) }
63
63
 
64
+ # Only for a caller who may read customers: a promotion is readable
65
+ # under `read_promotions`, and a customer rule would otherwise carry
66
+ # every targeted customer's email and notes along with it.
64
67
  many :customers,
65
68
  resource: proc { Spree.api.admin_customer_serializer },
66
- if: proc { |rule| rule.respond_to?(:customers) }
69
+ if: proc { |rule| rule.respond_to?(:customers) && params[:customer_records] }
67
70
 
68
71
  many :customer_groups,
69
72
  resource: proc { Spree.api.admin_customer_group_serializer },
@@ -34,6 +34,19 @@ module Spree
34
34
  attribute :rule_ids do |promotion|
35
35
  promotion.promotion_rules.map(&:prefixed_id)
36
36
  end
37
+
38
+ # The rows under the same names the write accepts, on request: a
39
+ # reader holding only `rule_ids` cannot tell what a promotion does
40
+ # without a request per rule.
41
+ many :promotion_rules,
42
+ key: :rules,
43
+ resource: proc { Spree.api.admin_promotion_rule_serializer },
44
+ if: proc { expand?('rules') }
45
+
46
+ many :promotion_actions,
47
+ key: :actions,
48
+ resource: proc { Spree.api.admin_promotion_action_serializer },
49
+ if: proc { expand?('actions') }
37
50
  end
38
51
  end
39
52
  end
@@ -11,7 +11,6 @@ module Spree
11
11
 
12
12
  typelize memo: [:string, nullable: true],
13
13
  metadata: 'Record<string, unknown>',
14
- documents: "Array<{ kind: string; url: string }>",
15
14
  stock_location_id: [:string, nullable: true],
16
15
  created_by_id: [:string, nullable: true],
17
16
  created_by_type: [:string, nullable: true, enum: Spree::Actor::BUILT_IN_KINDS, enum_type_name: 'ActorKind'],
@@ -39,6 +38,7 @@ module Spree
39
38
 
40
39
  # Paperwork the provider produced beside the label: a cross-border
41
40
  # return is declared like any other export.
41
+ typelize({ kind: :string, url: :string }, multi: true)
42
42
  attribute :documents do |return_record|
43
43
  return_record.provider.documents(return_record).map(&:as_json)
44
44
  end
@@ -14,10 +14,11 @@ module Spree
14
14
  description: [:string, nullable: true],
15
15
  required: :boolean, position: :number, status: [:string, enum: Spree::SellerRequirementStatus::STATUSES, enum_type_name: 'SellerRequirementState'],
16
16
  blocking: :boolean, action_url: [:string, nullable: true],
17
- blocker: ['{ state: string; message: string | null } | null'],
18
17
  required_policy_name: [:string, nullable: true]
19
18
 
20
- attributes :id, :kind, :name, :description, :required, :position, :status, :action_url, :blocker
19
+ attributes :id, :kind, :name, :description, :required, :position, :status, :action_url
20
+ typelize({ state: :string, message: [:string, { nullable: true }] }, nullable: true)
21
+ attribute :blocker, &:blocker
21
22
 
22
23
  # Whether this stands between the seller and approval. Serialized
23
24
  # rather than left to each client to derive, so the dashboard's
@@ -20,7 +20,6 @@ module Spree
20
20
  deleted_at: [:string, nullable: true],
21
21
  on_holiday: :boolean, sellable: :boolean,
22
22
  products_count: :number, users_count: :number,
23
- onboarding_progress: '{ done: number; total: number }',
24
23
  onboarding_complete: :boolean,
25
24
  metadata: 'Record<string, unknown> | null'
26
25
 
@@ -56,6 +55,11 @@ module Spree
56
55
  # so the minimum-products requirement asks the same question free.
57
56
  attributes :products_count
58
57
 
58
+ # What is live and what waits on the operator's review, read in the
59
+ # same query as the total above.
60
+ typelize(Spree::Product::ALL_STATUSES.to_h { |status| [status.to_sym, :number] })
61
+ attribute :products_count_by_status, &:products_count_by_status
62
+
59
63
  attributes :users_count
60
64
 
61
65
  # How far through the marketplace's checklist this seller is — what
@@ -63,7 +67,8 @@ module Spree
63
67
  # requirements themselves, with their submissions, stay on the
64
68
  # heavier `onboarding` action; a list of sellers needs the number,
65
69
  # not the rows behind it.
66
- attributes :onboarding_progress
70
+ typelize({ done: :number, total: :number })
71
+ attribute :onboarding_progress, &:onboarding_progress
67
72
 
68
73
  attribute :onboarding_complete, &:onboarding_complete?
69
74
 
@@ -78,6 +83,11 @@ module Spree
78
83
  resource: proc { Spree.api.admin_address_serializer },
79
84
  if: proc { |seller| seller.returns_address.present? }
80
85
 
86
+ # The stock location the returns address is read from. The address
87
+ # is not the seller's own to write — it is edited on this location.
88
+ typelize returns_location_id: [:string, nullable: true]
89
+ prefixed_id_attributes :returns_location
90
+
81
91
  expandable_custom_fields admin: true
82
92
  end
83
93
  end
@@ -7,7 +7,6 @@ module Spree
7
7
  fulfillment_type: [:string, enum: %w[shipping digital pickup pickup_point], enum_type_name: 'FulfillmentType'],
8
8
  tracking: [:string, nullable: true], tracking_url: [:string, nullable: true],
9
9
  fulfilled_at: [:string, nullable: true], delivered_at: [:string, nullable: true], unpriced: :boolean,
10
- items: 'Array<{ item_id: string; variant_id: string; quantity: number }>',
11
10
  pickup_point_data: ['Record<string, unknown>', nullable: true],
12
11
  selected_delivery_rate_id: [:string, nullable: true]
13
12
 
@@ -40,6 +39,7 @@ module Spree
40
39
 
41
40
  # Which items (and how many) are in this fulfillment.
42
41
  # A line item can be split across fulfillments with different quantities.
42
+ typelize({ item_id: :string, variant_id: :string, quantity: :number }, multi: true)
43
43
  attribute :items do |fulfillment|
44
44
  fulfillment.manifest.filter_map do |item|
45
45
  next unless item.line_item
@@ -0,0 +1,26 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ class ProductCategorySerializer < BaseSerializer
5
+ typelize product_id: [:string, nullable: true],
6
+ category_id: [:string, nullable: true]
7
+
8
+ attribute :product_id do |product_category|
9
+ prefixed_id_for(Spree::Product, product_category.product_id)
10
+ end
11
+
12
+ attribute :category_id do |product_category|
13
+ prefixed_id_for(Spree::Category, product_category.category_id)
14
+ end
15
+
16
+ private
17
+
18
+ def prefixed_id_for(klass, id)
19
+ return nil if id.nil?
20
+
21
+ "#{klass._prefix_id_prefix}_#{Spree::PrefixedId::SQIDS.encode([id])}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ class ProductCollectionSerializer < BaseSerializer
5
+ typelize product_id: [:string, nullable: true],
6
+ collection_id: [:string, nullable: true]
7
+
8
+ attribute :product_id do |product_collection|
9
+ prefixed_id_for(Spree::Product, product_collection.product_id)
10
+ end
11
+
12
+ attribute :collection_id do |product_collection|
13
+ prefixed_id_for(Spree::Collection, product_collection.collection_id)
14
+ end
15
+
16
+ private
17
+
18
+ def prefixed_id_for(klass, id)
19
+ return nil if id.nil?
20
+
21
+ "#{klass._prefix_id_prefix}_#{Spree::PrefixedId::SQIDS.encode([id])}"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -16,7 +16,6 @@ module Spree
16
16
  failed_rows_count: :number,
17
17
  processing_errors: [:string, nullable: true],
18
18
  delimiter: :string,
19
- schema_fields: 'Array<{ name: string; label: string; required: boolean }>',
20
19
  csv_headers: [:string, multi: true],
21
20
  sample_row: 'Record<string, string | null>',
22
21
  original_filename: [:string, nullable: true],
@@ -52,6 +51,7 @@ module Spree
52
51
  import.rows_status_counts['failed'] || 0
53
52
  end
54
53
 
54
+ typelize({ name: :string, label: :string, required: :boolean }, multi: true)
55
55
  attribute :schema_fields do |import|
56
56
  import.schema_fields.map do |field|
57
57
  { name: field[:name], label: field[:label], required: field[:required].present? }
@@ -7,8 +7,7 @@ module Spree
7
7
  expires_at: [:string, nullable: true],
8
8
  accepted_at: [:string, nullable: true],
9
9
  inviter_email: :string,
10
- invitee_exists: [:boolean, optional: true],
11
- seller: '{ id: string; name: string }'
10
+ invitee_exists: [:boolean, optional: true]
12
11
 
13
12
  attributes :email,
14
13
  created_at: :iso8601, expires_at: :iso8601, accepted_at: :iso8601
@@ -29,6 +28,7 @@ module Spree
29
28
 
30
29
  # The smallest shape the acceptance page's title ("Join <seller>")
31
30
  # needs; the page is public, so nothing more of the seller goes out.
31
+ typelize({ id: :string, name: :string })
32
32
  attribute :seller do |invitation|
33
33
  { id: invitation.resource.prefixed_id, name: invitation.resource.name }
34
34
  end
@@ -13,7 +13,7 @@ module Spree
13
13
  class OrderLineItemSerializer < V3::BaseSerializer
14
14
  typelize name: :string, sku: [:string, nullable: true], options_text: [:string, nullable: true],
15
15
  quantity: :number, currency: [:string, enum: Spree::Money::Rounding::CURRENCY_CODES, enum_type_name: 'CurrencyCode'], variant_id: [:string, nullable: true],
16
- thumbnail_url: [:string, nullable: true]
16
+ thumbnail_url: [:string, nullable: true], product_id: [:string, nullable: true]
17
17
 
18
18
  attributes :name, :options_text, :quantity, :currency
19
19
 
@@ -22,7 +22,7 @@ module Spree
22
22
  money_attributes :discounted_amount,
23
23
  :additional_tax_total, :total
24
24
 
25
- prefixed_id_attributes :variant
25
+ prefixed_id_attributes :variant, :product
26
26
 
27
27
  # How a seller finds the item on their own shelf.
28
28
  attribute :sku do |line_item|
@@ -18,12 +18,20 @@ module Spree
18
18
  typelize status: [:string, enum: Spree::Product.statuses, enum_type_name: 'ProductStatus'], metadata: 'Record<string, unknown>',
19
19
  product_type_id: [:string, nullable: true],
20
20
  delivery_profile_id: [:string, nullable: true],
21
+ total_on_hand: [:number, nullable: true],
21
22
  submission: ['ProductSubmission', nullable: true]
22
23
 
23
24
  attributes :status, :metadata, created_at: :iso8601, updated_at: :iso8601
24
25
 
25
26
  prefixed_id_attributes :product_type, :delivery_profile
26
27
 
28
+ # Null when any variant is not tracked: its stock is unlimited, which
29
+ # is not a number to send.
30
+ attribute :total_on_hand do |product|
31
+ total = product.total_on_hand
32
+ total.to_i unless total.infinite?
33
+ end
34
+
27
35
  many :variants,
28
36
  resource: proc { Spree.api.seller_variant_serializer },
29
37
  if: proc { expand?('variants') }
@@ -10,13 +10,14 @@ module Spree
10
10
  description: [:string, nullable: true],
11
11
  required: :boolean, position: :number, status: [:string, enum: Spree::SellerRequirementStatus::STATUSES, enum_type_name: 'SellerRequirementState'],
12
12
  blocking: :boolean, action_url: [:string, nullable: true],
13
- blocker: ['{ state: string; message: string | null } | null'],
14
13
  accepts_submissions: :boolean, requires_file: :boolean,
15
14
  accepted_content_types: [:string, multi: true],
16
15
  required_policy_name: [:string, nullable: true],
17
16
  terms_html: [:string, nullable: true]
18
17
 
19
- attributes :id, :kind, :name, :description, :required, :position, :status, :action_url, :blocker
18
+ attributes :id, :kind, :name, :description, :required, :position, :status, :action_url
19
+ typelize({ state: :string, message: [:string, { nullable: true }] }, nullable: true)
20
+ attribute :blocker, &:blocker
20
21
 
21
22
  attribute :blocking, &:blocking?
22
23
 
@@ -1,6 +1,8 @@
1
- # Disable automatic type generation on boot — types are generated manually
2
- # via `bundle exec rake typelizer:generate`. Set ENABLE_TYPELIZER=1 to enable.
3
- ENV["DISABLE_TYPELIZER"] ||= "true" unless ENV["ENABLE_TYPELIZER"]
1
+ # Types are generated only by `bundle exec rake typelizer:generate`, which sets
2
+ # TYPELIZER=true before booting; the engine's `spree.api.typelizer` initializer
3
+ # keeps Typelizer from generating on its own.
4
+
5
+ require 'spree/api/typelizer_generation'
4
6
 
5
7
  Rails.application.config.after_initialize do
6
8
  api_root = Spree::Api::Engine.root
@@ -10,22 +12,11 @@ Rails.application.config.after_initialize do
10
12
  config.comments = true
11
13
  config.listen = false
12
14
 
13
- # Teach typelizer's Alba plugin about custom Alba types (see alba.rb);
14
- # unmapped typed attributes crash generation.
15
- config.plugin_configs = {
16
- alba: {
17
- ts_mapper: Typelizer::SerializerPlugins::Alba::ALBA_TS_MAPPER.merge(
18
- 'iso8601' => { type: :string },
19
- 'string' => { type: :string }
20
- )
21
- }
22
- }
15
+ config.plugin_configs = Spree::Api::TypelizerGeneration.plugin_configs
23
16
 
24
17
  # Serializers that exist only for the Admin API. Event serializers stay in:
25
18
  # they describe the `data` of the typed webhook events the Store SDK ships.
26
- store_excluded = %w[
27
- Asset CartPromotion OrderPromotion Report TaxCategory
28
- ].to_set
19
+ store_excluded = Spree::Api::TypelizerGeneration::STORE_EXCLUDED
29
20
 
30
21
  # Store SDK — no prefix, package provides namespace
31
22
  config.writer(:store) do |c|
@@ -74,9 +65,7 @@ Rails.application.config.after_initialize do
74
65
  # branch; Category and CustomField are not, but the store product
75
66
  # serializer this branch inherits references them, so their types still
76
67
  # have to exist in the package.
77
- store_nested_for_seller = %w[
78
- Category CustomField OptionType OptionValue Seller
79
- ].to_set
68
+ store_nested_for_seller = Spree::Api::TypelizerGeneration::SELLER_STORE_TYPES
80
69
 
81
70
  config.writer(:seller) do |c|
82
71
  c.output_dir = api_root.join('../../packages/seller-sdk/src/types/generated')
data/config/routes.rb CHANGED
@@ -285,6 +285,7 @@ Spree::Core::Engine.add_routes do
285
285
  patch 'me', to: 'me#update'
286
286
 
287
287
  resource :updates, only: :show
288
+ resource :diagnostics, only: :show
288
289
 
289
290
  # Store Settings
290
291
  resource :store, only: [:show, :update], controller: 'store' do
@@ -366,6 +367,11 @@ Spree::Core::Engine.add_routes do
366
367
  # (docs/plans/6.0-seller-product-submission.md).
367
368
  patch :approve
368
369
  patch :reject
370
+ # One product's status, through the workflow that publishes its
371
+ # event; bulk_status_update writes the column for many at once.
372
+ patch :activate
373
+ patch :archive
374
+ patch :draft
369
375
  end
370
376
  collection do
371
377
  post :bulk_status_update
@@ -605,7 +611,7 @@ Spree::Core::Engine.add_routes do
605
611
  # The operator is the only one who can repair a seller whose team
606
612
  # has locked itself out, which the seller's own panel cannot do.
607
613
  resources :team, only: [:index, :destroy], controller: 'sellers/team'
608
- resources :invitations, only: [:index, :destroy], controller: 'sellers/invitations' do
614
+ resources :invitations, only: [:index, :create, :destroy], controller: 'sellers/invitations' do
609
615
  patch :resend, on: :member
610
616
  resource :acceptance_link, only: [:show], controller: 'sellers/invitations/acceptance_links'
611
617
  end
@@ -925,6 +931,7 @@ Spree::Core::Engine.add_routes do
925
931
 
926
932
  resources :digital_assets, controller: 'products/digital_assets',
927
933
  only: [:index, :create, :show, :update, :destroy]
934
+ resources :media, controller: 'products/media', only: [:destroy]
928
935
  end
929
936
 
930
937