maintenance_tasks 2.10.1 → 2.13.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 +4 -4
- data/README.md +160 -45
- data/app/controllers/maintenance_tasks/application_controller.rb +15 -5
- data/app/helpers/maintenance_tasks/application_helper.rb +1 -1
- data/app/helpers/maintenance_tasks/tasks_helper.rb +28 -11
- data/app/jobs/concerns/maintenance_tasks/task_job_concern.rb +33 -4
- data/app/models/maintenance_tasks/run.rb +103 -71
- data/app/models/maintenance_tasks/task.rb +42 -0
- data/app/models/maintenance_tasks/task_data_index.rb +2 -1
- data/app/views/layouts/maintenance_tasks/_navbar.html.erb +1 -1
- data/app/views/layouts/maintenance_tasks/application.html.erb +35 -4
- data/app/views/maintenance_tasks/runs/_arguments.html.erb +1 -1
- data/app/views/maintenance_tasks/runs/_run.html.erb +14 -8
- data/app/views/maintenance_tasks/runs/_serializable.html.erb +15 -18
- data/app/views/maintenance_tasks/tasks/_task.html.erb +5 -5
- data/app/views/maintenance_tasks/tasks/index.html.erb +6 -4
- data/app/views/maintenance_tasks/tasks/show.html.erb +16 -12
- data/db/migrate/20201211151756_create_maintenance_tasks_runs.rb +10 -2
- data/db/migrate/20210219212931_change_cursor_to_string.rb +1 -1
- data/db/migrate/20210225152418_remove_index_on_task_name.rb +1 -1
- data/db/migrate/20210517131953_add_arguments_to_maintenance_tasks_runs.rb +1 -1
- data/db/migrate/20211210152329_add_lock_version_to_maintenance_tasks_runs.rb +1 -1
- data/db/migrate/20220706101937_change_runs_tick_columns_to_bigints.rb +1 -1
- data/db/migrate/20220713131925_add_index_on_task_name_and_status_to_runs.rb +1 -1
- data/db/migrate/20230622035229_add_metadata_to_runs.rb +1 -1
- data/lib/maintenance_tasks/engine.rb +4 -0
- data/lib/maintenance_tasks.rb +50 -12
- metadata +13 -15
- data/lib/patches/active_record_batch_enumerator.rb +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8402f1f4b8919e892a6334373ad397d49b5c6c78893f2f107b990b90653435d1
|
|
4
|
+
data.tar.gz: 1459d5b92233a1196aeb83c80bec9da525f04e975a50f6a415cdcd8dc647bd59
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3dff16b85a650f4d73cf79df20267d1278b1181f2ba6717742581909ebfd39145dfdd9b9cf1453edaa785474fb6f7dda87af2bad534e6398bf010945b02f2da3
|
|
7
|
+
data.tar.gz: c7b75c1edc684fad66c5080ee3642fe469038e1f4ab2329ead0fd3c65e5f0ca2a940250dc988658dce672e2de0d056741b9a317310afa23165ba27794722ce1d
|
data/README.md
CHANGED
|
@@ -74,9 +74,11 @@ The generator creates and runs a migration to add the necessary table to your
|
|
|
74
74
|
database. It also mounts Maintenance Tasks in your `config/routes.rb`. By
|
|
75
75
|
default the web UI can be accessed in the new `/maintenance_tasks` path.
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
77
|
+
This gem uses the [Rails Error Reporter][rails-error-reporting] to report errors.
|
|
78
|
+
If you are using a bug tracking service you may want to subscribe to the
|
|
79
|
+
reporter. See [Reporting Errors](#reporting-errors) for more information.
|
|
80
|
+
|
|
81
|
+
[rails-error-reporting]: https://guides.rubyonrails.org/error_reporting.html
|
|
80
82
|
|
|
81
83
|
### Active Job Dependency
|
|
82
84
|
|
|
@@ -114,12 +116,12 @@ The typical Maintenance Tasks workflow is as follows:
|
|
|
114
116
|
1. [Generate a class describing the Task](#creating-a-task) and the work to be
|
|
115
117
|
done.
|
|
116
118
|
2. Run the Task
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
- either by [using the included web UI](#running-a-task-from-the-web-ui),
|
|
120
|
+
- or by [using the command line](#running-a-task-from-the-command-line),
|
|
121
|
+
- or by [using Ruby](#running-a-task-from-ruby).
|
|
120
122
|
3. [Monitor the Task](#monitoring-your-tasks-status)
|
|
121
|
-
|
|
122
|
-
|
|
123
|
+
- either by using the included web UI,
|
|
124
|
+
- or by manually checking your task’s run’s status in your database.
|
|
123
125
|
4. Optionally, delete the Task code if you no longer need it.
|
|
124
126
|
|
|
125
127
|
### Creating a Task
|
|
@@ -168,7 +170,8 @@ end
|
|
|
168
170
|
When processing records from an Active Record Relation, records are fetched in
|
|
169
171
|
batches internally, and then each record is passed to the `#process` method.
|
|
170
172
|
Maintenance Tasks will query the database to fetch records in batches of 100 by
|
|
171
|
-
default, but the batch size can be modified using the `collection_batch_size`
|
|
173
|
+
default, but the batch size can be modified using the `collection_batch_size`
|
|
174
|
+
macro:
|
|
172
175
|
|
|
173
176
|
```ruby
|
|
174
177
|
# app/tasks/maintenance/update_posts_task.rb
|
|
@@ -502,13 +505,44 @@ set of values will be used to populate a dropdown in the user interface. The
|
|
|
502
505
|
following types are supported:
|
|
503
506
|
|
|
504
507
|
* Arrays
|
|
505
|
-
* Procs and lambdas that optionally accept the Task instance, and return an
|
|
506
|
-
|
|
508
|
+
* Procs and lambdas that optionally accept the Task instance, and return an
|
|
509
|
+
Array.
|
|
510
|
+
* Callable objects that receive one argument, the Task instance, and return an
|
|
511
|
+
Array.
|
|
507
512
|
* Methods that return an Array, called on the Task instance.
|
|
508
513
|
|
|
509
514
|
For enumerables that don't match the supported types, a text field will be
|
|
510
515
|
rendered instead.
|
|
511
516
|
|
|
517
|
+
### Masking Task Parameters
|
|
518
|
+
|
|
519
|
+
Task attributes can be masked in the UI by adding `mask_attribute` class method
|
|
520
|
+
in the task class. This will replace the value in the arguments list with
|
|
521
|
+
`[FILTERED]` in the UI.
|
|
522
|
+
|
|
523
|
+
```ruby
|
|
524
|
+
# app/tasks/maintenance/sensitive_params_task.rb
|
|
525
|
+
|
|
526
|
+
module Maintenance
|
|
527
|
+
class SensitiveParamsTask < MaintenanceTasks::Task
|
|
528
|
+
attribute :sensitive_content, :string
|
|
529
|
+
|
|
530
|
+
mask_attribute :sensitive_content
|
|
531
|
+
end
|
|
532
|
+
end
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
If you have any filtered parameters in the global [Rails parameter
|
|
536
|
+
filter][rails-parameter-filter], they will be automatically taken into account
|
|
537
|
+
when masking the parameters, which means that you can mask parameters across all
|
|
538
|
+
tasks by adding them to the global rails parameters filter.
|
|
539
|
+
|
|
540
|
+
[rails-parameter-filter]:https://guides.rubyonrails.org/configuring.html#config-filter-parameters
|
|
541
|
+
|
|
542
|
+
```ruby
|
|
543
|
+
Rails.application.config.filter_parameters += %i[token]
|
|
544
|
+
```
|
|
545
|
+
|
|
512
546
|
### Custom cursor columns to improve performance
|
|
513
547
|
|
|
514
548
|
The [job-iteration gem][job-iteration], on which this gem depends, adds an
|
|
@@ -899,10 +933,10 @@ a Task can be in:
|
|
|
899
933
|
|
|
900
934
|
The Maintenance Tasks engine uses Rails sessions for flash messages and storing
|
|
901
935
|
the CSRF token. For the engine to work in an API-only Rails application, you
|
|
902
|
-
need to add a [session middleware][] and the `ActionDispatch::Flash`
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
936
|
+
need to add a [session middleware][] and the `ActionDispatch::Flash` middleware.
|
|
937
|
+
The engine also defines a strict [Content Security Policy][], make sure to
|
|
938
|
+
include `ActionDispatch::ContentSecurityPolicy::Middleware` in your app's
|
|
939
|
+
middleware stack to ensure the CSP is delivered to the user's browser.
|
|
906
940
|
|
|
907
941
|
[session middleware]: https://guides.rubyonrails.org/api_app.html#using-session-middlewares
|
|
908
942
|
[Content Security Policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
|
|
@@ -926,8 +960,8 @@ module YourApplication
|
|
|
926
960
|
end
|
|
927
961
|
```
|
|
928
962
|
|
|
929
|
-
You can read more in the [Using Rails for API-only Applications][rails api]
|
|
930
|
-
guide.
|
|
963
|
+
You can read more in the [Using Rails for API-only Applications][rails api]
|
|
964
|
+
Rails guide.
|
|
931
965
|
|
|
932
966
|
[rails api]: https://guides.rubyonrails.org/api_app.html
|
|
933
967
|
|
|
@@ -944,9 +978,9 @@ infrastructure or code changes.
|
|
|
944
978
|
This means a Task can safely be interrupted, re-enqueued and resumed without any
|
|
945
979
|
intervention at the end of an iteration, after the `process` method returns.
|
|
946
980
|
|
|
947
|
-
By default, a running Task will be interrupted after running for more 5
|
|
948
|
-
This is [configured in the `job-iteration` gem][max-job-runtime] and
|
|
949
|
-
tweaked in an initializer if necessary.
|
|
981
|
+
By default, a running Task will be interrupted after running for more than 5
|
|
982
|
+
minutes. This is [configured in the `job-iteration` gem][max-job-runtime] and
|
|
983
|
+
can be tweaked in an initializer if necessary.
|
|
950
984
|
|
|
951
985
|
[max-job-runtime]: https://github.com/Shopify/job-iteration/blob/-/guides/best-practices.md#max-job-runtime
|
|
952
986
|
|
|
@@ -988,44 +1022,89 @@ If you are stuck in `pausing` and wish to preserve your tasks's position
|
|
|
988
1022
|
There are a few configurable options for the gem. Custom configurations should
|
|
989
1023
|
be placed in a `maintenance_tasks.rb` initializer.
|
|
990
1024
|
|
|
991
|
-
####
|
|
1025
|
+
#### Reporting errors
|
|
992
1026
|
|
|
993
1027
|
Exceptions raised while a Task is performing are rescued and information about
|
|
994
1028
|
the error is persisted and visible in the UI.
|
|
995
1029
|
|
|
996
|
-
|
|
997
|
-
|
|
1030
|
+
Errors are also sent to `Rails.error.report`, which can be configured by
|
|
1031
|
+
your application. See the [Error Reporting in Rails
|
|
1032
|
+
Applications][rails-error-reporting] guide for more details.
|
|
1033
|
+
|
|
1034
|
+
Reports to the error reporter will contain the following data:
|
|
1035
|
+
|
|
1036
|
+
* `error`: The exception that was raised.
|
|
1037
|
+
* `context`: A hash with additional information about the Task and the error:
|
|
1038
|
+
* `task_name`: The name of the Task that errored
|
|
1039
|
+
* `started_at`: The time the Task started
|
|
1040
|
+
* `ended_at`: The time the Task errored
|
|
1041
|
+
* `run_id`: The id of the errored Task run
|
|
1042
|
+
* `tick_count`: The tick count at the time of the error
|
|
1043
|
+
* `errored_element`: The element, if any, that was being processed when the
|
|
1044
|
+
* `source`: This will be `maintenance-tasks`
|
|
1045
|
+
* `handled`: the value of `MaintenanceTasks.report_errors_as_handled` (default `true`, see below)
|
|
1046
|
+
|
|
1047
|
+
Note that `context` may be empty if the Task produced an error before any
|
|
1048
|
+
context could be gathered (for example, if deserializing the job to process your
|
|
1049
|
+
Task failed).
|
|
1050
|
+
|
|
1051
|
+
Here's an example custom subscriber to the Rails error reporter for integrating
|
|
1052
|
+
with an exception monitoring service (Bugsnag):
|
|
998
1053
|
|
|
999
1054
|
```ruby
|
|
1000
1055
|
# config/initializers/maintenance_tasks.rb
|
|
1056
|
+
MaintenanceTasks.report_errors_as_handled = false
|
|
1057
|
+
|
|
1058
|
+
class MaintenanceTasksErrorSubscriber
|
|
1059
|
+
def report(error, handled:, severity:, context:, source: nil)
|
|
1060
|
+
return unless source == "maintenance-tasks"
|
|
1001
1061
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1062
|
+
unless handled
|
|
1063
|
+
Bugsnag.notify(error) do |notification|
|
|
1064
|
+
notification.add_metadata(:task, context)
|
|
1065
|
+
end
|
|
1066
|
+
else
|
|
1067
|
+
Rails.logger.info(error)
|
|
1068
|
+
end
|
|
1005
1069
|
end
|
|
1006
1070
|
end
|
|
1071
|
+
|
|
1072
|
+
Rails.error.subscribe(MaintenanceTasksErrorSubscriber.new)
|
|
1007
1073
|
```
|
|
1008
1074
|
|
|
1009
|
-
|
|
1075
|
+
`MaintenanceTasks.report_errors_as_handled` determines the value for `handled` in this example.
|
|
1076
|
+
By default (for backwards compatibility) this is `true`.
|
|
1077
|
+
Setting this to `false` provides more accurate error reporting as it allows to distinguish between
|
|
1078
|
+
expected (e.g., via `report_on`) and unexpected errors in error subscribers.
|
|
1079
|
+
`false` will be the default in v3.0.
|
|
1010
1080
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1081
|
+
#### Reporting errors during iteration
|
|
1082
|
+
|
|
1083
|
+
By default, errors raised during task iteration will be raised to the
|
|
1084
|
+
application and iteration will stop. However, you may want to handle some errors
|
|
1085
|
+
and continue iteration. `MaintenanceTasks::Task.report_on` can be used to rescue
|
|
1086
|
+
certain exceptions and report them to the Rails error reporter. Any keyword
|
|
1087
|
+
arguments are passed to
|
|
1088
|
+
[ActiveSupport::ErrorReporter#report][as-error-reporter-report]:
|
|
1089
|
+
|
|
1090
|
+
[as-error-reporter-report]: https://api.rubyonrails.org/classes/ActiveSupport/ErrorReporter.html#method-i-report
|
|
1091
|
+
|
|
1092
|
+
```ruby
|
|
1093
|
+
class MyTask < MaintenanceTasks::Task
|
|
1094
|
+
report_on(MyException, OtherException, severity: :info, context: {task_name: "my_task"})
|
|
1095
|
+
end
|
|
1096
|
+
```
|
|
1097
|
+
|
|
1098
|
+
`MaintenanceTasks::Task` also includes `ActiveSupport::Rescuable` which you can
|
|
1099
|
+
use to implement custom error handling.
|
|
1100
|
+
|
|
1101
|
+
```ruby
|
|
1102
|
+
class MyTask < MaintenanceTasks::Task
|
|
1103
|
+
rescue_from(MyException) do |exception|
|
|
1104
|
+
handle(exception)
|
|
1105
|
+
end
|
|
1106
|
+
end
|
|
1107
|
+
```
|
|
1029
1108
|
|
|
1030
1109
|
#### Customizing the maintenance tasks module
|
|
1031
1110
|
|
|
@@ -1188,6 +1267,42 @@ The value for `MaintenanceTasks.stuck_task_duration` must be an
|
|
|
1188
1267
|
`ActiveSupport::Duration`. If no value is specified, it will default to 5
|
|
1189
1268
|
minutes.
|
|
1190
1269
|
|
|
1270
|
+
#### Configure status reload frequency
|
|
1271
|
+
|
|
1272
|
+
`MaintenanceTasks.status_reload_frequency` can be configured to specify how often
|
|
1273
|
+
the run status should be reloaded during iteration. By default, the status is
|
|
1274
|
+
reloaded every second, but this can be increased to improve performance. Note that increasing the reload interval impacts how quickly
|
|
1275
|
+
your task will stop if it is paused or interrupted.
|
|
1276
|
+
|
|
1277
|
+
```ruby
|
|
1278
|
+
# config/initializers/maintenance_tasks.rb
|
|
1279
|
+
MaintenanceTasks.status_reload_frequency = 10.seconds # Reload status every 10 seconds
|
|
1280
|
+
```
|
|
1281
|
+
|
|
1282
|
+
Individual tasks can also override this setting using the `reload_status_every` method:
|
|
1283
|
+
|
|
1284
|
+
```ruby
|
|
1285
|
+
# app/tasks/maintenance/update_posts_task.rb
|
|
1286
|
+
|
|
1287
|
+
module Maintenance
|
|
1288
|
+
class UpdatePostsTask < MaintenanceTasks::Task
|
|
1289
|
+
# Reload status every 5 seconds instead of the global default
|
|
1290
|
+
reload_status_every(5.seconds)
|
|
1291
|
+
|
|
1292
|
+
def collection
|
|
1293
|
+
Post.all
|
|
1294
|
+
end
|
|
1295
|
+
|
|
1296
|
+
def process(post)
|
|
1297
|
+
post.update!(content: "New content!")
|
|
1298
|
+
end
|
|
1299
|
+
end
|
|
1300
|
+
end
|
|
1301
|
+
```
|
|
1302
|
+
|
|
1303
|
+
This optimization can significantly reduce database queries, especially for short iterations.
|
|
1304
|
+
This is especially useful if the task doesn't need to check for cancellation/pausing very often.
|
|
1305
|
+
|
|
1191
1306
|
#### Metadata
|
|
1192
1307
|
|
|
1193
1308
|
`MaintenanceTasks.metadata` can be configured to specify a proc from which to
|
|
@@ -8,16 +8,26 @@ module MaintenanceTasks
|
|
|
8
8
|
BULMA_CDN = "https://cdn.jsdelivr.net"
|
|
9
9
|
|
|
10
10
|
content_security_policy do |policy|
|
|
11
|
-
policy.
|
|
11
|
+
policy.style_src_elem(
|
|
12
12
|
BULMA_CDN,
|
|
13
|
-
#
|
|
14
|
-
"'sha256-
|
|
13
|
+
# <style> tag in app/views/layouts/maintenance_tasks/application.html.erb
|
|
14
|
+
"'sha256-WHHDQLdkleXnAN5zs0GDXC5ls41CHUaVsJtVpaNx+EM='",
|
|
15
15
|
)
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
capybara_lockstep_scripts = [
|
|
17
|
+
"'sha256-1AoN3ZtJC5OvqkMgrYvhZjp4kI8QjJjO7TAyKYiDw+U='",
|
|
18
|
+
"'sha256-QVSzZi6ZsX/cu4h+hIs1iVivG1BxUmJggiEsGDIXBG0='", # with debug on
|
|
19
|
+
] if defined?(Capybara::Lockstep)
|
|
20
|
+
policy.script_src_elem(
|
|
21
|
+
# <script> tag in app/views/layouts/maintenance_tasks/application.html.erb
|
|
18
22
|
"'sha256-NiHKryHWudRC2IteTqmY9v1VkaDUA/5jhgXkMTkgo2w='",
|
|
23
|
+
# <script> tag for capybara-lockstep
|
|
24
|
+
*capybara_lockstep_scripts,
|
|
19
25
|
)
|
|
26
|
+
|
|
27
|
+
policy.require_trusted_types_for # disable because we use new DOMParser().parseFromString
|
|
20
28
|
policy.frame_ancestors(:self)
|
|
29
|
+
policy.connect_src(:self)
|
|
30
|
+
policy.form_action(:self)
|
|
21
31
|
end
|
|
22
32
|
|
|
23
33
|
protect_from_forgery with: :exception
|
|
@@ -14,7 +14,7 @@ module MaintenanceTasks
|
|
|
14
14
|
# @param datetime [ActiveSupport::TimeWithZone] the time to be presented.
|
|
15
15
|
# @return [String] the HTML to render with the relative datetime in words.
|
|
16
16
|
def time_ago(datetime)
|
|
17
|
-
time_tag(datetime, title: datetime.utc
|
|
17
|
+
time_tag(datetime, title: datetime.utc, class: "is-clickable") do
|
|
18
18
|
time_ago_in_words(datetime) + " ago"
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -47,7 +47,7 @@ module MaintenanceTasks
|
|
|
47
47
|
progress_bar = tag.progress(
|
|
48
48
|
value: progress.value,
|
|
49
49
|
max: progress.max,
|
|
50
|
-
class: ["progress"] + STATUS_COLOURS.fetch(run.status),
|
|
50
|
+
class: ["progress", "mt-4"] + STATUS_COLOURS.fetch(run.status),
|
|
51
51
|
)
|
|
52
52
|
progress_text = tag.p(tag.i(progress.text))
|
|
53
53
|
tag.div(progress_bar + progress_text, class: "block")
|
|
@@ -60,7 +60,10 @@ module MaintenanceTasks
|
|
|
60
60
|
# @return [String] the span element containing the status, with the
|
|
61
61
|
# appropriate tag class attached.
|
|
62
62
|
def status_tag(status)
|
|
63
|
-
tag.span(
|
|
63
|
+
tag.span(
|
|
64
|
+
status.capitalize,
|
|
65
|
+
class: ["tag", "has-text-weight-medium", "pr-2", "mr-4"] + STATUS_COLOURS.fetch(status),
|
|
66
|
+
)
|
|
64
67
|
end
|
|
65
68
|
|
|
66
69
|
# Reports the approximate elapsed time a Run has been processed so far based
|
|
@@ -101,7 +104,7 @@ module MaintenanceTasks
|
|
|
101
104
|
)
|
|
102
105
|
end
|
|
103
106
|
|
|
104
|
-
# Resolves values covered by the inclusion validator for a
|
|
107
|
+
# Resolves values covered by the inclusion validator for a Task attribute.
|
|
105
108
|
# Supported option types:
|
|
106
109
|
# - Arrays
|
|
107
110
|
# - Procs and lambdas that optionally accept the Task instance, and return an Array.
|
|
@@ -112,7 +115,7 @@ module MaintenanceTasks
|
|
|
112
115
|
#
|
|
113
116
|
# Returned values are used to populate a dropdown list of options.
|
|
114
117
|
#
|
|
115
|
-
# @param
|
|
118
|
+
# @param task [Task] The Task for which the value needs to be resolved.
|
|
116
119
|
# @param parameter_name [String] The parameter name.
|
|
117
120
|
#
|
|
118
121
|
# @return [Array] value of the resolved inclusion option.
|
|
@@ -149,24 +152,27 @@ module MaintenanceTasks
|
|
|
149
152
|
# If the parameter has a `validates_inclusion_of` validator, return a dropdown list of options instead.
|
|
150
153
|
def parameter_field(form_builder, parameter_name)
|
|
151
154
|
inclusion_values = resolve_inclusion_value(form_builder.object, parameter_name)
|
|
152
|
-
|
|
155
|
+
if inclusion_values
|
|
156
|
+
return tag.div(form_builder.select(parameter_name, inclusion_values, prompt: "Select a value"), class: "select")
|
|
157
|
+
end
|
|
153
158
|
|
|
154
159
|
case form_builder.object.class.attribute_types[parameter_name]
|
|
155
160
|
when ActiveModel::Type::Integer
|
|
156
|
-
form_builder.number_field(parameter_name)
|
|
161
|
+
form_builder.number_field(parameter_name, class: "input")
|
|
157
162
|
when ActiveModel::Type::Decimal, ActiveModel::Type::Float
|
|
158
|
-
form_builder.number_field(parameter_name, { step: "any" })
|
|
163
|
+
form_builder.number_field(parameter_name, { step: "any", class: "input" })
|
|
159
164
|
when ActiveModel::Type::DateTime
|
|
160
|
-
form_builder.datetime_field(parameter_name) + datetime_field_help_text
|
|
165
|
+
form_builder.datetime_field(parameter_name, class: "input") + datetime_field_help_text
|
|
161
166
|
when ActiveModel::Type::Date
|
|
162
|
-
form_builder.date_field(parameter_name)
|
|
167
|
+
form_builder.date_field(parameter_name, class: "input")
|
|
163
168
|
when ActiveModel::Type::Time
|
|
164
|
-
form_builder.time_field(parameter_name)
|
|
169
|
+
form_builder.time_field(parameter_name, class: "input")
|
|
165
170
|
when ActiveModel::Type::Boolean
|
|
166
|
-
form_builder.check_box(parameter_name)
|
|
171
|
+
form_builder.check_box(parameter_name, class: "checkbox")
|
|
167
172
|
else
|
|
168
173
|
form_builder.text_area(parameter_name, class: "textarea")
|
|
169
174
|
end
|
|
175
|
+
.then { |input| tag.div(input, class: "control") }
|
|
170
176
|
end
|
|
171
177
|
|
|
172
178
|
# Return helper text for the datetime-local form field.
|
|
@@ -182,5 +188,16 @@ module MaintenanceTasks
|
|
|
182
188
|
class: "content is-small",
|
|
183
189
|
)
|
|
184
190
|
end
|
|
191
|
+
|
|
192
|
+
# Checks if an attribute is required for a given Task.
|
|
193
|
+
#
|
|
194
|
+
# @param task [MaintenanceTasks::TaskDataShow] The TaskDataShow instance.
|
|
195
|
+
# @param parameter_name [Symbol] The name of the attribute to check.
|
|
196
|
+
# @return [Boolean] Whether the attribute is required.
|
|
197
|
+
def attribute_required?(task, parameter_name)
|
|
198
|
+
task.class.validators_on(parameter_name).any? do |validator|
|
|
199
|
+
validator.kind == :presence
|
|
200
|
+
end
|
|
201
|
+
end
|
|
185
202
|
end
|
|
186
203
|
end
|
|
@@ -101,7 +101,8 @@ module MaintenanceTasks
|
|
|
101
101
|
throw(:abort, :skip_complete_callbacks) if @run.stopping?
|
|
102
102
|
task_iteration(input)
|
|
103
103
|
@ticker.tick
|
|
104
|
-
|
|
104
|
+
|
|
105
|
+
reload_run_status
|
|
105
106
|
end
|
|
106
107
|
|
|
107
108
|
def task_iteration(input)
|
|
@@ -112,7 +113,7 @@ module MaintenanceTasks
|
|
|
112
113
|
end
|
|
113
114
|
rescue => error
|
|
114
115
|
@errored_element = input
|
|
115
|
-
raise error
|
|
116
|
+
raise error unless @task.rescue_with_handler(error)
|
|
116
117
|
end
|
|
117
118
|
|
|
118
119
|
def before_perform
|
|
@@ -127,6 +128,8 @@ module MaintenanceTasks
|
|
|
127
128
|
@ticker = Ticker.new(MaintenanceTasks.ticker_delay) do |ticks, duration|
|
|
128
129
|
@run.persist_progress(ticks, duration)
|
|
129
130
|
end
|
|
131
|
+
|
|
132
|
+
@last_status_reload = nil
|
|
130
133
|
end
|
|
131
134
|
|
|
132
135
|
def on_start
|
|
@@ -181,11 +184,37 @@ module MaintenanceTasks
|
|
|
181
184
|
task_name: @run.task_name,
|
|
182
185
|
started_at: @run.started_at,
|
|
183
186
|
ended_at: @run.ended_at,
|
|
187
|
+
run_id: @run.id,
|
|
188
|
+
tick_count: @run.tick_count,
|
|
184
189
|
}
|
|
185
190
|
end
|
|
186
|
-
errored_element = @errored_element if defined?(@errored_element)
|
|
191
|
+
task_context[:errored_element] = @errored_element if defined?(@errored_element)
|
|
187
192
|
ensure
|
|
188
|
-
MaintenanceTasks.error_handler
|
|
193
|
+
if MaintenanceTasks.instance_variable_get(:@error_handler)
|
|
194
|
+
errored_element = task_context.delete(:errored_element)
|
|
195
|
+
MaintenanceTasks.error_handler.call(error, task_context.except(:run_id, :tick_count), errored_element)
|
|
196
|
+
else
|
|
197
|
+
Rails.error.report(
|
|
198
|
+
error,
|
|
199
|
+
handled: MaintenanceTasks.report_errors_as_handled,
|
|
200
|
+
context: task_context,
|
|
201
|
+
source: "maintenance-tasks",
|
|
202
|
+
)
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def reload_run_status
|
|
207
|
+
return unless should_reload_status?
|
|
208
|
+
|
|
209
|
+
@run.reload_status
|
|
210
|
+
@last_status_reload = Time.now
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def should_reload_status?
|
|
214
|
+
return true if @last_status_reload.nil?
|
|
215
|
+
|
|
216
|
+
time_since_last_reload = Time.now - @last_status_reload
|
|
217
|
+
time_since_last_reload >= @task.status_reload_frequency
|
|
189
218
|
end
|
|
190
219
|
end
|
|
191
220
|
end
|