solid_queue_web 0.5.0 → 0.5.5
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/app/controllers/solid_queue_web/failed_jobs_controller.rb +24 -9
- data/app/controllers/solid_queue_web/jobs_controller.rb +13 -28
- data/app/controllers/solid_queue_web/queues/jobs_controller.rb +63 -0
- data/app/models/solid_queue_web/job.rb +13 -0
- data/app/views/solid_queue_web/failed_jobs/index.html.erb +33 -4
- data/app/views/solid_queue_web/jobs/index.html.erb +11 -21
- data/app/views/solid_queue_web/jobs/show.html.erb +13 -8
- data/app/views/solid_queue_web/queues/jobs/destroy.turbo_stream.erb +9 -0
- data/app/views/solid_queue_web/queues/jobs/index.html.erb +89 -0
- data/config/routes.rb +5 -0
- data/lib/solid_queue_web/version.rb +1 -1
- metadata +5 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4fede0428a11872f9c6d461a4c58b658c59a5dbb4549de6fe0f13d1f1bafd6ae
|
|
4
|
+
data.tar.gz: 3fd89b402ec8034cdcec4e80c260e83dedcf4c96519375283fe0678696548d88
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 361aaa9eca22abd7a34f574c434f04700dcce5d0c0f29dd0bf4f929bccedf08b32db280db3c8e29c6b237325b791dc09528cced2f9d8419b8f02879e32a0aa36
|
|
7
|
+
data.tar.gz: f2d79da345dec941c24a468729f78de4b210d9f4d8bb6b8203c6e60da16577e7b5538c2b66b8a544524bbc32cf7289fc26a77e3529e27444f825b889afde582a
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module SolidQueueWeb
|
|
2
2
|
class FailedJobsController < ApplicationController
|
|
3
|
+
before_action :set_filter_params, only: [ :index, :retry_all, :discard_all ]
|
|
4
|
+
|
|
3
5
|
def index
|
|
4
|
-
@pagy, @failed_jobs = pagy(
|
|
5
|
-
SolidQueue::FailedExecution.includes(:job).order(created_at: :desc)
|
|
6
|
-
)
|
|
6
|
+
@pagy, @failed_jobs = pagy(filtered_scope.order(created_at: :desc))
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def retry
|
|
@@ -23,16 +23,31 @@ module SolidQueueWeb
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def retry_all
|
|
26
|
-
|
|
27
|
-
jobs = executions.map(&:job)
|
|
26
|
+
jobs = filtered_scope.map(&:job)
|
|
28
27
|
SolidQueue::FailedExecution.retry_all(jobs)
|
|
29
|
-
redirect_to failed_jobs_path,
|
|
28
|
+
redirect_to failed_jobs_path(queue: @queue, q: @search),
|
|
29
|
+
notice: "#{jobs.size} #{"job".pluralize(jobs.size)} queued for retry."
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def discard_all
|
|
33
|
-
|
|
34
|
-
SolidQueue::FailedExecution.
|
|
35
|
-
redirect_to failed_jobs_path,
|
|
33
|
+
jobs = filtered_scope.map(&:job)
|
|
34
|
+
SolidQueue::FailedExecution.discard_all_from_jobs(jobs)
|
|
35
|
+
redirect_to failed_jobs_path(queue: @queue, q: @search),
|
|
36
|
+
notice: "#{jobs.size} #{"job".pluralize(jobs.size)} discarded."
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def set_filter_params
|
|
42
|
+
@queue = params[:queue].presence
|
|
43
|
+
@search = params[:q].presence
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def filtered_scope
|
|
47
|
+
scope = SolidQueue::FailedExecution.includes(:job)
|
|
48
|
+
scope = scope.references(:job).where(solid_queue_jobs: { queue_name: @queue }) if @queue.present?
|
|
49
|
+
scope = scope.references(:job).where("solid_queue_jobs.class_name LIKE ?", "%#{@search}%") if @search.present?
|
|
50
|
+
scope
|
|
36
51
|
end
|
|
37
52
|
end
|
|
38
53
|
end
|
|
@@ -1,23 +1,11 @@
|
|
|
1
1
|
module SolidQueueWeb
|
|
2
2
|
class JobsController < ApplicationController
|
|
3
|
-
before_action :
|
|
4
|
-
|
|
5
|
-
STATUSES = %w[ready scheduled claimed blocked failed].freeze
|
|
6
|
-
DISCARDABLE = %w[ready scheduled blocked].freeze
|
|
7
|
-
EXECUTION_MODELS = {
|
|
8
|
-
"ready" => SolidQueue::ReadyExecution,
|
|
9
|
-
"scheduled" => SolidQueue::ScheduledExecution,
|
|
10
|
-
"claimed" => SolidQueue::ClaimedExecution,
|
|
11
|
-
"blocked" => SolidQueue::BlockedExecution,
|
|
12
|
-
"failed" => SolidQueue::FailedExecution
|
|
13
|
-
}.freeze
|
|
3
|
+
before_action :set_status, only: [ :destroy, :discard_all ]
|
|
14
4
|
|
|
15
5
|
def index
|
|
16
|
-
@status = params[:status].presence_in(STATUSES) || "ready"
|
|
17
|
-
@queue = params[:queue].presence
|
|
6
|
+
@status = params[:status].presence_in(Job::STATUSES) || "ready"
|
|
18
7
|
@search = params[:q].presence
|
|
19
|
-
@jobs = EXECUTION_MODELS[@status].includes(:job)
|
|
20
|
-
@jobs = @jobs.where(jobs: { queue_name: @queue }) if @queue.present?
|
|
8
|
+
@jobs = Job::EXECUTION_MODELS[@status].includes(:job)
|
|
21
9
|
@jobs = @jobs.references(:job).where("solid_queue_jobs.class_name LIKE ?", "%#{@search}%") if @search.present?
|
|
22
10
|
@pagy, @jobs = pagy(@jobs.order(created_at: :desc))
|
|
23
11
|
end
|
|
@@ -26,7 +14,6 @@ module SolidQueueWeb
|
|
|
26
14
|
@job = SolidQueue::Job
|
|
27
15
|
.includes(:ready_execution, :scheduled_execution, :claimed_execution, :blocked_execution, :failed_execution)
|
|
28
16
|
.find(params[:id])
|
|
29
|
-
@failed_execution = @job.failed_execution
|
|
30
17
|
@execution_status = derive_status(@job)
|
|
31
18
|
end
|
|
32
19
|
|
|
@@ -37,24 +24,24 @@ module SolidQueueWeb
|
|
|
37
24
|
@remaining_count = filtered_scope(model).count
|
|
38
25
|
respond_to do |format|
|
|
39
26
|
format.turbo_stream
|
|
40
|
-
format.html { redirect_to jobs_path(status: @status
|
|
27
|
+
format.html { redirect_to jobs_path(status: @status), notice: "Job discarded." }
|
|
41
28
|
end
|
|
42
29
|
rescue ArgumentError => e
|
|
43
|
-
redirect_to jobs_path(status: @status
|
|
30
|
+
redirect_to jobs_path(status: @status), alert: e.message
|
|
44
31
|
rescue => e
|
|
45
|
-
redirect_to jobs_path(status: @status
|
|
32
|
+
redirect_to jobs_path(status: @status), alert: "Could not discard job: #{e.message}"
|
|
46
33
|
end
|
|
47
34
|
|
|
48
35
|
def discard_all
|
|
49
36
|
model = execution_model_for!(@status)
|
|
50
37
|
jobs = filtered_scope(model).map(&:job)
|
|
51
38
|
model.discard_all_from_jobs(jobs)
|
|
52
|
-
redirect_to jobs_path(status: @status
|
|
39
|
+
redirect_to jobs_path(status: @status),
|
|
53
40
|
notice: "#{jobs.size} #{"job".pluralize(jobs.size)} discarded."
|
|
54
41
|
rescue ArgumentError => e
|
|
55
|
-
redirect_to jobs_path(status: @status
|
|
42
|
+
redirect_to jobs_path(status: @status), alert: e.message
|
|
56
43
|
rescue => e
|
|
57
|
-
redirect_to jobs_path(status: @status
|
|
44
|
+
redirect_to jobs_path(status: @status), alert: "Could not discard jobs: #{e.message}"
|
|
58
45
|
end
|
|
59
46
|
|
|
60
47
|
private
|
|
@@ -68,19 +55,17 @@ module SolidQueueWeb
|
|
|
68
55
|
"finished"
|
|
69
56
|
end
|
|
70
57
|
|
|
71
|
-
def
|
|
58
|
+
def set_status
|
|
72
59
|
@status = params[:status]
|
|
73
|
-
@queue = params[:queue].presence
|
|
74
60
|
end
|
|
75
61
|
|
|
76
62
|
def filtered_scope(model)
|
|
77
|
-
|
|
78
|
-
@queue.present? ? scope.where(jobs: { queue_name: @queue }) : scope
|
|
63
|
+
model.includes(:job)
|
|
79
64
|
end
|
|
80
65
|
|
|
81
66
|
def execution_model_for!(status)
|
|
82
|
-
raise ArgumentError, "Cannot discard #{status} jobs from this page." unless DISCARDABLE.include?(status)
|
|
83
|
-
EXECUTION_MODELS[status]
|
|
67
|
+
raise ArgumentError, "Cannot discard #{status} jobs from this page." unless Job::DISCARDABLE.include?(status)
|
|
68
|
+
Job::EXECUTION_MODELS[status]
|
|
84
69
|
end
|
|
85
70
|
end
|
|
86
71
|
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module SolidQueueWeb
|
|
2
|
+
module Queues
|
|
3
|
+
class JobsController < ApplicationController
|
|
4
|
+
before_action :set_queue
|
|
5
|
+
before_action :set_status, only: [ :destroy, :discard_all ]
|
|
6
|
+
|
|
7
|
+
def index
|
|
8
|
+
@status = params[:status].presence_in(Job::STATUSES) || "ready"
|
|
9
|
+
@search = params[:q].presence
|
|
10
|
+
@jobs = Job::EXECUTION_MODELS[@status].includes(:job)
|
|
11
|
+
.where(solid_queue_jobs: { queue_name: @queue })
|
|
12
|
+
@jobs = @jobs.references(:job).where("solid_queue_jobs.class_name LIKE ?", "%#{@search}%") if @search.present?
|
|
13
|
+
@pagy, @jobs = pagy(@jobs.order(created_at: :desc))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def destroy
|
|
17
|
+
model = execution_model_for!(@status)
|
|
18
|
+
@execution = model.find(params[:id])
|
|
19
|
+
@execution.discard
|
|
20
|
+
@remaining_count = filtered_scope(model).count
|
|
21
|
+
respond_to do |format|
|
|
22
|
+
format.turbo_stream
|
|
23
|
+
format.html { redirect_to queue_jobs_path(queue_name: @queue, status: @status), notice: "Job discarded." }
|
|
24
|
+
end
|
|
25
|
+
rescue ArgumentError => e
|
|
26
|
+
redirect_to queue_jobs_path(queue_name: @queue, status: @status), alert: e.message
|
|
27
|
+
rescue => e
|
|
28
|
+
redirect_to queue_jobs_path(queue_name: @queue, status: @status), alert: "Could not discard job: #{e.message}"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def discard_all
|
|
32
|
+
model = execution_model_for!(@status)
|
|
33
|
+
jobs = filtered_scope(model).map(&:job)
|
|
34
|
+
model.discard_all_from_jobs(jobs)
|
|
35
|
+
redirect_to queue_jobs_path(queue_name: @queue, status: @status),
|
|
36
|
+
notice: "#{jobs.size} #{"job".pluralize(jobs.size)} discarded."
|
|
37
|
+
rescue ArgumentError => e
|
|
38
|
+
redirect_to queue_jobs_path(queue_name: @queue, status: @status), alert: e.message
|
|
39
|
+
rescue => e
|
|
40
|
+
redirect_to queue_jobs_path(queue_name: @queue, status: @status), alert: "Could not discard jobs: #{e.message}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def set_queue
|
|
46
|
+
@queue = params[:queue_name]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def set_status
|
|
50
|
+
@status = params[:status]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def filtered_scope(model)
|
|
54
|
+
model.includes(:job).where(solid_queue_jobs: { queue_name: @queue })
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def execution_model_for!(status)
|
|
58
|
+
raise ArgumentError, "Cannot discard #{status} jobs from this page." unless Job::DISCARDABLE.include?(status)
|
|
59
|
+
Job::EXECUTION_MODELS[status]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module SolidQueueWeb
|
|
2
|
+
class Job
|
|
3
|
+
STATUSES = %w[ready scheduled claimed blocked failed].freeze
|
|
4
|
+
DISCARDABLE = %w[ready scheduled blocked].freeze
|
|
5
|
+
EXECUTION_MODELS = {
|
|
6
|
+
"ready" => SolidQueue::ReadyExecution,
|
|
7
|
+
"scheduled" => SolidQueue::ScheduledExecution,
|
|
8
|
+
"claimed" => SolidQueue::ClaimedExecution,
|
|
9
|
+
"blocked" => SolidQueue::BlockedExecution,
|
|
10
|
+
"failed" => SolidQueue::FailedExecution
|
|
11
|
+
}.freeze
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -2,14 +2,33 @@
|
|
|
2
2
|
<h1 class="sqd-page-title">Failed Jobs</h1>
|
|
3
3
|
<% if @failed_jobs.any? %>
|
|
4
4
|
<div class="sqd-actions">
|
|
5
|
-
<%= button_to "Retry All", retry_all_failed_jobs_path,
|
|
5
|
+
<%= button_to "Retry All", retry_all_failed_jobs_path,
|
|
6
|
+
method: :post,
|
|
7
|
+
params: { queue: @queue, q: @search },
|
|
8
|
+
class: "sqd-btn sqd-btn--primary",
|
|
6
9
|
data: { confirm: "Retry all #{@failed_jobs.size} failed jobs?" } %>
|
|
7
|
-
<%= button_to "Discard All", discard_all_failed_jobs_path,
|
|
10
|
+
<%= button_to "Discard All", discard_all_failed_jobs_path,
|
|
11
|
+
method: :post,
|
|
12
|
+
params: { queue: @queue, q: @search },
|
|
13
|
+
class: "sqd-btn sqd-btn--danger",
|
|
8
14
|
data: { confirm: "Discard all #{@failed_jobs.size} failed jobs? This cannot be undone." } %>
|
|
9
15
|
</div>
|
|
10
16
|
<% end %>
|
|
11
17
|
</div>
|
|
12
18
|
|
|
19
|
+
<form class="sqd-search" action="<%= failed_jobs_path %>" method="get" data-controller="search">
|
|
20
|
+
<% if @queue.present? %>
|
|
21
|
+
<input type="hidden" name="queue" value="<%= @queue %>">
|
|
22
|
+
<% end %>
|
|
23
|
+
<input class="sqd-search__input" type="search" name="q" value="<%= @search %>"
|
|
24
|
+
placeholder="Filter by job class…" autocomplete="off" aria-label="Filter by job class"
|
|
25
|
+
data-action="input->search#filter">
|
|
26
|
+
<button type="submit" class="sqd-btn sqd-btn--muted">Search</button>
|
|
27
|
+
<% if @search.present? %>
|
|
28
|
+
<%= link_to "Clear", failed_jobs_path(queue: @queue), class: "sqd-btn sqd-btn--muted" %>
|
|
29
|
+
<% end %>
|
|
30
|
+
</form>
|
|
31
|
+
|
|
13
32
|
<% if @pagy.last > 1 %>
|
|
14
33
|
<%= @pagy.series_nav.html_safe %>
|
|
15
34
|
<% end %>
|
|
@@ -33,7 +52,10 @@
|
|
|
33
52
|
<% job = execution.job %>
|
|
34
53
|
<tr>
|
|
35
54
|
<td><%= link_to job.class_name, job_path(job) %></td>
|
|
36
|
-
<td
|
|
55
|
+
<td>
|
|
56
|
+
<%= link_to job.queue_name, failed_jobs_path(queue: job.queue_name, q: @search),
|
|
57
|
+
class: "sqd-mono", style: "color: inherit;" %>
|
|
58
|
+
</td>
|
|
37
59
|
<td>
|
|
38
60
|
<% if execution.exception_class.present? %>
|
|
39
61
|
<div class="sqd-error-msg sqd-truncate" title="<%= execution.exception_class %>: <%= execution.message %>">
|
|
@@ -56,4 +78,11 @@
|
|
|
56
78
|
</tbody>
|
|
57
79
|
</table>
|
|
58
80
|
<% end %>
|
|
59
|
-
</div>
|
|
81
|
+
</div>
|
|
82
|
+
|
|
83
|
+
<% if @queue.present? %>
|
|
84
|
+
<p style="margin-top: 0.75rem; font-size: 13px; color: var(--muted);">
|
|
85
|
+
Filtering by queue: <strong><%= @queue %></strong> —
|
|
86
|
+
<%= link_to "Clear filter", failed_jobs_path(q: @search) %>
|
|
87
|
+
</p>
|
|
88
|
+
<% end %>
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
<h1 class="sqd-page-title" style="margin-bottom: 1.5rem;">Jobs</h1>
|
|
2
2
|
|
|
3
3
|
<%= turbo_frame_tag "jobs-table", data: { turbo_action: "advance" } do %>
|
|
4
|
-
<% discardable = SolidQueueWeb::
|
|
4
|
+
<% discardable = SolidQueueWeb::Job::DISCARDABLE.include?(@status) %>
|
|
5
5
|
|
|
6
6
|
<div class="sqd-page-header">
|
|
7
7
|
<div class="sqd-filters">
|
|
8
|
-
<%= link_to "Ready", jobs_path(status: "ready",
|
|
9
|
-
<%= link_to "Scheduled", jobs_path(status: "scheduled",
|
|
10
|
-
<%= link_to "Running", jobs_path(status: "claimed",
|
|
11
|
-
<%= link_to "Blocked", jobs_path(status: "blocked",
|
|
12
|
-
<%= link_to "Failed", jobs_path(status: "failed",
|
|
8
|
+
<%= link_to "Ready", jobs_path(status: "ready", q: @search), class: @status == "ready" ? "active" : "" %>
|
|
9
|
+
<%= link_to "Scheduled", jobs_path(status: "scheduled", q: @search), class: @status == "scheduled" ? "active" : "" %>
|
|
10
|
+
<%= link_to "Running", jobs_path(status: "claimed", q: @search), class: @status == "claimed" ? "active" : "" %>
|
|
11
|
+
<%= link_to "Blocked", jobs_path(status: "blocked", q: @search), class: @status == "blocked" ? "active" : "" %>
|
|
12
|
+
<%= link_to "Failed", jobs_path(status: "failed", q: @search), class: @status == "failed" ? "active" : "" %>
|
|
13
13
|
</div>
|
|
14
14
|
<% if discardable && @jobs.any? %>
|
|
15
15
|
<div class="sqd-actions">
|
|
16
16
|
<%= button_to "Discard All", discard_all_jobs_path,
|
|
17
17
|
method: :post,
|
|
18
|
-
params: { status: @status
|
|
18
|
+
params: { status: @status },
|
|
19
19
|
class: "sqd-btn sqd-btn--danger",
|
|
20
20
|
data: { confirm: "Discard all #{@jobs.size} #{@status} jobs? This cannot be undone." } %>
|
|
21
21
|
</div>
|
|
@@ -24,15 +24,12 @@
|
|
|
24
24
|
|
|
25
25
|
<form class="sqd-search" action="<%= jobs_path %>" method="get" data-controller="search">
|
|
26
26
|
<input type="hidden" name="status" value="<%= @status %>">
|
|
27
|
-
<% if @queue.present? %>
|
|
28
|
-
<input type="hidden" name="queue" value="<%= @queue %>">
|
|
29
|
-
<% end %>
|
|
30
27
|
<input class="sqd-search__input" type="search" name="q" value="<%= @search %>"
|
|
31
28
|
placeholder="Filter by job class…" autocomplete="off" aria-label="Filter by job class"
|
|
32
29
|
data-action="input->search#filter">
|
|
33
30
|
<button type="submit" class="sqd-btn sqd-btn--muted">Search</button>
|
|
34
31
|
<% if @search.present? %>
|
|
35
|
-
<%= link_to "Clear", jobs_path(status: @status
|
|
32
|
+
<%= link_to "Clear", jobs_path(status: @status), class: "sqd-btn sqd-btn--muted" %>
|
|
36
33
|
<% end %>
|
|
37
34
|
</form>
|
|
38
35
|
|
|
@@ -57,10 +54,10 @@
|
|
|
57
54
|
<tr id="execution_<%= execution.id %>">
|
|
58
55
|
<td>
|
|
59
56
|
<span class="sqd-badge sqd-badge--<%= @status %>"><%= @status %></span>
|
|
60
|
-
<%= link_to job.class_name, job_path(job), style: "margin-left: 0.5rem;" %>
|
|
57
|
+
<%= link_to job.class_name, job_path(job), style: "margin-left: 0.5rem;", data: { turbo_frame: "_top" } %>
|
|
61
58
|
</td>
|
|
62
59
|
<td>
|
|
63
|
-
<%= link_to job.queue_name,
|
|
60
|
+
<%= link_to job.queue_name, queue_jobs_path(queue_name: job.queue_name, status: @status),
|
|
64
61
|
class: "sqd-mono", style: "color: inherit;" %>
|
|
65
62
|
</td>
|
|
66
63
|
<td><%= job.priority %></td>
|
|
@@ -72,7 +69,7 @@
|
|
|
72
69
|
<td class="sqd-row-actions">
|
|
73
70
|
<%= button_to "Discard", job_path(execution),
|
|
74
71
|
method: :delete,
|
|
75
|
-
params: { status: @status
|
|
72
|
+
params: { status: @status },
|
|
76
73
|
class: "sqd-btn sqd-btn--danger sqd-btn--sm",
|
|
77
74
|
data: { confirm: "Discard this job?" } %>
|
|
78
75
|
</td>
|
|
@@ -87,11 +84,4 @@
|
|
|
87
84
|
<% if @pagy.last > 1 %>
|
|
88
85
|
<%= @pagy.series_nav.html_safe %>
|
|
89
86
|
<% end %>
|
|
90
|
-
|
|
91
|
-
<% if @queue.present? %>
|
|
92
|
-
<p style="margin-top: 0.75rem; font-size: 13px; color: var(--muted);">
|
|
93
|
-
Filtering by queue: <strong><%= @queue %></strong> —
|
|
94
|
-
<%= link_to "Clear filter", jobs_path(status: @status) %>
|
|
95
|
-
</p>
|
|
96
|
-
<% end %>
|
|
97
87
|
<% end %>
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
</div>
|
|
8
8
|
|
|
9
9
|
<div class="sqd-actions">
|
|
10
|
-
<% if @execution_status == "failed" && @failed_execution %>
|
|
11
|
-
<%= button_to "Retry", retry_failed_job_path(@failed_execution), method: :post,
|
|
10
|
+
<% if @execution_status == "failed" && @job.failed_execution %>
|
|
11
|
+
<%= button_to "Retry", retry_failed_job_path(@job.failed_execution), method: :post,
|
|
12
12
|
class: "sqd-btn sqd-btn--primary" %>
|
|
13
|
-
<%= button_to "Discard", failed_job_path(@failed_execution), method: :delete,
|
|
13
|
+
<%= button_to "Discard", failed_job_path(@job.failed_execution), method: :delete,
|
|
14
14
|
class: "sqd-btn sqd-btn--danger",
|
|
15
15
|
data: { confirm: "Discard this job?" } %>
|
|
16
|
-
<% elsif SolidQueueWeb::
|
|
16
|
+
<% elsif SolidQueueWeb::Job::DISCARDABLE.include?(@execution_status) %>
|
|
17
17
|
<% execution = @job.public_send("#{@execution_status}_execution") %>
|
|
18
18
|
<% if execution %>
|
|
19
19
|
<%= button_to "Discard", job_path(execution),
|
|
@@ -45,6 +45,11 @@
|
|
|
45
45
|
<dt>Concurrency Key</dt>
|
|
46
46
|
<dd class="sqd-mono"><%= @job.concurrency_key.presence || "—" %></dd>
|
|
47
47
|
|
|
48
|
+
<% if @job.blocked_execution %>
|
|
49
|
+
<dt>Blocked Until</dt>
|
|
50
|
+
<dd class="sqd-mono"><%= @job.blocked_execution.expires_at ? @job.blocked_execution.expires_at.strftime("%Y-%m-%d %H:%M:%S %Z") : "—" %></dd>
|
|
51
|
+
<% end %>
|
|
52
|
+
|
|
48
53
|
<dt>Enqueued At</dt>
|
|
49
54
|
<dd class="sqd-mono"><%= @job.created_at.strftime("%Y-%m-%d %H:%M:%S %Z") %></dd>
|
|
50
55
|
|
|
@@ -62,14 +67,14 @@
|
|
|
62
67
|
</div>
|
|
63
68
|
</div>
|
|
64
69
|
|
|
65
|
-
<% if @failed_execution %>
|
|
70
|
+
<% if @job.failed_execution %>
|
|
66
71
|
<div class="sqd-card sqd-detail-section" style="margin-top: 1.5rem;">
|
|
67
72
|
<h2 class="sqd-section-title sqd-section-title--danger">Error</h2>
|
|
68
73
|
<p class="sqd-error-header">
|
|
69
|
-
<strong><%= @failed_execution.exception_class %></strong>: <%= @failed_execution.message %>
|
|
74
|
+
<strong><%= @job.failed_execution.exception_class %></strong>: <%= @job.failed_execution.message %>
|
|
70
75
|
</p>
|
|
71
|
-
<% if @failed_execution.backtrace.present? %>
|
|
72
|
-
<pre class="sqd-pre sqd-pre--muted" style="margin-top: 0.75rem;"><%= Array(@failed_execution.backtrace).join("\n") %></pre>
|
|
76
|
+
<% if @job.failed_execution.backtrace.present? %>
|
|
77
|
+
<pre class="sqd-pre sqd-pre--muted" style="margin-top: 0.75rem;"><%= Array(@job.failed_execution.backtrace).join("\n") %></pre>
|
|
73
78
|
<% end %>
|
|
74
79
|
</div>
|
|
75
80
|
<% end %>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<% if @remaining_count == 0 %>
|
|
2
|
+
<%= turbo_stream.replace "jobs-list" do %>
|
|
3
|
+
<div class="sqd-card" id="jobs-list">
|
|
4
|
+
<div class="sqd-empty">No <%= @status %> jobs in <%= @queue %>.</div>
|
|
5
|
+
</div>
|
|
6
|
+
<% end %>
|
|
7
|
+
<% else %>
|
|
8
|
+
<%= turbo_stream.remove "execution_#{@execution.id}" %>
|
|
9
|
+
<% end %>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
<div class="sqd-page-header">
|
|
2
|
+
<div>
|
|
3
|
+
<div class="sqd-breadcrumb">
|
|
4
|
+
<%= link_to "Queues", queues_path %> › <%= @queue %>
|
|
5
|
+
</div>
|
|
6
|
+
<h1 class="sqd-page-title">Jobs</h1>
|
|
7
|
+
</div>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<%= turbo_frame_tag "jobs-table", data: { turbo_action: "advance" } do %>
|
|
11
|
+
<% discardable = SolidQueueWeb::Job::DISCARDABLE.include?(@status) %>
|
|
12
|
+
|
|
13
|
+
<div class="sqd-page-header">
|
|
14
|
+
<div class="sqd-filters">
|
|
15
|
+
<%= link_to "Ready", queue_jobs_path(queue_name: @queue, status: "ready", q: @search), class: @status == "ready" ? "active" : "" %>
|
|
16
|
+
<%= link_to "Scheduled", queue_jobs_path(queue_name: @queue, status: "scheduled", q: @search), class: @status == "scheduled" ? "active" : "" %>
|
|
17
|
+
<%= link_to "Running", queue_jobs_path(queue_name: @queue, status: "claimed", q: @search), class: @status == "claimed" ? "active" : "" %>
|
|
18
|
+
<%= link_to "Blocked", queue_jobs_path(queue_name: @queue, status: "blocked", q: @search), class: @status == "blocked" ? "active" : "" %>
|
|
19
|
+
<%= link_to "Failed", queue_jobs_path(queue_name: @queue, status: "failed", q: @search), class: @status == "failed" ? "active" : "" %>
|
|
20
|
+
</div>
|
|
21
|
+
<% if discardable && @jobs.any? %>
|
|
22
|
+
<div class="sqd-actions">
|
|
23
|
+
<%= button_to "Discard All", discard_all_queue_jobs_path(queue_name: @queue),
|
|
24
|
+
method: :post,
|
|
25
|
+
params: { status: @status },
|
|
26
|
+
class: "sqd-btn sqd-btn--danger",
|
|
27
|
+
data: { confirm: "Discard all #{@jobs.size} #{@status} jobs in #{@queue}? This cannot be undone." } %>
|
|
28
|
+
</div>
|
|
29
|
+
<% end %>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<form class="sqd-search" action="<%= queue_jobs_path(queue_name: @queue) %>" method="get" data-controller="search">
|
|
33
|
+
<input type="hidden" name="status" value="<%= @status %>">
|
|
34
|
+
<input class="sqd-search__input" type="search" name="q" value="<%= @search %>"
|
|
35
|
+
placeholder="Filter by job class…" autocomplete="off" aria-label="Filter by job class"
|
|
36
|
+
data-action="input->search#filter">
|
|
37
|
+
<button type="submit" class="sqd-btn sqd-btn--muted">Search</button>
|
|
38
|
+
<% if @search.present? %>
|
|
39
|
+
<%= link_to "Clear", queue_jobs_path(queue_name: @queue, status: @status), class: "sqd-btn sqd-btn--muted" %>
|
|
40
|
+
<% end %>
|
|
41
|
+
</form>
|
|
42
|
+
|
|
43
|
+
<div class="sqd-card" id="jobs-list">
|
|
44
|
+
<% if @jobs.empty? %>
|
|
45
|
+
<div class="sqd-empty">No <%= @status %> jobs in <%= @queue %>.</div>
|
|
46
|
+
<% else %>
|
|
47
|
+
<table>
|
|
48
|
+
<thead>
|
|
49
|
+
<tr>
|
|
50
|
+
<th scope="col">Job Class</th>
|
|
51
|
+
<th scope="col">Priority</th>
|
|
52
|
+
<th scope="col">Scheduled At</th>
|
|
53
|
+
<th scope="col">Enqueued At</th>
|
|
54
|
+
<% if discardable %><th scope="col"><span class="sqd-sr-only">Actions</span></th><% end %>
|
|
55
|
+
</tr>
|
|
56
|
+
</thead>
|
|
57
|
+
<tbody>
|
|
58
|
+
<% @jobs.each do |execution| %>
|
|
59
|
+
<% job = execution.job %>
|
|
60
|
+
<tr id="execution_<%= execution.id %>">
|
|
61
|
+
<td>
|
|
62
|
+
<span class="sqd-badge sqd-badge--<%= @status %>"><%= @status %></span>
|
|
63
|
+
<%= link_to job.class_name, job_path(job), style: "margin-left: 0.5rem;", data: { turbo_frame: "_top" } %>
|
|
64
|
+
</td>
|
|
65
|
+
<td><%= job.priority %></td>
|
|
66
|
+
<td class="sqd-mono">
|
|
67
|
+
<%= job.scheduled_at ? job.scheduled_at.strftime("%Y-%m-%d %H:%M:%S") : "—" %>
|
|
68
|
+
</td>
|
|
69
|
+
<td class="sqd-mono"><%= job.created_at.strftime("%Y-%m-%d %H:%M:%S") %></td>
|
|
70
|
+
<% if discardable %>
|
|
71
|
+
<td class="sqd-row-actions">
|
|
72
|
+
<%= button_to "Discard", queue_job_path(queue_name: @queue, id: execution),
|
|
73
|
+
method: :delete,
|
|
74
|
+
params: { status: @status },
|
|
75
|
+
class: "sqd-btn sqd-btn--danger sqd-btn--sm",
|
|
76
|
+
data: { confirm: "Discard this job?" } %>
|
|
77
|
+
</td>
|
|
78
|
+
<% end %>
|
|
79
|
+
</tr>
|
|
80
|
+
<% end %>
|
|
81
|
+
</tbody>
|
|
82
|
+
</table>
|
|
83
|
+
<% end %>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<% if @pagy.last > 1 %>
|
|
87
|
+
<%= @pagy.series_nav.html_safe %>
|
|
88
|
+
<% end %>
|
|
89
|
+
<% end %>
|
data/config/routes.rb
CHANGED
|
@@ -8,6 +8,11 @@ SolidQueueWeb::Engine.routes.draw do
|
|
|
8
8
|
post :pause
|
|
9
9
|
post :resume
|
|
10
10
|
end
|
|
11
|
+
resources :jobs, path: "list", only: [ :index, :destroy ], controller: "queues/jobs" do
|
|
12
|
+
collection do
|
|
13
|
+
post :discard_all
|
|
14
|
+
end
|
|
15
|
+
end
|
|
11
16
|
end
|
|
12
17
|
resources :jobs, path: "list", only: [ :index, :show, :destroy ] do
|
|
13
18
|
collection do
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solid_queue_web
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.5.
|
|
4
|
+
version: 0.5.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chuck Smith
|
|
@@ -97,6 +97,7 @@ files:
|
|
|
97
97
|
- app/controllers/solid_queue_web/failed_jobs_controller.rb
|
|
98
98
|
- app/controllers/solid_queue_web/jobs_controller.rb
|
|
99
99
|
- app/controllers/solid_queue_web/processes_controller.rb
|
|
100
|
+
- app/controllers/solid_queue_web/queues/jobs_controller.rb
|
|
100
101
|
- app/controllers/solid_queue_web/queues_controller.rb
|
|
101
102
|
- app/controllers/solid_queue_web/recurring_tasks_controller.rb
|
|
102
103
|
- app/helpers/solid_queue_web/application_helper.rb
|
|
@@ -104,6 +105,7 @@ files:
|
|
|
104
105
|
- app/javascript/solid_queue_web/search_controller.js
|
|
105
106
|
- app/jobs/solid_queue_web/application_job.rb
|
|
106
107
|
- app/models/solid_queue_web/application_record.rb
|
|
108
|
+
- app/models/solid_queue_web/job.rb
|
|
107
109
|
- app/views/layouts/solid_queue_web/application.html.erb
|
|
108
110
|
- app/views/solid_queue_web/dashboard/index.html.erb
|
|
109
111
|
- app/views/solid_queue_web/failed_jobs/index.html.erb
|
|
@@ -112,6 +114,8 @@ files:
|
|
|
112
114
|
- app/views/solid_queue_web/jobs/show.html.erb
|
|
113
115
|
- app/views/solid_queue_web/processes/index.html.erb
|
|
114
116
|
- app/views/solid_queue_web/queues/index.html.erb
|
|
117
|
+
- app/views/solid_queue_web/queues/jobs/destroy.turbo_stream.erb
|
|
118
|
+
- app/views/solid_queue_web/queues/jobs/index.html.erb
|
|
115
119
|
- app/views/solid_queue_web/recurring_tasks/index.html.erb
|
|
116
120
|
- config/importmap.rb
|
|
117
121
|
- config/routes.rb
|