mission_control-jobs 0.4.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -1
  3. data/app/assets/stylesheets/mission_control/jobs/bulma.min.css +3 -0
  4. data/app/assets/stylesheets/mission_control/jobs/jobs.css +1 -1
  5. data/app/controllers/mission_control/jobs/application_controller.rb +8 -0
  6. data/app/controllers/mission_control/jobs/discards_controller.rb +1 -1
  7. data/app/controllers/mission_control/jobs/dispatches_controller.rb +9 -4
  8. data/app/controllers/mission_control/jobs/jobs_controller.rb +0 -1
  9. data/app/controllers/mission_control/jobs/recurring_tasks_controller.rb +16 -1
  10. data/app/helpers/mission_control/jobs/jobs_helper.rb +10 -5
  11. data/app/models/mission_control/jobs/recurring_task.rb +8 -0
  12. data/app/views/layouts/mission_control/jobs/application.html.erb +2 -1
  13. data/app/views/mission_control/jobs/jobs/_general_information.html.erb +11 -0
  14. data/app/views/mission_control/jobs/jobs/blocked/_actions.html.erb +1 -1
  15. data/app/views/mission_control/jobs/jobs/blocked/_job.html.erb +3 -2
  16. data/app/views/mission_control/jobs/jobs/scheduled/_actions.html.erb +1 -0
  17. data/app/views/mission_control/jobs/recurring_tasks/_actions.html.erb +5 -0
  18. data/app/views/mission_control/jobs/recurring_tasks/_recurring_task.html.erb +3 -0
  19. data/app/views/mission_control/jobs/recurring_tasks/_title.html.erb +3 -0
  20. data/app/views/mission_control/jobs/recurring_tasks/index.html.erb +3 -2
  21. data/config/routes.rb +1 -1
  22. data/lib/active_job/executing.rb +1 -1
  23. data/lib/active_job/jobs_relation.rb +5 -0
  24. data/lib/active_job/queue_adapters/solid_queue_ext/recurring_tasks.rb +12 -0
  25. data/lib/active_job/queue_adapters/solid_queue_ext.rb +7 -3
  26. data/lib/mission_control/jobs/engine.rb +8 -0
  27. data/lib/mission_control/jobs/version.rb +1 -1
  28. metadata +7 -5
@@ -25,7 +25,7 @@ table.jobs {
25
25
  width: 45%;
26
26
  }
27
27
  &.scheduled th.job-header {
28
- width: 60%;
28
+ width: 40%;
29
29
  }
