canvas_sync 0.12.0 → 0.16.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/lib/canvas_sync.rb +85 -30
  4. data/lib/canvas_sync/api_syncable.rb +4 -162
  5. data/lib/canvas_sync/class_callback_executor.rb +35 -0
  6. data/lib/canvas_sync/concerns/account/ancestry.rb +60 -0
  7. data/lib/canvas_sync/concerns/api_syncable.rb +189 -0
  8. data/lib/canvas_sync/concerns/legacy_columns.rb +34 -0
  9. data/lib/canvas_sync/generators/templates/migrations/create_group_memberships.rb +18 -0
  10. data/lib/canvas_sync/generators/templates/migrations/create_groups.rb +23 -0
  11. data/lib/canvas_sync/generators/templates/migrations/create_pseudonyms.rb +18 -0
  12. data/lib/canvas_sync/generators/templates/migrations/create_submissions.rb +1 -0
  13. data/lib/canvas_sync/generators/templates/models/account.rb +11 -1
  14. data/lib/canvas_sync/generators/templates/models/admin.rb +2 -1
  15. data/lib/canvas_sync/generators/templates/models/assignment.rb +2 -1
  16. data/lib/canvas_sync/generators/templates/models/assignment_group.rb +2 -1
  17. data/lib/canvas_sync/generators/templates/models/context_module.rb +2 -1
  18. data/lib/canvas_sync/generators/templates/models/context_module_item.rb +2 -1
  19. data/lib/canvas_sync/generators/templates/models/course.rb +4 -2
  20. data/lib/canvas_sync/generators/templates/models/enrollment.rb +2 -1
  21. data/lib/canvas_sync/generators/templates/models/group.rb +19 -0
  22. data/lib/canvas_sync/generators/templates/models/group_membership.rb +17 -0
  23. data/lib/canvas_sync/generators/templates/models/pseudonym.rb +8 -0
  24. data/lib/canvas_sync/generators/templates/models/role.rb +2 -1
  25. data/lib/canvas_sync/generators/templates/models/section.rb +2 -1
  26. data/lib/canvas_sync/generators/templates/models/submission.rb +3 -1
  27. data/lib/canvas_sync/generators/templates/models/term.rb +2 -1
  28. data/lib/canvas_sync/generators/templates/models/user.rb +4 -1
  29. data/lib/canvas_sync/importers/bulk_importer.rb +7 -1
  30. data/lib/canvas_sync/importers/legacy_importer.rb +4 -2
  31. data/lib/canvas_sync/job.rb +3 -1
  32. data/lib/canvas_sync/job_chain.rb +57 -0
  33. data/lib/canvas_sync/jobs/{sync_users_job.rb → sync_accounts_job.rb} +11 -6
  34. data/lib/canvas_sync/jobs/sync_provisioning_report_job.rb +2 -0
  35. data/lib/canvas_sync/processors/model_mappings.yml +81 -0
  36. data/lib/canvas_sync/processors/provisioning_report_processor.rb +28 -0
  37. data/lib/canvas_sync/record.rb +9 -0
  38. data/lib/canvas_sync/version.rb +1 -1
  39. data/spec/canvas_sync/canvas_sync_spec.rb +19 -16
  40. data/spec/canvas_sync/models/accounts_spec.rb +3 -0
  41. data/spec/canvas_sync/models/course_spec.rb +4 -0
  42. data/spec/canvas_sync/models/group_membership_spec.rb +26 -0
  43. data/spec/canvas_sync/models/group_spec.rb +26 -0
  44. data/spec/canvas_sync/models/user_spec.rb +2 -0
  45. data/spec/canvas_sync/processors/provisioning_report_processor_spec.rb +20 -0
  46. data/spec/dummy/app/models/account.rb +8 -1
  47. data/spec/dummy/app/models/admin.rb +2 -1
  48. data/spec/dummy/app/models/assignment.rb +2 -1
  49. data/spec/dummy/app/models/assignment_group.rb +2 -1
  50. data/spec/dummy/app/models/context_module.rb +2 -1
  51. data/spec/dummy/app/models/context_module_item.rb +2 -1
  52. data/spec/dummy/app/models/course.rb +4 -2
  53. data/spec/dummy/app/models/enrollment.rb +2 -1
  54. data/spec/dummy/app/models/group.rb +25 -0
  55. data/spec/dummy/app/models/group_membership.rb +23 -0
  56. data/spec/dummy/app/models/role.rb +2 -1
  57. data/spec/dummy/app/models/section.rb +2 -1
  58. data/spec/dummy/app/models/submission.rb +2 -1
  59. data/spec/dummy/app/models/term.rb +2 -1
  60. data/spec/dummy/app/models/user.rb +3 -1
  61. data/spec/dummy/config/application.rb +12 -1
  62. data/spec/dummy/config/database.yml +11 -11
  63. data/spec/dummy/config/environments/development.rb +3 -3
  64. data/spec/dummy/config/initializers/assets.rb +1 -1
  65. data/spec/dummy/db/migrate/20190702203627_create_submissions.rb +1 -0
  66. data/spec/dummy/db/migrate/20200415171620_create_groups.rb +29 -0
  67. data/spec/dummy/db/migrate/20200416214248_create_group_memberships.rb +24 -0
  68. data/spec/dummy/db/schema.rb +31 -1
  69. data/spec/factories/group_factory.rb +8 -0
  70. data/spec/factories/group_membership_factory.rb +6 -0
  71. data/spec/support/fixtures/reports/group_memberships.csv +3 -0
  72. data/spec/support/fixtures/reports/groups.csv +3 -0
  73. data/spec/support/fixtures/reports/submissions.csv +3 -3
  74. metadata +36 -6
  75. data/spec/canvas_sync/jobs/sync_users_job_spec.rb +0 -15
