sidekiq-undertaker 1.1.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-build.yml +31 -3
  3. data/.rubocop.yml +4 -0
  4. data/.rubocop_codeclimate.yml +9 -0
  5. data/CHANGELOG.md +19 -0
  6. data/README.md +8 -6
  7. data/assets/Undertaker_demo_1_job_1_error.png +0 -0
  8. data/assets/Undertaker_demo_1_job_all_errors.png +0 -0
  9. data/assets/Undertaker_demo_all_errors.png +0 -0
  10. data/assets/Undertaker_demo_all_jobs.png +0 -0
  11. data/assets/Undertaker_demo_all_jobs_1_error.png +0 -0
  12. data/lib/sidekiq/undertaker/dead_job.rb +10 -2
  13. data/lib/sidekiq/undertaker/job_distributor.rb +4 -0
  14. data/lib/sidekiq/undertaker/job_filter.rb +7 -2
  15. data/lib/sidekiq/undertaker/version.rb +1 -1
  16. data/lib/sidekiq/undertaker/web_extension/api_helpers.rb +104 -26
  17. data/lib/sidekiq/undertaker/web_extension.rb +17 -8
  18. data/sidekiq-undertaker.gemspec +6 -5
  19. 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 +32 -18
  20. 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 +37 -39
  21. 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 +37 -39
  22. data/spec/fixtures/approvals/sidekiq_undertaker_webextension/show_filter/when_job_classerror_classbucket_page_is_called/behaves_like_a_page/the_displayed_page_is_correct_for_sidekiqv6.approved.txt +231 -0
  23. data/spec/fixtures/approvals/sidekiq_undertaker_webextension/show_filter/when_job_classerror_classbucket_page_is_polled/behaves_like_a_page/the_displayed_page_is_correct_for_sidekiqv6.approved.txt +231 -0
  24. 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 +26 -21
  25. data/spec/fixtures/approvals/sidekiq_undertaker_webextension/show_morgue/when_job_classerrorbucket_is_called/with_job_class_error_and_specific_error_message/behaves_like_a_page/the_displayed_page_is_correct_for_sidekiqv6.approved.txt +284 -0
  26. data/spec/fixtures/approvals/sidekiq_undertaker_webextension/show_morgue/when_job_classerrorbucket_is_called/with_job_class_error_and_specific_error_message/with_pagination/behaves_like_a_page/the_displayed_page_is_correct_for_sidekiqv6.approved.txt +1310 -0
  27. 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 +24 -19
  28. 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 +72 -67
  29. data/spec/sidekiq/undertaker/dead_jobs_spec.rb +7 -4
  30. data/spec/sidekiq/undertaker/job_distributor_spec.rb +41 -19
  31. data/spec/sidekiq/undertaker/job_filter_spec.rb +26 -9
  32. data/spec/sidekiq/undertaker/web_extension_spec.rb +172 -14
  33. data/spec/spec_helper.rb +4 -0
  34. data/web/locales/en.yml +2 -0
  35. data/web/views/filter.erb +17 -1
  36. data/web/views/filter_job_class.erb +8 -8
  37. data/web/views/filter_job_class_error_class.erb +36 -0
  38. data/web/views/morgue.erb +9 -2
  39. metadata +38 -30
  40. data/.travis.yml +0 -51
  41. data/Demo_Filter.png +0 -0
  42. data/Demo_Job_Filter.png +0 -0
  43. data/Demo_Morgue_1_Job.png +0 -0
  44. data/Demo_Morgue_all.png +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 00e0e140904b2a6c034ae86443f4097405c27089bd402faa9c567b9a4ed494ac
4
- data.tar.gz: df53ecae31dbf8ab6535016bee735ab199eec205251e54332bd7e1f221c584f5
3
+ metadata.gz: c9faade9f819fcaff3bc2246d68197d085f6f90b10b82f38290f5a3cc5752f07
4
+ data.tar.gz: c08b3349474057a180eecf4cc64b0bb15f43112b264833bc6f9441b5ee139549
5
5
  SHA512:
