resque-approve 0.0.7 → 0.0.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/ClassLength
4
+
3
5
  module Resque
4
6
  module Plugins
5
7
  module Approve
@@ -15,6 +17,24 @@ module Resque
15
17
  @approval_key = approval_key
16
18
  end
17
19
 
20
+ def num_running
21
+ redis.get(running_key)
22
+ end
23
+
24
+ def reset_running
25
+ redis.set(running_key, 0)
26
+ end
27
+
28
+ def increment_running
29
+ Resque::Plugins::Approve::ApprovalKeyList.new.add_key approval_key
30
+ redis.incr(running_key)
31
+ end
32
+
33
+ def decrement_running
34
+ Resque::Plugins::Approve::ApprovalKeyList.new.add_key approval_key
35
+ redis.decr(running_key)
36
+ end
37
+
18
38
  def pause
19
39
  redis.set(pause_key, true)
20
40
  redis.set(paused_count_key, 0)
@@ -165,6 +185,10 @@ module Resque
165
185
  @pause_key ||= "approve.job_queue.#{approval_key}.paused"
166
186
  end
167
187
 
188
+ def running_key
189
+ @running_key ||= "approve.job_queue.#{approval_key}.running"
190
+ end
191
+
168
192
  def paused_count_key
169
193
  @paused_count_key ||= "approve.job_queue.#{approval_key}.paused.count"
170
194
  end
@@ -172,3 +196,5 @@ module Resque
172
196
  end
173
197
  end
174
198
  end
199
+
200
+ # rubocop:enable Metrics/ClassLength
@@ -3,7 +3,7 @@
3
3
  module Resque
4
4
  module Plugins
5
5
  module Approve
6
- VERSION = "0.0.7"
6
+ VERSION = "0.0.15"
7
7
  end
8
8
  end
9
9
  end
@@ -2,12 +2,15 @@
2
2
  <tr>
3
3
  <td>
4
4
  <a href="<%= u("approve") %>/job_list?<%= { approval_key: pending_job.approval_key }.to_param %>">
5
- <%= pending_job.approval_key %>
5
+ <%= pending_job.approval_key.presence || "&nbsp;&nbsp;&nbsp;".html_safe %>
6
6
  </a>
7
7
  </td>
8
8
  <td>
9
9
  <%= pending_job.num_jobs.to_i %>
10
10
  </td>
11
+ <td>
12
+ <%= pending_job.num_running.to_i %>
13
+ </td>
11
14
  <td>
12
15
  <% if pending_job.first_enqueued.present? %>
13
16
  <%= time_ago_in_words(pending_job.first_enqueued) %> ago
@@ -19,7 +19,7 @@
19
19
  (<%= job_details.queue_time %>)
20
20
  </td>
21
21
  <td>
22
- <pre><code><%= "".html_safe + job_details.args.to_yaml %></code></pre>
22
+ <pre><code><%= "".html_safe + job_details.uncompressed_args.to_yaml %></code></pre>
23
23
  </td>
24
24
  <td>
25
25
  <pre><code><%= "".html_safe + job_details.approve_options.to_hash.to_yaml %></code></pre>
@@ -28,6 +28,9 @@
28
28
  order_param("num_jobs", @sort_by, @sort_order) }.to_param %>">
29
29
  Pending Jobs
30
30
  </a></th>
31
+ <th>
32
+ Num Running
33
+ </th>
31
34
  <th><a href="<%= u("approve") %>?<%=
