canvas_sync 0.10.4 → 0.10.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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