bulkrax 5.0.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/bulkrax/entries_controller.rb +4 -2
  3. data/app/controllers/bulkrax/exporters_controller.rb +13 -9
  4. data/app/controllers/bulkrax/importers_controller.rb +10 -10
  5. data/app/helpers/bulkrax/application_helper.rb +1 -1
  6. data/app/helpers/bulkrax/importers_helper.rb +2 -2
  7. data/app/helpers/bulkrax/validation_helper.rb +4 -4
  8. data/app/jobs/bulkrax/create_relationships_job.rb +78 -59
  9. data/app/jobs/bulkrax/delete_job.rb +1 -1
  10. data/app/jobs/bulkrax/export_work_job.rb +2 -2
  11. data/app/jobs/bulkrax/import_file_set_job.rb +1 -1
  12. data/app/jobs/bulkrax/import_work_job.rb +20 -7
  13. data/app/jobs/bulkrax/importer_job.rb +2 -2
  14. data/app/jobs/bulkrax/schedule_relationships_job.rb +2 -1
  15. data/app/matchers/bulkrax/application_matcher.rb +1 -0
  16. data/app/models/bulkrax/csv_entry.rb +93 -24
  17. data/app/models/bulkrax/exporter.rb +18 -19
  18. data/app/models/bulkrax/importer.rb +5 -5
  19. data/app/models/bulkrax/importer_run.rb +6 -0
  20. data/app/models/bulkrax/oai_entry.rb +14 -2
  21. data/app/models/bulkrax/pending_relationship.rb +4 -0
  22. data/app/models/concerns/bulkrax/dynamic_record_lookup.rb +3 -1
  23. data/app/models/concerns/bulkrax/export_behavior.rb +6 -4
  24. data/app/models/concerns/bulkrax/has_matchers.rb +1 -0
  25. data/app/models/concerns/bulkrax/import_behavior.rb +6 -3
  26. data/app/models/concerns/bulkrax/importer_exporter_behavior.rb +9 -1
  27. data/app/models/concerns/bulkrax/status_info.rb +9 -4
  28. data/app/parsers/bulkrax/application_parser.rb +14 -16
  29. data/app/parsers/bulkrax/bagit_parser.rb +6 -17
  30. data/app/parsers/bulkrax/csv_parser.rb +43 -111
  31. data/app/parsers/bulkrax/oai_dc_parser.rb +2 -2
  32. data/app/parsers/bulkrax/parser_export_record_set.rb +281 -0
  33. data/app/parsers/bulkrax/xml_parser.rb +9 -5
  34. data/app/services/bulkrax/remove_relationships_for_importer.rb +4 -2
  35. data/app/views/bulkrax/entries/show.html.erb +1 -1
  36. data/app/views/bulkrax/exporters/_form.html.erb +60 -45
  37. data/app/views/bulkrax/exporters/edit.html.erb +1 -1
  38. data/app/views/bulkrax/exporters/index.html.erb +2 -2
  39. data/app/views/bulkrax/exporters/new.html.erb +1 -1
  40. data/app/views/bulkrax/exporters/show.html.erb +3 -3
  41. data/app/views/bulkrax/importers/_bagit_fields.html.erb +13 -12
  42. data/app/views/bulkrax/importers/_csv_fields.html.erb +13 -12
  43. data/app/views/bulkrax/importers/_form.html.erb +5 -5
  44. data/app/views/bulkrax/importers/_oai_fields.html.erb +12 -10
  45. data/app/views/bulkrax/importers/_xml_fields.html.erb +12 -11
  46. data/app/views/bulkrax/importers/show.html.erb +18 -16
  47. data/app/views/bulkrax/shared/_collection_entries_tab.html.erb +6 -6
  48. data/app/views/bulkrax/shared/_file_set_entries_tab.html.erb +6 -6
  49. data/app/views/bulkrax/shared/_work_entries_tab.html.erb +6 -6
  50. data/config/locales/bulkrax.en.yml +26 -0
  51. data/lib/bulkrax/entry_spec_helper.rb +190 -0
  52. data/lib/bulkrax/version.rb +1 -1
  53. data/lib/bulkrax.rb +124 -45
  54. data/lib/generators/bulkrax/templates/config/initializers/bulkrax.rb +1 -1
  55. data/lib/tasks/reset.rake +1 -1
  56. metadata +5 -3
@@ -13,15 +13,49 @@ module Bulkrax
13
13
  data.headers.flatten.compact.uniq
