Add Rake task to migrate milestone data to new tables

This commit is contained in:
Marko Lovic
2018-07-18 12:27:10 +02:00
committed by Javi Martín
parent c0f6fa182f
commit 4c3dadf1fb
2 changed files with 213 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
namespace :milestones do
def generate_table_migration_sql(new_table:, old_table:, columns:)
from_cols = ['id', *columns.keys]
to_cols = ['id', *columns.values]
<<~SQL
INSERT INTO #{new_table} (#{to_cols.join(', ')})
SELECT #{from_cols.join(', ')} FROM #{old_table};
SQL
end
def migrate_table!(new_table:, old_table:, columns:)
puts "Migrating data from '#{old_table}' to '#{new_table}'..."
result = ActiveRecord::Base.connection.execute(
generate_table_migration_sql(old_table: old_table,
new_table: new_table,
columns: columns)
)
puts "#{result.cmd_tuples} rows affected"
end
def populate_column!(table:, column:, value:)
puts "Populating column '#{column}' from table '#{table}' with '#{value}'..."
result = ActiveRecord::Base.connection.execute(
"UPDATE #{table} SET #{column} = '#{value}';"
)
puts "#{result.cmd_tuples} rows affected"
end
def count_rows(table)
ActiveRecord::Base.connection.query("SELECT COUNT(*) FROM #{table};")[0][0].to_i
end
desc "Migrate milestones and milestone status data after making the model polymorphic"
task migrate: :environment do
# This script copies all milestone-related data from the old tables to
# the new ones (preserving all primary keys). All 3 of the new tables
# must be empty.
#
# To clear the new tables to test this script:
#
# DELETE FROM milestone_statuses;
# DELETE FROM milestones;
# DELETE FROM milestone_translations;
#
start = Time.now
ActiveRecord::Base.transaction do
migrate_table! old_table: 'budget_investment_statuses',
new_table: 'milestone_statuses',
columns: {'name' => 'name',
'description' => 'description',
'hidden_at' => 'hidden_at',
'created_at' => 'created_at',
'updated_at' => 'updated_at'}
migrate_table! old_table: 'budget_investment_milestones',
new_table: 'milestones',
columns: {'investment_id' => 'milestoneable_id',
'title' => 'title',
'description' => 'description',
'created_at' => 'created_at',
'updated_at' => 'updated_at',
'publication_date' => 'publication_date',
'status_id' => 'status_id'}
populate_column! table: 'milestones',
column: 'milestoneable_type',
value: 'Budget::Investment'
migrate_table! old_table: 'budget_investment_milestone_translations',
new_table: 'milestone_translations',
columns: {'budget_investment_milestone_id' => 'milestone_id',
'locale' => 'locale',
'created_at' => 'created_at',
'updated_at' => 'updated_at',
'title' => 'title',
'description' => 'description'}
puts "Verifying that all rows were copied..."
unless count_rows('milestones') == count_rows('budget_investment_milestones') &&
count_rows('milestone_statuses') == count_rows('budget_investment_statuses') &&
count_rows('milestone_translations') == count_rows('budget_investment_milestone_translations')
raise "Number of rows of old and new tables do not match! Rolling back transaction..."
end
end
puts "Finished in %.3f seconds" % (Time.now - start)
end
end

View File

@@ -0,0 +1,121 @@
require "rails_helper"
require "rake"
describe "Milestones tasks" do
before do
Rake.application.rake_require "tasks/migrate_milestones_and_statuses"
Rake::Task.define_task(:environment)
end
describe "#migrate" do
let :run_rake_task do
Rake::Task["milestones:migrate"].reenable
Rake.application.invoke_task "milestones:migrate"
end
let!(:investment) { create(:budget_investment) }
before do
ActiveRecord::Base.connection.execute(
"INSERT INTO budget_investment_statuses " +
"(name, description, hidden_at, created_at, updated_at) " +
"VALUES ('open', 'Good', NULL, '#{Time.current - 1.day}', '#{Time.current}');"
)
status_id = ActiveRecord::Base.connection.execute(
"SELECT MAX(id) FROM budget_investment_statuses;"
).to_a.first["max"]
milestone_attributes = {
investment_id: investment.id,
title: "First",
description: "Interesting",
publication_date: Date.yesterday,
status_id: status_id,
created_at: Time.current - 1.day,
updated_at: Time.current
}
ActiveRecord::Base.connection.execute(
"INSERT INTO budget_investment_milestones " +
"(#{milestone_attributes.keys.join(", ")}) " +
"VALUES (#{milestone_attributes.values.map { |value| "'#{value}'"}.join(", ")})"
)
end
it "migrates statuses" do
run_rake_task
expect(Milestone::Status.count).to be 1
status = Milestone::Status.first
expect(status.name).to eq "open"
expect(status.description).to eq "Good"
expect(status.hidden_at).to be nil
expect(status.created_at.to_date).to eq Date.yesterday
expect(status.updated_at.to_date).to eq Date.today
end
it "migrates milestones" do
run_rake_task
expect(Milestone.count).to be 1
milestone = Milestone.first
expect(milestone.milestoneable_id).to eq investment.id
expect(milestone.milestoneable_type).to eq "Budget::Investment"
expect(milestone.title).to eq "First"
expect(milestone.description).to eq "Interesting"
expect(milestone.publication_date).to eq Date.yesterday
expect(milestone.status_id).to eq Milestone::Status.first.id
expect(milestone.created_at.to_date).to eq Date.yesterday
expect(milestone.updated_at.to_date).to eq Date.today
end
context "Statuses had been deleted" do
before do
ActiveRecord::Base.connection.execute(
"INSERT INTO budget_investment_statuses " +
"(name, description, hidden_at, created_at, updated_at) " +
"VALUES ('deleted', 'Del', NULL, '#{Time.current - 1.day}', '#{Time.current}');"
)
ActiveRecord::Base.connection.execute(
"DELETE FROM budget_investment_statuses WHERE name='deleted'"
)
ActiveRecord::Base.connection.execute(
"INSERT INTO budget_investment_statuses " +
"(name, description, hidden_at, created_at, updated_at) " +
"VALUES ('new', 'New', NULL, '#{Time.current - 1.day}', '#{Time.current}');"
)
status_id = ActiveRecord::Base.connection.execute(
"SELECT MAX(id) FROM budget_investment_statuses;"
).to_a.first["max"]
milestone_attributes = {
investment_id: investment.id,
title: "Last",
description: "Different",
publication_date: Date.yesterday,
status_id: status_id,
created_at: Time.current - 1.day,
updated_at: Time.current
}
ActiveRecord::Base.connection.execute(
"INSERT INTO budget_investment_milestones " +
"(#{milestone_attributes.keys.join(", ")}) " +
"VALUES (#{milestone_attributes.values.map { |value| "'#{value}'"}.join(", ")})"
)
end
it "migrates the status id correctly" do
run_rake_task
expect(Milestone.last.status_id).to eq Milestone::Status.last.id
end
end
end
end