canvas_sync 0.10.6 → 0.11.0

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 (36) hide show
  1. checksums.yaml +5 -5
  2. data/lib/canvas_sync.rb +10 -0
  3. data/lib/canvas_sync/api_syncable.rb +8 -0
  4. data/lib/canvas_sync/generators/templates/migrations/create_admins.rb +7 -4
  5. data/lib/canvas_sync/generators/templates/migrations/create_roles.rb +3 -4
  6. data/lib/canvas_sync/generators/templates/models/account.rb +2 -0
  7. data/lib/canvas_sync/generators/templates/models/admin.rb +4 -5
  8. data/lib/canvas_sync/generators/templates/models/role.rb +0 -4
  9. data/lib/canvas_sync/generators/templates/models/term.rb +0 -2
  10. data/lib/canvas_sync/job.rb +10 -0
  11. data/lib/canvas_sync/jobs/sync_admins_job.rb +9 -5
  12. data/lib/canvas_sync/jobs/sync_roles_job.rb +8 -5
  13. data/lib/canvas_sync/jobs/sync_terms_job.rb +8 -2
  14. data/lib/canvas_sync/version.rb +1 -1
  15. data/spec/canvas_sync/jobs/sync_admins_job_spec.rb +2 -1
  16. data/spec/canvas_sync/jobs/sync_roles_job_spec.rb +2 -1
  17. data/spec/canvas_sync/models/admins_spec.rb +3 -5
  18. data/spec/canvas_sync/models/roles_spec.rb +5 -5
  19. data/spec/canvas_sync/models/term_spec.rb +3 -3
  20. data/spec/dummy/app/models/account.rb +2 -0
  21. data/spec/dummy/app/models/admin.rb +4 -5
  22. data/spec/dummy/app/models/role.rb +0 -4
  23. data/spec/dummy/app/models/term.rb +0 -2
  24. data/spec/dummy/db/migrate/{20190702203628_create_roles.rb → 20190927204545_create_roles.rb} +3 -4
  25. data/spec/dummy/db/migrate/{20190702203629_create_admins.rb → 20190927204546_create_admins.rb} +7 -4
  26. data/spec/dummy/db/schema.rb +11 -9
  27. data/spec/factories/admin_factory.rb +0 -1
  28. data/spec/support/fake_canvas.rb +2 -2
  29. data/spec/support/fixtures/canvas_responses/roles.json +6 -0
  30. metadata +10 -21
  31. data/spec/dummy/db/development.sqlite3 +0 -0
  32. data/spec/dummy/db/test.sqlite3 +0 -0
  33. data/spec/dummy/log/development.log +0 -4188
  34. data/spec/dummy/log/test.log +0 -71696
  35. data/spec/support/fixtures/reports/provisioning_csv_unzipped/courses.csv +0 -3
  36. data/spec/support/fixtures/reports/provisioning_csv_unzipped/users.csv +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA256:
3
- metadata.gz: 85296cf62f289592ac9f7a9eae904ab8f4f95fa4e721c0746fb12b47e8f3679c
4
- data.tar.gz: 4e766399a5b695d589584454e4a0697480131bde11599887b13371d10b03c09d
2
+ SHA1:
3
+ metadata.gz: 4445382aaee8091e3d40bc0f836bfeaef14d99d1
4
+ data.tar.gz: fa0ff09fd9f2132ab29fb80a95c1353e38f2bbd8
5
5
  SHA512:
6
- metadata.gz: 543d433fcf85fcae47fa4937c796d7efe59954e39d38c9bdae63e03e4c0bfef1613483c4d2e2994fd3e481c9da1b7f5ea3e7ab8719f43103d74a4caa128baa41
7
- data.tar.gz: bbab77a8e574656e3030e643460694e60147f740095d6d37630d5ae6770c2eb60d0502e2a83db7cfce253035804084662e2255dd5154ed4c595c6589310e13b2
6
+ metadata.gz: 3acfbb161a7c11a8d2b6c093b52b55ef5b0bf82876ff0a8e28b0092753d56ea2b28f6fa65360bb4889f5145361897e45e813acd000e45b84f9e6e9b27ab538fa
7
+ data.tar.gz: be69604343fe4458a9e5cac5d26a8e307b909e3a2e2693d6aa9bdab38c5e3271cc73dab72f8d98959a92b0ef7b515d634c7f62701d61b9681e4f40b6abb17857
@@ -138,6 +138,16 @@ module CanvasSync
138
138
  sub_items
