From 4c8bc34475d0c9dc22d790f5ef3b05570e97048f Mon Sep 17 00:00:00 2001 From: Krzysztof Kotlarek Date: Tue, 19 Dec 2023 10:52:28 +1100 Subject: [PATCH] DEV: Custom generator for move setting from tl to groups (#24912) Ability to automatically generate migration when site setting is changed from trust level to groups. Example usage: rails generate site_setting_move_to_groups_migration min_trust_to_create_topic create_topic_allowed_groups --- .../USAGE | 8 ++ ...ting_move_to_groups_migration_generator.rb | 111 ++++++++++++++++ ...site_setting_rename_migration_generator.rb | 8 +- ...ove_to_groups_migrations_generator_spec.rb | 125 ++++++++++++++++++ 4 files changed, 247 insertions(+), 5 deletions(-) create mode 100644 lib/generators/site_setting_move_to_groups_migration/USAGE create mode 100644 lib/generators/site_setting_move_to_groups_migration/site_setting_move_to_groups_migration_generator.rb create mode 100644 spec/generator/site_setting_move_to_groups_migrations_generator_spec.rb diff --git a/lib/generators/site_setting_move_to_groups_migration/USAGE b/lib/generators/site_setting_move_to_groups_migration/USAGE new file mode 100644 index 00000000000..d8b1900bbf2 --- /dev/null +++ b/lib/generators/site_setting_move_to_groups_migration/USAGE @@ -0,0 +1,8 @@ +Description: + Generates migration to move data from trust level based site setting to group based site setting. It only works when moving from TrustLevelSetting or TrustLevelAndStaffSetting. + +Example: + bin/rails generate site_setting_move_to_groups_migration min_trust_to_create_topic create_topic_allowed_groups + + This will create: + db/migrate/20231206041353_fill_create_topic_allowed_groups_based_on_deprecated_settings.rb diff --git a/lib/generators/site_setting_move_to_groups_migration/site_setting_move_to_groups_migration_generator.rb b/lib/generators/site_setting_move_to_groups_migration/site_setting_move_to_groups_migration_generator.rb new file mode 100644 index 00000000000..31515686886 --- /dev/null +++ b/lib/generators/site_setting_move_to_groups_migration/site_setting_move_to_groups_migration_generator.rb @@ -0,0 +1,111 @@ +# frozen_string_literal: true + +class SiteSettingMoveToGroupsMigrationGenerator < Rails::Generators::Base + include SiteSettingExtension + + argument :old_name, type: :string, banner: "old setting name", required: true + argument :new_name, type: :string, banner: "new setting name", required: true + + def create_migration_file + migration_version = ActiveRecord::Migration.next_migration_number(0) + file_path = "db/migrate/#{migration_version}_fill_#{new_name}_based_on_deprecated_setting.rb" + class_name = "Fill#{new_name.classify}BasedOnDeprecatedSetting" + + validate_setting_name!(old_name) + validate_setting_name!(new_name) + validate_setting_type!(old_name) + + create_file file_path, <<~MIGRATION_FILE if setting_type(old_name) == "TrustLevelSetting" + # frozen_string_literal: true + + class #{class_name} < ActiveRecord::Migration[7.0] + def up + old_setting_trust_level = + DB.query_single( + "SELECT value FROM site_settings WHERE name = '#{old_name}' LIMIT 1", + ).first + + if old_setting_trust_level.present? + allowed_groups = "1\#\{old_setting_trust_level\}" + + DB.exec( + "INSERT INTO site_settings(name, value, data_type, created_at, updated_at) + VALUES('#{new_name}', :setting, '20', NOW(), NOW())", + setting: allowed_groups, + ) + end + end + + def down + raise ActiveRecord::IrreversibleMigration + end + end + MIGRATION_FILE + if setting_type(old_name) == "TrustLevelAndStaffSetting" + create_file file_path, <<~MIGRATION_FILE + # frozen_string_literal: true + + class #{class_name} < ActiveRecord::Migration[7.0] + def up + old_setting_trust_level = + DB.query_single( + "SELECT value FROM site_settings WHERE name = '#{old_name}' LIMIT 1", + ).first + + if old_setting_trust_level.present? + allowed_groups = + case old_setting_trust_level + when "admin" + "1" + when "staff" + "3" + when "0" + "10" + when "1" + "11" + when "2" + "12" + when "3" + "13" + when "4" + "14" + end + + DB.exec( + "INSERT INTO site_settings(name, value, data_type, created_at, updated_at) + VALUES('#{new_name}', :setting, '20', NOW(), NOW())", + setting: allowed_groups, + ) + end + end + + def down + raise ActiveRecord::IrreversibleMigration + end + end + MIGRATION_FILE + end + end + + private + + def validate_setting_name!(name) + if !SiteSetting.respond_to?(name) + say "Site setting with #{name} does not exist" + raise ArgumentError + end + end + + def setting_type(name) + @site_settings ||= load_settings(File.join(Rails.root, "config", "site_settings.yml")) + subgroup = @site_settings.values.find { |row| row.keys.include?(name) } + subgroup[name][:enum] + end + + def validate_setting_type!(name) + if !%w[TrustLevelSetting TrustLevelAndStaffSetting].include?(setting_type(name)) + say "Site setting with #{name} must be TrustLevelSetting" + raise ArgumentError + end + end +end diff --git a/lib/generators/site_setting_rename_migration/site_setting_rename_migration_generator.rb b/lib/generators/site_setting_rename_migration/site_setting_rename_migration_generator.rb index 01a05c9d87b..c3171dc3b76 100644 --- a/lib/generators/site_setting_rename_migration/site_setting_rename_migration_generator.rb +++ b/lib/generators/site_setting_rename_migration/site_setting_rename_migration_generator.rb @@ -5,8 +5,8 @@ class SiteSettingRenameMigrationGenerator < Rails::Generators::Base argument :new_name, type: :string, banner: "new setting name", required: true def create_migration_file - timestamp = Time.zone.now.to_s.tr("^0-9", "")[0..13] - file_path = "db/migrate/#{timestamp}_rename_#{old_name}_setting.rb" + migration_version = ActiveRecord::Migration.next_migration_number(0) + file_path = "db/migrate/#{migration_version}_rename_#{old_name}_setting.rb" class_name = "Rename#{old_name.classify}Setting" validate_setting_name!(old_name) @@ -30,9 +30,7 @@ class SiteSettingRenameMigrationGenerator < Rails::Generators::Base private def validate_setting_name!(name) - begin - SiteSetting.send(name) - rescue NoMethodError + if !SiteSetting.respond_to?(name) say "Site setting with #{name} does not exist" raise ArgumentError end diff --git a/spec/generator/site_setting_move_to_groups_migrations_generator_spec.rb b/spec/generator/site_setting_move_to_groups_migrations_generator_spec.rb new file mode 100644 index 00000000000..dd1454a4e58 --- /dev/null +++ b/spec/generator/site_setting_move_to_groups_migrations_generator_spec.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +require "rails_helper" +require "rails/generators" +require "generators/site_setting_move_to_groups_migration/site_setting_move_to_groups_migration_generator" + +RSpec.describe SiteSettingMoveToGroupsMigrationGenerator, type: :generator do + it "generates the correct migration for TrustLevelSetting" do + freeze_time DateTime.parse("2010-01-01 12:00") + described_class + .any_instance + .expects(:load_settings) + .returns({ branding: { "site_description" => { enum: "TrustLevelSetting" } } }) + + described_class.start(%w[site_description contact_email], destination_root: "#{Rails.root}/tmp") + file_path = + "#{Rails.root}/tmp/db/migrate/20100101120000_fill_contact_email_based_on_deprecated_setting.rb" + + expected_content = <<~EXPECTED_CONTENT + # frozen_string_literal: true + + class FillContactEmailBasedOnDeprecatedSetting < ActiveRecord::Migration[7.0] + def up + old_setting_trust_level = + DB.query_single( + "SELECT value FROM site_settings WHERE name = 'site_description' LIMIT 1", + ).first + + if old_setting_trust_level.present? + allowed_groups = "1\#\{old_setting_trust_level\}" + + DB.exec( + "INSERT INTO site_settings(name, value, data_type, created_at, updated_at) + VALUES('contact_email', :setting, '20', NOW(), NOW())", + setting: allowed_groups, + ) + end + end + + def down + raise ActiveRecord::IrreversibleMigration + end + end + EXPECTED_CONTENT + + expect(File.read(file_path)).to eq(expected_content) + File.delete(file_path) + end + + it "generates the correct migration for TrustLevelAndStaffSetting" do + freeze_time DateTime.parse("2010-01-01 12:00") + described_class + .any_instance + .expects(:load_settings) + .returns({ branding: { "title" => { enum: "TrustLevelAndStaffSetting" } } }) + + described_class.start(%w[title contact_email], destination_root: "#{Rails.root}/tmp") + file_path = + "#{Rails.root}/tmp/db/migrate/20100101120000_fill_contact_email_based_on_deprecated_setting.rb" + + expected_content = <<~EXPECTED_CONTENT + # frozen_string_literal: true + + class FillContactEmailBasedOnDeprecatedSetting < ActiveRecord::Migration[7.0] + def up + old_setting_trust_level = + DB.query_single( + "SELECT value FROM site_settings WHERE name = 'title' LIMIT 1", + ).first + + if old_setting_trust_level.present? + allowed_groups = + case old_setting_trust_level + when "admin" + "1" + when "staff" + "3" + when "0" + "10" + when "1" + "11" + when "2" + "12" + when "3" + "13" + when "4" + "14" + end + + DB.exec( + "INSERT INTO site_settings(name, value, data_type, created_at, updated_at) + VALUES('contact_email', :setting, '20', NOW(), NOW())", + setting: allowed_groups, + ) + end + end + + def down + raise ActiveRecord::IrreversibleMigration + end + end + EXPECTED_CONTENT + + expect(File.read(file_path)).to eq(expected_content) + File.delete(file_path) + end + + it "raises an error when old name is incorrect" do + expect { described_class.start(%w[wrong_name contact_email]) }.to raise_error(ArgumentError) + end + + it "raises an error when new name is incorrect" do + expect { described_class.start(%w[site_description wrong_name]) }.to raise_error(ArgumentError) + end + + it "raises an error when old setting is incorrect type" do + described_class + .any_instance + .expects(:load_settings) + .returns({ branding: { "site_description" => { enum: "EmojiSetSiteSetting" } } }) + expect { described_class.start(%w[site_description contact_email]) }.to raise_error( + ArgumentError, + ) + end +end