14
14
  end
15
15
 
16
+ class_attribute(:csv_read_data_options, default: {})
17
+
16
18
  # there's a risk that this reads the whole file into memory and could cause a memory leak
17
19
  def self.read_data(path)
18
20
  raise StandardError, 'CSV path empty' if path.blank?
19
- CSV.read(path,
21
+ options = {
20
22
  headers: true,
21
23
  header_converters: ->(h) { h.to_sym },
22
- encoding: 'utf-8')
24
+ encoding: 'utf-8'
25
+ }.merge(csv_read_data_options)
26
+
27
+ results = CSV.read(path, **options)
28
+ csv_wrapper_class.new(results)
23
29
  end
24
30
 
31
+ # The purpose of this class is to reject empty lines. This causes lots of grief in importing.
32
+ # But why not use {CSV.read}'s `skip_lines` option? Because for some CSVs, it will never finish
33
+ # reading the file.
34
+ #
35
+ # There is a spec that demonstrates this approach works.
36
+ class CsvWrapper
37
+ include Enumerable
38
+ def initialize(original)
39
+ @original = original
40
+ end
41
+
42
+ delegate :headers, to: :@original
43
+
44
+ def each
45
+ @original.each do |row|
46
+ next if all_fields_are_empty_for(row: row)
47
+ yield(row)
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def all_fields_are_empty_for(row:)
54
+ row.to_hash.values.all?(&:blank?)
55
+ end
56
+ end
57
+ class_attribute :csv_wrapper_class, default: CsvWrapper
58
+
25
59
  def self.data_for_entry(data, _source_id, parser)
26
60
  # If a multi-line CSV data is passed, grab the first row
27
61
  data = data.first if data.is_a?(CSV::Table)
@@ -35,11 +69,7 @@ module Bulkrax
35
69
  end
36
70
 
37
71
  def build_metadata
38
- raise StandardError, 'Record not found' if record.nil?
39
- unless importerexporter.parser.required_elements?(keys_without_numbers(record.keys))
40
- raise StandardError,
41
- "Missing required elements, missing element(s) are: #{importerexporter.parser.missing_elements(keys_without_numbers(record.keys)).join(', ')}"
42
- end
72
+ validate_record
43
73
 
44
74
  self.parsed_metadata = {}
45
75
  add_identifier
@@ -56,6 +86,12 @@ module Bulkrax
56
86
  self.parsed_metadata
57
87
  end
58
88
 
89
+ def validate_record
90
+ raise StandardError, 'Record not found' if record.nil?
91
+ raise StandardError, "Missing required elements, missing element(s) are: "\
92
+ "#{importerexporter.parser.missing_elements(record).join(', ')}" unless importerexporter.parser.required_elements?(record)
93
+ end
94
+
59
95
  def add_identifier
60
96
  self.parsed_metadata[work_identifier] = [record[source_identifier]]
61
97
  end
@@ -67,9 +103,10 @@ module Bulkrax
67
103
  end
68
104
 
69
105
  def add_metadata_for_model
70
- if factory_class == Collection
71
- add_collection_type_gid
72
- elsif factory_class == FileSet
106
+ if defined?(::Collection) && factory_class == ::Collection
107
+ add_collection_type_gid if defined?(::Hyrax)
108
+ # add any additional collection metadata methods here
109
+ elsif factory_class == Bulkrax.file_model_class
73
110
  validate_presence_of_filename!
74
111
  add_path_to_file
75
112
  validate_presence_of_parent!
@@ -106,7 +143,7 @@ module Bulkrax
106
143
  self.parsed_metadata = {}
107
144
 
108
145
  build_system_metadata
109
- build_files_metadata unless hyrax_record.is_a?(Collection)
146
+ build_files_metadata if defined?(Collection) && !hyrax_record.is_a?(Collection)
110
147
  build_relationship_metadata
111
148
  build_mapping_metadata
112
149
  self.save!
@@ -157,26 +194,48 @@ module Bulkrax
157
194
  end
158
195
  end
159
196
 
197
+ # The purpose of this helper module is to make easier the testing of the rather complex
198
+ # switching logic for determining the method we use for building the value.
199
+ module AttributeBuilderMethod
200
+ # @param key [Symbol]
201
+ # @param value [Hash<String, Object>]
202
+ # @param entry [Bulkrax::Entry]
203
+ #
204
+ # @return [NilClass] when we won't be processing this field
205
+ # @return [Symbol] (either :build_value or :build_object)
206
+ def self.for(key:, value:, entry:)
207
+ return if key == 'model'
208
+ return if key == 'file'
209
+ return if key == entry.related_parents_parsed_mapping
210
+ return if key == entry.related_children_parsed_mapping
211
+ return if value['excluded'] || value[:excluded]
212
+ return if Bulkrax.reserved_properties.include?(key) && !entry.field_supported?(key)
213
+
214
+ object_key = key if value.key?('object') || value.key?(:object)
215
+ return unless entry.hyrax_record.respond_to?(key.to_s) || object_key.present?
216
+
217
+ models_to_skip = Array.wrap(value['skip_object_for_model_names'] || value[:skip_object_for_model_names] || [])
218
+
219
+ return :build_value if models_to_skip.detect { |model| entry.factory_class.model_name.name == model }
220
+ return :build_object if object_key.present?
221
+
222
+ :build_value
223
+ end
224
+ end
225
+
160
226
  def build_mapping_metadata
161
227
  mapping = fetch_field_mapping
162
228
  mapping.each do |key, value|
163
- # these keys are handled by other methods
164
- next if ['model', 'file', related_parents_parsed_mapping, related_children_parsed_mapping].include?(key)
165
- next if value['excluded']
166
- next if Bulkrax.reserved_properties.include?(key) && !field_supported?(key)
167
-
168
- object_key = key if value.key?('object')
169
- next unless hyrax_record.respond_to?(key.to_s) || object_key.present?
229
+ method_name = AttributeBuilderMethod.for(key: key, value: value, entry: self)
230
+ next unless method_name
170
231
 
171
- if object_key.present?
172
- build_object(value)
173
- else
174
- build_value(key, value)
175
- end
232
+ send(method_name, key, value)
176
233
  end
177
234
  end
178
235
 
179
- def build_object(value)
236
+ def build_object(_key, value)
237
+ return unless hyrax_record.respond_to?(value['object'])
238
+
180
239
  data = hyrax_record.send(value['object'])
181
240
  return if data.empty?
182
241
 
@@ -185,6 +244,8 @@ module Bulkrax
185
244
  end
186
245
 
187
246
  def build_value(key, value)
247
+ return unless hyrax_record.respond_to?(key.to_s)
248
+
188
249
  data = hyrax_record.send(key.to_s)
189
250
  if data.is_a?(ActiveTriples::Relation)
190
251
  if value['join']
@@ -217,6 +278,14 @@ module Bulkrax
217
278
  end
218
279
 
219
280
  def object_metadata(data)
281
+ # NOTE: What is `d` in this case:
282
+ #
283
+ # "[{\"single_object_first_name\"=>\"Fake\", \"single_object_last_name\"=>\"Fakerson\", \"single_object_position\"=>\"Leader, Jester, Queen\", \"single_object_language\"=>\"english\"}]"
284
+ #
285
+ # The above is a stringified version of a Ruby string. Using eval is a very bad idea as it
286
+ # will execute the value of `d` within the full Ruby interpreter context.
287
+ #
288
+ # TODO: Would it be possible to store this as a non-string? Maybe the actual Ruby Array and Hash?
220
289
  data = data.map { |d| eval(d) }.flatten # rubocop:disable Security/Eval
221
290
 
222
291
  data.each_with_index do |obj, index|
@@ -18,18 +18,9 @@ module Bulkrax
18
18
 
19
19
  def export
20
20
  current_run && setup_export_path
21
- case self.export_from
22
- when 'collection'
23
- create_from_collection
24
- when 'importer'
25
- create_from_importer
26
- when 'worktype'
27
- create_from_worktype
28
- when 'all'
29
- create_from_all
30
- end
21
+ send("create_from_#{self.export_from}")
31
22
  rescue StandardError => e
32
- status_info(e)
23
+ set_status_info(e)
33
24
  end
34
25
 
35
26
  # #export_source accessors
@@ -69,7 +60,7 @@ module Bulkrax
69
60
  end
70
61
 
71
62
  def workflow_status_list
72
- Sipity::WorkflowState.all.map { |s| [s.name&.titleize, s.name] }.uniq
63
+ Sipity::WorkflowState.all.map { |s| [s.name&.titleize, s.name] }.uniq if defined?(::Hyrax)
73
64
  end
74
65
 
75
66
  # If field_mapping is empty, setup a default based on the export_properties