139
139
  end
140
140
 
141
+ # Given a Model or Relation, scope it down to items that should be synced
142
+ def sync_scope(scope)
143
+ terms = %i[should_canvas_sync active_for_canvas_sync should_sync active_for_sync active]
144
+ terms.each do |t|
145
+ return scope.send(t) if scope.respond_to?(t)
146
+ end
147
+ Rails.logger.warn("Could not filter Syncable Scope for model '#{scope.try(:model)&.name || scope.name}'")
148
+ scope
149
+ end
150
+
141
151
  # Syn any report to an specific set of models
142
152
  #
143
153
  # @param reports_mapping [Array<Hash>] List of reports with their specific model and params
@@ -21,6 +21,14 @@ module CanvasSync::ApiSyncable
21
21
  inst
22
22
  end
23
23
 
24
+ def create_or_update_from_api_params(api_params)
25
+ api_params = api_params.with_indifferent_access
26
+ inst = find_or_initialize_by(canvas_id: api_params[:id])
27
+ inst.update_from_api_params(api_params)
28
+ inst.save! if inst.changed?
29
+ inst
30
+ end
31
+
24
32
  def api_sync_options=(opts)
25
33
  @api_sync_options = opts
26
34
  end
@@ -5,13 +5,16 @@ class CreateAdmins < ActiveRecord::Migration[5.1]
5
5
  create_table :admins do |t|
6
6
  t.bigint :canvas_id, null: false
7
7
  t.string :role_name
8
- t.bigint :canvas_role_id, null: false
9
- t.json :user_data
10
- t.bigint :canvas_user_id, null: false
11
- t.string :workflow_state, null: false
8
+ t.bigint :canvas_account_id
9
+ t.bigint :canvas_role_id
10
+ t.bigint :canvas_user_id
11
+ t.string :workflow_state
12
12
 
13
13
  t.timestamps
14
14
  end
15
15
  add_index :admins, :canvas_id, unique: true
16
+ add_index :admins, :canvas_role_id
17
+ add_index :admins, :canvas_user_id
18
+ add_index :admins, :canvas_account_id
16
19
  end
17
20
  end
@@ -4,11 +4,10 @@ class CreateRoles < ActiveRecord::Migration[5.1]
4
4
  def change
5
5
  create_table :roles do |t|
6
6
  t.integer :canvas_id, null: false
7
- t.string :label, null: false
8
- t.string :base_role_type, null: false
9
- t.json :account
7
+ t.string :label
8
+ t.string :base_role_type
10
9
  t.integer :canvas_account_id
11
- t.string :workflow_state, null: false
10
+ t.string :workflow_state
12
11
  t.json :permissions
13
12
 
14
13
  t.timestamps
@@ -5,6 +5,8 @@ class Account < ApplicationRecord
5
5
 
6
6
  validates :canvas_id, uniqueness: true, presence: true
7
7
 
