Stéphane Meny
3 years ago
committed by
GitHub
25 changed files with 312 additions and 126 deletions
@ -1,6 +1,6 @@ |
|||||||
module Constants::Organisation |
module Constants::Organisation |
||||||
ORG_TYPE = { |
PROVIDER_TYPE = { |
||||||
"LA" => 1, |
LA: 1, |
||||||
"PRP" => 2, |
PRP: 2, |
||||||
}.freeze |
}.freeze |
||||||
end |
end |
||||||
|
@ -1,7 +1,7 @@ |
|||||||
module Constants::User |
module Constants::User |
||||||
ROLES = { |
ROLES = { |
||||||
"data_accessor" => 0, |
data_accessor: 0, |
||||||
"data_provider" => 1, |
data_provider: 1, |
||||||
"data_coordinator" => 2, |
data_coordinator: 2, |
||||||
}.freeze |
}.freeze |
||||||
end |
end |
||||||
|
@ -1,56 +0,0 @@ |
|||||||
class ImportService |
|
||||||
def initialize(storage_service, logger = Rails.logger) |
|
||||||
@storage_service = storage_service |
|
||||||
@logger = logger |
|
||||||
end |
|
||||||
|
|
||||||
def update_organisations(folder) |
|
||||||
filenames = @storage_service.list_files(folder) |
|
||||||
filenames.each do |filename| |
|
||||||
file_io = @storage_service.get_file_io(filename) |
|
||||||
create_organisation(file_io) |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
private |
|
||||||
|
|
||||||
def create_organisation(file_io) |
|
||||||
doc = Nokogiri::XML(file_io) |
|
||||||
name = field_value(doc, "name") |
|
||||||
old_visible_id = field_value(doc, "visible-id") |
|
||||||
begin |
|
||||||
Organisation.create!( |
|
||||||
name: name, |
|
||||||
providertype: field_value(doc, "institution-type"), |
|
||||||
phone: field_value(doc, "telephone-number"), |
|
||||||
holds_own_stock: to_boolean(field_value(doc, "holds-stock")), |
|
||||||
active: to_boolean(field_value(doc, "active")), |
|
||||||
old_association_type: field_value(doc, "old-association-type"), |
|
||||||
software_supplier_id: field_value(doc, "software-supplier-id"), |
|
||||||
housing_management_system: field_value(doc, "housing-management-system"), |
|
||||||
choice_based_lettings: to_boolean(field_value(doc, "choice-based-lettings")), |
|
||||||
common_housing_register: to_boolean(field_value(doc, "common-housing-register")), |
|
||||||
choice_allocation_policy: to_boolean(field_value(doc, "choice-allocation-policy")), |
|
||||||
cbl_proportion_percentage: field_value(doc, "cbl-proportion-percentage"), |
|
||||||
enter_affordable_logs: to_boolean(field_value(doc, "enter-affordable-logs")), |
|
||||||
owns_affordable_logs: to_boolean(field_value(doc, "owns-affordable-rent")), |
|
||||||
housing_registration_no: field_value(doc, "housing-registration-no"), |
|
||||||
general_needs_units: field_value(doc, "general-needs-units"), |
|
||||||
supported_housing_units: field_value(doc, "supported-housing-units"), |
|
||||||
unspecified_units: field_value(doc, "unspecified-units"), |
|
||||||
old_org_id: field_value(doc, "id"), |
|
||||||
old_visible_id: old_visible_id, |
|
||||||
) |
|
||||||
rescue ActiveRecord::RecordNotUnique |
|
||||||
@logger.warn("Organisation #{name} is already present with old visible ID #{old_visible_id}, skipping.") |
|
||||||
end |
|
||||||
end |
|
||||||
|
|
||||||
def field_value(doc, field) |
|
||||||
doc.at_xpath("//institution:#{field}")&.text |
|
||||||
end |
|
||||||
|
|
||||||
def to_boolean(input_string) |
|
||||||
input_string == "true" |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,27 @@ |
|||||||
|
module Imports |
||||||
|
class ImportService |
||||||
|
private |
||||||
|
|
||||||
|
def initialize(storage_service, logger = Rails.logger) |
||||||
|
@storage_service = storage_service |
||||||
|
@logger = logger |
||||||
|
end |
||||||
|
|
||||||
|
def import_from(folder, create_method) |
||||||
|
filenames = @storage_service.list_files(folder) |
||||||
|
filenames.each do |filename| |
||||||
|
file_io = @storage_service.get_file_io(filename) |
||||||
|
xml_document = Nokogiri::XML(file_io) |
||||||
|
send(create_method, xml_document) |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def field_value(xml_document, namespace, field) |
||||||
|
xml_document.at_xpath("//#{namespace}:#{field}")&.text |
||||||
|
end |
||||||
|
|
||||||
|
def to_boolean(input_string) |
||||||
|
input_string == "true" |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,46 @@ |
|||||||
|
module Imports |
||||||
|
class OrganisationImportService < ImportService |
||||||
|
def create_organisations(folder) |
||||||
|
import_from(folder, :create_organisation) |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
PROVIDER_TYPE = { |
||||||
|
"HOUSING-ASSOCIATION" => Organisation.provider_types[:PRP], |
||||||
|
}.freeze |
||||||
|
|
||||||
|
def create_organisation(xml_document) |
||||||
|
Organisation.create!( |
||||||
|
name: organisation_field_value(xml_document, "name"), |
||||||
|
provider_type: PROVIDER_TYPE[organisation_field_value(xml_document, "institution-type")], |
||||||
|
phone: organisation_field_value(xml_document, "telephone-number"), |
||||||
|
holds_own_stock: to_boolean(organisation_field_value(xml_document, "holds-stock")), |
||||||
|
active: to_boolean(organisation_field_value(xml_document, "active")), |
||||||
|
old_association_type: organisation_field_value(xml_document, "old-association-type"), |
||||||
|
software_supplier_id: organisation_field_value(xml_document, "software-supplier-id"), |
||||||
|
housing_management_system: organisation_field_value(xml_document, "housing-management-system"), |
||||||
|
choice_based_lettings: to_boolean(organisation_field_value(xml_document, "choice-based-lettings")), |
||||||
|
common_housing_register: to_boolean(organisation_field_value(xml_document, "common-housing-register")), |
||||||
|
choice_allocation_policy: to_boolean(organisation_field_value(xml_document, "choice-allocation-policy")), |
||||||
|
cbl_proportion_percentage: organisation_field_value(xml_document, "cbl-proportion-percentage"), |
||||||
|
enter_affordable_logs: to_boolean(organisation_field_value(xml_document, "enter-affordable-logs")), |
||||||
|
owns_affordable_logs: to_boolean(organisation_field_value(xml_document, "owns-affordable-rent")), |
||||||
|
housing_registration_no: organisation_field_value(xml_document, "housing-registration-no"), |
||||||
|
general_needs_units: organisation_field_value(xml_document, "general-needs-units"), |
||||||
|
supported_housing_units: organisation_field_value(xml_document, "supported-housing-units"), |
||||||
|
unspecified_units: organisation_field_value(xml_document, "unspecified-units"), |
||||||
|
old_org_id: organisation_field_value(xml_document, "id"), |
||||||
|
old_visible_id: organisation_field_value(xml_document, "visible-id"), |
||||||
|
) |
||||||
|
rescue ActiveRecord::RecordNotUnique |
||||||
|
name = organisation_field_value(xml_document, "name") |
||||||
|
old_visible_id = organisation_field_value(xml_document, "visible-id") |
||||||
|
@logger.warn("Organisation #{name} is already present with old visible ID #{old_visible_id}, skipping.") |
||||||
|
end |
||||||
|
|
||||||
|
def organisation_field_value(xml_document, field) |
||||||
|
field_value(xml_document, "institution", field) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,32 @@ |
|||||||
|
module Imports |
||||||
|
class UserImportService < ImportService |
||||||
|
def create_users(folder) |
||||||
|
import_from(folder, :create_user) |
||||||
|
end |
||||||
|
|
||||||
|
private |
||||||
|
|
||||||
|
PROVIDER_TYPE = { |
||||||
|
"Data Provider" => User.roles[:data_provider], |
||||||
|
}.freeze |
||||||
|
|
||||||
|
def create_user(xml_document) |
||||||
|
organisation = Organisation.find_by(old_org_id: user_field_value(xml_document, "institution")) |
||||||
|
User.create!( |
||||||
|
email: user_field_value(xml_document, "user-name"), |
||||||
|
name: user_field_value(xml_document, "full-name"), |
||||||
|
password: Devise.friendly_token, |
||||||
|
phone: user_field_value(xml_document, "telephone-no"), |
||||||
|
old_user_id: user_field_value(xml_document, "id"), |
||||||
|
organisation: organisation, |
||||||
|
role: PROVIDER_TYPE[user_field_value(xml_document, "user-type")], |
||||||
|
) |
||||||
|
rescue ActiveRecord::RecordNotUnique |
||||||
|
@logger.warn("User #{name} with old user id #{old_user_id} is already present, skipping.") |
||||||
|
end |
||||||
|
|
||||||
|
def user_field_value(xml_document, field) |
||||||
|
field_value(xml_document, "user", field) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,13 @@ |
|||||||
|
class AdditionalUserFields < ActiveRecord::Migration[7.0] |
||||||
|
def up |
||||||
|
change_table :users, bulk: true do |t| |
||||||
|
t.column :old_user_id, :string |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def down |
||||||
|
change_table :users, bulk: true do |t| |
||||||
|
t.remove :old_user_id |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,13 @@ |
|||||||
|
class AdditionalUserFields2 < ActiveRecord::Migration[7.0] |
||||||
|
def up |
||||||
|
change_table :users, bulk: true do |t| |
||||||
|
t.column :phone, :string |
||||||
|
end |
||||||
|
end |
||||||
|
|
||||||
|
def down |
||||||
|
change_table :users, bulk: true do |t| |
||||||
|
t.remove :phone |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,9 @@ |
|||||||
|
class RenameProviderType < ActiveRecord::Migration[7.0] |
||||||
|
def up |
||||||
|
rename_column :organisations, :providertype, :provider_type |
||||||
|
end |
||||||
|
|
||||||
|
def down |
||||||
|
rename_column :organisations, :provider_type, :providertype |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,21 @@ |
|||||||
|
require "nokogiri" |
||||||
|
|
||||||
|
namespace :core do |
||||||
|
desc "Import data XMLs from Softwire system" |
||||||
|
task :data_import, %i[type path] => :environment do |_task, args| |
||||||
|
type = args[:type] |
||||||
|
path = args[:path] |
||||||
|
raise "Usage: rake core:data_import['data_type', 'path/to/xml_files']" if path.blank? || type.blank? |
||||||
|
|
||||||
|
storage_service = StorageService.new(PaasConfigurationService.new, ENV["IMPORT_PAAS_INSTANCE"]) |
||||||
|
|
||||||
|
case type |
||||||
|
when "organisation" |
||||||
|
Imports::OrganisationImportService.new(storage_service).create_organisations(path) |
||||||
|
when "user" |
||||||
|
Imports::UserImportService.new(storage_service).create_users(path) |
||||||
|
else |
||||||
|
raise "Type #{type} is not supported by data_import" |
||||||
|
end |
||||||
|
end |
||||||
|
end |
@ -1,13 +0,0 @@ |
|||||||
require "nokogiri" |
|
||||||
|
|
||||||
namespace :data_import do |
|
||||||
desc "Import Organisation XMLs from Softwire system" |
|
||||||
|
|
||||||
# rake data_import:organisations['path/to/xml_files'] |
|
||||||
task :organisations, %i[path] => :environment do |_task, args| |
|
||||||
directory = args.path |
|
||||||
storage_service = StorageService.new(PaasConfigurationService.new, ENV["IMPORT_PAAS_INSTANCE"]) |
|
||||||
import_service = ImportService.new(storage_service) |
|
||||||
import_service.update_organisations(directory) |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,13 @@ |
|||||||
|
<user:user xmlns:user="dclg:user"> |
||||||
|
<user:id>fc7625a02b24ae16162aa63ae7cb33feeec0c373</user:id> |
||||||
|
<user:password>xxx</user:password> |
||||||
|
<user:full-name>John Doe</user:full-name> |
||||||
|
<user:user-name>john.doe@gov.uk</user:user-name> |
||||||
|
<user:institution>7c5bd5fb549c09a2c55d7cb90d7ba84927e64618</user:institution> |
||||||
|
<user:email>john.doe@gov.uk</user:email> |
||||||
|
<user:user-type>Data Provider</user:user-type> |
||||||
|
<user:active>true</user:active> |
||||||
|
<user:deleted>false</user:deleted> |
||||||
|
<user:contact-priority-id>None</user:contact-priority-id> |
||||||
|
<user:telephone-no>02012345678</user:telephone-no> |
||||||
|
</user:user> |
@ -1,32 +0,0 @@ |
|||||||
require "rails_helper" |
|
||||||
require "rake" |
|
||||||
|
|
||||||
describe "rake data_import:organisations", type: :task do |
|
||||||
subject(:task) { Rake::Task["data_import:organisations"] } |
|
||||||
|
|
||||||
let(:fixture_path) { "spec/fixtures/softwire_imports/organisations" } |
|
||||||
let(:instance_name) { "my_instance" } |
|
||||||
let(:storage_service) { instance_double(StorageService) } |
|
||||||
let(:paas_config_service) { instance_double(PaasConfigurationService) } |
|
||||||
let(:import_service) { instance_double(ImportService) } |
|
||||||
|
|
||||||
before do |
|
||||||
allow(StorageService).to receive(:new).and_return(storage_service) |
|
||||||
allow(PaasConfigurationService).to receive(:new).and_return(paas_config_service) |
|
||||||
allow(ImportService).to receive(:new).and_return(import_service) |
|
||||||
allow(ENV).to receive(:[]) |
|
||||||
allow(ENV).to receive(:[]).with("IMPORT_PAAS_INSTANCE").and_return(instance_name) |
|
||||||
|
|
||||||
Rake.application.rake_require("tasks/data_import/organisations") |
|
||||||
Rake::Task.define_task(:environment) |
|
||||||
task.reenable |
|
||||||
end |
|
||||||
|
|
||||||
it "creates an organisation from the given XML file" do |
|
||||||
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) |
|
||||||
expect(ImportService).to receive(:new).with(storage_service) |
|
||||||
expect(import_service).to receive(:update_organisations).with(fixture_path) |
|
||||||
|
|
||||||
task.invoke(fixture_path) |
|
||||||
end |
|
||||||
end |
|
@ -0,0 +1,48 @@ |
|||||||
|
require "rails_helper" |
||||||
|
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) { "my_instance" } |
||||||
|
let(:organisation_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") |
||||||
|
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(Imports::OrganisationImportService).to receive(:new).and_return(import_service) |
||||||
|
allow(ENV).to receive(:[]) |
||||||
|
allow(ENV).to receive(:[]).with("IMPORT_PAAS_INSTANCE").and_return(instance_name) |
||||||
|
end |
||||||
|
|
||||||
|
context "when importing organisation data" do |
||||||
|
it "creates an organisation from the given XML file" do |
||||||
|
expect(StorageService).to receive(:new).with(paas_config_service, instance_name) |
||||||
|
expect(Imports::OrganisationImportService).to receive(:new).with(storage_service) |
||||||
|
expect(import_service).to receive(:create_organisations).with(fixture_path) |
||||||
|
|
||||||
|
task.invoke(organisation_type, 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 type is not supported" do |
||||||
|
expect { task.invoke("unknown_type", "my_path") }.to raise_error(/Type unknown_type is not supported/) |
||||||
|
end |
||||||
|
end |
@ -0,0 +1,37 @@ |
|||||||
|
require "rails_helper" |
||||||
|
|
||||||
|
RSpec.describe Imports::UserImportService do |
||||||
|
let(:fixture_directory) { "spec/fixtures/softwire_imports/users" } |
||||||
|
let(:user_file) { File.open("#{fixture_directory}/fc7625a02b24ae16162aa63ae7cb33feeec0c373.xml") } |
||||||
|
let(:storage_service) { instance_double(StorageService) } |
||||||
|
|
||||||
|
context "when importing users" do |
||||||
|
subject(:import_service) { described_class.new(storage_service) } |
||||||
|
|
||||||
|
before do |
||||||
|
allow(storage_service).to receive(:list_files) |
||||||
|
.and_return(["user_directory/fc7625a02b24ae16162aa63ae7cb33feeec0c373.xml"]) |
||||||
|
allow(storage_service).to receive(:get_file_io) |
||||||
|
.with("user_directory/fc7625a02b24ae16162aa63ae7cb33feeec0c373.xml") |
||||||
|
.and_return(user_file) |
||||||
|
end |
||||||
|
|
||||||
|
it "successfully create a user with the expected data" do |
||||||
|
FactoryBot.create(:organisation, old_org_id: "7c5bd5fb549c09a2c55d7cb90d7ba84927e64618") |
||||||
|
import_service.create_users("user_directory") |
||||||
|
|
||||||
|
user = User.find_by(old_user_id: "fc7625a02b24ae16162aa63ae7cb33feeec0c373") |
||||||
|
expect(user.name).to eq("John Doe") |
||||||
|
expect(user.email).to eq("john.doe@gov.uk") |
||||||
|
expect(user.encrypted_password).not_to be_nil |
||||||
|
expect(user.phone).to eq("02012345678") |
||||||
|
expect(user).to be_data_provider |
||||||
|
expect(user.organisation.old_org_id).to eq("7c5bd5fb549c09a2c55d7cb90d7ba84927e64618") |
||||||
|
end |
||||||
|
|
||||||
|
it "refuses to create a user belonging to a non existing organisation" do |
||||||
|
expect { import_service.create_users("user_directory") } |
||||||
|
.to raise_error(ActiveRecord::RecordInvalid, /Organisation must exist/) |
||||||
|
end |
||||||
|
end |
||||||
|
end |
Loading…
Reference in new issue