canvas_sync 0.16.5 → 0.17.0.beta5

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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +49 -137
  3. data/app/models/canvas_sync/sync_batch.rb +5 -0
  4. data/db/migrate/20201018210836_create_canvas_sync_sync_batches.rb +11 -0
  5. data/lib/canvas_sync.rb +35 -97
  6. data/lib/canvas_sync/importers/bulk_importer.rb +4 -7
  7. data/lib/canvas_sync/job.rb +4 -10
  8. data/lib/canvas_sync/job_batches/batch.rb +403 -0
  9. data/lib/canvas_sync/job_batches/batch_aware_job.rb +62 -0
  10. data/lib/canvas_sync/job_batches/callback.rb +152 -0
  11. data/lib/canvas_sync/job_batches/chain_builder.rb +220 -0
  12. data/lib/canvas_sync/job_batches/context_hash.rb +147 -0
  13. data/lib/canvas_sync/job_batches/jobs/base_job.rb +7 -0
  14. data/lib/canvas_sync/job_batches/jobs/concurrent_batch_job.rb +19 -0
  15. data/lib/canvas_sync/job_batches/jobs/serial_batch_job.rb +75 -0
  16. data/lib/canvas_sync/job_batches/sidekiq.rb +93 -0
  17. data/lib/canvas_sync/job_batches/status.rb +83 -0
  18. data/lib/canvas_sync/jobs/begin_sync_chain_job.rb +35 -0
  19. data/lib/canvas_sync/jobs/report_checker.rb +3 -6
  20. data/lib/canvas_sync/jobs/report_processor_job.rb +2 -5
  21. data/lib/canvas_sync/jobs/report_starter.rb +28 -20
  22. data/lib/canvas_sync/jobs/sync_accounts_job.rb +3 -5
  23. data/lib/canvas_sync/jobs/sync_admins_job.rb +2 -4
  24. data/lib/canvas_sync/jobs/sync_assignment_groups_job.rb +2 -4
  25. data/lib/canvas_sync/jobs/sync_assignments_job.rb +2 -4
  26. data/lib/canvas_sync/jobs/sync_context_module_items_job.rb +2 -4
  27. data/lib/canvas_sync/jobs/sync_context_modules_job.rb +2 -4
  28. data/lib/canvas_sync/jobs/sync_provisioning_report_job.rb +4 -34
  29. data/lib/canvas_sync/jobs/sync_roles_job.rb +2 -5
  30. data/lib/canvas_sync/jobs/sync_simple_table_job.rb +11 -32
  31. data/lib/canvas_sync/jobs/sync_submissions_job.rb +2 -4
  32. data/lib/canvas_sync/jobs/sync_terms_job.rb +25 -8
  33. data/lib/canvas_sync/processors/assignment_groups_processor.rb +2 -3
  34. data/lib/canvas_sync/processors/assignments_processor.rb +2 -3
  35. data/lib/canvas_sync/processors/context_module_items_processor.rb +2 -3
  36. data/lib/canvas_sync/processors/context_modules_processor.rb +2 -3
  37. data/lib/canvas_sync/processors/normal_processor.rb +1 -2
  38. data/lib/canvas_sync/processors/provisioning_report_processor.rb +2 -10
  39. data/lib/canvas_sync/processors/submissions_processor.rb +2 -3
  40. data/lib/canvas_sync/version.rb +1 -1
  41. data/spec/canvas_sync/canvas_sync_spec.rb +136 -153
  42. data/spec/canvas_sync/jobs/job_spec.rb +9 -17
  43. data/spec/canvas_sync/jobs/report_checker_spec.rb +1 -3
  44. data/spec/canvas_sync/jobs/report_processor_job_spec.rb +0 -3
  45. data/spec/canvas_sync/jobs/report_starter_spec.rb +19 -28
  46. data/spec/canvas_sync/jobs/sync_admins_job_spec.rb +1 -4
  47. data/spec/canvas_sync/jobs/sync_assignment_groups_job_spec.rb +2 -1
  48. data/spec/canvas_sync/jobs/sync_assignments_job_spec.rb +3 -2
  49. data/spec/canvas_sync/jobs/sync_context_module_items_job_spec.rb +3 -2
  50. data/spec/canvas_sync/jobs/sync_context_modules_job_spec.rb +3 -2
  51. data/spec/canvas_sync/jobs/sync_provisioning_report_job_spec.rb +3 -35
  52. data/spec/canvas_sync/jobs/sync_roles_job_spec.rb +1 -4
  53. data/spec/canvas_sync/jobs/sync_simple_table_job_spec.rb +5 -12
  54. data/spec/canvas_sync/jobs/sync_submissions_job_spec.rb +2 -1
  55. data/spec/canvas_sync/jobs/sync_terms_job_spec.rb +1 -4
  56. data/spec/dummy/config/environments/test.rb +2 -0
  57. data/spec/dummy/db/schema.rb +9 -1
  58. data/spec/job_batching/batch_aware_job_spec.rb +100 -0
  59. data/spec/job_batching/batch_spec.rb +372 -0
  60. data/spec/job_batching/callback_spec.rb +38 -0
  61. data/spec/job_batching/flow_spec.rb +88 -0
  62. data/spec/job_batching/integration/integration.rb +57 -0
  63. data/spec/job_batching/integration/nested.rb +88 -0
  64. data/spec/job_batching/integration/simple.rb +47 -0
  65. data/spec/job_batching/integration/workflow.rb +134 -0
  66. data/spec/job_batching/integration_helper.rb +48 -0
  67. data/spec/job_batching/sidekiq_spec.rb +124 -0
  68. data/spec/job_batching/status_spec.rb +92 -0
  69. data/spec/job_batching/support/base_job.rb +14 -0
  70. data/spec/job_batching/support/sample_callback.rb +2 -0
  71. data/spec/spec_helper.rb +17 -0
  72. metadata +85 -8
  73. data/lib/canvas_sync/job_chain.rb +0 -102
  74. data/lib/canvas_sync/jobs/fork_gather.rb +0 -74
  75. data/spec/canvas_sync/jobs/fork_gather_spec.rb +0 -73
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CanvasSync::JobBatches::Batch::Status do
4
+ let(:bid) { 'BID' }
5
+ let(:batch) { CanvasSync::JobBatches::Batch.new(bid) }
6
+ subject { described_class.new(bid) }
7
+
8
+ describe '#join' do
9
+ it 'raises info' do
10
+ expect { subject.join }.to raise_error('Not supported')
11
+ end
12
+ end
13
+
14
+ describe '#pending' do
15
+ context 'when not initalized' do
16
+ it 'returns 0 pending jobs' do
17
+ expect(subject.pending).to eq(0)
18
+ end
19
+ end
20
+
21
+ context 'when more than 0' do
22
+ before { batch.jobs do TestWorker.perform_async end }
23
+ it 'returns pending jobs' do
24
+ expect(subject.pending).to eq(1)
25
+ end
26
+ end
27
+ end
28
+
29
+ describe '#failures' do
30
+ context 'when not initalized' do
31
+ it 'returns 0 failed jobs' do
32
+ expect(subject.failures).to eq(0)
33
+ end
34
+ end
35
+
36
+ context 'when more than 0' do
37
+ before { batch.increment_job_queue(bid) }
38
+ before { CanvasSync::JobBatches::Batch.process_failed_job(bid, 'FAILEDID') }
39
+
40
+ it 'returns failed jobs' do
41
+ expect(subject.failures).to eq(1)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#failure_info' do
47
+ context 'when not initalized' do
48
+ it 'returns empty array' do
49
+ expect(subject.failure_info).to eq([])
50
+ end
51
+ end
52
+
53
+ context 'when with error' do
54
+ before { CanvasSync::JobBatches::Batch.process_failed_job(bid, 'jid123') }
55
+
56
+ it 'returns array with failed jids' do
57
+ expect(subject.failure_info).to eq(['jid123'])
58
+ end
59
+ end
60
+ end
61
+
62
+ describe '#total' do
63
+ context 'when not initalized' do
64
+ it 'returns 0 failed jobs' do
65
+ expect(subject.total).to eq(0)
66
+ end
67
+ end
68
+
69
+ context 'when more than 0' do
70
+ before { batch.jobs do TestWorker.perform_async end }
71
+
72
+ it 'returns failed jobs' do
73
+ expect(subject.total).to eq(1)
74
+ end
75
+ end
76
+ end
77
+
78
+ describe '#data' do
79
+ it 'returns batch description' do
80
+ expect(subject.data).to include(total: 0, failures: 0, pending: 0, created_at: nil, complete: false, failure_info: [], parent_bid: nil)
81
+ end
82
+ end
83
+
84
+ describe '#created_at' do
85
+ it 'returns time' do
86
+ batch = CanvasSync::JobBatches::Batch.new
87
+ batch.jobs do TestWorker.perform_async end
88
+ status = described_class.new(batch.bid)
89
+ expect(status.created_at).not_to be_nil
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,14 @@
1
+ class BatchTestJobBase < ActiveJob::Base
2
+ def perform
3
+ end
4
+
5
+ def self.perform_async(*args)
6
+ perform_later(*args)
7
+ end
8
+ end
9
+
10
+ class FailingBatchTestJobBase < BatchTestJobBase
11
+ def perform
12
+ raise "Foo"
13
+ end
14
+ end
@@ -0,0 +1,2 @@
1
+ class SampleCallback; end
2
+ class SampleCallback2; end
@@ -13,6 +13,19 @@ require 'shoulda/matchers'
13
13
  require 'pry'
