Skip to content

Commit c49ff72

Browse files
committed
improve resilience of csv import of partners
1 parent b274fc0 commit c49ff72

10 files changed

+156
-6
lines changed

app/controllers/concerns/importable.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ module Importable
2525
def import_csv
2626
if params[:file].present?
2727
data = File.read(params[:file].path, encoding: "BOM|UTF-8")
28-
csv = CSV.parse(data, headers: true)
28+
csv = CSV.parse(data, headers: true, skip_blanks: true)
2929
if csv.count.positive? && csv.first.headers.all? { |header| !header.nil? }
3030
errors = resource_model.import_csv(csv, current_organization.id)
3131
if errors.empty?

app/models/partner.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class Partner < ApplicationRecord
5757
validate :default_storage_location_belongs_to_organization
5858

5959
before_save { email&.downcase! }
60+
before_create :default_send_reminders_to_false, if: :send_reminders_nil?
6061
before_update :invite_new_partner, if: :should_invite_because_email_changed?
6162

6263
scope :alphabetized, -> { order(:name) }
@@ -110,7 +111,9 @@ def self.import_csv(csv, organization_id)
110111

111112
svc = PartnerCreateService.new(organization: organization, partner_attrs: hash_rows)
112113
svc.call
113-
if svc.errors.present?
114+
if svc.errors.present? && svc.partner.errors.blank?
115+
errors << "#{svc.partner.name}: #{svc.errors.full_messages.to_sentence}"
116+
elsif svc.errors.present?
114117
errors << "#{svc.partner.name}: #{svc.partner.errors.full_messages.to_sentence}"
115118
end
116119
end
@@ -172,6 +175,14 @@ def correct_document_mime_type
172175
end
173176
end
174177

178+
def default_send_reminders_to_false
179+
self.send_reminders = false
180+
end
181+
182+
def send_reminders_nil?
183+
send_reminders.nil?
184+
end
185+
175186
def invite_new_partner
176187
UserInviteService.invite(email: email, roles: [Role::PARTNER], resource: self)
177188
end

app/services/partner_create_service.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@ def initialize(organization:, partner_attrs:)
99
end
1010

1111
def call
12-
if partner_attrs["default_storage_location"]
13-
default_storage_location_name = partner_attrs["default_storage_location"].titlecase
14-
default_storage_location_id = StorageLocation.find_by(name: default_storage_location_name)&.id
12+
if partner_attrs.has_key?("default_storage_location") && partner_attrs["default_storage_location"].blank?
13+
partner_attrs.delete("default_storage_location")
14+
elsif partner_attrs.has_key?("default_storage_location") && !partner_attrs["default_storage_location"].blank?
15+
default_storage_location_name = partner_attrs["default_storage_location"]&.titlecase
16+
default_storage_location_id = StorageLocation.find_by(name: default_storage_location_name, organization: organization.id)&.id
17+
if default_storage_location_id.nil?
18+
errors.add(:default_storage_location, "The default storage location is not a storage location for this partner's organization")
19+
end
1520
partner_attrs.delete("default_storage_location")
1621
partner_attrs["default_storage_location_id"] = default_storage_location_id
1722
end
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name,email,default_storage_location,send_reminders,quota,notes
2+
Partner 51,[email protected],,false,50,"great partner"
3+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name,email,send_reminders,quota,notes
2+
Partner 71,[email protected],false,50,"great partner"
3+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name,email,default_storage_location,send_reminders,quota,notes
2+
Partner 51,[email protected],"Smithsonian Conservation Center",,50,"great partner"
3+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name,email,default_storage_location,quota,notes
2+
Partner 51,[email protected],"Smithsonian Conservation Center",50,"great partner"
3+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name,email,default_storage_location,send_reminders,quota,notes
2+
Partner 31,[email protected],"Smithsonian Conservation Center",true,50,"great partner"
3+

spec/requests/partners_requests_spec.rb

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,46 @@
478478
end
479479
end
480480