@@ -84,12 +75,20 @@ module Bulkrax
84
75
  end
85
76
 
86
77
  def export_from_list
87
- [
88
- [I18n.t('bulkrax.exporter.labels.importer'), 'importer'],
89
- [I18n.t('bulkrax.exporter.labels.collection'), 'collection'],
90
- [I18n.t('bulkrax.exporter.labels.worktype'), 'worktype'],
91
- [I18n.t('bulkrax.exporter.labels.all'), 'all']
92
- ]
78
+ if defined?(::Hyrax)
79
+ [
80
+ [I18n.t('bulkrax.exporter.labels.importer'), 'importer'],
81
+ [I18n.t('bulkrax.exporter.labels.collection'), 'collection'],
82
+ [I18n.t('bulkrax.exporter.labels.worktype'), 'worktype'],
83
+ [I18n.t('bulkrax.exporter.labels.all'), 'all']
84
+ ]
85
+ else
86
+ [
87
+ [I18n.t('bulkrax.exporter.labels.importer'), 'importer'],
88
+ [I18n.t('bulkrax.exporter.labels.collection'), 'collection'],
89
+ [I18n.t('bulkrax.exporter.labels.all'), 'all']
90
+ ]
91
+ end
93
92
  end
94
93
 
95
94
  def export_type_list
@@ -131,7 +130,7 @@ module Bulkrax
131
130
  end
132
131
 
133
132
  def export_properties
134
- properties = Hyrax.config.registered_curation_concern_types.map { |work| work.constantize.properties.keys }.flatten.uniq.sort
133
+ properties = Bulkrax.curation_concerns.map { |work| work.properties.keys }.flatten.uniq.sort
135
134
  properties.reject { |prop| Bulkrax.reserved_properties.include?(prop) }
136
135
  end
137
136
 
@@ -15,7 +15,7 @@ module Bulkrax
15
15
  has_many :entries, as: :importerexporter, dependent: :destroy
16
16
 
17
17
  validates :name, presence: true
18
- validates :admin_set_id, presence: true
18
+ validates :admin_set_id, presence: true if defined?(::Hyrax)
19
19
  validates :parser_klass, presence: true
20
20
 
21
21
  delegate :valid_import?, :write_errored_entries_file, :visibility, to: :parser
@@ -47,12 +47,12 @@ module Bulkrax
47
47
  if importer_run.failed_records.positive?
48
48
  if importer_run.invalid_records.present?
49
49
  e = Bulkrax::ImportFailed.new('Failed with Invalid Records', importer_run.invalid_records.split("\n"))
50
- importer_run.importer.status_info(e)
50
+ importer_run.importer.set_status_info(e)
51
51
  else
52
- importer_run.importer.status_info('Complete (with failures)')
52
+ importer_run.importer.set_status_info('Complete (with failures)')
53
53
  end
54
54
  else
55
- importer_run.importer.status_info('Complete')
55
+ importer_run.importer.set_status_info('Complete')
56
56
  end
57
57
  end
58
58
 
@@ -160,7 +160,7 @@ module Bulkrax
160
160
  types = types_array || DEFAULT_OBJECT_TYPES
161
161
  parser.create_objects(types)
162
162
  rescue StandardError => e
163
- status_info(e)
163
+ set_status_info(e)
164
164
  end
165
165
 
166
166
  # Prepend the base_url to ensure unique set identifiers
@@ -9,5 +9,11 @@ module Bulkrax
9
9
  def parents
10
10
  pending_relationships.pluck(:parent_id).uniq
11
11
  end
12
+
13
+ def user
14
+ # An importer might not have a user, the CLI ingest need not assign a user. As such, we
15
+ # fallback to the configured user.
16
+ importer.user || Bulkrax.fallback_user_for_importer_exporter_processing
17
+ end
12
18
  end
13
19
  end
@@ -9,6 +9,15 @@ module Bulkrax
9
9
 
10
10
  delegate :record, to: :raw_record
11
11
 
12
+ # @api private
13
+ #
14
+ # Included to assist in testing; namely so that you can copy down an OAI entry, store it locally,
15
+ # and then manually construct an {OAI::GetRecordResponse}.
16
+ #
17
+ # @see Bulkrax::EntrySpecHelper.oai_entry_for
18
+ attr_writer :raw_record
19
+
20
+ # @return [OAI::GetRecordResponse]
12
21
  def raw_record
