bulkrax 5.0.0 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
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?