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