@@ -1,7 +1,8 @@
1
1
  # <%= autogenerated_model_warning %>
2
2
 
3
3
  class Role < ApplicationRecord
4
- include CanvasSync::ApiSyncable
4
+ include CanvasSync::Record
5
+ include CanvasSync::Concerns::ApiSyncable
5
6
 
6
7
  validates :canvas_id, uniqueness: true, presence: true
7
8
  has_many :admins, foreign_key: :canvas_role_id, primary_key: :canvas_id
@@ -1,7 +1,8 @@
1
1
  # <%= autogenerated_model_warning %>
2
2
 
3
3
  class Section < ApplicationRecord
4
- include CanvasSync::ApiSyncable
4
+ include CanvasSync::Record
5
+ include CanvasSync::Concerns::ApiSyncable
5
6
 
6
7
  validates :canvas_id, uniqueness: true, presence: true
7
8
  belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
@@ -1,7 +1,8 @@
1
1
  # <%= autogenerated_model_warning %>
2
2
 
3
3
  class Submission < ApplicationRecord
4
- include CanvasSync::ApiSyncable
4
+ include CanvasSync::Record
5
+ include CanvasSync::Concerns::ApiSyncable
5
6
 
6
7
  validates :canvas_id, uniqueness: true, presence: true
7
8
  belongs_to :assignment, primary_key: :canvas_id, foreign_key: :canvas_assignment_id, optional: true
@@ -13,6 +14,7 @@ class Submission < ApplicationRecord
13
14
  canvas_user_id: :user_id,
14
15
  submitted_at: :submitted_at,
15
16
  graded_at: :graded_at,
17
+ cached_due_date: :due_at,
16
18
  score: :score,
17
19
  excused: :excused,
18
20
  workflow_state: :workflow_state,
@@ -1,7 +1,8 @@
1
1
  # <%= autogenerated_model_warning %>
2
2
 
3
3
  class Term < ApplicationRecord
4
- include CanvasSync::ApiSyncable
4
+ include CanvasSync::Record
5
+ include CanvasSync::Concerns::ApiSyncable
5
6
 
6
7
  validates :canvas_id, uniqueness: true, presence: true
7
8
  has_many :courses, foreign_key: :canvas_term_id, primary_key: :canvas_id
@@ -1,13 +1,16 @@
1
1
  # <%= autogenerated_model_warning %>
2
2
 
3
3
  class User < ApplicationRecord
4
- include CanvasSync::ApiSyncable
4
+ include CanvasSync::Record
5
+ include CanvasSync::Concerns::ApiSyncable
5
6
 