13
22
  @raw_record ||= client.get_record(identifier: identifier, metadata_prefix: parser.parser_fields['metadata_prefix'])
14
23
  end
@@ -28,7 +37,7 @@ module Bulkrax
28
37
  def build_metadata
29
38
  self.parsed_metadata = {}
30
39
  self.parsed_metadata[work_identifier] = [record.header.identifier]
31
- self.raw_metadata = { xml: record.metadata.to_s }
40
+ self.raw_metadata = { record: record.metadata.to_s, header: record.header.to_s }
32
41
 
33
42
  # We need to establish the #factory_class before we proceed with the metadata. See
34
43
  # https://github.com/samvera-labs/bulkrax/issues/702 for further details.
@@ -91,7 +100,8 @@ module Bulkrax
91
100
  # If OAI-PMH doesn't return setSpec in the headers for GetRecord, use parser.collection_name
92
101
  # in this case, if 'All' is selected, records will not be added to a collection.
93
102
  def find_collection_ids
94
- return self.collection_ids if collections_created?
103
+ return self.collection_ids if defined?(@called_find_collection_ids)
104
+
95
105
  if sets.blank? || parser.collection_name != 'all'
96
106
  collection = find_collection(importerexporter.unique_collection_identifier(parser.collection_name))
97
107
  self.collection_ids << collection.id if collection.present? && !self.collection_ids.include?(collection.id)
@@ -101,6 +111,8 @@ module Bulkrax
101
111
  self.collection_ids << c.id if c.present? && !self.collection_ids.include?(c.id)
102
112
  end
103
113
  end
114
+
115
+ @called_find_collection_ids = true
104
116
  return self.collection_ids
105
117
  end
106
118
  end
@@ -3,5 +3,9 @@
3
3
  module Bulkrax
4
4
  class PendingRelationship < ApplicationRecord
5
5
  belongs_to :importer_run
6
+
7
+ # Ideally we wouldn't have a column named "order", as it is a reserved SQL term. However, if we
8
+ # quote the column, all is well...for the application.
9
+ scope :ordered, -> { order(Arel.sql("#{quoted_table_name}.#{connection.quote_column_name('order')}")) }
6
10
  end
7
11
  end
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bulkrax
4
+ # TODO: Extract methods to class methods; there's no reason for these methods to be a mixin.
5
+ # TODO: Add specs to test in isolation
4
6
  module DynamicRecordLookup
5
7
  # Search entries, collections, and every available work type for a record that
6
8
  # has the provided identifier.
@@ -40,7 +42,7 @@ module Bulkrax
40
42
  @available_work_types ||= if defined?(::Hyku)
41
43
  ::Site.instance.available_works.map(&:constantize)
42
44
  else
43
- ::Hyrax.config.curation_concerns
45
+ Bulkrax.curation_concerns
44
46
  end
45
47
  end
46
48
  end
@@ -1,4 +1,6 @@
1
1
  # frozen_string_literal: true
2
+ require 'marcel'
3
+
2
4
  module Bulkrax
3
5
  module ExportBehavior
4
6
  extend ActiveSupport::Concern
@@ -10,9 +12,9 @@ module Bulkrax
10
12
  rescue RSolr::Error::Http, CollectionsCreatedError => e
11
13
  raise e
12
14
  rescue StandardError => e
13
- status_info(e)
15
+ set_status_info(e)
14
16
  else
15
- status_info
17
+ set_status_info
16
18
  end
17
19
 
18
20
  def build_export_metadata
@@ -27,8 +29,8 @@ module Bulkrax
27
29
  def filename(file_set)
28
30
  return if file_set.original_file.blank?
29
31
  fn = file_set.original_file.file_name.first
30
- mime = Mime::Type.lookup(file_set.original_file.mime_type)
31
- ext_mime = MIME::Types.of(file_set.original_file.file_name).first
32
+ mime = ::Marcel::MimeType.for(file_set.original_file.mime_type)
33
+ ext_mime = ::Marcel::MimeType.for(file_set.original_file.file_name)
32
34
  if fn.include?(file_set.id) || importerexporter.metadata_only?
33
35
  filename = "#{fn}.#{mime.to_sym}"
34
36
  filename = fn if mime.to_s == ext_mime.to_s
@@ -147,6 +147,7 @@ module Bulkrax
147
147
  %W[
148
148
  file
149
149
  remote_files
150
+ rights_statement
150
151
  #{related_parents_parsed_mapping}
151
152
  #{related_children_parsed_mapping}
152
153
  ]