8
+ has_many :admins, primary_key: :canvas_id, foreign_key: :canvas_account_id
9
+
8
10
  api_syncable({
9
11
  name: :name,
10
12
  workflow_state: :workflow_state,
@@ -4,6 +4,7 @@ class Admin < ApplicationRecord
4
4
  include CanvasSync::ApiSyncable
5
5
 
6
6
  validates :canvas_id, uniqueness: true, presence: true
7
+ belongs_to :account, primary_key: :canvas_id, foreign_key: :canvas_account_id, optional: true
7
8
  belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id, optional: true
8
9
  belongs_to :role, primary_key: :canvas_id, foreign_key: :canvas_role_id, optional: true
9
10
 
@@ -11,16 +12,14 @@ class Admin < ApplicationRecord
11
12
  canvas_id: :id,
12
13
  role_name: :role,
13
14
  canvas_role_id: :role_id,
14
- user_data: :user,
15
15
  canvas_user_id: ->(r) { r['user']['id'] },
16
+ # NOTICE: The :account_id field is added by CanvasSync - it is not included in the Canvas API response
17
+ canvas_account_id: :account_id,
16
18
  workflow_state: :workflow_state,
17
19
  }, -> (api) {
18
- admins = api.account_admins('self').all_pages!
20
+ admins = api.account_admins(canvas_account_id).all_pages!
19
21
  admin_data = admins.find{|admin| admin['id'] == canvas_id }
20
22
  raise Footrest::HttpError::NotFound unless admin_data.present?
21
23
  admin_data
22
24
  })
23
-
24
- def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
25
-
26
25
  end
@@ -10,12 +10,8 @@ class Role < ApplicationRecord
10
10
  canvas_id: :id,
11
11
  label: :label,
12
12
  base_role_type: :base_role_type,
13
- account: :account,
14
13
  canvas_account_id: ->(r) { r.dig('account', 'id') },
15
14
  permissions: :permissions,
16
15
  workflow_state: :workflow_state,
17
16
  }, -> (api) { api.get("/api/v1/accounts/#{canvas_account_id}/roles/#{canvas_id}") })
18
-
19
- def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
20
-
21
17
  end
@@ -21,8 +21,6 @@ class Term < ApplicationRecord
21
21
  term_data
22
22
  })
23
23
 
24
- def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
25
-
26
24
  # This is a sample scope created by the CanvasSync gem; feel
27
25
  # free to customize it for your tool's requirements.
28
26
  scope :active, -> {
@@ -56,5 +56,15 @@ module CanvasSync
56
56
  status: JobLog::ENQUEUED_STATUS,
57
57
  )
58
58
  end
59
+
60
+ def update_or_create_model(model, params)
61
+ if model.respond_to? :create_or_update
62
+ model.create_or_update(params)
63
+ elsif model.method_defined? :update_from_api_params!
64
+ model.find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params)
65
+ else
66
+ raise "Could not create/update #{model.name}. It must have a create_or_update(params) ClassMethod or implement ApiSyncable."
67
+ end
68
+ end
59
69
  end
60
70
  end
@@ -7,12 +7,16 @@ module CanvasSync
7
7
  # @param job_chain [Hash]
8
8
  # @param options [Hash]
9
9
  def perform(job_chain, _options)
10
- updated_admins = []
11
- CanvasSync.get_canvas_sync_client(job_chain[:global_options]).account_admins("self").all_pages!.each do |admin_params|
12
- admin = Admin.create_or_update(admin_params)
13
- updated_admins.push(admin.id)
10
+ updated_admin_ids = []
11
+ api_client = CanvasSync.get_canvas_sync_client(job_chain[:global_options])
12
+ CanvasSync.sync_scope(Account).find_each do |acc|
13
+ api_client.account_admins(acc.canvas_id).all_pages_each do |admin_params|
14
+ admin_params[:account_id] = acc.canvas_id
15
+ admin = update_or_create_model(Admin, admin_params)
16
+ updated_admin_ids.push(admin.id)
17
+ end
14
18
  end
15
- Admin.where.not(id: updated_admins).delete_all
19
+ Admin.where.not(id: updated_admin_ids).update_all(workflow_state: 'inactive')
16
20
  CanvasSync.invoke_next(job_chain)
17
21
  end
18
22
  end
@@ -7,12 +7,15 @@ module CanvasSync
7
7
  # @param job_chain [Hash]
8
8
  # @param options [Hash]
9
9
  def perform(job_chain, _options)