6
7
  validates :canvas_id, uniqueness: true, presence: true
8
+ has_many :pseudonyms, primary_key: :canvas_id, foreign_key: :canvas_user_id
7
9
  has_many :enrollments, primary_key: :canvas_id, foreign_key: :canvas_user_id
8
10
  has_many :admins, primary_key: :canvas_id, foreign_key: :canvas_user_id
9
11
  has_many :admin_roles, through: :admins, source: :role
10
12
  has_many :submissions, primary_key: :canvas_id, foreign_key: :canvas_user_id
13
+ has_many :group_memberships, primary_key: :canvas_id, foreign_key: :canvas_user_id
11
14
 
12
15
  api_syncable({
13
16
  sis_id: :sis_user_id,
@@ -17,7 +17,13 @@ module CanvasSync
17
17
  # Note: passing the key [:on_duplicate_key_ignore] will override the default behavior of [:on_duplicate_key_update]
18
18
  # @yieldparam [Array] row if a block is passed in it will yield the current row from the CSV.
19
19
  # This can be used if you need to filter or massage the data in any way.
20
- def self.import(report_file_path, mapping, klass, conflict_target, import_args: {}) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/LineLength
20
+ def self.import(report_file_path, mapping, klass, conflict_target, import_args: {}, &blk) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/LineLength
21
+ ClassCallbackExecutor.run_if_defined(klass, :sync_import) do
22
+ perform_in_batches(report_file_path, mapping, klass, conflict_target, import_args: import_args, &blk)
23
+ end
24
+ end
25
+
26
+ def self.perform_in_batches(report_file_path, mapping, klass, conflict_target, import_args: {})
21
27
  csv_column_names = mapping.keys
22
28
  database_column_names = mapping.values.map { |value| value[:database_column_name] }
23
29
  rows = []
@@ -8,8 +8,10 @@ module CanvasSync
8
8
  # @param report_file_path [String]
9
9
  # @param klass [Object]
10
10
  def self.import(report_file_path, klass, account_id, options)
11
- CSV.foreach(report_file_path, headers: true, header_converters: :symbol) do |row|
12
- klass.create_or_update_from_csv(row, account_id, options)
11
+ ClassCallbackExecutor.run_if_defined(klass, :sync_import) do
12
+ CSV.foreach(report_file_path, headers: true, header_converters: :symbol) do |row|
13
+ klass.create_or_update_from_csv(row, account_id, options)
14
+ end
13
15
  end
14
16
  end
15
17
  end
@@ -61,7 +61,9 @@ module CanvasSync
61
61
  if model.respond_to? :create_or_update
62
62
  model.create_or_update(params)
63
63
  elsif model.method_defined? :update_from_api_params!
64
- model.find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params)
64
+ instance = model.find_or_initialize_by(canvas_id: params['id'])
65
+ instance.update_from_api_params!(params)
66
+ instance
65
67
  else
66
68
  raise "Could not create/update #{model.name}. It must have a create_or_update(params) ClassMethod or implement ApiSyncable."
67
69
  end
@@ -0,0 +1,57 @@
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
+ CanvasSync::invoke_next(self, extra_options: extra_options)
49
+ end
50
+
51
+ private
52
+
53
+ def job_matches_pattern(job_entry, pattern)
54
+ job_entry[:job].to_s == pattern.to_s
55
+ end
56
+ end
57
+ end
@@ -1,24 +1,29 @@
1
1
  module CanvasSync
2
2
  module Jobs
3
- class SyncUsersJob < ReportStarter
4
- # Starts a provisioning report for just users.
3
+ class SyncAccountsJob < ReportStarter
4
+ # Starts a provisioning report for just accounts.
5
5
  #
6
- # Provisioning reports do not scope users by term, so when we are
6
+ # Provisioning reports do not scope accounts by term, so when we are
7
7
  # running provisioning by term we sync users first so we don't duplicate
8
- # the work of syncing all users for each term.
8
+ # the work of syncing all accounts for each term.
9
9
  #
10
10
  # @param job_chain [Hash]
11
11
  # @param options [Hash]
12
12
  def perform(job_chain, options)
13
+ unless options[:root_account] == false
14
+ acc_params = CanvasSync.get_canvas_sync_client(job_chain[:global_options]).account("self")
15
+ update_or_create_model(Account, acc_params)
16
+ end
17
+
13
18
  super(
14
19
  job_chain,
15
20
  "proservices_provisioning_csv",
16
21
  merge_report_params(job_chain, options, {
17
- users: true,
22
+ accounts: true,
18
23
  include_deleted: true,
19
24
  }, term_scope: false),
20
25
  CanvasSync::Processors::ProvisioningReportProcessor.to_s,
21
- { models: ["users"] },
26
+ { models: ["accounts"] },
22
27
  )
23
28
  end
24
29
  end
@@ -49,6 +49,8 @@ module CanvasSync
49
49
  }
