spree_api 6.0.0.rc7 → 6.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 982115017cd530a3474eddb5fadd2ab6bd770236d449c9a4b93488d45c485c16
4
- data.tar.gz: c333427d0e3dc56730671eb612f44f4ced415f790f5eb4557c6cf2f7da5f6d34
3
+ metadata.gz: 02e3a969bdbac77ab7eba1edb5986472deb10d660fbad8b2c51df6e586f97709
4
+ data.tar.gz: c3f880037bc5751de97fbccdbcacae67d643c8ddf8aefa66a3ac804d488645a7
5
5
  SHA512:
6
- metadata.gz: c5cff8de77ced4e66a5f935e7dd3fed912cb9b8141e0675ae6014fa594022d4db451ba0ae9ef4673aebb41d70b2592529ebf858fab1cdf94e476568b72ef0ab6
7
- data.tar.gz: 2c71f0f8d3667f4e9e3ab61c7024d534abfb765a8c56f23daee05430fac53b303dac6463021f7e24c5cf690eba6226ef1c2824b1eb66cd665c4ee572214505f3
6
+ metadata.gz: cb6694163e15d45f56831fc65547a3a5728f022dd2d1e0610ea758b844ef05b1a5fd80340fad8cb4b75ef2f525582cd423212a052da3406e2e241822137c64ed
7
+ data.tar.gz: 423fe47f6ce5d99bd55eea7613e2594bc3d4c382eee23dd47ff28e1b2b1f43f678c8034c96e12ef6dafd30fa537d946a3f13d0e9c899bdf0f005c93c62c89df5
@@ -0,0 +1,35 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ # Refuses every maintenance task endpoint when the installation has
6
+ # turned them off (Spree::Config.maintenance_tasks_dashboard_enabled).
7
+ # The ability already withholds them from signed-in staff; this also
8
+ # covers secret API keys, which are authorized by scope instead.
9
+ #
10
+ # Runs ahead of every other callback, so the caller learns the feature
11
+ # is off rather than a lookup or permission failure that no grant could
12
+ # fix — whether the caller is signed in does not change that answer.
13
+ module MaintenanceTasksSwitch
14
+ extend ActiveSupport::Concern
15
+
16
+ included do
17
+ before_action :ensure_maintenance_tasks_enabled, prepend: true
18
+ end
19
+
20
+ private
21
+
22
+ def ensure_maintenance_tasks_enabled
23
+ return if Spree::Config.maintenance_tasks_dashboard_enabled
24
+
25
+ render_error(
26
+ code: ErrorHandler::ERROR_CODES[:access_denied],
27
+ message: I18n.t('spree.maintenance_tasks_turned_off'),
28
+ status: :forbidden
29
+ )
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,42 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ module MaintenanceTaskRuns
6
+ # POST /api/v3/admin/maintenance_task_runs/:maintenance_task_run_id/cancellation
7
+ #
8
+ # Stops a run for good. Work already committed stays committed; the
9
+ # runner records the outcome (docs/plans/6.0-maintenance-tasks.md).
10
+ class CancellationsController < Admin::BaseController
11
+ include MaintenanceTasksSwitch
12
+
13
+ scoped_resource :maintenance_tasks
14
+
15
+ def create
16
+ run = Spree::MaintenanceTaskRun.find_by_prefix_id!(params[:maintenance_task_run_id])
17
+ authorize! :update, run
18
+
19
+ result = Spree::MaintenanceTasks::Cancel.call(run: run)
20
+
21
+ if result.success?
22
+ render json: serialize_resource(result.value)
23
+ else
24
+ render_validation_error(result.error)
25
+ end
26
+ end
27
+
28
+ protected
29
+
30
+ def serializer_class
31
+ Spree.api.admin_maintenance_task_run_serializer
32
+ end
33
+
34
+ def serializer_params
35
+ super.merge(show_backtrace: true)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,92 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ # Firing a maintenance task and steering the run it creates
6
+ # (docs/plans/6.0-maintenance-tasks.md).
7
+ #
8
+ # No destroy: a run is the audit record of work that touched merchant
9
+ # data, so it is never removed through the API. Stopping one is a
10
+ # cancellation (MaintenanceTaskRuns::CancellationsController).
11
+ class MaintenanceTaskRunsController < ResourceController
12
+ include ActiveStorage::SetCurrent
13
+
14
+ include MaintenanceTasksSwitch
15
+
16
+ scoped_resource :maintenance_tasks
17
+
18
+ request_params :maintenance_task_run do |body|
19
+ body.attribute :task_name, :string,
20
+ required: true, on: :create,
21
+ example: 'capture_methods',
22
+ description: 'The task to run, as `task_name` from `GET /upgrade_steps`.'
23
+ body.attribute :arguments, :hash,
24
+ description: "Values for the task's declared parameters, keyed by parameter name. " \
25
+ 'A key the task does not declare is rejected.'
26
+ body.attribute :dry_run, :boolean,
27
+ description: 'Report what would change without writing. Ignored by tasks that do not support a dry run, which run for real.'
28
+ end
29
+
30
+ # POST /api/v3/admin/maintenance_task_runs
31
+ def create
32
+ authorize! :create, Spree::MaintenanceTaskRun
33
+
34
+ # Not normalized: a task argument holding a prefixed id is the
35
+ # task's to resolve, and decoding it here would hand it a raw key.
36
+ body = params.permit(*declared_permitted_attributes)
37
+
38
+ result = Spree::MaintenanceTasks::Start.call(
39
+ api_type: body[:task_name].to_s,
40
+ arguments: body[:arguments]&.to_h || {},
41
+ dry_run: ActiveModel::Type::Boolean.new.cast(body[:dry_run]) || false,
42
+ admin_user: try_spree_current_user,
43
+ api_key: current_api_key,
44
+ initiated_via: initiated_via
45
+ )
46
+
47
+ if result.success?
48
+ render json: serialize_resource(result.value), status: :created
49
+ else
50
+ render_validation_error(result.error)
51
+ end
52
+ end
53
+
54
+ protected
55
+
56
+ def model_class
57
+ Spree::MaintenanceTaskRun
58
+ end
59
+
60
+ def serializer_class
61
+ Spree.api.admin_maintenance_task_run_serializer
62
+ end
63
+
64
+ # Runs are installation-wide: an upgrade step converts every store's
65
+ # data, so a run belongs to no store.
66
+ def scope
67
+ model_class.all
68
+ end
69
+
70
+ def collection_includes
71
+ [{ admin_user: [{ spree_roles: :resource }, { avatar_attachment: :blob }] }]
72
+ end
73
+
74
+ # A backtrace names internal paths and class names, so it rides with
75
+ # the permission to act on runs rather than the one to read them.
76
+ def serializer_params
77
+ super.merge(show_backtrace: holds_permission?('write_maintenance_tasks'))
78
+ end
79
+
80
+ private
81
+
82
+ # A secret key belongs to an integration rather than a person, so the
83
+ # two are recorded distinctly — "who ran this" has a different answer
84
+ # for each.
85
+ def initiated_via
86
+ try_spree_current_user ? 'dashboard' : 'api'
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,114 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ # The upgrade manifest as an ordered checklist, so the dashboard can
6
+ # show what a release still needs and how far an operator has got
7
+ # (docs/plans/6.0-maintenance-tasks.md).
8
+ #
9
+ # Read-only: running a step is starting the maintenance task it names,
10
+ # which is what the runs endpoint already does. This exists to supply
11
+ # the order and the operator notes, neither of which a task knows about
12
+ # itself — a step's position in the manifest is a property of the
13
+ # upgrade, not of the class.
14
+ class UpgradeStepsController < BaseController
15
+ include MaintenanceTasksSwitch
16
+
17
+ scoped_resource :maintenance_tasks
18
+
19
+ # GET /api/v3/admin/upgrade_steps
20
+ #
21
+ # Every step of every manifest this installation has reached, from
22
+ # the first release whose runs are recorded, each marked pending or
23
+ # superseded. Superseded steps are the releases it has already been
24
+ # through: shown as history rather than hidden, but never runnable,
25
+ # since re-running a completed conversion is how an upgrade does
26
+ # damage. Earlier releases are left to the command line.
27
+ def index
28
+ authorize! :read, :maintenance_task
29
+
30
+ # A store installed fresh at this release has no upgrade to show —
31
+ # not the outstanding steps, and not the history of releases it was
32
+ # never on.
33
+ return render json: { data: [], meta: meta } unless Spree::Upgrade.relevant?
34
+
35
+ render json: {
36
+ data: steps.map { |step| present(step) },
37
+ meta: meta
38
+ }
39
+ end
40
+
41
+ private
42
+
43
+ def meta
44
+ { completed_version: Spree::Upgrade.completed_boundary }
45
+ end
46
+
47
+ # Superseded first: they are the earlier releases, so listing them
48
+ # above what is outstanding keeps the whole thing in release order.
49
+ def steps
50
+ @steps ||= (superseded_steps + Spree::Upgrade.pending_steps).select { |step| Spree::Upgrade.tracked?(step) }
51
+ end
52
+
53
+ def superseded_steps
54
+ @superseded_steps ||= Spree::Upgrade.superseded_steps
55
+ end
56
+
57
+ def superseded_ids
58
+ @superseded_ids ||= superseded_steps.map { |step| step['id'] }.to_set
59
+ end
60
+
61
+ def present(step)
62
+ task_name = Spree::Upgrade.task_class_name(step)
63
+ run = latest_runs[run_key(step, task_name)]
64
+
65
+ {
66
+ # A boundary this installation has already crossed. The dashboard
67
+ # shows these as done and refuses to run them.
68
+ superseded: superseded_ids.include?(step['id']),
69
+ id: step['id'],
70
+ name: step['name'],
71
+ notes: step['notes'],
72
+ from: step['from'],
73
+ to: step['to'],
74
+ docs: step['docs'],
75
+ task_name: Spree::MaintenanceTask.api_type_for(task_name),
76
+ # A rake-backed step runs inside the shared runner, so the id it
77
+ # was invoked with is what distinguishes one run from another.
78
+ arguments: Spree::Upgrade.task_arguments(step),
79
+ last_run: run && Spree.api.admin_maintenance_task_run_serializer.new(
80
+ run, params: { show_backtrace: holds_permission?('write_maintenance_tasks') }
81
+ ).to_h
82
+ }
83
+ end
84
+
85
+ # The most recent run per step. Reduced in Ruby rather than grouped
86
+ # in SQL: rake-backed runs are told apart by the step id inside their
87
+ # `arguments` JSON, and grouping on a JSON column is not portable
88
+ # across the adapters Spree supports. Only the three columns that
89
+ # pick the latest run are read for the whole history; the runs kept
90
+ # are then loaded in full.
91
+ def latest_runs
92
+ @latest_runs ||= begin
93
+ latest_ids = Spree::MaintenanceTaskRun.where(task_name: manifest_task_names).order(:id).
94
+ pluck(:id, :task_name, :arguments).
95
+ each_with_object({}) do |(id, task_name, arguments), result|
96
+ result[[task_name, arguments&.dig('step_id')]] = id
97
+ end
98
+ runs = Spree::MaintenanceTaskRun.includes(:admin_user).where(id: latest_ids.values).index_by(&:id)
99
+ latest_ids.transform_values { |id| runs[id] }
100
+ end
101
+ end
102
+
103
+ def manifest_task_names
104
+ steps.map { |step| Spree::Upgrade.task_class_name(step) }.uniq
105
+ end
106
+
107
+ def run_key(step, task_name)
108
+ [task_name, step['task_class'] ? nil : step['id']]
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,72 @@
1
+ module Spree
2
+ module Api
3
+ module V3
4
+ module Admin
5
+ # Admin API serializer for {Spree::MaintenanceTaskRun} — the progress
6
+ # the dashboard polls while a task runs, and the audit trail it shows
7
+ # afterwards (docs/plans/6.0-maintenance-tasks.md).
8
+ #
9
+ # Admin-only by design: there is no store counterpart, because nothing
10
+ # about operator-run data work is customer-facing.
11
+ class MaintenanceTaskRunSerializer < BaseSerializer
12
+ typelize task_name: :string,
13
+ status: [:string, enum: Spree::MaintenanceTaskRun.statuses, enum_type_name: 'MaintenanceTaskRunStatus'],
14
+ dry_run: :boolean,
15
+ initiated_via: :string,
16
+ tick_count: :number,
17
+ tick_total: [:number, nullable: true],
18
+ duration: [:number, nullable: true],
19
+ tallies: 'Record<string, number>',
20
+ arguments: 'Record<string, unknown>',
21
+ error_class: [:string, nullable: true],
22
+ error_message: [:string, nullable: true],
23
+ error_backtrace: [:string, nullable: true],
24
+ active: :boolean,
25
+ admin_user_id: [:string, nullable: true],
26
+ started_at: [:string, nullable: true],
27
+ ended_at: [:string, nullable: true]
28
+
29
+ attributes :status, :dry_run, :initiated_via,
30
+ :tick_count, :tick_total, :error_class, :error_message,
31
+ :started_at, :ended_at, :created_at, :updated_at
32
+
33
+ # The task's short name, as `GET /upgrade_steps` gives it.
34
+ attribute :task_name, &:task_api_type
35
+
36
+ # to_f before rounding: the column is a decimal, and a BigDecimal
37
+ # serializes as a quoted string — a client reading `duration` wants a
38
+ # number, and the schema says it gets one.
39
+ attribute :duration do |run|
40
+ run.duration&.to_f&.round(3)
41
+ end
42
+
43
+ attribute :tallies do |run|
44
+ run.tallies || {}
45
+ end
46
+
47
+ attribute :arguments do |run|
48
+ run.arguments || {}
49
+ end
50
+
51
+ # The failure detail an operator needs to fix the cause. Held back
52
+ # from read-only callers — a backtrace names internal paths and
53
+ # class names that a read scope has no business seeing.
54
+ attribute :error_backtrace do |run|
55
+ run.error_backtrace if params[:show_backtrace]
56
+ end
57
+
58
+ attribute :active, &:active?
59
+
60
+ attribute :admin_user_id do |run|
61
+ run.admin_user&.prefixed_id
62
+ end
63
+
64
+ # Who fired it. Pass `expand=admin_user` for the record itself.
65
+ one :admin_user,
66
+ resource: proc { Spree.api.admin_admin_user_serializer },
67
+ if: proc { expand?('admin_user') }
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
data/config/routes.rb CHANGED
@@ -509,6 +509,15 @@ Spree::Core::Engine.add_routes do
509
509
  post :test, on: :member
