AccountMigration: support chained migration case

This commit is contained in:
cmrd Senya 2019-04-26 20:29:26 +03:00
parent dd9ac758e8
commit 597d9e0275
2 changed files with 122 additions and 36 deletions

View file

@ -81,6 +81,10 @@ class AccountMigration < ApplicationRecord
new_person.owner
end
def newest_user
newest_person.owner
end
def lock_old_user!
old_user&.lock_access!
end
@ -106,7 +110,7 @@ class AccountMigration < ApplicationRecord
# We need to resend contacts of users of our pod for the remote new person so that the remote pod received this
# contact information from the authoritative source.
def dispatch_contacts
new_person.contacts.sharing.each do |contact|
newest_person.contacts.sharing.each do |contact|
Diaspora::Federation::Dispatcher.defer_dispatch(contact.user, contact)
end
end
@ -179,7 +183,7 @@ class AccountMigration < ApplicationRecord
def duplicate_person_contacts
Contact
.joins("INNER JOIN contacts as c2 ON (contacts.user_id = c2.user_id AND contacts.person_id=#{old_person.id} AND"\
" c2.person_id=#{new_person.id})")
" c2.person_id=#{newest_person.id})")
end
def duplicate_person_likes
@ -187,7 +191,7 @@ class AccountMigration < ApplicationRecord
.joins("INNER JOIN likes as l2 ON (likes.target_id = l2.target_id "\
"AND likes.target_type = l2.target_type "\
"AND likes.author_id=#{old_person.id} AND"\
" l2.author_id=#{new_person.id})")
" l2.author_id=#{newest_person.id})")
end
def duplicate_person_participations
@ -195,41 +199,41 @@ class AccountMigration < ApplicationRecord
.joins("INNER JOIN participations as p2 ON (participations.target_id = p2.target_id "\
"AND participations.target_type = p2.target_type "\
"AND participations.author_id=#{old_person.id} AND"\
" p2.author_id=#{new_person.id})")
" p2.author_id=#{newest_person.id})")
end
def duplicate_person_poll_participations
PollParticipation
.joins("INNER JOIN poll_participations as p2 ON (poll_participations.poll_id = p2.poll_id "\
"AND poll_participations.author_id=#{old_person.id} AND"\
" p2.author_id=#{new_person.id})")
" p2.author_id=#{newest_person.id})")
end
def eliminate_user_duplicates
Aspect
.joins("INNER JOIN aspects as a2 ON (aspects.name = a2.name AND aspects.user_id=#{old_user.id}
AND a2.user_id=#{new_user.id})")
AND a2.user_id=#{newest_user.id})")
.destroy_all
Contact
.joins("INNER JOIN contacts as c2 ON (contacts.person_id = c2.person_id AND contacts.user_id=#{old_user.id} AND"\
" c2.user_id=#{new_user.id})")
" c2.user_id=#{newest_user.id})")
.destroy_all
TagFollowing
.joins("INNER JOIN tag_followings as t2 ON (tag_followings.tag_id = t2.tag_id AND"\
" tag_followings.user_id=#{old_user.id} AND t2.user_id=#{new_user.id})")
" tag_followings.user_id=#{old_user.id} AND t2.user_id=#{newest_user.id})")
.destroy_all
end
def update_person_references
logger.debug "Updating references from person id=#{old_person.id} to person id=#{new_person.id}"
logger.debug "Updating references from person id=#{old_person.id} to person id=#{newest_person.id}"
eliminate_person_duplicates
update_references(person_references, old_person, new_person.id)
update_references(person_references, old_person, newest_person.id)
end
def update_user_references
logger.debug "Updating references from user id=#{old_user.id} to user id=#{new_user.id}"
logger.debug "Updating references from user id=#{old_user.id} to user id=#{newest_user.id}"
eliminate_user_duplicates
update_references(user_references, old_user, new_user.id)
update_references(user_references, old_user, newest_user.id)
end
def update_references(references, object, new_id)

View file

