sidekiq-undertaker 1.1.0 → 1.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.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-build.yml +1 -1
  3. data/CHANGELOG.md +104 -0
  4. data/README.md +7 -4
  5. data/assets/Undertaker_demo_1_job_1_error.png +0 -0
  6. data/assets/Undertaker_demo_1_job_all_errors.png +0 -0
  7. data/assets/Undertaker_demo_all_errors.png +0 -0
  8. data/assets/Undertaker_demo_all_jobs.png +0 -0
  9. data/assets/Undertaker_demo_all_jobs_1_error.png +0 -0
  10. data/lib/sidekiq/undertaker/version.rb +1 -1
  11. data/lib/sidekiq/undertaker/web_extension/api_helpers.rb +69 -12
  12. data/lib/sidekiq/undertaker/web_extension.rb +8 -1
  13. data/sidekiq-undertaker.gemspec +3 -1
  14. data/spec/fixtures/approvals/sidekiq_undertaker_webextension/show_filter/when_filter_page_is_called/behaves_like_a_page/the_displayed_page_is_correct_for_sidekiqv6.approved.txt +29 -15
  15. data/spec/fixtures/approvals/sidekiq_undertaker_webextension/show_filter/when_job_classbucket_page_is_called/behaves_like_a_page/the_displayed_page_is_correct_for_sidekiqv6.approved.txt +13 -15
  16. data/spec/fixtures/approvals/sidekiq_undertaker_webextension/show_filter/when_job_classbucket_page_is_polled/behaves_like_a_page/the_displayed_page_is_correct_for_sidekiqv6.approved.txt +13 -15
  17. data/spec/fixtures/approvals/sidekiq_undertaker_webextension/show_morgue/when_job_classerrorbucket_is_called/with_all_failures_and_errors/behaves_like_a_page/the_displayed_page_is_correct_for_sidekiqv6.approved.txt +19 -15
  18. data/spec/fixtures/approvals/sidekiq_undertaker_webextension/show_morgue/when_job_classerrorbucket_is_called/with_specific_job_class_and_a_specific_error/behaves_like_a_page/the_displayed_page_is_correct_for_sidekiqv6.approved.txt +19 -15
  19. data/spec/fixtures/approvals/sidekiq_undertaker_webextension/show_morgue/when_job_classerrorbucket_is_called/with_specific_job_class_and_a_specific_error/with_pagination/behaves_like_a_page/the_displayed_page_is_correct_for_sidekiqv6.approved.txt +19 -15
  20. data/spec/sidekiq/undertaker/job_distributor_spec.rb +3 -3
  21. data/spec/sidekiq/undertaker/job_filter_spec.rb +3 -3
  22. data/spec/sidekiq/undertaker/web_extension_spec.rb +115 -0
  23. data/web/locales/en.yml +2 -0
  24. data/web/views/filter.erb +16 -0
  25. data/web/views/morgue.erb +6 -0
  26. metadata +31 -14
  27. data/Demo_Filter.png +0 -0
  28. data/Demo_Job_Filter.png +0 -0
  29. data/Demo_Morgue_1_Job.png +0 -0
  30. data/Demo_Morgue_all.png +0 -0
@@ -18,7 +18,7 @@
18
18
  <meta name="google" content="notranslate" />
19
19
 
20
20
  </head>
21
- <body class="admin" data-poll-path="" data-locale="en">
21
+ <body class="admin" data-locale="en">
22
22
  <div class="navbar navbar-default navbar-fixed-top">
23
23
  <div class="container-fluid">
24
24
  <div class="navbar-header" data-navbar="static">
@@ -29,9 +29,8 @@
29
29
  </button>
30
30
  <div class="navbar-toggle collapsed navbar-livereload">
31
31
 
32
-
33
- <a id="live-poll" class="btn btn-primary" href="/sidekiq/undertaker/morgue/all/all/total_dead?poll=true">Live Poll</a>
34
-
32
+ <a class="live-poll-start live-poll btn btn-primary">Live Poll</a>
33
+ <a class="live-poll-stop live-poll btn btn-primary active">Stop Polling</a>
35
34
 
