<% if File.exist?(@exporter.exporter_export_zip_path) %>
<%= simple_form_for @exporter, method: :get, url: exporter_download_path(@exporter), html: { class: 'form-inline bulkrax-p-align' } do |form| %>
@@ -13,69 +13,45 @@
<%= render 'downloads', exporter: @exporter, form: form %>
<%= form.button :submit, value: 'Download', data: { disable_with: false } %>
<% end %>
+ <% else %>
+
+ No items matched this export’s criteria, so no files were generated.
+
<% end %>
-
- <%= t('bulkrax.exporter.labels.name') %>:
- <%= @exporter.name %>
-
-
-
- <%= t('bulkrax.exporter.labels.user') %>:
- <%= @exporter.user %>
-
-
-
- <%= t('bulkrax.exporter.labels.export_type') %>:
- <%= @exporter.export_type %>
-
-
-
- <%= t('bulkrax.exporter.labels.export_from') %>:
- <%= @exporter.export_from %>
-
+
<%= t('bulkrax.exporter.labels.name') %>: <%= @exporter.name %>
+
<%= t('bulkrax.exporter.labels.user') %>: <%= @exporter.user %>
+
<%= t('bulkrax.exporter.labels.export_type') %>: <%= @exporter.export_type %>
+
<%= t('bulkrax.exporter.labels.export_from') %>: <%= @exporter.export_from %>
<%= t('bulkrax.exporter.labels.export_source') %>:
<% case @exporter.export_from %>
<% when 'collection' %>
- <% collection = Collection.find(@exporter.export_source) %>
- <%= link_to collection&.title&.first, hyrax.dashboard_collection_path(collection.id) %>
+ <% collection = Bulkrax.object_factory.find_or_nil(@exporter.export_source) %>
+ <% id = collection&.id || @exporter.export_source %>
+ <% title = collection&.title&.first || @exporter.export_source %>
+ <%= link_to title, hyrax.dashboard_collection_path(id) %>
<% when 'importer' %>
- <% importer = Bulkrax::Importer.find(@exporter.export_source) %>
- <%= link_to importer.name, bulkrax.importer_path(importer.id) %>
+ <% importer = Bulkrax::Importer.find(@exporter.export_source) rescue nil %>
+ <% if importer %>
+ <%= link_to importer.name, importer_path(importer.id) %>
+ <% else %>
+ <%= @exporter.export_source %>
+ <% end %>
<% when 'worktype' %>
<%= @exporter.export_source %>
<% end %>
-
- <%= t('bulkrax.exporter.labels.parser_klass') %>:
- <%= @exporter.parser_klass %>
-
+
<%= t('bulkrax.exporter.labels.parser_klass') %>: <%= @exporter.parser_klass %>
+
<%= t('bulkrax.exporter.labels.limit') %>: <%= @exporter.limit %>
+
<%= t('bulkrax.exporter.labels.generated_metadata') %>: <%= @exporter.generated_metadata %>
+
<%= t('bulkrax.exporter.labels.include_thumbnails') %>: <%= @exporter.include_thumbnails %>
-
- <%= t('bulkrax.exporter.labels.limit') %>:
- <%= @exporter.limit %>
-
+ <%= render partial: 'bulkrax/shared/bulkrax_errors', locals: { item: @exporter } %>
+ <%= render partial: 'bulkrax/shared/bulkrax_field_mapping', locals: { item: @exporter } %>
-
- <%= t('bulkrax.exporter.labels.generated_metadata') %>:
- <%= @exporter.generated_metadata %>
-
-
-
- <%= t('bulkrax.exporter.labels.include_thumbnails') %>:
- <%= @exporter.include_thumbnails %>
-
-
-
- <%= render partial: 'bulkrax/shared/bulkrax_errors', locals: {item: @exporter} %>
-
- <%= render partial: 'bulkrax/shared/bulkrax_field_mapping', locals: {item: @exporter} %>
-
- <%# Currently, no parser-specific fields exist on Exporter,
- thus there's no real reason to always show this field %>
<% if @exporter.parser_fields.present? %>
<%= t('bulkrax.exporter.labels.parser_fields') %>:
@@ -85,30 +61,19 @@
<% end %>
-
<%= t('bulkrax.exporter.labels.field_mapping') %>:
-
-
+
Total Entries:
<%= @exporter.last_run&.total_work_entries %>
-
-
-
-
- <%= render partial: 'bulkrax/shared/work_entries_tab', locals: { item: @exporter, entries: @work_entries } %>
- <%= render partial: 'bulkrax/shared/collection_entries_tab', locals: { item: @exporter, entries: @collection_entries } %>
- <%= render partial: 'bulkrax/shared/file_set_entries_tab', locals: { item: @exporter, entries: @file_set_entries } %>
+
+ <%= render partial: 'bulkrax/shared/entries_tab', locals: { item: @exporter } %>
- <%= link_to 'Edit', edit_exporter_path(@exporter) %>
- |
+ <%= link_to 'Edit', edit_exporter_path(@exporter) %> |
<%= link_to 'Back', exporters_path %>
diff --git a/bin/importer b/bin/importer
index 5fbf7fd9a..c7041315e 100644
--- a/bin/importer
+++ b/bin/importer
@@ -2,145 +2,164 @@
# frozen_string_literal: true
require_relative '../config/environment'
-
require 'slop'
-
-def main(opts = {})
- check_required_params
-
- update = opts[:importer_id].present?
- port = opts[:port].presence
- url = build_url(opts.delete(:importer_id), opts.delete(:url), port)
-
- headers = { 'Content-Type' => 'application/json' }
- headers['Authorization'] = "Token: #{opts.delete(:auth_token)}"
- params = build_params(opts)
-
- logger.info("POST to #{url} - PARAMS #{params}")
-
- conn = Faraday.new(
- url: url,
- headers: headers
- )
+require 'faraday'
+require 'json'
+
+# -----------------------
+# Stable option interface
+# -----------------------
+# We avoid Bulkrax.api_definition to stay compatible across versions.
+# If your app requires different fields, add/remove them here.
+
+REQUIRED_CREATE_KEYS = %i[
+ name
+ parser_klass
+ user_id
+ admin_set_id
+ import_file_path
+].freeze
+
+# parser_fields you support
+PARSER_FIELD_KEYS = %i[
+ metadata_file_name
+ metadata_format
+ rights_statement
+ override_rights_statement
+ import_file_path
+ metadata_prefix
+ set
+ collection_name
+].freeze
+
+def main(opts)
+ check_required_params(opts)
+
+ update = opts[:importer_id].to_s.strip != ''
+ url = build_url(opts[:importer_id], opts[:url], opts[:port])
+ params = build_params(opts.dup)
+
+ headers = {
+ 'Content-Type' => 'application/json',
+ 'Authorization' => "Token: #{opts[:auth_token]}"
+ }
+
+ Rails.logger.info("POST to #{url} (update=#{update}) - PARAMS #{params.inspect}")
+
+ conn = Faraday.new(url:, headers:)
response = if update
- conn.put do |request|
- request.body = params.to_json
- end
+ conn.put { |r| r.body = params.to_json }
else
- conn.post do |request|
- request.body = params.to_json
- end
+ conn.post { |r| r.body = params.to_json }
end
- puts "#{response.status} - #{response.body.truncate(200)}"
+ puts "#{response.status} - #{response.body&.to_s&.slice(0, 200)}"
end
-def check_required_params
- if opts[:importer_id].blank? && invalid?(opts)
- puts 'Missing required parameters'
- help
- end
-
- if opts[:auth_token].blank? # rubocop:disable Style/GuardClause
+def check_required_params(opts)
+ if opts[:auth_token].to_s.strip.empty?
puts 'Missing Authentication Token --auth_token'
- exit
+ exit(1)
end
-end
-def invalid?(opts)
- required_params.each do |p|
- return true if opts[p.to_sym].blank?
- end
- false
-end
+ # For UPDATE, only importer_id + auth_token are required.
+ return if opts[:importer_id].to_s.strip != ''
-def required_params
- Bulkrax.api_definition['bulkrax']['importer'].map { |key, value| key if value['required'] == true }.compact
-end
+ missing = REQUIRED_CREATE_KEYS.select { |k| opts[k].to_s.strip.empty? }
+ return if missing.empty?
-def build_params(opts = {})
- params = {}
- params[:commit] = opts.delete(:commit)
- parser_fields = {
- metadata_file_name: opts.delete(:metadata_file_name),
- metadata_format: opts.delete(:metadata_format),
- rights_statement: opts.delete(:rights_statement),
- override_rights_statement: opts.delete(:override_rights_statement),
- import_file_path: opts.delete(:import_file_path),
- metadata_prefix: opts.delete(:metadata_prefix),
- set: opts.delete(:set),
- collection_name: opts.delete(:collection_name)
- }.compact
- params[:importer] = opts.compact
- params[:importer][:user_id] = opts.delete(:user_id)
- params[:importer][:admin_set_id] = opts.delete(:admin_set_id)
- params[:importer][:parser_fields] = parser_fields || {}
- params.compact
+ puts "Missing required parameters: #{missing.join(', ')}"
+ help_and_exit
end
-def build_url(importer_id, url, port = nil)
- if url.nil?
- protocol = Rails.application.config.force_ssl ? 'https://' : 'http://'
- host = Rails.application.config.action_mailer.default_url_options[:host]
- url = "#{protocol}#{host}"
- url = "#{url}:#{port}" if port
+def build_params(opts)
+ commit = opts.delete(:commit)
+ importer = {
+ name: opts.delete(:name),
+ parser_klass: opts.delete(:parser_klass),
+ user_id: opts.delete(:user_id),
+ admin_set_id: opts.delete(:admin_set_id),
+ parser_fields: {}
+ }
+
+ PARSER_FIELD_KEYS.each do |k|
+ v = opts.delete(k)
+ importer[:parser_fields][k] = v unless v.nil? || v.to_s.empty?
end
- path = Bulkrax::Engine.routes.url_helpers.polymorphic_path(Bulkrax::Importer)
- url = File.join(url, path)
- url = File.join(url, importer_id) if importer_id
- url
+
+ { commit:, importer: importer.compact }.compact
end
-def logger
- Rails.logger
+def build_url(importer_id, url, port = nil)
+ base =
+ if url.to_s.strip.empty?
+ protocol = Rails.application.config.force_ssl ? 'https://' : 'http://'
+ host = Rails.application.config.action_mailer.default_url_options[:host]
+ b = "#{protocol}#{host}"
+ b = "#{b}:#{port}" if port
+ b
+ else
+ url
+ end
+
+ helpers = Bulkrax::Engine.routes.url_helpers
+ path = importer_id ? helpers.importer_path(importer_id) : helpers.importers_path
+ File.join(base, path)
end
def version
puts "Bulkrax #{Bulkrax::VERSION}"
- puts "Slop #{Slop::VERSION}"
+ puts "Rails #{Rails.version}"
end
-# Format the help for the CLI
-def help
+def help_and_exit
puts 'CREATE:'
puts ' bin/importer --name "My Import" --parser_klass Bulkrax::CsvParser --commit "Create and Import" --import_file_path /data/tmp/import.csv --auth_token 12345'
puts 'UPDATE:'
puts ' bin/importer --importer_id 1 --commit "Update and Re-Import (update metadata only)" --import_file_path /data/tmp/import.csv --auth_token 12345'
- puts 'PARAMETERS:'
- Bulkrax.api_definition['bulkrax']['importer'].each_pair do |key, value|
- next if key == 'parser_fields'
- puts " --#{key}"
- value.each_pair do |k, v|
- next if k == 'contained_in'
- puts " #{k}: #{v}"
- end
- end
- puts ' --url'
- puts " Repository URL"
- exit
+ puts 'COMMON PARAMS:'
+ puts " --user_id --admin_set_id --rights_statement --override_rights_statement --metadata_format --metadata_prefix --set --collection_name --metadata_file_name --import_file_path --url --port"
+ exit(1)
end
-# Setup the options
+# -----------------------
+# CLI
+# -----------------------
options = Slop.parse do |o|
- o.on '--version', 'Print the version' do
+ o.on '--version', 'Print versions' do
version
exit
end
- o.on '--help', 'Print help' do
- help
- exit
+ o.on '--help', 'Help' do
+ help_and_exit
end
- Bulkrax.api_definition['bulkrax']['importer'].each_pair do |key, value|
- if value['required'].blank?
- o.string "--#{key}", value['definition'], default: nil
- else
- o.string "--#{key}", value['definition']
- end
- end
- o.string '--url', 'Repository URL'
+ # High-level importer attributes
+ o.string '--name', 'Importer name'
+ o.string '--parser_klass', 'Parser class, e.g. Bulkrax::CsvParser'
+ o.string '--user_id', 'User ID who owns the import'
+ o.string '--admin_set_id', 'AdminSet ID for created works'
+ o.string '--commit', 'Commit button label/action (engine uses this)'
+
+ # Parser fields
+ o.string '--metadata_file_name', 'Metadata file name (if needed)'
+ o.string '--metadata_format', 'Format, e.g. csv'
+ o.string '--rights_statement', 'Rights URI'
+ o.bool '--override_rights_statement','Override existing rights?'
+ o.string '--import_file_path', 'Server-accessible path to CSV/Bag'
+ o.string '--metadata_prefix', 'OAI prefix (for OAI parsers)'
+ o.string '--set', 'OAI set (for OAI parsers)'
+ o.string '--collection_name', 'Collection name to link to'
+
+ # Update target
+ o.string '--importer_id', 'Importer ID to update instead of create'
+
+ # Auth / endpoint
+ o.string '--auth_token', 'API token (Devise token auth)'
+ o.string '--url', 'Repository base URL (default: from config)'
+ o.integer '--port', 'Port to append to base URL'
end
main(options.to_hash)
diff --git a/config/initializers/bulkrax.rb b/config/initializers/bulkrax.rb
index 345da71c7..4c18e75cf 100644
--- a/config/initializers/bulkrax.rb
+++ b/config/initializers/bulkrax.rb
@@ -1,133 +1,96 @@
# frozen_string_literal: true
Bulkrax.setup do |config|
- # Add local parsers
- # config.parsers += [
- # { name: 'MODS - My Local MODS parser', class_name: 'Bulkrax::ModsXmlParser', partial: 'mods_fields' },
- # ]
+ # ----- Paths & host -----
+ # Prefer ENV, but fall back to tmp/* so dev/test still work
+ IMPORTS_DIR = Rails.root.join('tmp', 'bulkrax', 'imports').freeze
+ EXPORTS_DIR = Rails.root.join('tmp', 'bulkrax', 'exports').freeze
- # WorkType to use as the default if none is specified in the import
- # Default is the first returned by Hyrax.config.curation_concerns
- # config.default_work_type = GenericWork
-
- # Path to store pending imports
- config.import_path = ENV["SCHOLAR_BULKRAX_IMPORT_PATH"]
+ config.import_path = ENV.fetch('SCHOLAR_BULKRAX_IMPORT_PATH', IMPORTS_DIR.to_s)
+ config.export_path = ENV.fetch('SCHOLAR_BULKRAX_EXPORT_PATH', EXPORTS_DIR.to_s)
- # Path to store exports before download
- config.export_path = ENV["SCHOLAR_BULKRAX_EXPORT_PATH"]
+ # Needed for building absolute URLs in exports/downloads
+ config.server_name = ENV.fetch("SCHOLAR_BULKRAX_SERVER_NAME", "localhost:3000")
- # Server name for oai request header
- # config.server_name = 'my_server@name.com'
+ # Ensure directories exist (v9 expects writable paths)
+ [config.import_path, config.export_path].each do |p|
+ FileUtils.mkdir_p(p) unless File.directory?(p)
+ end
- # NOTE: Creating Collections using the collection_field_mapping will no longer be supported as of Bulkrax version 3.0.
- # Please configure Bulkrax to use related_parents_field_mapping and related_children_field_mapping instead.
- # Field_mapping for establishing a collection relationship (FROM work TO collection)
- # This value IS NOT used for OAI, so setting the OAI parser here will have no effect
- # The mapping is supplied per Entry, provide the full class name as a string, eg. 'Bulkrax::CsvEntry'
- # The default value for CSV is collection
- # Add/replace parsers, for example:
- # config.collection_field_mapping['Bulkrax::RdfEntry'] = 'http://opaquenamespace.org/ns/set'
-
- # Field mappings
- # Create a completely new set of mappings by replacing the whole set as follows
- # config.field_mappings = {
- # "Bulkrax::OaiDcParser" => { **individual field mappings go here*** }
- # }
+ # ----- Default work type (optional; keep your project’s default if set elsewhere)
+ # config.default_work_type = GenericWork
+ # ----- Field mappings -----
+ # Tip: use regex for split so behavior is explicit across versions
config.field_mappings = {
"Bulkrax::CsvParser" => {
- "title" => { from: ["title"], parsed: true, split: '\|' },
- "creator" => { from: ["creator"], split: '\|' },
+ "title" => { from: ["title"], parsed: true, split: /\|/ },
+ "creator" => { from: ["creator"], split: /\|/ },
"college" => { from: ["college"] },
"department" => { from: ["department"] },
"description" => { from: ["description"] },
- "license" => { from: ["license"], split: '\|', parsed: true },
+ "license" => { from: ["license"], split: /\|/, parsed: true },
"publisher" => { from: ["publisher"] },
"date_created" => { from: ["date_created"] },
- "alternate_title" => { from: ["alternate_title"], parsed: true, split: '\|' },
- "subject" => { from: ["subject"], split: '\|' },
- "geo_subject" => { from: ["geo_subject"], split: '\|' },
- "time_period" => { from: ["time_period"], split: '\|' },
- "language" => { from: ["language"], split: '\|' },
+ "alternate_title" => { from: ["alternate_title"], parsed: true, split: /\|/ },
+ "subject" => { from: ["subject"], split: /\|/ },
+ "geo_subject" => { from: ["geo_subject"], split: /\|/ },
+ "time_period" => { from: ["time_period"], split: /\|/ },
+ "language" => { from: ["language"], split: /\|/ },
"required_software" => { from: ["required_software"] },
"note" => { from: ["note"] },
- "related_url" => { from: ["related_url"], split: '\|' },
- "advisor" => { from: ["advisor"], parsed: true, split: '\|' },
- "committee_member" => { from: ["committee_member"], parsed: true, split: '\|' },
+ "related_url" => { from: ["related_url"], split: /\|/ },
+ "advisor" => { from: ["advisor"], parsed: true, split: /\|/ },
+ "committee_member" => { from: ["committee_member"], parsed: true, split: /\|/ },
"degree" => { from: ["degree"] },
"doi" => { from: ["doi"] },
"etd_publisher" => { from: ["etd_publisher"] },
"genre" => { from: ["genre"] },
- "issn" => { from: ["issn"], split: '\|' },
- "journal_title" => { from: ["journal_title"], split: '\|' }
+ "issn" => { from: ["issn"], split: /\|/ },
+ "journal_title" => { from: ["journal_title"], split: /\|/ }
}
}
- config.field_mappings['Bulkrax::BagitParser'] = {
- "title" => { from: ["title"], parsed: true, split: '\|' },
- "creator" => { from: ["creator"], split: '\|' },
+ config.field_mappings["Bulkrax::BagitParser"] = {
+ "title" => { from: ["title"], parsed: true, split: /\|/ },
+ "creator" => { from: ["creator"], split: /\|/ },
"college" => { from: ["college"] },
"department" => { from: ["department"] },
"description" => { from: ["description"] },
- "license" => { from: ["license"], split: '\|', parsed: true },
+ "license" => { from: ["license"], split: /\|/, parsed: true },
"publisher" => { from: ["publisher"] },
"date_created" => { from: ["date_created"] },
- "alternate_title" => { from: ["alternate_title"], parsed: true, split: '\|' },
- "subject" => { from: ["subject"], split: '\|' },
- "geo_subject" => { from: ["geo_subject"], split: '\|' },
- "time_period" => { from: ["time_period"], split: '\|' },
- "language" => { from: ["language"], split: '\|' },
+ "alternate_title" => { from: ["alternate_title"], parsed: true, split: /\|/ },
+ "subject" => { from: ["subject"], split: /\|/ },
+ "geo_subject" => { from: ["geo_subject"], split: /\|/ },
+ "time_period" => { from: ["time_period"], split: /\|/ },
+ "language" => { from: ["language"], split: /\|/ },
"required_software" => { from: ["required_software"] },
"note" => { from: ["note"] },
- "related_url" => { from: ["related_url"], split: '\|' },
- "advisor" => { from: ["advisor"], parsed: true, split: '\|' },
- "committee_member" => { from: ["committee_member"], parsed: true, split: '\|' },
+ "related_url" => { from: ["related_url"], split: /\|/ },
+ "advisor" => { from: ["advisor"], parsed: true, split: /\|/ },
+ "committee_member" => { from: ["committee_member"], parsed: true, split: /\|/ },
"degree" => { from: ["degree"] },
"doi" => { from: ["doi"] },
"etd_publisher" => { from: ["etd_publisher"] },
"genre" => { from: ["genre"] },
- "issn" => { from: ["issn"], split: '\|' },
- "journal_title" => { from: ["journal_title"], split: '\|' }
+ "issn" => { from: ["issn"], split: /\|/ },
+ "journal_title" => { from: ["journal_title"], split: /\|/ }
}
- # Add to, or change existing mappings as follows
- # e.g. to exclude date
- # config.field_mappings["Bulkrax::OaiDcParser"]["date"] = { from: ["date"], excluded: true }
- #
- # e.g. to import parent-child relationships
- config.field_mappings['Bulkrax::CsvParser']['parents'] = { from: ['parents'], related_parents_field_mapping: true }
- config.field_mappings['Bulkrax::CsvParser']['children'] = { from: ['children'], related_children_field_mapping: true }
+ # Relationships (v3+ approach — keep)
+ config.field_mappings["Bulkrax::CsvParser"]["parents"] = { from: ["parents"], related_parents_field_mapping: true }
+ config.field_mappings["Bulkrax::CsvParser"]["children"] = { from: ["children"], related_children_field_mapping: true }
+ config.field_mappings["Bulkrax::BagitParser"]["parents"] = { from: ["parents"], related_parents_field_mapping: true }
+ config.field_mappings["Bulkrax::BagitParser"]["children"] = { from: ["children"], related_children_field_mapping: true }
- config.field_mappings['Bulkrax::BagitParser']['parents'] = { from: ['parents'], related_parents_field_mapping: true }
- config.field_mappings['Bulkrax::BagitParser']['children'] = { from: ['children'], related_children_field_mapping: true }
- # (For more info on importing relationships, see Bulkrax Wiki: https://github.com/samvera-labs/bulkrax/wiki/Configuring-Bulkrax#parent-child-relationship-field-mappings)
- #
- # # e.g. to add the required source_identifier field
- # # config.field_mappings["Bulkrax::CsvParser"]["source_id"] = { from: ["old_source_id"], source_identifier: true }
- # If you want Bulkrax to fill in source_identifiers for you, see below
-
- # To duplicate a set of mappings from one parser to another
- # config.field_mappings["Bulkrax::OaiOmekaParser"] = {}
- # config.field_mappings["Bulkrax::OaiDcParser"].each {|key,value| config.field_mappings["Bulkrax::OaiOmekaParser"][key] = value }
-
- # Should Bulkrax make up source identifiers for you? This allow round tripping
- # and download errored entries to still work, but does mean if you upload the
- # same source record in two different files you WILL get duplicates.
- # It is given two aruguments, self at the time of call and the index of the reocrd
- # config.fill_in_blank_source_identifiers = ->(parser, index) { "b-#{parser.importer.id}-#{index}"}
- # or use a uuid
+ # Fill in missing source identifiers (round‑tripping & error re‑downloads)
config.fill_in_blank_source_identifiers = ->(_parser, _index) { SecureRandom.uuid }
- # config.fill_in_blank_source_identifiers = ->(obj, index) { "Scholar-#{obj.importerexporter.id}-#{index}" }
-
- # Properties that should not be used in imports/exports. They are reserved for use by Hyrax.
- # config.reserved_properties += ['my_field']
- # List of Questioning Authority properties that are controlled via YAML files in
- # the config/authorities/ directory. For example, the :rights_statement property
- # is controlled by the active terms in config/authorities/rights_statements.yml
- # Defaults: 'rights_statement' and 'license'
+ # QA/Reserved props (extend if you add controlled vocabs)
# config.qa_controlled_properties += ['my_field']
+ # config.reserved_properties += ['my_field']
end
-# Sidebar for hyrax 3+ support
+# Sidebar for Hyrax dashboard (works on Hyrax 3; guard for Hyrax 4+ if API changes)
Hyrax::DashboardController.sidebar_partials[:repository_content] << "hyrax/dashboard/sidebar/bulkrax_sidebar_additions" if Object.const_defined?(:Hyrax) && ::Hyrax::DashboardController&.respond_to?(:sidebar_partials)
diff --git a/config/initializers/bulkrax_force_object_factory.rb b/config/initializers/bulkrax_force_object_factory.rb
new file mode 100644
index 000000000..01ffca547
--- /dev/null
+++ b/config/initializers/bulkrax_force_object_factory.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+# -----------------------------------------------------------------------------
+# Force Bulkrax to use a minimal, ActiveFedora-backed object factory
+#
+# Context / Why:
+# • Bulkrax (v9.x) expects an `object_factory` that implements BOTH:
+# - `.query(query_string, **kwargs)` → search Solr
+# - `.find(id)` → fetch a single object by id
+# • In AF/Hyrax stacks without a Hyrax::SearchService wired up (or in
+# Sidekiq where Blacklight config may not be loaded), Bulkrax’s default
+# factory path can be nil or incomplete. That leads to exporter errors.
+#
+# What this initializer does:
+# • Defines `Bulkrax::SimpleSolrObjectFactory`, a tiny factory that:
+# - uses `ActiveFedora::SolrService.query` for Solr searches
+# - uses `ActiveFedora::Base.find` for fetching AF/Hyrax objects
+# - returns `nil` when an object is missing/tombstoned
+# - passes through objects that already look like AF models
+# • Sets `Bulkrax.object_factory` lazily to this simple factory and provides
+# a setter so you can override it elsewhere if you introduce a richer
+# implementation later.
+#
+# Notes:
+# • Works in all processes (web/console/Sidekiq) thanks to `to_prepare`.
+# • Pairs well with the separate “factory sanity” initializer that only
+# overrides the factory when it’s nil or missing required methods.
+# • If you replace this with a custom factory, ensure it responds to BOTH
+# `.query` and `.find`.
+# -----------------------------------------------------------------------------
+
+Rails.application.config.to_prepare do
+ module Bulkrax
+ module SimpleSolrObjectFactory
+ module_function
+
+ # Solr search (returns an array of Solr docs/hashes)
+ def query(q, **kwargs)
+ ActiveFedora::SolrService.query(q, **kwargs)
+ end
+
+ # Fetch AF/Hyrax object by id (works, collections, file sets)
+ # - Pass-through if an object (responds_to :id) is provided
+ # - Return nil if not found or tombstoned
+ def find(id, **)
+ return id if id.respond_to?(:id)
+ ActiveFedora::Base.find(id.to_s)
+ rescue ActiveFedora::ObjectNotFoundError, Ldp::Gone
+ nil
+ end
+ end
+
+ class << self
+ # Default to the simple AF-backed factory unless explicitly overridden
+ def object_factory
+ @object_factory ||= Bulkrax::SimpleSolrObjectFactory
+ end
+
+ # Allow apps to swap in a richer object factory if desired
+ attr_writer :object_factory
+ end
+ end
+end
diff --git a/config/initializers/bulkrax_object_factory_sanity.rb b/config/initializers/bulkrax_object_factory_sanity.rb
new file mode 100644
index 000000000..dbbf85c84
--- /dev/null
+++ b/config/initializers/bulkrax_object_factory_sanity.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+# -----------------------------------------------------------------------------
+# Bulkrax object_factory sanity check / safety net
+#
+# Why:
+# Bulkrax’s exporter expects `Bulkrax.object_factory` to implement BOTH:
+# - `.query(query_string, **kwargs)` → SOLR search
+# - `.find(id)` → fetch a single record
+#
+# In certain boots/reloads (e.g., dev code reloads, Sidekiq vs. web init order,
+# or future gem upgrades), `Bulkrax.object_factory` can end up `nil` or point to
+# something that doesn’t implement the full API. That leads to errors like:
+# - NoMethodError: undefined method `query' for nil:NilClass
+# - NoMethodError: undefined method `find' for
:Module
+#
+# What this does:
+# On each app prepare (boot + every code reload in development), if the current
+# factory is missing or incomplete, we set a safe default:
+# `Bulkrax::SimpleSolrObjectFactory` (which wraps ActiveFedora::SolrService).
+#
+# Notes:
+# • Non-invasive: if a valid custom factory is already set, we do NOTHING.
+# • Runs in all processes (web/console/Sidekiq) to prevent divergence.
+# • Assumes `Bulkrax::SimpleSolrObjectFactory` is defined elsewhere (e.g.,
+# in `config/initializers/bulkrax_force_object_factory.rb`) and provides:
+# def self.query(q, **kwargs); end
+# def self.find(id); end
+# • If you swap in your own factory, make sure it responds to BOTH `.query` and `.find`.
+# -----------------------------------------------------------------------------
+
+Rails.application.config.to_prepare do
+ next unless defined?(Bulkrax)
+
+ factory = Bulkrax.object_factory
+ needs_default = factory.nil? ||
+ !factory.respond_to?(:query) ||
+ !factory.respond_to?(:find)
+
+ if needs_default
+ Bulkrax.object_factory = Bulkrax::SimpleSolrObjectFactory
+ Rails.logger.info("[bulkrax] object_factory was nil/incomplete; defaulting to Bulkrax::SimpleSolrObjectFactory")
+ end
+end
diff --git a/config/initializers/hyrax.rb b/config/initializers/hyrax.rb
index 4a8ab8b98..7819034de 100644
--- a/config/initializers/hyrax.rb
+++ b/config/initializers/hyrax.rb
@@ -289,3 +289,6 @@
# set bulkrax default work type to first curation_concern if it isn't already set
Bulkrax.default_work_type = Hyrax.config.curation_concerns.first.to_s if Bulkrax.default_work_type.blank?
+
+# set bulkrax default work type to first curation_concern if it isn't already set
+Bulkrax.default_work_type = Hyrax.config.curation_concerns.first.to_s if Bulkrax.default_work_type.blank?
diff --git a/config/initializers/load_decorators.rb b/config/initializers/load_decorators.rb
new file mode 100644
index 000000000..fa0b7deb6
--- /dev/null
+++ b/config/initializers/load_decorators.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+# Auto-load all *_decorator.rb files in app/decorators on each reload
+Rails.configuration.to_prepare do
+ Dir.glob(Rails.root.join('app', 'decorators', '**', '*_decorator.rb')).each do |decorator|
+ require_dependency(decorator)
+ end
+ # Apply the Bulkrax exporter and importer decorator
+ Bulkrax::ExportersController.prepend(Bulkrax::ExportersControllerDecorator)
+ Bulkrax::ImportersController.prepend(Bulkrax::ImportersControllerDecorator)
+end
diff --git a/config/sidekiq.yml b/config/sidekiq.yml
index 7745fdf33..ab2ba761a 100644
--- a/config/sidekiq.yml
+++ b/config/sidekiq.yml
@@ -1,13 +1,10 @@
----
-max_retries: 3
+:concurrency: 5 # number of worker threads
:queues:
- - default
- - ingest
- - event
- - change
- - fixity_check
- - import
- - export
-:limits:
- fixity_check: 1
-
+ - [default, "5"] # optional weights; higher number = more throughput
+ - [ingest, "3"]
+ - [event, "2"]
+ - [change, "2"]
+ - [fixity_check, "1"]
+ - [import, "2"]
+ - [export, "2"]
+:strict: false # honor weights (round-robin by weight); omit if you want strict FIFO by queue order
diff --git a/db/migrate/20250811182714_create_bulkrax_importers.bulkrax.rb b/db/migrate/20250811182714_create_bulkrax_importers.bulkrax.rb
new file mode 100644
index 000000000..130f375a6
--- /dev/null
+++ b/db/migrate/20250811182714_create_bulkrax_importers.bulkrax.rb
@@ -0,0 +1,19 @@
+# This migration comes from bulkrax (originally 20181011230201)
+class CreateBulkraxImporters < ActiveRecord::Migration[5.1]
+ def change
+ unless table_exists?(:bulkrax_importers)
+ create_table :bulkrax_importers do |t|
+ t.string :name
+ t.string :admin_set_id
+ t.references :user, foreign_key: false
+ t.string :frequency
+ t.string :parser_klass
+ t.integer :limit
+ t.text :parser_fields
+ t.text :field_mapping
+
+ t.timestamps
+ end
+ end
+ end
+end
diff --git a/db/migrate/20250811182715_create_bulkrax_importer_runs.bulkrax.rb b/db/migrate/20250811182715_create_bulkrax_importer_runs.bulkrax.rb
new file mode 100644
index 000000000..52de523c1
--- /dev/null
+++ b/db/migrate/20250811182715_create_bulkrax_importer_runs.bulkrax.rb
@@ -0,0 +1,17 @@
+# This migration comes from bulkrax (originally 20181011230228)
+class CreateBulkraxImporterRuns < ActiveRecord::Migration[5.1]
+ def change
+ unless table_exists?(:bulkrax_importer_runs)
+ create_table :bulkrax_importer_runs do |t|
+ t.references :importer, foreign_key: {to_table: :bulkrax_importers}
+ t.integer :total_records, default: 0
+ t.integer :enqueued_records, default: 0
+ t.integer :processed_records, default: 0
+ t.integer :deleted_records, default: 0
+ t.integer :failed_records, default: 0
+
+ t.timestamps
+ end
+ end
+ end
+end
diff --git a/db/migrate/20250811182716_create_bulkrax_entries.bulkrax.rb b/db/migrate/20250811182716_create_bulkrax_entries.bulkrax.rb
new file mode 100644
index 000000000..57c828100
--- /dev/null
+++ b/db/migrate/20250811182716_create_bulkrax_entries.bulkrax.rb
@@ -0,0 +1,17 @@
+# This migration comes from bulkrax (originally 20190325183136)
+class CreateBulkraxEntries < ActiveRecord::Migration[5.1]
+ def change
+ unless table_exists?(:bulkrax_entries)
+ create_table :bulkrax_entries do |t|
+ t.string :identifier
+ t.string :collection_id
+ t.string :type
+ t.references :importer, foreign_key: {to_table: :bulkrax_importers}
+ t.text :raw_metadata
+ t.text :parsed_metadata
+
+ t.timestamps
+ end
+ end
+ end
+end
diff --git a/db/migrate/20250811182717_add_status_to_entry.bulkrax.rb b/db/migrate/20250811182717_add_status_to_entry.bulkrax.rb
new file mode 100644
index 000000000..4f8e0e4c0
--- /dev/null
+++ b/db/migrate/20250811182717_add_status_to_entry.bulkrax.rb
@@ -0,0 +1,10 @@
+# This migration comes from bulkrax (originally 20190601221109)
+class AddStatusToEntry < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_entries, :last_error, :text unless column_exists?(:bulkrax_entries, :last_error)
+ add_column :bulkrax_entries, :last_error_at, :datetime unless column_exists?(:bulkrax_entries, :last_error_at)
+
+ add_column :bulkrax_entries, :last_succeeded_at, :datetime unless column_exists?(:bulkrax_entries, :last_succeeded_at)
+
+ end
+end
diff --git a/db/migrate/20250811182718_add_collections_to_importer_runs.bulkrax.rb b/db/migrate/20250811182718_add_collections_to_importer_runs.bulkrax.rb
new file mode 100644
index 000000000..2c83d884e
--- /dev/null
+++ b/db/migrate/20250811182718_add_collections_to_importer_runs.bulkrax.rb
@@ -0,0 +1,7 @@
+# This migration comes from bulkrax (originally 20190715161939)
+class AddCollectionsToImporterRuns < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_importer_runs, :processed_collections, :integer, default: 0 unless column_exists?(:bulkrax_importer_runs, :processed_collections)
+ add_column :bulkrax_importer_runs, :failed_collections, :integer, default: 0 unless column_exists?(:bulkrax_importer_runs, :failed_collections)
+ end
+end
diff --git a/db/migrate/20250811182719_change_collection_ids_on_entries.bulkrax.rb b/db/migrate/20250811182719_change_collection_ids_on_entries.bulkrax.rb
new file mode 100644
index 000000000..66cf7ff07
--- /dev/null
+++ b/db/migrate/20250811182719_change_collection_ids_on_entries.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20190715162044)
+class ChangeCollectionIdsOnEntries < ActiveRecord::Migration[5.1]
+ def change
+ rename_column :bulkrax_entries, :collection_id, :collection_ids if column_exists?(:bulkrax_entries, :collection_id)
+ end
+end
diff --git a/db/migrate/20250811182720_create_bulkrax_exporters.bulkrax.rb b/db/migrate/20250811182720_create_bulkrax_exporters.bulkrax.rb
new file mode 100644
index 000000000..73d212f8a
--- /dev/null
+++ b/db/migrate/20250811182720_create_bulkrax_exporters.bulkrax.rb
@@ -0,0 +1,20 @@
+# This migration comes from bulkrax (originally 20190729124607)
+class CreateBulkraxExporters < ActiveRecord::Migration[5.1]
+ def change
+ unless table_exists?(:bulkrax_exporters)
+ create_table :bulkrax_exporters do |t|
+ t.string :name
+ t.references :user, foreign_key: false
+ t.string :parser_klass
+ t.integer :limit
+ t.text :parser_fields
+ t.text :field_mapping
+ t.string :export_source
+ t.string :export_from
+ t.string :export_type
+
+ t.timestamps
+ end
+ end
+ end
+end
diff --git a/db/migrate/20250811182721_create_bulkrax_exporter_runs.bulkrax.rb b/db/migrate/20250811182721_create_bulkrax_exporter_runs.bulkrax.rb
new file mode 100644
index 000000000..006f617c8
--- /dev/null
+++ b/db/migrate/20250811182721_create_bulkrax_exporter_runs.bulkrax.rb
@@ -0,0 +1,15 @@
+# This migration comes from bulkrax (originally 20190729134158)
+class CreateBulkraxExporterRuns < ActiveRecord::Migration[5.1]
+ def change
+ unless table_exists?(:bulkrax_exporter_runs)
+ create_table :bulkrax_exporter_runs do |t|
+ t.references :exporter, foreign_key: { to_table: :bulkrax_exporters }
+ t.integer :total_records, default: 0
+ t.integer :enqueued_records, default: 0
+ t.integer :processed_records, default: 0
+ t.integer :deleted_records, default: 0
+ t.integer :failed_records, default: 0
+ end
+ end
+ end
+end
diff --git a/db/migrate/20250811182722_change_importer_and_exporter_to_polymorphic.bulkrax.rb b/db/migrate/20250811182722_change_importer_and_exporter_to_polymorphic.bulkrax.rb
new file mode 100644
index 000000000..eab0a389b
--- /dev/null
+++ b/db/migrate/20250811182722_change_importer_and_exporter_to_polymorphic.bulkrax.rb
@@ -0,0 +1,19 @@
+# This migration comes from bulkrax (originally 20190731114016)
+module Bulkrax
+ class Entry < ApplicationRecord
+ belongs_to :importer
+ end
+end
+
+class ChangeImporterAndExporterToPolymorphic < ActiveRecord::Migration[5.1]
+ def change
+ begin # deal with odd bug around foreign keys in ci
+ remove_foreign_key :bulkrax_entries, column: :importer_id if foreign_key_exists?(:bulkrax_entries, column: :importer_id)
+ rescue ArgumentError
+ # do nothing
+ end
+ remove_index :bulkrax_entries, :importer_id if index_exists?(:bulkrax_entries, :importer_id)
+ rename_column :bulkrax_entries, :importer_id, :importerexporter_id if column_exists?(:bulkrax_entries, :importer_id)
+ add_column :bulkrax_entries, :importerexporter_type, :string, after: :id, default: 'Bulkrax::Importer' unless column_exists?(:bulkrax_entries, :importerexporter_type)
+ end
+end
diff --git a/db/migrate/20250811182723_add_total_collection_records_to_importer_runs.bulkrax.rb b/db/migrate/20250811182723_add_total_collection_records_to_importer_runs.bulkrax.rb
new file mode 100644
index 000000000..e7bf4a4e9
--- /dev/null
+++ b/db/migrate/20250811182723_add_total_collection_records_to_importer_runs.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20191203225129)
+class AddTotalCollectionRecordsToImporterRuns < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_importer_runs, :total_collection_entries, :integer, default: 0 unless column_exists?(:bulkrax_importer_runs, :total_collection_entries)
+ end
+end
diff --git a/db/migrate/20250811182724_add_children_to_importer_runs.bulkrax.rb b/db/migrate/20250811182724_add_children_to_importer_runs.bulkrax.rb
new file mode 100644
index 000000000..54e4ff0dc
--- /dev/null
+++ b/db/migrate/20250811182724_add_children_to_importer_runs.bulkrax.rb
@@ -0,0 +1,7 @@
+# This migration comes from bulkrax (originally 20191204191623)
+class AddChildrenToImporterRuns < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_importer_runs, :processed_children, :integer, default: 0 unless column_exists?(:bulkrax_importer_runs, :processed_children)
+ add_column :bulkrax_importer_runs, :failed_children, :integer, default: 0 unless column_exists?(:bulkrax_importer_runs, :failed_children)
+ end
+end
diff --git a/db/migrate/20250811182725_change_total_records_to_total_work_entries.bulkrax.rb b/db/migrate/20250811182725_change_total_records_to_total_work_entries.bulkrax.rb
new file mode 100644
index 000000000..4be6dd0fe
--- /dev/null
+++ b/db/migrate/20250811182725_change_total_records_to_total_work_entries.bulkrax.rb
@@ -0,0 +1,7 @@
+# This migration comes from bulkrax (originally 20191204223857)
+class ChangeTotalRecordsToTotalWorkEntries < ActiveRecord::Migration[5.1]
+ def change
+ rename_column :bulkrax_importer_runs, :total_records, :total_work_entries if column_exists?(:bulkrax_importer_runs, :total_records)
+ rename_column :bulkrax_exporter_runs, :total_records, :total_work_entries if column_exists?(:bulkrax_exporter_runs, :total_records)
+ end
+end
diff --git a/db/migrate/20250811182726_change_entry_last_error.bulkrax.rb b/db/migrate/20250811182726_change_entry_last_error.bulkrax.rb
new file mode 100644
index 000000000..a4fcc7d5c
--- /dev/null
+++ b/db/migrate/20250811182726_change_entry_last_error.bulkrax.rb
@@ -0,0 +1,20 @@
+# This migration comes from bulkrax (originally 20191212155530)
+class ChangeEntryLastError < ActiveRecord::Migration[5.1]
+
+ def up
+ # Use raw query to change text to JSON as last_error field is now serialized
+ results = ActiveRecord::Base.connection.execute("SELECT id, last_error from bulkrax_entries WHERE last_error IS NOT null AND last_error LIKE '%\n\n%'")
+ results.each do | error |
+ old_errors = error['last_error'].gsub("'","''").split("\n\n")
+ new_error = {
+ 'error_class' => 'unknown',
+ 'error_message' => old_errors.first,
+ 'error_trace' => old_errors.last
+ }
+ ActiveRecord::Base.connection.execute("UPDATE bulkrax_entries SET last_error = '#{new_error.to_json}' WHERE id = '#{error['id']}'")
+ end
+ end
+
+ def down; end
+
+end
diff --git a/db/migrate/20250811182727_add_validate_only_to_bulkrax_importers.bulkrax.rb b/db/migrate/20250811182727_add_validate_only_to_bulkrax_importers.bulkrax.rb
new file mode 100644
index 000000000..b10e3bc78
--- /dev/null
+++ b/db/migrate/20250811182727_add_validate_only_to_bulkrax_importers.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20200108194557)
+class AddValidateOnlyToBulkraxImporters < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_importers, :validate_only, :boolean unless column_exists?(:bulkrax_importers, :validate_only)
+ end
+end
diff --git a/db/migrate/20250811182728_add_status_to_importers.bulkrax.rb b/db/migrate/20250811182728_add_status_to_importers.bulkrax.rb
new file mode 100644
index 000000000..ad7f948b9
--- /dev/null
+++ b/db/migrate/20250811182728_add_status_to_importers.bulkrax.rb
@@ -0,0 +1,10 @@
+# This migration comes from bulkrax (originally 20200301232856)
+class AddStatusToImporters < ActiveRecord::Migration[5.1]
+ def change
+ if table_exists?(:bulkrax_importers)
+ add_column :bulkrax_importers, :last_error, :text unless column_exists?(:bulkrax_importers, :last_error)
+ add_column :bulkrax_importers, :last_error_at, :datetime unless column_exists?(:bulkrax_importers, :last_error_at)
+ add_column :bulkrax_importers, :last_succeeded_at, :datetime unless column_exists?(:bulkrax_importers, :last_succeeded_at)
+ end
+ end
+end
diff --git a/db/migrate/20250811182729_remove_foreign_key_from_bulkrax_entries.bulkrax.rb b/db/migrate/20250811182729_remove_foreign_key_from_bulkrax_entries.bulkrax.rb
new file mode 100644
index 000000000..55ca09951
--- /dev/null
+++ b/db/migrate/20250811182729_remove_foreign_key_from_bulkrax_entries.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20200312190638)
+class RemoveForeignKeyFromBulkraxEntries < ActiveRecord::Migration[5.1]
+ def change
+ remove_foreign_key :bulkrax_entries, :bulkrax_importers if foreign_key_exists?(:bulkrax_entries, :bulkrax_importers)
+ end
+end
diff --git a/db/migrate/20250811182730_add_status_to_exporters.bulkrax.rb b/db/migrate/20250811182730_add_status_to_exporters.bulkrax.rb
new file mode 100644
index 000000000..ec388093f
--- /dev/null
+++ b/db/migrate/20250811182730_add_status_to_exporters.bulkrax.rb
@@ -0,0 +1,8 @@
+# This migration comes from bulkrax (originally 20200326235838)
+class AddStatusToExporters < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_exporters, :last_error, :text unless column_exists?(:bulkrax_exporters, :last_error)
+ add_column :bulkrax_exporters, :last_error_at, :datetime unless column_exists?(:bulkrax_exporters, :last_error_at)
+ add_column :bulkrax_exporters, :last_succeeded_at, :datetime unless column_exists?(:bulkrax_exporters, :last_succeeded_at)
+ end
+end
diff --git a/db/migrate/20250811182731_add_invalid_record_to_importer_run.bulkrax.rb b/db/migrate/20250811182731_add_invalid_record_to_importer_run.bulkrax.rb
new file mode 100644
index 000000000..a6e284e13
--- /dev/null
+++ b/db/migrate/20250811182731_add_invalid_record_to_importer_run.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20200601204556)
+class AddInvalidRecordToImporterRun < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_importer_runs, :invalid_records, :text unless column_exists?(:bulkrax_importer_runs, :invalid_records)
+ end
+end
diff --git a/db/migrate/20250811182732_create_bulkrax_statuses.bulkrax.rb b/db/migrate/20250811182732_create_bulkrax_statuses.bulkrax.rb
new file mode 100644
index 000000000..0cc2b7df0
--- /dev/null
+++ b/db/migrate/20250811182732_create_bulkrax_statuses.bulkrax.rb
@@ -0,0 +1,19 @@
+# This migration comes from bulkrax (originally 20200818055819)
+class CreateBulkraxStatuses < ActiveRecord::Migration[5.1]
+ def change
+ unless table_exists?(:bulkrax_statuses)
+ create_table :bulkrax_statuses do |t|
+ t.string :status_message
+ t.string :error_class
+ t.string :error_message
+ t.text :error_backtrace
+ t.integer :statusable_id
+ t.string :statusable_type
+ t.integer :runnable_id
+ t.string :runnable_type
+
+ t.timestamps
+ end
+ end
+ end
+end
diff --git a/db/migrate/20250811182733_move_to_statuses.bulkrax.rb b/db/migrate/20250811182733_move_to_statuses.bulkrax.rb
new file mode 100644
index 000000000..122efa727
--- /dev/null
+++ b/db/migrate/20250811182733_move_to_statuses.bulkrax.rb
@@ -0,0 +1,31 @@
+# This migration comes from bulkrax (originally 20200819054016)
+class MoveToStatuses < ActiveRecord::Migration[5.1]
+ def change
+ Bulkrax::Importer.find_each do |i|
+ add_status(i)
+ end
+
+ Bulkrax::Exporter.find_each do |i|
+ add_status(i)
+ end
+
+ Bulkrax::Entry.find_each do |i|
+ add_status(i)
+ end
+ end
+
+ def add_status(i)
+ return if i.statuses.present?
+ if i.last_error
+ i.statuses.create(
+ status_message: 'Failed',
+ runnable: i.last_run,
+ error_class: i.last_error['error_class'],
+ error_message: i.last_error['error_message'],
+ error_backtrace: i.last_error['error_trace']
+ )
+ else
+ i.statuses.create(status_message: 'Complete', runnable: i.last_run)
+ end
+ end
+end
diff --git a/db/migrate/20250811182734_add_date_filter_and_status_to_bulkrax_exporters.bulkrax.rb b/db/migrate/20250811182734_add_date_filter_and_status_to_bulkrax_exporters.bulkrax.rb
new file mode 100644
index 000000000..ec53bff5a
--- /dev/null
+++ b/db/migrate/20250811182734_add_date_filter_and_status_to_bulkrax_exporters.bulkrax.rb
@@ -0,0 +1,8 @@
+# This migration comes from bulkrax (originally 20201106014204)
+class AddDateFilterAndStatusToBulkraxExporters < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_exporters, :start_date, :date unless column_exists?(:bulkrax_exporters, :start_date)
+ add_column :bulkrax_exporters, :finish_date, :date unless column_exists?(:bulkrax_exporters, :finish_date)
+ add_column :bulkrax_exporters, :work_visibility, :string unless column_exists?(:bulkrax_exporters, :work_visibility)
+ end
+end
diff --git a/db/migrate/20250811182735_add_workflow_status_to_bulkrax_exporter.bulkrax.rb b/db/migrate/20250811182735_add_workflow_status_to_bulkrax_exporter.bulkrax.rb
new file mode 100644
index 000000000..c860bbfe9
--- /dev/null
+++ b/db/migrate/20250811182735_add_workflow_status_to_bulkrax_exporter.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20201117220007)
+class AddWorkflowStatusToBulkraxExporter < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_exporters, :workflow_status, :string unless column_exists?(:bulkrax_exporters, :workflow_status)
+ end
+end
diff --git a/db/migrate/20250811182736_remove_unused_last_error.bulkrax.rb b/db/migrate/20250811182736_remove_unused_last_error.bulkrax.rb
new file mode 100644
index 000000000..e97bdd965
--- /dev/null
+++ b/db/migrate/20250811182736_remove_unused_last_error.bulkrax.rb
@@ -0,0 +1,8 @@
+# This migration comes from bulkrax (originally 20210806044408)
+class RemoveUnusedLastError < ActiveRecord::Migration[5.1]
+ def change
+ remove_column :bulkrax_entries, :last_error if column_exists?(:bulkrax_entries, :last_error)
+ remove_column :bulkrax_exporters, :last_error if column_exists?(:bulkrax_exporters, :last_error)
+ remove_column :bulkrax_importers, :last_error if column_exists?(:bulkrax_importers, :last_error)
+ end
+end
diff --git a/db/migrate/20250811182737_increase_text_sizes.bulkrax.rb b/db/migrate/20250811182737_increase_text_sizes.bulkrax.rb
new file mode 100644
index 000000000..822e6a013
--- /dev/null
+++ b/db/migrate/20250811182737_increase_text_sizes.bulkrax.rb
@@ -0,0 +1,13 @@
+# This migration comes from bulkrax (originally 20210806065737)
+class IncreaseTextSizes < ActiveRecord::Migration[5.1]
+ def change
+ change_column :bulkrax_entries, :raw_metadata, :text, limit: 16777215
+ change_column :bulkrax_entries, :parsed_metadata, :text, limit: 16777215
+ change_column :bulkrax_exporters, :parser_fields, :text, limit: 16777215
+ change_column :bulkrax_exporters, :field_mapping, :text, limit: 16777215
+ change_column :bulkrax_importers, :parser_fields, :text, limit: 16777215
+ change_column :bulkrax_importers, :field_mapping, :text, limit: 16777215
+ change_column :bulkrax_importer_runs, :invalid_records, :text, limit: 16777215
+ change_column :bulkrax_statuses, :error_backtrace, :text, limit: 16777215
+ end
+end
diff --git a/db/migrate/20250811182738_change_bulkrax_statuses_error_message_column_type_to_text.bulkrax.rb b/db/migrate/20250811182738_change_bulkrax_statuses_error_message_column_type_to_text.bulkrax.rb
new file mode 100644
index 000000000..f50343df7
--- /dev/null
+++ b/db/migrate/20250811182738_change_bulkrax_statuses_error_message_column_type_to_text.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20211004170708)
+class ChangeBulkraxStatusesErrorMessageColumnTypeToText < ActiveRecord::Migration[5.1]
+ def change
+ change_column :bulkrax_statuses, :error_message, :text
+ end
+end
diff --git a/db/migrate/20250811182739_rename_children_counters_to_relationships.bulkrax.rb b/db/migrate/20250811182739_rename_children_counters_to_relationships.bulkrax.rb
new file mode 100644
index 000000000..a275e698c
--- /dev/null
+++ b/db/migrate/20250811182739_rename_children_counters_to_relationships.bulkrax.rb
@@ -0,0 +1,7 @@
+# This migration comes from bulkrax (originally 20211203195233)
+class RenameChildrenCountersToRelationships < ActiveRecord::Migration[5.1]
+ def change
+ rename_column :bulkrax_importer_runs, :processed_children, :processed_relationships unless column_exists?(:bulkrax_importer_runs, :processed_relationships)
+ rename_column :bulkrax_importer_runs, :failed_children, :failed_relationships unless column_exists?(:bulkrax_importer_runs, :failed_relationships)
+ end
+end
diff --git a/db/migrate/20250811182740_add_file_set_counters_to_importer_runs.bulkrax.rb b/db/migrate/20250811182740_add_file_set_counters_to_importer_runs.bulkrax.rb
new file mode 100644
index 000000000..708bbc615
--- /dev/null
+++ b/db/migrate/20250811182740_add_file_set_counters_to_importer_runs.bulkrax.rb
@@ -0,0 +1,8 @@
+# This migration comes from bulkrax (originally 20211220195027)
+class AddFileSetCountersToImporterRuns < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_importer_runs, :processed_file_sets, :integer, default: 0 unless column_exists?(:bulkrax_importer_runs, :processed_file_sets)
+ add_column :bulkrax_importer_runs, :failed_file_sets, :integer, default: 0 unless column_exists?(:bulkrax_importer_runs, :failed_file_sets)
+ add_column :bulkrax_importer_runs, :total_file_set_entries, :integer, default: 0 unless column_exists?(:bulkrax_importer_runs, :total_file_set_entries)
+ end
+end
diff --git a/db/migrate/20250811182741_add_import_attempts_to_entries.bulkrax.rb b/db/migrate/20250811182741_add_import_attempts_to_entries.bulkrax.rb
new file mode 100644
index 000000000..a12b84a8b
--- /dev/null
+++ b/db/migrate/20250811182741_add_import_attempts_to_entries.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20220118001339)
+class AddImportAttemptsToEntries < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_entries, :import_attempts, :integer, default: 0 unless column_exists?(:bulkrax_entries, :import_attempts)
+ end
+end
diff --git a/db/migrate/20250811182742_add_work_counters_to_importer_runs.bulkrax.rb b/db/migrate/20250811182742_add_work_counters_to_importer_runs.bulkrax.rb
new file mode 100644
index 000000000..44e40773e
--- /dev/null
+++ b/db/migrate/20250811182742_add_work_counters_to_importer_runs.bulkrax.rb
@@ -0,0 +1,7 @@
+# This migration comes from bulkrax (originally 20220119213325)
+class AddWorkCountersToImporterRuns < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_importer_runs, :processed_works, :integer, default: 0 unless column_exists?(:bulkrax_importer_runs, :processed_works)
+ add_column :bulkrax_importer_runs, :failed_works, :integer, default: 0 unless column_exists?(:bulkrax_importer_runs, :failed_works)
+ end
+end
diff --git a/db/migrate/20250811182743_create_bulkrax_pending_relationships.bulkrax.rb b/db/migrate/20250811182743_create_bulkrax_pending_relationships.bulkrax.rb
new file mode 100644
index 000000000..1ad1a3f1a
--- /dev/null
+++ b/db/migrate/20250811182743_create_bulkrax_pending_relationships.bulkrax.rb
@@ -0,0 +1,14 @@
+# This migration comes from bulkrax (originally 20220301001839)
+class CreateBulkraxPendingRelationships < ActiveRecord::Migration[5.1]
+ def change
+ unless table_exists?(:bulkrax_pending_relationships)
+ create_table :bulkrax_pending_relationships do |t|
+ t.belongs_to :bulkrax_importer_run, foreign_key: true, null: false
+ t.string :parent_id, null: false
+ t.string :child_id, null: false
+
+ t.timestamps
+ end
+ end
+ end
+end
diff --git a/db/migrate/20250811182744_add_order_to_bulkrax_pending_relationships.bulkrax.rb b/db/migrate/20250811182744_add_order_to_bulkrax_pending_relationships.bulkrax.rb
new file mode 100644
index 000000000..c782539c5
--- /dev/null
+++ b/db/migrate/20250811182744_add_order_to_bulkrax_pending_relationships.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20220303212810)
+class AddOrderToBulkraxPendingRelationships < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_pending_relationships, :order, :integer, default: 0 unless column_exists?(:bulkrax_pending_relationships, :order)
+ end
+end
diff --git a/db/migrate/20250811182745_add_include_thumbnails_to_bulkrax_exporters.bulkrax.rb b/db/migrate/20250811182745_add_include_thumbnails_to_bulkrax_exporters.bulkrax.rb
new file mode 100644
index 000000000..a69915345
--- /dev/null
+++ b/db/migrate/20250811182745_add_include_thumbnails_to_bulkrax_exporters.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20220412233954)
+class AddIncludeThumbnailsToBulkraxExporters < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_exporters, :include_thumbnails, :boolean, default: false unless column_exists?(:bulkrax_exporters, :include_thumbnails)
+ end
+end
diff --git a/db/migrate/20250811182746_add_generated_metadata_to_bulkrax_exporters.bulkrax.rb b/db/migrate/20250811182746_add_generated_metadata_to_bulkrax_exporters.bulkrax.rb
new file mode 100644
index 000000000..1aad9c9fe
--- /dev/null
+++ b/db/migrate/20250811182746_add_generated_metadata_to_bulkrax_exporters.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20220413180915)
+class AddGeneratedMetadataToBulkraxExporters < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_exporters, :generated_metadata, :boolean, default: false unless column_exists?(:bulkrax_exporters, :generated_metadata)
+ end
+end
diff --git a/db/migrate/20250811182747_rename_bulkrax_importer_run_to_importer_run.bulkrax.rb b/db/migrate/20250811182747_rename_bulkrax_importer_run_to_importer_run.bulkrax.rb
new file mode 100644
index 000000000..ad3b60082
--- /dev/null
+++ b/db/migrate/20250811182747_rename_bulkrax_importer_run_to_importer_run.bulkrax.rb
@@ -0,0 +1,18 @@
+# This migration comes from bulkrax (originally 20220609001128)
+class RenameBulkraxImporterRunToImporterRun < ActiveRecord::Migration[5.1]
+ def up
+ if column_exists?(:bulkrax_pending_relationships, :bulkrax_importer_run_id)
+ remove_foreign_key :bulkrax_pending_relationships, :bulkrax_importer_runs
+ remove_index :bulkrax_pending_relationships, column: :bulkrax_importer_run_id
+
+ rename_column :bulkrax_pending_relationships, :bulkrax_importer_run_id, :importer_run_id
+
+ add_foreign_key :bulkrax_pending_relationships, :bulkrax_importer_runs, column: :importer_run_id
+ add_index :bulkrax_pending_relationships, :importer_run_id, name: 'index_bulkrax_pending_relationships_on_importer_run_id'
+ end
+ end
+
+ def down
+ rename_column :bulkrax_pending_relationships, :importer_run_id, :bulkrax_importer_run_id
+ end
+end
diff --git a/db/migrate/20250811182748_add_indices_to_bulkrax.bulkrax.rb b/db/migrate/20250811182748_add_indices_to_bulkrax.bulkrax.rb
new file mode 100644
index 000000000..2028971ae
--- /dev/null
+++ b/db/migrate/20250811182748_add_indices_to_bulkrax.bulkrax.rb
@@ -0,0 +1,26 @@
+# This migration comes from bulkrax (originally 20230608153601)
+# This migration comes from bulkrax (originally 20230608153601)
+class AddIndicesToBulkrax < ActiveRecord::Migration[5.1]
+ def change
+ check_and_add_index :bulkrax_entries, :identifier
+ check_and_add_index :bulkrax_entries, :type
+ check_and_add_index :bulkrax_entries, [:importerexporter_id, :importerexporter_type], name: 'bulkrax_entries_importerexporter_idx'
+ check_and_add_index :bulkrax_pending_relationships, :parent_id
+ check_and_add_index :bulkrax_pending_relationships, :child_id
+ check_and_add_index :bulkrax_statuses, [:statusable_id, :statusable_type], name: 'bulkrax_statuses_statusable_idx'
+ check_and_add_index :bulkrax_statuses, [:runnable_id, :runnable_type], name: 'bulkrax_statuses_runnable_idx'
+ check_and_add_index :bulkrax_statuses, :error_class
+ end
+
+ if RUBY_VERSION =~ /^2/
+ def check_and_add_index(table_name, column_name, options = {})
+ add_index(table_name, column_name, options) unless index_exists?(table_name, column_name, options)
+ end
+ elsif RUBY_VERSION =~ /^3/
+ def check_and_add_index(table_name, column_name, **options)
+ add_index(table_name, column_name, **options) unless index_exists?(table_name, column_name, **options)
+ end
+ else
+ raise "Ruby version #{RUBY_VERSION} is unknown"
+ end
+end
diff --git a/db/migrate/20250811182749_denormalize_status_message.bulkrax.rb b/db/migrate/20250811182749_denormalize_status_message.bulkrax.rb
new file mode 100644
index 000000000..b8784bbdc
--- /dev/null
+++ b/db/migrate/20250811182749_denormalize_status_message.bulkrax.rb
@@ -0,0 +1,8 @@
+# This migration comes from bulkrax (originally 20240208005801)
+class DenormalizeStatusMessage < ActiveRecord::Migration[5.2]
+ def change
+ add_column :bulkrax_entries, :status_message, :string, default: 'Pending' unless column_exists?(:bulkrax_entries, :status_message)
+ add_column :bulkrax_importers, :status_message, :string, default: 'Pending' unless column_exists?(:bulkrax_importers, :status_message)
+ add_column :bulkrax_exporters, :status_message, :string, default: 'Pending' unless column_exists?(:bulkrax_exporters, :status_message)
+ end
+end
diff --git a/db/migrate/20250811182750_update_identifier_index.bulkrax.rb b/db/migrate/20250811182750_update_identifier_index.bulkrax.rb
new file mode 100644
index 000000000..396857423
--- /dev/null
+++ b/db/migrate/20250811182750_update_identifier_index.bulkrax.rb
@@ -0,0 +1,7 @@
+# This migration comes from bulkrax (originally 20240209070952)
+class UpdateIdentifierIndex < ActiveRecord::Migration[5.2]
+ def change
+ remove_index :bulkrax_entries, :identifier if index_exists?(:bulkrax_entries, :identifier )
+ add_index :bulkrax_entries, [:identifier, :importerexporter_id, :importerexporter_type], name: 'bulkrax_identifier_idx' unless index_exists?(:bulkrax_entries, [:identifier, :importerexporter_id, :importerexporter_type], name: 'bulkrax_identifier_idx')
+ end
+end
diff --git a/db/migrate/20250811182751_add_index_to_metadata_bulkrax_identifier.bulkrax.rb b/db/migrate/20250811182751_add_index_to_metadata_bulkrax_identifier.bulkrax.rb
new file mode 100644
index 000000000..f4678d5e2
--- /dev/null
+++ b/db/migrate/20250811182751_add_index_to_metadata_bulkrax_identifier.bulkrax.rb
@@ -0,0 +1,19 @@
+# This migration comes from bulkrax (originally 20240307053156)
+class AddIndexToMetadataBulkraxIdentifier < ActiveRecord::Migration[5.2]
+ def up
+ return unless table_exists?(:orm_resources)
+ return if index_exists?(:orm_resources, "(((metadata -> 'bulkrax_identifier'::text) ->> 0))", name: 'index_on_bulkrax_identifier')
+
+ # This creates an expression index on the first element of the bulkrax_identifier array
+ add_index :orm_resources,
+ "(metadata -> 'bulkrax_identifier' ->> 0)",
+ name: 'index_on_bulkrax_identifier',
+ where: "metadata -> 'bulkrax_identifier' IS NOT NULL"
+ end
+
+ def down
+ return unless table_exists?(:orm_resources)
+
+ remove_index :orm_resources, name: 'index_on_bulkrax_identifier'
+ end
+end
diff --git a/db/migrate/20250811182752_add_file_name_to_uploaded_files.bulkrax.rb b/db/migrate/20250811182752_add_file_name_to_uploaded_files.bulkrax.rb
new file mode 100644
index 000000000..2e8216098
--- /dev/null
+++ b/db/migrate/20250811182752_add_file_name_to_uploaded_files.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20240806161142)
+class AddFileNameToUploadedFiles < ActiveRecord::Migration[5.2]
+ def change
+ add_column :uploaded_files, :filename, :string if table_exists?(:uploaded_files) && !column_exists?(:uploaded_files, :filename)
+ end
+end
diff --git a/db/migrate/20250811182753_add_error_tracking_to_pending_relationships.bulkrax.rb b/db/migrate/20250811182753_add_error_tracking_to_pending_relationships.bulkrax.rb
new file mode 100644
index 000000000..be828f61b
--- /dev/null
+++ b/db/migrate/20250811182753_add_error_tracking_to_pending_relationships.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20240823173525)
+class AddErrorTrackingToPendingRelationships < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_pending_relationships, :status_message, :string, default: 'Pending' unless column_exists?(:bulkrax_pending_relationships, :status_message)
+ end
+end
diff --git a/db/migrate/20250811182754_add_last_imported_at_to_bulkrax_importers.bulkrax.rb b/db/migrate/20250811182754_add_last_imported_at_to_bulkrax_importers.bulkrax.rb
new file mode 100644
index 000000000..6f3ecbfdf
--- /dev/null
+++ b/db/migrate/20250811182754_add_last_imported_at_to_bulkrax_importers.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20240916182737)
+class AddLastImportedAtToBulkraxImporters < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_importers, :last_imported_at, :datetime unless column_exists?(:bulkrax_importers, :last_imported_at)
+ end
+end
diff --git a/db/migrate/20250811182755_add_next_import_at_to_bulkrax_importers.bulkrax.rb b/db/migrate/20250811182755_add_next_import_at_to_bulkrax_importers.bulkrax.rb
new file mode 100644
index 000000000..14291602b
--- /dev/null
+++ b/db/migrate/20250811182755_add_next_import_at_to_bulkrax_importers.bulkrax.rb
@@ -0,0 +1,6 @@
+# This migration comes from bulkrax (originally 20240916182823)
+class AddNextImportAtToBulkraxImporters < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_importers, :next_import_at, :datetime unless column_exists?(:bulkrax_importers, :next_import_at)
+ end
+end
diff --git a/db/migrate/20250811182756_entry_error_denormalization.bulkrax.rb b/db/migrate/20250811182756_entry_error_denormalization.bulkrax.rb
new file mode 100644
index 000000000..6fe328bb0
--- /dev/null
+++ b/db/migrate/20250811182756_entry_error_denormalization.bulkrax.rb
@@ -0,0 +1,8 @@
+# This migration comes from bulkrax (originally 20241203010707)
+class EntryErrorDenormalization < ActiveRecord::Migration[5.1]
+ def change
+ add_column :bulkrax_entries, :error_class, :string unless column_exists?(:bulkrax_entries, :error_class)
+ add_column :bulkrax_importers, :error_class, :string unless column_exists?(:bulkrax_importers, :error_class)
+ add_column :bulkrax_exporters, :error_class, :string unless column_exists?(:bulkrax_exporters, :error_class)
+ end
+end
diff --git a/db/migrate/20250811182757_faster_first_entry.bulkrax.rb b/db/migrate/20250811182757_faster_first_entry.bulkrax.rb
new file mode 100644
index 000000000..5fd256b59
--- /dev/null
+++ b/db/migrate/20250811182757_faster_first_entry.bulkrax.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+# This migration comes from bulkrax (originally 20241205212513)
+class FasterFirstEntry < ActiveRecord::Migration[5.2]
+ def change
+ add_index :bulkrax_entries, [:importerexporter_id, :importerexporter_type, :id], name: 'index_bulkrax_entries_on_importerexporter_id_type_and_id' unless index_exists?(:bulkrax_entries, [:importerexporter_id, :importerexporter_type, :id],
+ name: 'index_bulkrax_entries_on_importerexporter_id_type_and_id')
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 29b146688..b680695e5 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2022_06_09_001128) do
+ActiveRecord::Schema.define(version: 2025_08_11_182757) do
create_table "bookmarks", force: :cascade do |t|
t.integer "user_id", null: false
@@ -37,6 +37,12 @@
t.datetime "last_succeeded_at"
t.string "importerexporter_type", default: "Bulkrax::Importer"
t.integer "import_attempts", default: 0
+ t.string "status_message", default: "Pending"
+ t.string "error_class"
+ t.index ["identifier", "importerexporter_id", "importerexporter_type"], name: "bulkrax_identifier_idx"
+ t.index ["importerexporter_id", "importerexporter_type", "id"], name: "index_bulkrax_entries_on_importerexporter_id_type_and_id"
+ t.index ["importerexporter_id", "importerexporter_type"], name: "bulkrax_entries_importerexporter_idx"
+ t.index ["type"], name: "index_bulkrax_entries_on_type"
end
create_table "bulkrax_exporter_runs", force: :cascade do |t|
@@ -69,6 +75,8 @@
t.string "workflow_status"
t.boolean "include_thumbnails", default: false
t.boolean "generated_metadata", default: false
+ t.string "status_message", default: "Pending"
+ t.string "error_class"
t.index ["user_id"], name: "index_bulkrax_exporters_on_user_id"
end
@@ -92,6 +100,8 @@
t.integer "total_file_set_entries", default: 0
t.integer "processed_works", default: 0
t.integer "failed_works", default: 0
+ t.integer "processed_children", default: 0
+ t.integer "failed_children", default: 0
t.index ["importer_id"], name: "index_bulkrax_importer_runs_on_importer_id"
end
@@ -109,6 +119,10 @@
t.boolean "validate_only"
t.datetime "last_error_at"
t.datetime "last_succeeded_at"
+ t.string "status_message", default: "Pending"
+ t.datetime "last_imported_at"
+ t.datetime "next_import_at"
+ t.string "error_class"
t.index ["user_id"], name: "index_bulkrax_importers_on_user_id"
end
@@ -119,7 +133,10 @@
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "order", default: 0
+ t.string "status_message", default: "Pending"
+ t.index ["child_id"], name: "index_bulkrax_pending_relationships_on_child_id"
t.index ["importer_run_id"], name: "index_bulkrax_pending_relationships_on_importer_run_id"
+ t.index ["parent_id"], name: "index_bulkrax_pending_relationships_on_parent_id"
end
create_table "bulkrax_statuses", force: :cascade do |t|
@@ -133,6 +150,9 @@
t.string "runnable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.index ["error_class"], name: "index_bulkrax_statuses_on_error_class"
+ t.index ["runnable_id", "runnable_type"], name: "bulkrax_statuses_runnable_idx"
+ t.index ["statusable_id", "statusable_type"], name: "bulkrax_statuses_statusable_idx"
end
create_table "change_manager_changes", force: :cascade do |t|
@@ -656,6 +676,7 @@
t.string "file_set_uri"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
+ t.string "filename"
t.index ["file_set_uri"], name: "index_uploaded_files_on_file_set_uri"
t.index ["user_id"], name: "index_uploaded_files_on_user_id"
end
diff --git a/db/seeds.rb b/db/seeds.rb
index a3a1edbe7..83b268b71 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -104,15 +104,28 @@ class AddSeedObjects < ActiveRecord::Migration[5.1]
puts "#{works_per_user} Generic Works created for #{user.email}"
end
- # Generate complete works
+ # Get (or create) the default "User Collection" type, then use its GID
+ default_ct =
+ if Hyrax::CollectionType.respond_to?(:find_or_create_default_collection_type)
+ Hyrax::CollectionType.find_or_create_default_collection_type
+ else
+ Hyrax::CollectionType.find_by(machine_id: Hyrax::CollectionType::USER_COLLECTION_MACHINE_ID) ||
+ Hyrax::CollectionType.create!(title: 'User Collection',
+ machine_id: Hyrax::CollectionType::USER_COLLECTION_MACHINE_ID,
+ nestable: true, discoverable: true, sharable: true,
+ share_applies_to_new_works: true, allow_multiple_membership: true,
+ require_membership: false, assigns_workflow: false, assigns_visibility: false)
+ end
+ default_ct_gid = default_ct.to_global_id.to_s
+ # Generate complete works
complete_collection = Collection.create(
title: ["Complete Works"],
depositor: many_deposits.email,
creator: ["Deposits, Many"],
edit_users: [many_deposits.email],
description: ["This is a collection of works with all their metadata filled in."],
- collection_type_gid: "gid://scholar-uc/hyrax-collectiontype/1",
+ collection_type_gid: default_ct_gid,
visibility: Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PUBLIC
)
diff --git a/spec/requests/bulkrax/bulkrax_exporters_entry_table_spec.rb b/spec/requests/bulkrax/bulkrax_exporters_entry_table_spec.rb
new file mode 100644
index 000000000..b79531144
--- /dev/null
+++ b/spec/requests/bulkrax/bulkrax_exporters_entry_table_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+# spec/requests/bulkrax/exporters_entry_table_spec.rb
+require 'rails_helper'
+
+RSpec.describe 'Bulkrax exporter entry_table', type: :request do
+ include Devise::Test::IntegrationHelpers
+ include Warden::Test::Helpers
+
+ before { Warden.test_mode! }
+ after { Warden.test_reset! }
+
+ it 'returns JSON (even with no entries) for an admin user' do
+ user = User.find_by(email: 'spec-admin@example.com') ||
+ User.create!(email: 'spec-admin@example.com', password: 'password123')
+
+ # grant admin
+ role = Role.find_or_create_by!(name: 'admin')
+ role.users << user unless role.users.include?(user)
+
+ exporter = Bulkrax::Exporter.create!(
+ name: 'Test Exporter',
+ user: user,
+ parser_klass: 'Bulkrax::CsvParser',
+ export_type: 'metadata',
+ export_from: 'worktype',
+ export_source: 'GenericWork', # change if your work class differs
+ generated_metadata: false,
+ include_thumbnails: false
+ )
+
+ sign_in user
+ login_as(user, scope: :user)
+
+ # Route helper can differ by mount/engine; the raw path is stable in your app:
+ get "/exporters/#{exporter.id}/entry_table.json"
+
+ expect(response).to have_http_status(:ok).or have_http_status(:no_content)
+ JSON.parse(response.body) if response.body.present?
+ end
+end