10
- updated_roles = []
11
- CanvasSync.get_canvas_sync_client(job_chain[:global_options]).list_roles("self").all_pages!.each do |role_params|
12
- role = Role.create_or_update(role_params)
13
- updated_roles.push(role.id)
10
+ updated_role_ids = []
11
+ api_client = CanvasSync.get_canvas_sync_client(job_chain[:global_options])
12
+ CanvasSync.sync_scope(Account).find_each do |acc|
13
+ api_client.list_roles(acc.canvas_id, state: %w[active inactive]).all_pages_each do |role_params|
14
+ role = update_or_create_model(Role, role_params)
15
+ updated_role_ids.push(role.id)
16
+ end
14
17
  end
15
- Role.where.not(id: updated_roles).delete_all
18
+ Role.where.not(id: updated_role_ids).update_all(workflow_state: 'inactive')
16
19
  CanvasSync.invoke_next(job_chain)
17
20
  end
18
21
  end
@@ -10,9 +10,15 @@ module CanvasSync
10
10
  def perform(job_chain, _options)
11
11
  CanvasSync.get_canvas_sync_client(job_chain[:global_options]).terms("self").all_pages!.each do |term_params|
12
12
  if job_chain[:global_options][:account_id]
13
- Term.create_or_update(term_params, job_chain[:global_options][:account_id])
13
+ # These branches are primarily to support Legacy apps
14
+ if Term.respond_to?(:create_or_update) && Term.method(:create_or_update).arity.abs == 2
15
+ Term.create_or_update(term_params, job_chain[:global_options][:account_id])
16
+ else
17
+ term_params[:account_id] |= job_chain[:global_options][:account_id]
18
+ update_or_create_model(Term, term_params)
19
+ end
14
20
  else
15
- Term.create_or_update(term_params)
21
+ update_or_create_model(Term, term_params)
16
22
  end
17
23
  end
18
24
 
@@ -1,3 +1,3 @@
1
1
  module CanvasSync
2
- VERSION = "0.10.6".freeze
2
+ VERSION = "0.11.0".freeze
3
3
  end
@@ -2,10 +2,11 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe CanvasSync::Jobs::SyncAdminsJob do
4
4
  describe '#perform' do
5
+ let!(:account) { FactoryGirl.create(:account, canvas_id: 1) }
5
6
  let(:admin_params) { open_canvas_fixture('admins') }
6
7
  let(:job_chain) { { jobs: [], global_options: {}} }
7
8
 
8
- it 'retrieves all terms from the Canvas API and then invokes the next job' do
9
+ it 'retrieves all admins from the Canvas API and then invokes the next job' do
9
10
  expect(CanvasSync).to receive(:invoke_next).with(job_chain)
10
11
 
11
12
  expect {
@@ -2,10 +2,11 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe CanvasSync::Jobs::SyncRolesJob do
4
4
  describe '#perform' do
5
+ let!(:account) { FactoryGirl.create(:account, canvas_id: 1) }
5
6
  let(:role_params) { open_canvas_fixture('roles') }
6
7
  let(:job_chain) { { jobs: [], global_options: {}} }
7
8
 
8
- it 'retrieves all terms from the Canvas API and then invokes the next job' do
9
+ it 'retrieves all roles from the Canvas API and then invokes the next job' do
9
10
  expect(CanvasSync).to receive(:invoke_next).with(job_chain)
10
11
 
11
12
  expect {
@@ -8,19 +8,18 @@ RSpec.describe Admin, type: :model do
8
8
  it { should validate_uniqueness_of(:canvas_id) }
9
9
  end
10
10
 
11
- describe '.create_or_update' do
11
+ describe '.create_or_update_from_api_params' do
12
12
  let(:admin_params) { open_canvas_fixture('admins')[0] }
13
13
 
14
14
  context 'the admin does not already exist' do
15
15
  it 'creates it' do
16
16
  expect {
17
- Admin.create_or_update(admin_params)
17
+ Admin.create_or_update_from_api_params(admin_params)
18
18
  }.to change { Admin.count }.by(1)
19
19
 
20
20
  admin = Admin.last
21
21
  expect(admin.role_name).to eq(admin_params['role'])
22
22
  expect(admin.canvas_role_id).to eq(admin_params['role_id'])
23
- expect(admin.user_data).to eq(admin_params['user'])
24
23
  expect(admin.canvas_user_id).to eq(admin_params['user']['id'])
25
24
  expect(admin.workflow_state).to eq(admin_params['workflow_state'])
26
25
  end
@@ -31,13 +30,12 @@ RSpec.describe Admin, type: :model do
31
30
 
32
31
  it 'updates it' do
33
32
  expect {
34
- Admin.create_or_update(admin_params)
33
+ Admin.create_or_update_from_api_params(admin_params)
35
34
  }.to_not change { Admin.count }
36
35
 
37
36
  existing_admin.reload
38
37
  expect(existing_admin.role_name).to eq(admin_params['role'])
39
38
  expect(existing_admin.canvas_role_id).to eq(admin_params['role_id'])
40
- expect(existing_admin.user_data).to eq(admin_params['user'])
41
39
  expect(existing_admin.canvas_user_id).to eq(admin_params['user']['id'])
42
40
  expect(existing_admin.workflow_state).to eq(admin_params['workflow_state'])
43
41
  end
@@ -8,19 +8,19 @@ RSpec.describe Role, type: :model do
8
8
  it { should validate_uniqueness_of(:canvas_id) }
9
9
  end
10
10
 
11
- describe '.create_or_update' do
11
+ describe '.create_or_update_from_api_params' do
12
12
  let(:role_params) { open_canvas_fixture('roles')[0] }
13
13
 
14
14
  context 'the role does not already exist' do
15
15
  it 'creates it' do
16
16
  expect {
17
- Role.create_or_update(role_params)
17
+ Role.create_or_update_from_api_params(role_params)
18
18
  }.to change { Role.count }.by(1)
19
19
 
20
20
  role = Role.last
21
21
  expect(role.label).to eq(role_params['label'])
22
22
  expect(role.base_role_type).to eq(role_params['base_role_type'])
23
- expect(role.account).to eq(role_params['account'])
23
+ expect(role.canvas_account_id).to eq(role_params['account']['id'])
24
24
  expect(role.permissions).to eq(role_params['permissions'])
25
25
  expect(role.workflow_state).to eq(role_params['workflow_state'])
26
26
  end
@@ -31,13 +31,13 @@ RSpec.describe Role, type: :model do
31
31
 
32
32
  it 'updates it' do
33
33
  expect {
34
- Role.create_or_update(role_params)
34
+ Role.create_or_update_from_api_params(role_params)
35
35
  }.to_not change { Role.count }
36
36
 
37
37
  existing_role.reload
38
38
  expect(existing_role.label).to eq(role_params['label'])
39
39
  expect(existing_role.base_role_type).to eq(role_params['base_role_type'])
40
- expect(existing_role.account).to eq(role_params['account'])
40
+ expect(existing_role.canvas_account_id).to eq(role_params['account']['id'])
41
41
  expect(existing_role.permissions).to eq(role_params['permissions'])
42
42
  expect(existing_role.workflow_state).to eq(role_params['workflow_state'])
43
43
  end
@@ -29,13 +29,13 @@ RSpec.describe Term, type: :model do
29
29
  end
30
30
  end
31
31
 
32
- describe '.create_or_update' do
32
+ describe '.create_or_update_from_api_params' do
33
33
  let(:term_params) { open_canvas_fixture('terms')['enrollment_terms'][0] }
34
34
 
35
35
  context 'the term does not already exist' do
36
36
  it 'creates it' do
37
37
  expect {
38
- Term.create_or_update(term_params)
38
+ Term.create_or_update_from_api_params(term_params)
39
39
  }.to change { Term.count }.by(1)
40
40
 
41
41
  term = Term.last
@@ -54,7 +54,7 @@ RSpec.describe Term, type: :model do
54
54
 
55
55
  it 'updates it' do
56
56
  expect {
57
- Term.create_or_update(term_params)
57
+ Term.create_or_update_from_api_params(term_params)
58
58
  }.to_not change { Term.count }
59
59
 
60
60
  existing_term.reload
@@ -11,6 +11,8 @@ class Account < ApplicationRecord
11
11
 
12
12
  validates :canvas_id, uniqueness: true, presence: true
13
13
 
14
+ has_many :admins, primary_key: :canvas_id, foreign_key: :canvas_account_id
15
+
14
16
  api_syncable({
15
17
  name: :name,
16
18
  workflow_state: :workflow_state,
@@ -10,6 +10,7 @@ class Admin < ApplicationRecord
10
10
  include CanvasSync::ApiSyncable
11
11
 
12
12
  validates :canvas_id, uniqueness: true, presence: true
13
+ belongs_to :account, primary_key: :canvas_id, foreign_key: :canvas_account_id, optional: true
13
14
  belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id, optional: true
14
15
  belongs_to :role, primary_key: :canvas_id, foreign_key: :canvas_role_id, optional: true
15
16
 
@@ -17,16 +18,14 @@ class Admin < ApplicationRecord
17
18
  canvas_id: :id,
18
19
  role_name: :role,
19
20
  canvas_role_id: :role_id,
20
- user_data: :user,
21
21
  canvas_user_id: ->(r) { r['user']['id'] },
22
+ # NOTICE: The :account_id field is added by CanvasSync - it is not included in the Canvas API response
23
+ canvas_account_id: :account_id,
22
24
  workflow_state: :workflow_state,
23
25
  }, -> (api) {
24
- admins = api.account_admins('self').all_pages!
26
+ admins = api.account_admins(canvas_account_id).all_pages!
25
27
  admin_data = admins.find{|admin| admin['id'] == canvas_id }
26
28
  raise Footrest::HttpError::NotFound unless admin_data.present?
27
29
  admin_data
28
30
  })
29
-
30
- def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
31
-
32
31
  end
@@ -16,12 +16,8 @@ class Role < ApplicationRecord
16
16
  canvas_id: :id,
17
17
  label: :label,
18
18
  base_role_type: :base_role_type,
19
- account: :account,
20
19
  canvas_account_id: ->(r) { r.dig('account', 'id') },
21
20
  permissions: :permissions,
22
21
  workflow_state: :workflow_state,
23
22
  }, -> (api) { api.get("/api/v1/accounts/#{canvas_account_id}/roles/#{canvas_id}") })
