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 +4 -4
- data/README.md +25 -2
- data/db/migrate/20190916154829_add_fork_count_to_canvas_sync_job_logs.rb +13 -0
- data/lib/canvas_sync.rb +13 -0
- data/lib/canvas_sync/jobs/fork_gather.rb +26 -0
- data/lib/canvas_sync/jobs/sync_provisioning_report_job.rb +17 -7
- data/lib/canvas_sync/jobs/sync_simple_table_job.rb +17 -7
- data/lib/canvas_sync/version.rb +1 -1
- data/spec/canvas_sync/jobs/fork_gather_spec.rb +30 -0
- data/spec/canvas_sync/jobs/sync_provisioning_report_job_spec.rb +3 -0
- data/spec/canvas_sync/jobs/sync_simple_table_job_spec.rb +1 -0
- data/spec/dummy/db/schema.rb +2 -1
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f1cc1054e379b920c5acee75583700fdffaae885a4cea48df1d8de97ed70c02e
|
|
4
|
+
data.tar.gz: 83741af3ec47ee4b72db5e4a354a8713c002fc6930cfe352a80c5f57a36dba4a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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)
|
data/lib/canvas_sync/version.rb
CHANGED
|
@@ -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(
|
data/spec/dummy/db/schema.rb
CHANGED
|
@@ -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:
|
|
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
|
+
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
|
+
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
|