32
35
  { sort: "first_enqueued",
33
36
  page_size: @page_size,
@@ -64,3 +67,6 @@
64
67
  </div>
65
68
  <div class="approve_reset"/>
66
69
  <br/>
70
+ <div>
71
+ Approval v<%= Resque::Plugins::Approve::VERSION %>
72
+ </div>
@@ -41,7 +41,7 @@
41
41
  Params
42
42
  </td>
43
43
  <td>
44
- <pre><code><%= "".html_safe + job_details.args.to_yaml %></code></pre>
44
+ <pre><code><%= "".html_safe + job_details.uncompressed_args.to_yaml %></code></pre>
45
45
  </td>
46
46
  </tr>
47
47
  <tr>
@@ -80,3 +80,6 @@
80
80
  <div class="approve_reset"/>
81
81
 
82
82
  <br/>
83
+ <div>
84
+ Approval v<%= Resque::Plugins::Approve::VERSION %>
85
+ </div>
@@ -12,6 +12,10 @@
12
12
  </a>
13
13
  </p>
14
14
 
15
+ <p>
16
+ Num active jobs in this queue = <%= pending_queue.num_running %>
17
+ </p>
18
+
15
19
  <% if pending_queue.paused? %>
16
20
  <p>
17
21
  Paused. Skipped <%= pending_queue.num_ignored %> resume actions.
@@ -60,6 +64,11 @@
60
64
  </form>
61
65
  <% end %>
62
66
 
67
+ <form method="POST" action="<%= u("approve/reset_running") %>?<%=
68
+ { approval_key: pending_queue.approval_key }.to_param %>">
69
+ <input type="submit" name="" value="Reset Running"/>
70
+ </form>
71
+
63
72
  <% if pending_queue.paused? %>
64
73
  <form method="POST" action="<%= u("approve/resume") %>?<%=
65
74
  { approval_key: pending_queue.approval_key }.to_param %>">
@@ -74,3 +83,8 @@
74
83
 
75
84
  <div class="approve_reset"/>
76
85
  <br/>
86
+ <div>
87
+ <p>
88
+ Approval v<%= Resque::Plugins::Approve::VERSION %>
89
+ </p>
90
+ </div>
@@ -355,7 +355,7 @@ RSpec.describe Resque::Plugins::Approve::PendingJobQueue do
355
355
  describe "first_enqueued" do
356
356
  it "returns the time of the first enqueued item" do
357
357
  4.times do |index|
358
- expect(job_queue.first_enqueued).to be_within(1.second).of((5 - index).hours.ago)
358
+ expect(job_queue.first_enqueued).to be_within(2.seconds).of((5 - index).hours.ago)
359
359
 
360
360
  job_queue.remove_one
361
361
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "rails_helper"
4
4
 
5
- # rubocop:disable Layout/AlignHash
6
5
  RSpec.describe Resque::Plugins::Approve::PendingJob do
7
6
  let(:key_list) { Resque::Plugins::Approve::ApprovalKeyList.new }
8
7
  let!(:key) { Faker::Lorem.sentence }
@@ -38,6 +37,20 @@ RSpec.describe Resque::Plugins::Approve::PendingJob do
38
37
 
39
38
  job
40
39
  end
40
+ let(:approval_require_approval_job) do
41
+ job = Resque::Plugins::Approve::PendingJob.new(SecureRandom.uuid, class_name: job_class, args: [requires_approval: true])
42
+
43
+ key_list.add_job(job)
44
+
45
+ job
46
+ end
47
+ let(:approval_hash_only_args_job) do
48
+ job = Resque::Plugins::Approve::PendingJob.new(SecureRandom.uuid, class_name: job_class, args: [some_hash: "hash", approval_key: key])
49
+
50
+ key_list.add_job(job)
51
+
52
+ job
53
+ end
41
54
  let(:approval_no_hash_args_job) do
42
55
  job = Resque::Plugins::Approve::PendingJob.new(SecureRandom.uuid,
43
56
  class_name: job_class,
@@ -87,36 +100,58 @@ RSpec.describe Resque::Plugins::Approve::PendingJob do
87
100
  describe "initialize" do
88
101
  it "extracts delay arguments from job with no argument" do
89
102
  expect(no_args_job.args).to eq []
103
+ expect(Resque::Plugins::Approve::PendingJob.new(no_args_job.id).args).to eq []
90
104
  expect(no_args_job.approve_options).to eq({}.with_indifferent_access)
91
105
  end
92
106
 
93
107
  it "extracts delay arguments from job with no hash arguments" do
94
108
  expect(no_hash_args_job.args).to eq [1, "fred", "something else", 888]
109
+ expect(Resque::Plugins::Approve::PendingJob.new(no_hash_args_job.id).args).to eq [1, "fred", "something else", 888]
95
110
  expect(no_hash_args_job.approve_options).to eq({}.with_indifferent_access)
96
111
  end
97
112
 
98
113
  it "extracts delay arguments from job with no approval arguments" do
99
114
  expect(hash_args_job.args).to eq [1, "fred", "something else", 888, "other_arg" => 1, "something else" => "something"]
115
+ expect(Resque::Plugins::Approve::PendingJob.new(hash_args_job.id).args).
116
+ to eq [1, "fred", "something else", 888, "other_arg" => 1, "something else" => "something"]
100
117
  expect(hash_args_job.approve_options).to eq({}.with_indifferent_access)
101
118
  end
102
119
 
103
120
  it "extracts delay arguments from job with no argument and approval_args" do
104
121
  expect(approval_only_args_job.args).to eq []
122
+ expect(Resque::Plugins::Approve::PendingJob.new(approval_only_args_job.id).args).to eq []
105
123
  expect(approval_only_args_job.approve_options).to eq("approval_key" => key)
106
124
  end
107
125
 
126
+ it "extracts delay arguments from job with no argument and require_approval" do
127
+ expect(approval_require_approval_job.args).to eq []
128
+ expect(Resque::Plugins::Approve::PendingJob.new(approval_require_approval_job.id).args).to eq []
129
+ expect(approval_require_approval_job.approve_options).to eq("approval_key" => "Some_Queue", "requires_approval" => true)
130
+ end
131
+
132
+ it "extracts delay arguments from job with only hash arguments and approval_args" do
133
+ expect(approval_hash_only_args_job.args).to eq [{ "some_hash" => "hash" }]
134
+ expect(Resque::Plugins::Approve::PendingJob.new(approval_hash_only_args_job.id).args).to eq [{ "some_hash" => "hash" }]
135
+ expect(approval_hash_only_args_job.approve_options).to eq("approval_key" => key)
136
+ end
137
+
108
138
  it "extracts delay arguments from job with no hash arguments and approval args" do
109
139
  expect(approval_no_hash_args_job.args).to eq [1, "fred", "something else", 888]
140
+ expect(Resque::Plugins::Approve::PendingJob.new(approval_no_hash_args_job.id).args).to eq [1, "fred", "something else", 888]
110
141
  expect(approval_no_hash_args_job.approve_options).to eq("approval_key" => key)
111
142
  end
112
143
 
113
144
  it "extracts delay arguments from job with no approval arguments and approval args" do
114
145
  expect(approval_hash_args_job.args).to eq [1, "fred", "something else", 888, "other_arg" => 1, "something else" => "something"]
146
+ expect(Resque::Plugins::Approve::PendingJob.new(approval_hash_args_job.id).args).
147
+ to eq [1, "fred", "something else", 888, "other_arg" => 1, "something else" => "something"]
115
148
  expect(approval_hash_args_job.approve_options).to eq("approval_key" => key)
116
149
  end
117
150
 
118
151
  it "extracts all delay arguments from job with no approval arguments and approval args" do
119
152
  expect(approval_all_args_job.args).to eq [1, "fred", "something else", 888, "other_arg" => 1, "something else" => "something"]
153
+ expect(Resque::Plugins::Approve::PendingJob.new(approval_all_args_job.id).args).
154
+ to eq [1, "fred", "something else", 888, "other_arg" => 1, "something else" => "something"]
120
155
 
121
156
  expect(approval_all_args_job.approve_options[:approval_key]).to eq key
122
157
  expect(approval_all_args_job.approve_options["approval_key"]).to eq key
@@ -124,8 +159,8 @@ RSpec.describe Resque::Plugins::Approve::PendingJob do
124
159
  expect(approval_all_args_job.approve_options[:approval_queue]).to eq "Another Queue"
125
160
  expect(approval_all_args_job.approve_options["approval_queue"]).to eq "Another Queue"
126
161
 
127
- expect(approval_all_args_job.approve_options[:approval_at]).to be_within(2.seconds).of(2.hours.from_now)
128
- expect(approval_all_args_job.approve_options["approval_at"]).to be_within(2.seconds).of(2.hours.from_now)
162
+ expect(approval_all_args_job.approve_options[:approval_at].to_time).to be_within(2.seconds).of(2.hours.from_now)
163
+ expect(approval_all_args_job.approve_options["approval_at"].to_time).to be_within(2.seconds).of(2.hours.from_now)
129
164
  end
130
165
  end
131
166
 
@@ -165,6 +200,8 @@ RSpec.describe Resque::Plugins::Approve::PendingJob do
165
200
 
166
201
  it "has args" do
167
202
  expect(job.args).to eq [1, "fred", "something else", 888, "other_arg" => 1, "something else" => "something"]
203
+ expect(Resque::Plugins::Approve::PendingJob.new(job.id).args).
204
+ to eq [1, "fred", "something else", 888, "other_arg" => 1, "something else" => "something"]
168
205
  end
169
206
 
170
207
  it "has the approval_key" do
@@ -176,11 +213,11 @@ RSpec.describe Resque::Plugins::Approve::PendingJob do
176
213
  end
177
214
 
178
215
  it "has the approval_at" do
179
- expect(job.approval_at).to be_within(1.second).of(2.hours.from_now)
216
+ expect(job.approval_at).to be_within(2.seconds).of(2.hours.from_now)
180
217
  end
181
218
 
182
219
  it "has the queue_time" do
183
- expect(job.queue_time).to be_within(1.second).of(2.hours.ago)
220
+ expect(job.queue_time).to be_within(2.seconds).of(2.hours.ago)
184
221
  end
185
222
 
186
223
  it "has the queue" do
@@ -245,6 +282,148 @@ RSpec.describe Resque::Plugins::Approve::PendingJob do
245
282
 
246
283
  expect(job).not_to be_requires_approval
247
284
  end
285
+
286
+ it "requires approval if requires_approval is passed" do
287
+ expect(approval_require_approval_job).to be_requires_approval
288
+ end
289
+
290
+ context "MaxActiveJob" do
291
+ let(:job_class) { MaxActiveJob }
292
+ let(:num_queue) { Resque::Plugins::Approve::PendingJobQueue.new("Some_Queue") }
293
+
294
+ before(:each) do
295
+ num_queue.reset_running
296
+ end
297
+
298
+ it "does not require approval if num jobs below max" do
299
+ expect(approval_require_approval_job).not_to be_requires_approval
300
+
301
+ expect(approval_require_approval_job.args).not_to eq ["approval_key" => "Some_Queue"]
302
+ expect(approval_require_approval_job.uncompressed_args).to eq ["approval_key" => "Some_Queue"]
303
+ expect(Resque::Plugins::Approve::PendingJob.new(approval_require_approval_job.id).uncompressed_args).to eq ["approval_key" => "Some_Queue"]
304
+ expect(approval_require_approval_job.approve_options).to eq("approval_key" => "Some_Queue", "requires_approval" => true)
305
+ end
306
+
307
+ it "does require approval if num jobs above max" do
308
+ 10.times { num_queue.increment_running }
309
+
310
+ expect(approval_require_approval_job).to be_requires_approval
311
+
312
+ expect(approval_require_approval_job.args).not_to eq ["approval_key" => "Some_Queue"]
313
+ expect(approval_require_approval_job.uncompressed_args).to eq ["approval_key" => "Some_Queue"]
314
+ expect(Resque::Plugins::Approve::PendingJob.new(approval_require_approval_job.id).uncompressed_args).to eq ["approval_key" => "Some_Queue"]
315
+ expect(approval_require_approval_job.approve_options).to eq("approval_key" => "Some_Queue", "requires_approval" => true)
316
+ end
317
+
318
+ it "calls the perform function" do
319
+ allow(Resque.logger).to receive(:warn).and_call_original
320
+
321
+ MaxActiveJob.perform(*approval_require_approval_job.args)
322
+
323
+ expect(Resque.logger).to have_received(:warn).with("Args:\n[]").exactly(2).times
324
+ end
325
+
326
+ it "does not approve if number of running jobs too high" do
327
+ approval_require_approval_job
328
+
329
+ expect(Resque::Plugins::Approve::PendingJobQueue.new("Some_Queue").num_jobs).to eq 1
330
+
331
+ 10.times { num_queue.increment_running }
332
+
333
+ allow(Resque.logger).to receive(:warn).and_call_original
334
+
335
+ job_class.approve_one
336
+
337
+ expect(Resque.logger).not_to have_received(:warn)
338
+ expect(Resque::Plugins::Approve::PendingJobQueue.new("Some_Queue").num_jobs).to eq 1
339
+
340
+ expect(num_queue.num_running.to_i).to eq 10
341
+ end
342
+
343
+ it "approves the next item in the queue" do
344
+ allow(Resque::Plugins::Approve).to receive(:approve_one).and_call_original
345
+
346
+ MaxActiveJob.perform(*approval_require_approval_job.args)
347
+
348
+ expect(Resque::Plugins::Approve).to have_received(:approve_one).with("Some_Queue").exactly(2).times
349
+ end
350
+
351
+ it "approves the next item in the queue even if there is an exception" do
352
+ allow(Resque.logger).to receive(:warn).with("Args:\n[]").and_raise "Error"
353
+ allow(Resque::Plugins::Approve).to receive(:approve_one).and_call_original
354
+
355
+ expect { MaxActiveJob.perform(*approval_require_approval_job.args) }.to raise_error
356
+
357
+ expect(Resque.logger).to have_received(:warn).with("Args:\n[]").exactly(2).times
358
+ expect(Resque::Plugins::Approve).to have_received(:approve_one).with("Some_Queue").exactly(2).times
359
+ end
360
+
361
+ it "approves the next item in the queue if non-default queue used" do
362
+ job = Resque::Plugins::Approve::PendingJob.new(SecureRandom.uuid, class_name: job_class, args: [approval_key: "New Key"])
363
+
364
+ key_list.add_job(job)
365
+
366
+ allow(Resque.logger).to receive(:warn).and_call_original
367
+ allow(Resque::Plugins::Approve).to receive(:approve_one).and_call_original
368
+
369
+ MaxActiveJob.perform(*job.args)
370
+
371
+ expect(Resque.logger).to have_received(:warn).with("Args:\n[]").exactly(2).times
372
+ expect(Resque::Plugins::Approve).to have_received(:approve_one).with("New Key").exactly(2).times
373
+ end
374
+
375
+ it "approves the next item in the queue if non-default queue used and queue full" do
376
+ job = Resque::Plugins::Approve::PendingJob.new(SecureRandom.uuid, class_name: job_class, args: [requires_approval: true])
377
+
378
+ 10.times { num_queue.increment_running }
379
+ key_list.add_job(job)
380
+
381
+ allow(Resque.logger).to receive(:warn).and_call_original
382
+ allow(Resque::Plugins::Approve).to receive(:approve_one).and_call_original
383
+
384
+ MaxActiveJob.perform(*job.args)
385
+
386
+ expect(Resque::Plugins::Approve).to have_received(:approve_one).with("Some_Queue").exactly(2).times
387
+ expect(Resque.logger).to have_received(:warn).with("Args:\n[]").exactly(2).times
388
+ expect(num_queue.num_running.to_i).to eq 9
389
+ end
390
+
391
+ it "enqueues the job" do
392
+ allow(Resque.logger).to receive(:warn).and_call_original
393
+ allow(Resque::Plugins::Approve).to receive(:approve_one).and_call_original
394
+
395
+ Resque.enqueue MaxActiveJob, requires_approval: true
396
+
397
+ expect(Resque.logger).to have_received(:warn).with("Args:\n[]")
398
+ expect(Resque::Plugins::Approve).to have_received(:approve_one).with("Some_Queue")
399
+ end
400
+
401
+ it "enqueues a job with arguments" do
402
+ allow(Resque.logger).to receive(:warn).and_call_original
403
+ allow(Resque::Plugins::Approve).to receive(:approve_one).and_call_original
404
+
405
+ Resque.enqueue MaxActiveJob, param: "value", requires_approval: true
406
+
407
+ expect(Resque.logger).to have_received(:warn).with("Args:\n[{\"param\":\"value\"}]")
408
+ expect(Resque::Plugins::Approve).to have_received(:approve_one).with("Some_Queue")
409
+ end
410
+
411
+ it "does not enqueue a job if paused" do
412
+ num_queue.pause
413
+
414
+ expect(num_queue.num_jobs).to be_zero
415
+
416
+ allow(Resque.logger).to receive(:warn).and_call_original
417
+ allow(Resque::Plugins::Approve).to receive(:approve_one).and_call_original
418
+
419
+ Resque.enqueue MaxActiveJob, requires_approval: true
420
+
421
+ expect(Resque.logger).not_to have_received(:warn).with("Args:\n[]")
422
+ expect(Resque::Plugins::Approve).not_to have_received(:approve_one).with("Some_Queue")
423
+
424
+ expect(num_queue.num_jobs).to eq 1
425
+ end
426
+ end
248
427
  end
249
428
 
250
429
  describe "enqueue_job" do
@@ -322,5 +501,84 @@ RSpec.describe Resque::Plugins::Approve::PendingJob do
322
501
  expect(Resque::Plugins::Approve::ApprovalKeyList.new.num_queues).to eq 0
323
502
  end
324
503
  end
504
+
505
+ describe "max_active_jobs?" do
506
+ it "returns false if not set on class" do
507
+ expect(no_args_job.max_active_jobs?).to be_falsey
508
+ end
509
+
510
+ context "MaxActiveJob" do
511
+ let(:job_class) { MaxActiveJob }
512
+
513
+ it "returns true if a max is set" do
514
+ expect(no_args_job.max_active_jobs?).to be_truthy
515
+ end
516
+ end
517
+ end
518
+
519
+ describe "requires_approval" do
520
+ context "DefaultApprovalQueue" do
521
+ let(:job_class) { DefaultApprovalQueue }
522
+
523
+ it "sets the key to the default queue" do
524
+ expect(approval_require_approval_job.args).to eq []
525
+ expect(Resque::Plugins::Approve::PendingJob.new(approval_require_approval_job.id).args).to eq []
526
+ expect(approval_require_approval_job.approve_options).to eq("approval_key" => "Default Approval Queue", "requires_approval" => true)
527
+ end
528
+ end
529
+ end
530
+
531
+ describe "max_active_jobs" do
532
+ it "returns -1 if not set on class" do
533
+ expect(no_args_job.max_active_jobs).to eq(-1)
534
+ end
535
+
536
+ context "MaxActiveJob" do
537
+ let(:job_class) { MaxActiveJob }
538
+
539
+ it "returns value if max set" do
540
+ expect(no_args_job.max_active_jobs).to eq 10
541
+ end
542
+ end
543
+ end
544
+
545
+ describe "uncompressed_args" do
546
+ let(:test_args) { [1, "fred", "something else", 888, "approval_key" => "Some_Queue", "other_arg" => 1, "something else" => "something"] }
547
+ let(:compressed_args) { [{ :resque_compressed => true, :payload => MaxActiveJob.compressed_args(test_args) }] }
548
+ let(:job_class) { MaxActiveJob }
549
+
550
+ it "decompresses args" do
551
+ job = Resque::Plugins::Approve::PendingJob.new(SecureRandom.uuid,
552
+ class_name: job_class,
553
+ args: compressed_args)
554
+
555
+ key_list.add_job(job)
556
+
557
+ expect(Resque::Plugins::Approve::PendingJob.new(job.id).uncompressed_args).to eq test_args
558
+ end
559
+
560
+ it "does not decompress args if not compressed" do
561
+ job = Resque::Plugins::Approve::PendingJob.new(SecureRandom.uuid,
562
+ class_name: job_class,
563
+ args: test_args)
564
+
565
+ key_list.add_job(job)
566
+
567
+ expect(Resque::Plugins::Approve::PendingJob.new(job.id).uncompressed_args).to eq test_args
568
+ end
569
+
570
+ context "Not compressable" do
571
+ let(:test_args) { [1, "fred", "something else", 888, "other_arg" => 1, "something else" => "something"] }
572
+
573
+ it "does not decompress args if not compressable" do
574
+ job = Resque::Plugins::Approve::PendingJob.new(SecureRandom.uuid,
575
+ class_name: BasicJob,
576
+ args: test_args)
577
+
578
+ key_list.add_job(job)
579
+
580
+ expect(Resque::Plugins::Approve::PendingJob.new(job.id).uncompressed_args).to eq test_args
581
+ end
582
+ end
583
+ end
325
584
  end
326
- # rubocop:enable Layout/AlignHash