50
50
 
51
51
  options[:models].each do |model|
52
+ # group_membership is the only model param that is singular :(
53
+ model = 'group_membership' if model == 'group_memberships'
52
54
  params[model] = true
53
55
  end
54
56
 
@@ -29,6 +29,25 @@ users:
29
29
  database_column_name: login_id
30
30
  type: string
31
31
 
32
+ pseudonyms:
33
+ conflict_target: pseudonym_id
34
+ report_columns:
35
+ pseudonym_id:
36
+ database_column_name: canvas_id
37
+ type: integer
38
+ canvas_user_id:
39
+ database_column_name: canvas_user_id
40
+ type: integer
41
+ sis_user_id:
42
+ database_column_name: sis_id
43
+ type: string
44
+ unique_id:
45
+ database_column_name: unique_id
46
+ type: string
47
+ status:
48
+ database_column_name: workflow_state
49
+ type: string
50
+
32
51
  accounts:
33
52
  conflict_target: canvas_account_id
34
53
  report_columns:
@@ -230,6 +249,9 @@ submissions:
230
249
  submission_date:
231
250
  database_column_name: submitted_at
232
251
  type: datetime
252
+ due_at:
253
+ database_column_name: due_at
254
+ type: datetime
233
255
  graded_date:
234
256
  database_column_name: graded_at
235
257
  type: datetime
@@ -326,3 +348,62 @@ context_module_items:
326
348
  assignment_id:
327
349
  database_column_name: canvas_assignment_id
328
350
  type: integer
351
+
352
+ groups:
353
+ conflict_target: canvas_group_id
354
+ report_columns:
355
+ canvas_group_id:
356
+ database_column_name: canvas_id
357
+ type: integer
358
+ group_id:
359
+ database_column_name: sis_id
360
+ type: string
361
+ canvas_group_category_id:
362
+ database_column_name: canvas_group_category_id
363
+ type: integer
364
+ group_category_id:
365
+ database_column_name: group_category_sis_id
366
+ type: integer
367
+ canvas_account_id:
368
+ database_column_name: canvas_account_id
369
+ type: integer
370
+ canvas_course_id:
371
+ database_column_name: canvas_course_id
372
+ type: integer
373
+ name:
374
+ database_column_name: name
375
+ type: string
376
+ status:
377
+ database_column_name: workflow_state
378
+ type: string
379
+ context_id:
380
+ database_column_name: context_id
381
+ type: integer
382
+ context_type:
383
+ database_column_name: context_type
384
+ type: string
385
+ max_membership:
386
+ database_column_name: max_membership
387
+ type: integer
388
+
389
+ group_memberships:
390
+ conflict_target: canvas_group_membership_id
391
+ report_columns:
392
+ canvas_group_membership_id:
393
+ database_column_name: canvas_id
394
+ type: integer
395
+ canvas_group_id:
396
+ database_column_name: canvas_group_id
397
+ type: integer
398
+ group_id:
399
+ database_column_name: group_sis_id
400
+ type: string
401
+ canvas_user_id:
402
+ database_column_name: canvas_user_id
403
+ type: integer
404
+ user_id:
405
+ database_column_name: user_sis_id
406
+ type: string
407
+ status:
408
+ database_column_name: workflow_state
409
+ type: string
@@ -78,6 +78,15 @@ module CanvasSync
78
78
  )
79
79
  end
80
80
 
