tasks: prevent the PJ migrations task updating from touching dossiers

This commit is contained in:
Pierre de La Morinerie 2019-06-12 16:58:47 +00:00
parent 60a61da644
commit f12668fbfb
2 changed files with 80 additions and 23 deletions

View file

@ -25,9 +25,16 @@ class PieceJustificativeToChampPieceJointeMigrationService
populate_champs_pjs!(procedure, types_de_champ_pj, &progress) populate_champs_pjs!(procedure, types_de_champ_pj, &progress)
# Only destroy the old types PJ once everything has been safely migrated to # Only destroy the old types PJ once everything has been safely migrated to
# champs PJs. Destroying the types PJ will cascade and destroy the PJs, # champs PJs.
# and delete the linked objects from remote storage. This means that no other
# cleanup action is required. # First destroy the individual PJ champs on all dossiers.
# It will cascade and destroy the PJs, and delete the linked objects from remote storage.
procedure.dossiers.unscope(where: :hidden_at).includes(:champs).find_each do |dossier|
destroy_pieces_justificatives(dossier)
end
# Now we can destroy the type de champ themselves,
# without cascading the timestamp update on all attached dossiers.
procedure.types_de_piece_justificative.destroy_all procedure.types_de_piece_justificative.destroy_all
end end
@ -49,8 +56,11 @@ class PieceJustificativeToChampPieceJointeMigrationService
rake_puts "Error received. Rolling back migration of procedure #{procedure.id}" rake_puts "Error received. Rolling back migration of procedure #{procedure.id}"
types_de_champ_pj.each do |type_champ| types_de_champ_pj.each do |type_champ|
type_champ.champ.each { |c| c.piece_justificative_file.purge } # First destroy all the individual champs on dossiers
type_champ.destroy type_champ.champ.each { |c| destroy_champ_pj(c.dossier.reload, c) }
# Now we can destroy the type de champ itself,
# without cascading the timestamp update on all attached dossiers.
type_champ.reload.destroy
end end
rake_puts "Migration of procedure #{procedure.id} rolled back." rake_puts "Migration of procedure #{procedure.id} rolled back."
@ -62,7 +72,9 @@ class PieceJustificativeToChampPieceJointeMigrationService
def migrate_dossier!(dossier, types_de_champ_pj) def migrate_dossier!(dossier, types_de_champ_pj)
# Add the new pieces justificatives champs to the dossier # Add the new pieces justificatives champs to the dossier
champs_pj = types_de_champ_pj.map(&:build_champ) champs_pj = types_de_champ_pj.map(&:build_champ)
dossier.champs += champs_pj preserving_updated_at(dossier) do
dossier.champs += champs_pj
end
# Copy the dossier old pieces jointes to the new champs # Copy the dossier old pieces jointes to the new champs
# (even if the champs already existed, so that we ensure a clean state) # (even if the champs already existed, so that we ensure a clean state)
@ -71,7 +83,9 @@ class PieceJustificativeToChampPieceJointeMigrationService
pj = dossier.retrieve_last_piece_justificative_by_type(type_pj_id) pj = dossier.retrieve_last_piece_justificative_by_type(type_pj_id)
if pj.present? if pj.present?
convert_pj_to_champ!(pj, champ) preserving_updated_at(dossier) do
convert_pj_to_champ!(pj, champ)
end
champ.update_columns( champ.update_columns(
updated_at: pj.updated_at, updated_at: pj.updated_at,
@ -137,4 +151,26 @@ class PieceJustificativeToChampPieceJointeMigrationService
def make_empty_blob(pj) def make_empty_blob(pj)
storage_service.make_empty_blob(pj.content, pj.updated_at.iso8601, filename: pj.original_filename) storage_service.make_empty_blob(pj.content, pj.updated_at.iso8601, filename: pj.original_filename)
end end
def preserving_updated_at(model)
original_modification_date = model.updated_at
begin
yield
ensure
model.update_column(:updated_at, original_modification_date)
end
end
def destroy_pieces_justificatives(dossier)
preserving_updated_at(dossier) do
dossier.pieces_justificatives.destroy_all
end
end
def destroy_champ_pj(dossier, champ)
preserving_updated_at(dossier) do
champ.piece_justificative_file.purge
champ.destroy
end
end
end end

View file

@ -26,6 +26,18 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
end end
end end
def timestamps(dossier)
# Reload dossier because the resolution of in-database timestamps is
# different from the resolution of in-memory timestamps, causing the
# tests to fail on fractional time differences.
dossier.reload
{
created_at: dossier.created_at,
updated_at: dossier.updated_at
}
end
def expect_storage_service_to_convert_object def expect_storage_service_to_convert_object
expect(storage_service).to receive(:make_blob) expect(storage_service).to receive(:make_blob)
expect(storage_service).to receive(:copy_from_carrierwave_to_active_storage!) expect(storage_service).to receive(:copy_from_carrierwave_to_active_storage!)
@ -64,6 +76,8 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
end end
context 'no notifications are sent to instructeurs' do context 'no notifications are sent to instructeurs' do
let!(:initial_dossier_timestamps) { timestamps(dossier) }
context 'when there is a PJ' do context 'when there is a PJ' do
let(:pjs) { make_pjs } let(:pjs) { make_pjs }
@ -80,24 +94,18 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
dossier.reload dossier.reload
end end
it 'the champ has the same created_at as the PJ' do it 'the champ has the same timestamps as the PJ' do
expect(dossier.champs.last.created_at).to eq(pjs.last.created_at) expect(dossier.champs.last.created_at).to eq(pjs.last.created_at)
expect(dossier.champs.last.updated_at).to eq(pjs.last.updated_at)
end end
it 'the champ has the same updated_at as the PJ' do it 'does not change the dossier timestamps' do
expect(dossier.champs.last.updated_at).to eq(pjs.last.updated_at) expect(dossier.created_at).to eq(initial_dossier_timestamps[:created_at])
expect(dossier.updated_at).to eq(initial_dossier_timestamps[:updated_at])
end end
end end
context 'when there is no PJ' do context 'when there is no PJ' do
let!(:expected_updated_at) do
# Reload dossier because the resolution of in-database timestamps is
# different from the resolution of in-memory timestamps, causing the
# tests to fail on fractional time differences.
dossier.reload
dossier.updated_at
end
before do before do
Timecop.travel(1.hour) { service.convert_procedure_pjs_to_champ_pjs(procedure) } Timecop.travel(1.hour) { service.convert_procedure_pjs_to_champ_pjs(procedure) }
@ -105,12 +113,14 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
dossier.reload dossier.reload
end end
it 'the champ has the same created_at as the dossier' do it 'the champ has the same timestamps as the dossier' do
expect(dossier.champs.last.created_at).to eq(dossier.created_at) expect(dossier.champs.last.created_at).to eq(initial_dossier_timestamps[:created_at])
expect(dossier.champs.last.updated_at).to eq(initial_dossier_timestamps[:updated_at])
end end
it 'the champ has the same updated_at as the dossier' do it 'does not change the dossier timestamps' do
expect(dossier.champs.last.updated_at).to eq(expected_updated_at) expect(dossier.created_at).to eq(initial_dossier_timestamps[:created_at])
expect(dossier.updated_at).to eq(initial_dossier_timestamps[:updated_at])
end end
end end
end end
@ -192,6 +202,9 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
) )
end end
let!(:initial_dossier_timestamps) { timestamps(dossier) }
let!(:initial_failing_dossier_timestamps) { timestamps(failing_dossier) }
before do before do
allow(storage_service).to receive(:checksum).and_return('cafe') allow(storage_service).to receive(:checksum).and_return('cafe')
allow(storage_service).to receive(:fix_content_type) allow(storage_service).to receive(:fix_content_type)
@ -204,8 +217,10 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
end end
def try_convert(procedure) def try_convert(procedure)
service.convert_procedure_pjs_to_champ_pjs(procedure) Timecop.travel(1.hour) { service.convert_procedure_pjs_to_champ_pjs(procedure) }
rescue StandardError, SignalException => e rescue StandardError, SignalException => e
dossier.reload
failing_dossier.reload
e e
end end
@ -234,6 +249,12 @@ describe PieceJustificativeToChampPieceJointeMigrationService do
.not_to change { procedure.types_de_piece_justificative.count } .not_to change { procedure.types_de_piece_justificative.count }
end end
it 'does not change the dossiers timestamps' do
try_convert(procedure)
expect(dossier.updated_at).to eq(initial_dossier_timestamps[:updated_at])
expect(failing_dossier.updated_at).to eq(initial_failing_dossier_timestamps[:updated_at])
end
it 'does not leave stale blobs behind' do it 'does not leave stale blobs behind' do
expect { try_convert(procedure) } expect { try_convert(procedure) }
.not_to change { ActiveStorage::Blob.count } .not_to change { ActiveStorage::Blob.count }