canvas_sync 0.10.6 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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