81
+ def bulk_process_pseudonyms(report_file_path)
82
+ CanvasSync::Importers::BulkImporter.import(
83
+ report_file_path,
84
+ mapping[:pseudonyms][:report_columns],
85
+ Pseudonym,
86
+ mapping[:pseudonyms][:conflict_target].to_sym,
87
+ )
88
+ end
89
+
81
90
  def bulk_process_accounts(report_file_path)
82
91
  CanvasSync::Importers::BulkImporter.import(
83
92
  report_file_path,
@@ -122,6 +131,25 @@ module CanvasSync
122
131
  mapping[:xlist][:conflict_target].to_sym,
123
132
  )
124
133
  end
134
+
135
+ def bulk_process_groups(report_file_path)
136
+ CanvasSync::Importers::BulkImporter.import(
137
+ report_file_path,
138
+ mapping[:groups][:report_columns],
139
+ Group,
140
+ mapping[:groups][:conflict_target].to_sym,
141
+ )
142
+ end
143
+
144
+ # Note that group membership is singular because we override the model name param in sync_provisioning_report_job
145
+ def bulk_process_group_membership(report_file_path)
146
+ CanvasSync::Importers::BulkImporter.import(
147
+ report_file_path,
148
+ mapping[:group_memberships][:report_columns],
149
+ GroupMembership,
150
+ mapping[:group_memberships][:conflict_target].to_sym,
151
+ )
152
+ end
125
153
  end
126
154
  end
127
155
  end
@@ -0,0 +1,9 @@
1
+ module CanvasSync
2
+ module Record
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ define_model_callbacks :sync_import
7
+ end
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module CanvasSync
2
- VERSION = "0.12.0".freeze
2
+ VERSION = "0.16.1".freeze
3
3
  end
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  RSpec.describe CanvasSync do
4
4
  describe '.provisioning_sync' do
5
5
  it 'invokes the first job in the queue and passes on the rest of the job chain' do
6
- expected_job_chain = CanvasSync.default_provisioning_report_chain(['courses'], nil)
6
+ expected_job_chain = CanvasSync.default_provisioning_report_chain(['courses'], nil).chain_data
7
7
  first_job = expected_job_chain[:jobs].shift
8
8
 
9
9
  expect(CanvasSync::Jobs::SyncTermsJob).to receive(:perform_later)
@@ -31,10 +31,10 @@ RSpec.describe CanvasSync do
31
31
  provisioning: { c: 3 },
32
32
  global: { d: 4 },
33
33
  })
