canvas_sync 0.10.4 → 0.10.5

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: 3094e5606d612a10ba9f2fb9facf242d4fcfdbe561029f801642a5e1223b000b
4
- data.tar.gz: ace716fc5b7e53a3d0f95849c17c1655dc130554ad33231db65099f41c283afa
3
+ metadata.gz: f1cc1054e379b920c5acee75583700fdffaae885a4cea48df1d8de97ed70c02e
4
+ data.tar.gz: 83741af3ec47ee4b72db5e4a354a8713c002fc6930cfe352a80c5f57a36dba4a
5
5
  SHA512:
6
- metadata.gz: 2e94c64b6d9768069a03af99281c771720ebd411bb8d858a9bed9cc13fcf45d3f223e82b76c4018a42467c420131aac18cf4225ddf4c7eae37b55fffbea68d58
7
- data.tar.gz: aa51f5e60f81e4b4fcf4808cb446947cc154a1f7d31df525621b792610acdeaec59edad52a26758ec47ece97e5c81e99bf66104157ae56a372fabee5bbbdd73d
6
+ metadata.gz: dc7ad8ad007bda5edb7cf980b767113075a4be84cf1c2a228cf8287f8aba2c587bb170574a0168216b0bcaa19c8c395224d37249954b23203a50df234e5d9599
7
+ data.tar.gz: be307cba8401af1b7697d4c843d260e290517aca0f3d3866a72db6f327f6d3726df894a5306f2b39f2e10eacbb22465a45b5b8398c810304d53145ef59a17ad7
data/README.md CHANGED
@@ -91,6 +91,29 @@ This gem also helps with syncing and processing other reports if needed. In orde
91
91
  - Integrate your reports with the `ReportStarter`
92
92
  - Tell the gem what jobs to run
93
93
 
94
+ ### Extensible chain
95
+ It is sometimes desired to extend or customize the chain of jobs that are run with CanvasSync.
96
+ This can be achieved with the following pattern:
97
+
98
+ ```ruby
99
+ job_chain = CanvasSync.default_provisioning_report_chain(
100
+ %i[list of models to sync]
101
+ )
102
+
103
+ # CanvasSync forks the chain for each term within a term scope. The ForkGather Job can be used to unfork the chain.
104
+ # For example multiple Terms are in the term_scope. CanvasSync syncs Accounts, Terms, and Users (if enabled) and then
105
+ # forks the chain to sync other models (eg Course, Sections, etc.) per-Term.
106
+ # ForkGather will wait until all the forked chains are complete before continuing.
107
+ # TL;DR: Jobs placed after SyncProvisioningReportJob and before ForkGather will run once per Term per Sync;
108
+ # Jobs placed before SyncProvisioningReportJob or after ForkGather will run once per Sync
109
+ job_chain[:jobs] << { job: CanvasSync::Jobs::ForkGather, options: {} }
110
+
111
+ # Add a custom job to the end of the chain. Custom jobs must accept 2 arguments (job_chain, options) and call CanvasSync.invoke_next(job_chain) when complete
112
+ job_chain[:jobs] << { job: CanvasSyncCompleteWorker, options: { job_id: job.id } }
113
+
114
+ CanvasSync.invoke_next(job_chain)
115
+ ```
116
+
94
117
  ### Processor
95
118
 
96
119
  Your processor class must implement a `process` class method that receives a `report_file_path` and a hash of `options`. (See the `CanvasSync::Processors::ProvisioningReportProcessor` for an example.) The gem handles the work of enqueueing and downloading the report and then passes the file path to your class to process as needed. A simple example might be:
@@ -141,7 +164,7 @@ The `CanvasSync.process_jobs` method allows you to pass in a chain of jobs to ru
141
164
 
142
165
  Here is an example that runs our new report job first followed by the builtin provisioning job:
143
166
 
144
- ```
167
+ ```ruby
145
168
  job_chain = {
146
169
  jobs: [
147
170
  { job: MyReallyCoolReportJob, options: {} },
@@ -156,7 +179,7 @@ CanvasSync.process_jobs(job_chain)
156
179
  What if you've got some other job that you want run that doesn't deal with a report? No problem! Just make sure you call `CanvasSync.invoke_next` at the end of your job. Example:
157
180
 
158
181
 
159
- ```
182
+ ```ruby
160
183
  class SomeRandomJob < CanvasSync::Job
161
184
  def perform(job_chain, options)
162
185
  i_dunno_do_something!
@@ -0,0 +1,13 @@
1
+ if Rails.version.to_f >= 5.0
2
+ class AddForkCountToCanvasSyncJobLogs < ActiveRecord::Migration[Rails.version.to_f]
3
+ def change
4
+ add_column :canvas_sync_job_logs, :fork_count, :integer
5
+ end
6
+ end
7
+ else
8
+ class AddForkCountToCanvasSyncJobLogs < ActiveRecord::Migration
9
+ def change
10
+ add_column :canvas_sync_job_logs, :fork_count, :integer
11
+ end
12
+ end
13
+ end
data/lib/canvas_sync.rb CHANGED
@@ -6,6 +6,7 @@ require "canvas_sync/sidekiq_job"
6
6
  require "canvas_sync/api_syncable"
7
7
  require "canvas_sync/jobs/report_starter"
8
8
  require "canvas_sync/jobs/report_checker"
9
+ require "canvas_sync/jobs/fork_gather"
9
10
  require "canvas_sync/jobs/report_processor_job"
10
11
  require "canvas_sync/jobs/sync_provisioning_report_job"
11
12
  require "canvas_sync/jobs/sync_simple_table_job.rb"
@@ -118,6 +119,18 @@ module CanvasSync
118
119
  next_job_class.perform_later(duped_job_chain, next_job[:options])
119
120
  end
120
121
 
122
+ def fork(job_log, job_chain, keys: [])
123
+ duped_job_chain = Marshal.load(Marshal.dump(job_chain))
124
+ duped_job_chain[:global_options][:fork_path] ||= []
125
+ duped_job_chain[:global_options][:fork_keys] ||= []
126
+ duped_job_chain[:global_options][:fork_path] << job_log.job_id
127
+ duped_job_chain[:global_options][:fork_keys] << ['canvas_term_id']
128
+ sub_items = yield duped_job_chain
129
+ sub_count = sub_items.respond_to?(:count) ? sub_items.count : sub_items
130
+ job_log.update!(fork_count: sub_count)
131
+ sub_items
132
+ end
133
+
121
134
  # Syn any report to an specific set of models
122
135
  #
123
136
  # @param reports_mapping [Array<Hash>] List of reports with their specific model and params
@@ -0,0 +1,26 @@
1
+ module CanvasSync
2
+ module Jobs
3
+ class ForkGather < CanvasSync::Job
4
+ def perform(job_chain, options)
5
+ fork_item = (job_chain[:global_options][:fork_path] || []).pop
6
+
7
+ if fork_item.present?
8
+ forked_job = CanvasSync::JobLog.find_by(job_id: fork_item)
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
+ (job_chain[:global_options][:fork_keys] || []).pop&.each do |k|
16
+ job_chain[:global_options].delete(k.to_sym)
17
+ end
18
+ CanvasSync.invoke_next(job_chain)
19
+ end
20
+ else
21
+ CanvasSync.invoke_next(job_chain)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -8,13 +8,23 @@ module CanvasSync
8
8
  # models to sync.
9
9
  def perform(job_chain, options)
10
10
  if options[:term_scope]
11
- Term.send(options[:term_scope]).find_each do |term|
12
- # Deep copy the job_chain so each report gets the correct term id passed into
13
- # its options with no side effects
14
- term_id = get_term_id(term)
15
- duped_job_chain = Marshal.load(Marshal.dump(job_chain))
16
- duped_job_chain[:global_options][:canvas_term_id] = term_id
17
- start_report(report_params(options, term_id), duped_job_chain, options)
11
+ sub_reports = CanvasSync.fork(@job_log, job_chain, keys: [:canvas_term_id]) do |job_chain|
12
+ Term.send(options[:term_scope]).find_each.map do |term|
13
+ # Deep copy the job_chain so each report gets the correct term id passed into
14
+ # its options with no side effects
15
+ term_id = get_term_id(term)
16
+ duped_job_chain = Marshal.load(Marshal.dump(job_chain))
17
+ duped_job_chain[:global_options][:canvas_term_id] = term_id
18
+ {
19
+ job_chain: duped_job_chain,
20
+ params: report_params(options, term_id),
21
+ options: options,
22
+ }
23
+ end
24
+ end
25
+
26
+ sub_reports.each do |r|
27
+ start_report(r[:params], r[:job_chain], r[:options])
18
28
  end
19
29
  else
20
30
  start_report(report_params(options), job_chain, options)
@@ -9,13 +9,23 @@ module CanvasSync
9
9
  # @param options [Hash]
10
10
  def perform(job_chain, options)
11
11
  if options[:term_scope]
12
- Term.send(options[:term_scope]).find_each do |term|
13
- # Deep copy the job_chain so each report gets the correct term id passed into
14
- # its options with no side effects
15
- term_id = get_term_id(term)
16
- duped_job_chain = Marshal.load(Marshal.dump(job_chain))
17
- duped_job_chain[:global_options][:canvas_term_id] = term_id
18
- start_report(report_params(options, term_id), duped_job_chain, options)
12
+ sub_reports = CanvasSync.fork(@job_log, job_chain, keys: [:canvas_term_id]) do |job_chain|
13
+ Term.send(options[:term_scope]).find_each.map do |term|
14
+ # Deep copy the job_chain so each report gets the correct term id passed into
15
+ # its options with no side effects
16
+ term_id = get_term_id(term)
17
+ duped_job_chain = Marshal.load(Marshal.dump(job_chain))
18
+ duped_job_chain[:global_options][:canvas_term_id] = term_id
19
+ {
20
+ job_chain: duped_job_chain,
21
+ params: report_params(options, term_id),
22
+ options: options,
23
+ }
24
+ end
25
+ end
26
+
27
+ sub_reports.each do |r|
28
+ start_report(r[:params], r[:job_chain], r[:options])
19
29
  end
20
30
  else
21
31
  start_report(report_params(options), job_chain, options)
@@ -1,3 +1,3 @@
1
1
  module CanvasSync
2
- VERSION = "0.10.4".freeze
2
+ VERSION = "0.10.5".freeze
3
3
  end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe CanvasSync::Jobs::ForkGather do
4
+ describe '#perform' do
5
+ let!(:job_log) { CanvasSync::JobLog.create!(job_id: 'BLAH', fork_count: 3) }
6
+ let(:job_chain) { {jobs: [], global_options: { fork_path: ['BLAH'] }} }
7
+
8
+ it 'decrements fork_count' do
9
+ CanvasSync::Jobs::ForkGather.perform_now(job_chain, {})
10
+ expect(job_log.reload.fork_count).to eq 2
11
+ end
12
+
13
+ it 'does not continue chain if fork_count > 0' do
14
+ expect(CanvasSync).not_to receive(:invoke_next)
15
+ CanvasSync::Jobs::ForkGather.perform_now(job_chain, {})
16
+ end
17
+
18
+ it 'continues the chain if fork_count <= 0' do
19
+ job_log.update!(fork_count: 1)
20
+ expect(CanvasSync).to receive(:invoke_next)
21
+ CanvasSync::Jobs::ForkGather.perform_now(job_chain, {})
22
+ end
23
+
24
+ it 'continues if no fork_path is specified' do
25
+ job_chain[:global_options].delete(:fork_path)
26
+ expect(CanvasSync).to receive(:invoke_next)
27
+ CanvasSync::Jobs::ForkGather.perform_now(job_chain, {})
28
+ end
29
+ end
30
+ end
@@ -12,6 +12,7 @@ RSpec.describe CanvasSync::Jobs::SyncProvisioningReportJob do
12
12
  it 'enqueues a ReportStarter for a provisioning report for the specified models for each term' do
13
13
  expected_job_chain = Marshal.load(Marshal.dump(job_chain))
14
14
  expected_job_chain[:global_options][:canvas_term_id] = active_term_1.canvas_id
15
+ expected_job_chain[:global_options] = hash_including(expected_job_chain[:global_options])
15
16
 
16
17
  expect(CanvasSync::Jobs::ReportStarter).to receive(:perform_later)
17
18
  .with(
@@ -31,6 +32,7 @@ RSpec.describe CanvasSync::Jobs::SyncProvisioningReportJob do
31
32
 
32
33
  expected_job_chain_2 = Marshal.load(Marshal.dump(job_chain))
33
34
  expected_job_chain_2[:global_options][:canvas_term_id] = active_term_2.canvas_id
35
+ expected_job_chain_2[:global_options] = hash_including(expected_job_chain_2[:global_options])
34
36
 
35
37
  expect(CanvasSync::Jobs::ReportStarter).to receive(:perform_later)
36
38
  .with(
@@ -52,6 +54,7 @@ RSpec.describe CanvasSync::Jobs::SyncProvisioningReportJob do
52
54
  job_chain,
53
55
  { models: ['users', 'courses'], term_scope: 'active' }
54
56
  )
57
+ expect(CanvasSync::JobLog.last.fork_count).to eq 2
55
58
  end
56
59
  end
57
60
 
@@ -11,6 +11,7 @@ RSpec.describe CanvasSync::Jobs::SyncSimpleTableJob do
11
11
  it 'enqueues a ReportStarter for a provisioning report for the specified model for a term' do
12
12
  expected_job_chain = Marshal.load(Marshal.dump(job_chain))
13
13
  expected_job_chain[:global_options][:canvas_term_id] = active_term_1.canvas_id
14
+ expected_job_chain[:global_options] = hash_including(expected_job_chain[:global_options])
14
15
 
15
16
  expect(CanvasSync::Jobs::ReportStarter).to receive(:perform_later)
16
17
  .with(
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 20190702203632) do
13
+ ActiveRecord::Schema.define(version: 20190916154829) do
14
14
 
15
15
  # These are extensions that must be enabled in order to support this database
16
16
  enable_extension "plpgsql"
@@ -92,6 +92,7 @@ ActiveRecord::Schema.define(version: 20190702203632) do
92
92
  t.datetime "created_at", null: false
93
93
  t.datetime "updated_at", null: false
94
94
  t.string "job_id"
95
+ t.integer "fork_count"
95
96
  t.index ["job_id"], name: "index_canvas_sync_job_logs_on_job_id"
96
97
  end
97
98
 
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.10.4
4
+ version: 0.10.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nate Collings
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-09-11 00:00:00.000000000 Z
11
+ date: 2019-09-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -319,6 +319,7 @@ files:
319
319
  - config/initializers/apartment.rb
320
320
  - db/migrate/20170915210836_create_canvas_sync_job_log.rb
321
321
  - db/migrate/20180725155729_add_job_id_to_canvas_sync_job_logs.rb
322
+ - db/migrate/20190916154829_add_fork_count_to_canvas_sync_job_logs.rb
322
323
  - lib/canvas_sync.rb
323
324
  - lib/canvas_sync/api_syncable.rb
324
325
  - lib/canvas_sync/config.rb
@@ -366,6 +367,7 @@ files:
366
367
  - lib/canvas_sync/importers/bulk_importer.rb
367
368
  - lib/canvas_sync/importers/legacy_importer.rb
368
369
  - lib/canvas_sync/job.rb
370
+ - lib/canvas_sync/jobs/fork_gather.rb
369
371
  - lib/canvas_sync/jobs/report_checker.rb
370
372
  - lib/canvas_sync/jobs/report_processor_job.rb
371
373
  - lib/canvas_sync/jobs/report_starter.rb
@@ -392,6 +394,7 @@ files:
392
394
  - lib/canvas_sync/sidekiq_job.rb
393
395
  - lib/canvas_sync/version.rb
394
396
  - spec/canvas_sync/canvas_sync_spec.rb
397
+ - spec/canvas_sync/jobs/fork_gather_spec.rb
395
398
  - spec/canvas_sync/jobs/job_spec.rb
396
399
  - spec/canvas_sync/jobs/report_checker_spec.rb
397
400
  - spec/canvas_sync/jobs/report_processor_job_spec.rb
@@ -562,6 +565,7 @@ specification_version: 4
562
565
  summary: Gem for generating Canvas models and migrations and syncing data from Canvas
563
566
  test_files:
564
567
  - spec/canvas_sync/canvas_sync_spec.rb
568
+ - spec/canvas_sync/jobs/fork_gather_spec.rb
565
569
  - spec/canvas_sync/jobs/job_spec.rb
566
570
  - spec/canvas_sync/jobs/report_checker_spec.rb
567
571
  - spec/canvas_sync/jobs/report_processor_job_spec.rb