populate root_account_id on calendar_events

closes INTEROP-5844
flag=none

test plan:
- have existing CalendarEvents without root account id,
  preferably at least one with each context type
  (Course, CourseSection, Group, User w/no effective
  context, User w/effective context, AppointmentGroup)
- run `DataFixup::PopulateRootAccountIdOnCalendarEvents.run`
- verify all events (except User w/o effective context)
  have a root_account_id

Change-Id: I7d3c3c7221c25b3ae2d8c1b8819307cc66be229e
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/242024
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
QA-Review: Weston Dransfield <wdransfield@instructure.com>
Reviewed-by: Cody Cutrer <cody@instructure.com>
Reviewed-by: Mysti Lilla <mysti@instructure.com>
Product-Review: Xander Moffatt <xmoffatt@instructure.com>
This commit is contained in:
Xander Moffatt 2020-07-06 14:40:55 -06:00
parent 75f7970346
commit 81f3dbad4f
5 changed files with 195 additions and 0 deletions

View File

@ -0,0 +1,32 @@
#
# Copyright (C) 2020 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
class PopulateRootAccountIdOnCalendarEvents < ActiveRecord::Migration[5.2]
tag :postdeploy
def up
CalendarEvent.find_ids_in_ranges(batch_size: 100_000) do |min, max|
DataFixup::PopulateRootAccountIdOnCalendarEvents.send_later_if_production_enqueue_args(
:run,
{:priority => Delayed::LOWER_PRIORITY, :n_strand => ["root_account_id_backfill_strand", Shard.current.database_server.id]},
min, max
)
end
end
def down; end
end

View File

@ -0,0 +1,46 @@
#
# Copyright (C) 2020 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
module DataFixup::PopulateRootAccountIdOnCalendarEvents
def self.run(min, max)
# CalendarEvents with context of Course, Group, or CourseSection are handled
# by PopulateRootAccountIdOnModels.
# events with context of AppointmentGroup and User are special cases and
# are handled here.
# effective_context is populated in one of two ways:
# 1. as part of an appointment group, which will either be a Course or CourseSection
# 2. from a parent event, not part of an appointment group. This will be the parent
# event's context, which will be one of Course/CourseSection/Group.
effective_context_types = [
Course,
CourseSection,
Group
]
effective_context_types.each do |model|
qtn = model.quoted_table_name # to appease the linter on line 41
CalendarEvent.find_ids_in_ranges(start_at: min, end_at: max) do |batch_min, batch_max|
CalendarEvent.where(id: batch_min..batch_max, context_type: ["AppointmentGroup", "User"]).
where("effective_context_code like ?", "#{model.table_name.singularize}%").
# pull id from effective context code that looks like `course_1` or `course_section_1`
joins("INNER JOIN #{qtn} ON #{qtn}.id = cast(reverse(split_part(reverse(calendar_events.effective_context_code), '_', 1)) as integer)").
update_all("root_account_id=#{qtn}.root_account_id")
end
end
end
end

View File

@ -63,6 +63,7 @@ module DataFixup::PopulateRootAccountIdOnModels
AssignmentGroup => :context,
AssignmentOverride => :assignment,
AssignmentOverrideStudent => :assignment,
CalendarEvent => [:context_course, :context_group, :context_course_section],
ContentMigration => [:account, :course, :group],
ContentParticipation => :content,
ContentParticipationCount => :course,

View File

@ -0,0 +1,93 @@
#
# Copyright (C) 2020 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
require 'spec_helper'
describe DataFixup::PopulateRootAccountIdOnCalendarEvents do
it 'ignores CalendarEvents with Course context' do
event = CalendarEvent.create!(context: course_model)
event.update_column(:root_account_id, nil)
DataFixup::PopulateRootAccountIdOnCalendarEvents.run(event.id, event.id)
expect(event.reload.root_account_id).to be_nil
end
it 'ignores CalendarEvents with Group context' do
event = CalendarEvent.create!(context: group_model)
event.update_column(:root_account_id, nil)
DataFixup::PopulateRootAccountIdOnCalendarEvents.run(event.id, event.id)
expect(event.reload.root_account_id).to be_nil
end
it 'ignores CalendarEvents with CourseSection context' do
section = CourseSection.create!(course: course_model)
event = CalendarEvent.create!(context: section)
event.update_column(:root_account_id, nil)
DataFixup::PopulateRootAccountIdOnCalendarEvents.run(event.id, event.id)
expect(event.reload.root_account_id).to be_nil
end
it 'ignores CalendarEvents with User context and no effective context' do
event = CalendarEvent.create!(context: user_model)
event.update_column(:root_account_id, nil)
DataFixup::PopulateRootAccountIdOnCalendarEvents.run(event.id, event.id)
expect(event.reload.root_account_id).to be_nil
end
it 'sets root_account_id from effective context for CalendarEvent with User context' do
course = course_model
event = CalendarEvent.create!(context: user_model, effective_context_code: course.asset_string)
event.update_column(:root_account_id, nil)
DataFixup::PopulateRootAccountIdOnCalendarEvents.run(event.id, event.id)
expect(event.reload.root_account_id).to eq course.root_account_id
end
it 'sets root_account_id from effective context for CalendarEvent with AppointmentGroup context' do
course = course_model
ag = AppointmentGroup.create!
AppointmentGroupContext.create!(appointment_group: ag, context: course)
event = CalendarEvent.create!(context: ag.reload)
event.update_column(:root_account_id, nil)
DataFixup::PopulateRootAccountIdOnCalendarEvents.run(event.id, event.id)
expect(event.reload.root_account_id).to eq course.root_account_id
end
it 'sets root_account_id from effective context for child CalendarEvent' do
course = course_model
parent = CalendarEvent.create!(context: course)
event = CalendarEvent.create!(context: user_model, parent_event: parent)
event.update_column(:root_account_id, nil)
DataFixup::PopulateRootAccountIdOnCalendarEvents.run(event.id, event.id)
expect(event.reload.root_account_id).to eq course.root_account_id
end
it 'sets root_account_id from effective context for multiple CalendarEvents' do
course = course_model
e1 = CalendarEvent.create!(context: user_model, effective_context_code: course.asset_string)
ag = AppointmentGroup.create!
AppointmentGroupContext.create!(appointment_group: ag, context: course)
e2 = CalendarEvent.create!(context: ag.reload)
parent = CalendarEvent.create!(context: course)
e3 = CalendarEvent.create!(context: user_model, parent_event: parent)
events = [e1, e2, e3]
CalendarEvent.where(id: events.map(&:id)).update_all(root_account_id: nil)
DataFixup::PopulateRootAccountIdOnCalendarEvents.run(e1.id, e3.id)
events.each do |e|
expect(e.reload.root_account_id).to eq course.root_account_id
end
end
end

View File

@ -142,6 +142,29 @@ describe DataFixup::PopulateRootAccountIdOnModels do
expect(@override_student.reload.root_account_id).to eq @course.root_account_id
end
context 'with CalendarEvent' do
context 'when context is Course' do
it_behaves_like 'a datafixup that populates root_account_id' do
let(:record) { CalendarEvent.create!(context: @course) }
let(:reference_record) { @course }
end
end
context 'when context is Group' do
it_behaves_like 'a datafixup that populates root_account_id' do
let(:record) { CalendarEvent.create!(context: group_model(context: @course)) }
let(:reference_record) { @course }
end
end
context 'when context is CourseSection' do
it_behaves_like 'a datafixup that populates root_account_id' do
let(:record) { CalendarEvent.create!(context: CourseSection.create!(course: @course)) }
let(:reference_record) { @course }
end
end
end
context 'with ContextExternalTool' do
it_behaves_like 'a datafixup that populates root_account_id' do
let(:record) { external_tool_model(context: @course) }