canvas_sync 0.20.5 → 0.21.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.
- checksums.yaml +4 -4
- data/README.md +64 -12
- data/app/controllers/{api → canvas_sync/api}/v1/health_check_controller.rb +1 -1
- data/app/controllers/canvas_sync/api/v1/live_events_controller.rb +122 -0
- data/config/routes.rb +7 -0
- data/lib/canvas_sync/concerns/live_event_sync.rb +46 -0
- data/lib/canvas_sync/generators/install_live_events_generator.rb +0 -1
- data/lib/canvas_sync/generators/templates/models/account.rb +1 -0
- data/lib/canvas_sync/generators/templates/models/assignment.rb +1 -0
- data/lib/canvas_sync/generators/templates/models/assignment_group.rb +1 -0
- data/lib/canvas_sync/generators/templates/models/context_module.rb +1 -0
- data/lib/canvas_sync/generators/templates/models/context_module_item.rb +1 -0
- data/lib/canvas_sync/generators/templates/models/course.rb +8 -0
- data/lib/canvas_sync/generators/templates/models/enrollment.rb +12 -0
- data/lib/canvas_sync/generators/templates/models/section.rb +7 -0
- data/lib/canvas_sync/generators/templates/models/submission.rb +1 -0
- data/lib/canvas_sync/generators/templates/models/user.rb +8 -0
- data/lib/canvas_sync/generators/templates/services/live_events/assignment_event.rb +1 -1
- data/lib/canvas_sync/generators/templates/services/live_events/assignment_group_event.rb +1 -1
- data/lib/canvas_sync/generators/templates/services/live_events/course_event.rb +1 -3
- data/lib/canvas_sync/generators/templates/services/live_events/course_section_event.rb +1 -1
- data/lib/canvas_sync/generators/templates/services/live_events/enrollment_event.rb +1 -1
- data/lib/canvas_sync/generators/templates/services/live_events/grade_event.rb +1 -1
- data/lib/canvas_sync/generators/templates/services/live_events/module_event.rb +1 -1
- data/lib/canvas_sync/generators/templates/services/live_events/module_item_event.rb +1 -1
- data/lib/canvas_sync/generators/templates/services/live_events/submission_event.rb +1 -1
- data/lib/canvas_sync/generators/templates/services/live_events/syllabus_event.rb +1 -1
- data/lib/canvas_sync/generators/templates/services/live_events/user_event.rb +1 -3
- data/lib/canvas_sync/{generators/templates/services/live_events/base_event.rb → live_events/base_handler.rb} +6 -10
- data/lib/canvas_sync/live_events/process_event_job.rb +26 -0
- data/lib/canvas_sync/live_events.rb +38 -0
- data/lib/canvas_sync/version.rb +1 -1
- data/lib/canvas_sync.rb +1 -0
- data/spec/canvas_sync/live_events/live_event_sync_spec.rb +27 -0
- data/spec/canvas_sync/live_events/live_events_controller_spec.rb +54 -0
- data/spec/canvas_sync/live_events/process_event_job_spec.rb +38 -0
- data/spec/dummy/app/models/account.rb +1 -0
- data/spec/dummy/app/models/assignment.rb +1 -0
- data/spec/dummy/app/models/assignment_group.rb +1 -0
- data/spec/dummy/app/models/context_module.rb +1 -0
- data/spec/dummy/app/models/context_module_item.rb +1 -0
- data/spec/dummy/app/models/course.rb +8 -0
- data/spec/dummy/app/models/enrollment.rb +12 -0
- data/spec/dummy/app/models/section.rb +7 -0
- data/spec/dummy/app/models/submission.rb +1 -0
- data/spec/dummy/app/models/user.rb +8 -0
- data/spec/dummy/app/services/live_events/assignment_event.rb +1 -1
- data/spec/dummy/app/services/live_events/course_event.rb +1 -3
- data/spec/dummy/app/services/live_events/course_section_event.rb +1 -1
- data/spec/dummy/app/services/live_events/enrollment_event.rb +1 -1
- data/spec/dummy/app/services/live_events/grade_event.rb +1 -1
- data/spec/dummy/app/services/live_events/module_event.rb +1 -1
- data/spec/dummy/app/services/live_events/module_item_event.rb +1 -1
- data/spec/dummy/app/services/live_events/submission_event.rb +1 -1
- data/spec/dummy/app/services/live_events/syllabus_event.rb +1 -1
- data/spec/dummy/app/services/live_events/user_event.rb +1 -3
- data/spec/dummy/config/routes.rb +1 -0
- metadata +218 -181
- data/app/controllers/api/v1/live_events_controller.rb +0 -18
- data/lib/canvas_sync/concerns/auto_relations.rb +0 -11
@@ -0,0 +1,38 @@
|
|
1
|
+
require_relative './live_events/base_handler.rb'
|
2
|
+
require_relative './live_events/process_event_job.rb'
|
3
|
+
|
4
|
+
module CanvasSync
|
5
|
+
module LiveEvents
|
6
|
+
|
7
|
+
@@registered_handlers = []
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def listen(event_types = nil, &blk)
|
11
|
+
if event_types != nil
|
12
|
+
blk = wrap_method(blk) do |inner, event|
|
13
|
+
meta = event[:metadata]
|
14
|
+
payload = event[:payload]
|
15
|
+
|
16
|
+
if event_types.include?(meta[:event_name])
|
17
|
+
inner.call(*args)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
@@registered_handlers << blk
|
23
|
+
end
|
24
|
+
|
25
|
+
def registered_handlers
|
26
|
+
@@registered_handlers
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def wrap_method(inner, &outer)
|
32
|
+
->(*args) {
|
33
|
+
outer.call(inner, *args)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/canvas_sync/version.rb
CHANGED
data/lib/canvas_sync.rb
CHANGED
@@ -8,6 +8,7 @@ require "canvas_sync/job"
|
|
8
8
|
require "canvas_sync/sidekiq_job"
|
9
9
|
require "canvas_sync/api_syncable"
|
10
10
|
require "canvas_sync/record"
|
11
|
+
require "canvas_sync/live_events"
|
11
12
|
require "canvas_sync/jobs/report_starter"
|
12
13
|
require "canvas_sync/jobs/report_checker"
|
13
14
|
require "canvas_sync/jobs/report_processor_job"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::Concerns::LiveEventSync do
|
4
|
+
before(:all) do
|
5
|
+
User.include(CanvasSync::Concerns::LiveEventSync)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:event) {
|
9
|
+
{
|
10
|
+
"metadata" => {
|
11
|
+
event_name: "user_created",
|
12
|
+
},
|
13
|
+
"payload" => {
|
14
|
+
user_id: 124,
|
15
|
+
},
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
describe "#perform" do
|
20
|
+
it "determines ID from payload" do
|
21
|
+
expect_any_instance_of(User).to receive(:process_live_event).with(:created, event["payload"], event["metadata"]) do |user, *args|
|
22
|
+
expect(user.canvas_id).to eql 124
|
23
|
+
end
|
24
|
+
User.cs_internal_process_live_event(event.with_indifferent_access)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "json/jwt"
|
3
|
+
|
4
|
+
RSpec.describe CanvasSync::Api::V1::LiveEventsController, type: :controller do
|
5
|
+
routes { CanvasSync::Engine.routes }
|
6
|
+
|
7
|
+
let(:event) {
|
8
|
+
{
|
9
|
+
"metadata" => {
|
10
|
+
event_name: "user_created",
|
11
|
+
},
|
12
|
+
"payload" => {
|
13
|
+
user_id: 42660000000000001,
|
14
|
+
},
|
15
|
+
}.with_indifferent_access
|
16
|
+
}
|
17
|
+
|
18
|
+
describe "#transform_ids!" do
|
19
|
+
it "transforms sharded IDs to local IDs" do
|
20
|
+
controller.send(:transform_ids!, event)
|
21
|
+
expect(event[:payload][:user_id]).to eql 1
|
22
|
+
expect(event[:payload][:sharded_user_id]).to eql 42660000000000001
|
23
|
+
end
|
24
|
+
|
25
|
+
xit "it does not transform cross-shard IDs" do
|
26
|
+
# TODO
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#process_dataservices_event" do
|
31
|
+
let(:private_key) { OpenSSL::PKey::RSA.new(2048) }
|
32
|
+
let(:jwk) { JSON::JWK.new(private_key) }
|
33
|
+
let(:jwks) { JSON::JWK::Set.new([jwk]) }
|
34
|
+
|
35
|
+
before :each do
|
36
|
+
allow(controller).to receive(:dataservices_jwks).and_return(jwks)
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:event_data) { event.to_json }
|
40
|
+
|
41
|
+
it "triggers a background job" do
|
42
|
+
expect(controller).to receive(:dispatch_event)
|
43
|
+
expect(controller).to receive(:validate_tenant!)
|
44
|
+
post :process_event, params: { "_json" => JSON::JWT.new(event).sign(jwk).to_s }
|
45
|
+
expect(response).to be_successful
|
46
|
+
end
|
47
|
+
|
48
|
+
it "does not allow unsigned events" do
|
49
|
+
expect(controller).to_not receive(:validate_tenant!)
|
50
|
+
post :process_event, params: { "_json" => event.to_json }
|
51
|
+
expect(response).to_not be_successful
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe CanvasSync::LiveEvents::ProcessEventJob do
|
4
|
+
before(:all) do
|
5
|
+
User.include(CanvasSync::Concerns::LiveEventSync)
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:event) {
|
9
|
+
{
|
10
|
+
"metadata" => {
|
11
|
+
event_name: "user_created",
|
12
|
+
},
|
13
|
+
"payload" => {
|
14
|
+
user_id: 1,
|
15
|
+
},
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
describe "#perform" do
|
20
|
+
it "invokes a legacy handler if present" do
|
21
|
+
expect(LiveEvents::UserCreatedEvent).to receive(:perform_later)
|
22
|
+
described_class.perform_now(event)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "does not invoke new handlers if legacy is present" do
|
26
|
+
expect(CanvasSync::LiveEvents).to_not receive(:registered_handlers)
|
27
|
+
described_class.perform_now(event)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "invokes new handlers" do
|
31
|
+
hide_const("LiveEvents::UserCreatedEvent")
|
32
|
+
expect(CanvasSync::LiveEvents).to receive(:registered_handlers).and_call_original
|
33
|
+
expect(User).to receive(:cs_internal_process_live_event).and_call_original
|
34
|
+
expect_any_instance_of(User).to receive(:process_live_event).with(:created, event["payload"], event["metadata"])
|
35
|
+
described_class.perform_now(event)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -10,6 +10,7 @@ class Account < ApplicationRecord
|
|
10
10
|
include CanvasSync::Record
|
11
11
|
include CanvasSync::Concerns::ApiSyncable
|
12
12
|
# include CanvasSync::Concerns::Account::Ancestry # Add support for the ancestry Gem
|
13
|
+
# include CanvasSync::Concerns::LiveEventSync
|
13
14
|
|
14
15
|
canvas_sync_features :defaults
|
15
16
|
|
@@ -12,6 +12,14 @@ class Course < ApplicationRecord
|
|
12
12
|
|
13
13
|
canvas_sync_features :defaults
|
14
14
|
|
15
|
+
# include CanvasSync::Concerns::LiveEventSync
|
16
|
+
# after_process_live_event do
|
17
|
+
# if account.nil?
|
18
|
+
# acc = Account.new(canvas_id: canvas_account_id)
|
19
|
+
# acc.sync_from_api
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
|
15
23
|
validates :canvas_id, uniqueness: true, presence: true
|
16
24
|
belongs_to :term, foreign_key: :canvas_term_id, primary_key: :canvas_id, optional: true
|
17
25
|
has_many :enrollments, primary_key: :canvas_id, foreign_key: :canvas_course_id
|
@@ -12,6 +12,18 @@ class Enrollment < ApplicationRecord
|
|
12
12
|
|
13
13
|
canvas_sync_features :defaults
|
14
14
|
|
15
|
+
# include CanvasSync::Concerns::LiveEventSync
|
16
|
+
# after_process_live_event do
|
17
|
+
# if user.nil?
|
18
|
+
# u = User.new(canvas_id: canvas_user_id)
|
19
|
+
# u.sync_from_api
|
20
|
+
# end
|
21
|
+
# if course.nil?
|
22
|
+
# c = Course.new(canvas_id: canvas_course_id)
|
23
|
+
# c.sync_from_api
|
24
|
+
# end
|
25
|
+
# end
|
26
|
+
|
15
27
|
validates :canvas_id, uniqueness: true, presence: true
|
16
28
|
belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id, optional: true
|
17
29
|
belongs_to :role, primary_key: :canvas_id, foreign_key: :canvas_role_id, optional: true
|
@@ -12,6 +12,13 @@ class Section < ApplicationRecord
|
|
12
12
|
|
13
13
|
canvas_sync_features :defaults
|
14
14
|
|
15
|
+
# include CanvasSync::Concerns::LiveEventSync
|
16
|
+
# after_process_live_event do
|
17
|
+
# # A section change could constitute a crosslisting change, which means
|
18
|
+
# # we need to make sure all our enrollments are pointing to the correct course
|
19
|
+
# enrollments.update_all(canvas_course_id: canvas_course_id)
|
20
|
+
# end
|
21
|
+
|
15
22
|
validates :canvas_id, uniqueness: true, presence: true
|
16
23
|
belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
|
17
24
|
has_many :enrollments, primary_key: :canvas_id, foreign_key: :canvas_section_id
|
@@ -12,6 +12,14 @@ class User < ApplicationRecord
|
|
12
12
|
|
13
13
|
canvas_sync_features :defaults
|
14
14
|
|
15
|
+
# include CanvasSync::Concerns::LiveEventSync
|
16
|
+
# around_process_live_event do |user, blk|
|
17
|
+
# blk.call
|
18
|
+
# rescue Footrest::HttpError::Unauthorized => e
|
19
|
+
# # This can happen when a new user is created, but hasn't setup a login on Canvas yet.
|
20
|
+
# Rails.logger.info("Failed to fetch user #{canvas_user_id}: #{e.backtrace}")
|
21
|
+
# end
|
22
|
+
|
15
23
|
validates :canvas_id, uniqueness: true, presence: true
|
16
24
|
has_many :pseudonyms, primary_key: :canvas_id, foreign_key: :canvas_user_id
|
17
25
|
has_many :enrollments, primary_key: :canvas_id, foreign_key: :canvas_user_id
|
@@ -11,8 +11,7 @@
|
|
11
11
|
|
12
12
|
|
13
13
|
module LiveEvents
|
14
|
-
class CourseEvent < LiveEvents::
|
15
|
-
|
14
|
+
class CourseEvent < CanvasSync::LiveEvents::BaseHandler
|
16
15
|
def process
|
17
16
|
course = Course.where(canvas_id: local_canvas_id(payload[:course_id])).first_or_initialize
|
18
17
|
course.canvas_account_id = local_canvas_id(payload[:account_id])
|
@@ -22,7 +21,6 @@ module LiveEvents
|
|
22
21
|
end
|
23
22
|
course.sync_from_api
|
24
23
|
end
|
25
|
-
|
26
24
|
end
|
27
25
|
|
28
26
|
class CourseCreatedEvent < LiveEvents::CourseEvent; end
|
@@ -11,7 +11,7 @@
|
|
11
11
|
|
12
12
|
|
13
13
|
module LiveEvents
|
14
|
-
class CourseSectionEvent < LiveEvents::
|
14
|
+
class CourseSectionEvent < CanvasSync::LiveEvents::BaseHandler
|
15
15
|
|
16
16
|
def process
|
17
17
|
section = Section.where(canvas_id: local_canvas_id(payload[:course_section_id])).first_or_initialize
|
@@ -11,7 +11,7 @@
|
|
11
11
|
|
12
12
|
|
13
13
|
module LiveEvents
|
14
|
-
class SubmissionEvent < LiveEvents::
|
14
|
+
class SubmissionEvent < CanvasSync::LiveEvents::BaseHandler
|
15
15
|
|
16
16
|
def process
|
17
17
|
submission = Submission.where(canvas_id: local_canvas_id(payload["submission_id"])).first_or_initialize
|
@@ -11,8 +11,7 @@
|
|
11
11
|
|
12
12
|
|
13
13
|
module LiveEvents
|
14
|
-
class UserEvent < LiveEvents::
|
15
|
-
|
14
|
+
class UserEvent < CanvasSync::LiveEvents::BaseHandler
|
16
15
|
def process
|
17
16
|
canvas_user_id = local_canvas_id(payload[:user_id])
|
18
17
|
user = User.where(canvas_id: canvas_user_id).first_or_initialize
|
@@ -21,7 +20,6 @@ module LiveEvents
|
|
21
20
|
# This can happen when a new user is created, but hasn't setup a login on Canvas yet.
|
22
21
|
Rails.logger.info("Failed to fetch user #{canvas_user_id}: #{e.backtrace}")
|
23
22
|
end
|
24
|
-
|
25
23
|
end
|
26
24
|
|
27
25
|
class UserCreatedEvent < LiveEvents::UserEvent; end
|
data/spec/dummy/config/routes.rb
CHANGED