@ -149,6 +149,36 @@ shared_examples_for "migration scenarios initiated locally" do
end
end
shared_examples_for "migration scenarios with local user rename" do
it "updates user references" do
invited_user = FactoryGirl.create(:user, invited_by: old_user)
aspect = FactoryGirl.create(:aspect, user: old_user, name: r_str)
contact = FactoryGirl.create(:contact, user: old_user)
service = FactoryGirl.create(:service, user: old_user)
pref = UserPreference.create!(user: old_user, email_type: "also_commented")
tag_following = FactoryGirl.create(:tag_following, user: old_user)
block = FactoryGirl.create(:block, user: old_user)
notification = FactoryGirl.create(:notification, recipient: old_user)
report = FactoryGirl.create(:report, user: old_user)
authorization = FactoryGirl.create(:auth_with_read, user: old_user)
share_visibility = FactoryGirl.create(:share_visibility, user: old_user)
run_migration
expect(invited_user.reload.invited_by).to eq(new_user)
expect(aspect.reload.user).to eq(new_user)
expect(contact.reload.user).to eq(new_user)
expect(service.reload.user).to eq(new_user)
expect(pref.reload.user).to eq(new_user)
expect(tag_following.reload.user).to eq(new_user)
expect(block.reload.user).to eq(new_user)
expect(notification.reload.recipient).to eq(new_user)
expect(report.reload.user).to eq(new_user)
expect(authorization.reload.user).to eq(new_user)
expect(share_visibility.reload.user).to eq(new_user)
end
end
describe "account migration" do
# this is the case when we receive account migration message from the federation
context "remotely initiated" do
@ -173,6 +203,22 @@ describe "account migration" do
include_examples "every migration scenario"
include_examples "migration scenarios initiated remotely"
context "when new person has been migrated before" do
let(:intermidiate_person) { create_remote_user("remote-d.net").person }
before do
AccountMigration.create!(old_person: intermidiate_person, new_person: new_person).perform!
end
def run_migration
AccountMigration.create!(old_person: old_person, new_person: intermidiate_person).perform!
end
include_examples "every migration scenario"
include_examples "migration scenarios initiated remotely"
end
end
# this is the case when we're a pod, which was left by a person in favor of remote one
@ -200,6 +246,24 @@ describe "account migration" do
user.reload
end
end
context "when new person has been migrated before" do
let(:intermidiate_person) { create_remote_user("remote-d.net").person }
before do
AccountMigration.create!(old_person: intermidiate_person, new_person: new_person).perform!
end
def run_migration
AccountMigration.create!(old_person: old_user.person, new_person: intermidiate_person).perform!
end
include_examples "every migration scenario"
include_examples "migration scenarios initiated remotely"
it_behaves_like "migration scenarios with local old user"
end
end
end
@ -228,6 +292,24 @@ describe "account migration" do
it_behaves_like "migration scenarios initiated locally" do
let!(:remote_contact) { create_remote_contact(new_user, "remote-friend.org") }
end
context "when new person has been migrated before" do
let(:intermidiate_person) { FactoryGirl.create(:user).person }
before do
AccountMigration.create!(old_person: intermidiate_person, new_person: new_person).perform!
end
def run_migration
AccountMigration.create!(
old_person: old_person,
new_person: intermidiate_person,
old_private_key: old_user.serialized_private_key
).perform!
end
include_examples "every migration scenario"
end
end
# this is the case when a user changes diaspora id but stays on the same pod
@ -254,32 +336,32 @@ describe "account migration" do
expect(old_user.reload).to be_a_clear_account
end
it "updates user references" do
invited_user = FactoryGirl.create(:user, invited_by: old_user)
aspect = FactoryGirl.create(:aspect, user: old_user, name: r_str)
contact = FactoryGirl.create(:contact, user: old_user)
service = FactoryGirl.create(:service, user: old_user)
pref = UserPreference.create!(user: old_user, email_type: "also_commented")
tag_following = FactoryGirl.create(:tag_following, user: old_user)
block = FactoryGirl.create(:block, user: old_user)
notification = FactoryGirl.create(:notification, recipient: old_user)
report = FactoryGirl.create(:report, user: old_user)
authorization = FactoryGirl.create(:auth_with_read, user: old_user)
share_visibility = FactoryGirl.create(:share_visibility, user: old_user)
include_examples "migration scenarios with local user rename"
context "when new user has been migrated before" do
let(:intermidiate_person) { FactoryGirl.create(:user).person }
before do
AccountMigration.create!(old_person: intermidiate_person, new_person: new_person).perform!
end
def run_migration
AccountMigration.create!(
old_person: old_person,
new_person: intermidiate_person
).perform!
end
include_examples "every migration scenario"
it_behaves_like "migration scenarios with local old user"
it "clears the old user account" do
run_migration
expect(old_user.reload).to be_a_clear_account
end
expect(invited_user.reload.invited_by).to eq(new_user)
expect(aspect.reload.user).to eq(new_user)
expect(contact.reload.user).to eq(new_user)
expect(service.reload.user).to eq(new_user)
expect(pref.reload.user).to eq(new_user)
expect(tag_following.reload.user).to eq(new_user)
expect(block.reload.user).to eq(new_user)
expect(notification.reload.recipient).to eq(new_user)
expect(report.reload.user).to eq(new_user)
expect(authorization.reload.user).to eq(new_user)
expect(share_visibility.reload.user).to eq(new_user)
include_examples "migration scenarios with local user rename"
end
end
end