sidekiq-status 1.1.0 → 2.1.3

Sign up to get free protection for your applications and to get access to all the features.
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: