mission_control-jobs 1.0.2 → 1.1.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 +16 -9
- data/app/controllers/concerns/mission_control/jobs/job_filters.rb +9 -1
- data/app/controllers/mission_control/jobs/application_controller.rb +1 -0
- data/app/controllers/mission_control/jobs/discards_controller.rb +1 -1
- data/app/controllers/mission_control/jobs/jobs_controller.rb +1 -1
- data/app/controllers/mission_control/jobs/retries_controller.rb +1 -1
- data/app/helpers/mission_control/jobs/navigation_helper.rb +0 -8
- data/app/views/layouts/mission_control/jobs/_application_selection.html.erb +3 -0
- data/app/views/mission_control/jobs/jobs/_filters.html.erb +32 -22
- data/app/views/mission_control/jobs/jobs/_general_information.html.erb +7 -1
- data/app/views/mission_control/jobs/jobs/blocked/_job.html.erb +3 -2
- data/app/views/mission_control/jobs/jobs/failed/_actions.html.erb +2 -2
- data/app/views/mission_control/jobs/queues/_queue.html.erb +1 -1
- data/lib/active_job/job_proxy.rb +4 -0
- data/lib/active_job/queues.rb +9 -5
- data/lib/mission_control/jobs/engine.rb +2 -2
- data/lib/mission_control/jobs/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2bba1e3be2d257eea8a3956ca3f6b8c9e6708f2fc81fe23290953ac66ecd79b0
|
4
|
+
data.tar.gz: c470df35a98c7f0f90163540dca2d3c8bd9ca14128ae283731c0e920fdae140e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8a6068c3fa2f86d32f07fb458b644efb9e8e0661f1feb65e26bf41d4051bca594ab29d15819b9ab94051451015b1d051bf33b7152797c87ec1952a074f968b2c
|
7
|
+
data.tar.gz: 972eb81ce61459dcdd2e881ce88a5bf69da431f1b2a17ed6e10a3bf8572222f27b111035747c46492fc459b485542b5dfd072cfb04c4ae1dca6a0cbc5b73bc0c
|
data/README.md
CHANGED
@@ -54,8 +54,6 @@ For example, if you're using the Dockerfile generated by Rails with an API-only
|
|
54
54
|
RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile
|
55
55
|
```
|
56
56
|
|
57
|
-
*Note: Legacy CSS bundlers `sass-rails` and `sassc-rails` may fail to compile some of the CSS vendored into this library from [Bulma](https://github.com/jgthms/bulma), which was created in [Dart SASS](https://sass-lang.com/dart-sass/). You will therefore need to upgrade to `dartsass-rails` or some library that relies on it, like `cssbundling-rails`.*
|
58
|
-
|
59
57
|
### Authentication
|
60
58
|
|
61
59
|
Mission Control comes with **HTTP basic authentication enabled and closed** by default. Credentials are stored in [Rails's credentials](https://edgeguides.rubyonrails.org/security.html#custom-credentials) like this:
|
@@ -76,6 +74,15 @@ To set them up for different environments you can use the `RAILS_ENV` environmen
|
|
76
74
|
RAILS_ENV=production bin/rails mission_control:jobs:authentication:configure
|
77
75
|
```
|
78
76
|
|
77
|
+
User and password can also be configured by hand like this:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
Rails.application.configure do
|
81
|
+
MissionControl::Jobs.http_basic_auth_user = "dev"
|
82
|
+
MissionControl::Jobs.http_basic_auth_password = "secret"
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
79
86
|
#### Custom authentication
|
80
87
|
|
81
88
|
You can provide your own authentication mechanism, for example, if you have a certain type of admin user in your app that can access Mission Control. To make this easier, you can specify a different controller as the base class for Mission Control's controllers. By default, Mission Control's controllers will extend the host app's `ApplicationController`, but you can change this easily:
|
@@ -158,19 +165,19 @@ SERVERS_BY_APP.each do |app, servers|
|
|
158
165
|
end
|
159
166
|
|
160
167
|
# Default:
|
161
|
-
#
|
168
|
+
#
|
162
169
|
# @return Array<String, ActiveJob::QueueAdapters::Base)
|
163
170
|
# An array where:
|
164
171
|
# * the String represents the symbolic name for this server within the UI
|
165
172
|
# * ActiveJob::QueueAdapters::Base adapter instance used to access this Application Server/Service
|
166
|
-
[ server, queue_adapter ]
|
167
|
-
|
173
|
+
[ server, queue_adapter ]
|
174
|
+
|
168
175
|
# Optional return formats:
|
169
|
-
#
|
176
|
+
#
|
170
177
|
# @return Array<String, Array<ActiveJob::QueueAdapters::Base>>
|
171
|
-
# * This is equivalent, and behaves identically to, the format the default format above.
|
178
|
+
# * This is equivalent, and behaves identically to, the format the default format above.
|
172
179
|
# [ server, [ queue_adapter ]] # without optional backtrace cleaner
|
173
|
-
#
|
180
|
+
#
|
174
181
|
# @return Array<String, Array<ActiveJob::QueueAdapters::Base, ActiveSupport::BacktraceCleaner>>
|
175
182
|
# * This format adds an optional ActiveSupport::BacktraceCleaner to override the system wide
|
176
183
|
# backtrace cleaner for *this* Application Server/Service.
|
@@ -246,7 +253,7 @@ Available job servers:
|
|
246
253
|
|
247
254
|
And then:
|
248
255
|
```
|
249
|
-
>> connect_to
|
256
|
+
>> connect_to hey:solid_queue
|
250
257
|
Connected to hey:solid_queue
|
251
258
|
```
|
252
259
|
|
@@ -4,7 +4,7 @@ module MissionControl::Jobs::JobFilters
|
|
4
4
|
included do
|
5
5
|
before_action :set_filters
|
6
6
|
|
7
|
-
helper_method :active_filters
|
7
|
+
helper_method :active_filters?, :jobs_filter_param
|
8
8
|
end
|
9
9
|
|
10
10
|
private
|
@@ -20,6 +20,14 @@ module MissionControl::Jobs::JobFilters
|
|
20
20
|
@job_filters.any?
|
21
21
|
end
|
22
22
|
|
23
|
+
def jobs_filter_param
|
24
|
+
if @job_filters&.any?
|
25
|
+
{ filter: @job_filters }
|
26
|
+
else
|
27
|
+
{}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
23
31
|
def finished_at_range_params
|
24
32
|
range_start, range_end = params.dig(:filter, :finished_at_start), params.dig(:filter, :finished_at_end)
|
25
33
|
if range_start || range_end
|
@@ -12,6 +12,7 @@ class MissionControl::Jobs::ApplicationController < MissionControl::Jobs.base_co
|
|
12
12
|
include MissionControl::Jobs::BasicAuthentication
|
13
13
|
include MissionControl::Jobs::ApplicationScoped, MissionControl::Jobs::NotFoundRedirections
|
14
14
|
include MissionControl::Jobs::AdapterFeatures
|
15
|
+
include MissionControl::Jobs::JobFilters
|
15
16
|
|
16
17
|
around_action :set_current_locale
|
17
18
|
|
@@ -13,6 +13,6 @@ class MissionControl::Jobs::DiscardsController < MissionControl::Jobs::Applicati
|
|
13
13
|
|
14
14
|
def redirect_location
|
15
15
|
status = @job.status.presence_in(supported_job_statuses) || :failed
|
16
|
-
application_jobs_url(@application, status)
|
16
|
+
application_jobs_url(@application, status, **jobs_filter_param)
|
17
17
|
end
|
18
18
|
end
|
@@ -3,7 +3,7 @@ class MissionControl::Jobs::RetriesController < MissionControl::Jobs::Applicatio
|
|
3
3
|
|
4
4
|
def create
|
5
5
|
@job.retry
|
6
|
-
redirect_to application_jobs_url(@application, :failed), notice: "Retried job with id #{@job.job_id}"
|
6
|
+
redirect_to application_jobs_url(@application, :failed, **jobs_filter_param), notice: "Retried job with id #{@job.job_id}"
|
7
7
|
end
|
8
8
|
|
9
9
|
private
|
@@ -37,14 +37,6 @@ module MissionControl::Jobs::NavigationHelper
|
|
37
37
|
MissionControl::Jobs::Current.server.name == server.name
|
38
38
|
end
|
39
39
|
|
40
|
-
def jobs_filter_param
|
41
|
-
if @job_filters&.any?
|
42
|
-
{ filter: @job_filters }
|
43
|
-
else
|
44
|
-
{}
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
40
|
def jobs_count_with_status(status)
|
49
41
|
count = ActiveJob.jobs.with_status(status).count
|
50
42
|
if count.infinite?
|
@@ -1,6 +1,9 @@
|
|
1
1
|
<nav class="navbar" role="navigation" aria-label="main navigation">
|
2
2
|
<div class="navbar-menu is-active mb-4">
|
3
3
|
<div class="navbar-start">
|
4
|
+
<% if defined?(main_app.root_path) %>
|
5
|
+
<%= link_to "Back to main app", main_app.root_path %>
|
6
|
+
<% end %>
|
4
7
|
</div>
|
5
8
|
|
6
9
|
<div class="navbar-end">
|
@@ -1,45 +1,55 @@
|
|
1
1
|
<div class="filter level-left">
|
2
|
-
|
3
|
-
|
4
|
-
<%= form_for :filter, url: application_jobs_path(MissionControl::Jobs::Current.application, jobs_status), method: :get,
|
5
|
-
data: { controller: "form", action: "input->form#debouncedSubmit" } do |form| %>
|
2
|
+
<%= form_for :filter, url: application_jobs_path(MissionControl::Jobs::Current.application, jobs_status), method: :get,
|
3
|
+
data: { controller: "form", action: "input->form#debouncedSubmit" } do |form| %>
|
6
4
|
|
5
|
+
<div class="field is-grouped">
|
6
|
+
<div class="control">
|
7
|
+
<%= form.label :job_class_name, class: "label" %>
|
7
8
|
<div class="select is-rounded">
|
8
9
|
<%= form.text_field :job_class_name, value: @job_filters[:job_class_name], class: "input", list: "job-classes", placeholder: "Filter by job class...", autocomplete: "off" %>
|
9
10
|
</div>
|
11
|
+
</div>
|
10
12
|
|
13
|
+
<div class="control">
|
14
|
+
<%= form.label :queue_name, class: "label" %>
|
11
15
|
<div class="select is-rounded">
|
12
16
|
<%= form.text_field :queue_name, value: @job_filters[:queue_name], class: "input", list: "queue-names", placeholder: "Filter by queue name...", autocomplete: "off" %>
|
13
17
|
</div>
|
18
|
+
</div>
|
14
19
|
|
15
|
-
|
20
|
+
<% if jobs_status == "finished" %>
|
21
|
+
<div class="control">
|
22
|
+
<%= form.label :finished_at_start, class: "label" %>
|
16
23
|
<div class="select is-rounded">
|
17
24
|
<%= form.datetime_field :finished_at_start, value: @job_filters[:finished_at]&.begin, class: "input", placeholder: "Finished from" %>
|
18
25
|
</div>
|
26
|
+
</div>
|
19
27
|
|
28
|
+
<div class="control">
|
29
|
+
<%= form.label :finished_at_end, class: "label" %>
|
20
30
|
<div class="select is-rounded">
|
21
31
|
<%= form.datetime_field :finished_at_end, value: @job_filters[:finished_at]&.end, class: "input", placeholder: "Finished to" %>
|
22
32
|
</div>
|
23
|
-
|
33
|
+
</div>
|
34
|
+
<% end %>
|
24
35
|
|
25
|
-
|
36
|
+
<%= hidden_field_tag :server_id, MissionControl::Jobs::Current.server.id %>
|
26
37
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
38
|
+
<datalist id="job-classes" class="is-hidden">
|
39
|
+
<% job_class_names.each do |job_class_name| %>
|
40
|
+
<option value="<%= job_class_name %>"></option>
|
41
|
+
<% end %>
|
42
|
+
</datalist>
|
32
43
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
<% end %>
|
39
|
-
</div>
|
44
|
+
<datalist id="queue-names" class="is-hidden">
|
45
|
+
<% queue_names.each do |queue_name| %>
|
46
|
+
<option value="<%= queue_name %>"></option>
|
47
|
+
<% end %>
|
48
|
+
</datalist>
|
40
49
|
|
41
|
-
|
42
|
-
|
50
|
+
<div class="control is-align-self-flex-end">
|
51
|
+
<%= link_to "Clear", application_jobs_path(MissionControl::Jobs::Current.application, jobs_status, job_class_name: nil, queue_name: nil, finished_at: nil..nil), class: "button" %>
|
52
|
+
</div>
|
43
53
|
</div>
|
44
|
-
|
54
|
+
<% end %>
|
45
55
|
</div>
|
@@ -47,11 +47,17 @@
|
|
47
47
|
<% end %>
|
48
48
|
<% if job.finished_at.present? %>
|
49
49
|
<tr>
|
50
|
-
<th>Finished
|
50
|
+
<th>Finished</th>
|
51
51
|
<td>
|
52
52
|
<%= time_distance_in_words_with_title(job.finished_at) %> ago
|
53
53
|
</td>
|
54
54
|
</tr>
|
55
|
+
<tr>
|
56
|
+
<th>Duration</th>
|
57
|
+
<td>
|
58
|
+
<%= job.duration.round(3) %> seconds
|
59
|
+
</td>
|
60
|
+
</tr>
|
55
61
|
<% end %>
|
56
62
|
<% if job.worker_id.present? %>
|
57
63
|
<tr>
|
@@ -1,6 +1,7 @@
|
|
1
1
|
<td><%= link_to job.queue_name, application_queue_path(@application, job.queue) %></td>
|
2
|
-
<td
|
3
|
-
|
2
|
+
<td>
|
3
|
+
<div class="is-family-monospace is-size-7"><%= job.blocked_by %></div>
|
4
|
+
<div class="has-text-grey is-size-7"><%= job.blocked_until ? "Expires #{bidirectional_time_distance_in_words_with_title(job.blocked_until)}" : "" %></div>
|
4
5
|
</td>
|
5
6
|
<td class="pr-0">
|
6
7
|
<%= render "mission_control/jobs/jobs/blocked/actions", job: job %>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<div class="buttons is-right">
|
2
|
-
<%= button_to "Discard", application_job_discard_path(@application, job.job_id), class: "button is-danger is-light mr-0",
|
2
|
+
<%= button_to "Discard", application_job_discard_path(@application, job.job_id, params: jobs_filter_param), class: "button is-danger is-light mr-0",
|
3
3
|
form: { data: { turbo_confirm: "This will delete the job and can't be undone. Are you sure?" } } %>
|
4
|
-
<%= button_to "Retry", application_job_retry_path(@application, job.job_id), class: "button is-warning is-light mr-0" %>
|
4
|
+
<%= button_to "Retry", application_job_retry_path(@application, job.job_id, params: jobs_filter_param), class: "button is-warning is-light mr-0" %>
|
5
5
|
</div>
|
data/lib/active_job/job_proxy.rb
CHANGED
@@ -24,6 +24,10 @@ class ActiveJob::JobProxy < ActiveJob::Base
|
|
24
24
|
raise UnsupportedError, "A JobProxy doesn't support immediate execution, only enqueuing."
|
25
25
|
end
|
26
26
|
|
27
|
+
def duration
|
28
|
+
finished_at - scheduled_at
|
29
|
+
end
|
30
|
+
|
27
31
|
ActiveJob::JobsRelation::STATUSES.each do |status|
|
28
32
|
define_method "#{status}?" do
|
29
33
|
self.status == status
|
data/lib/active_job/queues.rb
CHANGED
@@ -13,17 +13,21 @@ class ActiveJob::Queues
|
|
13
13
|
include Enumerable
|
14
14
|
|
15
15
|
delegate :each, to: :values
|
16
|
-
delegate :values, to: :
|
17
|
-
delegate :
|
16
|
+
delegate :values, to: :queues_by_id, private: true
|
17
|
+
delegate :size, :length, :to_s, :inspect, to: :queues_by_id
|
18
18
|
|
19
19
|
def initialize(queues)
|
20
|
-
@
|
20
|
+
@queues_by_id = queues.index_by(&:id).with_indifferent_access
|
21
21
|
end
|
22
22
|
|
23
23
|
def to_h
|
24
|
-
|
24
|
+
queues_by_id.dup
|
25
|
+
end
|
26
|
+
|
27
|
+
def [](name)
|
28
|
+
queues_by_id[name.to_s.parameterize]
|
25
29
|
end
|
26
30
|
|
27
31
|
private
|
28
|
-
attr_reader :
|
32
|
+
attr_reader :queues_by_id
|
29
33
|
end
|
@@ -35,8 +35,8 @@ module MissionControl
|
|
35
35
|
end
|
36
36
|
|
37
37
|
initializer "mission_control-jobs.http_basic_auth" do |app|
|
38
|
-
MissionControl::Jobs.http_basic_auth_user
|
39
|
-
MissionControl::Jobs.http_basic_auth_password
|
38
|
+
MissionControl::Jobs.http_basic_auth_user ||= app.credentials.dig(:mission_control, :http_basic_auth_user)
|
39
|
+
MissionControl::Jobs.http_basic_auth_password ||= app.credentials.dig(:mission_control, :http_basic_auth_password)
|
40
40
|
end
|
41
41
|
|
42
42
|
initializer "mission_control-jobs.active_job.extensions" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mission_control-jobs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jorge Manrubia
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-07-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|