24
-
25
- def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
26
-
27
23
  end
@@ -27,8 +27,6 @@ class Term < ApplicationRecord
27
27
  term_data
28
28
  })
29
29
 
30
- def self.create_or_update(params); find_or_initialize_by(canvas_id: params['id']).update_from_api_params!(params); end
31
-
32
30
  # This is a sample scope created by the CanvasSync gem; feel
33
31
  # free to customize it for your tool's requirements.
34
32
  scope :active, -> {
@@ -10,11 +10,10 @@ class CreateRoles < ActiveRecord::Migration[5.1]
10
10
  def change
11
11
  create_table :roles do |t|
12
12
  t.integer :canvas_id, null: false
13
- t.string :label, null: false
14
- t.string :base_role_type, null: false
15
- t.json :account
13
+ t.string :label
14
+ t.string :base_role_type
16
15
  t.integer :canvas_account_id
17
- t.string :workflow_state, null: false
16
+ t.string :workflow_state
18
17
  t.json :permissions
19
18
 
20
19
  t.timestamps
@@ -11,13 +11,16 @@ class CreateAdmins < ActiveRecord::Migration[5.1]
11
11
  create_table :admins do |t|
12
12
  t.bigint :canvas_id, null: false
13
13
  t.string :role_name
14
- t.bigint :canvas_role_id, null: false
15
- t.json :user_data
16
- t.bigint :canvas_user_id, null: false
17
- t.string :workflow_state, null: false
14
+ t.bigint :canvas_account_id
15
+ t.bigint :canvas_role_id
16
+ t.bigint :canvas_user_id
17
+ t.string :workflow_state
18
18
 
19
19
  t.timestamps
20
20
  end
21
21
  add_index :admins, :canvas_id, unique: true
22
+ add_index :admins, :canvas_role_id
23
+ add_index :admins, :canvas_user_id
24
+ add_index :admins, :canvas_account_id
22
25
  end
23
26
  end