36
35
 
37
36
 
@@ -95,9 +94,8 @@
95
94
  <li class="navbar-livereload">
96
95
  <div class="poll-wrapper">
97
96
 
98
-
99
- <a id="live-poll" class="btn btn-primary" href="/sidekiq/undertaker/morgue/all/all/total_dead?poll=true">Live Poll</a>
100
-
97
+ <a class="live-poll-start live-poll btn btn-primary">Live Poll</a>
98
+ <a class="live-poll-stop live-poll btn btn-primary active">Stop Polling</a>
101
99
 
102
100
 
103
101
 
@@ -114,40 +112,40 @@
114
112
  <div class="col-sm-12 summary_bar">
115
113
  <ul class="list-unstyled summary row">
116
114
  <li class="processed col-sm-1">
117
- <span class="count">0</span>
115
+ <span id="txtProcessed" class="count">0</span>
118
116
  <span class="desc">Processed</span>
119
117
  </li>
120
118
  <li class="failed col-sm-1">
121
- <span class="count">0</span>
119
+ <span id="txtFailed" class="count">0</span>
122
120
  <span class="desc">Failed</span>
123
121
  </li>
124
122
  <li class="busy col-sm-1">
125
123
  <a href="/sidekiq/busy">
126
- <span class="count">0</span>
124
+ <span id="txtBusy" class="count">0</span>
127
125
  <span class="desc">Busy</span>
128
126
  </a>
129
127
  </li>
130
128
  <li class="enqueued col-sm-1">
131
129
  <a href="/sidekiq/queues">
132
- <span class="count">0</span>
130
+ <span id="txtEnqueued" class="count">0</span>
133
131
  <span class="desc">Enqueued</span>
134
132
  </a>
135
133
  </li>
136
134
  <li class="retries col-sm-1">
137
135
  <a href="/sidekiq/retries">
138
- <span class="count">0</span>
136
+ <span id="txtRetries" class="count">0</span>
139
137
  <span class="desc">Retries</span>
140
138
  </a>
141
139
  </li>
142
140
  <li class="scheduled col-sm-1">
143
141
  <a href="/sidekiq/scheduled">
144
- <span class="count">0</span>
142
+ <span id="txtScheduled" class="count">0</span>
145
143
  <span class="desc">Scheduled</span>
146
144
  </a>
147
145
  </li>
148
146
  <li class="dead col-sm-1">
149
147
  <a href="/sidekiq/morgue">
150
- <span class="count">4</span>
148
+ <span id="txtDead" class="count">4</span>
151
149
  <span class="desc">Dead</span>
152
150
  </a>
153
151
  </li>
@@ -278,6 +276,7 @@
278
276
 
279
277
  </table>
280
278
  <input class="btn btn-primary btn-xs pull-left" type="submit" name="retry" value="Revive" />
279
+ <input class="btn btn-secondary btn-xs pull-left" type="submit" name="export" value="Export" />
281
280
  <input class="btn btn-danger btn-xs pull-left" type="submit" name="delete" value="Bury" />
282
281
  </form>
283
282
 
@@ -286,6 +285,11 @@
286
285
  <input class="btn btn-danger btn-xs pull-right" type="submit" name="delete" value="Bury All" data-confirm="Are you sure?" />
287
286
  </form>
288
287
 
288
+ <form action="/sidekiq/undertaker/morgue/all/all/total_dead/export" method="post">
289
+ <input type='hidden' name='authenticity_token' value='stubbed-csrf-token'/>
290
+ <input class="btn btn-secondary btn-xs pull-right" type="submit" name="export" value="Export All" />
291
+ </form>
292
+
289
293
  <form action="/sidekiq/undertaker/morgue/all/all/total_dead/retry" method="post">
290
294
  <input type='hidden' name='authenticity_token' value='stubbed-csrf-token'/>
