solid_queue_monitor 0.2.0 → 0.3.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 +24 -10
- data/Rakefile +8 -8
- data/app/controllers/solid_queue_monitor/application_controller.rb +25 -0
- data/app/controllers/solid_queue_monitor/base_controller.rb +70 -67
- data/app/controllers/solid_queue_monitor/failed_jobs_controller.rb +8 -7
- data/app/controllers/solid_queue_monitor/in_progress_jobs_controller.rb +16 -10
- data/app/controllers/solid_queue_monitor/overview_controller.rb +13 -12
- data/app/controllers/solid_queue_monitor/queues_controller.rb +4 -2
- data/app/controllers/solid_queue_monitor/ready_jobs_controller.rb +8 -7
- data/app/controllers/solid_queue_monitor/recurring_jobs_controller.rb +7 -6
- data/app/controllers/solid_queue_monitor/scheduled_jobs_controller.rb +8 -7
- data/app/presenters/solid_queue_monitor/base_presenter.rb +42 -41
- data/app/presenters/solid_queue_monitor/failed_jobs_presenter.rb +51 -41
- data/app/presenters/solid_queue_monitor/in_progress_jobs_presenter.rb +13 -2
- data/app/presenters/solid_queue_monitor/jobs_presenter.rb +18 -9
- data/app/presenters/solid_queue_monitor/queues_presenter.rb +5 -5
- data/app/presenters/solid_queue_monitor/ready_jobs_presenter.rb +10 -2
- data/app/presenters/solid_queue_monitor/recurring_jobs_presenter.rb +5 -2
- data/app/presenters/solid_queue_monitor/scheduled_jobs_presenter.rb +15 -9
- data/app/presenters/solid_queue_monitor/stats_presenter.rb +3 -1
- data/app/services/solid_queue_monitor/authentication_service.rb +7 -5
- data/app/services/solid_queue_monitor/execute_job_service.rb +3 -1
- data/app/services/solid_queue_monitor/failed_job_service.rb +38 -36
- data/app/services/solid_queue_monitor/html_generator.rb +5 -2
- data/app/services/solid_queue_monitor/pagination_service.rb +3 -1
- data/app/services/solid_queue_monitor/stats_calculator.rb +3 -1
- data/app/services/solid_queue_monitor/status_calculator.rb +4 -1
- data/app/services/solid_queue_monitor/stylesheet_generator.rb +23 -21
- data/config/initializers/solid_queue_monitor.rb +3 -1
- data/config/routes.rb +6 -4
- data/lib/generators/solid_queue_monitor/install_generator.rb +7 -5
- data/lib/generators/solid_queue_monitor/templates/initializer.rb +3 -1
- data/lib/solid_queue_monitor/engine.rb +5 -3
- data/lib/solid_queue_monitor/version.rb +2 -2
- data/lib/solid_queue_monitor.rb +9 -13
- data/lib/tasks/app.rake +42 -40
- metadata +9 -148
- data/app/controllers/solid_queue_monitor/monitor_controller.rb +0 -318
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SolidQueueMonitor
|
2
4
|
class BasePresenter
|
3
5
|
include ActionView::Helpers::DateHelper
|
@@ -9,7 +11,7 @@ module SolidQueueMonitor
|
|
9
11
|
{ only_path: true }
|
10
12
|
end
|
11
13
|
|
12
|
-
def section_wrapper(
|
14
|
+
def section_wrapper(_title, content)
|
13
15
|
<<-HTML
|
14
16
|
<div class="section-wrapper">
|
15
17
|
<div class="section">
|
@@ -21,36 +23,36 @@ module SolidQueueMonitor
|
|
21
23
|
|
22
24
|
def generate_pagination(current_page, total_pages)
|
23
25
|
return '' if total_pages <= 1
|
24
|
-
|
26
|
+
|
25
27
|
html = '<div class="pagination">'
|
26
|
-
|
28
|
+
|
27
29
|
# Previous page link
|
28
|
-
if current_page > 1
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
html += if current_page > 1
|
31
|
+
"<a href=\"?page=#{current_page - 1}#{query_params}\" class=\"pagination-link pagination-nav\">Previous</a>"
|
32
|
+
else
|
33
|
+
'<span class="pagination-link pagination-nav disabled">Previous</span>'
|
34
|
+
end
|
35
|
+
|
34
36
|
# Page links
|
35
37
|
visible_pages = calculate_visible_pages(current_page, total_pages)
|
36
|
-
|
38
|
+
|
37
39
|
visible_pages.each do |page|
|
38
|
-
if page == :gap
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
html += if page == :gap
|
41
|
+
'<span class="pagination-gap">...</span>'
|
42
|
+
elsif page == current_page
|
43
|
+
"<span class=\"pagination-current\">#{page}</span>"
|
44
|
+
else
|
45
|
+
"<a href=\"?page=#{page}#{query_params}\" class=\"pagination-link\">#{page}</a>"
|
46
|
+
end
|
45
47
|
end
|
46
|
-
|
48
|
+
|
47
49
|
# Next page link
|
48
|
-
if current_page < total_pages
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
html += if current_page < total_pages
|
51
|
+
"<a href=\"?page=#{current_page + 1}#{query_params}\" class=\"pagination-link pagination-nav\">Next</a>"
|
52
|
+
else
|
53
|
+
'<span class="pagination-link pagination-nav disabled">Next</span>'
|
54
|
+
end
|
55
|
+
|
54
56
|
html += '</div>'
|
55
57
|
html
|
56
58
|
end
|
@@ -72,30 +74,31 @@ module SolidQueueMonitor
|
|
72
74
|
|
73
75
|
def format_datetime(datetime)
|
74
76
|
return '-' unless datetime
|
77
|
+
|
75
78
|
datetime.strftime('%Y-%m-%d %H:%M:%S')
|
76
79
|
end
|
77
80
|
|
78
81
|
def format_arguments(arguments)
|
79
|
-
return '-'
|
80
|
-
|
82
|
+
return '-' if arguments.blank?
|
83
|
+
|
81
84
|
# For ActiveJob format
|
82
85
|
if arguments.is_a?(Hash) && arguments['arguments'].present?
|
83
86
|
return "<code>#{arguments['arguments'].inspect}</code>"
|
84
87
|
elsif arguments.is_a?(Array) && arguments.length == 1 && arguments[0].is_a?(Hash) && arguments[0]['arguments'].present?
|
85
88
|
return "<code>#{arguments[0]['arguments'].inspect}</code>"
|
86
89
|
end
|
87
|
-
|
90
|
+
|
88
91
|
# For regular arguments format
|
89
92
|
"<code>#{arguments.inspect}</code>"
|
90
93
|
end
|
91
94
|
|
92
95
|
def format_hash(hash)
|
93
|
-
return '-'
|
94
|
-
|
96
|
+
return '-' if hash.blank?
|
97
|
+
|
95
98
|
formatted = hash.map do |key, value|
|
96
99
|
"<strong>#{key}:</strong> #{value.to_s.truncate(50)}"
|
97
100
|
end.join(', ')
|
98
|
-
|
101
|
+
|
99
102
|
"<code>#{formatted}</code>"
|
100
103
|
end
|
101
104
|
|
@@ -106,7 +109,7 @@ module SolidQueueMonitor
|
|
106
109
|
controller.request.path
|
107
110
|
else
|
108
111
|
# Fallback to a default path if we can't get the current path
|
109
|
-
|
112
|
+
'/solid_queue'
|
110
113
|
end
|
111
114
|
end
|
112
115
|
|
@@ -116,7 +119,7 @@ module SolidQueueMonitor
|
|
116
119
|
if path_parts.length >= 3
|
117
120
|
"/#{path_parts[1]}/#{path_parts[2]}"
|
118
121
|
else
|
119
|
-
|
122
|
+
'/solid_queue'
|
120
123
|
end
|
121
124
|
end
|
122
125
|
|
@@ -127,19 +130,17 @@ module SolidQueueMonitor
|
|
127
130
|
params << "class_name=#{@filters[:class_name]}" if @filters && @filters[:class_name].present?
|
128
131
|
params << "queue_name=#{@filters[:queue_name]}" if @filters && @filters[:queue_name].present?
|
129
132
|
params << "status=#{@filters[:status]}" if @filters && @filters[:status].present?
|
130
|
-
|
133
|
+
|
131
134
|
params.empty? ? '' : "&#{params.join('&')}"
|
132
135
|
end
|
133
136
|
|
134
137
|
# Helper method to get the full path for a route
|
135
138
|
def full_path(route_name, *args)
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
Rails.application.routes.url_helpers.send("solid_queue_#{route_name}", *args)
|
142
|
-
end
|
139
|
+
# Try to use the engine routes first
|
140
|
+
SolidQueueMonitor::Engine.routes.url_helpers.send(route_name, *args)
|
141
|
+
rescue NoMethodError
|
142
|
+
# Fall back to main app routes
|
143
|
+
Rails.application.routes.url_helpers.send("solid_queue_#{route_name}", *args)
|
143
144
|
end
|
144
145
|
end
|
145
|
-
end
|
146
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SolidQueueMonitor
|
2
4
|
class FailedJobsPresenter < BasePresenter
|
3
5
|
include Rails.application.routes.url_helpers
|
@@ -11,7 +13,8 @@ module SolidQueueMonitor
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def render
|
14
|
-
section_wrapper('Failed Jobs',
|
16
|
+
section_wrapper('Failed Jobs',
|
17
|
+
generate_filter_form + generate_table + generate_pagination(@current_page, @total_pages))
|
15
18
|
end
|
16
19
|
|
17
20
|
private
|
@@ -30,13 +33,18 @@ module SolidQueueMonitor
|
|
30
33
|
<input type="text" name="queue_name" id="queue_name" value="#{@filters[:queue_name]}" placeholder="Filter by queue">
|
31
34
|
</div>
|
32
35
|
|
36
|
+
<div class="filter-group">
|
37
|
+
<label for="arguments">Arguments:</label>
|
38
|
+
<input type="text" name="arguments" id="arguments" value="#{@filters[:arguments]}" placeholder="Filter by arguments">
|
39
|
+
</div>
|
40
|
+
|
33
41
|
<div class="filter-actions">
|
34
42
|
<button type="submit" class="filter-button">Apply Filters</button>
|
35
43
|
<a href="#{failed_jobs_path}" class="reset-button">Reset</a>
|
36
44
|
</div>
|
37
45
|
</form>
|
38
46
|
</div>
|
39
|
-
|
47
|
+
|
40
48
|
<div class="bulk-actions-bar">
|
41
49
|
<button type="button" class="action-button retry-button" id="retry-selected-top" disabled>Retry Selected</button>
|
42
50
|
<button type="button" class="action-button discard-button" id="discard-selected-top" disabled>Discard Selected</button>
|
@@ -55,6 +63,7 @@ module SolidQueueMonitor
|
|
55
63
|
<th>Job</th>
|
56
64
|
<th>Queue</th>
|
57
65
|
<th>Error</th>
|
66
|
+
<th>Arguments</th>
|
58
67
|
<th>Actions</th>
|
59
68
|
</tr>
|
60
69
|
</thead>
|
@@ -64,7 +73,7 @@ module SolidQueueMonitor
|
|
64
73
|
</table>
|
65
74
|
</div>
|
66
75
|
</form>
|
67
|
-
|
76
|
+
|
68
77
|
<script>
|
69
78
|
document.addEventListener('DOMContentLoaded', function() {
|
70
79
|
// Handle select all checkboxes
|
@@ -73,13 +82,13 @@ module SolidQueueMonitor
|
|
73
82
|
const retrySelectedBtn = document.getElementById('retry-selected-top');
|
74
83
|
const discardSelectedBtn = document.getElementById('discard-selected-top');
|
75
84
|
const form = document.getElementById('failed-jobs-form');
|
76
|
-
|
85
|
+
#{' '}
|
77
86
|
function updateButtonState() {
|
78
87
|
const checkedBoxes = document.querySelectorAll('.job-checkbox:checked');
|
79
88
|
retrySelectedBtn.disabled = checkedBoxes.length === 0;
|
80
89
|
discardSelectedBtn.disabled = checkedBoxes.length === 0;
|
81
90
|
}
|
82
|
-
|
91
|
+
#{' '}
|
83
92
|
function toggleAll(checked) {
|
84
93
|
checkboxes.forEach(checkbox => {
|
85
94
|
checkbox.checked = checked;
|
@@ -87,36 +96,36 @@ module SolidQueueMonitor
|
|
87
96
|
selectAllHeader.checked = checked;
|
88
97
|
updateButtonState();
|
89
98
|
}
|
90
|
-
|
99
|
+
#{' '}
|
91
100
|
selectAllHeader.addEventListener('change', function() {
|
92
101
|
toggleAll(this.checked);
|
93
102
|
});
|
94
|
-
|
103
|
+
#{' '}
|
95
104
|
checkboxes.forEach(checkbox => {
|
96
105
|
checkbox.addEventListener('change', function() {
|
97
106
|
updateButtonState();
|
98
|
-
|
107
|
+
#{' '}
|
99
108
|
// Update select all checkboxes if needed
|
100
109
|
const allChecked = document.querySelectorAll('.job-checkbox:checked').length === checkboxes.length;
|
101
110
|
selectAllHeader.checked = allChecked;
|
102
111
|
});
|
103
112
|
});
|
104
|
-
|
113
|
+
#{' '}
|
105
114
|
// Handle bulk actions
|
106
115
|
retrySelectedBtn.addEventListener('click', function() {
|
107
116
|
const selectedIds = Array.from(document.querySelectorAll('.job-checkbox:checked')).map(cb => cb.value);
|
108
117
|
if (selectedIds.length === 0) return;
|
109
|
-
|
118
|
+
#{' '}
|
110
119
|
if (confirm('Are you sure you want to retry the selected jobs?')) {
|
111
120
|
form.action = '#{retry_failed_jobs_path}';
|
112
|
-
|
121
|
+
#{' '}
|
113
122
|
// Add a special flag to indicate this should redirect properly
|
114
123
|
const redirectInput = document.createElement('input');
|
115
124
|
redirectInput.type = 'hidden';
|
116
125
|
redirectInput.name = 'redirect_cleanly';
|
117
126
|
redirectInput.value = 'true';
|
118
127
|
form.appendChild(redirectInput);
|
119
|
-
|
128
|
+
#{' '}
|
120
129
|
// Add selected IDs as hidden inputs
|
121
130
|
selectedIds.forEach(id => {
|
122
131
|
const input = document.createElement('input');
|
@@ -125,10 +134,10 @@ module SolidQueueMonitor
|
|
125
134
|
input.value = id;
|
126
135
|
form.appendChild(input);
|
127
136
|
});
|
128
|
-
|
137
|
+
#{' '}
|
129
138
|
// Submit the form and then replace the URL location immediately after
|
130
139
|
form.submit();
|
131
|
-
|
140
|
+
#{' '}
|
132
141
|
// Delay the redirect to give the form time to submit
|
133
142
|
setTimeout(function() {
|
134
143
|
// Reset to the clean URL without query parameters
|
@@ -136,21 +145,21 @@ module SolidQueueMonitor
|
|
136
145
|
}, 100);
|
137
146
|
}
|
138
147
|
});
|
139
|
-
|
148
|
+
#{' '}
|
140
149
|
discardSelectedBtn.addEventListener('click', function() {
|
141
150
|
const selectedIds = Array.from(document.querySelectorAll('.job-checkbox:checked')).map(cb => cb.value);
|
142
151
|
if (selectedIds.length === 0) return;
|
143
|
-
|
152
|
+
#{' '}
|
144
153
|
if (confirm('Are you sure you want to discard the selected jobs?')) {
|
145
154
|
form.action = '#{discard_failed_jobs_path}';
|
146
|
-
|
155
|
+
#{' '}
|
147
156
|
// Add a special flag to indicate this should redirect properly
|
148
157
|
const redirectInput = document.createElement('input');
|
149
158
|
redirectInput.type = 'hidden';
|
150
159
|
redirectInput.name = 'redirect_cleanly';
|
151
160
|
redirectInput.value = 'true';
|
152
161
|
form.appendChild(redirectInput);
|
153
|
-
|
162
|
+
#{' '}
|
154
163
|
// Add selected IDs as hidden inputs
|
155
164
|
selectedIds.forEach(id => {
|
156
165
|
const input = document.createElement('input');
|
@@ -159,10 +168,10 @@ module SolidQueueMonitor
|
|
159
168
|
input.value = id;
|
160
169
|
form.appendChild(input);
|
161
170
|
});
|
162
|
-
|
171
|
+
#{' '}
|
163
172
|
// Submit the form and then replace the URL location immediately after
|
164
173
|
form.submit();
|
165
|
-
|
174
|
+
#{' '}
|
166
175
|
// Delay the redirect to give the form time to submit
|
167
176
|
setTimeout(function() {
|
168
177
|
// Reset to the clean URL without query parameters
|
@@ -170,58 +179,58 @@ module SolidQueueMonitor
|
|
170
179
|
}, 100);
|
171
180
|
}
|
172
181
|
});
|
173
|
-
|
182
|
+
#{' '}
|
174
183
|
// Initialize button state
|
175
184
|
updateButtonState();
|
176
|
-
|
185
|
+
#{' '}
|
177
186
|
// Global function for retry action
|
178
187
|
window.submitRetryForm = function(id) {
|
179
188
|
const form = document.createElement('form');
|
180
189
|
form.method = 'post';
|
181
|
-
form.action = '#{retry_failed_job_path(id:
|
190
|
+
form.action = '#{retry_failed_job_path(id: 'PLACEHOLDER')}';
|
182
191
|
form.action = form.action.replace('PLACEHOLDER', id);
|
183
192
|
form.style.display = 'none';
|
184
|
-
|
193
|
+
#{' '}
|
185
194
|
// Add a special flag to indicate this should redirect properly
|
186
195
|
const redirectInput = document.createElement('input');
|
187
196
|
redirectInput.type = 'hidden';
|
188
197
|
redirectInput.name = 'redirect_cleanly';
|
189
198
|
redirectInput.value = 'true';
|
190
199
|
form.appendChild(redirectInput);
|
191
|
-
|
200
|
+
#{' '}
|
192
201
|
document.body.appendChild(form);
|
193
|
-
|
202
|
+
#{' '}
|
194
203
|
// Submit the form and then replace the URL location immediately after
|
195
204
|
form.submit();
|
196
|
-
|
205
|
+
#{' '}
|
197
206
|
// Delay the redirect to give the form time to submit
|
198
207
|
setTimeout(function() {
|
199
208
|
// Reset to the clean URL without query parameters
|
200
209
|
window.history.replaceState({}, '', window.location.pathname);
|
201
210
|
}, 100);
|
202
211
|
};
|
203
|
-
|
212
|
+
#{' '}
|
204
213
|
// Global function for discard action
|
205
214
|
window.submitDiscardForm = function(id) {
|
206
215
|
if (confirm('Are you sure you want to discard this job?')) {
|
207
216
|
const form = document.createElement('form');
|
208
217
|
form.method = 'post';
|
209
|
-
form.action = '#{discard_failed_job_path(id:
|
218
|
+
form.action = '#{discard_failed_job_path(id: 'PLACEHOLDER')}';
|
210
219
|
form.action = form.action.replace('PLACEHOLDER', id);
|
211
220
|
form.style.display = 'none';
|
212
|
-
|
221
|
+
#{' '}
|
213
222
|
// Add a special flag to indicate this should redirect properly
|
214
223
|
const redirectInput = document.createElement('input');
|
215
224
|
redirectInput.type = 'hidden';
|
216
225
|
redirectInput.name = 'redirect_cleanly';
|
217
226
|
redirectInput.value = 'true';
|
218
227
|
form.appendChild(redirectInput);
|
219
|
-
|
228
|
+
#{' '}
|
220
229
|
document.body.appendChild(form);
|
221
|
-
|
230
|
+
#{' '}
|
222
231
|
// Submit the form and then replace the URL location immediately after
|
223
232
|
form.submit();
|
224
|
-
|
233
|
+
#{' '}
|
225
234
|
// Delay the redirect to give the form time to submit
|
226
235
|
setTimeout(function() {
|
227
236
|
// Reset to the clean URL without query parameters
|
@@ -237,7 +246,7 @@ module SolidQueueMonitor
|
|
237
246
|
def generate_row(failed_execution)
|
238
247
|
job = failed_execution.job
|
239
248
|
error = parse_error(failed_execution.error)
|
240
|
-
|
249
|
+
|
241
250
|
<<-HTML
|
242
251
|
<tr>
|
243
252
|
<td><input type="checkbox" class="job-checkbox" value="#{failed_execution.id}"></td>
|
@@ -260,14 +269,15 @@ module SolidQueueMonitor
|
|
260
269
|
<pre class="error-backtrace">#{error[:backtrace]}</pre>
|
261
270
|
</details>
|
262
271
|
</td>
|
272
|
+
<td>#{format_arguments(job.arguments)}</td>
|
263
273
|
<td class="actions-cell">
|
264
274
|
<div class="job-actions">
|
265
|
-
<a href="javascript:void(0)"
|
266
|
-
onclick="submitRetryForm(#{failed_execution.id})"
|
275
|
+
<a href="javascript:void(0)"#{' '}
|
276
|
+
onclick="submitRetryForm(#{failed_execution.id})"#{' '}
|
267
277
|
class="action-button retry-button">Retry</a>
|
268
|
-
|
269
|
-
<a href="javascript:void(0)"
|
270
|
-
onclick="submitDiscardForm(#{failed_execution.id})"
|
278
|
+
#{' '}
|
279
|
+
<a href="javascript:void(0)"#{' '}
|
280
|
+
onclick="submitDiscardForm(#{failed_execution.id})"#{' '}
|
271
281
|
class="action-button discard-button">Discard</a>
|
272
282
|
</div>
|
273
283
|
</td>
|
@@ -277,7 +287,7 @@ module SolidQueueMonitor
|
|
277
287
|
|
278
288
|
def parse_error(error)
|
279
289
|
return { message: 'Unknown error', backtrace: '' } unless error
|
280
|
-
|
290
|
+
|
281
291
|
if error.is_a?(String)
|
282
292
|
{ message: error, backtrace: '' }
|
283
293
|
elsif error.is_a?(Hash)
|
@@ -303,4 +313,4 @@ module SolidQueueMonitor
|
|
303
313
|
job.queue_name
|
304
314
|
end
|
305
315
|
end
|
306
|
-
end
|
316
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SolidQueueMonitor
|
2
4
|
class InProgressJobsPresenter < BasePresenter
|
3
5
|
include SolidQueueMonitor::Engine.routes.url_helpers
|
@@ -10,7 +12,8 @@ module SolidQueueMonitor
|
|
10
12
|
end
|
11
13
|
|
12
14
|
def render
|
13
|
-
section_wrapper('In Progress Jobs',
|
15
|
+
section_wrapper('In Progress Jobs',
|
16
|
+
generate_filter_form + generate_table + generate_pagination(@current_page, @total_pages))
|
14
17
|
end
|
15
18
|
|
16
19
|
private
|
@@ -24,6 +27,11 @@ module SolidQueueMonitor
|
|
24
27
|
<input type="text" name="class_name" id="class_name" value="#{@filters[:class_name]}" placeholder="Filter by class name">
|
25
28
|
</div>
|
26
29
|
|
30
|
+
<div class="filter-group">
|
31
|
+
<label for="arguments">Arguments:</label>
|
32
|
+
<input type="text" name="arguments" id="arguments" value="#{@filters[:arguments]}" placeholder="Filter by arguments">
|
33
|
+
</div>
|
34
|
+
|
27
35
|
<div class="filter-actions">
|
28
36
|
<button type="submit" class="filter-button">Apply Filters</button>
|
29
37
|
<a href="#{in_progress_jobs_path}" class="reset-button">Reset</a>
|
@@ -40,6 +48,8 @@ module SolidQueueMonitor
|
|
40
48
|
<thead>
|
41
49
|
<tr>
|
42
50
|
<th>Job</th>
|
51
|
+
<th>Queue</th>
|
52
|
+
<th>Arguments</th>
|
43
53
|
<th>Started At</th>
|
44
54
|
<th>Process ID</th>
|
45
55
|
</tr>
|
@@ -62,10 +72,11 @@ module SolidQueueMonitor
|
|
62
72
|
<span class="job-timestamp">Queued at: #{format_datetime(job.created_at)}</span>
|
63
73
|
</div>
|
64
74
|
</td>
|
75
|
+
<td>#{format_arguments(job.arguments)}</td>
|
65
76
|
<td>#{format_datetime(execution.created_at)}</td>
|
66
77
|
<td>#{execution.process_id}</td>
|
67
78
|
</tr>
|
68
79
|
HTML
|
69
80
|
end
|
70
81
|
end
|
71
|
-
end
|
82
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SolidQueueMonitor
|
2
4
|
class JobsPresenter < BasePresenter
|
3
5
|
include Rails.application.routes.url_helpers
|
@@ -39,6 +41,11 @@ module SolidQueueMonitor
|
|
39
41
|
<input type="text" name="queue_name" id="queue_name" value="#{@filters[:queue_name]}" placeholder="Filter by queue">
|
40
42
|
</div>
|
41
43
|
|
44
|
+
<div class="filter-group">
|
45
|
+
<label for="arguments">Arguments:</label>
|
46
|
+
<input type="text" name="arguments" id="arguments" value="#{@filters[:arguments]}" placeholder="Filter by arguments">
|
47
|
+
</div>
|
48
|
+
|
42
49
|
<div class="filter-group">
|
43
50
|
<label for="status">Status:</label>
|
44
51
|
<select name="status" id="status">
|
@@ -68,6 +75,7 @@ module SolidQueueMonitor
|
|
68
75
|
<th>ID</th>
|
69
76
|
<th>Job</th>
|
70
77
|
<th>Queue</th>
|
78
|
+
<th>Arguments</th>
|
71
79
|
<th>Status</th>
|
72
80
|
<th>Created At</th>
|
73
81
|
<th>Actions</th>
|
@@ -90,6 +98,7 @@ module SolidQueueMonitor
|
|
90
98
|
<td>#{job.id}</td>
|
91
99
|
<td>#{job.class_name}</td>
|
92
100
|
<td>#{job.queue_name}</td>
|
101
|
+
<td>#{format_arguments(job.arguments)}</td>
|
93
102
|
<td><span class='status-badge status-#{status}'>#{status}</span></td>
|
94
103
|
<td>#{format_datetime(job.created_at)}</td>
|
95
104
|
HTML
|
@@ -99,8 +108,8 @@ module SolidQueueMonitor
|
|
99
108
|
# Find the failed execution record for this job
|
100
109
|
failed_execution = SolidQueue::FailedExecution.find_by(job_id: job.id)
|
101
110
|
|
102
|
-
if failed_execution
|
103
|
-
|
111
|
+
row_html += if failed_execution
|
112
|
+
<<-HTML
|
104
113
|
<td class="actions-cell">
|
105
114
|
<div class="job-actions">
|
106
115
|
<form method="post" action="#{retry_failed_job_path(id: failed_execution.id)}" class="inline-form">
|
@@ -115,15 +124,15 @@ module SolidQueueMonitor
|
|
115
124
|
</form>
|
116
125
|
</div>
|
117
126
|
</td>
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
127
|
+
HTML
|
128
|
+
else
|
129
|
+
'<td></td>'
|
130
|
+
end
|
122
131
|
else
|
123
|
-
row_html +=
|
132
|
+
row_html += '<td></td>'
|
124
133
|
end
|
125
134
|
|
126
|
-
row_html +=
|
135
|
+
row_html += '</tr>'
|
127
136
|
row_html
|
128
137
|
end
|
129
138
|
|
@@ -131,4 +140,4 @@ module SolidQueueMonitor
|
|
131
140
|
SolidQueueMonitor::StatusCalculator.new(job).calculate
|
132
141
|
end
|
133
142
|
end
|
134
|
-
end
|
143
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SolidQueueMonitor
|
2
4
|
class QueuesPresenter < BasePresenter
|
3
5
|
def initialize(records)
|
@@ -43,8 +45,6 @@ module SolidQueueMonitor
|
|
43
45
|
HTML
|
44
46
|
end
|
45
47
|
|
46
|
-
private
|
47
|
-
|
48
48
|
def ready_jobs_count(queue_name)
|
49
49
|
SolidQueue::ReadyExecution.where(queue_name: queue_name).count
|
50
50
|
end
|
@@ -55,8 +55,8 @@ module SolidQueueMonitor
|
|
55
55
|
|
56
56
|
def failed_jobs_count(queue_name)
|
57
57
|
SolidQueue::FailedExecution.joins(:job)
|
58
|
-
|
59
|
-
|
58
|
+
.where(solid_queue_jobs: { queue_name: queue_name })
|
59
|
+
.count
|
60
60
|
end
|
61
61
|
end
|
62
|
-
end
|
62
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SolidQueueMonitor
|
2
4
|
class ReadyJobsPresenter < BasePresenter
|
3
5
|
def initialize(jobs, current_page: 1, total_pages: 1, filters: {})
|
@@ -8,7 +10,8 @@ module SolidQueueMonitor
|
|
8
10
|
end
|
9
11
|
|
10
12
|
def render
|
11
|
-
section_wrapper('Ready Jobs',
|
13
|
+
section_wrapper('Ready Jobs',
|
14
|
+
generate_filter_form + generate_table + generate_pagination(@current_page, @total_pages))
|
12
15
|
end
|
13
16
|
|
14
17
|
private
|
@@ -27,6 +30,11 @@ module SolidQueueMonitor
|
|
27
30
|
<input type="text" name="queue_name" id="queue_name" value="#{@filters[:queue_name]}" placeholder="Filter by queue">
|
28
31
|
</div>
|
29
32
|
|
33
|
+
<div class="filter-group">
|
34
|
+
<label for="arguments">Arguments:</label>
|
35
|
+
<input type="text" name="arguments" id="arguments" value="#{@filters[:arguments]}" placeholder="Filter by arguments">
|
36
|
+
</div>
|
37
|
+
|
30
38
|
<div class="filter-actions">
|
31
39
|
<button type="submit" class="filter-button">Apply Filters</button>
|
32
40
|
<a href="#{ready_jobs_path}" class="reset-button">Reset</a>
|
@@ -69,4 +77,4 @@ module SolidQueueMonitor
|
|
69
77
|
HTML
|
70
78
|
end
|
71
79
|
end
|
72
|
-
end
|
80
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module SolidQueueMonitor
|
2
4
|
class RecurringJobsPresenter < BasePresenter
|
3
5
|
include Rails.application.routes.url_helpers
|
@@ -11,7 +13,8 @@ module SolidQueueMonitor
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def render
|
14
|
-
section_wrapper('Recurring Jobs',
|
16
|
+
section_wrapper('Recurring Jobs',
|
17
|
+
generate_filter_form + generate_table + generate_pagination(@current_page, @total_pages))
|
15
18
|
end
|
16
19
|
|
17
20
|
private
|
@@ -74,4 +77,4 @@ module SolidQueueMonitor
|
|
74
77
|
HTML
|
75
78
|
end
|
76
79
|
end
|
77
|
-
end
|
80
|
+
end
|