@@ -11,16 +11,16 @@ module Bulkrax
11
11
  unless self.importerexporter.validate_only
12
12
  raise CollectionsCreatedError unless collections_created?
13
13
  @item = factory.run!
14
- add_user_to_permission_templates! if self.class.to_s.include?("Collection")
14
+ add_user_to_permission_templates! if self.class.to_s.include?("Collection") && defined?(::Hyrax)
15
15
  parent_jobs if self.parsed_metadata[related_parents_parsed_mapping]&.join.present?
16
16
  child_jobs if self.parsed_metadata[related_children_parsed_mapping]&.join.present?
17
17
  end
18
18
  rescue RSolr::Error::Http, CollectionsCreatedError => e
19
19
  raise e
20
20
  rescue StandardError => e
21
- status_info(e)
21
+ set_status_info(e)
22
22
  else
23
- status_info
23
+ set_status_info
24
24
  ensure
25
25
  self.save!
26
26
  end
@@ -93,6 +93,8 @@ module Bulkrax
93
93
  end
94
94
 
95
95
  def add_admin_set_id
96
+ return unless defined?(::Hyrax)
97
+
96
98
  self.parsed_metadata['admin_set_id'] = importerexporter.admin_set_id if self.parsed_metadata['admin_set_id'].blank?
97
99
  end
98
100
 
@@ -165,6 +167,7 @@ module Bulkrax
165
167
  # @param field [String] name of the controlled property
166
168
  # @return [Boolean] provided value is a present, active authority ID for the provided field
167
169
  def active_id_for_authority?(value, field)
170
+ return false unless defined?(::Hyrax)
168
171
  field_service = ('Hyrax::' + "#{field}_service".camelcase).constantize
169
172
  active_authority_ids = field_service.new.active_elements.map { |ae| ae['id'] }
170
173
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'marcel'
2
3
 
3
4
  module Bulkrax
4
5
  module ImporterExporterBehavior
@@ -50,7 +51,14 @@ module Bulkrax
50
51
 
51
52
  # Is this a zip file?
52
53
  def zip?
53
- parser_fields&.[]('import_file_path') && MIME::Types.type_for(parser_fields['import_file_path']).include?('application/zip')
54
+ filename = parser_fields&.[]('import_file_path')
55
+ return false unless filename
56
+ return false unless File.file?(filename)
57
+ returning_value = false
58
+ File.open(filename) do |file|
59
+ returning_value = ::Marcel::MimeType.for(file).include?('application/zip')
60
+ end
61
+ returning_value
54
62
  end
55
63
  end
56
64
  end
@@ -33,16 +33,21 @@ module Bulkrax
33
33
  current_status&.created_at
34
34
  end
35
35
 
36
- def status_info(e = nil, current_run = nil)
36
+ def set_status_info(e = nil, current_run = nil)
37
+ runnable = current_run || last_run
37
38
  if e.nil?
38
- self.statuses.create!(status_message: 'Complete', runnable: current_run || last_run)
39
+ self.statuses.create!(status_message: 'Complete', runnable: runnable)
39
40
  elsif e.is_a?(String)
40
- self.statuses.create!(status_message: e, runnable: current_run || last_run)
41
+ self.statuses.create!(status_message: e, runnable: runnable)
41
42
  else
42
- self.statuses.create!(status_message: 'Failed', runnable: current_run || last_run, error_class: e.class.to_s, error_message: e.message, error_backtrace: e.backtrace)
43
+ self.statuses.create!(status_message: 'Failed', runnable: runnable, error_class: e.class.to_s, error_message: e.message, error_backtrace: e.backtrace)
43
44
  end
44
45
  end
45
46
 
47
+ alias status_info set_status_info
48
+
49
+ deprecation_deprecate status_info: "Favor Bulkrax::StatusInfo.set_status_info. We will be removing .status_info in Bulkrax v6.0.0"
50
+
46
51
  # api compatible with previous error structure
47
52
  def last_error
48
53
  return unless current_status && current_status.error_class.present?
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require 'zip'
3
+ require 'marcel'
3
4
 
4
5
  module Bulkrax
5
6
  # An abstract class that establishes the API for Bulkrax's import and export parsing.
@@ -10,10 +11,11 @@ module Bulkrax
10
11
  alias importer importerexporter
11
12
  alias exporter importerexporter