34
- expect(chain).to eq({
34
+ expect(chain.chain_data).to eq({
35
35
  jobs: [
36
36
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: { a: 1 } },
37
- { job: CanvasSync::Jobs::SyncUsersJob.to_s, options: { b: 2 } },
37
+ { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { models: ['users'], b: 2 } },
38
38
  { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: 'active', models: ['courses'], c: 3 } }
39
39
  ],
40
40
  global_options: { legacy_support: false, d: 4 }
@@ -44,10 +44,10 @@ RSpec.describe CanvasSync do
44
44
  context 'we are syncing users with a term scope' do
45
45
  it 'syncs the users in a separate job that runs first' do
46
46
  chain = CanvasSync.default_provisioning_report_chain(['users', 'courses'], :active)
47
- expect(chain).to eq({
47
+ expect(chain.chain_data).to eq({
48
48
  jobs: [
49
49
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
50
- { job: CanvasSync::Jobs::SyncUsersJob.to_s, options: {} },
50
+ { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { models: ['users'] } },
51
51
  { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: 'active', models: ['courses'] } }
52
52
  ],
53
53
  global_options: { legacy_support: false }
@@ -58,7 +58,7 @@ RSpec.describe CanvasSync do
58
58
  context 'we are syncing users without a term scope' do
59
59
  it 'syncs users along with the rest of the provisioning report' do
60
60
  chain = CanvasSync.default_provisioning_report_chain(['users', 'courses'])
61
- expect(chain).to eq({
61
+ expect(chain.chain_data).to eq({
62
62
  jobs: [
63
63
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
64
64
  { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: ['users', 'courses'] } }
@@ -71,7 +71,7 @@ RSpec.describe CanvasSync do
71
71
  context 'we are syncing roles with a term scope' do
72
72
  it 'syncs the roles in a separate job that runs first' do
73
73
  chain = CanvasSync.default_provisioning_report_chain(['roles', 'courses'], :active)
74
- expect(chain).to eq({
74
+ expect(chain.chain_data).to eq({
75
75
  jobs: [
76
76
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
77
77
  { job: CanvasSync::Jobs::SyncRolesJob.to_s, options: {} },
@@ -85,7 +85,7 @@ RSpec.describe CanvasSync do
85
85
  context 'we are syncing roles without a term scope' do
86
86
  it 'syncs roles separately even with no term scope' do
87
87
  chain = CanvasSync.default_provisioning_report_chain(['roles', 'courses'])
88
- expect(chain).to eq({
88
+ expect(chain.chain_data).to eq({
89
89
  jobs: [
90
90
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
91
91
  { job: CanvasSync::Jobs::SyncRolesJob.to_s, options: {} },
@@ -99,7 +99,7 @@ RSpec.describe CanvasSync do
99
99
  context 'we are syncing admins with a term scope' do
100
100
  it 'syncs the admins in a separate job that runs first' do
101
101
  chain = CanvasSync.default_provisioning_report_chain(['admins', 'courses'], :active)
102
- expect(chain).to eq({
102
+ expect(chain.chain_data).to eq({
103
103
  jobs: [
104
104
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
105
105
  { job: CanvasSync::Jobs::SyncAdminsJob.to_s, options: {} },
@@ -113,7 +113,7 @@ RSpec.describe CanvasSync do
113
113
  context 'we are syncing admins without a term scope' do
114
114
  it 'syncs admins separately even with no term scope' do
115
115
  chain = CanvasSync.default_provisioning_report_chain(['admins', 'courses'])
116
- expect(chain).to eq({
116
+ expect(chain.chain_data).to eq({
117
117
  jobs: [
118
118
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
119
119
  { job: CanvasSync::Jobs::SyncAdminsJob.to_s, options: {} },
@@ -128,7 +128,7 @@ RSpec.describe CanvasSync do
128
128
  it "appends the SyncAssignmentsJob" do
129
129
  chain = CanvasSync.default_provisioning_report_chain(%w[users enrollments assignments])
130
130
 
131
- expect(chain).to eq(
131
+ expect(chain.chain_data).to eq(
132
132
  jobs: [
133
133
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
134
134
  { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments] } },
@@ -143,7 +143,7 @@ RSpec.describe CanvasSync do
143
143
  it "appends the SyncSubmissionsJob" do
144
144
  chain = CanvasSync.default_provisioning_report_chain(%w[users enrollments submissions])
145
145
 
146
- expect(chain).to eq(
146
+ expect(chain.chain_data).to eq(
147
147
  jobs: [
148
148
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
149
149
  { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments] } },
@@ -158,7 +158,7 @@ RSpec.describe CanvasSync do
158
158
  it "appends the SyncAssignmentGroupsJob" do
159
159
  chain = CanvasSync.default_provisioning_report_chain(%w[users enrollments assignment_groups])
160
160
 
161
- expect(chain).to eq(
161
+ expect(chain.chain_data).to eq(
162
162
  jobs: [
163
163
  { job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} },
164
164
  { job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: nil, models: %w[users enrollments] } },
@@ -185,7 +185,7 @@ RSpec.describe CanvasSync do
185
185
  }
186
186
  }
187
187
  ]
188
- )
188
+ ).chain_data
189
189
  first_job = expected_job_chain[:jobs].shift
190
190
 
191
191
  expect(CanvasSync::Jobs::SyncSimpleTableJob).to receive(:perform_later)
@@ -242,9 +242,12 @@ RSpec.describe CanvasSync do
242
242
  global_options: {}
243
243
  }
244
244
 
245
- expect(chain).to eq(expected_job_chain)
245
+ expect(chain.chain_data).to eq(expected_job_chain)
246
246
 
247
247
  end
248
248
  end
249
-
249
+
250
+ describe ".sync_scope" do
251
+
252
+ end
250
253
  end