291
295
  <input class="btn btn-danger btn-xs pull-right" type="submit" name="retry" value="Revive All" data-confirm="Are you sure?" />
@@ -307,7 +311,7 @@
307
311
  <p class="navbar-text redis-url" title="redis://127.0.0.1:6379/0">redis://127.0.0.1:6379/0</p>
308
312
  </li>
309
313
  <li>
310
- <p class="navbar-text server-utc-time">20:57:00 UTC</p>
314
+ <p id="serverUtcTime" class="navbar-text server-utc-time">20:57:00 UTC</p>
311
315
  </li>
312
316
  <li>
313
317
  <p class="navbar-text"><a style="color: gray;" href="https://github.com/mperham/sidekiq/wiki">docs</a></p>
@@ -18,7 +18,7 @@
18
18
  <meta name="google" content="notranslate" />
19
19
 
20
20
  </head>
21
- <body class="admin" data-poll-path="" data-locale="en">
21
+ <body class="admin" data-locale="en">
22
22
  <div class="navbar navbar-default navbar-fixed-top">
23
23
  <div class="container-fluid">
24
24
  <div class="navbar-header" data-navbar="static">
@@ -29,9 +29,8 @@
29
29
  </button>
30
30
  <div class="navbar-toggle collapsed navbar-livereload">
31
31
 
32
-
33
- <a id="live-poll" class="btn btn-primary" href="/sidekiq/undertaker/morgue/HardWorker/RuntimeError/1_hour?poll=true">Live Poll</a>
34
-
32
+ <a class="live-poll-start live-poll btn btn-primary">Live Poll</a>
33
+ <a class="live-poll-stop live-poll btn btn-primary active">Stop Polling</a>
35
34
 
36
35
 
37
36
 
@@ -95,9 +94,8 @@
95
94
  <li class="navbar-livereload">
96
95
  <div class="poll-wrapper">
97
96
 
98
-
99
- <a id="live-poll" class="btn btn-primary" href="/sidekiq/undertaker/morgue/HardWorker/RuntimeError/1_hour?poll=true">Live Poll</a>
100
-
97
+ <a class="live-poll-start live-poll btn btn-primary">Live Poll</a>
98
+ <a class="live-poll-stop live-poll btn btn-primary active">Stop Polling</a>
101
99
 
102
100
 
103
101
 
@@ -114,40 +112,40 @@
114
112
  <div class="col-sm-12 summary_bar">
115
113
  <ul class="list-unstyled summary row">
116
114
  <li class="processed col-sm-1">
117
- <span class="count">0</span>
115
+ <span id="txtProcessed" class="count">0</span>
118
116
  <span class="desc">Processed</span>
119
117
  </li>
120
118
  <li class="failed col-sm-1">
121
- <span class="count">0</span>
119
+ <span id="txtFailed" class="count">0</span>
122
120
  <span class="desc">Failed</span>
123
121
  </li>
124
122
  <li class="busy col-sm-1">
125
123
  <a href="/sidekiq/busy">
126
- <span class="count">0</span>
124
+ <span id="txtBusy" class="count">0</span>
127
125
  <span class="desc">Busy</span>
128
126
  </a>
129
127
  </li>
130
128
  <li class="enqueued col-sm-1">
131
129
  <a href="/sidekiq/queues">
132
- <span class="count">0</span>
130
+ <span id="txtEnqueued" class="count">0</span>
133
131
  <span class="desc">Enqueued</span>
134
132
  </a>
135
133
  </li>
136
134
  <li class="retries col-sm-1">
137
135
  <a href="/sidekiq/retries">
138
- <span class="count">0</span>
136
+ <span id="txtRetries" class="count">0</span>
139
137
  <span class="desc">Retries</span>
140
138
  </a>
141
139
  </li>
142
140
  <li class="scheduled col-sm-1">
143
141
  <a href="/sidekiq/scheduled">
144
- <span class="count">0</span>
142
+ <span id="txtScheduled" class="count">0</span>
145
143
  <span class="desc">Scheduled</span>
146
144
  </a>
147
145
  </li>
148
146
  <li class="dead col-sm-1">
149
147
  <a href="/sidekiq/morgue">
150
- <span class="count">4</span>
148
+ <span id="txtDead" class="count">4</span>
151
149
  <span class="desc">Dead</span>
152
150
  </a>
153
151
  </li>
@@ -236,6 +234,7 @@
236
234
 
237
235
  </table>
238
236
  <input class="btn btn-primary btn-xs pull-left" type="submit" name="retry" value="Revive" />
237
+ <input class="btn btn-secondary btn-xs pull-left" type="submit" name="export" value="Export" />
239
238
  <input class="btn btn-danger btn-xs pull-left" type="submit" name="delete" value="Bury" />
240
239
  </form>
241
240
 
@@ -244,6 +243,11 @@
244
243
  <input class="btn btn-danger btn-xs pull-right" type="submit" name="delete" value="Bury All" data-confirm="Are you sure?" />
245
244
  </form>
246
245
 
246
+ <form action="/sidekiq/undertaker/morgue/HardWorker/RuntimeError/1_hour/export" method="post">
247
+ <input type='hidden' name='authenticity_token' value='stubbed-csrf-token'/>
248
+ <input class="btn btn-secondary btn-xs pull-right" type="submit" name="export" value="Export All" />
249
+ </form>
250
+
247
251
  <form action="/sidekiq/undertaker/morgue/HardWorker/RuntimeError/1_hour/retry" method="post">
248
252
  <input type='hidden' name='authenticity_token' value='stubbed-csrf-token'/>
249
253
  <input class="btn btn-danger btn-xs pull-right" type="submit" name="retry" value="Revive All" data-confirm="Are you sure?" />
@@ -265,7 +269,7 @@
265
269
  <p class="navbar-text redis-url" title="redis://127.0.0.1:6379/0">redis://127.0.0.1:6379/0</p>
266
270
  </li>
267
271
  <li>
268
- <p class="navbar-text server-utc-time">20:57:00 UTC</p>
272
+ <p id="serverUtcTime" class="navbar-text server-utc-time">20:57:00 UTC</p>
269
273
  </li>
270
274
  <li>
271
275
  <p class="navbar-text"><a style="color: gray;" href="https://github.com/mperham/sidekiq/wiki">docs</a></p>
@@ -18,7 +18,7 @@
18
18
  <meta name="google" content="notranslate" />
19
19
 
20
20
  </head>
21
- <body class="admin" data-poll-path="" data-locale="en">
21
+ <body class="admin" data-locale="en">
22
22
  <div class="navbar navbar-default navbar-fixed-top">
23
23
  <div class="container-fluid">
24
24
  <div class="navbar-header" data-navbar="static">
@@ -29,9 +29,8 @@
29
29
  </button>
30
30
  <div class="navbar-toggle collapsed navbar-livereload">
31
31
 
32
-
33
- <a id="live-poll" class="btn btn-primary" href="/sidekiq/undertaker/morgue/HardWorker/RuntimeError/1_hour?poll=true">Live Poll</a>
34
-
32
+ <a class="live-poll-start live-poll btn btn-primary">Live Poll</a>
33
+ <a class="live-poll-stop live-poll btn btn-primary active">Stop Polling</a>
35
34
 
36
35
 
37
36
 
@@ -95,9 +94,8 @@
95
94
  <li class="navbar-livereload">
96
95
  <div class="poll-wrapper">
97
96
 
98
-
99
- <a id="live-poll" class="btn btn-primary" href="/sidekiq/undertaker/morgue/HardWorker/RuntimeError/1_hour?poll=true">Live Poll</a>
100
-
97
+ <a class="live-poll-start live-poll btn btn-primary">Live Poll</a>
98
+ <a class="live-poll-stop live-poll btn btn-primary active">Stop Polling</a>
101
99
 
