diff --git a/.env.development b/.env.development index 6060a8815..ca07e2000 100644 --- a/.env.development +++ b/.env.development @@ -1,6 +1,6 @@ # Application SCHOLAR_APPLICATION_URL=http://localhost:3000 -SCHOLAR_JOB_QUEUE_ADAPTER=:inline +SCHOLAR_JOB_QUEUE_ADAPTER=sidekiq SCHOLAR_PRODUCTION_MAILER_URL=localhost:3000 SCHOLAR_INITIALIZERS_SECRET_KEY=secret_key diff --git a/Gemfile b/Gemfile index e2fa468f7..3257dc3c3 100644 --- a/Gemfile +++ b/Gemfile @@ -12,7 +12,7 @@ gem 'bigdecimal', '1.3.5' # For BrowseEverything to work with Hyrax 2.x then we need to pin BE gem 'browse-everything', '1.1.0' -gem 'bulkrax', '~> 4.3.0' +gem 'bulkrax', '~> 9.1.0' gem 'hydra-remote_identifier', github: 'uclibs/hydra-remote_identifier', branch: 'scholar-datacite' gem 'kaltura', '0.1.1' gem 'rack', '2.2.3' diff --git a/Gemfile.lock b/Gemfile.lock index d5256398f..ae9bb144f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -80,15 +80,15 @@ GEM rdf-vocab (< 3.1.5) rsolr (>= 1.1.2, < 3) ruby-progressbar (~> 1.0) - active-triples (1.1.1) + active-triples (1.2.0) activemodel (>= 3.0.0) activesupport (>= 3.0.0) rdf (>= 2.0.2, < 4.0) rdf-vocab (>= 2.0, < 4.0) - active_attr (0.15.4) - actionpack (>= 3.0.2, < 7.1) - activemodel (>= 3.0.2, < 7.1) - activesupport (>= 3.0.2, < 7.1) + active_attr (0.17.1) + actionpack (>= 3.0.2, < 8.1) + activemodel (>= 3.0.2, < 8.1) + activesupport (>= 3.0.2, < 8.1) active_encode (0.8.2) rails sprockets (< 4) @@ -97,15 +97,15 @@ GEM globalid (>= 0.3.6) activemodel (5.2.4.6) activesupport (= 5.2.4.6) - activemodel-serializers-xml (1.0.2) - activemodel (> 5.x) - activesupport (> 5.x) + activemodel-serializers-xml (1.0.3) + activemodel (>= 5.0.0.a) + activesupport (>= 5.0.0.a) builder (~> 3.1) activerecord (5.2.4.6) activemodel (= 5.2.4.6) activesupport (= 5.2.4.6) arel (>= 9.0) - activerecord-import (1.4.0) + activerecord-import (2.2.0) activerecord (>= 4.2) activestorage (5.2.4.6) actionpack (= 5.2.4.6) @@ -116,39 +116,41 @@ GEM i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - addressable (2.8.1) - public_suffix (>= 2.0.2, < 6.0) - airbrussh (1.4.1) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) + airbrussh (1.5.3) sshkit (>= 1.6.1, != 1.7.0) almond-rails (0.3.0) rails (>= 4.2) - amazing_print (1.4.0) arel (9.0.0) - ast (2.4.2) - autoprefixer-rails (10.4.7.0) + ast (2.4.3) + autoprefixer-rails (10.4.21.0) execjs (~> 2) - awesome_nested_set (3.5.0) - activerecord (>= 4.0.0, < 7.1) - aws-eventstream (1.2.0) - aws-partitions (1.626.0) - aws-sdk-core (3.142.0) - aws-eventstream (~> 1, >= 1.0.2) - aws-partitions (~> 1, >= 1.525.0) - aws-sigv4 (~> 1.1) + awesome_nested_set (3.8.0) + activerecord (>= 4.0.0, < 8.1) + aws-eventstream (1.4.0) + aws-partitions (1.1144.0) + aws-sdk-core (3.229.0) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.58.0) - aws-sdk-core (~> 3, >= 3.127.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.114.0) - aws-sdk-core (~> 3, >= 3.127.0) + logger + aws-sdk-kms (1.110.0) + aws-sdk-core (~> 3, >= 3.228.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.196.1) + aws-sdk-core (~> 3, >= 3.228.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.4) + aws-sigv4 (~> 1.5) aws-sdk-xray (1.4.0) aws-sdk-core (~> 3) aws-sigv4 (~> 1.0) - aws-sigv4 (1.5.1) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) - aws-xray-sdk (0.13.0) + aws-xray-sdk (0.16.0) aws-sdk-xray (~> 1.4.0) multi_json (~> 1) axiom-types (0.1.1) @@ -159,12 +161,13 @@ GEM babel-transpiler (0.7.0) babel-source (>= 4.0, < 6) execjs (~> 2.0) - bagit (0.4.4) + bagit (0.6.0) docopt (~> 0.5.0) validatable (~> 1.6) + base64 (0.3.0) bcp47 (0.3.3) i18n - bcrypt (3.1.18) + bcrypt (3.1.20) bigdecimal (1.3.5) bindex (0.8.1) bixby (5.0.2) @@ -198,7 +201,7 @@ GEM bootstrap_form (5.1.0) actionpack (>= 5.2) activemodel (>= 5.2) - brakeman (5.3.1) + brakeman (5.4.1) breadcrumbs_on_rails (3.0.1) browse-everything (1.1.0) addressable (~> 2.5) @@ -212,34 +215,36 @@ GEM signet (~> 0.8) sprockets (~> 3.7) typhoeus - builder (3.2.4) - bulkrax (4.3.0) - bagit (~> 0.4) + builder (3.3.0) + bulkrax (9.1.0) + bagit (~> 0.6.0) coderay + denormalize_fields iso8601 (~> 0.9.0) kaminari language_list (~> 1.2, >= 1.2.1) - libxml-ruby (~> 3.1.0) + libxml-ruby (~> 5.0) loofah (>= 2.2.3) + marcel oai (>= 0.4, < 2.x) rack (>= 2.0.6) rails (>= 5.1.6) rdf (>= 2.0.2, < 4.0) rubyzip simple_form - bundler-audit (0.9.1) + bundler-audit (0.9.2) bundler (>= 1.2.0, < 3) thor (~> 1.0) byebug (11.1.3) cancancan (1.17.0) - capistrano (3.17.1) + capistrano (3.19.2) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) sshkit (>= 1.9.0) capistrano-bundler (1.6.0) capistrano (~> 3.1) - capistrano-rails (1.6.2) + capistrano-rails (1.7.0) capistrano (~> 3.1) capistrano-bundler (>= 1.1, < 3) capistrano-rvm (0.1.2) @@ -255,14 +260,14 @@ GEM capybara-maleficent (0.3.0) activesupport (>= 4.2.0) capybara (>= 2.13, < 4.0) - carrierwave (1.3.2) + carrierwave (1.3.4) activemodel (>= 4.0.0) activesupport (>= 4.0.0) mime-types (>= 1.16) - ssrf_filter (~> 1.0) + ssrf_filter (~> 1.0, < 1.1.0) childprocess (0.9.0) ffi (~> 1.0, >= 1.0.11) - clamby (1.6.8) + clamby (1.6.11) clipboard-rails (1.7.1) coderay (1.1.3) coercible (1.0.0) @@ -274,23 +279,28 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.1.10) - connection_pool (2.2.5) - coveralls_reborn (0.28.0) + concurrent-ruby (1.3.5) + connection_pool (2.5.3) + coveralls_reborn (0.29.0) simplecov (~> 0.22.0) term-ansicolor (~> 1.7) thor (~> 1.2) tins (~> 1.32) - crack (0.4.5) + crack (1.0.0) + bigdecimal rexml crass (1.0.6) - database_cleaner (2.0.1) - database_cleaner-active_record (~> 2.0.0) - database_cleaner-active_record (2.0.1) + csv (3.3.5) + database_cleaner (2.1.0) + database_cleaner-active_record (>= 2, < 3) + database_cleaner-active_record (2.2.2) activerecord (>= 5.a) - database_cleaner-core (~> 2.0.0) + database_cleaner-core (~> 2.0) database_cleaner-core (2.0.1) + date (3.4.1) declarative (0.0.20) + denormalize_fields (1.3.0) + activerecord (>= 4.1.14, < 8.0.0) deprecation (1.1.0) activesupport descendants_tracker (0.0.4) @@ -301,18 +311,17 @@ GEM railties (>= 4.1.0, < 6.0) responders warden (~> 1.2.3) - devise-guests (0.8.1) + devise-guests (0.8.3) devise - diff-lcs (1.5.0) - docile (1.4.0) + diff-lcs (1.6.2) + docile (1.4.1) docopt (0.5.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) + domain_name (0.6.20240107) dotenv (2.8.1) dotenv-rails (2.8.1) dotenv (= 2.8.1) railties (>= 3.2) - draper (4.0.2) + draper (4.0.4) actionpack (>= 5.0) activemodel (>= 5.0) activemodel-serializers-xml (>= 1.0) @@ -322,76 +331,78 @@ GEM dropbox_api (0.1.21) faraday (< 3.0) oauth2 (~> 1.1) - dry-configurable (0.15.0) - concurrent-ruby (~> 1.0) - dry-core (~> 0.6) - dry-container (0.10.1) - concurrent-ruby (~> 1.0) - dry-core (0.8.1) + dry-configurable (1.0.1) + dry-core (~> 1.0, < 2) + zeitwerk (~> 2.6) + dry-core (1.0.0) concurrent-ruby (~> 1.0) + zeitwerk (~> 2.6) dry-equalizer (0.3.0) - dry-events (0.3.0) + dry-events (1.0.1) concurrent-ruby (~> 1.0) - dry-core (~> 0.5, >= 0.5) - dry-inflector (0.3.0) + dry-core (~> 1.0, < 2) + dry-inflector (1.1.0) dry-initializer (3.1.1) - dry-logic (1.2.0) + dry-logic (1.5.0) concurrent-ruby (~> 1.0) - dry-core (~> 0.5, >= 0.5) - dry-matcher (0.9.0) - dry-core (~> 0.4, >= 0.4.8) - dry-monads (1.4.0) + dry-core (~> 1.0, < 2) + zeitwerk (~> 2.6) + dry-matcher (0.10.0) + dry-core (~> 1.0) + dry-monads (1.6.0) concurrent-ruby (~> 1.0) - dry-core (~> 0.7) - dry-schema (1.10.2) + dry-core (~> 1.0, < 2) + zeitwerk (~> 2.6) + dry-schema (1.13.4) concurrent-ruby (~> 1.0) - dry-configurable (~> 0.13, >= 0.13.0) - dry-core (~> 0.5, >= 0.5) + dry-configurable (~> 1.0, >= 1.0.1) + dry-core (~> 1.0, < 2) dry-initializer (~> 3.0) - dry-logic (~> 1.0) - dry-types (~> 1.5) - dry-struct (1.4.0) - dry-core (~> 0.5, >= 0.5) - dry-types (~> 1.5) + dry-logic (>= 1.4, < 2) + dry-types (>= 1.7, < 2) + zeitwerk (~> 2.6) + dry-struct (1.6.0) + dry-core (~> 1.0, < 2) + dry-types (>= 1.7, < 2) ice_nine (~> 0.11) - dry-transaction (0.13.3) - dry-container (>= 0.2.8) - dry-events (>= 0.1.0) - dry-matcher (>= 0.7.0) - dry-monads (>= 0.4.0) - dry-types (1.5.1) + zeitwerk (~> 2.6) + dry-transaction (0.15.0) + dry-core (~> 1.0) + dry-events (~> 1.0) + dry-matcher (~> 0.10) + dry-monads (~> 1.6) + dry-types (1.7.1) concurrent-ruby (~> 1.0) - dry-container (~> 0.3) - dry-core (~> 0.5, >= 0.5) - dry-inflector (~> 0.1, >= 0.1.2) - dry-logic (~> 1.0, >= 1.0.2) - dry-validation (1.8.1) + dry-core (~> 1.0) + dry-inflector (~> 1.0) + dry-logic (~> 1.4) + zeitwerk (~> 2.6) + dry-validation (1.10.0) concurrent-ruby (~> 1.0) - dry-container (~> 0.7, >= 0.7.1) - dry-core (~> 0.5, >= 0.5) + dry-core (~> 1.0, < 2) dry-initializer (~> 3.0) - dry-schema (~> 1.8, >= 1.8.0) - ebnf (2.3.1) - amazing_print (~> 1.4) + dry-schema (>= 1.12, < 2) + zeitwerk (~> 2.6) + ebnf (2.3.5) htmlentities (~> 4.3) rdf (~> 3.2) scanf (~> 1.0) sxp (~> 1.2) - unicode-types (~> 1.7) - email_validator (2.2.3) + unicode-types (~> 1.8) + email_validator (2.2.4) activemodel equivalent-xml (0.6.0) nokogiri (>= 1.4.3) - erubi (1.11.0) - ethon (0.15.0) + erubi (1.13.1) + ethon (0.16.0) ffi (>= 1.15.0) - execjs (2.8.1) + execjs (2.10.0) factory_bot (4.11.1) activesupport (>= 3.0.0) factory_bot_rails (4.11.1) factory_bot (~> 4.11.1) railties (>= 3.0.0) - faraday (0.17.5) + faraday (0.17.6) multipart-post (>= 1.2, < 3) faraday-encoding (0.0.4) faraday @@ -399,27 +410,30 @@ GEM faraday (>= 0.7.4, < 1.0) fcrepo_wrapper (0.9.0) ruby-progressbar - ffi (1.15.5) - ffi-compiler (1.0.1) - ffi (>= 1.0.0) + ffi (1.17.2-arm64-darwin) + ffi (1.17.2-x86_64-darwin) + ffi-compiler (1.3.2) + ffi (>= 1.15.5) rake - figaro (1.2.0) + figaro (1.3.0) thor (>= 0.14.0, < 2) - flipflop (2.7.0) + flipflop (2.8.0) activesupport (>= 4.0) terminal-table (>= 1.8) flot-rails (0.0.7) jquery-rails - font-awesome-rails (4.7.0.8) - railties (>= 3.2, < 8.0) + font-awesome-rails (4.7.0.9) + railties (>= 3.2, < 9.0) gems (1.2.0) - geocoder (1.8.0) - globalid (1.0.0) + geocoder (1.8.5) + base64 (>= 0.1.0) + csv (>= 3.0.0) + globalid (1.1.0) activesupport (>= 5.0) google-api-client (0.53.0) google-apis-core (~> 0.1) google-apis-generator (~> 0.1) - google-apis-core (0.7.0) + google-apis-core (0.11.3) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -427,19 +441,18 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.a) rexml - webrick - google-apis-discovery_v1 (0.11.0) - google-apis-core (>= 0.7, < 2.a) - google-apis-drive_v3 (0.27.0) - google-apis-core (>= 0.7, < 2.a) - google-apis-generator (0.9.0) + google-apis-discovery_v1 (0.14.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-drive_v3 (0.46.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-generator (0.12.0) activesupport (>= 5.0) gems (~> 1.2) - google-apis-core (>= 0.7, < 2.a) + google-apis-core (>= 0.11.0, < 2.a) google-apis-discovery_v1 (~> 0.5) thor (>= 0.20, < 2.a) - google-apis-sheets_v4 (0.17.0) - google-apis-core (>= 0.7, < 2.a) + google-apis-sheets_v4 (0.26.0) + google-apis-core (>= 0.11.0, < 2.a) google_drive (3.0.7) google-apis-drive_v3 (>= 0.5.0, < 1.0.0) google-apis-sheets_v4 (>= 0.4.0, < 1.0.0) @@ -452,8 +465,8 @@ GEM multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (~> 0.15) - grape (1.6.2) - activesupport + grape (2.0.0) + activesupport (>= 5) builder dry-types (>= 1.1) mustermann-grape (~> 1.0.0) @@ -464,23 +477,25 @@ GEM haml (5.2.2) temple (>= 0.8.0) tilt - hashdiff (1.0.1) + hashdiff (1.2.0) hashie (5.0.0) hiredis (0.6.3) htmlentities (4.3.4) - http (5.1.0) + http (5.3.1) addressable (~> 2.8) http-cookie (~> 1.0) http-form_data (~> 2.2) - llhttp-ffi (~> 0.4.0) - http-cookie (1.0.5) + llhttp-ffi (~> 0.5.0) + http-cookie (1.0.8) domain_name (~> 0.5) http-form_data (2.3.0) - http_logger (0.7.0) - httparty (0.20.0) - mime-types (~> 3.0) + http_logger (1.0.1) + httparty (0.23.1) + csv + mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) - httpclient (2.8.3) + httpclient (2.9.0) + mutex_m hydra-access-controls (11.0.7) active-fedora (>= 10.0.0) activesupport (>= 4, < 6) @@ -508,21 +523,23 @@ GEM simple_form (>= 4.1.0, < 6.0) sprockets (~> 3.7) sprockets-es6 - hydra-file_characterization (1.1.2) + hydra-file_characterization (1.2.0) activesupport (>= 3.0.0) hydra-head (11.0.7) hydra-access-controls (= 11.0.7) hydra-core (= 11.0.7) rails (>= 5.2, < 6.1) - hydra-pcdm (1.1.0) - active-fedora (>= 10, < 14) + hydra-pcdm (1.4.0) + active-fedora (>= 10) mime-types (>= 1) - hydra-role-management (1.0.3) - blacklight + rdf-vocab + hydra-role-management (1.2.0) + blacklight (< 8) bootstrap_form bundler (>= 1.5) cancancan json (>= 1.8) + psych (~> 3.0) hydra-works (1.2.0) activesupport (>= 4.2.10, < 6.0) hydra-derivatives (~> 3.0) @@ -579,44 +596,45 @@ GEM signet solrizer (>= 3.4, < 5) tinymce-rails (~> 4.1) - i18n (1.12.0) + i18n (1.14.7) concurrent-ruby (~> 1.0) ice_nine (0.11.2) - iiif-image-api (0.2.0) + iiif-image-api (0.3.0) activesupport iiif_manifest (0.5.0) activesupport (>= 4) iso8601 (0.9.1) - jbuilder (2.11.5) + jbuilder (2.13.0) actionview (>= 5.0.0) activesupport (>= 5.0.0) - jmespath (1.6.1) + jmespath (1.6.2) jquery-datatables-rails (3.4.0) actionpack (>= 3.1) jquery-rails railties (>= 3.1) sass-rails - jquery-rails (4.5.0) + jquery-rails (4.6.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) jquery-ui-rails (6.0.1) railties (>= 3.2.16) - json (2.6.2) - json-canonicalization (0.3.0) - json-ld (3.2.3) + json (2.13.2) + json-canonicalization (0.4.0) + json-ld (3.2.5) htmlentities (~> 4.3) - json-canonicalization (~> 0.3) + json-canonicalization (~> 0.3, >= 0.3.2) link_header (~> 0.0, >= 0.0.8) multi_json (~> 1.15) - rack (~> 2.2) - rdf (~> 3.2, >= 3.2.9) - json-ld-preloaded (3.2.0) + rack (>= 2.2, < 4) + rdf (~> 3.2, >= 3.2.10) + json-ld-preloaded (3.2.2) json-ld (~> 3.2) rdf (~> 3.2) - json-schema (3.0.0) - addressable (>= 2.8) - jwt (2.5.0) + json-schema (5.1.0) + addressable (~> 2.8) + jwt (2.10.2) + base64 kaltura (0.1.1) hashie (>= 1.0.0) httparty (>= 0.7.8) @@ -635,8 +653,8 @@ GEM kaminari_route_prefix (0.1.1) kaminari (~> 1.0) language_list (1.2.1) - ld-patch (3.2.0) - ebnf (~> 2.2) + ld-patch (3.2.2) + ebnf (~> 2.3) rdf (~> 3.2) rdf-xsd (~> 3.2) sparql (~> 3.2) @@ -658,7 +676,7 @@ GEM rdf-vocab (~> 3.0) legato (0.7.0) multi_json - libxml-ruby (3.1.0) + libxml-ruby (5.0.5) link_header (0.0.8) linkeddata (3.1.1) equivalent-xml (~> 0.6) @@ -689,15 +707,18 @@ GEM listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - llhttp-ffi (0.4.0) + llhttp-ffi (0.5.1) ffi-compiler (~> 1.0) rake (~> 13.0) - logger (1.5.1) - loofah (2.18.0) + logger (1.7.0) + loofah (2.24.1) crass (~> 1.0.2) - nokogiri (>= 1.5.9) - mail (2.7.1) + nokogiri (>= 1.12.0) + mail (2.8.1) mini_mime (>= 0.1.1) + net-imap + net-pop + net-smtp mailboxer (0.15.1) carrierwave (>= 0.5.8) rails (>= 5.0.0) @@ -705,43 +726,56 @@ GEM activesupport marcel (0.3.3) mimemagic (~> 0.3.2) - matrix (0.4.2) + matrix (0.4.3) memoist (0.16.2) - method_source (1.0.0) - mime-types (3.4.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2022.0105) + method_source (1.1.0) + mime-types (3.7.0) + logger + mime-types-data (~> 3.2025, >= 3.2025.0507) + mime-types-data (3.2025.0805) mimemagic (0.3.10) nokogiri (~> 1) rake - mini_magick (4.11.0) - mini_mime (1.1.2) - mini_portile2 (2.8.0) - minitar (0.9) - minitest (5.16.3) + mini_magick (4.13.2) + mini_mime (1.1.5) + minitar (0.12.1) + minitest (5.25.5) multi_json (1.15.0) multi_xml (0.6.0) - multipart-post (2.2.3) - mustermann (3.0.0) + multipart-post (2.4.1) + mustermann (3.0.4) ruby2_keywords (~> 0.0.1) mustermann-grape (1.0.2) mustermann (>= 1.0.0) + mutex_m (0.3.0) mysql2 (0.5.6) nest (3.2.0) redic - net-http-persistent (4.0.1) - connection_pool (~> 2.2) - net-scp (1.2.1) - net-ssh (>= 2.6.5) - net-ssh (7.0.1) + net-http-persistent (4.0.6) + connection_pool (~> 2.2, >= 2.2.4) + net-imap (0.4.22) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-scp (4.1.0) + net-ssh (>= 2.6.5, < 8.0.0) + net-sftp (4.0.0) + net-ssh (>= 5.0.0, < 8.0.0) + net-smtp (0.5.1) + net-protocol + net-ssh (7.3.0) netrc (0.11.0) - nio4r (2.5.8) + nio4r (2.7.4) noid (0.9.0) noid-rails (3.0.3) actionpack (>= 5.0.0, < 7) noid (~> 0.9) - nokogiri (1.13.8) - mini_portile2 (~> 2.8.0) + nokogiri (1.15.7-arm64-darwin) + racc (~> 1.4) + nokogiri (1.15.7-x86_64-darwin) racc (~> 1.4) nokogumbo (2.0.5) nokogiri (~> 1.8, >= 1.8.4) @@ -755,22 +789,24 @@ GEM version_gem (~> 1.1) oauth-tty (1.0.5) version_gem (~> 1.1, >= 1.1.1) - oauth2 (1.4.10) + oauth2 (1.4.11) faraday (>= 0.17.3, < 3.0) jwt (>= 1.0, < 3.0) multi_json (~> 1.3) multi_xml (~> 0.5) - rack (>= 1.2, < 3) - okcomputer (1.18.5) + rack (>= 1.2, < 4) + okcomputer (1.18.6) omniauth (1.9.2) hashie (>= 3.4.6) rack (>= 1.6.2, < 3) omniauth-oauth2 (1.3.1) oauth2 (~> 1.0) omniauth (~> 1.2) - omniauth-openid (2.0.1) - omniauth (>= 1.0, < 3.0) - rack-openid (~> 1.4.0) + omniauth-openid (2.0.2) + omniauth (>= 1.1) + rack-openid (~> 1.4) + ruby-openid (~> 2.1, >= 2.1.8) + version_gem (~> 1.1, >= 1.1.8) omniauth-orcid (0.6) omniauth (~> 1.0) omniauth-oauth2 (~> 1.1) @@ -780,33 +816,37 @@ GEM rails (> 3.2.0) orm_adapter (0.5.0) os (1.1.4) - parallel (1.22.1) - parser (3.1.2.1) + ostruct (0.6.3) + parallel (1.27.0) + parser (3.3.9.0) ast (~> 2.4.1) + racc parslet (2.0.0) posix-spawn (0.3.15) power_converter (0.1.2) - public_suffix (5.0.0) + prism (1.4.0) + psych (3.3.4) + public_suffix (5.1.1) pul_uv_rails (2.0.1) puma (4.3.12) nio4r (~> 2.0) - qa (5.9.0) + qa (5.15.0) activerecord-import deprecation faraday (< 3.0, != 2.0.0) geocoder ldpath nokogiri (~> 1.6) - rails (>= 5.0, < 6.2) + rails (>= 5.0, < 8.1) rdf - racc (1.6.0) + racc (1.8.1) rack (2.2.3) rack-accept (0.4.5) rack (>= 0.4) rack-openid (1.4.2) rack (>= 1.1.0) ruby-openid (>= 2.1.8) - rack-test (2.0.2) + rack-test (2.2.0) rack (>= 1.3) rails (5.2.4.6) actioncable (= 5.2.4.6) @@ -825,13 +865,17 @@ GEM actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) activesupport (>= 5.0.1.rc1) - rails-dom-testing (2.0.3) - activesupport (>= 4.2.0) + rails-dom-testing (2.3.0) + activesupport (>= 5.0.0) + minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.4.3) - loofah (~> 2.3) - rails_autolink (1.1.6) - rails (> 3.1) + rails-html-sanitizer (1.6.2) + loofah (~> 2.21) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + rails_autolink (1.1.8) + actionview (> 3.1) + activesupport (> 3.1) + railties (> 3.1) railties (5.2.4.6) actionpack (= 5.2.4.6) activesupport (= 5.2.4.6) @@ -839,11 +883,11 @@ GEM rake (>= 0.8.7) thor (>= 0.19.0, < 2.0) rainbow (3.1.1) - rake (13.0.6) + rake (13.3.0) rb-fsevent (0.11.2) - rb-inotify (0.10.1) + rb-inotify (0.11.1) ffi (~> 1.0) - rdf (3.2.9) + rdf (3.2.12) link_header (~> 0.0, >= 0.0.8) rdf-aggregate-repo (3.2.1) rdf (~> 3.2) @@ -861,7 +905,7 @@ GEM rdf (~> 3.2) sparql (~> 3.2) sxp (~> 1.2) - rdf-normalize (0.5.0) + rdf-normalize (0.6.1) rdf (~> 3.2) rdf-rdfa (3.1.0) haml (~> 5.1) @@ -869,11 +913,11 @@ GEM rdf (~> 3.1) rdf-aggregate-repo (~> 3.1) rdf-xsd (~> 3.1) - rdf-rdfxml (3.1.1) + rdf-rdfxml (3.2.2) + builder (~> 3.2) htmlentities (~> 4.3) - rdf (~> 3.1) - rdf-rdfa (~> 3.1) - rdf-xsd (~> 3.1) + rdf (~> 3.2) + rdf-xsd (~> 3.2) rdf-reasoner (0.8.0) rdf (~> 3.2) rdf-xsd (~> 3.2) @@ -901,52 +945,52 @@ GEM rexml (~> 3.2) redic (1.5.3) hiredis - redis (5.4.0) + redis (5.4.1) redis-client (>= 0.22.0) - redis-client (0.24.0) + redis-client (0.25.2) connection_pool - redis-namespace (1.9.0) + redis-namespace (1.11.0) redis (>= 4) - redlock (1.3.0) - redis (>= 3.0.0, < 6.0) - regexp_parser (2.5.0) + redlock (2.0.6) + redis-client (>= 0.14.1, < 1.0.0) + regexp_parser (2.11.1) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) - request_store (1.5.1) + request_store (1.7.0) rack (>= 1.4) - responders (3.0.1) - actionpack (>= 5.0) - railties (>= 5.0) + responders (3.1.1) + actionpack (>= 5.2) + railties (>= 5.2) rest-client (2.0.2) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) retriable (3.1.2) - rexml (3.2.5) - riiif (2.4.0) + rexml (3.4.1) + riiif (2.7.0) deprecation (>= 1.0.0) iiif-image-api (>= 0.1.0) - railties (>= 4.2, < 8) - rsolr (2.5.0) + railties (>= 4.2, < 9) + rsolr (2.6.0) builder (>= 2.1.2) faraday (>= 0.9, < 3, != 2.0.0) - rspec-activemodel-mocks (1.1.0) + rspec-activemodel-mocks (1.3.0) activemodel (>= 3.0) activesupport (>= 3.0) rspec-mocks (>= 2.99, < 4.0) - rspec-core (3.11.0) - rspec-support (~> 3.11.0) - rspec-expectations (3.11.0) + rspec-core (3.13.5) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) - rspec-its (1.3.0) + rspec-support (~> 3.13.0) + rspec-its (1.3.1) rspec-core (>= 3.0.0) rspec-expectations (>= 3.0.0) - rspec-mocks (3.11.1) + rspec-mocks (3.13.5) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.11.0) + rspec-support (~> 3.13.0) rspec-rails (5.1.2) actionpack (>= 5.2) activesupport (>= 5.2) @@ -957,8 +1001,8 @@ GEM rspec-support (~> 3.10) rspec-retry (0.6.2) rspec-core (> 3.3) - rspec-support (3.11.0) - rspec_junit_formatter (0.5.1) + rspec-support (3.13.4) + rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) rubocop (1.28.2) parallel (~> 1.10) @@ -969,9 +1013,10 @@ GEM rubocop-ast (>= 1.17.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.21.0) - parser (>= 3.1.1.0) - rubocop-performance (1.14.3) + rubocop-ast (1.46.0) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-performance (1.19.1) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) rubocop-rails (2.15.2) @@ -986,7 +1031,7 @@ GEM multipart-post oauth2 ruby-openid (2.9.2) - ruby-progressbar (1.11.0) + ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) rubyzip (1.3.0) samvera-nesting_indexer (2.0.0) @@ -1032,27 +1077,27 @@ GEM connection_pool (>= 2.2.2) rack (~> 2.0) redis (>= 4.5.0) - sidekiq-limit_fetch (3.4.0) - sidekiq (>= 4) - signet (0.17.0) + sidekiq-limit_fetch (4.4.1) + sidekiq (>= 6) + signet (0.19.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simple_form (5.1.0) + simple_form (5.3.1) actionpack (>= 5.2) activemodel (>= 5.2) simplecov (0.22.0) docile (~> 1.1) simplecov-html (~> 0.11) simplecov_json_formatter (~> 0.1) - simplecov-html (0.12.3) + simplecov-html (0.13.2) simplecov-lcov (0.8.0) simplecov_json_formatter (0.1.4) - slop (4.9.2) - snaky_hash (2.0.1) - hashie - version_gem (~> 1.1, >= 1.1.1) + slop (4.10.1) + snaky_hash (2.0.3) + hashie (>= 0.1.0, < 6) + version_gem (>= 1.1.8, < 3) solr_wrapper (4.0.2) http minitar @@ -1062,19 +1107,20 @@ GEM activesupport nokogiri xml-simple - sparql (3.2.4) - builder (~> 3.2) - ebnf (~> 2.2, >= 2.3.1) + sparql (3.2.6) + builder (~> 3.2, >= 3.2.4) + ebnf (~> 2.3, >= 2.3.5) logger (~> 1.5) - rdf (~> 3.2, >= 3.2.8) - rdf-aggregate-repo (~> 3.2) + rdf (~> 3.2, >= 3.2.11) + rdf-aggregate-repo (~> 3.2, >= 3.2.1) rdf-xsd (~> 3.2) - sparql-client (~> 3.2, >= 3.2.1) - sxp (~> 1.2, >= 1.2.2) - sparql-client (3.2.1) - net-http-persistent (~> 4.0, >= 4.0.1) - rdf (~> 3.2, >= 3.2.6) - sprockets (3.7.2) + sparql-client (~> 3.2, >= 3.2.2) + sxp (~> 1.2, >= 1.2.4) + sparql-client (3.2.2) + net-http-persistent (~> 4.0, >= 4.0.2) + rdf (~> 3.2, >= 3.2.11) + sprockets (3.7.5) + base64 concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-es6 (0.9.2) @@ -1086,25 +1132,31 @@ GEM activesupport (>= 5.2) sprockets (>= 3.0.0) sqlite3 (1.3.13) - sshkit (1.21.3) + sshkit (1.24.0) + base64 + logger net-scp (>= 1.1.2) + net-sftp (>= 2.1.2) net-ssh (>= 2.8.0) - ssrf_filter (1.1.1) - sxp (1.2.2) - matrix + ostruct + ssrf_filter (1.0.8) + sxp (1.2.4) + matrix (~> 0.4) rdf (~> 3.2) sync (0.5.0) - temple (0.8.2) - term-ansicolor (1.7.1) + temple (0.10.4) + term-ansicolor (1.11.2) tins (~> 1.0) - terminal-table (3.0.2) - unicode-display_width (>= 1.1.1, < 3) - thor (1.2.1) + terminal-table (4.0.0) + unicode-display_width (>= 1.1.1, < 4) + thor (1.4.0) thread_safe (0.3.6) - tilt (2.0.11) - tins (1.32.1) + tilt (2.6.1) + timeout (0.4.3) + tins (1.39.1) + bigdecimal sync - tinymce-rails (4.9.11) + tinymce-rails (4.9.11.1) railties (>= 3.1.1) trailblazer-option (0.1.2) turbolinks (5.2.1) @@ -1114,21 +1166,19 @@ GEM actionpack (>= 3.1) jquery-rails railties (>= 3.1) - typhoeus (1.4.0) + typhoeus (1.4.1) ethon (>= 0.9.0) - tzinfo (1.2.10) + tzinfo (1.2.11) thread_safe (~> 0.1) uber (0.1.0) - uglifier (4.2.0) + uglifier (4.2.1) execjs (>= 0.3.0, < 3) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) - unicode-display_width (2.2.0) - unicode-types (1.7.0) + unicode-display_width (2.6.0) + unicode-types (1.10.0) validatable (1.6.7) - vcr (6.1.0) - version_gem (1.1.3) + vcr (6.3.1) + base64 + version_gem (1.1.8) virtus (2.0.0) axiom-types (~> 0.1) coercible (~> 1.0) @@ -1144,21 +1194,23 @@ GEM nokogiri (~> 1.6) rubyzip (~> 1.0) selenium-webdriver (~> 3.0) - webmock (3.18.1) + webmock (3.25.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.7.0) - websocket-driver (0.7.5) + websocket-driver (0.8.0) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xml-simple (1.1.9) rexml xpath (3.2.0) nokogiri (~> 1.8) + zeitwerk (2.6.18) PLATFORMS - ruby + arm64-darwin-22 + x86_64-darwin-22 DEPENDENCIES active-fedora (~> 12.2.4) @@ -1169,7 +1221,7 @@ DEPENDENCIES bootstrap-sass (~> 3.4.1) brakeman browse-everything (= 1.1.0) - bulkrax (~> 4.3.0) + bulkrax (~> 9.1.0) bundler (= 2.4.22) bundler-audit byebug diff --git a/README.md b/README.md index e09f2de00..5262b0d65 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,12 @@ 1. Start fedora: ```fcrepo_wrapper -p 8984``` 1. Start solr in new tab: ```solr_wrapper -d solr/config/ --collection_name hydra-development``` 1. Start redis in new tab: ```redis-server``` +1. Configure .env.development with required sidekiq variables: + ``` + SCHOLAR_JOB_QUEUE_ADAPTER=sidekiq + SCHOLAR_DATABASE_POOL=5 # must be >= Sidekiq concurrency set in config/sidekiq.yml + ``` +1. In a new tab. start sidekiq: `bundle exec sidekiq -C config/sidekiq.yml` 1. Run the database migrations: `bundle exec rake db:migrate` 1. Start the rails server in new tab: `rails server` 1. Visit the site at [http://localhost:3000] (http://localhost:3000) diff --git a/app/decorators/exporters_controller_decorator.rb b/app/decorators/exporters_controller_decorator.rb new file mode 100644 index 000000000..23b052955 --- /dev/null +++ b/app/decorators/exporters_controller_decorator.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true +# Ensures @exporter is loaded for the entry_table action. +# In your routes, entry_table uses :exporter_id, while other member actions use :id. +# Without this, @exporter is nil and the DataTables AJAX /entry_table.json 500s. + +module Bulkrax + module ExportersControllerDecorator + def self.prepended(base) + base.before_action :ensure_exporter_loaded_for_entry_table, only: [:entry_table] + end + + private + + def ensure_exporter_loaded_for_entry_table + @exporter ||= Bulkrax::Exporter.find_by(id: params[:exporter_id] || params[:id]) + head :not_found unless @exporter + end + end +end diff --git a/app/decorators/importers_controller_decorator.rb b/app/decorators/importers_controller_decorator.rb new file mode 100644 index 000000000..0f70b7482 --- /dev/null +++ b/app/decorators/importers_controller_decorator.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Bulkrax + module ImportersControllerDecorator + def self.prepended(base) + base.before_action :ensure_importer_loaded_for_entry_table, only: [:entry_table] + end + + private + + def ensure_importer_loaded_for_entry_table + @importer ||= Bulkrax::Importer.find_by(id: params[:importer_id] || params[:id]) + head :not_found unless @importer + end + end +end diff --git a/app/models/ability.rb b/app/models/ability.rb index caabde437..6b182e3ba 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -39,6 +39,24 @@ def custom_permissions # rubocop:disable Metrics/MethodLength can [:manage], Etd if current_user.admin? end + # Allow importing if the user can create any work (or is admin) + def can_import_works? + return false if current_user.nil? + current_user.admin? || can_create_any_work? + end + + # Allow exporting if the user can create any work (or is admin) + def can_export_works? + return false if current_user.nil? + current_user.admin? || can_create_any_work? + end + + # Fallback helper in case your Hyrax doesn't already define this. + # (Hyrax::Ability often has it; this is safe to keep.) + def can_create_any_work? + Array(Hyrax.config.curation_concerns).any? { |klass| can?(:create, klass) } + end + private def look_for_collection(collection_export) diff --git a/app/views/bulkrax/exporters/show.html.erb b/app/views/bulkrax/exporters/show.html.erb index 3ae3f0fbb..56631e31c 100644 --- a/app/views/bulkrax/exporters/show.html.erb +++ b/app/views/bulkrax/exporters/show.html.erb @@ -4,8 +4,8 @@

Exporter: <%= @exporter.name %>

-
-
+
+
<% 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