-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Creation of a rake task to delete duplicated half signup users
- Loading branch information
Showing
3 changed files
with
232 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# frozen_string_literal: true | ||
|
||
# rubocop:disable Rails/Output | ||
class ClearDuplicatedHalfSignupUsersJob < ApplicationJob | ||
include Decidim::Logging | ||
|
||
def perform | ||
duplicated_numbers = find_duplicated_phone_numbers | ||
return puts("No duplicated phone numbers found") if duplicated_numbers.empty? | ||
|
||
duplicated_numbers.each do |phone_info| | ||
phone_number, phone_country = phone_info | ||
|
||
users_with_phone = Decidim::User.where(phone_number: phone_number, phone_country: phone_country) | ||
quick_auth_users = users_with_phone.select { |user| user.email.include?("quick_auth") } | ||
other_users = users_with_phone.reject { |user| user.email.include?("quick_auth") } | ||
|
||
quick_auth_users.each { |user| soft_delete_user(user, "Duplicated account") } | ||
|
||
alert_about_duplicated_numbers(phone_number, other_users) if other_users.map(&:email).uniq.size > 1 | ||
end | ||
end | ||
|
||
private | ||
|
||
def find_duplicated_phone_numbers | ||
Decidim::User | ||
.where.not(phone_number: [nil, ""]) | ||
.where.not(phone_country: [nil, ""]) | ||
.group(:phone_number, :phone_country) | ||
.having("count(*) > 1") | ||
.pluck(:phone_number, :phone_country) | ||
end | ||
|
||
def soft_delete_user(user, reason) | ||
if user.email.include?("quick_auth") | ||
user.update(phone_number: nil, phone_country: nil) | ||
|
||
form = Decidim::DeleteAccountForm.from_params(delete_reason: reason) | ||
previous_email = user.email | ||
Decidim::DestroyAccount.call(user, form) do | ||
on(:ok) do | ||
log!("User #{user.id} (#{previous_email}) has been deleted", :info) | ||
puts("User #{user.id} (#{previous_email}) has been deleted") | ||
end | ||
on(:invalid) do | ||
log!("Failed to delete user #{user.id} (#{user.email}): #{form.errors.full_messages}", :warn) | ||
puts("Failed to delete user #{user.id} (#{user.email}): #{form.errors.full_messages}") | ||
end | ||
end | ||
else | ||
log!("Not a Quick Auth account, skipping deletion", :info) | ||
puts("Not a Quick Auth account, skipping deletion") | ||
end | ||
end | ||
|
||
def alert_about_duplicated_numbers(phone_number, users) | ||
obfuscated_number = obfuscate_phone_number(phone_number) | ||
emails = users.map(&:email) | ||
email_pairs = emails.each_slice(2).map { |pair| pair.join(" | ") } | ||
|
||
message = <<~MSG | ||
\nALERT: Duplicated Phone Number Detected! | ||
Phone Number: #{obfuscated_number} | ||
Users with this number: | ||
#{email_pairs.join("\n")} | ||
MSG | ||
|
||
log!(message, :warn) | ||
puts(message) | ||
end | ||
|
||
def obfuscate_phone_number(phone_number) | ||
return "No phone number" if phone_number.blank? | ||
|
||
visible_prefix = phone_number[0..1] | ||
visible_suffix = phone_number[-2..] | ||
obfuscated_middle = "*" * (phone_number.length - 4) | ||
|
||
visible_prefix + obfuscated_middle + visible_suffix | ||
end | ||
end | ||
# rubocop:enable Rails/Output |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# frozen_string_literal: true | ||
|
||
namespace :decidim do | ||
desc "Clear duplicated users with the same phone_numbers in the database" | ||
task clear_duplicated_users: :environment do | ||
ClearDuplicatedHalfSignupUsersJob.perform_now | ||
end | ||
end |
140 changes: 140 additions & 0 deletions
140
spec/jobs/clear_duplicated_half_signup_users_job_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
# frozen_string_literal: true | ||
|
||
require "spec_helper" | ||
|
||
module Decidim | ||
describe ClearDuplicatedHalfSignupUsersJob do | ||
let!(:user1) { create(:user, phone_number: "1234567890", phone_country: "US", email: "[email protected]") } | ||
let!(:user2) { create(:user, phone_number: "1234567890", phone_country: "US", email: "[email protected]") } | ||
let!(:user3) { create(:user, phone_number: "1234567890", phone_country: "US", email: "[email protected]") } | ||
let!(:user4) { create(:user, phone_number: "9876543210", phone_country: "US", email: "[email protected]") } | ||
|
||
before do | ||
allow_any_instance_of(Decidim::DestroyAccount).to receive(:call).and_return(true) | ||
end | ||
|
||
describe "#perform" do | ||
context "when no duplicated phone numbers are found" do | ||
it "prints that no duplicated phone numbers are found" do | ||
allow_any_instance_of(ClearDuplicatedHalfSignupUsersJob).to receive(:find_duplicated_phone_numbers).and_return([]) | ||
|
||
expect { ClearDuplicatedHalfSignupUsersJob.perform_now }.to output(/No duplicated phone numbers found/).to_stdout | ||
end | ||
end | ||
|
||
context "when duplicated phone numbers are found" do | ||
before do | ||
user1 | ||
user2 | ||
user3 | ||
end | ||
|
||
it "soft deletes quick_auth users" do | ||
expect_any_instance_of(ClearDuplicatedHalfSignupUsersJob).to receive(:soft_delete_user).with(user2, "Duplicated account") | ||
|
||
ClearDuplicatedHalfSignupUsersJob.perform_now | ||
end | ||
|
||
it "alerts about duplicated phone numbers for non-quick_auth users" do | ||
expect(Rails.logger).to receive(:warn).with(/ALERT: Duplicated Phone Number Detected/) | ||
|
||
ClearDuplicatedHalfSignupUsersJob.perform_now | ||
end | ||
|
||
it "does not soft delete non quick_auth users" do | ||
expect_any_instance_of(ClearDuplicatedHalfSignupUsersJob).not_to receive(:soft_delete_user).with(user1, "Duplicated account") | ||
|
||
ClearDuplicatedHalfSignupUsersJob.perform_now | ||
end | ||
|
||
it "does not soft delete users with different phone numbers" do | ||
expect_any_instance_of(ClearDuplicatedHalfSignupUsersJob).not_to receive(:soft_delete_user).with(user4, "Duplicated account") | ||
|
||
ClearDuplicatedHalfSignupUsersJob.perform_now | ||
end | ||
end | ||
end | ||
|
||
describe "#soft_delete_user" do | ||
let(:user) { create(:user, phone_number: "1234567890", phone_country: "US", email: "[email protected]") } | ||
|
||
it "updates the user to nullify the phone number and country" do | ||
expect(user).to receive(:update).with(phone_number: nil, phone_country: nil) | ||
|
||
subject.send(:soft_delete_user, user, "Duplicated account") | ||
end | ||
|
||
it "calls the Decidim::DestroyAccount service to delete the user" do | ||
allow_any_instance_of(Decidim::DestroyAccount).to receive(:call).and_return(true) | ||
|
||
subject.send(:soft_delete_user, user, "Duplicated account") | ||
end | ||
|
||
it "does not delete a non-quick_auth account" do | ||
user_with_diff_email = create(:user, phone_number: "1234567890", phone_country: "US", email: "[email protected]") | ||
|
||
expect_any_instance_of(Decidim::DestroyAccount).not_to receive(:call) | ||
expect(Rails.logger).to receive(:info).with(/Not a Quick Auth account, skipping deletion/) | ||
|
||
subject.send(:soft_delete_user, user_with_diff_email, "Duplicated account") | ||
end | ||
end | ||
|
||
describe "#alert_about_duplicated_numbers" do | ||
let(:users) { [user1, user2, user3] } | ||
|
||
it "obfuscates the phone number and logs an alert message" do | ||
obfuscated_number = "12****90" | ||
allow(subject).to receive(:obfuscate_phone_number).with("1234567890").and_return(obfuscated_number) | ||
expect(Rails.logger).to receive(:warn).with(/ALERT: Duplicated Phone Number Detected/) | ||
|
||
subject.send(:alert_about_duplicated_numbers, "1234567890", users) | ||
end | ||
|
||
it "logs the correct message with the obfuscated phone number" do | ||
obfuscated_number = "12****90" | ||
allow(subject).to receive(:obfuscate_phone_number).with("1234567890").and_return(obfuscated_number) | ||
|
||
allow(Rails.logger).to receive(:warn) { |message| | ||
expect(message).to include("Phone Number: #{obfuscated_number}") | ||
} | ||
|
||
subject.send(:alert_about_duplicated_numbers, "1234567890", users) | ||
end | ||
end | ||
|
||
describe "#obfuscate_phone_number" do | ||
it "returns an obfuscated phone number with visible prefix and suffix" do | ||
result = subject.send(:obfuscate_phone_number, "1234567890") | ||
|
||
expect(result).to eq("12******90") | ||
end | ||
|
||
it 'returns "No phone number" if the phone number is blank' do | ||
result = subject.send(:obfuscate_phone_number, "") | ||
|
||
expect(result).to eq("No phone number") | ||
end | ||
|
||
it 'returns "No phone number" when given nil' do | ||
result = subject.send(:obfuscate_phone_number, nil) | ||
|
||
expect(result).to eq("No phone number") | ||
end | ||
end | ||
|
||
describe "#find_duplicated_phone_numbers" do | ||
context "when there are duplicates" do | ||
it "finds duplicated phone numbers" do | ||
user1 | ||
user2 | ||
user3 | ||
|
||
result = subject.send(:find_duplicated_phone_numbers) | ||
|
||
expect(result).to include(%w(1234567890 US)) | ||
end | ||
end | ||
end | ||
end | ||
end |