102
100
 
103
101
 
@@ -114,40 +112,40 @@
114
112
  <div class="col-sm-12 summary_bar">
115
113
  <ul class="list-unstyled summary row">
116
114
  <li class="processed col-sm-1">
117
- <span class="count">0</span>
115
+ <span id="txtProcessed" class="count">0</span>
118
116
  <span class="desc">Processed</span>
119
117
  </li>
120
118
  <li class="failed col-sm-1">
121
- <span class="count">0</span>
119
+ <span id="txtFailed" class="count">0</span>
122
120
  <span class="desc">Failed</span>
123
121
  </li>
124
122
  <li class="busy col-sm-1">
125
123
  <a href="/sidekiq/busy">
126
- <span class="count">0</span>
124
+ <span id="txtBusy" class="count">0</span>
127
125
  <span class="desc">Busy</span>
128
126
  </a>
129
127
  </li>
130
128
  <li class="enqueued col-sm-1">
131
129
  <a href="/sidekiq/queues">
132
- <span class="count">0</span>
130
+ <span id="txtEnqueued" class="count">0</span>
133
131
  <span class="desc">Enqueued</span>
134
132
  </a>
135
133
  </li>
136
134
  <li class="retries col-sm-1">
137
135
  <a href="/sidekiq/retries">
138
- <span class="count">0</span>
136
+ <span id="txtRetries" class="count">0</span>
139
137
  <span class="desc">Retries</span>
140
138
  </a>
141
139
  </li>
142
140
  <li class="scheduled col-sm-1">
143
141
  <a href="/sidekiq/scheduled">
144
- <span class="count">0</span>
142
+ <span id="txtScheduled" class="count">0</span>
145
143
  <span class="desc">Scheduled</span>
146
144
  </a>
147
145
  </li>
148
146
  <li class="dead col-sm-1">
149
147
  <a href="/sidekiq/morgue">
150
- <span class="count">54</span>
148
+ <span id="txtDead" class="count">54</span>
151
149
  <span class="desc">Dead</span>
152
150
  </a>
153
151
  </li>
@@ -1262,6 +1260,7 @@
1262
1260
 
1263
1261
  </table>
1264
1262
  <input class="btn btn-primary btn-xs pull-left" type="submit" name="retry" value="Revive" />
1263
+ <input class="btn btn-secondary btn-xs pull-left" type="submit" name="export" value="Export" />
1265
1264
  <input class="btn btn-danger btn-xs pull-left" type="submit" name="delete" value="Bury" />
1266
1265
  </form>
1267
1266
 
@@ -1270,6 +1269,11 @@
1270
1269
  <input class="btn btn-danger btn-xs pull-right" type="submit" name="delete" value="Bury All" data-confirm="Are you sure?" />
1271
1270
  </form>
1272
1271
 
1272
+ <form action="/sidekiq/undertaker/morgue/HardWorker/RuntimeError/1_hour/export" method="post">
1273
+ <input type='hidden' name='authenticity_token' value='stubbed-csrf-token'/>
1274
+ <input class="btn btn-secondary btn-xs pull-right" type="submit" name="export" value="Export All" />
1275
+ </form>
1276
+
1273
1277
  <form action="/sidekiq/undertaker/morgue/HardWorker/RuntimeError/1_hour/retry" method="post">
1274
1278
  <input type='hidden' name='authenticity_token' value='stubbed-csrf-token'/>
1275
1279
  <input class="btn btn-danger btn-xs pull-right" type="submit" name="retry" value="Revive All" data-confirm="Are you sure?" />
@@ -1291,7 +1295,7 @@
1291
1295
  <p class="navbar-text redis-url" title="redis://127.0.0.1:6379/0">redis://127.0.0.1:6379/0</p>
1292
1296
  </li>
1293
1297
  <li>
1294
- <p class="navbar-text server-utc-time">20:57:00 UTC</p>
1298
+ <p id="serverUtcTime" class="navbar-text server-utc-time">20:57:00 UTC</p>
1295
1299
  </li>
