canvas_sync 0.12.0 → 0.16.1
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/lib/canvas_sync.rb +85 -30
- data/lib/canvas_sync/api_syncable.rb +4 -162
- data/lib/canvas_sync/class_callback_executor.rb +35 -0
- data/lib/canvas_sync/concerns/account/ancestry.rb +60 -0
- data/lib/canvas_sync/concerns/api_syncable.rb +189 -0
- data/lib/canvas_sync/concerns/legacy_columns.rb +34 -0
- data/lib/canvas_sync/generators/templates/migrations/create_group_memberships.rb +18 -0
- data/lib/canvas_sync/generators/templates/migrations/create_groups.rb +23 -0
- data/lib/canvas_sync/generators/templates/migrations/create_pseudonyms.rb +18 -0
- data/lib/canvas_sync/generators/templates/migrations/create_submissions.rb +1 -0
- data/lib/canvas_sync/generators/templates/models/account.rb +11 -1
- data/lib/canvas_sync/generators/templates/models/admin.rb +2 -1
- data/lib/canvas_sync/generators/templates/models/assignment.rb +2 -1
- data/lib/canvas_sync/generators/templates/models/assignment_group.rb +2 -1
- data/lib/canvas_sync/generators/templates/models/context_module.rb +2 -1
- data/lib/canvas_sync/generators/templates/models/context_module_item.rb +2 -1
- data/lib/canvas_sync/generators/templates/models/course.rb +4 -2
- data/lib/canvas_sync/generators/templates/models/enrollment.rb +2 -1
- data/lib/canvas_sync/generators/templates/models/group.rb +19 -0
- data/lib/canvas_sync/generators/templates/models/group_membership.rb +17 -0
- data/lib/canvas_sync/generators/templates/models/pseudonym.rb +8 -0
- data/lib/canvas_sync/generators/templates/models/role.rb +2 -1
- data/lib/canvas_sync/generators/templates/models/section.rb +2 -1
- data/lib/canvas_sync/generators/templates/models/submission.rb +3 -1
- data/lib/canvas_sync/generators/templates/models/term.rb +2 -1
- data/lib/canvas_sync/generators/templates/models/user.rb +4 -1
- data/lib/canvas_sync/importers/bulk_importer.rb +7 -1
- data/lib/canvas_sync/importers/legacy_importer.rb +4 -2
- data/lib/canvas_sync/job.rb +3 -1
- data/lib/canvas_sync/job_chain.rb +57 -0
- data/lib/canvas_sync/jobs/{sync_users_job.rb → sync_accounts_job.rb} +11 -6
- data/lib/canvas_sync/jobs/sync_provisioning_report_job.rb +2 -0
- data/lib/canvas_sync/processors/model_mappings.yml +81 -0
- data/lib/canvas_sync/processors/provisioning_report_processor.rb +28 -0
- data/lib/canvas_sync/record.rb +9 -0
- data/lib/canvas_sync/version.rb +1 -1
- data/spec/canvas_sync/canvas_sync_spec.rb +19 -16
- data/spec/canvas_sync/models/accounts_spec.rb +3 -0
- data/spec/canvas_sync/models/course_spec.rb +4 -0
- data/spec/canvas_sync/models/group_membership_spec.rb +26 -0
- data/spec/canvas_sync/models/group_spec.rb +26 -0
- data/spec/canvas_sync/models/user_spec.rb +2 -0
- data/spec/canvas_sync/processors/provisioning_report_processor_spec.rb +20 -0
- data/spec/dummy/app/models/account.rb +8 -1
- data/spec/dummy/app/models/admin.rb +2 -1
- data/spec/dummy/app/models/assignment.rb +2 -1
- data/spec/dummy/app/models/assignment_group.rb +2 -1
- data/spec/dummy/app/models/context_module.rb +2 -1
- data/spec/dummy/app/models/context_module_item.rb +2 -1
- data/spec/dummy/app/models/course.rb +4 -2
- data/spec/dummy/app/models/enrollment.rb +2 -1
- data/spec/dummy/app/models/group.rb +25 -0
- data/spec/dummy/app/models/group_membership.rb +23 -0
- data/spec/dummy/app/models/role.rb +2 -1
- data/spec/dummy/app/models/section.rb +2 -1
- data/spec/dummy/app/models/submission.rb +2 -1
- data/spec/dummy/app/models/term.rb +2 -1
- data/spec/dummy/app/models/user.rb +3 -1
- data/spec/dummy/config/application.rb +12 -1
- data/spec/dummy/config/database.yml +11 -11
- data/spec/dummy/config/environments/development.rb +3 -3
- data/spec/dummy/config/initializers/assets.rb +1 -1
- data/spec/dummy/db/migrate/20190702203627_create_submissions.rb +1 -0
- data/spec/dummy/db/migrate/20200415171620_create_groups.rb +29 -0
- data/spec/dummy/db/migrate/20200416214248_create_group_memberships.rb +24 -0
- data/spec/dummy/db/schema.rb +31 -1
- data/spec/factories/group_factory.rb +8 -0
- data/spec/factories/group_membership_factory.rb +6 -0
- data/spec/support/fixtures/reports/group_memberships.csv +3 -0
- data/spec/support/fixtures/reports/groups.csv +3 -0
- data/spec/support/fixtures/reports/submissions.csv +3 -3
- metadata +36 -6
- data/spec/canvas_sync/jobs/sync_users_job_spec.rb +0 -15
@@ -0,0 +1,189 @@
|
|
1
|
+
module CanvasSync::Concerns
|
2
|
+
module ApiSyncable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
NON_EXISTANT_ERRORS = [Faraday::Error::ResourceNotFound, Footrest::HttpError::NotFound]
|
5
|
+
|
6
|
+
class_methods do
|
7
|
+
def find_or_fetch(canvas_id, save: false, retries: 1, **kwargs)
|
8
|
+
inst = find_or_initialize_by(canvas_id: canvas_id)
|
9
|
+
return inst if inst.persisted?
|
10
|
+
|
11
|
+
api_response = inst.request_from_api(retries: retries, **kwargs)
|
12
|
+
api_sync_race_create!(inst, save: save) do |inst2|
|
13
|
+
inst2.assign_from_api_params(api_response, **kwargs)
|
14
|
+
end
|
15
|
+
rescue *NON_EXISTANT_ERRORS
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_or_fetch!(*args)
|
20
|
+
inst = find_or_fetch(*args)
|
21
|
+
raise ActiveRecord::RecordNotFound unless inst.present?
|
22
|
+
inst
|
23
|
+
end
|
24
|
+
|
25
|
+
def create_or_update_from_api_params(api_params)
|
26
|
+
api_params = api_params.with_indifferent_access
|
27
|
+
api_sync_race_create!(api_params[:id]) do |inst|
|
28
|
+
inst.assign_from_api_params(api_params)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def api_sync_options=(opts)
|
33
|
+
@api_sync_options = opts
|
34
|
+
end
|
35
|
+
|
36
|
+
def api_sync_options
|
37
|
+
@api_sync_options || superclass.try(:api_sync_options)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Define the model as being syncable via the Canvas API and configure sync options/parameters
|
41
|
+
# @param [Hash] map A hash of local_field => (:api_response_key | ->(api_response){ value })
|
42
|
+
# @param [->(bearcat?){ api_response }] fetch <description>
|
43
|
+
# @param [Hash] options <description>
|
44
|
+
# @option options [] :mark_deleted Hash to be merged | Symbol to invoke | ->(){ }
|
45
|
+
# @yield [api_response, [mapped_data]] Callback to merge data into a Model instance
|
46
|
+
def api_syncable(map, fetch, options={}, &blk)
|
47
|
+
default_options = {
|
48
|
+
mark_deleted: -> {
|
49
|
+
%i[workflow_state= status=].each do |sym|
|
50
|
+
next unless self.respond_to?(sym)
|
51
|
+
self.send(sym, 'deleted')
|
52
|
+
return
|
53
|
+
end
|
54
|
+
},
|
55
|
+
field_map: map,
|
56
|
+
fetch_from_api: fetch,
|
57
|
+
process_response: blk,
|
58
|
+
}
|
59
|
+
default_options.merge!(options)
|
60
|
+
self.api_sync_options = default_options.merge!(options)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def api_sync_race_create!(inst, save: true)
|
66
|
+
inst = find_or_initialize_by(canvas_id: inst) unless inst.is_a?(self)
|
67
|
+
yield inst
|
68
|
+
inst.save! if save && inst.changed?
|
69
|
+
inst
|
70
|
+
rescue ActiveRecord::RecordNotUnique
|
71
|
+
inst = find_by(canvas_id: inst.canvas_id)
|
72
|
+
yield inst
|
73
|
+
inst.save! if save && inst.changed?
|
74
|
+
inst
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Call the API and Syncs this model.
|
79
|
+
# Calls the mark_deleted workflow if a 404 is received.
|
80
|
+
# @param [Number] retries Number of retries
|
81
|
+
# @return [Hash] Response Hash from API
|
82
|
+
def sync_from_api(retries: 3, **kwargs)
|
83
|
+
api_response = request_from_api(retries: retries, **kwargs)
|
84
|
+
update_from_api_params!(api_response, **kwargs)
|
85
|
+
api_response
|
86
|
+
rescue *NON_EXISTANT_ERRORS
|
87
|
+
api_mark_deleted
|
88
|
+
save! if changed?
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
|
92
|
+
# Fetch this model from the API and return the response
|
93
|
+
# @param [Number] retries Number of retries
|
94
|
+
# @return [Hash] Response Hash from API
|
95
|
+
def request_from_api(retries: 3, **kwargs)
|
96
|
+
api_call_with_retry(retries || 3) {
|
97
|
+
blk = api_sync_options[:fetch_from_api]
|
98
|
+
case blk.arity
|
99
|
+
when 1
|
100
|
+
self.instance_exec(canvas_sync_client, &blk)
|
101
|
+
else
|
102
|
+
self.instance_exec(&blk)
|
103
|
+
end
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
# Apply a response Hash from the API to this model's attributes, but do not save
|
108
|
+
# @param [Hash] api_params API-format Hash
|
109
|
+
# @return [self] self
|
110
|
+
def assign_from_api_params(api_params, **kwargs)
|
111
|
+
options = self.api_sync_options
|
112
|
+
api_params = api_params.with_indifferent_access
|
113
|
+
|
114
|
+
map = options[:field_map]
|
115
|
+
mapped_params = {}
|
116
|
+
if map.present?
|
117
|
+
map.each do |local_name, remote_name|
|
118
|
+
if remote_name.respond_to?(:call)
|
119
|
+
mapped_params[local_name] = self.instance_exec(api_params, &remote_name)
|
120
|
+
elsif api_params.include?(remote_name)
|
121
|
+
mapped_params[local_name] = api_params[remote_name]
|
122
|
+
if remote_name == :id
|
123
|
+
current_value = send("#{local_name}")
|
124
|
+
raise "Mismatched Canvas ID" if current_value.present? && current_value != api_params[remote_name]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
apply_block = options[:process_response]
|
131
|
+
if apply_block.present?
|
132
|
+
case apply_block.arity
|
133
|
+
when 1
|
134
|
+
self.instance_exec(api_params, &apply_block)
|
135
|
+
when 2
|
136
|
+
self.instance_exec(api_params, mapped_params, &apply_block)
|
137
|
+
end
|
138
|
+
else
|
139
|
+
mapped_params.each do |local_name, val|
|
140
|
+
send("#{local_name}=", val)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
# Apply a response Hash from the API to this model's attributes and save if changed?
|
147
|
+
# @param [Hash] api_params API-format Hash
|
148
|
+
# @return [self] self
|
149
|
+
def update_from_api_params(*args)
|
150
|
+
assign_from_api_params(*args)
|
151
|
+
save if changed?
|
152
|
+
end
|
153
|
+
|
154
|
+
# Apply a response Hash from the API to this model's attributes, and save! if changed?
|
155
|
+
# @param [Hash] api_params API-format Hash
|
156
|
+
# @return [self] self
|
157
|
+
def update_from_api_params!(*args)
|
158
|
+
assign_from_api_params(*args)
|
159
|
+
save! if changed?
|
160
|
+
end
|
161
|
+
|
162
|
+
def api_sync_options
|
163
|
+
self.class.api_sync_options
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def api_call_with_retry(retries=3)
|
169
|
+
tries ||= retries
|
170
|
+
yield
|
171
|
+
rescue Faraday::ConnectionFailed => e
|
172
|
+
raise e if (tries -= 1).zero?
|
173
|
+
sleep 5
|
174
|
+
retry
|
175
|
+
end
|
176
|
+
|
177
|
+
def api_mark_deleted
|
178
|
+
action = api_sync_options[:mark_deleted]
|
179
|
+
case action
|
180
|
+
when Hash
|
181
|
+
assign_attributes(action)
|
182
|
+
when Symbol
|
183
|
+
send(action)
|
184
|
+
when Proc
|
185
|
+
self.instance_exec(&action)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Concern that can be used when dealing with Legacy code.
|
2
|
+
#
|
3
|
+
# It automatically aliases `canvas_id` -> `canvas_<model>_id` or vice-versa.
|
4
|
+
module CanvasSync::Concerns
|
5
|
+
module LegacyColumns
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
class_methods do
|
9
|
+
def inherited(subclass)
|
10
|
+
super.tap do
|
11
|
+
legacy_column_apply(subclass)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def legacy_column_apply(cls)
|
18
|
+
cid_column = "canvas_#{subclass.name.downcase}_id"
|
19
|
+
column_names = subclass.columns.map(&:name)
|
20
|
+
return if column_names.include?('canvas_id') && column_names.include?(cid_column)
|
21
|
+
if column_names.include?('canvas_id')
|
22
|
+
subclass.alias_attribute(cid_column.to_sym, :canvas_id)
|
23
|
+
elsif column_names.include?(cid_column)
|
24
|
+
subclass.alias_attribute(:canvas_id, cid_column.to_sym)
|
25
|
+
end
|
26
|
+
rescue ActiveRecord::StatementInvalid
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
included do
|
31
|
+
legacy_column_apply(self)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# <%= autogenerated_migration_warning %>
|
2
|
+
|
3
|
+
class CreateGroupMemberships < ActiveRecord::Migration[5.1]
|
4
|
+
def change
|
5
|
+
create_table :group_memberships do |t|
|
6
|
+
t.bigint :canvas_id, null: false
|
7
|
+
t.bigint :canvas_user_id, null: false
|
8
|
+
t.bigint :canvas_group_id, null: false
|
9
|
+
t.string :group_sis_id
|
10
|
+
t.string :user_sis_id
|
11
|
+
t.string :workflow_state
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
|
16
|
+
add_index :group_memberships, :canvas_id, unique: true
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# <%= autogenerated_migration_warning %>
|
2
|
+
|
3
|
+
class CreateGroups < ActiveRecord::Migration[5.1]
|
4
|
+
def change
|
5
|
+
create_table :groups do |t|
|
6
|
+
t.bigint :canvas_id, null: false
|
7
|
+
t.string :sis_id
|
8
|
+
t.bigint :canvas_group_category_id
|
9
|
+
t.string :group_category_sis_id
|
10
|
+
t.bigint :canvas_account_id
|
11
|
+
t.bigint :canvas_course_id
|
12
|
+
t.string :name
|
13
|
+
t.string :workflow_state
|
14
|
+
t.bigint :context_id
|
15
|
+
t.string :context_type
|
16
|
+
t.integer :max_membership
|
17
|
+
|
18
|
+
t.timestamps
|
19
|
+
end
|
20
|
+
|
21
|
+
add_index :groups, :canvas_id, unique: true
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# <%= autogenerated_migration_warning %>
|
2
|
+
|
3
|
+
class CreatePseudonyms < ActiveRecord::Migration[5.1]
|
4
|
+
def change
|
5
|
+
create_table :pseudonyms do |t|
|
6
|
+
t.bigint :canvas_id, null: false
|
7
|
+
t.bigint :canvas_user_id
|
8
|
+
t.string :sis_id
|
9
|
+
t.string :unique_id
|
10
|
+
t.string :workflow_state
|
11
|
+
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
add_index :pseudonyms, :canvas_id, unique: true
|
16
|
+
add_index :pseudonyms, :canvas_user_id
|
17
|
+
end
|
18
|
+
end
|
@@ -1,11 +1,21 @@
|
|
1
1
|
# <%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class Account < ApplicationRecord
|
4
|
-
include CanvasSync::
|
4
|
+
include CanvasSync::Record
|
5
|
+
include CanvasSync::Concerns::ApiSyncable
|
6
|
+
# include CanvasSync::Concerns::Account::Ancestry # Add support for the ancestry Gem
|
5
7
|
|
6
8
|
validates :canvas_id, uniqueness: true, presence: true
|
7
9
|
|
8
10
|
has_many :admins, primary_key: :canvas_id, foreign_key: :canvas_account_id
|
11
|
+
belongs_to :canvas_parent, class_name: 'Account', optional: true,
|
12
|
+
primary_key: :canvas_id, foreign_key: :canvas_parent_account_id
|
13
|
+
has_many :sub_accounts, class_name: 'Account',
|
14
|
+
primary_key: :canvas_id, foreign_key: :canvas_parent_account_id
|
15
|
+
has_many :groups, primary_key: :canvas_id, foreign_key: :canvas_account_id
|
16
|
+
|
17
|
+
scope :active, -> { where.not(workflow_state: 'deleted') }
|
18
|
+
# scope :should_canvas_sync, -> { active } # Optional - uses .active if not given
|
9
19
|
|
10
20
|
api_syncable({
|
11
21
|
name: :name,
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# <%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class Admin < ApplicationRecord
|
4
|
-
include CanvasSync::
|
4
|
+
include CanvasSync::Record
|
5
|
+
include CanvasSync::Concerns::ApiSyncable
|
5
6
|
|
6
7
|
validates :canvas_id, uniqueness: true, presence: true
|
7
8
|
belongs_to :account, primary_key: :canvas_id, foreign_key: :canvas_account_id, optional: true
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# <%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class Assignment < ApplicationRecord
|
4
|
-
include CanvasSync::
|
4
|
+
include CanvasSync::Record
|
5
|
+
include CanvasSync::Concerns::ApiSyncable
|
5
6
|
|
6
7
|
validates :canvas_id, uniqueness: true, presence: true
|
7
8
|
belongs_to :context, polymorphic: true, optional: true, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# <%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class AssignmentGroup < ApplicationRecord
|
4
|
-
include CanvasSync::
|
4
|
+
include CanvasSync::Record
|
5
|
+
include CanvasSync::Concerns::ApiSyncable
|
5
6
|
|
6
7
|
validates :canvas_id, uniqueness: true, presence: true
|
7
8
|
belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
|
@@ -4,7 +4,8 @@
|
|
4
4
|
# 1 - Module is a reserved word in Rails and you can't call a model a Module
|
5
5
|
# 2 - Canvas calls them ContextModules
|
6
6
|
class ContextModule < ApplicationRecord
|
7
|
-
include CanvasSync::
|
7
|
+
include CanvasSync::Record
|
8
|
+
include CanvasSync::Concerns::ApiSyncable
|
8
9
|
|
9
10
|
belongs_to :context, polymorphic: true, optional: true, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
|
10
11
|
has_many :context_module_items, primary_key: :canvas_id, foreign_key: :canvas_context_module_id
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# # <%= autogenerated_migration_warning %>
|
2
2
|
|
3
3
|
class ContextModuleItem < ApplicationRecord
|
4
|
-
include CanvasSync::
|
4
|
+
include CanvasSync::Record
|
5
|
+
include CanvasSync::Concerns::ApiSyncable
|
5
6
|
|
6
7
|
belongs_to :context_module, primary_key: :canvas_id, foreign_key: :canvas_context_module_id, optional: true
|
7
8
|
belongs_to :content, polymorphic: true, optional: true, primary_key: :canvas_id, foreign_key: :canvas_content_id, foreign_type: :canvas_content_type
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# <%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class Course < ApplicationRecord
|
4
|
-
include CanvasSync::
|
4
|
+
include CanvasSync::Record
|
5
|
+
include CanvasSync::Concerns::ApiSyncable
|
5
6
|
|
6
7
|
validates :canvas_id, uniqueness: true, presence: true
|
7
8
|
belongs_to :term, foreign_key: :canvas_term_id, primary_key: :canvas_id, optional: true
|
@@ -10,7 +11,8 @@ class Course < ApplicationRecord
|
|
10
11
|
has_many :assignments, as: :context, primary_key: :canvas_id, foreign_key: :canvas_context_id, foreign_type: :canvas_context_type
|
11
12
|
has_many :submissions, primary_key: :canvas_id, foreign_key: :canvas_course_id
|
12
13
|
has_many :assignment_groups, primary_key: :canvas_id, foreign_key: :canvas_course_id
|
13
|
-
|
14
|
+
has_many :groups, primary_key: :canvas_id, foreign_key: :canvas_course_id
|
15
|
+
|
14
16
|
api_syncable({
|
15
17
|
sis_id: :sis_course_id,
|
16
18
|
course_code: :course_code,
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# <%= autogenerated_model_warning %>
|
2
2
|
|
3
3
|
class Enrollment < ApplicationRecord
|
4
|
-
include CanvasSync::
|
4
|
+
include CanvasSync::Record
|
5
|
+
include CanvasSync::Concerns::ApiSyncable
|
5
6
|
|
6
7
|
validates :canvas_id, uniqueness: true, presence: true
|
7
8
|
belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id, optional: true
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# <%= autogenerated_model_warning %>
|
2
|
+
|
3
|
+
class Group < ApplicationRecord
|
4
|
+
include CanvasSync::Record
|
5
|
+
include CanvasSync::Concerns::ApiSyncable
|
6
|
+
|
7
|
+
validates :canvas_id, uniqueness: true, presence: true
|
8
|
+
belongs_to :course, primary_key: :canvas_id, foreign_key: :canvas_course_id, optional: true
|
9
|
+
belongs_to :account, primary_key: :canvas_id, foreign_key: :canvas_account_id, optional: true
|
10
|
+
has_many :group_memberships, primary_key: :canvas_id, foreign_key: :canvas_group_id
|
11
|
+
|
12
|
+
api_syncable({
|
13
|
+
canvas_id: :id,
|
14
|
+
name: :name,
|
15
|
+
canvas_course_id: :course_id,
|
16
|
+
sis_id: :sis_group_id,
|
17
|
+
workflow_state: ->(r){ 'available' },
|
18
|
+
}, -> (api) { api.group(canvas_id) })
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# <%= autogenerated_model_warning %>
|
2
|
+
|
3
|
+
class GroupMembership < ApplicationRecord
|
4
|
+
include CanvasSync::Record
|
5
|
+
include CanvasSync::Concerns::ApiSyncable
|
6
|
+
|
7
|
+
validates :canvas_id, uniqueness: true, presence: true
|
8
|
+
belongs_to :group, primary_key: :canvas_id, foreign_key: :canvas_group_id
|
9
|
+
belongs_to :user, primary_key: :canvas_id, foreign_key: :canvas_user_id
|
10
|
+
|
11
|
+
api_syncable({
|
12
|
+
canvas_id: :id,
|
13
|
+
canvas_group_id: :group_id,
|
14
|
+
canvas_user_id: :user_id,
|
15
|
+
workflow_state: :workflow_state,
|
16
|
+
}, -> (api) { api.group_membership(canvas_group_id, canvas_id) })
|
17
|
+
end
|