sidekiq-undertaker 1.1.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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') %>" />