1296
1300
  <li>
1297
1301
  <p class="navbar-text"><a style="color: gray;" href="https://github.com/mperham/sidekiq/wiki">docs</a></p>
@@ -45,21 +45,21 @@ module Sidekiq
45
45
  let(:dead_job2) do
46
46
  DeadJob.new(
47
47
  job: job2, # 'A', 'E1'
48
- time_elapsed_since_failure: 10 + 60 * 60,
48
+ time_elapsed_since_failure: 10 + (60 * 60),
49
49
  bucket_name: "3_hours"
50
50
  )
51
51
  end
52
52
  let(:dead_job3) do
53
53
  DeadJob.new(
54
54
  job: job3, # 'B', 'E1'
55
- time_elapsed_since_failure: 10 + 60 * 60,
55
+ time_elapsed_since_failure: 10 + (60 * 60),
56
56
  bucket_name: "3_hours"
57
57
  )
58
58
  end
59
59
  let(:dead_job4) do
60
60
  DeadJob.new(
61
61
  job: job4, # 'B', 'E2'
62
- time_elapsed_since_failure: 10 + 60 * 60 * 24,
62
+ time_elapsed_since_failure: 10 + (60 * 60 * 24),
63
63
  bucket_name: "1_day"
64
64
  )
65
65
  end
@@ -9,7 +9,7 @@ module Sidekiq
9
9
  let(:job1) do
10
10
  instance_double(Sidekiq::JobRecord, item: {
11
11
  "class" => "HardWorkTask",
12
- "failed_at" => Time.now.to_i - 5 * 60,
12
+ "failed_at" => Time.now.to_i - (5 * 60),
13
13
  "error_class" => "NoMethodError"
14
14
  })
15
15
  end
@@ -17,7 +17,7 @@ module Sidekiq
17
17
  let(:job2) do
18
18
  instance_double(Sidekiq::JobRecord, item: {
19
19
  "class" => "HardWorkTask",
20
- "failed_at" => Time.now.to_i - 2 * 60 * 60,
20
+ "failed_at" => Time.now.to_i - (2 * 60 * 60),
21
21
  "error_class" => "RandomError"
22
22
  })
23
23
  end
@@ -25,7 +25,7 @@ module Sidekiq
25
25
  let(:job3) do
26
26
  instance_double(Sidekiq::JobRecord, item: {
27
27
  "class" => "LazyWorkTask",
28
- "failed_at" => Time.now.to_i - 2 * 60 * 60,
28
+ "failed_at" => Time.now.to_i - (2 * 60 * 60),
29
29
  "error_class" => "NoMethodError"
30
30
  })
31
31
  end
@@ -6,6 +6,7 @@ require "sidekiq/api"
6
6
  require "sidekiq/web"
7
7
  require "sinatra"
8
8
  require "rack/test"
9
+ require "stringio"
9
10
 
10
11
  module Sidekiq
11
12
  # rubocop:disable Metrics/ModuleLength
@@ -205,6 +206,67 @@ module Sidekiq
205
206
  end
206
207
  end
207
208
 