14
14
  require 'pry-nav'
15
15
 
16
+ require 'sidekiq/testing'
17
+ Sidekiq::Testing.fake!
18
+
19
+ require 'fakeredis/rspec'
20
+ Dir[File.dirname(__FILE__) + "/job_batching/support/**/*.rb"].each {|f| require f }
21
+
22
+ # Fix an issue with fakeredis with Redis >=4.2
23
+ class Redis::Connection::Memory
24
+ def exists(*keys)
25
+ keys.count { |key| data.key?(key) }
26
+ end
27
+ end
28
+
16
29
  ActiveRecord::Migration.maintain_test_schema!
17
30
 
18
31
  RSpec.configure do |config|
@@ -51,3 +64,7 @@ end
51
64
  def canvas_sync_client
52
65
  Bearcat::Client.new(token: 'cool-token', prefix: 'http://test.instructure.com')
53
66
  end
67
+
68
+ def set_batch_context(ctx)
69
+ allow_any_instance_of(ActiveJob::Base).to receive(:batch_context).and_return(ctx.with_indifferent_access)
70
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: canvas_sync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.5
4
+ version: 0.17.0.beta5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Collings
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-10-27 00:00:00.000000000 Z
11
+ date: 2020-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -234,6 +234,34 @@ dependencies:
234
234
  - - ">="
235
235
  - !ruby/object:Gem::Version
236
236
  version: '0'
237
+ - !ruby/object:Gem::Dependency
238
+ name: fakeredis
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ type: :development
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: '0'
251
+ - !ruby/object:Gem::Dependency
252
+ name: sidekiq
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - ">="
256
+ - !ruby/object:Gem::Version
257
+ version: '0'
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - ">="
263
+ - !ruby/object:Gem::Version
264
+ version: '0'
237
265
  - !ruby/object:Gem::Dependency
238
266
  name: activejob
239
267
  requirement: !ruby/object:Gem::Requirement
@@ -248,6 +276,20 @@ dependencies:
248
276
  - - ">="
249
277
  - !ruby/object:Gem::Version
250
278
  version: '0'
279
+ - !ruby/object:Gem::Dependency
280
+ name: redis
281
+ requirement: !ruby/object:Gem::Requirement
282
+ requirements:
283
+ - - ">="
284
+ - !ruby/object:Gem::Version
285
+ version: '4.2'
286
+ type: :runtime
287
+ prerelease: false
288
+ version_requirements: !ruby/object:Gem::Requirement
289
+ requirements:
290
+ - - ">="
291
+ - !ruby/object:Gem::Version
292
+ version: '4.2'
251
293
  - !ruby/object:Gem::Dependency
252
294
  name: rails
253
295
  requirement: !ruby/object:Gem::Requirement
@@ -316,10 +358,12 @@ files:
316
358
  - app/controllers/api/v1/health_check_controller.rb
317
359
  - app/controllers/api/v1/live_events_controller.rb
318
360
  - app/models/canvas_sync/job_log.rb
361
+ - app/models/canvas_sync/sync_batch.rb
319
362
  - config/initializers/apartment.rb
320
363
  - db/migrate/20170915210836_create_canvas_sync_job_log.rb
321
364
  - db/migrate/20180725155729_add_job_id_to_canvas_sync_job_logs.rb
322
365
  - db/migrate/20190916154829_add_fork_count_to_canvas_sync_job_logs.rb
366
+ - db/migrate/20201018210836_create_canvas_sync_sync_batches.rb
323
367
  - lib/canvas_sync.rb
324
368
  - lib/canvas_sync/api_syncable.rb
325
369
  - lib/canvas_sync/class_callback_executor.rb
@@ -377,8 +421,17 @@ files:
377
421
  - lib/canvas_sync/importers/bulk_importer.rb
378
422
  - lib/canvas_sync/importers/legacy_importer.rb
379
423
  - lib/canvas_sync/job.rb
380
- - lib/canvas_sync/job_chain.rb
381
- - lib/canvas_sync/jobs/fork_gather.rb
424
+ - lib/canvas_sync/job_batches/batch.rb
425
+ - lib/canvas_sync/job_batches/batch_aware_job.rb
426
+ - lib/canvas_sync/job_batches/callback.rb
427
+ - lib/canvas_sync/job_batches/chain_builder.rb
428
+ - lib/canvas_sync/job_batches/context_hash.rb
429
+ - lib/canvas_sync/job_batches/jobs/base_job.rb
430
+ - lib/canvas_sync/job_batches/jobs/concurrent_batch_job.rb
431
+ - lib/canvas_sync/job_batches/jobs/serial_batch_job.rb
432
+ - lib/canvas_sync/job_batches/sidekiq.rb
433
+ - lib/canvas_sync/job_batches/status.rb
434
+ - lib/canvas_sync/jobs/begin_sync_chain_job.rb
382
435
  - lib/canvas_sync/jobs/report_checker.rb
383
436
  - lib/canvas_sync/jobs/report_processor_job.rb
384
437
  - lib/canvas_sync/jobs/report_starter.rb
@@ -407,7 +460,6 @@ files:
407
460
  - lib/canvas_sync/sidekiq_job.rb
408
461
  - lib/canvas_sync/version.rb
409
462
  - spec/canvas_sync/canvas_sync_spec.rb
410
- - spec/canvas_sync/jobs/fork_gather_spec.rb
411
463
  - spec/canvas_sync/jobs/job_spec.rb
412
464
  - spec/canvas_sync/jobs/report_checker_spec.rb
413
465
  - spec/canvas_sync/jobs/report_processor_job_spec.rb
@@ -542,6 +594,19 @@ files:
542
594
  - spec/factories/submission_factory.rb
543
595
  - spec/factories/term_factory.rb
544
596
  - spec/factories/user_factory.rb
597
+ - spec/job_batching/batch_aware_job_spec.rb
598
+ - spec/job_batching/batch_spec.rb
599
+ - spec/job_batching/callback_spec.rb
600
+ - spec/job_batching/flow_spec.rb
601
+ - spec/job_batching/integration/integration.rb
602
+ - spec/job_batching/integration/nested.rb
603
+ - spec/job_batching/integration/simple.rb
604
+ - spec/job_batching/integration/workflow.rb
605
+ - spec/job_batching/integration_helper.rb
606
+ - spec/job_batching/sidekiq_spec.rb
607
+ - spec/job_batching/status_spec.rb
608
+ - spec/job_batching/support/base_job.rb
609
+ - spec/job_batching/support/sample_callback.rb
545
610
  - spec/spec_helper.rb
546
611
  - spec/support/fake_canvas.rb
547
612
  - spec/support/fixtures/canvas_responses/admins.json
@@ -578,9 +643,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
578
643
  version: '0'
579
644
  required_rubygems_version: !ruby/object:Gem::Requirement
580
645
  requirements:
581
- - - ">="
646
+ - - ">"
582
647
  - !ruby/object:Gem::Version
583
- version: '0'
648
+ version: 1.3.1
584
649
  requirements: []
585
650
  rubygems_version: 3.1.2
586
651
  signing_key:
@@ -588,7 +653,6 @@ specification_version: 4
588
653
  summary: Gem for generating Canvas models and migrations and syncing data from Canvas
589
654
  test_files:
590
655
  - spec/canvas_sync/canvas_sync_spec.rb
591
- - spec/canvas_sync/jobs/fork_gather_spec.rb
592
656
  - spec/canvas_sync/jobs/job_spec.rb
593
657
  - spec/canvas_sync/jobs/report_checker_spec.rb
594
658
  - spec/canvas_sync/jobs/report_processor_job_spec.rb
@@ -723,6 +787,19 @@ test_files:
723
787
  - spec/factories/submission_factory.rb
724
788
  - spec/factories/term_factory.rb
725
789
  - spec/factories/user_factory.rb
790
+ - spec/job_batching/batch_aware_job_spec.rb
791
+ - spec/job_batching/batch_spec.rb
792
+ - spec/job_batching/callback_spec.rb
793
+ - spec/job_batching/flow_spec.rb
794
+ - spec/job_batching/integration/integration.rb
795
+ - spec/job_batching/integration/nested.rb
796
+ - spec/job_batching/integration/simple.rb
797
+ - spec/job_batching/integration/workflow.rb
798
+ - spec/job_batching/integration_helper.rb
799
+ - spec/job_batching/sidekiq_spec.rb
800
+ - spec/job_batching/status_spec.rb
801
+ - spec/job_batching/support/base_job.rb
802
+ - spec/job_batching/support/sample_callback.rb
726
803
  - spec/spec_helper.rb
727
804
  - spec/support/fake_canvas.rb
728
805
  - spec/support/fixtures/canvas_responses/admins.json
@@ -1,102 +0,0 @@
1
- module CanvasSync
2
- class JobChain
3
- attr_reader :chain_data
4
-
5
- delegate_missing_to :chain_data
6
-
7
- VALID_PLACEMENT_PARAMETERS = %i[before after].freeze
8
-
9
- def initialize(chain_data)
10
- @chain_data = chain_data
11
- end
12
-
13
- def jobs
14
- chain_data[:jobs]
15
- end
16
-
17
- def global_options
18
- chain_data[:global_options]
19
- end
20
-
21
- def merge_options(job, options)
22
- jobs.each do |j|
23
- j[:options].deep_merge!(options) if job_matches_pattern(j, job)
24
- end
25
- end
26
-
27
- def insert(new_job, **kwargs)
28
- invalid_params = kwargs.keys - VALID_PLACEMENT_PARAMETERS
29
- raise "Invalid placement parameters: #{invalid_params.map(&:to_s).join(', ')}" if invalid_params.present?
30
- raise "Exactly one placement parameter may be provided" if kwargs.values.compact!.length > 1
31
-
32
- if !kwargs.present?
33
- jobs << new_job
34
- else
35
- placement = kwargs.keys[0]
36
- relative_to = kwargs.values[0]
37
-
38
- index = jobs.index { |job| job_matches_pattern(job, relative_to) }
39
- raise "Could not find a \"#{relative_to}\" job in the chain" if index.nil?
40
-
41
- index += 1 if placement == :after
42
- new_job[:job] = new_job[:job].to_s
43
- jobs.insert(index, new_job)
44
- end
45
- end
46
-
47
- def process!(extra_options: {})
48
- perform_next(extra_options)
49
- end
50
-
51
- def duplicate
52
- self.class.new(Marshal.load(Marshal.dump(chain_data)))
53
- end
54
-
55
- def normalize!
56
- @chain_data[:global_options] ||= {}
57
- end
58
-
59
- def serialize
60
- normalize!
61
- chain_data
62
- end
63
-
64
- def perform_next(extra_options = {})
65
- return if jobs.empty?
66
-
67
- # Make sure all job classes are serialized as strings
68
- jobs.each { |job| job[:job] = job[:job].to_s }
69
-
70
- duped_job_chain = duplicate
71
-
72
- jobs = duped_job_chain[:jobs]
73
- next_job = jobs.shift
74
- next_job_class = next_job[:job].constantize
75
- next_options = next_job[:options] || {}
76
- next_options.merge!(extra_options)
77
- next_job_class.perform_later(duped_job_chain.serialize, next_options)
78
- end
79
-
80
- def fork(job_log, keys: [])
81
- duped_job_chain = duplicate
82
- duped_job_chain[:fork_state] ||= {}
83
- duped_job_chain[:fork_state][:forking_path] ||= []
84
- duped_job_chain[:fork_state][:pre_fork_globals] ||= []
85
-
86
- duped_job_chain[:fork_state][:forking_path] << job_log.job_id
87
- duped_job_chain[:fork_state][:pre_fork_globals] << global_options
88
- # duped_job_chain[:global_options][:on_failure] ||= ['CanvasSync::Jobs::ForkGather.handle_branch_error']
89
-
90
- sub_items = yield duped_job_chain
91
- sub_count = sub_items.respond_to?(:count) ? sub_items.count : sub_items
92
- job_log.update!(fork_count: sub_count)
93
- sub_items
94
- end
95
-
96
- private
97
-
98
- def job_matches_pattern(job_entry, pattern)
99
- job_entry[:job].to_s == pattern.to_s
100
- end
101
- end
102
- end
@@ -1,74 +0,0 @@
1
- module CanvasSync
2
- module Jobs
3
- class ForkGather < CanvasSync::Job
4
- def perform(job_chain, options)
5
- forked_job = self.class.forked_at_job(job_chain)
6
-
7
- while true
8
- if forked_job.present?
9
- forked_job.with_lock do
10
- forked_job.fork_count -= 1
11
- forked_job.save!
12
- end
13
-
14
- if forked_job.fork_count <= 0
15
- pfgs = job_chain[:fork_state][:pre_fork_globals].pop
16
- job_chain[:global_options] = pfgs
17
-
18
- if options[:gather_all]
19
- # If we want to gather all, repeat for the next level fork
20
- forked_job = self.class.forked_at_job(job_chain)
21
- else
22
- forked_job = nil
23
- end
24
- else
25
- # If a fork was found and it isn't complete, break the loop before continuing the chain
26
- break
27
- end
28
-
29
- # Repeat this logic for [if gather_all] the next fork up, or [if not gather_all] nil
30
- next
31
- end
32
-
33
- # If there is no current fork (either not in a fork, or all forks were closed), continue the chain
34
- CanvasSync.invoke_next(job_chain)
35
- break
36
- end
37
- end
38
-
39
- def self.handle_branch_error(e, job_chain:, skip_invoke: false, **kwargs)
40
- return nil unless job_chain&.dig(:fork_state, :forking_path).present?
41
-
42
- duped_chain = CanvasSync.duplicate_chain(job_chain)
43
- job_list = duped_chain[:jobs]
44
- while job_list.count > 0
45
- job_class_name = job_list[0][:job]
46
- job_class = job_class_name.constantize
47
- break if job_class <= CanvasSync::Jobs::ForkGather
48
- job_list.shift
49
- end
50
-
51
- return nil unless job_list.present?
52
-
53
- if skip_invoke
54
- duped_chain
55
- else
56
- CanvasSync.invoke_next(duped_chain)
57
- true
58
- end
59
- end
60
-
61
- protected
62
-
63
- def self.forked_at_job(job_chain)
64
- fork_item = (job_chain.dig(:fork_state, :forking_path) || []).pop
65
-
66
- if fork_item.present?
67
- CanvasSync::JobLog.find_by(job_id: fork_item)
68
- else
69
- nil
70
- end
71
- end
72
- end
73
- end
74
- end