diff --git a/app/controllers/concerns/bulkrax/datatables_behavior.rb b/app/controllers/concerns/bulkrax/datatables_behavior.rb index 570ac18db..cbe50bfe6 100644 --- a/app/controllers/concerns/bulkrax/datatables_behavior.rb +++ b/app/controllers/concerns/bulkrax/datatables_behavior.rb @@ -143,6 +143,26 @@ def format_entries(entries, item) } end + def format_statuses(statuses, item) + result = statuses.map do |status| + { + identifier: view_context.link_to(status.id, importer_status_path(item, status)), + id: status.id, + status_message: status.status_message, + error_class: status.error_class, + created_at: status.created_at, + updated_at: status.updated_at, + runnable_id: view_context.link_to(status.runnable_id, importer_path(item)), + actions: status_util_links(status, item) + } + end + { + data: result, + recordsTotal: statuses.size, + recordsFiltered: statuses.size + } + end + def entry_util_links(e, item) links = [] links << view_context.link_to(view_context.raw(''), view_context.item_entry_path(item, e)) @@ -151,6 +171,13 @@ def entry_util_links(e, item) links.join(" ") end + def status_util_links(status, item) + links = [] + links << view_context.link_to(view_context.raw(''), importer_status_path(item, status)) + links << "" if view_context.an_importer?(item) + links.join(" ") + end + def status_message_for(e) if e.status_message == "Complete" " #{e.status_message}" diff --git a/app/models/bulkrax/csv_entry.rb b/app/models/bulkrax/csv_entry.rb index c51a64d99..b56f391bf 100644 --- a/app/models/bulkrax/csv_entry.rb +++ b/app/models/bulkrax/csv_entry.rb @@ -83,6 +83,10 @@ def build_metadata add_local self.parsed_metadata + rescue => e + self.save! + self.set_status_info(e, self) + raise e end # limited metadata is needed for delete jobs diff --git a/config/routes.rb b/config/routes.rb index c4dfc4c6b..286ea5343 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,6 +8,7 @@ get :exporter_table end resources :entries, only: %i[show update destroy] + resources :statuses, only: :show end resources :importers do put :continue @@ -19,6 +20,7 @@ post :external_sets end resources :entries, only: %i[show update destroy] + resources :statuses, only: :show get :upload_corrected_entries post :upload_corrected_entries_file end diff --git a/spec/controllers/concerns/bulkrax/datatables_behavior_spec.rb b/spec/controllers/concerns/bulkrax/datatables_behavior_spec.rb index abfca8849..843d3c5f5 100644 --- a/spec/controllers/concerns/bulkrax/datatables_behavior_spec.rb +++ b/spec/controllers/concerns/bulkrax/datatables_behavior_spec.rb @@ -95,6 +95,33 @@ def current_user end end + describe '#format_statuses' do + let(:item) { FactoryBot.create(:bulkrax_importer) } + let(:entry_1) { FactoryBot.create(:bulkrax_entry, importerexporter: item) } + let(:entry_2) { FactoryBot.create(:bulkrax_entry, importerexporter: item) } + let(:entries) { [entry_1, entry_2] } + let(:status_1) { FactoryBot.create(:bulkrax_status) } + let(:status_2) { FactoryBot.create(:bulkrax_status) } + let(:status_3) { FactoryBot.create(:bulkrax_status) } + let(:statuses) { [status_1, status_2, status_3] } + + it 'returns a hash with the correct structure' do + get :index + result = controller.format_statuses(statuses, item) + expect(result).to be_a(Hash) + expect(result.keys).to contain_exactly(:data, :recordsTotal, :recordsFiltered) + expect(result[:data]).to be_a(Array) + expect(result[:data].first.keys).to contain_exactly(:identifier, :id, :status_message, :updated_at, :error_class, :actions, :created_at, :runnable_id) + end + + it 'returns the correct number of statuses' do + get :index + result = controller.format_statuses(statuses, item) + expect(result[:recordsTotal]).to eq(statuses.size) + expect(result[:recordsFiltered]).to eq(statuses.size) + end + end + describe '#entry_util_links' do include Bulkrax::Engine.routes.url_helpers @@ -124,6 +151,28 @@ def current_user end end + describe '#status_util_links' do + include Bulkrax::Engine.routes.url_helpers + + let(:item) { FactoryBot.create(:bulkrax_importer) } + let(:entry) { FactoryBot.create(:bulkrax_entry, importerexporter: item) } + let(:status) { FactoryBot.create(:bulkrax_status) } + + it 'returns a string of HTML links' do + get :index + result = controller.status_util_links(status, item) + expect(result).to be_a(String) + expect(result).to include('fa-info-circle') + expect(result).to include('fa-repeat') + end + + it 'includes a link to the status' do + get :index + result = controller.status_util_links(status, item) + expect(result).to include(importer_status_path(item, status)) + end + end + describe '#status_message_for' do let(:item) { FactoryBot.create(:bulkrax_importer) } diff --git a/spec/models/bulkrax/csv_entry_spec.rb b/spec/models/bulkrax/csv_entry_spec.rb index 70331a5e7..b345a80d2 100644 --- a/spec/models/bulkrax/csv_entry_spec.rb +++ b/spec/models/bulkrax/csv_entry_spec.rb @@ -118,7 +118,14 @@ class ::Avocado < Work end it 'fails and stores an error' do + expect(subject.status_message).to eq 'Pending' + expect(subject.error_class).to eq nil expect { subject.build_metadata }.to raise_error(StandardError) + expect(subject.status_message).to eq 'Failed' + expect(subject.statuses[0].status_message).to eq 'Failed' + expect(subject.statuses[0].error_message).to eq 'Missing required elements, missing element(s) are: title' + expect(subject.statuses[0].error_backtrace.nil?).to eq false + expect(subject.error_class).to eq 'StandardError' end end @@ -227,8 +234,12 @@ class ::Avocado < Work end it 'raises a StandardError' do + expect(subject.status_message).to eq 'Pending' + expect(subject.error_class).to eq nil expect { subject.build_metadata } .to raise_error(StandardError, %("hello world" is not a valid and/or active authority ID for the :rights_statement field)) + expect(subject.status_message).to eq 'Failed' + expect(subject.error_class).to eq 'StandardError' end end end @@ -282,8 +293,12 @@ class ::Avocado < Work end it 'raises a StandardError' do + expect(subject.status_message).to eq 'Pending' + expect(subject.error_class).to eq nil expect { subject.build_metadata } .to raise_error(StandardError, %("hello world" is not a valid and/or active authority ID for the :license field)) + expect(subject.status_message).to eq 'Failed' + expect(subject.error_class).to eq 'StandardError' end end end diff --git a/spec/models/bulkrax/status_spec.rb b/spec/models/bulkrax/status_spec.rb index f4dff9655..a306845b7 100644 --- a/spec/models/bulkrax/status_spec.rb +++ b/spec/models/bulkrax/status_spec.rb @@ -4,6 +4,99 @@ module Bulkrax RSpec.describe Status, type: :model do - pending "add some examples to (or delete) #{__FILE__}" + context 'for exporters' do + context 'without errors' do + let(:exporter_without_errors) { FactoryBot.create(:bulkrax_exporter) } + it 'can display the current status' do + exporter_without_errors.save + expect(exporter_without_errors.statuses.count).to eq 0 + expect(exporter_without_errors.last_error_at).to eq nil + expect(exporter_without_errors.error_class).to eq nil + expect(exporter_without_errors.status).to eq 'Pending' + end + end + context 'with errors' do + end + end + context 'for importers' do + context 'without errors' do + let(:importer_without_errors) { FactoryBot.create(:bulkrax_importer) } + it 'can display the current status' do + importer_without_errors.save + expect(importer_without_errors.statuses.count).to eq 0 + expect(importer_without_errors.last_error_at).to eq nil + expect(importer_without_errors.error_class).to eq nil + expect(importer_without_errors.status).to eq 'Pending' + end + end + context 'with errors' do + end + end + context 'for csv entries' do + let(:importer) { FactoryBot.create(:bulkrax_importer_csv) } + let(:entry_without_errors) { FactoryBot.create(:bulkrax_csv_entry) } + let(:entry_with_errors) { Bulkrax::CsvEntry.new(importerexporter: importer) } + let(:collection) { FactoryBot.build(:collection) } + let(:hyrax_record) do + OpenStruct.new( + file_sets: [], + member_of_collections: [], + member_of_work_ids: [], + in_work_ids: [], + member_work_ids: [] + ) + end + before do + allow(entry_with_errors).to receive(:hyrax_record).and_return(hyrax_record) + end + context 'without errors' do + it 'can display the current status' do + entry_without_errors.save + expect(entry_without_errors.statuses.count).to eq 0 + expect(entry_without_errors.last_error_at).to eq nil + expect(entry_without_errors.error_class).to eq nil + expect(entry_without_errors.status).to eq 'Pending' + end + end + context 'with errors' do + context 'for missing metadata' do + before do + allow_any_instance_of(Bulkrax::CsvEntry).to receive(:collections_created?).and_return(true) + allow_any_instance_of(Bulkrax::CsvEntry).to receive(:find_collection).and_return(collection) + allow(entry_with_errors).to receive(:raw_metadata).and_return('source_identifier' => '1', 'some_field' => 'some data') + end + it 'can display a history' do + expect { entry_with_errors.build_metadata }.to raise_error(StandardError) + expect(entry_with_errors.statuses.count).to eq 1 + expect(entry_with_errors.status_message).to eq 'Failed' + expect(entry_with_errors.statuses[0].status_message).to eq 'Failed' + expect(entry_with_errors.statuses[0].error_message).to eq 'Missing required elements, missing element(s) are: title' + expect(entry_with_errors.statuses[0].error_backtrace.nil?).to eq false + expect(entry_with_errors.error_class).to eq 'StandardError' + expect { entry_with_errors.build_metadata }.to raise_error(StandardError) + expect(entry_with_errors.statuses.count).to eq 2 + end + end + context 'for missing collection' do + before do + allow_any_instance_of(Bulkrax::CsvEntry).to receive(:collections_created?).and_return(false) + allow_any_instance_of(Bulkrax::CsvEntry).to receive(:find_collection).and_return(nil) + allow(entry_with_errors).to receive(:raw_metadata).and_return('source_identifier' => '1', 'some_field' => 'some data', 'title' => 'Missing Collection Example') + end + it 'can display a history' do + pending("Making this test make sense") + expect { entry_with_errors.build_metadata }.to raise_error(CollectionsCreatedError) + expect(entry_with_errors.statuses.count).to eq 1 + expect(entry_with_errors.status_message).to eq 'Failed' + expect(entry_with_errors.statuses[0].status_message).to eq 'Failed' + expect(entry_with_errors.statuses[0].error_message).to eq 'Missing required elements, missing element(s) are: title' + expect(entry_with_errors.statuses[0].error_backtrace.nil?).to eq false + expect(entry_with_errors.error_class).to eq 'StandardError' + expect { entry_with_errors.build_metadata }.to raise_error(StandardError) + expect(entry_with_errors.statuses.count).to eq 2 + end + end + end + end end end