From fc47d8f855059054f1ebf9ae2733c78b56b9d9b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Meny?= Date: Fri, 27 May 2022 14:42:30 +0100 Subject: [PATCH] Rake task to fix empty tenant code --- .../imports/case_logs_field_import_service.rb | 40 ++++++++++ lib/tasks/data_import_field.rake | 18 +++++ spec/lib/tasks/data_import_spec.rb | 9 ++- spec/lib/tasks/date_import_field_spec.rb | 57 ++++++++++++++ .../case_logs_field_import_service_spec.rb | 75 +++++++++++++++++++ 5 files changed, 195 insertions(+), 4 deletions(-) create mode 100644 app/services/imports/case_logs_field_import_service.rb create mode 100644 lib/tasks/data_import_field.rake create mode 100644 spec/lib/tasks/date_import_field_spec.rb create mode 100644 spec/services/imports/case_logs_field_import_service_spec.rb diff --git a/app/services/imports/case_logs_field_import_service.rb b/app/services/imports/case_logs_field_import_service.rb new file mode 100644 index 000000000..7489cfef6 --- /dev/null +++ b/app/services/imports/case_logs_field_import_service.rb @@ -0,0 +1,40 @@ +module Imports + class CaseLogsFieldImportService < ImportService + def update_field(field, folder) + case field + when "tenant_code" + import_from(folder, :update_tenant_code) + else + raise "Updating #{field} is not supported by the field import service" + end + end + + private + + def update_tenant_code(xml_doc) + update_string_value(xml_doc, "_2bTenCode", "tenant_code") + end + + def update_string_value(xml_doc, src_field, dest_field) + old_id = field_value(xml_doc, "meta", "document-id") + record = CaseLog.find_by(old_id:) + + if record.present? + tenant_code = string_or_nil(xml_doc, src_field) + current_value = record.read_attribute(dest_field) + if tenant_code.present? && current_value.blank? + record.update_column(dest_field, tenant_code) + else + @logger.info("Case Log #{record.id} has a value for #{dest_field}, skipping update") + end + else + @logger.warn("Could not find record matching legacy ID #{old_id}") + end + end + + def string_or_nil(xml_doc, attribute) + str = field_value(xml_doc, "xmlns", attribute) + str.presence + end + end +end diff --git a/lib/tasks/data_import_field.rake b/lib/tasks/data_import_field.rake new file mode 100644 index 000000000..45a53ec65 --- /dev/null +++ b/lib/tasks/data_import_field.rake @@ -0,0 +1,18 @@ +namespace :core do + desc "Update database field from data XMLs provided by Softwire" + task :data_import_field, %i[field path] => :environment do |_task, args| + field = args[:field] + path = args[:path] + raise "Usage: rake core:data_import_field['field','path/to/xml_files']" if path.blank? || field.blank? + + storage_service = StorageService.new(PaasConfigurationService.new, ENV["IMPORT_PAAS_INSTANCE"]) + + # We only allow a reduced list of known fields to be updatable + case field + when "tenant_code" + Imports::CaseLogsFieldImportService.new(storage_service).update_field(field, path) + else + raise "Field #{field} cannot be updated by data_import_field" + end + end +end diff --git a/spec/lib/tasks/data_import_spec.rb b/spec/lib/tasks/data_import_spec.rb index 3d69913a9..d1023b100 100644 --- a/spec/lib/tasks/data_import_spec.rb +++ b/spec/lib/tasks/data_import_spec.rb @@ -4,13 +4,9 @@ require "rake" describe "rake core:data_import", type: :task do subject(:task) { Rake::Task["core:data_import"] } - let(:fixture_path) { "spec/fixtures/softwire_imports/organisations" } let(:instance_name) { "paas_import_instance" } - let(:type) { "organisation" } - let(:storage_service) { instance_double(StorageService) } let(:paas_config_service) { instance_double(PaasConfigurationService) } - let(:import_service) { instance_double(Imports::OrganisationImportService) } before do Rake.application.rake_require("tasks/data_import") @@ -24,6 +20,10 @@ describe "rake core:data_import", type: :task do end context "when importing organisation data" do + let(:type) { "organisation" } + let(:import_service) { instance_double(Imports::OrganisationImportService) } + let(:fixture_path) { "spec/fixtures/softwire_imports/organisations" } + before do allow(Imports::OrganisationImportService).to receive(:new).and_return(import_service) end @@ -40,6 +40,7 @@ describe "rake core:data_import", type: :task do context "when importing user data" do let(:type) { "user" } let(:import_service) { instance_double(Imports::UserImportService) } + let(:fixture_path) { "spec/fixtures/softwire_imports/users" } before do allow(Imports::UserImportService).to receive(:new).and_return(import_service) diff --git a/spec/lib/tasks/date_import_field_spec.rb b/spec/lib/tasks/date_import_field_spec.rb new file mode 100644 index 000000000..e86cecf1d --- /dev/null +++ b/spec/lib/tasks/date_import_field_spec.rb @@ -0,0 +1,57 @@ +require "rails_helper" +require "rake" + +describe "rake core:data_import_field", type: :task do + subject(:task) { Rake::Task["core:data_import_field"] } + + let(:instance_name) { "paas_import_instance" } + let(:storage_service) { instance_double(StorageService) } + let(:paas_config_service) { instance_double(PaasConfigurationService) } + + before do + Rake.application.rake_require("tasks/data_import_field") + Rake::Task.define_task(:environment) + task.reenable + + allow(StorageService).to receive(:new).and_return(storage_service) + allow(PaasConfigurationService).to receive(:new).and_return(paas_config_service) + allow(ENV).to receive(:[]) + allow(ENV).to receive(:[]).with("IMPORT_PAAS_INSTANCE").and_return(instance_name) + end + + context "when importing a case log field" do + let(:import_service) { instance_double(Imports::CaseLogsFieldImportService) } + let(:fixture_path) { "spec/fixtures/softwire_imports/case_logs" } + + before do + allow(Imports::CaseLogsFieldImportService).to receive(:new).and_return(import_service) + allow(import_service).to receive(:update_field) + end + + context "and we update the tenant_code field" do + let(:field) { "tenant_code" } + + it "properly configures the storage service" do + expect(StorageService).to receive(:new).with(paas_config_service, instance_name) + task.invoke(field, fixture_path) + end + + it "calls the expected update method with parameters" do + expect(import_service).to receive(:update_field).with(field, fixture_path) + task.invoke(field, fixture_path) + end + end + + it "raises an exception if no parameters are provided" do + expect { task.invoke }.to raise_error(/Usage/) + end + + it "raises an exception if a single parameter is provided" do + expect { task.invoke("one_parameter") }.to raise_error(/Usage/) + end + + it "raises an exception if the field is not supported" do + expect { task.invoke("random_field", "my_path") }.to raise_error("Field random_field cannot be updated by data_import_field") + end + end +end diff --git a/spec/services/imports/case_logs_field_import_service_spec.rb b/spec/services/imports/case_logs_field_import_service_spec.rb new file mode 100644 index 000000000..5166ebe31 --- /dev/null +++ b/spec/services/imports/case_logs_field_import_service_spec.rb @@ -0,0 +1,75 @@ +require "rails_helper" + +RSpec.describe Imports::CaseLogsFieldImportService do + subject(:import_service) { described_class.new(storage_service, logger) } + + let(:storage_service) { instance_double(StorageService) } + let(:logger) { instance_double(ActiveSupport::Logger) } + + let(:real_2021_2022_form) { Form.new("config/forms/2021_2022.json", "2021_2022") } + let(:fixture_directory) { "spec/fixtures/softwire_imports/case_logs" } + + def open_file(directory, filename) + File.open("#{directory}/#{filename}.xml") + end + + before do + # Owning and Managing organisations + FactoryBot.create(:organisation, old_visible_id: "1", provider_type: "PRP") + + # Created by users + FactoryBot.create(:user, old_user_id: "c3061a2e6ea0b702e6f6210d5c52d2a92612d2aa") + + # Stub the form handler to use the real form + allow(FormHandler.instance).to receive(:get_form).with("2021_2022").and_return(real_2021_2022_form) + + WebMock.stub_request(:get, /api.postcodes.io\/postcodes\/LS166FT/) + .to_return(status: 200, body: '{"status":200,"result":{"codes":{"admin_district":"E08000035"}}}', headers: {}) + end + + context "when updating a specific log value" do + let(:case_log_id) { "0ead17cb-1668-442d-898c-0d52879ff592" } + let(:case_log_file) { open_file(fixture_directory, case_log_id) } + let(:case_log_xml) { Nokogiri::XML(case_log_file) } + let(:remote_folder) { "case_logs" } + let(:field) { "tenant_code" } + + before do + # Stub the S3 file listing and download + allow(storage_service).to receive(:list_files) + .and_return(["#{remote_folder}/#{case_log_id}.xml"]) + allow(storage_service).to receive(:get_file_io) + .with("#{remote_folder}/#{case_log_id}.xml") + .and_return(case_log_file) + end + + context "and the case log was previously imported" do + before do + Imports::CaseLogsImportService.new(storage_service, logger).create_logs(fixture_directory) + case_log_file.rewind + end + + it "logs that the tenant_code already has a value and does not update the case_log" do + expect(logger).to receive(:info).with(/Case Log \d+ has a value for tenant_code, skipping update/) + + import_service.send(:update_field, field, remote_folder) + case_log = CaseLog.find_by(old_id: case_log_id) + expect(case_log.tenant_code).not_to eq(nil) + end + end + + context "and the case log was previously imported with empty fields" do + before do + Imports::CaseLogsImportService.new(storage_service, logger).create_logs(fixture_directory) + case_log_file.rewind + CaseLog.find_by(old_id: case_log_id).update_column("tenant_code", nil) + end + + it "updates the case_log" do + import_service.send(:update_field, field, remote_folder) + case_log = CaseLog.find_by(old_id: case_log_id) + expect(case_log.tenant_code).not_to eq(nil) + end + end + end +end