Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for nil user.language_id Bug #969

Open
wants to merge 1 commit into
base: integration
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions db/migrate/20241212180047_set_language_id_not_null_on_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
class SetLanguageIdNotNullOnUsers < ActiveRecord::Migration[6.1]

def up
# Ensure default language exists before applying the changes
default_language_id = handle_default_language_id
# Disallow null values for user.language_id
# Also, set all currently null user.language_id values to the app's default language_id
change_column_null :users, :language_id, false, default_language_id
end

def down
# Allow null values for user.language_id
change_column_null :users, :language_id, true
end

private

# Return Language.default.id or raise an exception if not found, causing the migration to fail
def handle_default_language_id
default_language = Language.default

if default_language.nil?
Rails.logger.error 'Error: Language.default not found. Please ensure the default language is set.'
message = 'Migration aborted: No database changes were performed due to missing Language.default.'
raise StandardError, message
end
default_language.id
end
end
4 changes: 2 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2024_08_20_190548) do
ActiveRecord::Schema.define(version: 2024_12_12_180047) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -819,7 +819,7 @@
t.string "api_token"
t.integer "invited_by_id"
t.string "invited_by_type"
t.integer "language_id"
t.integer "language_id", null: false
t.string "recovery_email"
t.boolean "active", default: true
t.integer "department_id"
Expand Down
93 changes: 93 additions & 0 deletions lib/tasks/update_nil_user_language_ids_from_orgs.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# frozen_string_literal: true

namespace :data_migration do
desc 'Updates nil `user.language_id` values using `user.org.language_id`'
task update_nil_user_language_ids_from_orgs: :environment do
users_to_be_updated = query_nil_language_users_for_update
# Exit rake task if users_to_be_updated is empty
ensure_update_needed(users_to_be_updated)
# user_ids will be used to later output the updated results
user_ids = users_to_be_updated.pluck(:id)
# Perform the update
update_users(users_to_be_updated)
# Output a breakdown of the updated results
output_update_results(user_ids)
# Output if any users still have nil language_id after update
output_remaining_nil_language_users
puts 'Rake task completed.'
end

private

# Queries users that will be updated within this rake task
# The following nil_language users WILL NOT be updated within this rake task:
# - users where `user.org == nil` or `user.org.language_id == nil`
# (However, no such users should exist)
def query_nil_language_users_for_update
query_nil_language_users.joins(:org)
.where.not(orgs: { language_id: nil })
end

def query_nil_language_users
User.where(language_id: nil)
end

# Exits rake task if there is no update to perform
# Otherwise, outputs number of users to be updated
def ensure_update_needed(users_to_be_updated)
if users_to_be_updated.empty?
print_and_warn('No users to be updated. Exiting rake task.')
exit(0)
else
puts "Found #{users_to_be_updated.count} users to update."
end
end

# Performs a single bulk update on users_to_be_updated
def update_users(users_to_be_updated)
puts 'Proceeding to use `user.org.language_id` to update each nil `user.language_id`'
puts '------------------------------------------------------------------------'

# Subquery for update_all() action
# Fetches `user.org.language_id` for all users in `users_to_be_updated`
subquery = Org.where('orgs.id = users.org_id').select(:language_id).limit(1)

# Batch update `users_to_be_updated` with `user.org.language_id` values from subquery
users_to_be_updated.update_all("language_id = (#{subquery.to_sql})")

puts 'Update complete.'
end

# Outputs a breakdown of the updated language_id counts
def output_update_results(user_ids)
language_id_counts = User.where(id: user_ids)
.group(:language_id)
.count

puts 'Summary of language_id counts for updated users:'

language_id_counts.each do |language_id, count|
puts "language_id: #{language_id}, count: #{count}"
end

puts "Total count: #{language_id_counts.values.sum}"
end

def output_remaining_nil_language_users
# Query remaining users with nil language_id
# Now we are using query_nil_language_users (not query_nil_language_users_for_update)
remaining_users = query_nil_language_users
if remaining_users.exists?
# NOTE: Any remaining nil language_id users will be addressed in the SetLanguageIdNotNullOnUsers migration
print_and_warn("#{remaining_users.count} users still have a nil language_id.")
else
puts 'Successfully updated language_id for all users (i.e. All users now have a non-nil language_id value).'
end
end

# Message is printed to console and logs a warning
def print_and_warn(message)
puts message
Rails.logger.warn(message)
end
end