canvas_sync 0.3.3 → 0.3.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.
Files changed (52) hide show
  1. checksums.yaml +5 -5
  2. data/app/controllers/api/v1/health_check_controller.rb +7 -0
  3. data/app/controllers/api/v1/live_events_controller.rb +16 -0
  4. data/lib/canvas_sync.rb +20 -5
  5. data/lib/canvas_sync/generators/install_generator.rb +1 -1
  6. data/lib/canvas_sync/generators/install_live_events_generator.rb +38 -0
  7. data/lib/canvas_sync/generators/templates/{course.rb → models/course.rb} +0 -0
  8. data/lib/canvas_sync/generators/templates/{create_courses.rb → models/create_courses.rb} +0 -0
  9. data/lib/canvas_sync/generators/templates/{create_enrollments.rb → models/create_enrollments.rb} +0 -0
  10. data/lib/canvas_sync/generators/templates/models/create_roles.rb +17 -0
  11. data/lib/canvas_sync/generators/templates/{create_sections.rb → models/create_sections.rb} +0 -0
  12. data/lib/canvas_sync/generators/templates/{create_terms.rb → models/create_terms.rb} +0 -0
  13. data/lib/canvas_sync/generators/templates/{create_users.rb → models/create_users.rb} +0 -0
  14. data/lib/canvas_sync/generators/templates/{enrollment.rb → models/enrollment.rb} +0 -0
  15. data/lib/canvas_sync/generators/templates/models/role.rb +18 -0
  16. data/lib/canvas_sync/generators/templates/{section.rb → models/section.rb} +0 -0
  17. data/lib/canvas_sync/generators/templates/{term.rb → models/term.rb} +0 -0
  18. data/lib/canvas_sync/generators/templates/{user.rb → models/user.rb} +0 -0
  19. data/lib/canvas_sync/generators/templates/services/live_events/assignment/assignment_created_event.rb +5 -0
  20. data/lib/canvas_sync/generators/templates/services/live_events/assignment/assignment_event.rb +63 -0
  21. data/lib/canvas_sync/generators/templates/services/live_events/assignment/assignment_updated_event.rb +4 -0
  22. data/lib/canvas_sync/generators/templates/services/live_events/base_event.rb +23 -0
  23. data/lib/canvas_sync/generators/templates/services/live_events/course/course_created_event.rb +4 -0
  24. data/lib/canvas_sync/generators/templates/services/live_events/course/course_event.rb +42 -0
  25. data/lib/canvas_sync/generators/templates/services/live_events/course/course_updated_event.rb +4 -0
  26. data/lib/canvas_sync/generators/templates/services/live_events/enrollment/enrollment_created_event.rb +4 -0
  27. data/lib/canvas_sync/generators/templates/services/live_events/enrollment/enrollment_event.rb +58 -0
  28. data/lib/canvas_sync/generators/templates/services/live_events/enrollment/enrollment_updated_event.rb +4 -0
  29. data/lib/canvas_sync/generators/templates/services/live_events/grade/grade_changed_event.rb +4 -0
  30. data/lib/canvas_sync/generators/templates/services/live_events/grade/grade_event.rb +56 -0
  31. data/lib/canvas_sync/generators/templates/services/live_events/submission/submission_created_event.rb +4 -0
  32. data/lib/canvas_sync/generators/templates/services/live_events/submission/submission_event.rb +57 -0
  33. data/lib/canvas_sync/generators/templates/services/live_events/submission/submission_updated_event.rb +4 -0
  34. data/lib/canvas_sync/generators/templates/services/live_events/syllabus/syllabus_event.rb +35 -0
  35. data/lib/canvas_sync/generators/templates/services/live_events/syllabus/syllabus_updated_event.rb +2 -0
  36. data/lib/canvas_sync/generators/templates/services/live_events/user/user_created_event.rb +4 -0
  37. data/lib/canvas_sync/generators/templates/services/live_events/user/user_event.rb +35 -0
  38. data/lib/canvas_sync/generators/templates/services/live_events/user/user_updated_event.rb +4 -0
  39. data/lib/canvas_sync/importers/bulk_importer.rb +1 -1
  40. data/lib/canvas_sync/jobs/sync_roles_job.rb +18 -0
  41. data/lib/canvas_sync/version.rb +1 -1
  42. data/spec/canvas_sync/canvas_sync_spec.rb +28 -0
  43. data/spec/canvas_sync/jobs/sync_roles_job_spec.rb +16 -0
  44. data/spec/canvas_sync/models/roles_spec.rb +46 -0
  45. data/spec/dummy/app/models/role.rb +24 -0
  46. data/spec/dummy/config/database.yml +4 -4
  47. data/spec/dummy/db/migrate/20180103162102_create_roles.rb +23 -0
  48. data/spec/dummy/db/schema.rb +16 -1
  49. data/spec/factories/role_factory.rb +8 -0
  50. data/spec/support/fake_canvas.rb +4 -0
  51. data/spec/support/fixtures/canvas_responses/roles.json +748 -0
  52. metadata +52 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 4cb4e4d83e146469a152b7582f9dff23adbf57e5
4
- data.tar.gz: 7ac7ec7146259a6099093c63134a070af1656a07
2
+ SHA256:
3
+ metadata.gz: 5deb79781b2837c369b7dc299b0ced0082af7612027280eacdc63f4473dd9396
4
+ data.tar.gz: 9c1e5af6281752f7a467c6238c08e0810a108083c1e6b170f0fa81364e4d63ae
5
5
  SHA512:
6
- metadata.gz: 1b98e4574ce7bce06cfba710c003fe8e8b4bf48b3f84326307d099bc9ab80136384c367ef4913e85a80331e4f0a855e41c40ea89e5cb4f30dec7af9e81c7585e
7
- data.tar.gz: d572b58ae50eab9287e04972f79a72f0619cb25623a8615d4c2e55f93f58d54c9232e5371ffce4d844405380fb8c7e4e4d5706909f88f730d75878ddab10280a
6
+ metadata.gz: 26045a4eb9f2f7c9f6cfb7bf7b978314322781d30945fe5ed4a60f138da3deea8fb407e31d19102003c55bb412ce1f10836ee076ddc6e2b8fef7a00db62e2eee
7
+ data.tar.gz: 57d54b949ad57b42fdd948886693af3a4c04361be9fbff6919ca6bd0fa4ce5cee50a7431f251ffbbd19e5b166b011ccd0ea150b272a0e665fa62276cd52907a9
@@ -0,0 +1,7 @@
1
+ class Api::V1::HealthCheckController < ActionController::Base
2
+
3
+ def ping
4
+ head :ok
5
+ end
6
+
7
+ end
@@ -0,0 +1,16 @@
1
+ class Api::V1::LiveEventsController < ActionController::Base
2
+
3
+ def process_event
4
+ payload = SymmetricEncryption.decrypt(params[:payload])
5
+ payload = JSON.parse(payload)
6
+ Rails.logger.debug("Processing event type: #{payload['attributes']['event_name']}")
7
+ Rails.logger.debug("Payload: #{payload}")
8
+ event = "LiveEvents::#{payload['attributes']['event_name'].camelcase}Event".constantize
9
+ event.perform_later(payload)
10
+ head :ok
11
+ rescue => e
12
+ Rails.logger.error("Live Events Error: #{e.message} - #{e.backtrace}")
13
+ render json: {error: "Live Events Error: #{e.message}"}, status: 422
14
+ end
15
+
16
+ end
data/lib/canvas_sync.rb CHANGED
@@ -9,18 +9,20 @@ require "canvas_sync/jobs/report_processor_job"
9
9
  require "canvas_sync/jobs/sync_provisioning_report_job"
10
10
  require "canvas_sync/jobs/sync_terms_job"
11
11
  require "canvas_sync/jobs/sync_users_job"
12
+ require "canvas_sync/jobs/sync_roles_job"
12
13
 
13
14
  Dir[File.dirname(__FILE__) + "/canvas_sync/processors/*.rb"].each {|file| require file }
14
15
  Dir[File.dirname(__FILE__) + "/canvas_sync/importers/*.rb"].each {|file| require file }
15
16
  Dir[File.dirname(__FILE__) + "/canvas_sync/generators/*.rb"].each {|file| require file }
16
17
 
17
18
  module CanvasSync
18
- SUPPORTED_MODELS = %w(users courses terms enrollments sections)
19
+ SUPPORTED_MODELS = %w(users courses terms enrollments sections roles)
20
+ SUPPORTED_LIVE_EVENTS = %w(course enrollment submission assignment user syllabus grade)
19
21
 
20
22
  # Runs a standard provisioning sync job with no extra report types.
21
- # Terms will be synced first using the API. If you are syncing users
22
- # and have also specified a Term scope, Users will by synced first, before
23
- # every other model (as Users are never scoped to Term).
23
+ # Terms will be synced first using the API. If you are syncing users/roles
24
+ # and have also specified a Term scope, Users/Roles will by synced first, before
25
+ # every other model (as Users/Roles are never scoped to Term).
24
26
  #
25
27
  # @param models [Array<String>] A list of models to sync. e.g., ['users', 'courses'].
26
28
  # must be one of SUPPORTED_MODELS
@@ -63,7 +65,7 @@ module CanvasSync
63
65
  next_job_class.perform_later(duped_job_chain, next_job[:options])
64
66
  end
65
67
 
66
- # Syncs terms, users if necessary, then the rest of the specified models.
68
+ # Syncs terms, users/roles if necessary, then the rest of the specified models.
67
69
  #
68
70
  # @param models [Array<String>]
69
71
  # @param term_scope [String]
@@ -86,6 +88,12 @@ module CanvasSync
86
88
  models = models - ['users']
87
89
  end
88
90
 
91
+ if models.include?('roles')
92
+ # Sync all roles first when scoping by term, because roles cannot be scoped to term
93
+ jobs.push({ job: CanvasSync::Jobs::SyncRolesJob.to_s, options: {} })
94
+ models = models - ['roles']
95
+ end
96
+
89
97
  jobs.push({ job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s, options: { term_scope: term_scope, models: models } })
90
98
 
91
99
  global_options = { legacy_support: legacy_support }
@@ -114,4 +122,11 @@ module CanvasSync
114
122
  raise "Invalid model(s) specified: #{invalid.join(', ')}. Only #{SUPPORTED_MODELS.join(', ')} are supported."
115
123
  end
116
124
  end
125
+
126
+ def self.validate_live_events!(events)
127
+ invalid = events - SUPPORTED_LIVE_EVENTS
128
+ if invalid.length > 0
129
+ raise "Invalid live event(s) specified: #{invalid.join(', ')}. Only #{SUPPORTED_LIVE_EVENTS.join(', ')} are supported."
130
+ end
131
+ end
117
132
  end
@@ -4,7 +4,7 @@ require "rails/generators/migration"
4
4
  module CanvasSync
5
5
  class InstallGenerator < Rails::Generators::Base
6
6
  include Rails::Generators::Migration
7
- source_root File.expand_path('../templates', __FILE__)
7
+ source_root File.expand_path('../templates/models', __FILE__)
8
8
  class_option :models, type: :string, required: true
9
9
 
10
10
  def self.next_migration_number(path)
@@ -0,0 +1,38 @@
1
+ require "rails/generators"
2
+
3
+ module CanvasSync
4
+ class InstallLiveEventsGenerator < Rails::Generators::Base
5
+ source_root File.expand_path('../templates/services/live_events', __FILE__)
6
+ class_option :events, type: :string, required: true
7
+
8
+ def autogenerated_event_warning
9
+ <<-HERE.strip_heredoc
10
+ #
11
+ # AUTO GENERATED LIVE EVENT
12
+ # This was auto generated by the CanvasSync Gem.
13
+ # You can customize it as needed, but make sure you test
14
+ # any changes you make to the auto generated methods.
15
+ #
16
+ HERE
17
+ end
18
+
19
+ # Generates the specified live events. Invoke with:
20
+ #
21
+ # bin/rails generate canvas_sync:install_live_events --events users,courses
22
+ #
23
+ # Install all live events with:
24
+ #
25
+ # bin/rails generate canvas_sync:install_live_events --events all
26
+ def generate_live_events
27
+ events = options['events'] == 'all' ? CanvasSync::SUPPORTED_LIVE_EVENTS : options['events'].split(',')
28
+ CanvasSync.validate_live_events!(events)
29
+
30
+ events.each do |event|
31
+ Dir.glob("#{File.dirname(__FILE__)}/templates/services/live_events/#{event}/*.rb") do |rb_file|
32
+ template "#{rb_file}", "app/services/live_events/#{File.basename(rb_file)}"
33
+ end
34
+ end
35
+ template 'base_event.rb', 'app/services/live_events/base_event.rb'
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ <%= autogenerated_migration_warning %>
2
+
3
+ class CreateRoles < ActiveRecord::Migration[5.1]
4
+ def change
5
+ create_table :roles do |t|
6
+ t.integer :canvas_role_id, null: false
7
+ t.string :label, null: false
8
+ t.string :base_role_type, null: false
9
+ t.json :account
10
+ t.string :workflow_state, null: false
11
+ t.json :permissions
12
+
13
+ t.timestamps
14
+ end
15
+ add_index :roles, :canvas_role_id, unique: true
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ <%= autogenerated_model_warning %>
2
+
3
+ class Role < ApplicationRecord
4
+ validates :canvas_role_id, uniqueness: true, presence: true
5
+
6
+ def self.create_or_update(role_params)
7
+ role = Role.find_or_initialize_by(canvas_role_id: role_params['id'])
8
+
9
+ role.assign_attributes(label: role_params['label'],
10
+ base_role_type: role_params['base_role_type'],
11
+ account: role_params['account'],
12
+ permissions: role_params['permissions'],
13
+ workflow_state: role_params['workflow_state'])
14
+
15
+ role.save! if role.changed?
16
+ role
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ <%= autogenerated_event_warning %>
2
+
3
+ class LiveEvents::AssignmentCreatedEvent < LiveEvents::AssignmentEvent
4
+
5
+ end
@@ -0,0 +1,63 @@
1
+ <%= autogenerated_event_warning %>
2
+
3
+ class LiveEvents::AssignmentEvent < LiveEvents::BaseEvent
4
+ # The following is provided in the live events call:
5
+ # {
6
+ # assignment_id: assignment.global_id,
7
+ # context_id: assignment.global_context_id,
8
+ # context_type: assignment.context_type,
9
+ # workflow_state: assignment.workflow_state,
10
+ # title: LiveEvents.truncate(assignment.title),
11
+ # description: LiveEvents.truncate(assignment.description),
12
+ # due_at: assignment.due_at,
13
+ # unlock_at: assignment.unlock_at,
14
+ # lock_at: assignment.lock_at,
15
+ # updated_at: assignment.updated_at,
16
+ # points_possible: assignment.points_possible
17
+ # }
18
+
19
+ def perform(event_payload)
20
+ super
21
+ attrs = {
22
+ canvas_assignment_id: local_canvas_id(payload[:assignment_id]),
23
+ course_id: course.try(:id),
24
+ name: payload[:title],
25
+ workflow_state: payload[:workflow_state],
26
+ description: payload[:description],
27
+ due_at: payload[:due_at],
28
+ unlock_at: payload[:unlock_at],
29
+ lock_at: payload[:lock_at],
30
+ points_possible: payload[:points_possible],
31
+ canvas_updated_at: payload[:updated_at]
32
+ }
33
+ create_or_update(attrs)
34
+ rescue => e
35
+ log_error e
36
+ ensure
37
+ true
38
+ end
39
+
40
+ private
41
+ def create_or_update(attrs)
42
+ assignment = Assignment.where(canvas_assignment_id: attrs[:canvas_assignment_id]).first_or_initialize
43
+ assignment.assign_attributes(attrs)
44
+ assignment.save!
45
+ assignment
46
+ end
47
+
48
+ # This could be nil if the context type for this assignment is not 'Course' of if a local course was not found
49
+ def canvas_course_id
50
+ payload[:context_type] == 'Course' ? local_canvas_id(payload[:context_id]) : nil
51
+ end
52
+
53
+ # This could be nil if the context type for this assignment is not 'Course' of if a local course was not found
54
+ def course
55
+ unless @course
56
+ if canvas_course_id.present?
57
+ @course = Course.find_by(canvas_course_id: canvas_course_id)
58
+ end
59
+ end
60
+ @course
61
+ end
62
+
63
+ end
@@ -0,0 +1,4 @@
1
+ <%= autogenerated_event_warning %>
2
+
3
+ class LiveEvents::AssignmentUpdatedEvent < LiveEvents::AssignmentEvent
4
+ end
@@ -0,0 +1,23 @@
1
+ <%= autogenerated_event_warning %>
2
+
3
+ class LiveEvents::BaseEvent
4
+
5
+ attr_accessor :payload
6
+
7
+ def perform(event_payload)
8
+ @payload = HashWithIndifferentAccess.new(event_payload['body'])
9
+ end
10
+
11
+ # Live events will use a canvas global ID (cross shard) for any ID's provided. This method will return the local ID.
12
+ def local_canvas_id(id)
13
+ id.to_i % 10_000_000_000_000
14
+ end
15
+
16
+ def log_error(e)
17
+ Rails.logger.error "Error processing Live Event: #{self.class.name}"
18
+ Rails.logger.error "#{payload}"
19
+ Rails.logger.error e.message
20
+ Rails.logger.error e.backtrace.join("\n")
21
+ end
22
+
23
+ end
@@ -0,0 +1,4 @@
1
+ <%= autogenerated_event_warning %>
2
+
3
+ class LiveEvents::CourseCreatedEvent < LiveEvents::CourseEvent
4
+ end
@@ -0,0 +1,42 @@
1
+ <%= autogenerated_event_warning %>
2
+
3
+ class LiveEvents::CourseEvent < LiveEvents::BaseEvent
4
+
5
+ # The following is provided in the live events call:
6
+ # {
7
+ # course_id: course.global_id,
8
+ # uuid: course.uuid,
9
+ # account_id: course.global_account_id,
10
+ # name: course.name,
11
+ # created_at: course.created_at,
12
+ # updated_at: course.updated_at,
13
+ # workflow_state: course.workflow_state
14
+ # }
15
+
16
+
17
+ def perform(event_payload)
18
+ super
19
+ attrs = {
20
+ canvas_course_id: local_canvas_id(payload[:course_id]),
21
+ canvas_account_id: local_canvas_id(payload[:account_id]),
22
+ name: payload[:name],
23
+ workflow_state: payload[:workflow_state]
24
+ }
25
+ create_or_update(attrs)
26
+ rescue => e
27
+ log_error e
28
+ ensure
29
+ true
30
+ end
31
+
32
+ private
33
+
34
+ def create_or_update(attrs)
35
+ course = Course.where(canva_course_id: attrs[:canva_course_id]).first_or_initialize
36
+ course.assign_attributes(attrs)
37
+ course.save!
38
+ course
39
+ end
40
+
41
+ end
42
+
@@ -0,0 +1,4 @@
1
+ <%= autogenerated_event_warning %>
2
+
3
+ class LiveEvents::CourseUpdatedEvent < LiveEvents::CourseEvent
4
+ end
@@ -0,0 +1,4 @@
1
+ <%= autogenerated_event_warning %>
2
+
3
+ class LiveEvents::EnrollmentCreatedEvent < LiveEvents::EnrollmentEvent
4
+ end
@@ -0,0 +1,58 @@
1
+ <%= autogenerated_event_warning %>
2
+
3
+ class LiveEvents::EnrollmentEvent < LiveEvents::BaseEvent
4
+
5
+ # The following is provided in the live events call:
6
+ # {
7
+ #
8
+ # enrollment_id: enrollment.global_id,
9
+ # course_id: enrollment.global_course_id,
10
+ # user_id: enrollment.global_user_id,
11
+ # user_name: enrollment.user_name,
12
+ # type: enrollment.type,
13
+ # created_at: enrollment.created_at,
14
+ # updated_at: enrollment.updated_at,
15
+ # limit_privileges_to_course_section: enrollment.limit_privileges_to_course_section,
16
+ # course_section_id: enrollment.global_course_section_id,
17
+ # workflow_state: enrollment.workflow_state
18
+ # }
19
+
20
+ def perform(paload)
21
+ super
22
+ return if course.nil? || section.nil? || user.nil?
23
+ attrs = {
24
+
25
+ canvas_enrollment_id: local_canvas_id(payload[:enrollment_id]),
26
+ course_id: course.try(:id),
27
+ user_id: user.try(:id),
28
+ type: payload[:type],
29
+ section_id: section.try(:id),
30
+ workflow_state: payload[:workflow_state]
31
+ }
32
+ create_or_update(attrs)
33
+ rescue => e
34
+ log_error e
35
+ ensure
36
+ true
37
+ end
38
+
39
+ private
40
+ def create_or_update(attrs)
41
+ enrollment = Enrollment.where(canvas_enrollment_id: attrs[:canvas_enrollment_id]).first_or_initialize
42
+ enrollment.assign_attributes(attrs)
43
+ enrollment.save!
44
+ enrollment
45
+ end
46
+
47
+ def course
48
+ @course ||= Course.find_by(canvas_course_id: local_canvas_id(payload[:course_id]))
49
+ end
50
+
51
+ def section
52
+ @section ||= Section.find_by(canvas_section_id: local_canvas_id(payload[:section_id]))
53
+ end
54
+
55
+ def user
56
+ @user ||= User.find_by(canvas_user_id: local_canvas_id(payload[:user_id]))
57
+ end
58
+ end
@@ -0,0 +1,4 @@
1
+ <%= autogenerated_event_warning %>
2
+
3
+ class LiveEvents::EnrollmentUpdatedEvent < LiveEvents::EnrollmentEvent
4
+ end
@@ -0,0 +1,4 @@
1
+ <%= autogenerated_event_warning %>
2
+
3
+ class LiveEvents::GradeChangedEvent < LiveEvents::GradeEvent
4
+ end