481+
context "csv file with send_reminders header and field missing" do
482+
let(:file) { fixture_file_upload("partners_missing_send_reminders_field_and_header.csv", "text/csv") }
483+
subject { post import_csv_partners_path, params: { file: file } }
484+
485+
it "invokes .import_csv" do
486+
expect(model_class).to respond_to(:import_csv).with(2).arguments
487+
end
488+
489+
it "redirects to :index" do
490+
subject
491+
expect(response).to be_redirect
492+
end
493+
494+
it "defaults send_reminders to false" do
495+
subject
496+
partner = Partner.find_by(name: "Partner 51")
497+
expect(partner.send_reminders).to be(false)
498+
end
499+
end
500+
501+
context "csv file with send_reminders field missing" do
502+
let(:file) { fixture_file_upload("partners_missing_send_reminders_field.csv", "text/csv") }
503+
subject { post import_csv_partners_path, params: { file: file } }
504+
505+
it "invokes .import_csv" do
506+
expect(model_class).to respond_to(:import_csv).with(2).arguments
507+
end
508+
509+
it "redirects to :index" do
510+
subject
511+
expect(response).to be_redirect
512+
end
513+
514+
it "defaults send_reminders to false" do
515+
subject
516+
partner = Partner.find_by(name: "Partner 51")
517+
expect(partner.send_reminders).to be(false)
518+
end
519+
end
520+
481521
context "csv file with invalid email address" do
482522
let(:file) { fixture_file_upload("partners_with_invalid_email.csv", "text/csv") }
483523
subject { post import_csv_partners_path, params: { file: file } }
@@ -498,6 +538,44 @@
498538
end
499539
end
500540

541+
context "csv file with default storage location header and field missing" do
542+
let(:file) { fixture_file_upload("partners_missing_default_storage_location_field_and_header.csv", "text/csv") }
543+
subject { post import_csv_partners_path, params: { file: file } }
544+
545+
it "invokes .import_csv" do
546+
expect(model_class).to respond_to(:import_csv).with(2).arguments
547+
end
548+
549+
it "redirects to :index" do
550+
subject
551+
expect(response).to be_redirect
552+
end
553+
554+
it "presents a flash notice message" do
555+
subject
556+
expect(response).to have_notice "#{model_class.name.underscore.humanize.pluralize} were imported successfully!"
557+
end
558+
end
559+
560+
context "csv file with default storage location field missing" do
561+
let(:file) { fixture_file_upload("partners_missing_default_storage_location_field.csv", "text/csv") }
562+
subject { post import_csv_partners_path, params: { file: file } }
563+
564+
it "invokes .import_csv" do
565+
expect(model_class).to respond_to(:import_csv).with(2).arguments
566+
end
567+
568+
it "redirects to :index" do
569+
subject
570+
expect(response).to be_redirect
571+
end
572+
573+
it "presents a flash notice message" do
574+
subject
575+
expect(response).to have_notice "#{model_class.name.underscore.humanize.pluralize} were imported successfully!"
576+
end
577+
end
578+
501579
context "csv file with default storage location, email preferences, quota, and notes" do
502580
let(:file) { fixture_file_upload("partners_with_six_fields.csv", "text/csv") }
503581
subject { post import_csv_partners_path, params: { file: file } }
@@ -530,7 +608,7 @@
530608

531609
it "presents a flash error message" do
532610
subject
533-
expect(response).to have_error "The following Partners did not import successfully:\nPartner 4: Default storage location The default storage location is not a storage location for this partner's organization"
611+
expect(response).to have_error "The following Partners did not import successfully:\nPartner 4: default_storage_location The default storage location is not a storage location for this partner's organization"
534612
end
535613
end
536614

@@ -552,6 +630,21 @@
552630
expect(response).to have_notice "#{model_class.name.underscore.humanize.pluralize} were imported successfully!"
553631
end
554632
end
633+
634+
context "csv file with a blank line at the file's bottom" do
635+
let(:file) { fixture_file_upload("partners_with_final_line_blank.csv", "text/csv") }
636+
subject { post import_csv_partners_path, params: { file: file } }
637+
638+
it "redirects to :index" do
639+
subject
640+
expect(response).to be_redirect
641+
end
642+
643+
it "presents a flash notice message" do
644+
subject
645+
expect(response).to have_notice "#{model_class.name.underscore.humanize.pluralize} were imported successfully!"
646+
end
647+
end
555648
end
556649

557650
describe "POST #create" do

spec/services/partner_create_service_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,32 @@
4343
expect(query.first.enable_quantity_based_requests).to eq(true)
4444
end
4545

46+
context 'when send_reminders is nil' do
47+
before do
48+
partner_attrs.merge!(send_reminders: nil)
49+
end
50+
51+
it 'defaults send_reminders to false' do
52+
subject
53+
54+
partner = Partner.find_by(name: partner_attrs[:name])
55+
expect(partner.send_reminders).to be(false)
56+
end
57+
end
58+
59+
context 'when send_reminders is missing' do
60+
before do
61+
partner_attrs.delete(:send_reminders)
62+
end
63+
64+
it 'defaults send_reminders to false' do
65+
subject
66+
67+
partner = Partner.find_by(name: partner_attrs[:name])
68+
expect(partner.send_reminders).to be(false)
69+
end
70+
end
71+
4672
context 'but there was an unexpected issue with saving the' do
4773
let(:error_message) { Faker::Games::ElderScrolls.dragon }
4874

0 commit comments

Comments
 (0)