6
- metadata.gz: c91f3a2c28ac45514a4b2de47bfbbb9029bf1377daf88ae7aa589f807fb59e6d691fc717f6661041966561d76eb516de80e7cf52a9e8bb78a6c7ac8556b9ac5b
7
- data.tar.gz: 57177fcdb514ab963e3b67917938b8571e16f0209156505fd1f20cc32bf1f787f2922245406f8263f3a3d990934bb81fc445acee89329b69ade09025815d2ae7
6
+ metadata.gz: 3d5e1d644890b7ef70348f41faf14961852f76f41dbd3ee9f3fd85a57fc29ee97a3476e2bf224a41f6e76ec17219e7e7ac04abcd6f569fee956883c0b72ad786
7
+ data.tar.gz: df1f8590c18238ed71a20d380510375f0eba41ea07e2cfd49d4f1e2f3bd0d2d8f3aad43b0bb07fcea9d44dbd242ef0551e93bc3b5091d470d39d3e12e86b0fcf
@@ -34,12 +34,15 @@ jobs:
34
34
  - '2.7'
35
35
  - 'jruby'
36
36
  - 'jruby-9.3.2.0'
37
- - 'jruby-9.2.20.0'
37
+ - 'jruby-9.2.20.1'
38
38
  - 'truffleruby'
39
39
  - 'truffleruby+graalvm'
40
40
 
41
+ env:
42
+ JRUBY_OPTS: '--debug'
43
+
41
44
  steps:
42
- - uses: actions/checkout@v2
45
+ - uses: actions/checkout@v3
43
46
  - name: Set up Ruby
44
47
  # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
45
48
  # change this to (see https://github.com/ruby/setup-ruby#versioning):
@@ -48,4 +51,29 @@ jobs:
48
51
  ruby-version: ${{ matrix.ruby-version }}
49
52
  bundler-cache: true # runs 'bundle install' and caches installed gems automatically
50
53
  - name: Run tests
51
- run: bundle exec rspec spec
54
+ run: COVERAGE=true bundle exec rspec
55
+ - name: 'Upload Coverage Report'
56
+ uses: actions/upload-artifact@v3
57
+ if: ${{ matrix.ruby-version == 'ruby' }}
58
+ with:
59
+ name: coverage-report
60
+ path: ./coverage
61
+ retention-days: 1
62
+
63
+ coverage:
64
+ needs: [ test ]
65
+ name: coverage
66
+ runs-on: ubuntu-latest
67
+ steps:
68
+ - uses: actions/checkout@v3
69
+ - name: Download Coverage Report
70
+ uses: actions/download-artifact@v3
71
+ with:
72
+ name: coverage-report
73
+ path: ./coverage
74
+ - uses: paambaati/codeclimate-action@v3.0.0
75
+ env:
76
+ # Set CC_TEST_REPORTER_ID as secret of your repo
77
+ CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}}
78
+ with:
79
+ debug: true
data/.rubocop.yml CHANGED
@@ -7,3 +7,7 @@ inherit_from: .rubocop_todo.yml
7
7
 
8
8
  AllCops:
9
9
  TargetRubyVersion: 2.5
10
+
11
+ Layout/LineLength:
12
+ Exclude:
13
+ - 'sidekiq-undertaker.gemspec'
@@ -1,5 +1,14 @@
1
1
  require:
2
+ - rubocop-rake
2
3
  - rubocop-rspec
4
+
3
5
  inherit_from:
4
6
  - .rt_rubocop_defaults.yml
5
7
  - .rubocop_todo.yml
8
+
9
+ AllCops:
10
+ TargetRubyVersion: 2.5
11
+
12
+ Layout/LineLength:
13
+ Exclude:
14
+ - 'sidekiq-undertaker.gemspec'
data/CHANGELOG.md CHANGED
@@ -2,6 +2,25 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## [1.4.0] - 2022-06-03
6
+ ### Added
7
+ - Added a filter based on error messages
8
+
9
+ ### Changed
10
+ - Updated rubocop to version 1.30
11
+ - Updated rubyzip to version 2.3
12
+
13
+ ### Removed
14
+ - Removed Travis CI integration
15
+
16
+ ## [1.3.0] - 2022-04-27
17
+ ### Added
18
+ - Added option to export and re-import dead jobs
19
+
20
+ ## [1.2.0] - 2022-02-03
21
+ ### Changed
22
+ - Updated sidekiq dependency to 6.4.0
23
+
5
24
  ## [1.1.1] - 2022-02-03
