From 78452c2eb59cf997f54d4f3a95b1c8233e7e04ab Mon Sep 17 00:00:00 2001 From: SixtyGrit Date: Fri, 12 Jun 2026 12:13:50 -0700 Subject: [PATCH] Resolve CSV file references with spaces against nested attachments CsvParser#file_paths transliterated every file reference's spaces to underscores before checking existence, while remove_spaces_from_filenames only renames the top level of files/ (nested entries are deliberately left as-is, per the unzip_spec added with the guided importer). A CSV referencing a nested attachment whose name contains spaces (files/subdir/photo name.jpg) could therefore never resolve: the lookup demanded underscores, the disk kept spaces. Try the reference as written first, then fall back to the underscored variant for top-level files that remove_spaces_from_filenames renamed. --- app/parsers/bulkrax/csv_parser.rb | 17 +++++++++++------ spec/parsers/bulkrax/csv_parser_spec.rb | 24 ++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/app/parsers/bulkrax/csv_parser.rb b/app/parsers/bulkrax/csv_parser.rb index 5d66277e..65d2222c 100644 --- a/app/parsers/bulkrax/csv_parser.rb +++ b/app/parsers/bulkrax/csv_parser.rb @@ -372,12 +372,17 @@ def file_paths raise StandardError, "Record references local files but no files directory could be resolved from the import path" if files_dir.nil? r[file_mapping].split(split_pattern).map do |f| - file = File.join(files_dir, f.strip.tr(' ', '_')) - if File.exist?(file) # rubocop:disable Style/GuardClause - file - else - raise "File #{file} does not exist" - end + reference = f.strip + # Try the reference as written first, then the space->underscore + # variant produced by #remove_spaces_from_filenames. The rename only + # rewrites the top level of files/ (see unzip_spec), so nested + # attachments keep their spaces on disk and only match as written. + file = [reference, reference.tr(' ', '_')].uniq + .map { |candidate| File.join(files_dir, candidate) } + .find { |candidate| File.exist?(candidate) } + raise "File #{File.join(files_dir, reference)} does not exist" if file.nil? + + file end end.flatten.compact.uniq end diff --git a/spec/parsers/bulkrax/csv_parser_spec.rb b/spec/parsers/bulkrax/csv_parser_spec.rb index 712f20cf..f12c1983 100644 --- a/spec/parsers/bulkrax/csv_parser_spec.rb +++ b/spec/parsers/bulkrax/csv_parser_spec.rb @@ -622,6 +622,30 @@ module Bulkrax end end + context 'when a record references file names containing spaces' do + before do + allow(subject).to receive(:records).and_return([{ file: 'subdir/has space.jpg|top space.jpg' }]) + allow(subject).to receive(:path_to_files).and_return('spec/fixtures/csv/files') + end + + it 'resolves nested files as written and top-level files via the underscored variant' do + allow(File).to receive(:exist?).and_return(false) + allow(File).to receive(:exist?).with('spec/fixtures/csv/files/subdir/has space.jpg').and_return(true) + allow(File).to receive(:exist?).with('spec/fixtures/csv/files/top_space.jpg').and_return(true) + + expect(subject.file_paths).to contain_exactly( + 'spec/fixtures/csv/files/subdir/has space.jpg', + 'spec/fixtures/csv/files/top_space.jpg' + ) + end + + it 'raises naming the as-written reference when neither variant exists' do + allow(File).to receive(:exist?).and_return(false) + + expect { subject.file_paths }.to raise_error(RuntimeError, %r{subdir/has space\.jpg does not exist}) + end + end + context 'when a record file value is blank' do before do allow(subject).to receive(:records).and_return([{ file: '' }, { file: nil }])