209
+ describe "import" do
210
+ subject { post "/undertaker/import_jobs", "upload_file" => file }
211
+
212
+ let(:file) do
213
+ Rack::Test::UploadedFile.new(StringIO.new(file_content), file_content_type, original_filename: file_name)
214
+ end
215
+ let(:job) do
216
+ opts = default_job_opts.merge({ "class" => "SuperHardWorking" })
217
+
218
+ build_job(opts)
219
+ end
220
+
221
+ context "when the file is valid" do
222
+ let(:file_content) { [job.item].to_json }
223
+ let(:file_name) { "jobs.json" }
224
+ let(:file_content_type) { "application/json" }
225
+
226
+ it "redirects the response" do
227
+ subject
228
+ expect(last_response.status).to eq 302
229
+ end
230
+
231
+ it "adds the jobs to the deadset" do
232
+ expect { subject }.to change { Sidekiq::DeadSet.new.size }.from(4).to(5)
233
+ end
234
+ end
235
+
236
+ context "when the file type is not valid" do
237
+ let(:file_content) { "" }
238
+ let(:file_name) { "jobs.zip" }
239
+ let(:file_content_type) { "application/zip" }
240
+
241
+ it "returns status 400" do
242
+ subject
243
+ expect(last_response.status).to eq 400
244
+ end
245
+ end
246
+
247
+ context "when the file type is a json but not a Sidekiq Job" do
248
+ let(:file_content) { "{am_i_a_job: \"no\"}" }
249
+ let(:file_name) { "jobs.json" }
250
+ let(:file_content_type) { "application/json" }
251
+
252
+ it "returns status 400" do
253
+ subject
254
+ expect(last_response.status).to eq 400
255
+ end
256
+ end
257
+
258
+ context "when the content of the file is not a json" do
259
+ let(:file_content) { "DEFINETLY NOT A JSON" }
260
+ let(:file_name) { "jobs.json" }
261
+ let(:file_content_type) { "application/json" }
262
+
263
+ it "returns status 400" do
264
+ subject
265
+ expect(last_response.status).to eq 400
266
+ end
267
+ end
268
+ end
269
+
208
270
  describe "retry" do
209
271
  context "when job class, error and bucket are given" do
210
272
  subject { post "/undertaker/morgue/HardWorker/RuntimeError/1_hour/retry" }
@@ -262,6 +324,50 @@ module Sidekiq
262
324
  end
263
325
  end
264
326
 
327
+ describe "export" do
328
+ context "when job class, error and bucket are given" do
329
+ subject { post "/undertaker/morgue/HardWorker/RuntimeError/1_hour/export" }
330
+
331
+ let(:expected_redirect_url) { "http://example.org/undertaker/morgue/HardWorker/RuntimeError/1_hour" }
332
+ let(:expected_content_disposition_header) { "attachment; filename=\"2018-12-16_20-57.json\"" }
333
+
334
+ let(:params) do
335
+ { "job_class" => "HardWorker", "error_class" => "RuntimeError", "bucket_name" => "1_hour" }
336
+ end
337
+ let(:dead_jobs_set) { [dead_job1, dead_job2] }
338
+ let(:dead_job1) { Sidekiq::Undertaker::DeadJob.to_dead_job(Sidekiq::DeadSet.new.find_job(jid1)) }
339
+ let(:dead_job2) { Sidekiq::Undertaker::DeadJob.to_dead_job(Sidekiq::DeadSet.new.find_job(jid2)) }
340
+
341
+ before do
342
+ allow(Sidekiq::Undertaker::JobFilter).to receive(:filter_dead_jobs).with(params)
343
+ .and_return(dead_jobs_set)
344
+ end
345
+
346
+ it "exports the dead jobs" do
347
+ subject
348
+ expect(last_response.status).to eq 200
349
+ expect(last_response.content_type).to eq "application/json"
350
+ expect(last_response.headers["Content-Disposition"]).to eq expected_content_disposition_header
351
+ expect(last_response.body).to eq dead_jobs_set.map { |t| t.job.item }.to_json
352
+ end
353
+
354
+ context "when there are more jobs than the current CHUNK_SIZE" do
355
+ before do
356
+ stub_const("Sidekiq::Undertaker::WebExtension::APIHelpers::EXPORT_CHUNK_SIZE", 1)
357
+ end
358
+
359
+ let(:expected_content_disposition_header) { "attachment; filename=\"HardWorker_2018-12-16_20-57.zip\"" }
360
+
361
+ it "exports the dead jobs" do
362
+ subject
363
+ expect(last_response.status).to eq 200
364
+ expect(last_response.content_type).to eq "application/zip"
365
+ expect(last_response.headers["Content-Disposition"]).to eq expected_content_disposition_header
366
+ end
367
+ end
368
+ end
369
+ end
370
+
265
371
  describe "specific jobs" do
266
372
  let(:dead_job) { Sidekiq::DeadSet.new.find_job(jid1) }
267
373
 
@@ -282,6 +388,15 @@ module Sidekiq
282
388
  post "/undertaker/morgue", "key[]=#{job_refs[0]}&retry=Retry+Now"
283
389
  end
284
390
 
391
+ it "exports specific dead job now" do
392
+ post "/undertaker/morgue", "key[]=#{job_refs[0]}&export=now"
393
+
394
+ expect(last_response.status).to eq 200
395
+ expect(last_response.content_type).to eq "application/json"
396
+ expect(last_response.headers["Content-Disposition"]).to eq "attachment; filename=\"2018-12-16_20-57.json\""
397
+ expect(last_response.body).to eq [dead_job.item].to_json
398
+ end
399
+
285
400
  it "redirects on specific retry post" do
286
401
  post("/undertaker/morgue",
287
402
  "key[]=#{job_refs[0]}&retry=Retry+Now",
data/web/locales/en.yml CHANGED
@@ -3,3 +3,5 @@ en:
3
3
  UndertakerBuryAll: Bury All
4
4
  UndertakerRevive: Revive
5
5
  UndertakerReviveAll: Revive All
6
+ UndertakerExport: Export
7
+ UndertakerExportAll: Export All
data/web/views/filter.erb CHANGED
@@ -32,3 +32,19 @@
32
32
  </tr>
33
33
  <% end %>
34
34
  </table>
35
+
36
+ <header class="row header">
37
+ <div class="col-sm-12">
38
+ <h3>
39
+ Import Jobs
40
+ </h3>
41
+ </div>
42
+ </header>
43
+
44
+ <form enctype="multipart/form-data" method="post" action='<%="#{root_path}undertaker/import_jobs"%>'>
45
+ <%= respond_to?(:csrf_tag) && csrf_tag %>
46
+ <input type="file" id=upload_file" name="upload_file" >
47
+ <button class="btn btn-danger" style="margin-top: 10px" type="submit">
48
+ Import
49
+ </button>
50
+ </form>
data/web/views/morgue.erb CHANGED
@@ -56,6 +56,7 @@
56
56
  <% end %>
57
57
  </table>
58
58
  <input class="btn btn-primary btn-xs pull-left" type="submit" name="retry" value="<%= t('UndertakerRevive') %>" />
59
+ <input class="btn btn-secondary btn-xs pull-left" type="submit" name="export" value="<%= t('UndertakerExport') %>" />
59
60
  <input class="btn btn-danger btn-xs pull-left" type="submit" name="delete" value="<%= t('UndertakerBury') %>" />
60
61
  </form>
61
62
 
@@ -64,6 +65,11 @@
64
65
  <input class="btn btn-danger btn-xs pull-right" type="submit" name="delete" value="<%= t('UndertakerBuryAll') %>" data-confirm="<%= t('AreYouSure') %>" />
65
66
  </form>
66
67
 
68
+ <form action="<%= "#{root_path}undertaker/morgue/#{@req_job_class}/#{@req_error_class}/#{@req_bucket_name}/export" %>" method="post">
69
+ <%= respond_to?(:csrf_tag) && csrf_tag %>
70
+ <input class="btn btn-secondary btn-xs pull-right" type="submit" name="export" value="<%= t('UndertakerExportAll') %>" />
71
+ </form>
72
+
67
73
  <form action="<%= "#{root_path}undertaker/morgue/#{@req_job_class}/#{@req_error_class}/#{@req_bucket_name}/retry" %>" method="post">
68
74
  <%= respond_to?(:csrf_tag) && csrf_tag %>
69
75
  <input class="btn btn-danger btn-xs pull-right" type="submit" name="retry" value="<%= t('UndertakerReviveAll') %>" data-confirm="<%= t('AreYouSure') %>" />