mission_control-jobs 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +244 -0
- data/Rakefile +8 -0
- data/app/assets/config/mission_control_jobs_manifest.js +4 -0
- data/app/assets/stylesheets/mission_control/jobs/application.css +16 -0
- data/app/assets/stylesheets/mission_control/jobs/forms.css +8 -0
- data/app/assets/stylesheets/mission_control/jobs/jobs.css +7 -0
- data/app/controllers/concerns/mission_control/jobs/adapter_features.rb +20 -0
- data/app/controllers/concerns/mission_control/jobs/application_scoped.rb +38 -0
- data/app/controllers/concerns/mission_control/jobs/failed_jobs_bulk_operations.rb +17 -0
- data/app/controllers/concerns/mission_control/jobs/job_filters.rb +18 -0
- data/app/controllers/concerns/mission_control/jobs/job_scoped.rb +16 -0
- data/app/controllers/concerns/mission_control/jobs/not_found_redirections.rb +25 -0
- data/app/controllers/concerns/mission_control/jobs/queue_scoped.rb +12 -0
- data/app/controllers/mission_control/jobs/application_controller.rb +11 -0
- data/app/controllers/mission_control/jobs/bulk_discards_controller.rb +20 -0
- data/app/controllers/mission_control/jobs/bulk_retries_controller.rb +10 -0
- data/app/controllers/mission_control/jobs/discards_controller.rb +13 -0
- data/app/controllers/mission_control/jobs/jobs_controller.rb +37 -0
- data/app/controllers/mission_control/jobs/queues/pauses_controller.rb +15 -0
- data/app/controllers/mission_control/jobs/queues_controller.rb +24 -0
- data/app/controllers/mission_control/jobs/retries_controller.rb +13 -0
- data/app/controllers/mission_control/jobs/workers_controller.rb +18 -0
- data/app/helpers/mission_control/jobs/application_helper.rb +8 -0
- data/app/helpers/mission_control/jobs/dates_helper.rb +19 -0
- data/app/helpers/mission_control/jobs/jobs_helper.rb +63 -0
- data/app/helpers/mission_control/jobs/navigation_helper.rb +51 -0
- data/app/helpers/mission_control/jobs/ui_helper.rb +23 -0
- data/app/javascript/mission_control/jobs/application.js +4 -0
- data/app/javascript/mission_control/jobs/controllers/application.js +9 -0
- data/app/javascript/mission_control/jobs/controllers/form_controller.js +21 -0
- data/app/javascript/mission_control/jobs/controllers/index.js +11 -0
- data/app/javascript/mission_control/jobs/helpers/debounce_helpers.js +9 -0
- data/app/javascript/mission_control/jobs/helpers/index.js +1 -0
- data/app/jobs/mission_control/jobs/application_job.rb +6 -0
- data/app/mailers/mission_control/jobs/application_mailer.rb +8 -0
- data/app/models/mission_control/jobs/application_record.rb +7 -0
- data/app/models/mission_control/jobs/current.rb +3 -0
- data/app/models/mission_control/jobs/page.rb +48 -0
- data/app/models/mission_control/jobs/worker.rb +17 -0
- data/app/views/layouts/mission_control/jobs/_application_selection.html.erb +11 -0
- data/app/views/layouts/mission_control/jobs/_flash.html.erb +9 -0
- data/app/views/layouts/mission_control/jobs/_navigation.html.erb +9 -0
- data/app/views/layouts/mission_control/jobs/application.html.erb +25 -0
- data/app/views/layouts/mission_control/jobs/application_selection/_applications.html.erb +13 -0
- data/app/views/layouts/mission_control/jobs/application_selection/_servers.html.erb +15 -0
- data/app/views/mission_control/jobs/jobs/_error_information.html.erb +19 -0
- data/app/views/mission_control/jobs/jobs/_filters.html.erb +35 -0
- data/app/views/mission_control/jobs/jobs/_general_information.html.erb +54 -0
- data/app/views/mission_control/jobs/jobs/_job.html.erb +13 -0
- data/app/views/mission_control/jobs/jobs/_jobs_page.html.erb +15 -0
- data/app/views/mission_control/jobs/jobs/_raw_data.html.erb +4 -0
- data/app/views/mission_control/jobs/jobs/_title.html.erb +13 -0
- data/app/views/mission_control/jobs/jobs/_toolbar.html.erb +18 -0
- data/app/views/mission_control/jobs/jobs/blocked/_job.html.erb +3 -0
- data/app/views/mission_control/jobs/jobs/failed/_actions.html.erb +5 -0
- data/app/views/mission_control/jobs/jobs/failed/_job.html.erb +7 -0
- data/app/views/mission_control/jobs/jobs/finished/_job.html.erb +2 -0
- data/app/views/mission_control/jobs/jobs/in_progress/_job.html.erb +9 -0
- data/app/views/mission_control/jobs/jobs/index.html.erb +19 -0
- data/app/views/mission_control/jobs/jobs/scheduled/_job.html.erb +7 -0
- data/app/views/mission_control/jobs/jobs/show.html.erb +6 -0
- data/app/views/mission_control/jobs/queues/_actions.html.erb +7 -0
- data/app/views/mission_control/jobs/queues/_job.html.erb +15 -0
- data/app/views/mission_control/jobs/queues/_queue.html.erb +16 -0
- data/app/views/mission_control/jobs/queues/_queue_title.html.erb +17 -0
- data/app/views/mission_control/jobs/queues/index.html.erb +16 -0
- data/app/views/mission_control/jobs/queues/show.html.erb +25 -0
- data/app/views/mission_control/jobs/shared/_pagination_toolbar.html.erb +5 -0
- data/app/views/mission_control/jobs/workers/_configuration.html.erb +6 -0
- data/app/views/mission_control/jobs/workers/_job.html.erb +19 -0
- data/app/views/mission_control/jobs/workers/_jobs.html.erb +20 -0
- data/app/views/mission_control/jobs/workers/_raw_data.html.erb +6 -0
- data/app/views/mission_control/jobs/workers/_title.html.erb +11 -0
- data/app/views/mission_control/jobs/workers/_worker.html.erb +21 -0
- data/app/views/mission_control/jobs/workers/index.html.erb +17 -0
- data/app/views/mission_control/jobs/workers/show.html.erb +7 -0
- data/config/importmap.rb +6 -0
- data/config/routes.rb +33 -0
- data/lib/active_job/errors/invalid_operation.rb +5 -0
- data/lib/active_job/errors/job_not_found_error.rb +14 -0
- data/lib/active_job/errors/query_error.rb +5 -0
- data/lib/active_job/executing.rb +43 -0
- data/lib/active_job/execution_error.rb +8 -0
- data/lib/active_job/failed.rb +11 -0
- data/lib/active_job/job_proxy.rb +26 -0
- data/lib/active_job/jobs_relation.rb +300 -0
- data/lib/active_job/querying.rb +44 -0
- data/lib/active_job/queue.rb +62 -0
- data/lib/active_job/queue_adapters/resque_ext.rb +300 -0
- data/lib/active_job/queue_adapters/solid_queue_ext.rb +294 -0
- data/lib/active_job/queues.rb +29 -0
- data/lib/mission_control/jobs/adapter.rb +108 -0
- data/lib/mission_control/jobs/application.rb +17 -0
- data/lib/mission_control/jobs/applications.rb +8 -0
- data/lib/mission_control/jobs/console/context.rb +11 -0
- data/lib/mission_control/jobs/console/helpers.rb +26 -0
- data/lib/mission_control/jobs/engine.rb +88 -0
- data/lib/mission_control/jobs/errors/incompatible_adapter.rb +2 -0
- data/lib/mission_control/jobs/errors/resource_not_found.rb +2 -0
- data/lib/mission_control/jobs/identified_by_name.rb +18 -0
- data/lib/mission_control/jobs/identified_elements.rb +23 -0
- data/lib/mission_control/jobs/server/serializable.rb +24 -0
- data/lib/mission_control/jobs/server/workers.rb +15 -0
- data/lib/mission_control/jobs/server.rb +26 -0
- data/lib/mission_control/jobs/version.rb +5 -0
- data/lib/mission_control/jobs.rb +19 -0
- data/lib/resque/thread_safe_redis.rb +34 -0
- data/lib/tasks/mission_control/jobs_tasks.rake +4 -0
- metadata +364 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
module MissionControl::Jobs::DatesHelper
|
2
|
+
def time_ago_in_words_with_title(time)
|
3
|
+
tag.span time_ago_in_words(time), title: time.to_fs(:long)
|
4
|
+
end
|
5
|
+
|
6
|
+
def time_distance_in_words_with_title(time)
|
7
|
+
tag.span distance_of_time_in_words_to_now(time, include_seconds: true), title: "Since #{time.to_fs(:long)}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def bidirectional_time_distance_in_words_with_title(time)
|
11
|
+
time_distance = if time.past?
|
12
|
+
"#{distance_of_time_in_words_to_now(time, include_seconds: true)} ago"
|
13
|
+
else
|
14
|
+
"in #{distance_of_time_in_words_to_now(time, include_seconds: true)}"
|
15
|
+
end
|
16
|
+
|
17
|
+
tag.span time_distance, title: time.to_fs(:long)
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module MissionControl::Jobs::JobsHelper
|
2
|
+
def job_title(job)
|
3
|
+
job.job_class_name
|
4
|
+
end
|
5
|
+
|
6
|
+
def job_arguments(job)
|
7
|
+
renderable_job_arguments_for(job).join(", ")
|
8
|
+
end
|
9
|
+
|
10
|
+
def failed_job_error(job)
|
11
|
+
"#{job.last_execution_error.error_class}: #{job.last_execution_error.message}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def failed_job_backtrace(job)
|
15
|
+
job.last_execution_error.backtrace.join("\n")
|
16
|
+
end
|
17
|
+
|
18
|
+
def attribute_names_for_job_status(status)
|
19
|
+
case status.to_s
|
20
|
+
when "failed" then [ "Error", "" ]
|
21
|
+
when "blocked" then [ "Queue", "Blocked by", "Block expiry" ]
|
22
|
+
when "finished" then [ "Queue", "Finished" ]
|
23
|
+
when "scheduled" then [ "Queue", "Scheduled" ]
|
24
|
+
when "in_progress" then [ "Queue", "Run by", "Running for" ]
|
25
|
+
else []
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def renderable_job_arguments_for(job)
|
31
|
+
job.serialized_arguments.collect do |argument|
|
32
|
+
as_renderable_argument(argument)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def as_renderable_argument(argument)
|
37
|
+
case argument
|
38
|
+
when Hash
|
39
|
+
as_renderable_hash(argument)
|
40
|
+
when Array
|
41
|
+
as_renderable_array(argument)
|
42
|
+
else
|
43
|
+
ActiveJob::Arguments.deserialize([ argument ])
|
44
|
+
end
|
45
|
+
rescue ActiveJob::DeserializationError
|
46
|
+
argument.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
def as_renderable_hash(argument)
|
50
|
+
if argument["_aj_globalid"]
|
51
|
+
# don't deserialize as the class might not exist in the host app running the engine
|
52
|
+
argument["_aj_globalid"]
|
53
|
+
elsif argument["_aj_serialized"] == "ActiveJob::Serializers::ModuleSerializer"
|
54
|
+
argument["value"]
|
55
|
+
else
|
56
|
+
ActiveJob::Arguments.deserialize([ argument ])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def as_renderable_array(argument)
|
61
|
+
"(#{argument.collect { |part| as_renderable_argument(part) }.join(", ")})"
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module MissionControl::Jobs::NavigationHelper
|
2
|
+
attr_reader :page_title, :current_section
|
3
|
+
|
4
|
+
def navigation_sections
|
5
|
+
{ queues: [ "Queues", application_queues_path(@application) ] }.tap do |sections|
|
6
|
+
supported_job_statuses.without(:pending).each do |status|
|
7
|
+
sections[navigation_section_for_status(status)] = [ "#{status.to_s.titleize} jobs (#{jobs_count_with_status(status)})", application_jobs_path(@application, status) ]
|
8
|
+
end
|
9
|
+
|
10
|
+
sections[:workers] = [ "Workers", application_workers_path(@application) ] if workers_exposed?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def navigation_section_for_status(status)
|
15
|
+
if status.nil? || status == :pending
|
16
|
+
:queues
|
17
|
+
else
|
18
|
+
"#{status}_jobs".to_sym
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def navigation(title: nil, section: nil)
|
23
|
+
@page_title = title
|
24
|
+
@current_section = section
|
25
|
+
end
|
26
|
+
|
27
|
+
def selected_application?(application)
|
28
|
+
MissionControl::Jobs::Current.application.name == application.name
|
29
|
+
end
|
30
|
+
|
31
|
+
def selectable_applications
|
32
|
+
MissionControl::Jobs.applications.reject { |app| selected_application?(app) }
|
33
|
+
end
|
34
|
+
|
35
|
+
def selected_server?(server)
|
36
|
+
MissionControl::Jobs::Current.server.name == server.name
|
37
|
+
end
|
38
|
+
|
39
|
+
def jobs_filter_param
|
40
|
+
if @job_filters&.any?
|
41
|
+
{ filter: @job_filters }
|
42
|
+
else
|
43
|
+
{}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def jobs_count_with_status(status)
|
48
|
+
count = ApplicationJob.jobs.with_status(status).count
|
49
|
+
count.infinite? ? "..." : number_to_human(count)
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module MissionControl::Jobs::UiHelper
|
2
|
+
def blank_status_notice(message)
|
3
|
+
tag.div message, class: "mt-6 has-text-centered is-size-3 has-text-grey"
|
4
|
+
end
|
5
|
+
|
6
|
+
def blank_status_emoji(status)
|
7
|
+
case status.to_s
|
8
|
+
when "failed", "blocked" then "😌"
|
9
|
+
else ""
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def modifier_for_status(status)
|
14
|
+
case status.to_s
|
15
|
+
when "failed" then "is-danger"
|
16
|
+
when "blocked" then "is-warning"
|
17
|
+
when "finished" then "is-success"
|
18
|
+
when "scheduled" then "is-info"
|
19
|
+
when "in_progress" then "is-primary"
|
20
|
+
else "is-primary is-light"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import { debounce } from "helpers"
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static values = {
|
6
|
+
debounceTimeout: { type: Number, default: 300 }
|
7
|
+
}
|
8
|
+
|
9
|
+
initialize() {
|
10
|
+
this.debouncedSubmit = debounce(this.debouncedSubmit.bind(this), this.debounceTimeoutValue)
|
11
|
+
}
|
12
|
+
|
13
|
+
submit(event) {
|
14
|
+
const form = event.target.form || event.target.closest("form")
|
15
|
+
if (form) form.requestSubmit()
|
16
|
+
}
|
17
|
+
|
18
|
+
debouncedSubmit(event) {
|
19
|
+
this.submit(event)
|
20
|
+
}
|
21
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
// Import and register all your controllers from the importmap under controllers/*
|
2
|
+
|
3
|
+
import { application } from "controllers/application"
|
4
|
+
|
5
|
+
// Eager load all controllers defined in the import map under controllers/**/*_controller
|
6
|
+
import { eagerLoadControllersFrom } from "@hotwired/stimulus-loading"
|
7
|
+
eagerLoadControllersFrom("controllers", application)
|
8
|
+
|
9
|
+
// Lazy load controllers as they appear in the DOM (remember not to preload controllers in import map!)
|
10
|
+
// import { lazyLoadControllersFrom } from "@hotwired/stimulus-loading"
|
11
|
+
// lazyLoadControllersFrom("controllers", application)
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from "helpers/debounce_helpers"
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class MissionControl::Jobs::Page
|
2
|
+
DEFAULT_PAGE_SIZE = 10
|
3
|
+
|
4
|
+
attr_reader :jobs_relation, :index, :page_size
|
5
|
+
|
6
|
+
def initialize(jobs_relation, page: 1, page_size: DEFAULT_PAGE_SIZE)
|
7
|
+
@jobs_relation = jobs_relation
|
8
|
+
@page_size = page_size
|
9
|
+
@index = [ page, 1 ].max
|
10
|
+
end
|
11
|
+
|
12
|
+
def jobs
|
13
|
+
jobs_relation.limit(page_size).offset(offset)
|
14
|
+
end
|
15
|
+
|
16
|
+
def first?
|
17
|
+
index == 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def last?
|
21
|
+
index == pages_count || empty? || jobs.empty?
|
22
|
+
end
|
23
|
+
|
24
|
+
def empty?
|
25
|
+
total_count == 0
|
26
|
+
end
|
27
|
+
|
28
|
+
def previous_index
|
29
|
+
[ index - 1, 1 ].max
|
30
|
+
end
|
31
|
+
|
32
|
+
def next_index
|
33
|
+
pages_count ? [ index + 1, pages_count ].min : index + 1
|
34
|
+
end
|
35
|
+
|
36
|
+
def pages_count
|
37
|
+
(total_count.to_f / 10).ceil unless total_count.infinite?
|
38
|
+
end
|
39
|
+
|
40
|
+
def total_count
|
41
|
+
@total_count ||= jobs_relation.count # Potentially expensive when filtering and a lot of jobs, with adapter in charge of doing the filtering in memory
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def offset
|
46
|
+
(index - 1) * page_size
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class MissionControl::Jobs::Worker
|
2
|
+
include ActiveModel::Model
|
3
|
+
|
4
|
+
attr_accessor :id, :name, :hostname, :last_heartbeat_at, :configuration, :raw_data
|
5
|
+
|
6
|
+
def initialize(queue_adapter: ActiveJob::Base.queue_adapter, **kwargs)
|
7
|
+
@queue_adapter = queue_adapter
|
8
|
+
super(**kwargs)
|
9
|
+
end
|
10
|
+
|
11
|
+
def jobs
|
12
|
+
@jobs ||= ActiveJob::JobsRelation.new(queue_adapter: queue_adapter).in_progress.where(worker_id: id)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
attr_reader :queue_adapter
|
17
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<nav class="navbar" role="navigation" aria-label="main navigation">
|
2
|
+
<div class="navbar-menu is-active">
|
3
|
+
<div class="navbar-start">
|
4
|
+
</div>
|
5
|
+
|
6
|
+
<div class="navbar-end">
|
7
|
+
<%= render "layouts/mission_control/jobs/application_selection/servers", application: @application %>
|
8
|
+
<%= render "layouts/mission_control/jobs/application_selection/applications" if selectable_applications.any? %>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
</nav>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Mission control - <%= page_title %></title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
9
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css">
|
10
|
+
<%= stylesheet_link_tag "mission_control/jobs/application", "data-turbo-track": "reload" %>
|
11
|
+
<%= javascript_importmap_tags "application-mcj" %>
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
|
15
|
+
<section class="section">
|
16
|
+
<div class="container">
|
17
|
+
<%= render "layouts/mission_control/jobs/application_selection" %>
|
18
|
+
<%= render "layouts/mission_control/jobs/flash" %>
|
19
|
+
<%= render "layouts/mission_control/jobs/navigation" %>
|
20
|
+
<%= yield %>
|
21
|
+
</div>
|
22
|
+
</section>
|
23
|
+
|
24
|
+
</body>
|
25
|
+
</html>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<div class="application-selector navbar-item has-dropdown is-hoverable">
|
2
|
+
<a class="navbar-link">
|
3
|
+
<%= MissionControl::Jobs::Current.application.name %>
|
4
|
+
</a>
|
5
|
+
|
6
|
+
<div class="navbar-dropdown">
|
7
|
+
<% selectable_applications.each do |application| %>
|
8
|
+
<%= link_to application_queues_path(application, server_id: nil), class: "navbar-item" do %>
|
9
|
+
<span><%= application.name %></span>
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
12
|
+
</div>
|
13
|
+
</div>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<% if application.servers.many? %>
|
2
|
+
<div class="server-selector navbar-item">
|
3
|
+
<div class="tabs is-toggle is-toggle-rounded is-small">
|
4
|
+
<ul>
|
5
|
+
<% application.servers.each do |server| %>
|
6
|
+
<li class="<%= "is-active" if selected_server?(server) %>">
|
7
|
+
<%= link_to application_queues_path(application, server_id: server) do %>
|
8
|
+
<span><%= server.name %></span>
|
9
|
+
<% end %>
|
10
|
+
</li>
|
11
|
+
<% end %>
|
12
|
+
</ul>
|
13
|
+
</div>
|
14
|
+
</div>
|
15
|
+
<% end %>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<% if job.failed? %>
|
2
|
+
<a id="error"></a>
|
3
|
+
<h2 class="subtitle">Error information</h2>
|
4
|
+
|
5
|
+
<table class="table">
|
6
|
+
<tbody>
|
7
|
+
<tr>
|
8
|
+
<th>Type</th>
|
9
|
+
<td> <%= job.last_execution_error.error_class %> </td>
|
10
|
+
</tr>
|
11
|
+
<tr>
|
12
|
+
<th>Message</th>
|
13
|
+
<td> <%= job.last_execution_error.try(:message) || job.last_execution_error.inspect %> </td>
|
14
|
+
</tr>
|
15
|
+
</tbody>
|
16
|
+
</table>
|
17
|
+
|
18
|
+
<pre class="is-family-monospace mb-4"><%= failed_job_backtrace(job) %></pre>
|
19
|
+
<% end %>
|
@@ -0,0 +1,35 @@
|
|
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| %>
|
6
|
+
|
7
|
+
<div class="select is-rounded">
|
8
|
+
<%= form.text_field :job_class_name, value: @job_filters[:job_class_name], class: "input", list: "job-classes", placeholder: "Filter by job class..." %>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<div class="select is-rounded">
|
12
|
+
<%= form.text_field :queue_name, value: @job_filters[:queue_name], class: "input", list: "queue-names", placeholder: "Filter by queue name..." %>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<%= hidden_field_tag :server_id, MissionControl::Jobs::Current.server.id %>
|
16
|
+
|
17
|
+
<datalist id="job-classes" style="display: none;">
|
18
|
+
<% job_class_names.each do |job_class_name| %>
|
19
|
+
<option value="<%= job_class_name %>" />
|
20
|
+
<% end %>
|
21
|
+
</datalist>
|
22
|
+
|
23
|
+
<datalist id="queue-names" style="display: none;">
|
24
|
+
<% queue_names.each do |queue_name| %>
|
25
|
+
<option value="<%= queue_name %>" />
|
26
|
+
<% end %>
|
27
|
+
</datalist>
|
28
|
+
<% end %>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<div class="control">
|
32
|
+
<%= link_to "Clear", application_jobs_path(MissionControl::Jobs::Current.application, jobs_status, job_class_name: nil, queue_name: nil), class: "button" %>
|
33
|
+
</div>
|
34
|
+
</div>
|
35
|
+
</div>
|
@@ -0,0 +1,54 @@
|
|
1
|
+
<table class="table">
|
2
|
+
<tbody>
|
3
|
+
<tr>
|
4
|
+
<th>Arguments</th>
|
5
|
+
<td>
|
6
|
+
<div class="is-family-monospace">
|
7
|
+
<%= job_arguments(job) %>
|
8
|
+
</div>
|
9
|
+
</td>
|
10
|
+
</tr>
|
11
|
+
<tr>
|
12
|
+
<th>Job id</th>
|
13
|
+
<td><%= job.job_id %> </td>
|
14
|
+
</tr>
|
15
|
+
<tr>
|
16
|
+
<th>Queue</th>
|
17
|
+
<td>
|
18
|
+
<div class="tag">
|
19
|
+
<%= link_to job.queue_name, application_queue_path(@application, job.queue) %>
|
20
|
+
</div>
|
21
|
+
</td>
|
22
|
+
</tr>
|
23
|
+
<tr>
|
24
|
+
<th>Enqueued</th>
|
25
|
+
<td>
|
26
|
+
<%= time_ago_in_words_with_title(job.enqueued_at.to_datetime) %> ago
|
27
|
+
</td>
|
28
|
+
</tr>
|
29
|
+
<% if job.failed? %>
|
30
|
+
<tr>
|
31
|
+
<th>Failed</th>
|
32
|
+
<td>
|
33
|
+
<%= time_ago_in_words_with_title(job.failed_at) %> ago
|
34
|
+
</td>
|
35
|
+
</tr>
|
36
|
+
<% end %>
|
37
|
+
<% if job.finished_at.present? %>
|
38
|
+
<tr>
|
39
|
+
<th>Finished at</th>
|
40
|
+
<td>
|
41
|
+
<%= time_ago_in_words_with_title(job.finished_at) %> ago
|
42
|
+
</td>
|
43
|
+
</tr>
|
44
|
+
<% end %>
|
45
|
+
<% if job.worker_id.present? %>
|
46
|
+
<tr>
|
47
|
+
<th>Processed by</th>
|
48
|
+
<td>
|
49
|
+
<%= link_to "worker #{job.worker_id}", application_worker_path(@application, job.worker_id) %>
|
50
|
+
</td>
|
51
|
+
</tr>
|
52
|
+
<% end %>
|
53
|
+
</tbody>
|
54
|
+
</table>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<tr class="job">
|
2
|
+
<td>
|
3
|
+
<%= link_to job_title(job), application_job_path(@application, job.job_id) %>
|
4
|
+
|
5
|
+
<% if job.serialized_arguments.present? %>
|
6
|
+
<div class="is-family-monospace"><%= job_arguments(job) %></div>
|
7
|
+
<% end %>
|
8
|
+
|
9
|
+
<div class="has-text-grey is-size-7">Enqueued <%= time_ago_in_words_with_title(job.enqueued_at.to_datetime) %> ago</div>
|
10
|
+
</td>
|
11
|
+
|
12
|
+
<%= render "mission_control/jobs/jobs/#{jobs_status}/job", job: job %>
|
13
|
+
</tr>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<table class="jobs <%= jobs_status %> table queues is-hoverable is-fullwidth">
|
2
|
+
<tbody>
|
3
|
+
<thead>
|
4
|
+
<tr>
|
5
|
+
<th style="width: 35%;">Job</th>
|
6
|
+
<% attribute_names_for_job_status(jobs_status).each do |attribute| %>
|
7
|
+
<th><%= attribute %></th>
|
8
|
+
<% end %>
|
9
|
+
</tr>
|
10
|
+
</thead>
|
11
|
+
|
12
|
+
<%= render partial: "mission_control/jobs/jobs/job", collection: jobs_page.jobs %>
|
13
|
+
|
14
|
+
</tbody>
|
15
|
+
</table>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<h1 class="title">
|
2
|
+
<div class="level">
|
3
|
+
<div class="level-left">
|
4
|
+
<%= job_title(job) %>
|
5
|
+
<div class="<%= modifier_for_status(job.status) %> tag ml-4"><%= job.status %></div>
|
6
|
+
</div>
|
7
|
+
<div class="level-right">
|
8
|
+
<% if job.failed? %>
|
9
|
+
<%= render "mission_control/jobs/jobs/failed/actions", job: job %>
|
10
|
+
<% end %>
|
11
|
+
</div>
|
12
|
+
</div>
|
13
|
+
</h1>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<div class="buttons is-right level-right">
|
2
|
+
<% if active_filters? %>
|
3
|
+
<span class="mr-5">
|
4
|
+
<%= jobs_count %> jobs found
|
5
|
+
</span>
|
6
|
+
<% end %>
|
7
|
+
|
8
|
+
<% if jobs_status.failed? %>
|
9
|
+
<% target = active_filters? ? "selection" : "all" %>
|
10
|
+
|
11
|
+
<%= button_to "Discard #{target}", application_bulk_discards_path(@application, **jobs_filter_param),
|
12
|
+
method: :post, disabled: jobs_count == 0, class: "button is-danger is-light",
|
13
|
+
form: { data: { turbo_confirm: "This will delete #{jobs_count} jobs and can't be undone. Are you sure?" } } %>
|
14
|
+
<%= button_to "Retry #{target}", application_bulk_retries_path(@application, **jobs_filter_param),
|
15
|
+
method: :post, disabled: jobs_count == 0,
|
16
|
+
class: "button is-warning is-light mr-0" %>
|
17
|
+
<% end %>
|
18
|
+
</div>
|
@@ -0,0 +1,5 @@
|
|
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",
|
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" %>
|
5
|
+
</div>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<td>
|
2
|
+
<%= link_to failed_job_error(job), application_job_path(@application, job.job_id, anchor: "error") %>
|
3
|
+
<div class="has-text-grey"><%= time_ago_in_words_with_title(job.failed_at) %> ago</div>
|
4
|
+
</td>
|
5
|
+
<td class="pr-0">
|
6
|
+
<%= render "mission_control/jobs/jobs/failed/actions", job: job %>
|
7
|
+
</td>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<td><%= link_to job.queue_name, application_queue_path(@application, job.queue) %></td>
|
2
|
+
<td>
|
3
|
+
<% if job.worker_id %>
|
4
|
+
<%= link_to "worker #{job.worker_id}", application_worker_path(@application, job.worker_id) %>
|
5
|
+
<% else %>
|
6
|
+
—
|
7
|
+
<% end %>
|
8
|
+
</td>
|
9
|
+
<td><div class="has-text-grey"><%= job.started_at ? time_distance_in_words_with_title(job.started_at) : "(Finished)" %></div></td>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<% navigation(title: "#{jobs_status.titleize} jobs", section: "#{jobs_status}_jobs".to_sym) %>
|
2
|
+
|
3
|
+
<% if @jobs_page.empty? && !active_filters? %>
|
4
|
+
<%= blank_status_notice "There are no #{jobs_status.dasherize} jobs #{blank_status_emoji(jobs_status)}" %>
|
5
|
+
<% else %>
|
6
|
+
<div class="level">
|
7
|
+
<%= render "mission_control/jobs/jobs/filters", job_class_names: @job_class_names, queue_names: @queue_names %>
|
8
|
+
<%= render "mission_control/jobs/jobs/toolbar", jobs_count: @jobs_count %>
|
9
|
+
</div>
|
10
|
+
|
11
|
+
<% if @jobs_page.empty? %>
|
12
|
+
<%= blank_status_notice "No #{jobs_status.dasherize} jobs found with the given filters" %>
|
13
|
+
<% else %>
|
14
|
+
<%= render "mission_control/jobs/jobs/jobs_page", jobs_page: @jobs_page %>
|
15
|
+
|
16
|
+
<%= render "mission_control/jobs/shared/pagination_toolbar", jobs_page: @jobs_page %>
|
17
|
+
<% end %>
|
18
|
+
<% end %>
|
19
|
+
|