Created
May 13, 2024 14:24
-
-
Save bhavik2936/57997d916a20b66c3d511c637c9775e7 to your computer and use it in GitHub Desktop.
Demonstration of rake task for resetting the password_changed_at attribute with a random timestamp ranging from 60 to 90 days ago
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
namespace :cleanup do | |
desc 'Update users\' password_changed_at timestamp between 60 and 90 days old' | |
# Execute the rake task as cleanup:update_user_password_changed_at_timestamp | |
task update_user_password_changed_at_timestamp: :environment do | |
logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) | |
Client.each_tenant do |client| | |
logger.info "Queueing UpdateUserPasswordChangedAtTimestampJob for #{client.subdomain}" | |
UpdateUserPasswordChangedAtTimestampJob.perform_async(client.subdomain) | |
end | |
end | |
end |
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
require 'rails_helper' | |
describe 'cleanup rake tasks' do | |
context 'cleanup:update_user_password_changed_at_timestamp', rake: 'cleanup:update_user_password_changed_at_timestamp' do | |
let(:task) { Rake::Task['cleanup:update_user_password_changed_at_timestamp'] } | |
it 'should enqueue cleanup fix job for all clients' do | |
Sidekiq::Testing.fake! do | |
expect { | |
task.invoke | |
}.to change(UpdateUserPasswordChangedAtTimestampJob.jobs, :size).by(Client.count) | |
end | |
end | |
end | |
end |
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
module TaggedLogHelper | |
def tagged_logger | |
return @logger unless @logger.nil? | |
tags = Array.wrap(@logger_tags || self.class) | |
@logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)).tagged(*tags) | |
end | |
def log(message = nil, additional_tags = nil, level = Logger::INFO) | |
level = log_level_lookup(level) | |
tagged_logger.tagged(*additional_tags).log(level, message) | |
end | |
private | |
def log_level_lookup(level) | |
return level if level.is_a?(Integer) | |
levels = { | |
'debug' => Logger::DEBUG, | |
'info' => Logger::INFO, | |
'warn' => Logger::WARN, | |
'error' => Logger::ERROR, | |
'fatal' => Logger::FATAL | |
} | |
levels[level] || Logger::UNKNOWN | |
end | |
end |
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
class UpdateUserPasswordChangedAtTimestampJob | |
include TaggedLogHelper | |
include Sidekiq::Worker | |
sidekiq_options queue: 'bulk', retry: false | |
MIN_DAY_LIMIT = 60 | |
MAX_DAY_LIMIT = 90 | |
def perform(tenant) | |
Client.switch(tenant) do | |
log "Updating users' password_changed_at attribute for #{tenant}" | |
start = Time.now | |
update_password_changed_at_attribute | |
runtime = Time.now - start | |
log "Took #{runtime} seconds to update users' password_changed_at attribute for #{tenant}" | |
end | |
end | |
# Fetch number of users having password_changed_at attribute older than 90 days | |
def self.users_with_expired_password_attribute | |
User.where(password_changed_at: ...(DateTime.now - MAX_DAY_LIMIT.days)) | |
.count | |
end | |
private | |
def update_password_changed_at_attribute | |
User.where(password_changed_at: ...(DateTime.now - MAX_DAY_LIMIT.days)) | |
.in_batches.update_all(sql_to_update_password_changed_at_attribute) | |
end | |
# SQL to update password_changed_at with random timestamp falls between 60 and 90 days | |
def sql_to_update_password_changed_at_attribute | |
<<-SQL.squish | |
password_changed_at = CURRENT_TIMESTAMP - | |
MAKE_INTERVAL(days => FLOOR((RANDOM() * (#{MAX_DAY_LIMIT} - #{MIN_DAY_LIMIT}) + #{MIN_DAY_LIMIT}))::int) | |
SQL | |
end | |
end |
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
require 'rails_helper' | |
describe UpdateUserPasswordChangedAtTimestampJob do | |
subject { described_class.perform_async(Client.current!.subdomain) } | |
context 'enqueuing the job' do | |
it 'sends job to sidekiq' do | |
Sidekiq::Testing.fake! do | |
expect { | |
subject | |
}.to change(described_class.jobs, :size).by(1) | |
end | |
end | |
end | |
context '#perform' do | |
context 'when password has been changed recently' do | |
let!(:user) { create :user, password_changed_at: DateTime.now - 10.days } | |
it 'should not update password_changed_at attribute' do | |
expect { subject }.not_to change(user, :password_changed_at) | |
end | |
end | |
context 'when password_changed_at attribute is over 90 days' do | |
let(:old_date) { Date.new(2019, 12, 25) } | |
let!(:users) { create_list :user, 10, password_changed_at: old_date } | |
it 'should change password_changed_at attribute for all users' do | |
expect { subject }.to change(described_class, :users_with_expired_password_count).from(10).to(0) | |
end | |
it 'should randomly assign password_changed_at attribute between range of 60 to 90 days old' do | |
subject | |
password_changed_at_attributes = User.distinct.pluck(:password_changed_at) | |
expect(password_changed_at_attributes.length).to be > 1 | |
expect(password_changed_at_attributes).to all(be_between(DateTime.now - described_class::MAX_DAY_LIMIT, DateTime.now - described_class::MIN_DAY_LIMIT)) | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment