canvas_sync 0.3.12 → 0.3.13
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 +1 -1
- data/app/controllers/api/v1/health_check_controller.rb +8 -6
- data/app/controllers/api/v1/live_events_controller.rb +16 -14
- data/app/models/canvas_sync/job_log.rb +2 -2
- data/lib/canvas_sync.rb +20 -28
- data/lib/canvas_sync/engine.rb +2 -2
- data/lib/canvas_sync/generators/install_generator.rb +2 -2
- data/lib/canvas_sync/generators/install_live_events_generator.rb +4 -4
- data/lib/canvas_sync/generators/templates/services/live_events/syllabus/syllabus_updated_event.rb +3 -1
- data/lib/canvas_sync/importers/bulk_importer.rb +16 -16
- data/lib/canvas_sync/job.rb +5 -5
- data/lib/canvas_sync/jobs/report_checker.rb +22 -20
- data/lib/canvas_sync/jobs/report_processor_job.rb +1 -1
- data/lib/canvas_sync/jobs/report_starter.rb +9 -8
- data/lib/canvas_sync/jobs/sync_admins_job.rb +2 -2
- data/lib/canvas_sync/jobs/sync_provisioning_report_job.rb +2 -2
- data/lib/canvas_sync/jobs/sync_roles_job.rb +2 -2
- data/lib/canvas_sync/jobs/sync_terms_job.rb +3 -3
- data/lib/canvas_sync/jobs/sync_users_job.rb +6 -6
- data/lib/canvas_sync/processors/provisioning_report_processor.rb +12 -8
- data/lib/canvas_sync/version.rb +1 -1
- data/spec/dummy/log/test.log +794 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e20004cc4e924da4afb400f82f223abcf3b384afda17db07e66923528586f35
|
4
|
+
data.tar.gz: d0b270232aa379fc899a19b20c659f71b6bbf211e2c0dea70f010f3b02a59678
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbe225d20cc4f43382d70c3594798f4cf69013ca3b9094a6180e0be0f71db98c97d924792bb9711fd4868f6b0afbf9a17ebed8e2c569c23fcf94a2d88a3caf7b
|
7
|
+
data.tar.gz: f3d814550bbeac5deb732ca77f49942f703a6cd3ddca1351d53b794181c33527f6604007fed4dd3619c9af851cc0590f50a42d0c32f9872360c44868c686e72a
|
data/README.md
CHANGED
@@ -26,7 +26,7 @@ bundle exec rake db:migrate
|
|
26
26
|
|
27
27
|
For a list of currently supported models, see `CanvasSync::SUPPORTED_MODELS`.
|
28
28
|
|
29
|
-
Additionally, your Canvas instance must have the "Proserv Provisioning Report" enabled.
|
29
|
+
Additionally, your Canvas instance must have the "Proserv Provisioning Report" enabled. If you are syncing assignments the "Assignment Report" report must be enabled.
|
30
30
|
|
31
31
|
## Prerequisites
|
32
32
|
|
@@ -1,16 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
module Api
|
2
|
+
module V1
|
3
|
+
class LiveEventsController < ActionController::Base
|
4
|
+
def process_event # rubocop:disable Metrics/AbcSize
|
5
|
+
payload = SymmetricEncryption.decrypt(params[:payload])
|
6
|
+
payload = JSON.parse(payload)
|
7
|
+
Rails.logger.debug("Processing event type: #{payload['attributes']['event_name']}")
|
8
|
+
Rails.logger.debug("Payload: #{payload}")
|
9
|
+
event = "LiveEvents::#{payload['attributes']['event_name'].camelcase}Event".constantize
|
10
|
+
event.perform_later(payload)
|
11
|
+
head :ok
|
12
|
+
rescue => e # rubocop:disable Style/RescueStandardError
|
13
|
+
Rails.logger.error("Live Events Error: #{e.message} - #{e.backtrace}")
|
14
|
+
render json: { error: "Live Events Error: #{e.message}" }, status: 422
|
15
|
+
end
|
16
|
+
end
|
14
17
|
end
|
15
|
-
|
16
18
|
end
|
data/lib/canvas_sync.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require "bearcat"
|
2
2
|
require "canvas_sync/version"
|
3
3
|
require "canvas_sync/engine"
|
4
|
-
|
5
4
|
require "canvas_sync/job"
|
6
5
|
require "canvas_sync/jobs/report_starter"
|
7
6
|
require "canvas_sync/jobs/report_checker"
|
@@ -12,7 +11,6 @@ require "canvas_sync/jobs/sync_terms_job"
|
|
12
11
|
require "canvas_sync/jobs/sync_users_job"
|
13
12
|
require "canvas_sync/jobs/sync_roles_job"
|
14
13
|
require "canvas_sync/jobs/sync_admins_job"
|
15
|
-
|
16
14
|
Dir[File.dirname(__FILE__) + "/canvas_sync/processors/*.rb"].each { |file| require file }
|
17
15
|
Dir[File.dirname(__FILE__) + "/canvas_sync/importers/*.rb"].each { |file| require file }
|
18
16
|
Dir[File.dirname(__FILE__) + "/canvas_sync/generators/*.rb"].each { |file| require file }
|
@@ -77,36 +75,34 @@ module CanvasSync
|
|
77
75
|
# @param account_id [Integer, nil] This optional parameter can be used if your Term creation and
|
78
76
|
# canvas_sync_client methods require an account ID.
|
79
77
|
# @return [Hash]
|
80
|
-
def self.default_provisioning_report_chain(models, term_scope=nil, legacy_support=false, account_id=nil)
|
78
|
+
def self.default_provisioning_report_chain(models, term_scope=nil, legacy_support=false, account_id=nil) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/LineLength
|
81
79
|
term_scope = term_scope.to_s if term_scope
|
82
80
|
|
83
|
-
|
84
81
|
# Always sync Terms first
|
85
82
|
jobs = [{ job: CanvasSync::Jobs::SyncTermsJob.to_s, options: {} }]
|
86
83
|
|
87
|
-
|
88
|
-
|
89
|
-
jobs.push(
|
90
|
-
models
|
84
|
+
# Users, roles, and admins are synced before provisioning because they cannot be scoped to term
|
85
|
+
if models.include?("users") && term_scope.present?
|
86
|
+
jobs.push(job: CanvasSync::Jobs::SyncUsersJob.to_s, options: {})
|
87
|
+
models -= ["users"]
|
91
88
|
end
|
92
89
|
|
93
|
-
if models.include?(
|
94
|
-
|
95
|
-
|
96
|
-
models = models - ['roles']
|
90
|
+
if models.include?("roles")
|
91
|
+
jobs.push(job: CanvasSync::Jobs::SyncRolesJob.to_s, options: {})
|
92
|
+
models -= ["roles"]
|
97
93
|
end
|
98
94
|
|
99
|
-
if models.include?(
|
100
|
-
|
101
|
-
|
102
|
-
models = models - ['admins']
|
95
|
+
if models.include?("admins")
|
96
|
+
jobs.push(job: CanvasSync::Jobs::SyncAdminsJob.to_s, options: {})
|
97
|
+
models -= ["admins"]
|
103
98
|
end
|
104
99
|
|
105
|
-
jobs.push(
|
100
|
+
jobs.push(
|
101
|
+
job: CanvasSync::Jobs::SyncProvisioningReportJob.to_s,
|
102
|
+
options: { term_scope: term_scope, models: models },
|
103
|
+
)
|
106
104
|
|
107
|
-
if models.include?("assignments")
|
108
|
-
jobs.push(job: CanvasSync::Jobs::SyncAssignmentsJob.to_s, options: {})
|
109
|
-
end
|
105
|
+
jobs.push(job: CanvasSync::Jobs::SyncAssignmentsJob.to_s, options: {}) if models.include?("assignments")
|
110
106
|
|
111
107
|
global_options = { legacy_support: legacy_support }
|
112
108
|
global_options[:account_id] = account_id if account_id.present?
|
@@ -126,19 +122,15 @@ module CanvasSync
|
|
126
122
|
end
|
127
123
|
end
|
128
124
|
|
129
|
-
private
|
130
|
-
|
131
125
|
def self.validate_models!(models)
|
132
126
|
invalid = models - SUPPORTED_MODELS
|
133
|
-
if invalid.
|
134
|
-
|
135
|
-
end
|
127
|
+
return if invalid.empty?
|
128
|
+
raise "Invalid model(s) specified: #{invalid.join(', ')}. Only #{SUPPORTED_MODELS.join(', ')} are supported."
|
136
129
|
end
|
137
130
|
|
138
131
|
def self.validate_live_events!(events)
|
139
132
|
invalid = events - SUPPORTED_LIVE_EVENTS
|
140
|
-
if invalid.
|
141
|
-
|
142
|
-
end
|
133
|
+
return if invalid.empty?
|
134
|
+
raise "Invalid live event(s) specified: #{invalid.join(', ')}. Only #{SUPPORTED_LIVE_EVENTS.join(', ')} are supported."
|
143
135
|
end
|
144
136
|
end
|
data/lib/canvas_sync/engine.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "rails"
|
2
2
|
|
3
3
|
module CanvasSync
|
4
4
|
class Engine < ::Rails::Engine
|
@@ -9,7 +9,7 @@ module CanvasSync
|
|
9
9
|
app.config.paths["db/migrate"] << expanded_path
|
10
10
|
end
|
11
11
|
# Apartment will modify this, but it doesn't fully support engine migrations, so we'll reset it here
|
12
|
-
ActiveRecord::Migrator.migrations_paths = Rails.application.paths[
|
12
|
+
ActiveRecord::Migrator.migrations_paths = Rails.application.paths["db/migrate"].to_a
|
13
13
|
end
|
14
14
|
end
|
15
15
|
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(
|
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)
|
@@ -42,7 +42,7 @@ module CanvasSync
|
|
42
42
|
#
|
43
43
|
# bin/rails generate canvas_sync:install --models all
|
44
44
|
def generate_migrations_and_models
|
45
|
-
models = options[
|
45
|
+
models = options["models"] == "all" ? CanvasSync::SUPPORTED_MODELS : options["models"].split(",")
|
46
46
|
CanvasSync.validate_models!(models)
|
47
47
|
|
48
48
|
models.each do |model|
|
@@ -2,7 +2,7 @@ require "rails/generators"
|
|
2
2
|
|
3
3
|
module CanvasSync
|
4
4
|
class InstallLiveEventsGenerator < Rails::Generators::Base
|
5
|
-
source_root File.expand_path(
|
5
|
+
source_root File.expand_path("../templates/services/live_events", __FILE__)
|
6
6
|
class_option :events, type: :string, required: true
|
7
7
|
|
8
8
|
def autogenerated_event_warning
|
@@ -24,15 +24,15 @@ module CanvasSync
|
|
24
24
|
#
|
25
25
|
# bin/rails generate canvas_sync:install_live_events --events all
|
26
26
|
def generate_live_events
|
27
|
-
events = options[
|
27
|
+
events = options["events"] == "all" ? CanvasSync::SUPPORTED_LIVE_EVENTS : options["events"].split(",")
|
28
28
|
CanvasSync.validate_live_events!(events)
|
29
29
|
|
30
30
|
events.each do |event|
|
31
31
|
Dir.glob("#{File.dirname(__FILE__)}/templates/services/live_events/#{event}/*.rb") do |rb_file|
|
32
|
-
template
|
32
|
+
template rb_file.to_s, "app/services/live_events/#{File.basename(rb_file)}"
|
33
33
|
end
|
34
34
|
end
|
35
|
-
template
|
35
|
+
template "base_event.rb", "app/services/live_events/base_event.rb"
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
@@ -17,9 +17,9 @@ 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: {})
|
20
|
+
def self.import(report_file_path, mapping, klass, conflict_target, import_args: {}) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/LineLength
|
21
21
|
csv_column_names = mapping.keys
|
22
|
-
database_column_names = mapping.values.map {|value| value[:database_column_name]}
|
22
|
+
database_column_names = mapping.values.map { |value| value[:database_column_name] }
|
23
23
|
rows = []
|
24
24
|
row_ids = {}
|
25
25
|
|
@@ -32,9 +32,9 @@ module CanvasSync
|
|
32
32
|
|
33
33
|
rows << csv_column_names.map do |column|
|
34
34
|
if mapping[column][:type].to_sym == :datetime
|
35
|
-
#
|
35
|
+
# TODO: add some timezone config to the mapping.
|
36
36
|
# In cases where the timestamp or date doesn't include a timezone, you should be able to specify one
|
37
|
-
DateTime.parse(row[column]).utc rescue nil
|
37
|
+
DateTime.parse(row[column]).utc rescue nil # rubocop:disable Style/RescueModifier
|
38
38
|
else
|
39
39
|
row[column]
|
40
40
|
end
|
@@ -50,17 +50,17 @@ module CanvasSync
|
|
50
50
|
perform_import(klass, database_column_names, rows, mapping[conflict_target][:database_column_name], import_args)
|
51
51
|
end
|
52
52
|
|
53
|
-
private
|
54
|
-
|
55
53
|
def self.perform_import(klass, columns, rows, conflict_target, import_args={})
|
56
|
-
return if rows.length
|
54
|
+
return if rows.length.zero?
|
57
55
|
columns = columns.dup
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
56
|
+
|
57
|
+
options = { validate: false, on_duplicate_key_update: {
|
58
|
+
conflict_target: conflict_target,
|
59
|
+
condition: condition_sql(klass, columns),
|
60
|
+
columns: columns
|
61
|
+
} }.merge(import_args)
|
62
|
+
|
63
|
+
options.delete(:on_duplicate_key_update) if options.key?(:on_duplicate_key_ignore)
|
64
64
|
klass.import(columns, rows, options)
|
65
65
|
end
|
66
66
|
|
@@ -75,13 +75,13 @@ module CanvasSync
|
|
75
75
|
# run_the_users_sync!
|
76
76
|
# changed = User.where("updated_at >= ?", started_at)
|
77
77
|
def self.condition_sql(klass, columns)
|
78
|
-
columns_str = columns.map {|c| "#{klass.quoted_table_name}.#{c}"}.join(", ")
|
79
|
-
excluded_str = columns.map {|c| "EXCLUDED.#{c}"}.join(", ")
|
78
|
+
columns_str = columns.map { |c| "#{klass.quoted_table_name}.#{c}" }.join(", ")
|
79
|
+
excluded_str = columns.map { |c| "EXCLUDED.#{c}" }.join(", ")
|
80
80
|
"(#{columns_str}) IS DISTINCT FROM (#{excluded_str})"
|
81
81
|
end
|
82
82
|
|
83
83
|
def self.batch_size
|
84
|
-
batch_size = ENV[
|
84
|
+
batch_size = ENV["BULK_IMPORTER_BATCH_SIZE"].to_i
|
85
85
|
batch_size > 0 ? batch_size : DEFAULT_BATCH_SIZE
|
86
86
|
end
|
87
87
|
end
|
data/lib/canvas_sync/job.rb
CHANGED
@@ -5,20 +5,20 @@ module CanvasSync
|
|
5
5
|
class Job < ActiveJob::Base
|
6
6
|
around_perform do |job, block|
|
7
7
|
@job_log = CanvasSync::JobLog.create(
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
started_at: Time.now,
|
9
|
+
job_class: self.class.name,
|
10
|
+
job_arguments: job.arguments,
|
11
11
|
)
|
12
12
|
|
13
13
|
begin
|
14
14
|
block.call
|
15
|
-
rescue => e
|
15
|
+
rescue => e # rubocop:disable Style/RescueStandardError
|
16
16
|
@job_log.exception = "#{e.class}: #{e.message}"
|
17
17
|
@job_log.backtrace = e.backtrace
|
18
18
|
@job_log.status = JobLog::ERROR_STATUS
|
19
19
|
raise e
|
20
20
|
ensure
|
21
|
-
if @job_log.job_class ==
|
21
|
+
if @job_log.job_class == "CanvasSync::Jobs::ReportChecker" && @job_log.status != JobLog::ERROR_STATUS
|
22
22
|
@job_log.destroy
|
23
23
|
else
|
24
24
|
@job_log.completed_at = Time.now
|
@@ -10,32 +10,34 @@ module CanvasSync
|
|
10
10
|
# @param processor [String] a stringified report processor class name
|
11
11
|
# @param options [Hash] hash of options that will be passed to the job processor
|
12
12
|
# @return [nil]
|
13
|
-
def perform(job_chain, report_name, report_id, processor, options)
|
14
|
-
account_id = options[:account_id] || job_chain[:global_options][:account_id] ||
|
15
|
-
report_status = CanvasSync.get_canvas_sync_client(job_chain[:global_options])
|
13
|
+
def perform(job_chain, report_name, report_id, processor, options) # rubocop:disable Metrics/AbcSize
|
14
|
+
account_id = options[:account_id] || job_chain[:global_options][:account_id] || "self"
|
15
|
+
report_status = CanvasSync.get_canvas_sync_client(job_chain[:global_options])
|
16
|
+
.report_status(account_id, report_name, report_id)
|
16
17
|
|
17
|
-
case report_status[
|
18
|
-
when
|
18
|
+
case report_status["status"].downcase
|
19
|
+
when "complete"
|
19
20
|
CanvasSync::Jobs::ReportProcessorJob.perform_later(
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
job_chain,
|
22
|
+
report_name,
|
23
|
+
report_status["attachment"]["url"],
|
24
|
+
processor,
|
25
|
+
options,
|
25
26
|
)
|
26
|
-
when
|
27
|
-
message = "Report failed to process; status was #{report_status} for report_name: #{report_name}, report_id: #{report_id}"
|
27
|
+
when "error", "deleted"
|
28
|
+
message = "Report failed to process; status was #{report_status} for report_name: #{report_name}, report_id: #{report_id}" # rubocop:disable Metrics/LineLength
|
28
29
|
Rails.logger.error(message)
|
29
30
|
raise message
|
30
31
|
else
|
31
|
-
CanvasSync::Jobs::ReportChecker
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
CanvasSync::Jobs::ReportChecker
|
33
|
+
.set(wait: report_checker_wait_time)
|
34
|
+
.perform_later(
|
35
|
+
job_chain,
|
36
|
+
report_name,
|
37
|
+
report_id,
|
38
|
+
processor,
|
39
|
+
options,
|
40
|
+
)
|
39
41
|
end
|
40
42
|
end
|
41
43
|
end
|
@@ -11,7 +11,7 @@ module CanvasSync
|
|
11
11
|
# so that any later jobs in the chain will use the same generated report
|
12
12
|
# @return [nil]
|
13
13
|
def perform(job_chain, report_name, report_params, processor, options, allow_redownloads: false)
|
14
|
-
account_id = options[:account_id] || job_chain[:global_options][:account_id] ||
|
14
|
+
account_id = options[:account_id] || job_chain[:global_options][:account_id] || "self"
|
15
15
|
|
16
16
|
report_id = if allow_redownloads
|
17
17
|
get_cached_report(job_chain, account_id, report_name, report_params)
|
@@ -20,11 +20,11 @@ module CanvasSync
|
|
20
20
|
end
|
21
21
|
|
22
22
|
CanvasSync::Jobs::ReportChecker.set(wait: report_checker_wait_time).perform_later(
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
23
|
+
job_chain,
|
24
|
+
report_name,
|
25
|
+
report_id,
|
26
|
+
processor,
|
27
|
+
options,
|
28
28
|
)
|
29
29
|
end
|
30
30
|
|
@@ -41,8 +41,9 @@ module CanvasSync
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def start_report(job_chain, account_id, report_name, report_params)
|
44
|
-
report = CanvasSync.get_canvas_sync_client(job_chain[:global_options])
|
45
|
-
|
44
|
+
report = CanvasSync.get_canvas_sync_client(job_chain[:global_options])
|
45
|
+
.start_report(account_id, report_name, report_params)
|
46
|
+
report["id"]
|
46
47
|
end
|
47
48
|
end
|
48
49
|
end
|
@@ -6,9 +6,9 @@ module CanvasSync
|
|
6
6
|
#
|
7
7
|
# @param job_chain [Hash]
|
8
8
|
# @param options [Hash]
|
9
|
-
def perform(job_chain,
|
9
|
+
def perform(job_chain, _options)
|
10
10
|
updated_admins = []
|
11
|
-
CanvasSync.get_canvas_sync_client(job_chain[:global_options]).account_admins(
|
11
|
+
CanvasSync.get_canvas_sync_client(job_chain[:global_options]).account_admins("self").all_pages!.each do |admin_params|
|
12
12
|
admin = Admin.create_or_update(admin_params)
|
13
13
|
updated_admins.push(admin.id)
|
14
14
|
end
|