30
30
  &.finished th.job-header {
31
31
  width: 65%;
@@ -1,6 +1,14 @@
1
1
  class MissionControl::Jobs::ApplicationController < MissionControl::Jobs.base_controller_class.constantize
2
+ ActionController::Base::MODULES.each do |mod|
3
+ include mod unless self < mod
4
+ end
5
+
2
6
  layout "mission_control/jobs/application"
3
7
 
8
+ # Include helpers if not already included
9
+ helper MissionControl::Jobs::ApplicationHelper unless self < MissionControl::Jobs::ApplicationHelper
10
+ helper Importmap::ImportmapTagsHelper unless self < Importmap::ImportmapTagsHelper
11
+
4
12
  include MissionControl::Jobs::ApplicationScoped, MissionControl::Jobs::NotFoundRedirections
5
13
  include MissionControl::Jobs::AdapterFeatures
6
14
 
@@ -8,7 +8,7 @@ class MissionControl::Jobs::DiscardsController < MissionControl::Jobs::Applicati
8
8
 
9
9
  private
10
10
  def jobs_relation
11
- ActiveJob.jobs.failed
11
+ ActiveJob.jobs
12
12
  end
13
13
 
14
14
  def redirect_location
@@ -3,11 +3,16 @@ class MissionControl::Jobs::DispatchesController < MissionControl::Jobs::Applica
3
3
 
4
4
  def create
5
5
  @job.dispatch
6
- redirect_to application_jobs_url(@application, :blocked), notice: "Dispatched job with id #{@job.job_id}"
6
+ redirect_to redirect_location, notice: "Dispatched job with id #{@job.job_id}"
7
7
  end
8
8
 
9
9
  private
10
- def jobs_relation
11
- ApplicationJob.jobs.blocked
12
- end
10
+ def jobs_relation
11
+ ActiveJob.jobs
12
+ end
13
+
14
+ def redirect_location
15
+ status = @job.status.presence_in(supported_job_statuses) || :blocked
16
+ application_jobs_url(@application, status)
17
+ end
13
18
  end
@@ -15,7 +15,6 @@ class MissionControl::Jobs::JobsController < MissionControl::Jobs::ApplicationCo
15
15
  end
16
16
 
17
17
  private
18
-
19
18
  def jobs_relation
20
19
  filtered_jobs
21
20
  end
@@ -1,6 +1,7 @@
1
1
  class MissionControl::Jobs::RecurringTasksController < MissionControl::Jobs::ApplicationController
2
2
  before_action :ensure_supported_recurring_tasks
3
- before_action :set_recurring_task, only: :show
3
+ before_action :set_recurring_task, only: [ :show, :update ]
4
+ before_action :ensure_recurring_task_can_be_enqueued, only: :update
4
5
 
5
6
  def index
6
7
  @recurring_tasks = MissionControl::Jobs::Current.server.recurring_tasks
@@ -10,6 +11,14 @@ class MissionControl::Jobs::RecurringTasksController < MissionControl::Jobs::App
10
11
  @jobs_page = MissionControl::Jobs::Page.new(@recurring_task.jobs, page: params[:page].to_i)
11
12
  end
12
13
 
14
+ def update
15
+ if (job = @recurring_task.enqueue) && job.successfully_enqueued?
16
+ redirect_to application_job_path(@application, job.job_id), notice: "Enqueued recurring task #{@recurring_task.id}"
17
+ else
18
+ redirect_to application_recurring_task_path(@application, @recurring_task.id), alert: "Something went wrong enqueuing this recurring task"
19
+ end
20
+ end
21
+
13
22
  private
14
23
  def ensure_supported_recurring_tasks
15
24
  unless recurring_tasks_supported?
@@ -20,4 +29,10 @@ class MissionControl::Jobs::RecurringTasksController < MissionControl::Jobs::App
20
29
  def set_recurring_task
21
30
  @recurring_task = MissionControl::Jobs::Current.server.find_recurring_task(params[:id])
22
31
  end
32
+
33
+ def ensure_recurring_task_can_be_enqueued
34
+ unless @recurring_task.runnable?
35
+ redirect_to application_recurring_task_path(@application, @recurring_task.id), alert: "This task can't be enqueued"
36
+ end
37
+ end
23
38
  end
@@ -26,7 +26,7 @@ module MissionControl::Jobs::JobsHelper
26
26
  def attribute_names_for_job_status(status)
27
27
  case status.to_s
28
28
  when "failed" then [ "Error", "" ]
29
- when "blocked" then [ "Queue", "Blocked by", "Block expiry", "" ]
29
+ when "blocked" then [ "Queue", "Blocked by", "" ]
30
30
  when "finished" then [ "Queue", "Finished" ]
31
31
  when "scheduled" then [ "Queue", "Scheduled", "" ]
32
32
  when "in_progress" then [ "Queue", "Run by", "Running since" ]
@@ -39,7 +39,6 @@ module MissionControl::Jobs::JobsHelper
39
39
  end
40
40
 
41
41
  private
42
-
43
42
  def renderable_job_arguments_for(job)
44
43
  job.serialized_arguments.collect do |argument|
45
44
  as_renderable_argument(argument)
@@ -53,7 +52,7 @@ module MissionControl::Jobs::JobsHelper
53
52
  when Array
54
53
  as_renderable_array(argument)
55
54
  else
56
- ActiveJob::Arguments.deserialize([ argument ])
55
+ ActiveJob::Arguments.deserialize([ argument ]).first
57
56
  end
58
57
  rescue ActiveJob::DeserializationError
59
58
  argument.to_s
@@ -65,12 +64,18 @@ module MissionControl::Jobs::JobsHelper
65
64
  argument["_aj_globalid"]
66
65
  elsif argument["_aj_serialized"] == "ActiveJob::Serializers::ModuleSerializer"
67
66
  argument["value"]
67
+ elsif argument["_aj_serialized"]
68
+ ActiveJob::Arguments.deserialize([ argument ]).first
68
69
  else
69
- ActiveJob::Arguments.deserialize([ argument ])
70
+ argument.without("_aj_symbol_keys", "_aj_ruby2_keywords")
71
+ .transform_values { |v| as_renderable_argument(v) }
72
+ .map { |k, v| "#{k}: #{v}" }
73
+ .join(", ")
74
+ .then { |s| "{#{s}}" }
70
75
  end
71
76
  end
72
77
 
73
78
  def as_renderable_array(argument)
74
- "(#{argument.collect { |part| as_renderable_argument(part) }.join(", ")})"
79
+ "[#{argument.collect { |part| as_renderable_argument(part) }.join(", ")}]"
75
80
  end
76
81
  end
@@ -12,6 +12,14 @@ class MissionControl::Jobs::RecurringTask
12
12
  ActiveJob::JobsRelation.new(queue_adapter: queue_adapter).where(recurring_task_id: id)
13
13
  end
14
14
 
15
+ def enqueue
16
+ queue_adapter.enqueue_recurring_task(id)
17
+ end
18
+
19
+ def runnable?
20
+ queue_adapter.can_enqueue_recurring_task?(id)
21
+ end
22
+
15
23
  private
16
24
  attr_reader :queue_adapter
17
25
  end
@@ -7,7 +7,8 @@
7
7
 
8
8
  <meta name="viewport" content="width=device-width,initial-scale=1">
9
9
  <meta name="turbo-cache-control" content="no-cache">
10
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.1/css/bulma.min.css">
10
+
11
+ <%= stylesheet_link_tag "mission_control/jobs/bulma.min" %>
11
12
  <%= stylesheet_link_tag "mission_control/jobs/application", "data-turbo-track": "reload" %>
12
13
  <%= javascript_importmap_tags "application", importmap: MissionControl::Jobs.importmap %>
13
14
  </head>
@@ -26,6 +26,17 @@
26
26
  <%= formatted_time(job.enqueued_at.to_datetime) %>
27
27
  </td>
28
28
  </tr>
29
+ <% if job.scheduled? %>
30
+ <tr>
31
+ <th>Scheduled</th>
32
+ <td>
33
+ <%= formatted_time(job.scheduled_at) %>
34
+ <% if job_delayed?(job) %>
35
+ <div class="is-danger tag ml-4">delayed</div>
36
+ <% end %>
37
+ </td>
38
+ </tr>
39
+ <% end %>
29
40
  <% if job.failed? %>
30
41
  <tr>
31
42
  <th>Failed</th>
@@ -1,3 +1,3 @@
1
1
  <div class="buttons is-right">
2
- <%= button_to "Dispatch", application_job_dispatch_path(@application, job.job_id), class: "button is-warning is-light mr-0" %>
2
+ <%= button_to "Run now", application_job_dispatch_path(@application, job.job_id), class: "button is-warning is-light mr-0" %>
3
3
  </div>
@@ -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></td>
3
- <td><%= formatted_time(job.blocked_until) %></td>
2
+ <td><div class="is-family-monospace is-size-7"><%= job.blocked_by %></div>
3
+ <div class="has-text-grey is-size-7">Until <%= formatted_time(job.blocked_until) %></div>
4
+ </td>
4
5
  <td class="pr-0">
5
6
  <%= render "mission_control/jobs/jobs/blocked/actions", job: job %>
6
7
  </td>
@@ -1,4 +1,5 @@
1
1
  <div class="buttons is-right">
2
+ <%= button_to "Run now", application_job_dispatch_path(@application, job.job_id), class: "button is-warning is-light mr-0" %>
2
3
  <%= button_to "Discard", application_job_discard_path(@application, job.job_id), class: "button is-danger is-light mr-0",
3
4
  form: { data: { turbo_confirm: "This will delete the job and can't be undone. Are you sure?" } } %>
4
5
  </div>
@@ -0,0 +1,5 @@
1
+ <div class="buttons is-right">
2
+ <% if recurring_task.runnable? %>
3
+ <%= button_to "Run now", application_recurring_task_path(@application, recurring_task.id), class: "button is-warning is-light mr-0", method: :put %>
4
+ <% end %>
5
+ </div>
@@ -16,4 +16,7 @@
16
16
  <td> <%= recurring_task.schedule %> </td>
17
17
  <td><div class="has-text-grey"><%= recurring_task.last_enqueued_at ? formatted_time(recurring_task.last_enqueued_at) : "Never" %></div></td>
18
18
  <td class="next_time"><div class="has-text-grey"><%= formatted_time(recurring_task.next_time) %></div></td>
19
+ <td class="pr-0">
20
+ <%= render "mission_control/jobs/recurring_tasks/actions", recurring_task: recurring_task %>
21
+ </td>
19
22
  </tr>
@@ -3,5 +3,8 @@
3
3
  <div class="level-left">
4
4
  <%= recurring_task.id %>
5
5
  </div>
6
+ <div class="level-right">
7
+ <%= render "mission_control/jobs/recurring_tasks/actions", recurring_task: recurring_task %>
8
+ </div>
6
9
  </div>
7
10
  </h1>
@@ -7,8 +7,9 @@
7
7
  <th></th>
8
8
  <th>Job</th>
9
9
  <th>Schedule</th>
10
- <th>Last enqueued at</th>
11
- <th>Next run</th>
10
+ <th>Last enqueued</th>
11
+ <th>Next</th>
12
+ <th></th>
12
13
  </tr>
13
14
  </thead>
14
15
 
data/config/routes.rb CHANGED
@@ -20,7 +20,7 @@ MissionControl::Jobs::Engine.routes.draw do
20
20
  resources :jobs, only: :index, path: ":status/jobs"
21
21
 
22
22
  resources :workers, only: [ :index, :show ]
23
- resources :recurring_tasks, only: [ :index, :show ]
23
+ resources :recurring_tasks, only: [ :index, :show, :update ]
24
24
  end
25
25
 
26
26
  # Allow referencing urls without providing an application_id. It will default to the first one.
@@ -25,7 +25,7 @@ module ActiveJob::Executing
25
25
  end
26
26
 
27
27
  def dispatch
28
- ActiveJob.jobs.blocked.dispatch_job(self)
28
+ ActiveJob.jobs.dispatch_job(self)
29
29
  end
30
30
 
31
31
  private
@@ -154,7 +154,12 @@ class ActiveJob::JobsRelation
154
154
  end
155
155
 
156
156
  # Dispatch the provided job.
157
+ #
158
+ # This operation is only valid for blocked or scheduled jobs. It will
159
+ # raise an error +ActiveJob::Errors::InvalidOperation+ otherwise.
157
160
  def dispatch_job(job)
161
+ raise ActiveJob::Errors::InvalidOperation, "This operation can only be performed on blocked or scheduled jobs, but this job is #{job.status}" unless job.blocked? || job.scheduled?
162
+
158
163
  queue_adapter.dispatch_job(job, self)
159
164
  end
160
165
 
@@ -20,6 +20,18 @@ module ActiveJob::QueueAdapters::SolidQueueExt::RecurringTasks
20
20
  end
21
21
  end
22
22
 
23
+ def enqueue_recurring_task(task_id)
24
+ if task = SolidQueue::RecurringTask.find_by(key: task_id)
25
+ task.enqueue(at: Time.now)
26
+ end
27
+ end
28
+
29
+ def can_enqueue_recurring_task?(task_id)
30
+ if task = SolidQueue::RecurringTask.find_by(key: task_id)
31
+ task.valid?
32
+ end
33
+ end
34
+
23
35
  private
24
36
  def recurring_task_attributes_from_solid_queue_recurring_task(task)
25
37
  {
@@ -123,9 +123,13 @@ module ActiveJob::QueueAdapters::SolidQueueExt
123
123
  end
124
124
 
125
125
  def dispatch_immediately(job)
126
- SolidQueue::Job.transaction do
127
- job.dispatch_bypassing_concurrency_limits
128
- job.blocked_execution.destroy!
126
+ if job.blocked?
127
+ SolidQueue::Job.transaction do
128
+ job.dispatch_bypassing_concurrency_limits
129
+ job.blocked_execution.destroy!
130
+ end
131
+ else
132
+ job.scheduled_execution.update!(scheduled_at: Time.now)
129
133
  end
130
134
  end
131
135
 
@@ -7,6 +7,13 @@ module MissionControl
7
7
  class Engine < ::Rails::Engine
8
8
  isolate_namespace MissionControl::Jobs
9
9
 
10
+ initializer "mission_control-jobs.middleware" do |app|
11
+ if app.config.api_only
12
+ app.middleware.use ActionDispatch::Flash
13
+ app.middleware.use ::Rack::MethodOverride
14
+ end
15
+ end
16
+
10
17
  config.mission_control = ActiveSupport::OrderedOptions.new unless config.try(:mission_control)
11
18
  config.mission_control.jobs = ActiveSupport::OrderedOptions.new
12
19
 
@@ -80,6 +87,7 @@ module MissionControl
80
87
  end
81
88
 
82
89
  initializer "mission_control-jobs.assets" do |app|
90
+ app.config.assets.paths << root.join("app/assets/stylesheets")
83
91
  app.config.assets.paths << root.join("app/javascript")
84
92
  app.config.assets.precompile += %w[ mission_control_jobs_manifest ]
85
93
  end
@@ -1,5 +1,5 @@
1
1
  module MissionControl
2
2
  module Jobs
3
- VERSION = "0.4.0"
3
+ VERSION = "0.6.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: 0.4.0
4
+ version: 0.6.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: 2024-11-06 00:00:00.000000000 Z
11
+ date: 2024-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -156,14 +156,14 @@ dependencies:
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: '1.0'
159
+ version: 1.0.1
160
160
  type: :development
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: '1.0'
166
+ version: 1.0.1
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: selenium-webdriver
169
169
  requirement: !ruby/object:Gem::Requirement
@@ -305,7 +305,7 @@ dependencies:
305
305
  - !ruby/object:Gem::Version
306
306
  version: '0'
307
307
  - !ruby/object:Gem::Dependency
308
- name: sprockets-rails
308
+ name: propshaft
309
309
  requirement: !ruby/object:Gem::Requirement
310
310
  requirements:
311
311
  - - ">="
@@ -358,6 +358,7 @@ files:
358
358
  - Rakefile
359
359
  - app/assets/config/mission_control_jobs_manifest.js
360
360
  - app/assets/stylesheets/mission_control/jobs/application.css
361
+ - app/assets/stylesheets/mission_control/jobs/bulma.min.css
361
362
  - app/assets/stylesheets/mission_control/jobs/forms.css
362
363
  - app/assets/stylesheets/mission_control/jobs/jobs.css
363
364
  - app/controllers/concerns/mission_control/jobs/adapter_features.rb
@@ -425,6 +426,7 @@ files:
425
426
  - app/views/mission_control/jobs/queues/_queue_title.html.erb
426
427
  - app/views/mission_control/jobs/queues/index.html.erb
427
428
  - app/views/mission_control/jobs/queues/show.html.erb
429
+ - app/views/mission_control/jobs/recurring_tasks/_actions.html.erb
428
430
  - app/views/mission_control/jobs/recurring_tasks/_general_information.html.erb
429
431
  - app/views/mission_control/jobs/recurring_tasks/_recurring_task.html.erb
430
432
  - app/views/mission_control/jobs/recurring_tasks/_title.html.erb