510
510
  end
511
511
 
512
+ # Maintenance tasks — see docs/plans/6.0-maintenance-tasks.md.
513
+ # A run names its task by the short name the upgrade steps give.
514
+ resources :upgrade_steps, only: [:index]
515
+ resources :maintenance_task_runs, only: [:index, :show, :create] do
516
+ scope module: :maintenance_task_runs do
517
+ resource :cancellation, only: [:create]
518
+ end
519
+ end
520
+
512
521
  # Promotions, with nested actions/rules/coupon codes.
513
522
  resources :promotions, concerns: :custom_fieldable do
514
523
  resources :promotion_actions, only: [:index, :show, :create, :update, :destroy]
@@ -41,6 +41,7 @@ module Spree
41
41
  store setup_task update_status api_key oauth_application oauth_access_token oauth_consent_request oauth_redirect
42
42
  oauth_protected_resource allowed_origin webhook_endpoint webhook_event webhook_delivery invitation
43
43
  invitation_acceptance_link role permission export saved_report import import_row import_mapping promotion
44
+ maintenance_task_run
44
45
  promotion_action promotion_rule coupon_code price_adjustment_tier price_list price_rule
45
46
  resource_translations email_template email_template_draft email_template_revision email_template_preview
46
47
  email_template_sample_record file_upload file_upload_target].freeze
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spree_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0.rc7
4
+ version: 6.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vendo Connect Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-26 00:00:00.000000000 Z
11
+ date: 2026-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rswag-specs
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - '='
46
46
  - !ruby/object:Gem::Version
47
- version: 6.0.0.rc7
47
+ version: 6.0.0
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - '='
53
53
  - !ruby/object:Gem::Version
54
- version: 6.0.0.rc7
54
+ version: 6.0.0
55
55
  description: Spree Commerce Store / Admin API and HTTP webhooks
56
56
  email:
57
57
  - hello@spreecommerce.org
@@ -65,6 +65,7 @@ files:
65
65
  - app/controllers/concerns/spree/api/v3/admin/auth_cookies.rb
66
66
  - app/controllers/concerns/spree/api/v3/admin/email_template_lookup.rb
67
67
  - app/controllers/concerns/spree/api/v3/admin/embed_permissions.rb
68
+ - app/controllers/concerns/spree/api/v3/admin/maintenance_tasks_switch.rb
68
69
  - app/controllers/concerns/spree/api/v3/admin/product_listing.rb
69
70
  - app/controllers/concerns/spree/api/v3/admin/product_membership.rb
70
71
  - app/controllers/concerns/spree/api/v3/admin/receivable_actions.rb
@@ -190,6 +191,8 @@ files:
190
191
  - app/controllers/spree/api/v3/admin/invitations/acceptance_links_controller.rb
191
192
  - app/controllers/spree/api/v3/admin/invitations_controller.rb
192
193
  - app/controllers/spree/api/v3/admin/locales_controller.rb
194
+ - app/controllers/spree/api/v3/admin/maintenance_task_runs/cancellations_controller.rb
195
+ - app/controllers/spree/api/v3/admin/maintenance_task_runs_controller.rb
193
196
  - app/controllers/spree/api/v3/admin/markets_controller.rb
194
197
  - app/controllers/spree/api/v3/admin/me_controller.rb
195
198
  - app/controllers/spree/api/v3/admin/media_controller.rb
@@ -276,6 +279,7 @@ files:
276
279
  - app/controllers/spree/api/v3/admin/translations_controller.rb
277
280
  - app/controllers/spree/api/v3/admin/translations_coverage_controller.rb
278
281
  - app/controllers/spree/api/v3/admin/updates_controller.rb
282
+ - app/controllers/spree/api/v3/admin/upgrade_steps_controller.rb
279
283
  - app/controllers/spree/api/v3/admin/variants_controller.rb
280
284
  - app/controllers/spree/api/v3/admin/webhook_deliveries_controller.rb
281
285
  - app/controllers/spree/api/v3/admin/webhook_endpoints_controller.rb
@@ -507,6 +511,7 @@ files:
507
511
  - app/serializers/spree/api/v3/admin/invitation_serializer.rb
508
512
  - app/serializers/spree/api/v3/admin/line_item_serializer.rb
509
513
  - app/serializers/spree/api/v3/admin/locale_serializer.rb
514
+ - app/serializers/spree/api/v3/admin/maintenance_task_run_serializer.rb
510
515
  - app/serializers/spree/api/v3/admin/market_serializer.rb
511
516
  - app/serializers/spree/api/v3/admin/media_serializer.rb
512
517
  - app/serializers/spree/api/v3/admin/membership_product_serializer.rb
@@ -831,9 +836,9 @@ licenses:
831
836
  - BSD-3-Clause
832
837
  metadata:
833
838
  bug_tracker_uri: https://github.com/spree/spree/issues
834
- changelog_uri: https://github.com/spree/spree/releases/tag/v6.0.0.rc7
839
+ changelog_uri: https://github.com/spree/spree/releases/tag/v6.0.0
835
840
  documentation_uri: https://docs.spreecommerce.org/
836
- source_code_uri: https://github.com/spree/spree/tree/v6.0.0.rc7
841
+ source_code_uri: https://github.com/spree/spree/tree/v6.0.0
837
842
  post_install_message:
838
843
  rdoc_options: []
839
844
  require_paths: