Data Migration Script for users_settings and users_notification_settings (#13390)
* schema file undelete description * feat: v1 of the script * Flesh out remaining enums under their categories * complete UsersSettings data update script * complete DUS for relevant attributes in users and profiles tables * complete DUS for users_notification_settings * alphabetize user_settings sql file * safeguard against null values for "null: false" settings * Set up actual UsersSettings DUS and specs files * fix broken DUS script * complete specs for UsersSetting DUS * Address QA of specs * complete specs for users_notification_settings DUS * fix the typos (thanks Julianna!) * add missing attribute "email_membership_newsletter" Co-authored-by: Arit Amana <msarit@gmail.com>
This commit is contained in:
parent
a4c06d3207
commit
bf02be9df8
9 changed files with 370 additions and 2 deletions
|
|
@ -0,0 +1,7 @@
|
|||
class RemoveUserIdIndexFromUsersSettings < ActiveRecord::Migration[6.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
remove_index :users_settings, column: :user_id, algorithm: :concurrently
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
class AddUniqueUserIdIndexToUsersSettings < ActiveRecord::Migration[6.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
add_index :users_settings, :user_id, unique: true, algorithm: :concurrently
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
class RemoveUserIdIndexFromUsersNotificationSettings < ActiveRecord::Migration[6.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
remove_index :users_notification_settings, column: :user_id, algorithm: :concurrently
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
class AddUniqueUserIdIndexToUsersNotificationSettings < ActiveRecord::Migration[6.1]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def change
|
||||
add_index :users_notification_settings, :user_id, unique: true, algorithm: :concurrently
|
||||
end
|
||||
end
|
||||
|
|
@ -1445,7 +1445,7 @@ ActiveRecord::Schema.define(version: 2021_05_12_025422) do
|
|||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.bigint "user_id", null: false
|
||||
t.boolean "welcome_notifications", default: true, null: false
|
||||
t.index ["user_id"], name: "index_users_notification_settings_on_user_id"
|
||||
t.index ["user_id"], name: "index_users_notification_settings_on_user_id", unique: true
|
||||
end
|
||||
|
||||
create_table "users_roles", id: false, force: :cascade do |t|
|
||||
|
|
@ -1474,7 +1474,7 @@ ActiveRecord::Schema.define(version: 2021_05_12_025422) do
|
|||
t.boolean "permit_adjacent_sponsors", default: true
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.bigint "user_id", null: false
|
||||
t.index ["user_id"], name: "index_users_settings_on_user_id"
|
||||
t.index ["user_id"], name: "index_users_settings_on_user_id", unique: true
|
||||
end
|
||||
|
||||
create_table "users_suspended_usernames", primary_key: "username_hash", id: :string, force: :cascade do |t|
|
||||
|
|
|
|||
|
|
@ -0,0 +1,86 @@
|
|||
module DataUpdateScripts
|
||||
class MigrateRelevantFieldsFromUsersToUsersSettings
|
||||
def run
|
||||
# rubocop:disable Metrics/BlockLength(RuboCop)
|
||||
ActiveRecord::Base.transaction do
|
||||
ActiveRecord::Base.connection.execute(
|
||||
<<~SQL.squish,
|
||||
WITH settings_data AS (
|
||||
SELECT
|
||||
data ->> 'brand_color1' AS brand_color1,
|
||||
data ->> 'brand_color2' AS brand_color2,
|
||||
CASE WHEN config_font='default' THEN 0
|
||||
WHEN config_font='comic_sans' THEN 1
|
||||
WHEN config_font='monospace' THEN 2
|
||||
WHEN config_font='open_dyslexic' THEN 3
|
||||
WHEN config_font='sans_serif' THEN 4
|
||||
WHEN config_font='serif' THEN 5
|
||||
ELSE 0
|
||||
END
|
||||
config_font,
|
||||
CASE WHEN config_navbar='default_navbar' THEN 0
|
||||
WHEN config_navbar='static_navbar' THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
config_navbar,
|
||||
CASE WHEN config_theme='default_theme' THEN 0
|
||||
WHEN config_theme='minimal_light_theme' THEN 1
|
||||
WHEN config_theme='night_theme' THEN 2
|
||||
WHEN config_theme='pink_theme' THEN 3
|
||||
WHEN config_theme='ten_x_hacker_theme' THEN 4
|
||||
ELSE 0
|
||||
END
|
||||
config_theme,
|
||||
COALESCE(display_announcements, true),
|
||||
COALESCE((data ->> 'display_email_on_profile')::boolean, false) as display_email_on_profile,
|
||||
COALESCE(display_sponsors, true),
|
||||
CASE WHEN editor_version='v2' THEN 0
|
||||
WHEN editor_version='v1' THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
editor_version,
|
||||
experience_level,
|
||||
COALESCE(feed_mark_canonical, false),
|
||||
COALESCE(feed_referential_link, true),
|
||||
feed_url,
|
||||
inbox_guidelines,
|
||||
CASE WHEN inbox_type='private' THEN 0
|
||||
WHEN inbox_type='open' THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
inbox_type,
|
||||
COALESCE(permit_adjacent_sponsors, true),
|
||||
users.id AS user_id,
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM users
|
||||
JOIN profiles
|
||||
ON profiles.user_id = users.id
|
||||
)
|
||||
INSERT INTO users_settings (brand_color1, brand_color2, config_font, config_navbar, config_theme, display_announcements, display_email_on_profile, display_sponsors, editor_version, experience_level, feed_mark_canonical, feed_referential_link, feed_url, inbox_guidelines, inbox_type, permit_adjacent_sponsors, user_id, created_at, updated_at)
|
||||
SELECT * FROM settings_data
|
||||
ON CONFLICT (user_id) DO UPDATE
|
||||
SET brand_color1 = EXCLUDED.brand_color1,
|
||||
brand_color2 = EXCLUDED.brand_color2,
|
||||
config_font = EXCLUDED.config_font,
|
||||
config_navbar = EXCLUDED.config_navbar,
|
||||
config_theme = EXCLUDED.config_theme,
|
||||
display_announcements = EXCLUDED.display_announcements,
|
||||
display_email_on_profile = EXCLUDED.display_email_on_profile,
|
||||
display_sponsors = EXCLUDED.display_sponsors,
|
||||
editor_version = EXCLUDED.editor_version,
|
||||
experience_level = EXCLUDED.experience_level,
|
||||
feed_mark_canonical = EXCLUDED.feed_mark_canonical,
|
||||
feed_referential_link = EXCLUDED.feed_referential_link,
|
||||
feed_url = EXCLUDED.feed_url,
|
||||
inbox_guidelines = EXCLUDED.inbox_guidelines,
|
||||
inbox_type = EXCLUDED.inbox_type,
|
||||
permit_adjacent_sponsors = EXCLUDED.permit_adjacent_sponsors,
|
||||
updated_at = NOW();
|
||||
SQL
|
||||
)
|
||||
end
|
||||
# rubocop:enable Metrics/BlockLength(RuboCop)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
module DataUpdateScripts
|
||||
class MigrateRelevantFieldsFromUsersToUsersNotificationSettings
|
||||
def run
|
||||
# rubocop:disable Metrics/BlockLength(RuboCop)
|
||||
ActiveRecord::Base.transaction do
|
||||
ActiveRecord::Base.connection.execute(
|
||||
<<~SQL.squish,
|
||||
WITH notification_settings_data AS (
|
||||
SELECT
|
||||
users.id AS user_id,
|
||||
COALESCE(email_badge_notifications, true),
|
||||
COALESCE(email_comment_notifications, true),
|
||||
COALESCE(email_community_mod_newsletter, false),
|
||||
COALESCE(email_connect_messages, true),
|
||||
COALESCE(email_digest_periodic, false),
|
||||
COALESCE(email_follower_notifications, true),
|
||||
COALESCE(email_membership_newsletter, false),
|
||||
COALESCE(email_mention_notifications, true),
|
||||
COALESCE(email_newsletter, false),
|
||||
COALESCE(email_tag_mod_newsletter, false),
|
||||
COALESCE(email_unread_notifications, true),
|
||||
COALESCE(mobile_comment_notifications, true),
|
||||
COALESCE(mod_roundrobin_notifications, true),
|
||||
COALESCE(reaction_notifications, true),
|
||||
COALESCE(welcome_notifications, true),
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM users
|
||||
)
|
||||
INSERT INTO users_notification_settings (user_id, email_badge_notifications, email_comment_notifications, email_community_mod_newsletter, email_connect_messages, email_digest_periodic, email_follower_notifications, email_membership_newsletter, email_mention_notifications, email_newsletter, email_tag_mod_newsletter, email_unread_notifications, mobile_comment_notifications, mod_roundrobin_notifications, reaction_notifications, welcome_notifications, created_at, updated_at)
|
||||
SELECT * FROM notification_settings_data
|
||||
ON CONFLICT (user_id) DO UPDATE
|
||||
SET email_badge_notifications = EXCLUDED.email_badge_notifications,
|
||||
email_comment_notifications = EXCLUDED.email_comment_notifications,
|
||||
email_community_mod_newsletter = EXCLUDED.email_community_mod_newsletter,
|
||||
email_connect_messages = EXCLUDED.email_connect_messages,
|
||||
email_digest_periodic = EXCLUDED.email_digest_periodic,
|
||||
email_follower_notifications = EXCLUDED.email_follower_notifications,
|
||||
email_membership_newsletter = EXCLUDED.email_membership_newsletter,
|
||||
email_mention_notifications = EXCLUDED.email_mention_notifications,
|
||||
email_newsletter = EXCLUDED.email_newsletter,
|
||||
email_tag_mod_newsletter = EXCLUDED.email_tag_mod_newsletter,
|
||||
email_unread_notifications = EXCLUDED.email_unread_notifications,
|
||||
mobile_comment_notifications = EXCLUDED.mobile_comment_notifications,
|
||||
mod_roundrobin_notifications = EXCLUDED.mod_roundrobin_notifications,
|
||||
reaction_notifications = EXCLUDED.reaction_notifications,
|
||||
welcome_notifications = EXCLUDED.welcome_notifications,
|
||||
updated_at = NOW();
|
||||
SQL
|
||||
)
|
||||
end
|
||||
# rubocop:enable Metrics/BlockLength(RuboCop)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
require "rails_helper"
|
||||
require Rails.root.join(
|
||||
"lib/data_update_scripts/20210503174302_migrate_relevant_fields_from_users_to_users_notification_settings.rb",
|
||||
)
|
||||
|
||||
describe DataUpdateScripts::MigrateRelevantFieldsFromUsersToUsersNotificationSettings do
|
||||
let(:users_notification_setting) { Users::NotificationSetting.last }
|
||||
|
||||
before do
|
||||
Users::NotificationSetting.destroy_all
|
||||
User.destroy_all
|
||||
end
|
||||
|
||||
context "when migrating data" do
|
||||
it "sets the expected number of records" do
|
||||
create_list(:user, 3)
|
||||
|
||||
expect do
|
||||
described_class.new.run
|
||||
end.to change(Users::NotificationSetting, :count).from(0).to(3)
|
||||
end
|
||||
|
||||
it "sets the correct User records" do
|
||||
create(:user, welcome_notifications: true)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_notification_setting.welcome_notifications).to be(true)
|
||||
end
|
||||
|
||||
it "sets the correct types" do
|
||||
create(:user)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_notification_setting.reaction_notifications).to be_in([true, false])
|
||||
end
|
||||
|
||||
it "sets a fallback value for values that are null" do
|
||||
create(:user, email_newsletter: nil)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_notification_setting.email_newsletter).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
it "assigns updated_at and created_at timestamps that are more current than the original values" do
|
||||
user = create(:user, created_at: 1.minute.ago, updated_at: 1.minute.ago)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_notification_setting.created_at).to be > user.created_at
|
||||
expect(users_notification_setting.updated_at).to be > user.updated_at
|
||||
end
|
||||
|
||||
context "when the user id exists in both the users_notification_settings and users tables" do
|
||||
it "replaces the users_notification_settings values with values from the users table" do
|
||||
user = create(:user, email_newsletter: true)
|
||||
user_id = user.id
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_notification_setting.user_id).to eq(user_id)
|
||||
expect(users_notification_setting.email_newsletter).to be(true)
|
||||
|
||||
user.update_columns(email_newsletter: false)
|
||||
|
||||
described_class.new.run
|
||||
users_notification_setting.reload
|
||||
|
||||
expect(users_notification_setting.user_id).to eq(user_id)
|
||||
expect(users_notification_setting.email_newsletter).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,123 @@
|
|||
require "rails_helper"
|
||||
require Rails.root.join(
|
||||
"lib/data_update_scripts/20210423155327_migrate_relevant_fields_from_users_to_users_settings.rb",
|
||||
)
|
||||
|
||||
describe DataUpdateScripts::MigrateRelevantFieldsFromUsersToUsersSettings do
|
||||
let(:users_setting) { Users::Setting.last }
|
||||
|
||||
before do
|
||||
Users::Setting.destroy_all
|
||||
Profile.destroy_all
|
||||
User.destroy_all
|
||||
end
|
||||
|
||||
context "when migrating data" do
|
||||
it "sets the expected number of records" do
|
||||
create_list(:user, 3)
|
||||
|
||||
expect do
|
||||
described_class.new.run
|
||||
end.to change(Users::Setting, :count).from(0).to(3)
|
||||
end
|
||||
|
||||
it "sets the correct User records" do
|
||||
create(:user, config_font: "sans_serif")
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.config_font).to eq("sans_serif")
|
||||
end
|
||||
|
||||
it "sets the correct Profile records" do
|
||||
profile = create(
|
||||
:profile,
|
||||
:with_DEV_info,
|
||||
user: create(:user, :without_profile),
|
||||
display_email_on_profile: true,
|
||||
)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.display_email_on_profile).to eq(profile.display_email_on_profile)
|
||||
end
|
||||
|
||||
it "sets the correct types" do
|
||||
create(:user)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.permit_adjacent_sponsors).to be_in([true, false])
|
||||
end
|
||||
|
||||
it "casts data (display_email_on_profile) to the correct types (boolean)" do
|
||||
create(
|
||||
:profile,
|
||||
:with_DEV_info,
|
||||
user: create(:user, :without_profile),
|
||||
display_email_on_profile: true,
|
||||
)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.display_email_on_profile).to be_in([true, false])
|
||||
end
|
||||
|
||||
it "sets a fallback value for values that are null" do
|
||||
create(:user, display_announcements: nil)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.display_announcements).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context "when migrating enum settings" do
|
||||
it "assigns the correct mapping for enum settings" do
|
||||
create(:user, config_font: "monospace")
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.monospace?).to be(true)
|
||||
end
|
||||
|
||||
it "assigns the fallback value when the value passed does not have an enum defined" do
|
||||
user = create(:user)
|
||||
user.update_columns(config_font: "fake_font")
|
||||
user.reload
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.default?).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
it "assigns updated_at and created_at timestamps that are more current than the original values" do
|
||||
user = create(:user, created_at: 1.minute.ago, updated_at: 1.minute.ago)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.created_at).to be > user.created_at
|
||||
expect(users_setting.updated_at).to be > user.updated_at
|
||||
end
|
||||
|
||||
context "when the user id exists in both the users_settings and users tables" do
|
||||
it "replaces the users_settings values with values from the users table" do
|
||||
user = create(:user, display_announcements: true)
|
||||
user_id = user.id
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.user_id).to eq(user_id)
|
||||
expect(users_setting.display_announcements).to be(true)
|
||||
|
||||
user.update_columns(display_announcements: false)
|
||||
|
||||
described_class.new.run
|
||||
users_setting.reload
|
||||
|
||||
expect(users_setting.user_id).to eq(user_id)
|
||||
expect(users_setting.display_announcements).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue