FEATURE: Merge user associated accounts, favouring the target user upon conflict (#26645)

This commit is contained in:
Natalie Tay 2024-04-16 17:37:33 +08:00 committed by GitHub
parent bbe62d88d2
commit 1fea2bf1c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 71 additions and 0 deletions

View File

@ -18,6 +18,7 @@ class UserMerger
merge_user_visits
update_site_settings
merge_user_attributes
merge_user_associated_accounts
DiscourseEvent.trigger(:merging_users, @source_user, @target_user)
update_user_stats
@ -294,6 +295,29 @@ class UserMerger
SQL
end
def merge_user_associated_accounts
if @acting_user
::MessageBus.publish "/merge_user",
{
message:
I18n.t("admin.user.merge_user.merging_user_associated_accounts"),
},
user_ids: [@acting_user.id]
end
UserAssociatedAccount.where(user_id: @source_user.id).update_all(<<~SQL)
user_id = CASE
WHEN EXISTS (
SELECT 1
FROM user_associated_accounts AS conflicts
WHERE (conflicts.user_id = #{@target_user.id} AND conflicts.provider_name = user_associated_accounts.provider_name)
)
THEN NULL
ELSE #{@target_user.id}
END
SQL
end
def update_user_ids
if @acting_user
::MessageBus.publish "/merge_user",

View File

@ -2931,6 +2931,7 @@ en:
updating_site_settings: "Updating site settings…"
updating_user_stats: "Updating user stats…"
merging_user_attributes: "Merging user attributes…"
merging_user_associated_accounts: "Merging user associated accounts…"
updating_user_ids: "Updating user ids…"
deleting_source_user: "Deleting source user…"
user:

View File

@ -0,0 +1,8 @@
# frozen_string_literal: true
Fabricator(:user_associated_account) do
provider_name "meecrosof"
provider_uid { sequence(:key) { |i| "#{i + 1}" } }
user
info { |attrs| { name: attrs[:user].username, email: attrs[:user].email } }
end

View File

@ -1084,6 +1084,44 @@ RSpec.describe UserMerger do
end
end
context "with user associated accounts (UAAs)" do
context "when only merging account has UAAs" do
it "transfers the source user UAA to the target" do
source_uaa = Fabricate(:user_associated_account, user: source_user)
merge_users!
expect(source_uaa.reload.user).to eq(target_user)
end
end
context "when both accounts have UAAs" do
context "when both accounts' UAAs have different provider_names" do
it "transfers the source user UAA to the target and keeps both" do
source_uaa = Fabricate(:user_associated_account, user: source_user, provider_name: "x")
target_uaa = Fabricate(:user_associated_account, user: target_user, provider_name: "y")
merge_users!
expect(target_uaa.reload.user).to eq(target_user)
expect(source_uaa.reload.user).to eq(target_user)
end
end
context "when both accounts' UAAs have same provider_names" do
it "keeps only the target UAA" do
source_uaa = Fabricate(:user_associated_account, user: source_user, provider_name: "x")
target_uaa = Fabricate(:user_associated_account, user: target_user, provider_name: "x")
merge_users!
expect(target_uaa.reload.user).to eq(target_user)
expect(source_uaa.reload.user).to eq(nil)
end
end
end
end
it "updates users" do
walter.update!(approved_by: source_user)
upload = Fabricate(:upload)