6
25
  ### Added
7
26
  - Added CHANGELOG.md
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Sidekiq::Undertaker
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/sidekiq-undertaker.svg)](https://badge.fury.io/rb/sidekiq-undertaker)
4
- [![Build Status](https://travis-ci.org/ThomasKoppensteiner/sidekiq-undertaker.svg?branch=master)](https://travis-ci.org/ThomasKoppensteiner/sidekiq-undertaker)
4
+ [![Ruby-Build](https://github.com/ThomasKoppensteiner/sidekiq-undertaker/actions/workflows/ruby-build.yml/badge.svg)](https://github.com/ThomasKoppensteiner/sidekiq-undertaker/actions/workflows/ruby-build.yml)
5
5
  [![Code Climate](https://codeclimate.com/github/ThomasKoppensteiner/sidekiq-undertaker.svg)](https://codeclimate.com/github/ThomasKoppensteiner/sidekiq-undertaker)
6
6
  [![Test Coverage](https://api.codeclimate.com/v1/badges/d442eb0a323d8911661f/test_coverage)](https://codeclimate.com/github/ThomasKoppensteiner/sidekiq-undertaker/test_coverage)
7
7
  [![Ruby Style Guide](https://img.shields.io/badge/code_style-rubocop-brightgreen.svg)](https://github.com/rubocop-hq/rubocop)
@@ -45,24 +45,24 @@ Or install it yourself as:
45
45
 
46
46
  The filter page shows a table with time-buckets as columns and rows for each job class.
47
47
 
48
- ![Sidekiq Undertaker](Demo_Filter.png)
48
+ ![Sidekiq Undertaker](assets/Undertaker_demo_all_jobs.png)
49
49
 
50
50
  #### Job Filter View
51
51
 
52
52
  For each job class, you can drill down to view error distribution based on
53
53
  error class.
54
54
 
55
- ![Sidekiq Undertaker](Demo_Job_Filter.png)
55
+ ![Sidekiq Undertaker](assets/Undertaker_demo_1_job_all_errors.png)
56
56
 
57
57
  #### Morgue View
58
58
  Finally, click on the individual error counts to display details of the
59
59
  errors in a list form.
60
60
 
61
- ![Sidekiq Undertaker](Demo_Morgue_1_Job.png)
61
+ ![Sidekiq Undertaker](assets/Undertaker_demo_1_job_1_error.png)
62
62
 
63
63
  The morgue view can, for example, also show an error distribution over all job classes.
64
64
 
65
- ![Sidekiq Undertaker](Demo_Morgue_all.png)
65
+ ![Sidekiq Undertaker](assets/Undertaker_demo_all_jobs_1_error.png)
66
66
 
67
67
  ## Contributing
68
68
 
@@ -81,7 +81,9 @@ this fork was renamed to `sidekiq-undertaker`.
81
81
 
82
82
  The [Sidekiq-Cleaner](https://github.com/HackingHabits/sidekiq-cleaner) gem was originally created by [Madan Thangavelu](https://github.com/HackingHabits).
83
83
  [Tout](https://github.com/Tout/sidekiq-cleaner) and [TheWudu](https://github.com/TheWudu/sidekiq-cleaner) also contributed to it.
84
- Thank you [Zlatko Rednjak](https://github.com/Rednjak) for adding the initial version of the `CHANGELOG.md`.
84
+ [Zlatko Rednjak](https://github.com/Rednjak) added the initial version of the `CHANGELOG.md`.
85
+ [Lucas Dell'Isola](https://github.com/ldellisola) added the export/import feature for dead jobs.
86
+ [thoesl](https://github.com/thoesl) added the error message based filter.
85
87
 
86
88
  For the complete list of network members have a look at the [fork overview](https://github.com/ThomasKoppensteiner/sidekiq-undertaker/network/members).
87
89
 
Binary file
@@ -27,7 +27,7 @@ module Sidekiq
27
27
  end
28
28
  end
29
29
 
30
- attr_reader :job_class, :time_elapsed_since_failure, :error_class, :bucket_name, :job
30
+ attr_reader :job_class, :time_elapsed_since_failure, :error_class, :error_msg, :bucket_name, :job
31
31
 
32
32
  def initialize(args)
33
33
  @job = args.fetch(:job)
@@ -35,12 +35,14 @@ module Sidekiq
35
35
  @bucket_name = args.fetch(:bucket_name)
36
36
  @job_class = args.fetch(:job_class, job.item["class"])
37
37
  @error_class = args.fetch(:error_class, job.item["error_class"])
38
+ @error_msg = shorten_error_msg(args.fetch(:error_message, job.item["error_message"]).to_s)
38
39
  end
39
40
 
40
41
  def ==(other)
41
42
  job_class == other.job_class &&
42
43
  time_elapsed_since_failure == other.time_elapsed_since_failure &&
43
44
  error_class == other.error_class &&
45
+ error_msg == other.error_msg &&
44
46
  bucket_name == other.bucket_name &&
45
47
  job_eql?(other.job)
46
48
  end
@@ -48,7 +50,13 @@ module Sidekiq
48
50
 
49
51
  private
50
52
 
51
- attr_writer :job_class, :time_elapsed_since_failure, :error_class, :bucket_name, :job
53
+ attr_writer :job_class, :time_elapsed_since_failure, :error_class, :error_message, :bucket_name, :job
54
+
55
+ def shorten_error_msg(msg)
56
+ max_error_msg_length = 30
57
+
58
+ msg.length > max_error_msg_length ? "#{msg[0, max_error_msg_length]}..." : msg
59
+ end
52
60
 
53
61
  def job_eql?(other_job) # rubocop:disable Metrics/AbcSize
54
62
  job.jid == other_job.jid &&
@@ -19,6 +19,10 @@ module Sidekiq
19
19
  group_by(:error_class)
20
20
  end
21
21
 
22
+ def group_by_error_msg
23
+ group_by(:error_msg)
24
+ end
25
+
22
26
  private
23
27
 
24
28
  def distribute
@@ -7,7 +7,7 @@ module Sidekiq
7
7
  class JobFilter
8
8
  class << self
9
9
  def filter_dead_jobs(filters = {})
10
- # filters can have keys in (job_class, error_class, bucket_name)
10
+ # filters can have keys in (job_class, error_class, error_msg, bucket_name)
11
11
  dead_jobs = []
12
12
  Sidekiq::Undertaker::DeadJob.for_each do |dead_job|
13
13
  filter_passed = true
@@ -26,7 +26,8 @@ module Sidekiq
26
26
  value.nil? ||
27
27
  total_dead_bucket?(filter, value) ||
28
28
  all_jobs?(filter, value) ||
29
- all_errors?(filter, value)
29
+ all_errors?(filter, value) ||
30
+ all_error_msgs?(filter, value)
30
31
  end
31
32
 
32
33
  def total_dead_bucket?(filter, value)
@@ -40,6 +41,10 @@ module Sidekiq
40
41
  def all_errors?(filter, value)
41
42
  filter == "error_class" && value == "all"
42
43
  end
44
+
45
+ def all_error_msgs?(filter, value)
46
+ filter == "error_msg" && value == "all"
47
+ end
43
48
  end
44
49
  end
45
50
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sidekiq
4
4
  module Undertaker
5
- VERSION = "1.1.1"
5
+ VERSION = "1.4.0"
6
6
  end
7
7
  end
@@ -2,15 +2,21 @@
2
2
 
3
3
  require "sidekiq/undertaker/job_distributor"
4
4
  require "sidekiq/undertaker/job_filter"
5
+ require "base64"
6
+ require "json"
7
+ require "zip"
5
8
 
6
9
  module Sidekiq
7
10
  module Undertaker
8
11
  module WebExtension
12
+ # rubocop:disable Metrics/ModuleLength
9
13
  module APIHelpers
14
+ EXPORT_CHUNK_SIZE = 2000
15
+
10
16
  def show_filter
11
17
  store_request_params
12
18
 
13
- @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(params)
19
+ @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(parsed_params)
14
20
  @distribution = Sidekiq::Undertaker::JobDistributor.new(@dead_jobs).group_by_job_class
15
21
  @total_dead = @dead_jobs.size
16
22
 
@@ -20,17 +26,27 @@ module Sidekiq
20
26
  def show_filter_by_job_class_bucket_name
21
27
  store_request_params
22
28
 
23
- @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(params)
29
+ @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(parsed_params)
24
30
  @distribution = Sidekiq::Undertaker::JobDistributor.new(@dead_jobs).group_by_error_class
25
31
  @total_dead = @dead_jobs.size
26
32
 
27
33
  render_result("filter_job_class.erb")
28
34
  end
29
35
 
30
- def show_undertaker_by_job_class_error_class_bucket_name
36
+ def show_filter_by_job_class_error_class_bucket_name
37
+ store_request_params
38
+
39
+ @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(parsed_params)
40
+ @distribution = Sidekiq::Undertaker::JobDistributor.new(@dead_jobs).group_by_error_msg
41
+ @total_dead = @dead_jobs.size
42
+
43
+ render_result("filter_job_class_error_class.erb")
44
+ end
45
+
46
+ def show_undertaker_by_job_class_error_class_error_msg_bucket_name
31
47
  store_request_params
32
48
 
33
- @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(params)
49
+ @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(parsed_params)
34
50
 
35
51
  # Display dead jobs as list
36
52
  @dead_jobs = @dead_jobs.map(&:job)
@@ -39,7 +55,7 @@ module Sidekiq
39
55
 
40
56
  # Pagination
41
57
  @total_dead = @dead_jobs.size
42
- @current_page = (params[:page] || 1).to_i
58
+ @current_page = (parsed_params[:page] || 1).to_i
43
59
  @count = 50 # per page
44
60
  @dead_jobs = @dead_jobs[((@current_page - 1) * @count), @count]
45
61
 
@@ -52,32 +68,36 @@ module Sidekiq
52
68
  params.delete("job_class")
53
69
  params.delete("bucket_name")
54
70
  params.delete("error_class")
71
+ params.delete("error_msg")
55
72
 
56
73
  render_result("morgue.erb")
57
74
  end
58
75
 
59
76
  def post_undertaker
60
- raise ::ArgumentError.new("Key missing") unless params["key"]
61
-
62
- params["key"].each do |key|
63
- job = Sidekiq::DeadSet.new.fetch(*parse_params(key)).first
64
- if job
65
- if params["retry"]
66
- job.retry
67
- elsif params["delete"]
68
- job.delete
69
- end
70
- end
71
- end
77
+ raise ::ArgumentError.new("Key missing") unless parsed_params["key"]
72
78
 
73
- redirect redirect_path(request)
79
+ jobs = parsed_params["key"].map { |k| Sidekiq::DeadSet.new.fetch(*parse_params(k)).first }.compact
80
+
81
+ handle_selected_jobs jobs
74
82
  rescue ::ArgumentError
75
83
  bad_request
76
84
  end
77
85
 
78
- def post_undertaker_job_class_error_class_buckent_name_delete
86
+ def handle_selected_jobs(jobs)
87
+ return send_data(*prepare_data(jobs.map(&:item), EXPORT_CHUNK_SIZE)) if parsed_params["export"]
88
+
89
+ if parsed_params["retry"]
90
+ jobs.each(&:retry)
91
+ elsif parsed_params["delete"]
92
+ jobs.each(&:delete)
93
+ end
94
+
95
+ redirect redirect_path(request)
96
+ end
97
+
98
+ def post_undertaker_job_class_error_class_error_msg_bucket_name_delete
79
99
  store_request_params
80
- @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(params)
100
+ @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(parsed_params)
81
101
  @dead_jobs.each do |dead_job|
82
102
  dead_job.job.delete
83
103
  end
@@ -85,10 +105,10 @@ module Sidekiq
85
105
  redirect redirect_path(request)
86
106
  end
87
107
 
88
- def post_undertaker_job_class_error_class_buckent_name_retry
108
+ def post_undertaker_job_class_error_class_error_msg_bucket_name_retry
89
109
  store_request_params
90
110
 
91
- @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(params)
111
+ @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(parsed_params)
92
112
  @dead_jobs.each do |dead_job|
93
113
  dead_job.job.retry
94
114
  end
@@ -96,14 +116,45 @@ module Sidekiq
96
116
  redirect redirect_path(request)
97
117
  end
98
118
 
119
+ def post_undertaker_job_class_error_class_error_msg_bucket_name_export
120
+ store_request_params
121
+
122
+ @dead_jobs = Sidekiq::Undertaker::JobFilter.filter_dead_jobs(parsed_params)
123
+ send_data(*prepare_data(@dead_jobs.map { |j| j.job.item }, EXPORT_CHUNK_SIZE))
124
+ end
125
+
126
+ def post_import_jobs
127
+ file = parsed_params["upload_file"]
128
+ raise ::ArgumentError.new("The file is not a json") if file.nil? || file[:type] != "application/json"
129
+
130
+ data = parsed_params["upload_file"][:tempfile].read
131
+ dead_set = Sidekiq::DeadSet.new
132
+
133
+ JSON.parse(data).each do |job|
134
+ dead_set.kill(Sidekiq.dump_json(job))
135
+ end
136
+ redirect redirect_path(request)
137
+ rescue StandardError
138
+ bad_request
139
+ end
140
+
99
141
  def render_result(template)
100
142
  render(:erb, File.read(File.join(view_path, template)))
101
143
  end
102
144
 
103
145
  def store_request_params
104
- @req_job_class = params["job_class"]
105
- @req_bucket_name = params["bucket_name"]
106
- @req_error_class = params["error_class"]
146
+ @req_job_class = parsed_params["job_class"]
147
+ @req_bucket_name = parsed_params["bucket_name"]
148
+ @req_error_class = parsed_params["error_class"]
149
+ @req_error_msg = parsed_params["error_msg"]
150
+ end
151
+
152
+ def parsed_params
153
+ @parsed_params ||= if params["error_msg"] && params["error_msg"] != "all"
154
+ params.merge("error_msg" => Base64.urlsafe_decode64(params["error_msg"]))
155
+ else
156
+ params
157
+ end
107
158
  end
108
159
 
109
160
  def view_path
@@ -112,13 +163,40 @@ module Sidekiq
112
163
 
113
164
  def redirect_path(request)
114
165
  path = request.referer ? URI.parse(request.referer).path : request.path
115
- path.gsub("/delete", "").gsub("/retry", "")
166
+ path.gsub("/delete", "").gsub("/retry", "").gsub("/export", "")
167
+ end
168
+
169
+ def prepare_data(data, chunk_size)
170
+ filename = Time.now.strftime("%Y-%m-%d_%H-%M").to_s
171
+ return [data.to_json, "application/json", "#{filename}.json"] if data.length <= chunk_size
172
+
173
+ filename = "#{@req_job_class}_#{filename}"
174
+ zip = Zip::OutputStream.write_buffer do |file|
175
+ data.each_slice(chunk_size).each_with_index do |chunk, index|
176
+ file.put_next_entry("#{filename}_part-#{index + 1}.json")
177
+ file.write chunk.to_json
178
+ end
179
+ end
180
+
181
+ [zip.string, "application/zip", "#{filename}.zip"]
182
+ end
183
+
184
+ def send_data(data, content_type = "text/plain", file_name = "attachment.txt")
185
+ [
186
+ 200,
187
+ {
188
+ "Content-Type" => content_type,
189
+ "Content-Disposition" => "attachment; filename=\"#{file_name}\""
190
+ },
191
+ [data]
192
+ ]
116
193
  end
117
194
 
118
195
  def bad_request
119
196
  [400, { "Content-Type" => "text/plain" }, ["Bad Request"]]
120
197
  end
121
198
  end
199
+ # rubocop:enable Metrics/ModuleLength
122
200
  end
123
201
  end
124
202
  end
@@ -12,25 +12,34 @@ module Sidekiq
12
12
  app.get "/undertaker/filter" do
13
13
  show_filter
14
14
  end
15
-
16
15
  app.get "/undertaker/filter/:job_class/:bucket_name" do
17
16
  show_filter_by_job_class_bucket_name
18
17
  end
19
-
20
- app.get "/undertaker/morgue/:job_class/:error_class/:bucket_name" do
21
- show_undertaker_by_job_class_error_class_bucket_name
18
+ app.get "/undertaker/filter/:job_class/:error_class/:bucket_name" do
19
+ show_filter_by_job_class_error_class_bucket_name
20
+ end
21
+ app.get "/undertaker/morgue/:job_class/:error_class/:error_msg/:bucket_name" do
22
+ show_undertaker_by_job_class_error_class_error_msg_bucket_name
22
23
  end
23
24
 
24
25
  app.post "/undertaker/morgue" do
25
26
  post_undertaker
26
27
  end
27
28
 
28
- app.post "/undertaker/morgue/:job_class/:error_class/:bucket_name/delete" do
29
- post_undertaker_job_class_error_class_buckent_name_delete
29
+ app.post "/undertaker/morgue/:job_class/:error_class/:error_msg/:bucket_name/delete" do
30
+ post_undertaker_job_class_error_class_error_msg_bucket_name_delete
31
+ end
32
+
33
+ app.post "/undertaker/morgue/:job_class/:error_class/:error_msg/:bucket_name/retry" do
34
+ post_undertaker_job_class_error_class_error_msg_bucket_name_retry
35
+ end
36
+
37
+ app.post "/undertaker/morgue/:job_class/:error_class/:error_msg/:bucket_name/export" do
38
+ post_undertaker_job_class_error_class_error_msg_bucket_name_export
30
39
  end
31
40
 
32
- app.post "/undertaker/morgue/:job_class/:error_class/:bucket_name/retry" do
33
- post_undertaker_job_class_error_class_buckent_name_retry
41
+ app.post "/undertaker/import_jobs" do
42
+ post_import_jobs
34
43
  end
35
44
  end
36
45
  # rubocop:enable Metrics/MethodLength
@@ -21,8 +21,9 @@ Gem::Specification.new do |spec|
21
21
  spec.metadata = {
22
22
  "homepage_uri" => "https://github.com/ThomasKoppensteiner/sidekiq-undertaker",
23
23
  "source_code_uri" => "https://github.com/ThomasKoppensteiner/sidekiq-undertaker",
24
+ "changelog_uri" => "https://github.com/ThomasKoppensteiner/sidekiq-undertaker/blob/master/CHANGELOG.md",
24
25
  "bug_tracker_uri" => "https://github.com/ThomasKoppensteiner/sidekiq-undertaker/issues",
25
- "build_status_uri" => "https://travis-ci.org/ThomasKoppensteiner/sidekiq-undertaker",
26
+ "build_status_uri" => "https://github.com/ThomasKoppensteiner/sidekiq-undertaker/actions/workflows/ruby-build.yml",
26
27
  "rubygems_mfa_required" => "true"
27
28
  }
28
29
 
@@ -30,7 +31,6 @@ Gem::Specification.new do |spec|
30
31
 
31
32
  spec.files = `git ls-files -z`.split("\x0")
32
33
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
33
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
34
34
  spec.require_paths = ["lib", "lib/sidekiq/undertaker"]
35
35
 
36
36
  spec.add_development_dependency "bundler", ">= 1.17", "<3"
@@ -46,13 +46,14 @@ Gem::Specification.new do |spec|
46
46
  spec.add_development_dependency "rspec-mocks", "~> 3.8"
47
47
  spec.add_development_dependency "rspec-sidekiq", "~> 3.0"
48
48
  spec.add_development_dependency "rt_rubocop_defaults", "~> 2.3"
49
- spec.add_development_dependency "rubocop", "~> 1.8"
49
+ spec.add_development_dependency "rubocop", "~> 1.30"
50
50
  spec.add_development_dependency "rubocop-rake", "~> 0.5"
51
51
  spec.add_development_dependency "rubocop-rspec", "~> 2.0"
52
52
  spec.add_development_dependency "rubocop_runner", "~> 2.1"
53
- spec.add_development_dependency "simplecov", "~> 0.14"
53
+ spec.add_development_dependency "simplecov", "~> 0.21"
54
54
  spec.add_development_dependency "sinatra", "~> 2.0"
55
55
  spec.add_development_dependency "timecop", "~> 0.9"
56
56
 
57
- spec.add_runtime_dependency "sidekiq", ">= 6.2.2", "< 6.3"
57
+ spec.add_runtime_dependency "rubyzip", "~> 2.3"
58
+ spec.add_runtime_dependency "sidekiq", ">= 6.4", "< 7"
58
59
  end