create roll back data for users

closes CORE-1395

test plan
 - import users
 - workflow_state changes should be logged

Change-Id: I1466d6ec9f0ac3d38f74fd1b7a68dd0fa1c77a2f
Reviewed-on: https://gerrit.instructure.com/150103
Tested-by: Jenkins
Reviewed-by: James Williams  <jamesw@instructure.com>
QA-Review: Rohan Cheeniyil <rcheeniyil@instructure.com>
Product-Review: Rob Orton <rob@instructure.com>
This commit is contained in:
Rob Orton 2018-05-12 05:50:01 -06:00
parent 9cfe51d449
commit 91a4234bd3
2 changed files with 80 additions and 22 deletions

View File

@ -20,7 +20,7 @@ module SIS
class UserImporter < BaseImporter
def process(messages)
start = Time.now
start = Time.zone.now
importer = Work.new(@batch, @root_account, @logger, messages)
User.skip_updating_account_associations do
User.process_as_sis(@sis_options) do
@ -34,18 +34,17 @@ module SIS
end
User.update_account_associations(importer.users_to_add_account_associations, :incremental => true, :precalculated_associations => {@root_account.id => 0})
User.update_account_associations(importer.users_to_update_account_associations)
importer.pseudos_to_set_sis_batch_ids.in_groups_of(1000, false) do |batch|
Pseudonym.where(:id => batch).update_all(:sis_batch_id => @batch.id)
end if @batch
@logger.debug("Users took #{Time.now - start} seconds")
return importer.success_count
importer.pseudos_to_set_sis_batch_ids.in_groups_of(1000, false) {|ids| Pseudonym.where(id: ids).update_all(sis_batch_id: @batch.id)} if @batch
SisBatchRollBackData.bulk_insert_roll_back_data(importer.roll_back_data) if @batch.using_parallel_importers?
@logger.debug("Users took #{Time.zone.now - start} seconds")
importer.success_count
end
private
class Work
attr_accessor :success_count, :users_to_set_sis_batch_ids,
:pseudos_to_set_sis_batch_ids, :users_to_add_account_associations,
:users_to_update_account_associations
:users_to_update_account_associations, :roll_back_data
def initialize(batch, root_account, logger, messages)
@batch = batch
@ -55,6 +54,7 @@ module SIS
@messages = messages
@success_count = 0
@roll_back_data = []
@users_to_set_sis_batch_ids = []
@pseudos_to_set_sis_batch_ids = []
@users_to_add_account_associations = []
@ -234,7 +234,10 @@ module SIS
User.transaction(:requires_new => true) do
if user.changed?
user_touched = true
if !user.save && user.errors.size > 0
if user.save
u_data = SisBatchRollBackData.build_data(sis_batch: @batch, context: user)
@roll_back_data << u_data if u_data
elsif user.errors.size > 0
message = generate_user_warning(user.errors.first.join(" "), user_row.user_id, user_row.login_id)
raise ImportError, message
end
@ -244,7 +247,10 @@ module SIS
pseudo.user_id = user.id
if pseudo.changed?
pseudo.sis_batch_id = @batch.id if @batch
if !pseudo.save_without_broadcasting && pseudo.errors.size > 0
if pseudo.save_without_broadcasting
p_data = SisBatchRollBackData.build_data(sis_batch: @batch, context: pseudo)
@roll_back_data << p_data if p_data
elsif pseudo.errors.size > 0
message = generate_user_warning(pseudo.errors.first.join(" "), user_row.user_id, user_row.login_id)
raise ImportError, message
end
@ -300,6 +306,8 @@ module SIS
if cc.changed?
if cc.valid?
cc.save_without_broadcasting
cc_data = SisBatchRollBackData.build_data(sis_batch: @batch, context: cc)
@roll_back_data << cc_data if cc_data
else
msg = "An email did not pass validation "
msg += "(" + "#{user_row.email}, error: "
@ -349,29 +357,49 @@ module SIS
elsif @batch && pseudo.sis_batch_id != @batch.id
@pseudos_to_set_sis_batch_ids << pseudo.id
end
@success_count += 1
maybe_write_roll_back_data
@success_count += 1
end
end
end
def remove_enrollments_if_last_login(user, user_id)
return false if @root_account.pseudonyms.active.where(user_id: user).
where("sis_user_id != ? OR sis_user_id IS NULL", user_id).exists?
enrollment_ids = @root_account.enrollments.active.where(user_id: user).
where.not(workflow_state: 'deleted').pluck(:id)
if enrollment_ids.any?
Enrollment.where(id: enrollment_ids).update_all(updated_at: Time.now.utc, workflow_state: 'deleted')
EnrollmentState.where(enrollment_id: enrollment_ids).update_all(state: 'deleted', state_is_current: true)
def maybe_write_roll_back_data
if @roll_back_data.count > 1000
SisBatchRollBackData.bulk_insert_roll_back_data(@roll_back_data)
@roll_back_data = []
end
end
d = enrollment_ids.count
def remove_enrollments_if_last_login(user, user_id)
return false if @root_account.pseudonyms.active.where(user_id: user).where("sis_user_id != ? OR sis_user_id IS NULL", user_id).exists?
enrollments = @root_account.enrollments.active.where(user_id: user).
where.not(workflow_state: 'deleted').select(:id, :workflow_state).to_a
if enrollments.any?
Enrollment.where(id: enrollments.map(&:id)).update_all(updated_at: Time.now.utc, workflow_state: 'deleted')
EnrollmentState.where(enrollment_id: enrollments.map(&:id)).update_all(state: 'deleted', state_is_current: true)
e_data = SisBatchRollBackData.build_dependent_data(sis_batch: @batch, contexts: enrollments, updated_state: 'deleted')
@roll_back_data.push(*e_data) if e_data
end
gms = @root_account.all_group_memberships.active.where(user_id: user).select(:id, :workflow_state).to_a
gm_data = SisBatchRollBackData.build_dependent_data(sis_batch: @batch, contexts: gms, updated_state: 'deleted')
@roll_back_data.push(*gm_data) if gm_data
admins = user.account_users.active.shard(@root_account).where(account_id: @root_account.all_accounts).select(:id, :workflow_state).to_a
a_data = SisBatchRollBackData.build_dependent_data(sis_batch: @batch, contexts: admins, updated_state: 'deleted')
@roll_back_data.push(*a_data) if a_data
root_admins = user.account_users.active.shard(@root_account).where(account_id: @root_account).select(:id, :workflow_state).to_a
r_data = SisBatchRollBackData.build_dependent_data(sis_batch: @batch, contexts: root_admins, updated_state: 'deleted')
@roll_back_data.push(*r_data) if r_data
d = enrollments.count
d += @root_account.all_group_memberships.active.where(user_id: user).
update_all(updated_at: Time.now.utc, workflow_state: 'deleted')
d += user.account_users.shard(@root_account).where(account_id: @root_account.all_accounts).
d += user.account_users.active.shard(@root_account).where(account_id: @root_account.all_accounts).
update_all(updated_at: Time.now.utc, workflow_state: 'deleted')
d += user.account_users.shard(@root_account).where(account_id: @root_account).
d += user.account_users.active.shard(@root_account).where(account_id: @root_account).
update_all(updated_at: Time.now.utc, workflow_state: 'deleted')
if d > 0
should_update_account_associations = true

View File

@ -1221,6 +1221,36 @@ describe SIS::CSV::UserImporter do
expect(gm.workflow_state).to eq 'deleted'
end
it 'should create rollback data' do
@account.enable_feature!(:refactor_of_sis_imports)
batch1 = @account.sis_batches.create! { |sb| sb.data = {} }
process_csv_data_cleanly(
"course_id,short_name,long_name,account_id,term_id,status",
"test_1,TC 101,Test Course 101,,,active"
)
process_csv_data_cleanly(
"user_id,login_id,first_name,last_name,email,status",
"user_1,user1,User,Uno,user@example.com,active"
)
process_csv_data_cleanly(
"course_id,user_id,role,section_id,status,associated_user_id,start_date,end_date",
"test_1,user_1,student,,active,,,"
)
c = @account.courses.where(sis_source_id: "test_1").first
g = c.groups.create(name: 'group1')
u = Pseudonym.where(sis_user_id: 'user_1').first.user
gm = g.group_memberships.create(user: u, workflow_state: 'accepted')
expect(gm.workflow_state).to eq 'accepted'
process_csv_data_cleanly(
"user_id,login_id,first_name,last_name,email,status",
"user_1,user1,User,Uno,user@example.com,deleted",
batch: batch1
)
expect(batch1.roll_back_data.count).to eq 4
expect(batch1.roll_back_data.pluck(:context_type).sort).to eq ["CommunicationChannel", "Enrollment", "GroupMembership", "Pseudonym"]
end
it 'removes account memberships when a user is deleted' do
@badmin = user_with_managed_pseudonym(:name => 'bad admin', :account => @account, :sis_user_id => 'badmin')
tie_user_to_account(@badmin, :account => @account)