12
13
  delegate :only_updates, :limit, :current_run, :errors, :mapping,
13
- :seen, :increment_counters, :parser_fields, :user, :keys_without_numbers,
14
- :key_without_numbers, :status, :status_info, :status_at,
15
- :exporter_export_path, :exporter_export_zip_path, :importer_unzip_path, :validate_only,
16
- to: :importerexporter
14
+ :seen, :increment_counters, :parser_fields, :user, :keys_without_numbers,
15
+ :key_without_numbers, :status, :set_status_info, :status_info, :status_at,
16
+ :exporter_export_path, :exporter_export_zip_path, :importer_unzip_path, :validate_only,
17
+ :zip?, :file?,
18
+ to: :importerexporter
17
19
 
18
20
  # @todo Convert to `class_attribute :parser_fiels, default: {}`
19
21
  def self.parser_fields
@@ -275,10 +277,16 @@ module Bulkrax
275
277
 
276
278
  # @return [Array<String>]
277
279
  def required_elements
280
+ matched_elements = ((importerexporter.mapping.keys || []) & (Bulkrax.required_elements || []))
281
+ unless matched_elements.count == Bulkrax.required_elements.count
282
+ missing_elements = Bulkrax.required_elements - matched_elements
283
+ error_alert = "Missing mapping for at least one required element, missing mappings are: #{missing_elements.join(', ')}"
284
+ raise StandardError, error_alert
285
+ end
278
286
  if Bulkrax.fill_in_blank_source_identifiers
279
- ['title']
287
+ Bulkrax.required_elements
280
288
  else
281
- ['title', source_identifier]
289
+ Bulkrax.required_elements + [source_identifier]
282
290
  end
283
291
  end
284
292
 
@@ -351,16 +359,6 @@ module Bulkrax
351
359
  end
352
360
  end
353
361
 
354
- # Is this a file?
355
- def file?
356
- parser_fields&.[]('import_file_path') && File.file?(parser_fields['import_file_path'])
357
- end
358
-
359
- # Is this a zip file?
360
- def zip?
361
- parser_fields&.[]('import_file_path') && MIME::Types.type_for(parser_fields['import_file_path']).include?('application/zip')
362
- end
363
-
364
362
  # Path for the import
365
363
  # @return [String]
366
364
  def import_file_path
@@ -11,7 +11,7 @@ module Bulkrax
11
11
  def valid_import?
12
12
  return true if import_fields.present?
13
13
  rescue => e
14
- status_info(e)
14
+ set_status_info(e)
15
15
  false
16
16
  end
17
17
 
@@ -51,7 +51,7 @@ module Bulkrax
51
51
  record_data = entry_class.data_for_entry(data_row, source_identifier, self)
52
52
  next record_data if importerexporter.metadata_only?
53
53
 
54
- record_data[:file] = bag.bag_files.join('|') if ::Hyrax.config.curation_concerns.include? record_data[:model]&.constantize
54
+ record_data[:file] = bag.bag_files.join('|') if Bulkrax.curation_concerns.include? record_data[:model]&.constantize
55
55
  record_data
56
56
  end
57
57
  else
@@ -82,19 +82,7 @@ module Bulkrax
82
82
  end
83
83
  importer.record_status
84
84
  rescue StandardError => e
85
- status_info(e)
86
- end
87
-
88
- def total
89
- @total = importer.parser_fields['total'] || 0 if importer?
90
-
91
- @total = if exporter?
92
- limit.nil? || limit.zero? ? current_record_ids.count : limit
93
- end
94
-
95
- return @total || 0
96
- rescue StandardError
97
- @total = 0
85
+ set_status_info(e)
98
86
  end
99
87
 
100
88
  # export methods
@@ -143,8 +131,8 @@ module Bulkrax
143
131
  begin
144
132
  bag.add_file(file_name, file.path) if bag.bag_files.select { |b| b.include?(file_name) }.blank?
145
133
  rescue => e
146
- entry.status_info(e)
147
- status_info(e)
134
+ entry.set_status_info(e)
135
+ set_status_info(e)
148
136
  end
149
137
  end
150
138
 
@@ -185,6 +173,7 @@ module Bulkrax
185
173
  File.join(path, id)
186
174
  end
187
175
 
176
+ # @todo(bjustice) - remove hyrax reference
188
177
  def write_triples(folder_count, e)
189
178
  sd = SolrDocument.find(e.identifier)
190
179
  return if sd.nil?