sidekiq-status 1.1.0 → 2.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7b21776f1059a82b3a04c1e81639bcfba2c9184a00906688639333b2ee0df42
4
- data.tar.gz: 06ffc2506014272d4a40272cc3204696efed21fee426000da8b6c3a6c5e183d9
3
+ metadata.gz: 73189e5075131660e13e76ce8e8eeedd84953c90998941dd73a16825dc96d951
4
+ data.tar.gz: 5923bc75138cee22d8b5edcc5fbb9ad42dad4e0d89f29df74df68656a73adadc
5
5
  SHA512:
6
- metadata.gz: a0f173c573f137b0626b9a76d8977e4d536083e685d376f8ea6201a9d9ed8f48b793f3e59b5f06e57c0c518210ffe7c1fefebbfce9ea4033e77a270eab73d9f1
7
- data.tar.gz: 4f963ddaaa7f5a205fc9b6927ed994734f177a6438dd893ba2df435aa626c15614c3676b33d1feaaea03c341cfedb7e344e1a862e004acbd457ae7f9347cabd9
6
+ metadata.gz: '09e500e508e3a0d309e5d817a0fb7e1f58b9013f32284608ae5e2512bd556d07e7846c240789a2df4d5790f0aa183b3623181fd14acb12fd14a863ae354f627e'
7
+ data.tar.gz: 37b1eb9dec966fc8d8645c0ea4139d9a4cee715aca5c5b988881d63599d77e4d7cc564472a3f72e58923b8fb903ed0254c123821dafa2122c622e0dfc5b1e3ca
data/.gitignore CHANGED
@@ -4,6 +4,7 @@ gemfiles/*.lock
4
4
  .bundle
5
5
  .config
6
6
  .rvmrc
7
+ .ruby-version
7
8
  .yardoc
8
9
  Gemfile.lock
9
10
  InstalledFiles
data/.gitlab-ci.yml ADDED
@@ -0,0 +1,17 @@
1
+ dependency_scanning:
2
+ image: docker:stable
3
+ variables:
4
+ DOCKER_DRIVER: overlay2
5
+ allow_failure: true
6
+ services:
7
+ - docker:stable-dind
8
+ script:
9
+ - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
10
+ - docker run
11
+ --env DEP_SCAN_DISABLE_REMOTE_CHECKS="${DEP_SCAN_DISABLE_REMOTE_CHECKS:-false}"
12
+ --volume "$PWD:/code"
13
+ --volume /var/run/docker.sock:/var/run/docker.sock
14
+ "registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$SP_VERSION" /code
15
+ artifacts:
16
+ reports:
17
+ dependency_scanning: gl-dependency-scanning-report.json
data/.travis.yml CHANGED
@@ -1,16 +1,20 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2
4
- - 2.3
5
- - 2.4
6
- - 2.5
3
+ - 2.6
4
+ - 2.7
5
+ - 3.0
6
+ - ruby-head
7
7
  gemfile:
8
- - gemfiles/sidekiq_3.x.gemfile
9
- - gemfiles/sidekiq_4.x.gemfile
10
8
  - gemfiles/sidekiq_5.x.gemfile
9
+ - gemfiles/sidekiq_6.1.gemfile
10
+ - gemfiles/sidekiq_6.x.gemfile
11
11
  before_install:
12
12
  - gem update --system
13
13
  - gem update bundler
14
14
  services: redis
15
15
  notifications:
16
16
  email: false
17
+ matrix:
18
+ fast_finish: true
19
+ allow_failures:
20
+ - rvm: ruby-head
data/Appraisals CHANGED
@@ -1,11 +1,11 @@
1
- appraise "sidekiq-3.x" do
2
- gem "sidekiq", "~> 3"
1
+ appraise "sidekiq-5.x" do
2
+ gem "sidekiq", "~> 5"
3
3
  end
4
4
 
5
- appraise "sidekiq-4.x" do
6
- gem "sidekiq", "~> 4"
5
+ appraise "sidekiq-6.1" do
6
+ gem "sidekiq", "~> 6.1"
7
7
  end
8
8
 
9
- appraise "sidekiq-5.x" do
10
- gem "sidekiq", "~> 5"
9
+ appraise "sidekiq-6.x" do
10
+ gem "sidekiq", "~> 6"
11
11
  end
data/CHANGELOG.md CHANGED
@@ -1,59 +1,27 @@
1
- **Version 1.1.0**
2
- + Fixes the use case of multiple services sharing the same redis instance (#135)
3
-
4
- **Version 1.0.2**
5
- + Fixes status not being set to `:failed` after retries
6
-
7
- **Version 1.0.1**
8
- + Fixes namespacing in `sidekiq-status/testing/inline`
9
-
10
- **Version 1.0.0**
11
- + Version number bumped to indicate stable release
12
-
13
- **Version 0.8.1**
14
- + Avoids transient celluloid dependency in Sidekiq < 5.x
15
-
16
- **Version 0.8.0**
17
- + Properly ignores jobs that do not have `Sidekiq::Status::Worker` included
18
- + Honors custom job expirations for ActiveJob jobs
19
- + Adds a `:retrying` status
20
- + Adds remove / retry buttons to the index page
21
- + Server middleware will now catches all exception types
22
- + Changes where server middleware is inserted in the chain
23
- + Reduces the amount of Redis calls made
24
- + Adds pagination / per page setting
25
- + Restores column sorting functionality
26
-
27
- **Version 0.7.0**
28
- + Sidekiq 4.2 and 5 now supported
29
- + Added full support for ActiveJob
30
- + Updated Web UI
31
- + Styling updated to stay consistent with Sidekiq UI
32
- + Added header sorting
33
- + Fixed argument formatting
34
- + Times now display using natural language via ChronicDuration
35
- + Test suite fixed
36
-
37
- **Version 0.6.0**
38
- + Updated Web UI
39
- + Will have all job statuses, previously it was showing only :working status
40
- + Bootstrap lables instead of badges for status
41
- + Added Arguments column to statuses page
42
- + New :interrupted status added
43
- + Added way to specify :expiration for Sidekiq::Status::ClientMiddleware
44
- + Bug fixes & Code cleaup
45
-
46
- **Version 0.5.3**
47
- + some tweaks in web UI, separate redis namespace
48
-
49
- **Version 0.5.2**
50
- + Sidekiq versions up to 3.3.* supported, jobs sorting options in web UI, more ruby versions
51
-
52
- **Version 0.5.1**
53
- + dependencies versions requirements relaxed
54
-
55
- **Version 0.5.0**
56
- + Sidekiq v3 support, redis pools support
57
-
58
- **Version 0.4.0**
59
- + WebUI added, per-worker expiration setting enabled
1
+ **Version 2.1.3**
2
+ * Fixes redis deprecation warnings (https://github.com/kenaniah/sidekiq-status/issues/11)
3
+
4
+ **Version 2.1.2**
5
+ * Casts values to strings when HTML-encoding
6
+
7
+ **Version 2.1.1**
8
+ * Ensures parameter outputs are properly HTML-encoded
9
+
10
+ **Version 2.1.0**
11
+ * Adds support for Sidekiq 6.2.2+ (https://github.com/mperham/sidekiq/issues/4955)
12
+
13
+ **Version 2.0.2**
14
+ * Fixes for dark mode theme
15
+
16
+ **Version 2.0.1**
17
+ * Adds support for dark mode to the job status page
18
+
19
+ **Version 2.0.0**
20
+ * Adds support for Ruby 2.7, 3.0
21
+ * Adds support for Sidekiq 6.x
22
+ * Removes support for Ruby 2.3, 2.4, 2.5
23
+ * Removes support for Sidekiq 3.x, 4.x
24
+
25
+ **Versions 1.1.4 and prior**
26
+
27
+ See https://github.com/utgarda/sidekiq-status/blob/master/CHANGELOG.md.
data/README.md CHANGED
@@ -1,16 +1,11 @@
1
1
  # Sidekiq::Status
2
- [![Gem Version](https://badge.fury.io/rb/sidekiq-status.svg)](http://badge.fury.io/rb/sidekiq-status)
3
- [![Code Climate](https://codeclimate.com/github/utgarda/sidekiq-status.svg)](https://codeclimate.com/github/utgarda/sidekiq-status)
4
- [![Build Status](https://secure.travis-ci.org/utgarda/sidekiq-status.svg)](http://travis-ci.org/utgarda/sidekiq-status)
5
- [![Dependency Status](https://gemnasium.com/utgarda/sidekiq-status.svg)](https://gemnasium.com/utgarda/sidekiq-status)
6
- [![Inline docs](http://inch-ci.org/github/utgarda/sidekiq-status.svg?branch=master)](http://inch-ci.org/github/utgarda/sidekiq-status)
2
+ [![Gem Version](https://badge.fury.io/rb/sidekiq-status.svg)](https://badge.fury.io/rb/sidekiq-status)
3
+ [![Build Status](https://www.travis-ci.com/kenaniah/sidekiq-status.svg?branch=main)](https://www.travis-ci.com/github/kenaniah/sidekiq-status)
4
+ [![Inline docs](https://inch-ci.org/github/kenaniah/sidekiq-status.svg?branch=main)](https://inch-ci.org/github/kenaniah/sidekiq-status)
7
5
 
8
- An extension to [Sidekiq](http://github.com/mperham/sidekiq) message processing to track your jobs. Inspired
9
- by [resque-status](http://github.com/quirkey/resque-status) and mostly copying its features, using Sidekiq's middleware.
6
+ Sidekiq-status is an extension to [Sidekiq](https://github.com/mperham/sidekiq) that tracks information about your Sidekiq and provides a UI to that purpose. It was inspired by [resque-status](https://github.com/quirkey/resque-status).
10
7
 
11
- Fully compatible with ActiveJob.
12
-
13
- Supports the latest versions of Sidekiq and all the way back to 3.x.
8
+ Requires Ruby 2.6+ and Sidekiq 5.0+ or newer.
14
9
 
15
10
  ## Installation
16
11
 
@@ -20,18 +15,18 @@ Add this line to your application's Gemfile:
20
15
  gem 'sidekiq-status'
21
16
  ```
22
17
 
23
- And then execute:
24
-
25
- ```bash
26
- $ bundle
27
- ```
28
-
29
18
  Or install it yourself as:
30
19
 
31
20
  ```bash
32
21
  gem install sidekiq-status
33
22
  ```
34
23
 
24
+ #### Migrating to Version 2.x from 1.x
25
+
26
+ Version 2.0.0 was published in order to add support for Ruby 3.0 and Sidekiq 6.x and to remove support for versions of both that are now end-of-life. **You should be able to upgrade cleanly from version 1.x to 2.x provided you are running Sidekiq 5.x or newer.**
27
+
28
+ Sidekiq-status version 1.1.4 provides support all the way back to Sidekiq 3.x and was maintained at https://github.com/utgarda/sidekiq-status/.
29
+
35
30
  ## Setup Checklist
36
31
 
37
32
  To get started:
@@ -63,9 +58,7 @@ Sidekiq.configure_server do |config|
63
58
  end
64
59
  ```
65
60
 
66
- **Note:** This method of configuration is new as of version 0.8.0.
67
-
68
- After that you can use your jobs as usual. You need to also include the `Sidekiq::Status::Worker` module in your jobs if you want the additional functionality of tracking progress and storing / retrieving job data.
61
+ Include the `Sidekiq::Status::Worker` module in your jobs if you want the additional functionality of tracking progress and storing / retrieving job data.
69
62
 
70
63
  ``` ruby
71
64
  class MyJob
@@ -78,7 +71,7 @@ class MyJob
78
71
  end
79
72
  ```
80
73
 
81
- As of version 0.8.0, _only jobs that include `Sidekiq::Status::Worker`_ will have their statuses tracked. Previous versions of this gem used to track statuses for all jobs, even when `Sidekiq::Status::Worker` was not included.
74
+ Note: _only jobs that include `Sidekiq::Status::Worker`_ will have their statuses tracked.
82
75
 
83
76
  To overwrite expiration on a per-worker basis, write an expiration method like the one below:
84
77
 
@@ -105,7 +98,7 @@ As sidekiq-status stores information about jobs in Redis, it is necessary to set
105
98
 
106
99
  As explained above, the default expiration may also be overridden on a per-job basis by defining it within the job itself via a method called `#expiration`.
107
100
 
108
- The expiration time set will be used as the [Redis expire time](http://redis.io/commands/expire), which is also known as the TTL (time to live). Once the expiration time has passed, all information about the job's status and any custom data stored via sidekiq-status will disappear.
101
+ The expiration time set will be used as the [Redis expire time](https://redis.io/commands/expire), which is also known as the TTL (time to live). Once the expiration time has passed, all information about the job's status and any custom data stored via sidekiq-status will disappear.
109
102
 
110
103
  It is advised that you set the expiration time greater than the amount of time required to complete the job.
111
104
 
@@ -131,7 +124,7 @@ Important: If you try any of the above status method after the expiration time,
131
124
 
132
125
  ### ActiveJob Support
133
126
 
134
- Version 0.7.0 has added full support for ActiveJob. The status of ActiveJob jobs will be tracked automatically.
127
+ This gem also supports ActiveJob jobs. Their status will be tracked automatically.
135
128
 
136
129
  To also enable job progress tracking and data storage features, simply add the `Sidekiq::Status::Worker` module to your base class, like below:
137
130
 
@@ -216,11 +209,11 @@ This gem provides an extension to Sidekiq's web interface with an index at `/sta
216
209
 
217
210
  ![Sidekiq Status Web](web/sidekiq-status-web.png)
218
211
 
219
- As of 0.7.0, status information for an individual job may be found at `/statuses/:job_id`.
212
+ Information for an individual job may be found at `/statuses/:job_id`.
220
213
 
221
214
  ![Sidekiq Status Web](web/sidekiq-status-single-web.png)
222
215
 
223
- As of 0.8.0, only jobs that include `Sidekiq::Status::Worker` will be reported in the web interface.
216
+ Note: _only jobs that include `Sidekiq::Status::Worker`_ will be reported in the web interface.
224
217
 
225
218
  #### Adding the Web Interface
226
219
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sidekiq", "~> 4"
5
+ gem "sidekiq", "~> 6.1"
6
6
 
7
7
  gemspec path: "../"
@@ -2,6 +2,6 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "sidekiq", "~> 3"
5
+ gem "sidekiq", "~> 6"
6
6
 
7
7
  gemspec path: "../"
@@ -1,4 +1,6 @@
1
1
  require 'sidekiq/api'
2
+ JOB_CLASS = Sidekiq.constants.include?(:JobRecord) ? Sidekiq::JobRecord : Sidekiq::Job
3
+
2
4
  module Sidekiq::Status
3
5
  # Should be in the client middleware chain
4
6
  class ClientMiddleware
@@ -34,7 +36,7 @@ module Sidekiq::Status
34
36
  initial_metadata = {
35
37
  jid: msg['jid'],
36
38
  status: :queued,
37
- worker: Sidekiq::Job.new(msg, queue).display_class,
39
+ worker: JOB_CLASS.new(msg, queue).display_class,
38
40
  args: display_args(msg, queue)
39
41
  }
40
42
  store_for_id msg['jid'], initial_metadata, job_class.new.expiration || @expiration, redis_pool
@@ -45,7 +47,7 @@ module Sidekiq::Status
45
47
  end
46
48
 
47
49
  def display_args(msg, queue)
48
- job = Sidekiq::Job.new(msg, queue)
50
+ job = JOB_CLASS.new(msg, queue)
49
51
  return job.display_args.to_a.empty? ? nil : job.display_args.to_json
50
52
  rescue Exception => e
51
53
  # For Sidekiq ~> 2.7
@@ -61,7 +61,7 @@ module Sidekiq::Status
61
61
  status = :retrying
62
62
  end
63
63
  end
64
- store_status worker.jid, status, expiry
64
+ store_status(worker.jid, status, expiry) if job_class && job_class.ancestors.include?(Sidekiq::Status::Worker)
65
65
  raise
66
66
  end
67
67
 
@@ -13,10 +13,10 @@ module Sidekiq::Status::Storage
13
13
  # @return [String] Redis operation status code
14
14
  def store_for_id(id, status_updates, expiration = nil, redis_pool=nil)
15
15
  redis_connection(redis_pool) do |conn|
16
- conn.multi do
17
- conn.hmset key(id), 'update_time', Time.now.to_i, *(status_updates.to_a.flatten(1))
18
- conn.expire key(id), (expiration || Sidekiq::Status::DEFAULT_EXPIRY)
19
- conn.publish "status_updates", id
16
+ conn.multi do |pipeline|
17
+ pipeline.hmset key(id), 'update_time', Time.now.to_i, *(status_updates.to_a.flatten(1))
18
+ pipeline.expire key(id), (expiration || Sidekiq::Status::DEFAULT_EXPIRY)
19
+ pipeline.publish "status_updates", id
20
20
  end[0]
21
21
  end
22
22
  end
@@ -90,7 +90,7 @@ module Sidekiq::Status::Storage
90
90
  # - end: end score (i.e. +inf or a unix timestamp)
91
91
  # - offset: current progress through (all) jobs (e.g.: 100 if you want jobs from 100 to BATCH_LIMIT)
92
92
  def schedule_batch(options)
93
- options[:conn].zrangebyscore "schedule", options[:start], options[:end], {limit: [options[:offset], BATCH_LIMIT]}
93
+ options[:conn].zrangebyscore "schedule", options[:start], options[:end], limit: [options[:offset], BATCH_LIMIT]
94
94
  end
95
95
 
96
96
  # Searches the jobs Array for the job_id
@@ -1,5 +1,5 @@
1
1
  module Sidekiq
2
2
  module Status
3
- VERSION = '1.1.0'
3
+ VERSION = '2.1.3'
4
4
  end
5
5
  end
@@ -8,6 +8,7 @@ module Sidekiq::Status
8
8
 
9
9
  DEFAULT_PER_PAGE_OPTS = [25, 50, 100].freeze
10
10
  DEFAULT_PER_PAGE = 25
11
+ COMMON_STATUS_HASH_KEYS = %w(update_time jid status worker args label pct_complete total at message)
11
12
 
12
13
  class << self
13
14
  def per_page_opts= arr
@@ -47,9 +48,14 @@ module Sidekiq::Status
47
48
  def add_details_to_status(status)
48
49
  status['label'] = status_label(status['status'])
49
50
  status["pct_complete"] ||= pct_complete(status)
51
+ status["custom"] = process_custom_data(status)
50
52
  return status
51
53
  end
52
54
 
55
+ def process_custom_data(hash)
56
+ hash.reject { |key, _| COMMON_STATUS_HASH_KEYS.include?(key) }
57
+ end
58
+
53
59
  def pct_complete(status)
54
60
  return 100 if status['status'] == 'complete'
55
61
  Sidekiq::Status::pct_complete(status['jid']) || 0
@@ -75,8 +81,11 @@ module Sidekiq::Status
75
81
 
76
82
  app.get '/statuses' do
77
83
 
78
- namespace_jids = Sidekiq.redis{ |conn| conn.keys('sidekiq:status:*') }
79
- jids = namespace_jids.map{ |id_namespace| id_namespace.split(':').last }
84
+ jids = Sidekiq.redis do |conn|
85
+ conn.scan_each(match: 'sidekiq:status:*', count: 100).map do |key|
86
+ key.split(':').last
87
+ end.uniq
88
+ end
80
89
  @statuses = []
81
90
 
82
91
  jids.each do |jid|
@@ -96,6 +105,10 @@ module Sidekiq::Status
96
105
  @statuses = @statuses.sort { |y,x| (x[sort_by] <=> y[sort_by]) || 1 }
97
106
  end
98
107
 
108
+ if params[:status] && params[:status] != "all"
109
+ @statuses = @statuses.select {|job_status| job_status["status"] == params[:status] }
110
+ end
111
+
99
112
  # Sidekiq pagination
100
113
  @total_size = @statuses.count
101
114
  @count = params[:per_page] ? params[:per_page].to_i : Sidekiq::Status::Web.default_per_page
@@ -123,7 +136,7 @@ module Sidekiq::Status
123
136
  job = Sidekiq::Status::get_all params['jid']
124
137
 
125
138
  if job.empty?
126
- halt [404, {"Content-Type" => "text/html"}, [erb(sidekiq_status_template(:status_not_found))]]
139
+ throw :halt, [404, {"Content-Type" => "text/html"}, [erb(sidekiq_status_template(:status_not_found))]]
127
140
  else
128
141
  @status = add_details_to_status(job)
129
142
  erb(sidekiq_status_template(:status))
@@ -135,21 +148,25 @@ module Sidekiq::Status
135
148
  job = Sidekiq::RetrySet.new.find_job(params[:jid])
136
149
  job ||= Sidekiq::DeadSet.new.find_job(params[:jid])
137
150
  job.retry if job
138
- halt [302, { "Location" => request.referer }, []]
151
+ throw :halt, [302, { "Location" => request.referer }, []]
139
152
  end
140
153
 
141
154
  # Removes a completed job from the status list
142
155
  app.delete '/statuses' do
143
156
  Sidekiq::Status.delete(params[:jid])
144
- halt [302, { "Location" => request.referer }, []]
157
+ throw :halt, [302, { "Location" => request.referer }, []]
145
158
  end
146
159
  end
147
160
  end
148
161
  end
149
162
 
150
- require 'sidekiq/web' unless defined?(Sidekiq::Web)
163
+ unless defined?(Sidekiq::Web)
164
+ require 'delegate' # Needed for sidekiq 5.x
165
+ require 'sidekiq/web'
166
+ end
167
+
151
168
  Sidekiq::Web.register(Sidekiq::Status::Web)
152
- ["per_page", "sort_by", "sort_dir"].each do |key|
169
+ ["per_page", "sort_by", "sort_dir", "status"].each do |key|
153
170
  Sidekiq::WebHelpers::SAFE_QPARAMS.push(key)
154
171
  end
155
172
  if Sidekiq::Web.tabs.is_a?(Array)
@@ -5,7 +5,7 @@ Gem::Specification.new do |gem|
5
5
  gem.authors = ['Evgeniy Tsvigun', 'Kenaniah Cerny']
6
6
  gem.email = ['utgarda@gmail.com', 'kenaniah@gmail.com']
7
7
  gem.summary = 'An extension to the sidekiq message processing to track your jobs'
8
- gem.homepage = 'http://github.com/utgarda/sidekiq-status'
8
+ gem.homepage = 'https://github.com/kenaniah/sidekiq-status'
9
9
  gem.license = 'MIT'
10
10
 
11
11
  gem.files = `git ls-files`.split($\)
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
14
14
  gem.require_paths = ['lib']
15
15
  gem.version = Sidekiq::Status::VERSION
16
16
 
17
- gem.add_dependency 'sidekiq', '>= 3.0'
17
+ gem.add_dependency 'sidekiq', '>= 5.0'
18
18
  gem.add_dependency 'chronic_duration'
19
19
  gem.add_development_dependency 'appraisal'
20
20
  gem.add_development_dependency 'colorize'
@@ -12,13 +12,13 @@ describe Sidekiq::Status::ClientMiddleware do
12
12
  describe "without :expiration parameter" do
13
13
 
14
14
  it "sets queued status" do
15
- expect(StubJob.perform_async arg1: 'val1').to eq(job_id)
15
+ expect(StubJob.perform_async 'arg1' => 'val1').to eq(job_id)
16
16
  expect(redis.hget("sidekiq:status:#{job_id}", :status)).to eq('queued')
17
17
  expect(Sidekiq::Status::queued?(job_id)).to be_truthy
18
18
  end
19
19
 
20
20
  it "sets status hash ttl" do
21
- expect(StubJob.perform_async arg1: 'val1').to eq(job_id)
21
+ expect(StubJob.perform_async 'arg1' => 'val1').to eq(job_id)
22
22
  expect(1..Sidekiq::Status::DEFAULT_EXPIRY).to cover redis.ttl("sidekiq:status:#{job_id}")
23
23
  end
24
24
 
@@ -50,7 +50,7 @@ describe Sidekiq::Status::ClientMiddleware do
50
50
  end
51
51
 
52
52
  it "overwrites default expiry value" do
53
- StubJob.perform_async arg1: 'val1'
53
+ StubJob.perform_async 'arg1' => 'val1'
54
54
  expect((Sidekiq::Status::DEFAULT_EXPIRY+1)..huge_expiration).to cover redis.ttl("sidekiq:status:#{job_id}")
55
55
  end
56
56
 
@@ -10,7 +10,7 @@ describe Sidekiq::Status::ServerMiddleware do
10
10
  allow(SecureRandom).to receive(:hex).once.and_return(job_id)
11
11
  start_server do
12
12
  thread = redis_thread 4, "status_updates", "job_messages_#{job_id}"
13
- expect(ConfirmationJob.perform_async arg1: 'val1').to eq(job_id)
13
+ expect(ConfirmationJob.perform_async 'arg1' => 'val1').to eq(job_id)
14
14
  expect(thread.value).to eq([
15
15
  job_id,
16
16
  job_id,
@@ -36,21 +36,39 @@ describe Sidekiq::Status::ServerMiddleware do
36
36
  it "sets failed status when Exception raised" do
37
37
  allow(SecureRandom).to receive(:hex).once.and_return(job_id)
38
38
  start_server do
39
- expect(capture_status_updates(3) {
40
- expect(FailingHardJob.perform_async).to eq(job_id)
41
- }).to eq([job_id]*3)
39
+ expect(capture_status_updates(3) {
40
+ expect(FailingHardJob.perform_async).to eq(job_id)
41
+ }).to eq([job_id]*3)
42
42
  end
43
43
  expect(redis.hget("sidekiq:status:#{job_id}", :status)).to eq('failed')
44
44
  expect(Sidekiq::Status::failed?(job_id)).to be_truthy
45
45
  end
46
46
 
47
+ context "when Sidekiq::Status::Worker is not included in the job" do
48
+ it "should not set a failed status" do
49
+ allow(SecureRandom).to receive(:hex).once.and_return(job_id)
50
+ start_server do
51
+ expect(FailingNoStatusJob.perform_async).to eq(job_id)
52
+ end
53
+ expect(redis.hget("sidekiq:status:#{job_id}", :status)).to be_nil
54
+ end
55
+
56
+ it "should not set any status when Exception raised" do
57
+ allow(SecureRandom).to receive(:hex).once.and_return(job_id)
58
+ start_server do
59
+ expect(FailingHardNoStatusJob.perform_async).to eq(job_id)
60
+ end
61
+ expect(redis.hget("sidekiq:status:#{job_id}", :status)).to be_nil
62
+ end
63
+ end
64
+
47
65
  context "sets interrupted status" do
48
66
  it "on system exit signal" do
49
67
  allow(SecureRandom).to receive(:hex).once.and_return(job_id)
50
68
  start_server do
51
- expect(capture_status_updates(3) {
52
- expect(ExitedJob.perform_async).to eq(job_id)
53
- }).to eq([job_id]*3)
69
+ expect(capture_status_updates(3) {
70
+ expect(ExitedJob.perform_async).to eq(job_id)
71
+ }).to eq([job_id]*3)
54
72
  end
55
73
  expect(redis.hget("sidekiq:status:#{job_id}", :status)).to eq('interrupted')
56
74
  expect(Sidekiq::Status::interrupted?(job_id)).to be_truthy
@@ -72,7 +90,7 @@ describe Sidekiq::Status::ServerMiddleware do
72
90
  it "sets status hash ttl" do
73
91
  allow(SecureRandom).to receive(:hex).once.and_return(job_id)
74
92
  start_server do
75
- expect(StubJob.perform_async arg1: 'val1').to eq(job_id)
93
+ expect(StubJob.perform_async 'arg1' => 'val1').to eq(job_id)
76
94
  end
77
95
  expect(1..Sidekiq::Status::DEFAULT_EXPIRY).to cover redis.ttl("sidekiq:status:#{job_id}")
78
96
  end
@@ -86,7 +104,7 @@ describe Sidekiq::Status::ServerMiddleware do
86
104
 
87
105
  it "overwrites default expiry value" do
88
106
  start_server(:expiration => huge_expiration) do
89
- StubJob.perform_async arg1: 'val1'
107
+ StubJob.perform_async 'arg1' => 'val1'
90
108
  end
91
109
  expect((Sidekiq::Status::DEFAULT_EXPIRY-1)..huge_expiration).to cover redis.ttl("sidekiq:status:#{job_id}")
92
110
  end
@@ -95,7 +113,7 @@ describe Sidekiq::Status::ServerMiddleware do
95
113
  overwritten_expiration = huge_expiration * 100
96
114
  allow_any_instance_of(StubJob).to receive(:expiration).and_return(overwritten_expiration)
97
115
  start_server(:expiration => huge_expiration) do
98
- StubJob.perform_async arg1: 'val1'
116
+ StubJob.perform_async 'arg1' => 'val1'
99
117
  end
100
118
  expect((huge_expiration+1)..overwritten_expiration).to cover redis.ttl("sidekiq:status:#{job_id}")
101
119
  end
@@ -13,6 +13,7 @@ describe 'sidekiq status web' do
13
13
  end
14
14
 
15
15
  before do
16
+ env 'rack.session', csrf: Base64.urlsafe_encode64('token')
16
17
  client_middleware
17
18
  allow(SecureRandom).to receive(:hex).and_return(job_id)
18
19
  end
@@ -31,6 +32,27 @@ describe 'sidekiq status web' do
31
32
  expect(last_response.body).to match(/working/)
32
33
  end
33
34
 
35
+ it 'allows filtering the list of jobs by status' do
36
+ capture_status_updates(2) do
37
+ LongJob.perform_async(0.5)
38
+ end
39
+
40
+ get '/statuses?status=working'
41
+ expect(last_response).to be_ok
42
+ expect(last_response.body).to match(/#{job_id}/)
43
+ expect(last_response.body).to match(/LongJob/)
44
+ expect(last_response.body).to match(/working/)
45
+ end
46
+
47
+ it 'allows filtering the list of jobs by completed status' do
48
+ capture_status_updates(2) do
49
+ LongJob.perform_async(0.5)
50
+ end
51
+ get '/statuses?status=completed'
52
+ expect(last_response).to be_ok
53
+ expect(last_response.body).to_not match(/LongJob/)
54
+ end
55
+
34
56
  it 'shows a single job in progress' do
35
57
  capture_status_updates(2) do
36
58
  LongJob.perform_async(1, 'another argument')
@@ -39,10 +61,21 @@ describe 'sidekiq status web' do
39
61
  get "/statuses/#{job_id}"
40
62
  expect(last_response).to be_ok
41
63
  expect(last_response.body).to match(/#{job_id}/)
42
- expect(last_response.body).to match(/1,"another argument"/)
64
+ expect(last_response.body).to match(/1,&quot;another argument&quot;/)
43
65
  expect(last_response.body).to match(/working/)
44
66
  end
45
67
 
68
+ it 'shows custom data for a single job' do
69
+ capture_status_updates(3) do
70
+ CustomDataJob.perform_async
71
+ end
72
+
73
+ get "/statuses/#{job_id}"
74
+ expect(last_response).to be_ok
75
+ expect(last_response.body).to match(/mister_cat/)
76
+ expect(last_response.body).to match(/meow/)
77
+ end
78
+
46
79
  it 'show an error when the requested job ID is not found' do
47
80
  get '/statuses/12345'
48
81
  expect(last_response).to be_not_found
@@ -105,7 +105,7 @@ describe Sidekiq::Status do
105
105
  second_job = LongJob.perform_in(3600)
106
106
  expect(second_job).to eq(job_id_1)
107
107
 
108
- initial_schedule = redis.zrange "schedule", 0, -1, {withscores: true}
108
+ initial_schedule = redis.zrange "schedule", 0, -1, withscores: true
109
109
  expect(initial_schedule.size).to be(2)
110
110
  expect(initial_schedule.select {|scheduled_job| JSON.parse(scheduled_job[0])["jid"] == job_id }.size).to be(1)
111
111
 
@@ -113,7 +113,7 @@ describe Sidekiq::Status do
113
113
  # Unused, therefore unfound => false
114
114
  expect(Sidekiq::Status.cancel(unused_id)).to be_falsey
115
115
 
116
- remaining_schedule = redis.zrange "schedule", 0, -1, {withscores: true}
116
+ remaining_schedule = redis.zrange "schedule", 0, -1, withscores: true
117
117
  expect(remaining_schedule.size).to be(initial_schedule.size - 1)
118
118
  expect(remaining_schedule.select {|scheduled_job| JSON.parse(scheduled_job[0])["jid"] == job_id }.size).to be(0)
119
119
  end
@@ -126,14 +126,14 @@ describe Sidekiq::Status do
126
126
  returned_job_id = LongJob.perform_at(scheduled_time)
127
127
  expect(returned_job_id).to eq(job_id)
128
128
 
129
- initial_schedule = redis.zrange "schedule", 0, -1, {withscores: true}
129
+ initial_schedule = redis.zrange "schedule", 0, -1, withscores: true
130
130
  expect(initial_schedule.size).to be(1)
131
131
  # wrong time, therefore unfound => false
132
132
  expect(Sidekiq::Status.cancel(returned_job_id, (scheduled_time + 1))).to be_falsey
133
- expect((redis.zrange "schedule", 0, -1, {withscores: true}).size).to be(1)
133
+ expect((redis.zrange "schedule", 0, -1, withscores: true).size).to be(1)
134
134
  # same id, same time, deletes
135
135
  expect(Sidekiq::Status.cancel(returned_job_id, (scheduled_time))).to be_truthy
136
- expect(redis.zrange "schedule", 0, -1, {withscores: true}).to be_empty
136
+ expect(redis.zrange "schedule", 0, -1, withscores: true).to be_empty
137
137
  end
138
138
  end
139
139
  end
@@ -159,7 +159,7 @@ describe Sidekiq::Status do
159
159
  end
160
160
 
161
161
  it "retries failed jobs" do
162
- allow(SecureRandom).to receive(:hex).and_return(retried_job_id)
162
+ allow(SecureRandom).to receive(:hex).exactly(3).times.and_return(retried_job_id)
163
163
  start_server do
164
164
  expect(capture_status_updates(3) {
165
165
  expect(RetriedJob.perform_async()).to eq(retried_job_id)
@@ -10,6 +10,16 @@ class StubJob
10
10
  end
11
11
  end
12
12
 
13
+ class StubNoStatusJob
14
+ include Sidekiq::Worker
15
+
16
+ sidekiq_options 'retry' => false
17
+
18
+ def perform(*args)
19
+ end
20
+ end
21
+
22
+
13
23
  class ExpiryJob < StubJob
14
24
  def expiration
15
25
  15
@@ -31,6 +41,13 @@ class DataJob < StubJob
31
41
  end
32
42
  end
33
43
 
44
+ class CustomDataJob < StubJob
45
+ def perform
46
+ store({mister_cat: 'meow'})
47
+ sleep 0.5
48
+ end
49
+ end
50
+
34
51
  class ProgressJob < StubJob
35
52
  def perform
36
53
  total 500
@@ -62,6 +79,12 @@ class FailingJob < StubJob
62
79
  end
63
80
  end
64
81
 
82
+ class FailingNoStatusJob < StubNoStatusJob
83
+ def perform
84
+ raise StandardError
85
+ end
86
+ end
87
+
65
88
  class RetryAndFailJob < StubJob
66
89
  sidekiq_options retry: 1
67
90
 
@@ -76,6 +99,12 @@ class FailingHardJob < StubJob
76
99
  end
77
100
  end
78
101
 
102
+ class FailingHardNoStatusJob < StubNoStatusJob
103
+ def perform
104
+ raise Exception
105
+ end
106
+ end
107
+
79
108
  class ExitedJob < StubJob
80
109
  def perform
81
110
  raise SystemExit
@@ -90,13 +119,13 @@ end
90
119
 
91
120
  class RetriedJob < StubJob
92
121
 
93
- sidekiq_options 'retry' => true
122
+ sidekiq_options retry: true
94
123
  sidekiq_retry_in do |count| 3 end # 3 second delay > job timeout in test suite
95
124
 
96
- def perform()
125
+ def perform
97
126
  Sidekiq.redis do |conn|
98
127
  key = "RetriedJob_#{jid}"
99
- unless conn.exists key
128
+ if [0, false].include? conn.exists(key)
100
129
  conn.set key, 'tried'
101
130
  raise StandardError
102
131
  end
data/web/views/status.erb CHANGED
@@ -1,3 +1,4 @@
1
+ <% require 'cgi'; def h(v); CGI.escape_html(v.to_s); end %>
1
2
  <style>
2
3
  .progress {
3
4
  background-color: #C8E1ED;
@@ -14,9 +15,9 @@
14
15
  </style>
15
16
 
16
17
  <h3>
17
- Job Status: <%= @status["jid"] %>
18
- <span class='label label-<%= @status["label"] %>'>
19
- <%= @status["status"] %>
18
+ Job Status: <%= h @status["jid"] %>
19
+ <span class='label label-<%= h @status["label"] %>'>
20
+ <%= h @status["status"] %>
20
21
  </span>
21
22
  </h3>
22
23
 
@@ -28,16 +29,16 @@
28
29
  </div>
29
30
  </div>
30
31
 
31
- <div class="panel panel-default">
32
+ <div class="panel panel-default" style="background-color: inherit">
32
33
  <div class="panel-body">
33
- <h4><%= @status["worker"] %></h4>
34
+ <h4><%= h @status["worker"] %></h4>
34
35
 
35
36
  <div class="row">
36
37
  <div class="col-sm-2">
37
38
  <strong>Arguments</strong>
38
39
  </div>
39
40
  <div class="col-sm-10">
40
- <p><%= @status["args"].empty? ? "<i>none</i>" : @status["args"] %></p>
41
+ <p><%= @status["args"].empty? ? "<i>none</i>" : h(@status["args"]) %></p>
41
42
  </div>
42
43
  </div>
43
44
 
@@ -46,7 +47,7 @@
46
47
  <strong>Message</strong>
47
48
  </div>
48
49
  <div class="col-sm-10">
49
- <p><%= @status["message"] || "<i>none</i>" %></p>
50
+ <p><%= h(@status["message"]) || "<i>none</i>" %></p>
50
51
  </div>
51
52
  </div>
52
53
 
@@ -65,6 +66,24 @@
65
66
  </p>
66
67
  </div>
67
68
  </div>
69
+
70
+ <% if @status["custom"].any? %>
71
+ <hr>
72
+ <% @status["custom"].each do |key, val| %>
73
+ <div class="row">
74
+ <div class="col-sm-2">
75
+ <strong><%= key %></strong>
76
+ </div>
77
+ <div class="col-sm-10">
78
+ <% if val && val.include?("\n") %>
79
+ <pre><%= h val %></pre>
80
+ <% else %>
81
+ <p><%= h(val) || "<i>none</i>" %></p>
82
+ <% end %>
83
+ </div>
84
+ </div>
85
+ <% end %>
86
+ <% end %>
68
87
  </div>
69
88
  </div>
70
89
 
@@ -1,5 +1,6 @@
1
- <h3>Job Status: <%= params[:jid] %></h3>
1
+ <% require 'cgi'; def h(v); CGI.escape_html(v.to_s); end %>
2
+ <h3>Job Status: <%= h params[:jid] %></h3>
2
3
 
3
- <div class="alert alert-danger" role="alert">
4
+ <div role="alert">
4
5
  <strong>Uh oh!</strong> That job can't be found. It may have expired already.
5
6
  </div>
@@ -1,3 +1,4 @@
1
+ <% require 'cgi'; def h(v); CGI.escape_html(v.to_s); end %>
1
2
  <style>
2
3
  .progress {
3
4
  background-color: #C8E1ED;
@@ -38,7 +39,7 @@
38
39
  display: flex;
39
40
  align-items: center;
40
41
  }
41
- .nav-container .per-page {
42
+ .nav-container .per-page, .filter-status {
42
43
  display: flex;
43
44
  align-items: center;
44
45
  margin: 20px 0 20px 10px;
@@ -57,6 +58,15 @@ function setPerPage(select){
57
58
  <h3 class="wi">Recent job statuses</h3>
58
59
  <div class="nav-container">
59
60
  <%= erb :_paging, locals: { url: "#{root_path}statuses" } %>
61
+ <div class="filter-status">
62
+ Filter Status:
63
+ <select class="form-control" onchange="setPerPage(this)">
64
+ <% (['all', 'complete', 'failed', 'interrupted', 'queued', 'retrying', 'stopped', 'working']).each do |status| %>
65
+ <option data-url="?<%= qparams(status: status)%>" value="<%= status %>" <%= 'selected="selected"' if status == (params[:status]) %>><%= status %></option>
66
+ <% end %>
67
+ </select>
68
+ </div>
69
+
60
70
  <div class="per-page">
61
71
  Per page:
62
72
  <select class="form-control" onchange="setPerPage(this)">
@@ -67,11 +77,11 @@ function setPerPage(select){
67
77
  </div>
68
78
  </div>
69
79
  </div>
70
- <table class="table table-hover table-bordered table-striped table-white">
80
+ <table class="table table-hover table-bordered table-striped">
71
81
  <tr>
72
- <% @headers.each do |h| %>
73
- <th class="header <%= h[:class] %> header_<%= h[:id] %>">
74
- <a href="<%= h[:url] %>"><%= h[:name] %></a>
82
+ <% @headers.each do |hdr| %>
83
+ <th class="header <%= h hdr[:class] %> header_<%= h hdr[:id] %>">
84
+ <a href="<%= h hdr[:url] %>"><%= h hdr[:name] %></a>
75
85
  </th>
76
86
  <% end %>
77
87
  <th class="header">
@@ -81,13 +91,13 @@ function setPerPage(select){
81
91
  <% @statuses.each do |container| %>
82
92
  <tr>
83
93
  <td>
84
- <div title='<%= container["jid"] %>'><a href="<%= root_path %>statuses/<%= container["jid"] %>"><%= container["worker"] %></a></div>
94
+ <div title='<%= h container["jid"] %>'><a href="<%= root_path %>statuses/<%= h container["jid"] %>"><%= h container["worker"] %></a></div>
85
95
  </td>
86
96
  <td>
87
- <div class='args' title='<%= container["jid"] %>'><%= container["args"] %></div>
97
+ <div class='args' title='<%= h container["jid"] %>'><%= h container["args"] %></div>
88
98
  </td>
89
99
  <td style='text-align: center;'>
90
- <div class='label label-<%= container["label"] %>'><%= container["status"] %></div>
100
+ <div class='label label-<%= h container["label"] %>'><%= h container["status"] %></div>
91
101
  </td>
92
102
  <% secs = Time.now.to_i - container["update_time"].to_i %>
93
103
  <td style='text-align: center; white-space: nowrap;' title="<%= Time.at(container["update_time"].to_i) %>">
@@ -100,11 +110,11 @@ function setPerPage(select){
100
110
  <td>
101
111
  <div class="progress progress-striped" style="margin-bottom: 0">
102
112
  <div class='message' style='text-align:right; padding-right:0.5em; background-color: transparent; float:right;'>
103
- <%= container["message"] %>
113
+ <%= h container["message"] %>
104
114
  </div>
105
115
  <% if container["pct_complete"].to_i > 0 %>
106
- <div class="bar message" style="width: <%= container["pct_complete"] %>%;">
107
- <%= container["pct_complete"] %>%
116
+ <div class="bar message" style="width: <%= h container["pct_complete"] %>%;">
117
+ <%= h container["pct_complete"] %>%
108
118
  </div>
109
119
  <% end %>
110
120
  </div>
@@ -112,7 +122,7 @@ function setPerPage(select){
112
122
  <td>
113
123
  <div class="actions">
114
124
  <form action="statuses" method="post">
115
- <input type="hidden" name="jid" value="<%= container["jid"] %>" />
125
+ <input type="hidden" name="jid" value="<%= h container["jid"] %>" />
116
126
  <%= csrf_tag %>
117
127
  <% if container["status"] == "complete" %>
118
128
  <input type="hidden" name="_method" value="delete" />
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-status
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 2.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evgeniy Tsvigun
8
8
  - Kenaniah Cerny
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-08-11 00:00:00.000000000 Z
12
+ date: 2022-02-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: sidekiq
@@ -17,14 +17,14 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '3.0'
20
+ version: '5.0'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: '3.0'
27
+ version: '5.0'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: chronic_duration
30
30
  requirement: !ruby/object:Gem::Requirement
@@ -123,7 +123,7 @@ dependencies:
123
123
  - - ">="
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
- description:
126
+ description:
127
127
  email:
128
128
  - utgarda@gmail.com
129
129
  - kenaniah@gmail.com
@@ -132,6 +132,7 @@ extensions: []
132
132
  extra_rdoc_files: []
133
133
  files:
134
134
  - ".gitignore"
135
+ - ".gitlab-ci.yml"
135
136
  - ".rspec"
136
137
  - ".travis.yml"
137
138
  - Appraisals
@@ -140,9 +141,9 @@ files:
140
141
  - LICENSE
141
142
  - README.md
142
143
  - Rakefile
143
- - gemfiles/sidekiq_3.x.gemfile
144
- - gemfiles/sidekiq_4.x.gemfile
145
144
  - gemfiles/sidekiq_5.x.gemfile
145
+ - gemfiles/sidekiq_6.1.gemfile
146
+ - gemfiles/sidekiq_6.x.gemfile
146
147
  - lib/sidekiq-status.rb
147
148
  - lib/sidekiq-status/client_middleware.rb
148
149
  - lib/sidekiq-status/server_middleware.rb
@@ -167,11 +168,11 @@ files:
167
168
  - web/views/status.erb
168
169
  - web/views/status_not_found.erb
169
170
  - web/views/statuses.erb
170
- homepage: http://github.com/utgarda/sidekiq-status
171
+ homepage: https://github.com/kenaniah/sidekiq-status
171
172
  licenses:
172
173
  - MIT
173
174
  metadata: {}
174
- post_install_message:
175
+ post_install_message:
175
176
  rdoc_options: []
176
177
  require_paths:
177
178
  - lib
@@ -186,9 +187,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
186
187
  - !ruby/object:Gem::Version
187
188
  version: '0'
188
189
  requirements: []
189
- rubyforge_project:
190
- rubygems_version: 2.7.3
191
- signing_key:
190
+ rubygems_version: 3.2.32
191
+ signing_key:
192
192
  specification_version: 4
193
193
  summary: An extension to the sidekiq message processing to track your jobs
194
194
  test_files: