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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: df0dc5c7b14924b9cb9063225ea71332d38e6550dded42c22e4d6698e9ed6b28
4
- data.tar.gz: 11fd491a0028f31c0c9975c560113580c06d79d3eb188e7968caccc1a84bf39d
3
+ metadata.gz: 2bba1e3be2d257eea8a3956ca3f6b8c9e6708f2fc81fe23290953ac66ecd79b0
4
+ data.tar.gz: c470df35a98c7f0f90163540dca2d3c8bd9ca14128ae283731c0e920fdae140e
5
5
  SHA512:
6
- metadata.gz: ca6d18a6d0ad2676ed21218df330d8fcea6a9daa106c780d36a0a148d951c939988120393bbc9a782fa5f0cd9179b0b6f14ecab09c1c7b7ca37e6fb17e1f3add
7
- data.tar.gz: 41c69feb7ce874eb91ca7c063463c98627832f95cdd20a6033846837c489c04f00031ddc1eaac44f6271a4ca939e47f6a3b00d921d20d040a09c615cb70b7817
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 "hey:solid_queue"
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
@@ -1,5 +1,5 @@
1
1
  class MissionControl::Jobs::JobsController < MissionControl::Jobs::ApplicationController
2
- include MissionControl::Jobs::JobScoped, MissionControl::Jobs::JobFilters
2
+ include MissionControl::Jobs::JobScoped
3
3
 
4
4
  skip_before_action :set_job, only: :index
5
5
 
@@ -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
- <div class="field is-grouped">
3
- <div class="control">
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
- <% if jobs_status == "finished" %>
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
- <% end %>
33
+ </div>
34
+ <% end %>
24
35
 
25
- <%= hidden_field_tag :server_id, MissionControl::Jobs::Current.server.id %>
36
+ <%= hidden_field_tag :server_id, MissionControl::Jobs::Current.server.id %>
26
37
 
27
- <datalist id="job-classes" class="is-hidden">
28
- <% job_class_names.each do |job_class_name| %>
29
- <option value="<%= job_class_name %>"></option>
30
- <% end %>
31
- </datalist>
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
- <datalist id="queue-names" class="is-hidden">
34
- <% queue_names.each do |queue_name| %>
35
- <option value="<%= queue_name %>"></option>
36
- <% end %>
37
- </datalist>
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
- <div class="control">
42
- <%= 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" %>
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
- </div>
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 at</th>
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><div class="is-family-monospace is-size-7"><%= job.blocked_by %></div>
3
- <div class="has-text-grey is-size-7">Expires <%= bidirectional_time_distance_in_words_with_title(job.blocked_until) %></div>
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>
@@ -1,6 +1,6 @@
1
1
  <tr class="queue">
2
2
  <td>
3
- <%= link_to queue.name, application_queue_path(@application, queue.name) %>
3
+ <%= link_to queue.name, application_queue_path(@application, queue) %>
4
4
  <% if queue.paused? %>
5
5
  <span class="ml-4 tag is-warning">Paused</span>
6
6
  <% end %>
@@ -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
@@ -13,17 +13,21 @@ class ActiveJob::Queues
13
13
  include Enumerable
14
14
 
15
15
  delegate :each, to: :values
16
- delegate :values, to: :queues_by_name, private: true
17
- delegate :[], :size, :length, :to_s, :inspect, to: :queues_by_name
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
- @queues_by_name = queues.index_by(&:name).with_indifferent_access
20
+ @queues_by_id = queues.index_by(&:id).with_indifferent_access
21
21
  end
22
22
 
23
23
  def to_h
24
- queues_by_name.dup
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 :queues_by_name
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 = 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)
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
@@ -1,5 +1,5 @@
1
1
  module MissionControl
2
2
  module Jobs
3
- VERSION = "1.0.2"
3
+ VERSION = "1.1.0"
4
4
  end
5
5
  end
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.2
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-02-10 00:00:00.000000000 Z
11
+ date: 2025-07-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord