diff --git a/.rubocop.yml b/.rubocop.yml index fc72478e3..014e8c55e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -80,6 +80,7 @@ Metrics/ModuleLength: Exclude: - 'app/helpers/hyrax_helper.rb' - 'spec/controllers/bulkrax/importers_controller_spec.rb' + - 'spec/controllers/bulkrax/exporters_controller_spec.rb' Rails/ApplicationJob: Enabled: false diff --git a/Gemfile b/Gemfile index 389a7c86c..a89794021 100644 --- a/Gemfile +++ b/Gemfile @@ -12,10 +12,12 @@ 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', '5.3.0' gem 'hydra-remote_identifier', github: 'uclibs/hydra-remote_identifier', branch: 'scholar-datacite' gem 'kaltura', '0.1.1' gem 'rack', '2.2.3' +gem 'redis', '>= 4.0.0' +gem 'redis-namespace', '~> 1.10' gem 'sidekiq-limit_fetch' gem 'willow_sword', github: 'notch8/willow_sword' diff --git a/Gemfile.lock b/Gemfile.lock index b3a204711..5060f091c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -89,15 +89,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) @@ -106,15 +106,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.0.0) activerecord (>= 4.2) activestorage (5.2.4.6) actionpack (= 5.2.4.6) @@ -125,39 +125,38 @@ 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) + autoprefixer-rails (10.4.19.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.3.0) + aws-partitions (1.1023.0) + aws-sdk-core (3.214.0) + aws-eventstream (~> 1, >= 1.3.0) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) 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) + aws-sdk-kms (1.96.0) + aws-sdk-core (~> 3, >= 3.210.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.176.1) + aws-sdk-core (~> 3, >= 3.210.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.10.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) @@ -168,12 +167,13 @@ GEM babel-transpiler (0.7.0) babel-source (>= 4.0, < 6) execjs (~> 2.0) - bagit (0.4.4) + bagit (0.4.6) docopt (~> 0.5.0) validatable (~> 1.6) + base64 (0.2.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) @@ -207,7 +207,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) @@ -221,14 +221,15 @@ GEM signet (~> 0.8) sprockets (~> 3.7) typhoeus - builder (3.2.4) - bulkrax (4.3.0) + builder (3.3.0) + bulkrax (5.3.0) bagit (~> 0.4) coderay + dry-monads (~> 1.4.0) iso8601 (~> 0.9.0) kaminari language_list (~> 1.2, >= 1.2.1) - libxml-ruby (~> 3.1.0) + libxml-ruby (~> 3.2.4) loofah (>= 2.2.3) oai (>= 0.4, < 2.x) rack (>= 2.0.6) @@ -236,19 +237,19 @@ GEM 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.6.3) capistrano (~> 3.1) capistrano-bundler (>= 1.1, < 3) capistrano-rvm (0.1.2) @@ -264,14 +265,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) @@ -283,22 +284,25 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.1.10) - connection_pool (2.2.5) + concurrent-ruby (1.3.4) + connection_pool (2.4.1) coveralls_reborn (0.28.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.0) + database_cleaner (2.1.0) + database_cleaner-active_record (>= 2, < 3) + database_cleaner-active_record (2.2.0) activerecord (>= 5.a) database_cleaner-core (~> 2.0.0) database_cleaner-core (2.0.1) + date (3.4.1) declarative (0.0.20) deprecation (1.1.0) activesupport @@ -310,13 +314,12 @@ 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.5.1) + 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) @@ -331,76 +334,81 @@ GEM dropbox_api (0.1.21) faraday (< 3.0) oauth2 (~> 1.1) - dry-configurable (0.15.0) - concurrent-ruby (~> 1.0) + dry-configurable (0.16.1) dry-core (~> 0.6) - dry-container (0.10.1) + zeitwerk (~> 2.6) + dry-container (0.11.0) concurrent-ruby (~> 1.0) - dry-core (0.8.1) + dry-core (0.9.1) concurrent-ruby (~> 1.0) + zeitwerk (~> 2.6) dry-equalizer (0.3.0) - dry-events (0.3.0) + dry-events (0.4.0) concurrent-ruby (~> 1.0) - dry-core (~> 0.5, >= 0.5) + dry-core (~> 0.9, >= 0.9) dry-inflector (0.3.0) dry-initializer (3.1.1) - dry-logic (1.2.0) + dry-logic (1.3.0) concurrent-ruby (~> 1.0) - dry-core (~> 0.5, >= 0.5) + dry-core (~> 0.9, >= 0.9) + zeitwerk (~> 2.6) dry-matcher (0.9.0) dry-core (~> 0.4, >= 0.4.8) dry-monads (1.4.0) concurrent-ruby (~> 1.0) dry-core (~> 0.7) - dry-schema (1.10.2) + dry-schema (1.11.3) concurrent-ruby (~> 1.0) - dry-configurable (~> 0.13, >= 0.13.0) - dry-core (~> 0.5, >= 0.5) + dry-configurable (~> 0.16, >= 0.16) + dry-core (~> 0.9, >= 0.9) 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.3) + dry-types (~> 1.6) + zeitwerk (~> 2.6) + dry-struct (1.5.2) + dry-core (~> 0.9, >= 0.9) + dry-types (~> 1.6) ice_nine (~> 0.11) - dry-transaction (0.13.3) + zeitwerk (~> 2.6) + dry-transaction (0.14.0) dry-container (>= 0.2.8) - dry-events (>= 0.1.0) + dry-events (>= 0.4.0) dry-matcher (>= 0.7.0) - dry-monads (>= 0.4.0) - dry-types (1.5.1) + dry-monads (>= 0.5.0) + dry-types (1.6.1) concurrent-ruby (~> 1.0) dry-container (~> 0.3) - dry-core (~> 0.5, >= 0.5) + dry-core (~> 0.9, >= 0.9) dry-inflector (~> 0.1, >= 0.1.2) - dry-logic (~> 1.0, >= 1.0.2) - dry-validation (1.8.1) + dry-logic (~> 1.3, >= 1.3) + zeitwerk (~> 2.6) + dry-validation (1.9.0) concurrent-ruby (~> 1.0) dry-container (~> 0.7, >= 0.7.1) - dry-core (~> 0.5, >= 0.5) + dry-core (~> 0.9, >= 0.9) dry-initializer (~> 3.0) - dry-schema (~> 1.8, >= 1.8.0) - ebnf (2.3.1) - amazing_print (~> 1.4) + dry-schema (~> 1.11, >= 1.11.0) + 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.0) + 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 @@ -408,27 +416,29 @@ 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.0-x86_64-darwin) + ffi-compiler (1.3.2) + ffi (>= 1.15.5) rake figaro (1.2.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.4) + 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) @@ -436,19 +446,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) @@ -461,8 +470,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) @@ -473,21 +482,23 @@ GEM haml (5.2.2) temple (>= 0.8.0) tilt - hashdiff (1.0.1) + hashdiff (1.1.2) hashie (5.0.0) hiredis (0.6.3) htmlentities (4.3.4) - http (5.1.0) + http (5.2.0) addressable (~> 2.8) + base64 (~> 0.1) 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) + httparty (0.22.0) + csv + mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) httpclient (2.8.3) hydra-access-controls (11.0.7) @@ -517,21 +528,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) @@ -588,44 +601,45 @@ GEM signet solrizer (>= 3.4, < 5) tinymce-rails (~> 4.1) - i18n (1.12.0) + i18n (1.14.6) 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.9.0) + 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.9.3) + base64 kaltura (0.1.1) hashie (>= 1.0.0) httparty (>= 0.7.8) @@ -644,8 +658,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) @@ -667,7 +681,7 @@ GEM rdf-vocab (~> 3.0) legato (0.7.0) multi_json - libxml-ruby (3.1.0) + libxml-ruby (3.2.4) link_header (0.0.8) linkeddata (3.1.1) equivalent-xml (~> 0.6) @@ -698,15 +712,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.0) ffi-compiler (~> 1.0) rake (~> 13.0) - logger (1.5.1) - loofah (2.18.0) + logger (1.6.3) + loofah (2.23.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) @@ -716,41 +733,51 @@ GEM mimemagic (~> 0.3.2) matrix (0.4.2) memoist (0.16.2) - method_source (1.0.0) - mime-types (3.4.1) + method_source (1.1.0) + mime-types (3.6.0) + logger mime-types-data (~> 3.2015) - mime-types-data (3.2022.0105) + mime-types-data (3.2024.1203) 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.4) 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.3) ruby2_keywords (~> 0.0.1) mustermann-grape (1.0.2) mustermann (>= 1.0.0) mysql2 (0.4.10) nest (3.2.0) redic - net-http-persistent (4.0.1) + net-http-persistent (4.0.5) connection_pool (~> 2.2) - net-scp (1.2.1) - net-ssh (>= 2.6.5) - net-ssh (7.0.1) + net-imap (0.4.18) + date + net-protocol + net-pop (0.1.2) + net-protocol + net-protocol (0.2.2) + timeout + net-scp (4.0.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.0) + 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-x86_64-darwin) racc (~> 1.4) nokogumbo (2.0.5) nokogiri (~> 1.8, >= 1.8.4) @@ -764,13 +791,14 @@ 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) + rack (>= 1.2, < 4) okcomputer (1.18.5) + rack (>= 1.2, < 4) omniauth (1.9.2) hashie (>= 3.4.6) rack (>= 1.6.2, < 3) @@ -789,35 +817,38 @@ 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.1) + parallel (1.26.3) + parser (3.3.6.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) + 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.14.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-protection (2.2.2) + rack-protection (3.0.6) rack - rack-test (2.0.2) + rack-test (2.1.0) rack (>= 1.3) rails (5.2.4.6) actioncable (= 5.2.4.6) @@ -836,13 +867,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.2.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) @@ -850,11 +885,11 @@ GEM rake (>= 0.8.7) thor (>= 0.19.0, < 2.0) rainbow (3.1.1) - rake (13.0.6) + rake (13.2.1) 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) @@ -872,7 +907,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) @@ -880,11 +915,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) @@ -912,49 +947,51 @@ GEM rexml (~> 3.2) redic (1.5.3) hiredis - redis (4.5.1) - redis-namespace (1.9.0) + redis (4.8.1) + redis-client (0.23.0) + connection_pool + 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.9.3) 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.3.9) + 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.2.1) 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.2) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.3) 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.2) 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) @@ -965,8 +1002,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.2) + rspec_junit_formatter (0.6.0) rspec-core (>= 2, < 4, != 2.12.0) rubocop (1.28.2) parallel (~> 1.10) @@ -977,9 +1014,9 @@ 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.37.0) + parser (>= 3.3.1.0) + rubocop-performance (1.19.1) rubocop (>= 1.7.0, < 2.0) rubocop-ast (>= 0.4.0) rubocop-rails (2.15.2) @@ -994,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) @@ -1036,29 +1073,30 @@ GEM activesupport (>= 4.0.0) show_me_the_cookies (6.0.0) capybara (>= 2, < 4) - sidekiq (5.2.10) + sidekiq (5.2.7) connection_pool (~> 2.2, >= 2.2.2) - rack (~> 2.0) + rack (>= 1.5.0) rack-protection (>= 1.5.0) - redis (~> 4.5, < 4.6.0) - sidekiq-limit_fetch (3.4.0) + redis (>= 3.3.5, < 5) + sidekiq-limit_fetch (4.3.2) + redis (>= 4.6.0) sidekiq (>= 4) - signet (0.17.0) + 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.1) simplecov-lcov (0.8.0) simplecov_json_formatter (0.1.4) - slop (4.9.2) + slop (4.10.1) snaky_hash (2.0.1) hashie version_gem (~> 1.1, >= 1.1.1) @@ -1071,19 +1109,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) @@ -1095,25 +1134,30 @@ GEM activesupport (>= 5.2) sprockets (>= 3.0.0) sqlite3 (1.3.13) - sshkit (1.21.3) + sshkit (1.23.2) + base64 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.3) + term-ansicolor (1.11.2) tins (~> 1.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) - thor (1.2.1) + thor (1.3.2) thread_safe (0.3.6) - tilt (2.0.11) - tins (1.32.1) + tilt (2.4.0) + timeout (0.4.2) + tins (1.37.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) @@ -1123,21 +1167,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.4) virtus (2.0.0) axiom-types (~> 0.1) coercible (~> 1.0) @@ -1153,21 +1195,21 @@ GEM nokogiri (~> 1.6) rubyzip (~> 1.0) selenium-webdriver (~> 3.0) - webmock (3.18.1) + webmock (3.24.0) 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.7.6) 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 + x86_64-darwin-22 DEPENDENCIES active-fedora (~> 12.2.4) @@ -1178,7 +1220,7 @@ DEPENDENCIES bootstrap-sass (~> 3.4.1) brakeman browse-everything (= 1.1.0) - bulkrax (~> 4.3.0) + bulkrax (= 5.3.0) bundler (= 2.4.22) bundler-audit byebug @@ -1218,6 +1260,8 @@ DEPENDENCIES rack (= 2.2.3) rails (~> 5.2.4.6) rails-controller-testing + redis (>= 4.0.0) + redis-namespace (~> 1.10) rest-client riiif (~> 2.0) rsolr (>= 1.0) diff --git a/app/controllers/concerns/hyrax/works_controller_behavior.rb b/app/controllers/concerns/hyrax/works_controller_behavior.rb deleted file mode 100644 index 8d15838e7..000000000 --- a/app/controllers/concerns/hyrax/works_controller_behavior.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'iiif_manifest' -require Hyrax::Engine.root.join('app/controllers/concerns/hyrax/works_controller_behavior.rb') - -module Hyrax - module WorksControllerBehavior - def create - fix_field_that_ends_with_double_quote('description') - fix_field_that_ends_with_double_quote('note') - if actor.create(actor_environment) - after_create_response - else - respond_to do |wants| - wants.html do - build_form - render 'new', status: :unprocessable_entity - end - wants.json { render_json_response(response_type: :unprocessable_entity, options: { errors: curation_concern.errors }) } - end - end - end - - private - - # add a space to the end of the field when the field ends in a double quote - # fileds that end in a double quote throw an error - def fix_field_that_ends_with_double_quote(field_name) - params[work_type][field_name] = params[work_type][field_name].gsub(/"$/, '" ') - end - - def work_type - curation_concern.class.to_s.underscore - end - end -end diff --git a/app/jobs/attach_files_to_work_job.rb b/app/jobs/attach_files_to_work_job.rb deleted file mode 100644 index 2550a86d4..000000000 --- a/app/jobs/attach_files_to_work_job.rb +++ /dev/null @@ -1,54 +0,0 @@ -# frozen_string_literal: true -# Converts UploadedFiles into FileSets and attaches them to works. -class AttachFilesToWorkJob < Hyrax::ApplicationJob - queue_as Hyrax.config.ingest_queue_name - - # @param [ActiveFedora::Base] work - the work object - # @param [Array] uploaded_files - an array of files to attach - def perform(work, uploaded_files, **work_attributes) - validate_files!(uploaded_files) - depositor = proxy_or_depositor(work) - user = User.find_by_user_key(depositor) - work_permissions = work.permissions.map(&:to_hash) - - # Temporary fix for edit access to files for owners on proxy deposit. - # See Hyrax issue #3416 - work_permissions << { name: depositor, type: "person", access: "edit" } if work.on_behalf_of - - metadata = visibility_attributes(work_attributes) - uploaded_files.each do |uploaded_file| - next if uploaded_file.file_set_uri.present? - - actor = Hyrax::Actors::FileSetActor.new(FileSet.create, user) - uploaded_file.update(file_set_uri: actor.file_set.uri) - actor.file_set.permissions_attributes = work_permissions - actor.create_metadata(metadata) - actor.create_content(uploaded_file) - actor.attach_to_work(work) - end - end - - private - - # The attributes used for visibility - sent as initial params to created FileSets. - def visibility_attributes(attributes) - attributes.slice(:visibility, :visibility_during_lease, - :visibility_after_lease, :lease_expiration_date, - :embargo_release_date, :visibility_during_embargo, - :visibility_after_embargo) - end - - def validate_files!(uploaded_files) - uploaded_files.each do |uploaded_file| - next if uploaded_file.is_a? Hyrax::UploadedFile - raise ArgumentError, "Hyrax::UploadedFile required, but #{uploaded_file.class} received: #{uploaded_file.inspect}" - end - end - - ## - # A work with files attached by a proxy user will set the depositor as the intended user - # that the proxy was depositing on behalf of. See tickets #2764, #2902. - def proxy_or_depositor(work) - work.on_behalf_of.presence || work.depositor - end -end diff --git a/app/services/work_loader.rb b/app/services/work_loader.rb deleted file mode 100644 index 4fed2af2b..000000000 --- a/app/services/work_loader.rb +++ /dev/null @@ -1,162 +0,0 @@ -# frozen_string_literal: true -class WorkLoader - NON_REPEATABLE_ATTRIBUTES = %i[ - visibility_during_embargo embargo_relase_date - visibility_after_embargo visibility - existing_identifier college department - degree - ].freeze - - REPEATABLE_ATTRIBUTES = %i[ - date_created - title creator publisher rights_statement - subject language related_url license - member_of_collection_ids alternate_title - journal_title issn time_period geo_subject - advisor committee_member description - ].freeze - - TEXT_ATTRIBUTES = %i[ - required_software note - ].freeze - - attr_accessor :curation_concern, :file_attributes, :user, :actor, :ability, :attributes_hash - - def initialize(attributes_hash) - @user = User.find_by_email attributes_hash.delete(:submitter_email) - @file_attributes = create_uploaded_files attributes_hash.delete(:files) - @curation_concern = new_curation_concern attributes_hash.delete(:work_type) - @attributes_hash = attributes_hash - end - - def create - work_pid = attributes_hash.delete(:pid) - curation_concern.id = work_pid unless work_pid.nil? - - current_ability = Ability.new(user) - env = Hyrax::Actors::Environment.new(curation_concern, current_ability, attributes_for_actor(attributes_hash)) - - actor.create(env) - - editor_emails = attributes_hash.delete(:edit_access) || [] - [editor_emails].flatten.each do |email| - curation_concern.edit_users += [email] - end - - reader_emails = attributes_hash.delete(:read_access) || [] - [reader_emails].flatten.each do |email| - curation_concern.read_users += [email] - end - - related_works = attributes_hash.delete(:member_work_ids) || [] - [related_works].flatten.each do |id| - curation_concern.members << ActiveFedora::Base.find(id) - end - - curation_concern.save - - file_attributes.each do |file| - file_set = FileSet.new - file_pid = file.delete(:pid) - file_set.id = file_pid unless file_pid.nil? - visibility = file[:visibility] || curation_concern.visibility - - actor = Hyrax::Actors::FileSetActor.new(file_set, user) - actor.create_metadata(visibility: visibility) - actor.create_content(file[:uploaded_file].file.file.to_file) - actor.attach_to_work(curation_concern) - actor.file_set.permissions_attributes = curation_concern.permissions.map(&:to_hash) - - file.update(file_set_uri: file_set.uri) - file_set.visibility = visibility - file_set.save - end - end - - def work_log - [curation_concern.id, curation_concern.owner] - end - - def file_log - curation_concern.file_sets.map do |file_set| - [file_set.id, curation_concern.id] - end - end - - def attributes_for_actor(attributes_hash) - attributes = {} - attributes_hash.keys.each do |a| - next unless curation_concern.respond_to? a - value = attributes_hash[a] - - if NON_REPEATABLE_ATTRIBUTES.include? a - if value.class == Array - raise "Repeated value passed for non-repeatable attribute" - else - attributes[a] = value - end - end - - if REPEATABLE_ATTRIBUTES.include? a - attributes[a] = if value.class == Array - value - else - [value] - end - end - - next unless TEXT_ATTRIBUTES.include? a - attributes[a] = if value.class == Array - value.join("\n") - else - value - end - end - - attributes["doi_assignment_strategy"] = if attributes_hash[:doi] == "TRUE" && attributes_hash[:identifier].nil? && attributes_hash[:publisher].present? - "mint_doi" - else - "not_now" - end - - attributes["remote_files"] = [] - attributes["uploaded_files"] = [] - unless attributes[:member_of_collection_ids].nil? - attributes.delete(:member_of_collection_ids) if attributes[:member_of_collection_ids][0].nil? - end - - attributes - end - - def ability - @ability ||= Ability.new(user) - end - - def create_uploaded_files(file_attributes) - file_attributes.map do |file| - file[:uploaded_file] = Hyrax::UploadedFile.create( - file: File.open(file[:path]), - user_id: user.id - ) - file - end - end - - def new_curation_concern(type) - case type - when "article" then Article.new - when "dataset" then Dataset.new - when "document" then Document.new - when "etd" then Etd.new - when "generic_work" then GenericWork.new - when "image" then Image.new - when "student_work" then StudentWork.new - when "medium" then Medium.new - else raise "No valid work type found" - end - end - - def actor - @actor ||= Hyrax::CurationConcern.actor - end -end diff --git a/config/initializers/redlock_patch.rb b/config/initializers/redlock_patch.rb new file mode 100644 index 000000000..7dd199a8c --- /dev/null +++ b/config/initializers/redlock_patch.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true +# config/initializers/redlock_patch.rb +module Redlock + class Client + def call_with_redis_namespace_fix(redis, command, *args) + # Ensure 'call' isn't used when redis-namespace is involved + if redis.respond_to?(:namespace) && command == :call + redis.client.call(*args) + else + redis.public_send(command, *args) + end + end + + def lock(resource, ttl, _options = {}) + @servers.each do |server| + call_with_redis_namespace_fix(server, :set, resource, "LOCKED", "NX", "PX", ttl) + end + # ... other operations + end + + def unlock(resource) + @servers.each do |server| + call_with_redis_namespace_fix(server, :del, resource) + end + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 1957240eb..9f2dc9ebb 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: 2023_06_08_153601) do create_table "bookmarks", force: :cascade do |t| t.integer "user_id", null: false @@ -28,7 +28,7 @@ t.string "identifier" t.string "collection_ids" t.string "type" - t.integer "importerexporter_id", null: false + t.integer "importerexporter_id" t.text "raw_metadata", limit: 16777215 t.text "parsed_metadata", limit: 16777215 t.datetime "created_at", null: false @@ -37,7 +37,9 @@ t.datetime "last_succeeded_at" t.string "importerexporter_type", default: "Bulkrax::Importer" t.integer "import_attempts", default: 0 - t.index ["importerexporter_id"], name: "index_bulkrax_entries_on_importerexporter_id" + t.index ["identifier"], name: "index_bulkrax_entries_on_identifier" + 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| @@ -120,7 +122,9 @@ t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "order", default: 0 + 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| @@ -134,6 +138,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| @@ -367,7 +374,7 @@ t.string "namespace", default: "default", null: false t.string "template", null: false t.text "counters" - t.integer "seq", limit: 8, default: 0 + t.integer "seq", default: 0 t.binary "rand" t.datetime "created_at", null: false t.datetime "updated_at", null: false diff --git a/spec/controllers/bulkrax/exporters_controller_spec.rb b/spec/controllers/bulkrax/exporters_controller_spec.rb index 048cf5874..fb432819d 100644 --- a/spec/controllers/bulkrax/exporters_controller_spec.rb +++ b/spec/controllers/bulkrax/exporters_controller_spec.rb @@ -32,11 +32,15 @@ module Bulkrax before do module Bulkrax::Auth def authenticate_user! + @current_user = User.first true end + + def current_user + @current_user + end end described_class.prepend Bulkrax::Auth - expect(controller).to receive(:authorize!).with(:read, :admin_dashboard).and_return(true) end # This should return the minimal set of attributes required to create a valid @@ -146,5 +150,20 @@ def authenticate_user! end end end + + describe 'DELETE #destroy' do + it 'destroys the requested exporter' do + exporter = Exporter.create! valid_attributes + expect do + delete :destroy, params: { id: exporter.to_param }, session: valid_session + end.to change(Exporter, :count).by(-1) + end + + it 'redirects to the exporters list' do + exporter = Exporter.create! valid_attributes + delete :destroy, params: { id: exporter.to_param }, session: valid_session + expect(response).to redirect_to(exporters_url) + end + end end end diff --git a/spec/controllers/bulkrax/importers_controller_spec.rb b/spec/controllers/bulkrax/importers_controller_spec.rb index cdaba724a..4fc512c5e 100644 --- a/spec/controllers/bulkrax/importers_controller_spec.rb +++ b/spec/controllers/bulkrax/importers_controller_spec.rb @@ -32,12 +32,16 @@ module Bulkrax before do module Bulkrax::Auth def authenticate_user! + @current_user = User.first true end + + def current_user + @current_user + end end described_class.prepend Bulkrax::Auth allow(Bulkrax::ImporterJob).to receive(:perform_later).and_return(true) - expect(controller).to receive(:authorize!).with(:read, :admin_dashboard).and_return(true) end # This should return the minimal set of attributes required to create a valid @@ -188,6 +192,40 @@ def authenticate_user! end end + describe 'DELETE #destroy' do + it 'destroys the requested importer' do + importer = Importer.create! valid_attributes + expect do + delete :destroy, params: { id: importer.to_param }, session: valid_session + end.to change(Importer, :count).by(-1) + end + + it 'redirects to the importers list' do + importer = Importer.create! valid_attributes + delete :destroy, params: { id: importer.to_param }, session: valid_session + expect(response).to redirect_to(importers_url) + end + end + + describe 'GET #export_errors', clean_downloads: true do + let(:importer) { FactoryBot.create(:bulkrax_importer_csv_failed, entries: [failed_entry]) } + let(:failed_entry) { FactoryBot.create(:bulkrax_csv_entry_failed) } + let(:import_file_path) { importer.errored_entries_csv_path } + + before do + importer.parser_fields.merge!(import_file_path: import_file_path) + end + + it 'writes a CSV file containing the contents of failed entries' do + expect(File.exist?(import_file_path)).to eq(false) + + get :export_errors, params: { importer_id: importer.to_param }, session: valid_session + + expect(File.exist?(import_file_path)).to eq(true) + expect(File.read(import_file_path)).to include('Title,') + end + end + describe 'GET #upload_corrected_entries' do it 'returns a success response' do importer = Importer.create! valid_attributes @@ -196,20 +234,172 @@ def authenticate_user! end end - context 'with invalid params' do - let(:bad_file_upload_params) do - { - parser_fields: { - file: nil + describe 'POST #upload_corrected_entries_file', clean_downloads: true do + context 'with valid params' do + let(:file_upload_params) do + { + parser_fields: { + file: fixture_file_upload('./spec/fixtures/csv/ok.csv') + } } - } + end + + it 'sets partial_import_file_path on the requested importer' do + importer = FactoryBot.create(:bulkrax_importer_csv_failed) + expect(importer.parser_fields['partial_import_file_path']).not_to be_present + + post :upload_corrected_entries_file, params: { importer_id: importer.to_param, importer: file_upload_params }, session: valid_session + expect(importer.reload.parser_fields['partial_import_file_path']).to be_present + end + + it 'invokes Bulkrax::ImporterJob' do + expect(Bulkrax::ImporterJob).to receive(:perform_later).exactly(1).times + importer = FactoryBot.create(:bulkrax_importer_csv_failed) + post :upload_corrected_entries_file, params: { importer_id: importer.to_param, importer: file_upload_params }, session: valid_session + end + + it 'redirects to the importer with a notice' do + importer = FactoryBot.create(:bulkrax_importer_csv_failed) + post :upload_corrected_entries_file, params: { importer_id: importer.to_param, importer: file_upload_params }, session: valid_session + expect(response).to redirect_to(importer_path(importer)) + expect(flash[:notice]).to include('successfully') + end end - it 'redirects to the upload_corrected_entries view with an alert' do - importer = Importer.create! valid_attributes - post :upload_corrected_entries_file, params: { importer_id: importer.to_param, importer: bad_file_upload_params }, session: valid_session - expect(response).to redirect_to(importer_upload_corrected_entries_path(importer)) - expect(flash[:alert]).to include('failed') + context 'with invalid params' do + let(:bad_file_upload_params) do + { + parser_fields: { + file: nil + } + } + end + + it 'redirects to the upload_corrected_entries view with an alert' do + importer = Importer.create! valid_attributes + post :upload_corrected_entries_file, params: { importer_id: importer.to_param, importer: bad_file_upload_params }, session: valid_session + expect(response).to redirect_to(importer_upload_corrected_entries_path(importer)) + expect(flash[:alert]).to include('failed') + end + end + end + + context 'with application/json request' do + before do + allow(controller).to receive(:api_request?).and_return(true) + allow(AdminSet).to receive(:find).with('admin_set/default') + allow(User).to receive(:batch_user).and_return(FactoryBot.create(:user)) + allow(controller).to receive(:valid_parser_fields?).and_return(true) + end + + context 'with valid params' do + before do + ENV['BULKRAX_API_TOKEN'] = '1234' + request.headers['Authorization'] = 'Token: 1234' + end + + it 'creates a new Importer' do + expect do + post :create, params: { importer: valid_attributes, commit: 'Create' }, session: valid_session + end.to change(Importer, :count).by(1) + end + + it 'returns a 201 Created' do + post :create, params: { importer: valid_attributes, commit: 'Create' }, session: valid_session + expect(response.status).to eq(201) + end + end + + context 'with invalid params' do + before do + ENV['BULKRAX_API_TOKEN'] = '1234' + request.headers['Authorization'] = 'Token: 1234' + end + + it "returns a 422 Unprocessable Entry" do + post :create, params: { importer: invalid_attributes, commit: 'Create' }, session: valid_session + expect(response.status).to eq(422) + end + end + + context 'without a valid auth token' do + it 'returns a 401 Not Authenticated' do + post :create, params: { importer: valid_attributes, commit: 'Create' }, session: valid_session + expect(response.status).to eq(401) + end + end + + describe 'PUT #update' do + before do + allow(AdminSet).to receive(:find).with('admin_set/default') + allow(User).to receive(:batch_user).and_return(FactoryBot.create(:user)) + end + + context 'with valid params' do + let(:new_attributes) do + { + name: 'Test Importer Updated', + admin_set_id: 'admin_set/default', + user_id: FactoryBot.create(:user).id, + parser_fields: { some_attribute: 'something' } + } + end + + before do + ENV['BULKRAX_API_TOKEN'] = '1234' + request.headers['Authorization'] = 'Token: 1234' + end + + it 'updates the requested importer' do + importer = Importer.create! valid_attributes + put :update, params: { id: importer.to_param, importer: new_attributes, commit: 'Update Importer' }, session: valid_session + importer.reload + expect(importer.name).to eq('Test Importer Updated') + end + + it 'returns a 200 OK' do + importer = Importer.create! valid_attributes + put :update, params: { id: importer.to_param, importer: valid_attributes, commit: 'Update Importer' }, session: valid_session + expect(response.status).to eq(200) + end + end + + context 'with invalid params' do + before do + ENV['BULKRAX_API_TOKEN'] = '1234' + request.headers['Authorization'] = 'Token: 1234' + end + + it "returns a 422 Unprocessable Entry" do + importer = Importer.create! valid_attributes + put :update, params: { id: importer.to_param, importer: invalid_attributes, commit: 'Update Importer' }, session: valid_session + expect(response.status).to eq(422) + end + end + + context 'without a valid auth token' do + it 'returns a 401 Unauthorized without an access token' do + importer = Importer.create! valid_attributes + put :update, params: { id: importer.to_param, importer: valid_attributes, commit: 'Update Importer' }, session: valid_session + expect(response.status).to eq(401) + end + end + end + + describe 'DELETE #destroy' do + it 'destroys the requested importer' do + importer = Importer.create! valid_attributes + expect do + delete :destroy, params: { id: importer.to_param }, session: valid_session + end.to change(Importer, :count).by(-1) + expect(response.status).to eq(200) + end + + it 'returns a 200 OK' do + importer = Importer.create! valid_attributes + delete :destroy, params: { id: importer.to_param }, session: valid_session + expect(response.status).to eq(200) + end end end end diff --git a/spec/features/hyrax/dashboard/collection_spec.rb b/spec/features/hyrax/dashboard/collection_spec.rb index 497a1dc18..53594a2e8 100644 --- a/spec/features/hyrax/dashboard/collection_spec.rb +++ b/spec/features/hyrax/dashboard/collection_spec.rb @@ -15,16 +15,10 @@ # Setting Title on admin sets to avoid false positive matches with collections. let(:admin_set_a) { create(:admin_set, creator: [admin_user.user_key], title: ['Set A'], with_permission_template: true) } let(:admin_set_b) { create(:admin_set, creator: [user.user_key], title: ['Set B'], edit_users: [user.user_key], with_permission_template: true) } - let(:collection1) { create(:public_collection, user: user, collection_type_gid: collection_type.gid, create_access: true) } - let(:collection2) { create(:public_collection, user: user, collection_type_gid: collection_type.gid, create_access: true) } - let(:collection3) { create(:public_collection, user: admin_user, collection_type_gid: collection_type.gid, create_access: true) } - let(:collection4) { create(:public_collection, user: admin_user, collection_type_gid: user_collection_type.gid, create_access: true) } - - before do - admin = Role.find_or_create_by(name: "admin") - admin.users << admin_user - admin.save - end + let(:collection1) { create(:public_collection_lw, user: user, collection_type_gid: collection_type.gid, with_permission_template: true) } + let(:collection2) { create(:public_collection_lw, user: user, collection_type_gid: collection_type.gid, with_permission_template: true) } + let(:collection3) { create(:public_collection_lw, user: admin_user, collection_type_gid: collection_type.gid, with_permission_template: true) } + let(:collection4) { create(:public_collection_lw, user: admin_user, collection_type_gid: user_collection_type.gid, with_permission_template: true) } describe 'Your Collections tab' do context 'when non-admin user' do @@ -62,7 +56,7 @@ it "has collection type and visibility filters" do expect(page).to have_button 'Visibility' - expect(page).to have_link 'Open Access', + expect(page).to have_link 'Public', href: /visibility_ssi.+#{Regexp.escape(CGI.escape(collection3.visibility))}/ expect(page).to have_button 'Collection Type' expect(page).to have_link collection_type.title, @@ -114,10 +108,8 @@ it "has collection type and visibility filters" do expect(page).to have_button 'Visibility' - skip 'problem encountered with :en locale prefix' do - expect(page).to have_link 'Open Access', - href: /visibility_ssi.+#{Regexp.escape(CGI.escape(collection3.visibility))}/ - end + expect(page).to have_link 'Public', + href: /visibility_ssi.+#{Regexp.escape(CGI.escape(collection3.visibility))}/ expect(page).to have_button 'Collection Type' expect(page).to have_link collection_type.title, href: /#{solr_gid_field}.+#{Regexp.escape(CGI.escape(collection_type.gid))}/ @@ -160,7 +152,7 @@ expect(page).to have_link 'All Collection' click_link 'All Collections' expect(page).to have_button 'Visibility' - expect(page).to have_link 'Open Access', + expect(page).to have_link 'Public', href: /visibility_ssi.+#{Regexp.escape(CGI.escape(collection1.visibility))}/ expect(page).to have_button 'Collection Type' expect(page).to have_link collection_type.title, @@ -221,7 +213,7 @@ expect(page).to have_link 'Managed Collections' click_link 'Managed Collections' expect(page).to have_button 'Visibility' - expect(page).to have_link 'Open Access', + expect(page).to have_link 'Public', href: /visibility_ssi.+#{Regexp.escape(CGI.escape(collection1.visibility))}/ expect(page).to have_button 'Collection Type' expect(page).to have_link collection_type.title, @@ -237,7 +229,6 @@ describe 'create collection' do let(:title) { "Test Collection" } - let(:creator) { "Test Creator" } let(:description) { "Description for collection we are testing." } context 'when user can create collections of multiple types' do @@ -256,26 +247,18 @@ choose('User Collection') click_on('Create collection') - skip 'problem encountered with :en locale prefix' do - expect(page).to have_selector('h1', text: 'New User Collection') - end - - expect(page).to have_selector "input#collection_title" - expect(page).to have_selector "input#collection_creator" - - title_element = find_by_id("collection_title") - title_element.set("Test Collection") # Add whitespace to test it getting removed + expect(page).to have_selector('h1', text: 'New User Collection') + expect(page).to have_selector "input.collection_title.multi_value" - creator_element = find_by_id("collection_creator") - expect(creator_element.value).to eq("User, Sample") - creator_element.set("Test Creator") # Add whitespace to test it getting removed + click_link('Additional fields') + expect(page).to have_selector "input.collection_creator.multi_value" - fill_in('Description', with: description) - select('Attribution 4.0 International', from: 'License') + fill_in('Title', with: title) + fill_in('Abstract or Summary', with: description) + fill_in('Related URL', with: 'http://example.com/') click_button("Save") expect(page).to have_content title - expect(find("input#collection_creator")).to have_field('collection_creator', with: creator) expect(page).to have_content description end @@ -294,24 +277,19 @@ end it 'makes a new collection' do - within('.collections-wrapper') do - click_link "New Collection" - end - - skip 'problem encountered with :en locale prefix' do - expect(page).to have_selector('h1', text: 'New User Collection') - end + click_link "New Collection" + expect(page).to have_selector('h1', text: 'New User Collection') + expect(page).to have_selector "input.collection_title.multi_value" - expect(page).to have_selector "input#collection_title" - expect(page).to have_selector "input#collection_creator" + click_link('Additional fields') + expect(page).to have_selector "input.collection_creator.multi_value" fill_in('Title', with: title) - fill_in('Creator', with: creator) - fill_in('Description', with: description) + fill_in('Abstract or Summary', with: description) + fill_in('Related URL', with: 'http://example.com/') click_button("Save") expect(page).to have_content title - expect(find("input#collection_creator").value).to eq('creator') expect(page).to have_content description end end @@ -322,13 +300,14 @@ visit '/dashboard/my/collections' end - it 'does not show New Collection button' do + it 'does show New Collection button' do expect(page).not_to have_link "New Collection" expect(page).not_to have_button "New Collection" end end end + # TODO: this section is still deactivated describe "adding works to a collection", skip: "we need to define a dashboard/works path" do let!(:collection) { create!(:collection, title: ["Barrel of monkeys"], user: user, with_permission_template: true) } let!(:work1) { create(:work, title: ["King Louie"], user: user) } @@ -353,8 +332,8 @@ end describe 'delete collection' do - let!(:empty_collection) { create(:public_collection, title: ['Empty Collection'], user: user, create_access: true) } - let!(:collection) { create(:public_collection, title: ['Collection with Work'], user: user, create_access: true) } + let!(:empty_collection) { create(:public_collection_lw, title: ['Empty Collection'], user: user, with_permission_template: true) } + let!(:collection) { create(:public_collection_lw, title: ['Collection with Work'], user: user, with_permission_template: true) } let!(:admin_user) { create(:admin) } let!(:empty_adminset) { create(:admin_set, title: ['Empty Admin Set'], creator: [admin_user.user_key], with_permission_template: true) } let!(:adminset) { create(:admin_set, title: ['Admin Set with Work'], creator: [admin_user.user_key], with_permission_template: true) } @@ -589,7 +568,7 @@ def get_url_fragment(type) describe 'collection show page' do let(:collection) do - create(:public_collection, user: user, description: ['collection description'], create_access: true) + build(:public_collection_lw, user: user, description: ['collection description'], with_permission_template: true) end let!(:work1) { create(:work, title: ["King Louie"], member_of_collections: [collection], user: user) } let!(:work2) { create(:work, title: ["King Kong"], member_of_collections: [collection], user: user) } @@ -651,42 +630,26 @@ def get_url_fragment(type) collection1 # create collections by referencing them collection2 sign_in user - # stub out characterization. Travis doesn't have fits installed, and it's not relevant to the test. - allow(CharacterizeJob).to receive(:perform_later) end - it "preselects the collection we are adding works to and adds the new work" do + it "preselects the collection we are adding works to and adds the selected works" do visit "/dashboard/collections/#{collection1.id}" - click_link 'Deposit new work through this collection' - choose "payload_concern", option: "GenericWork" - click_button 'Create work' - - # verify the collection is pre-selected - click_link "Relationships" # switch tab - expect(page).to have_selector("table tr", text: collection1.title.first) - expect(page).not_to have_selector("table tr", text: collection2.title.first) - - # add required file - click_link "Files" # switch tab - within('span#addfiles') do - attach_file("files[]", "#{fixture_path}/image.jp2", visible: false) - end - # set required metadata - click_link "Metadata" # switch tab - fill_in('generic_work_title', with: 'New Work for Collection') - fill_in('Creator', with: 'Doe, Jane') - fill_in('Program or Department', with: 'Digital Collections and Repositories') - fill_in('Description', with: 'test') - select('Libraries', from: 'College') - select 'Attribution-ShareAlike 4.0 International', from: 'generic_work_license' - # check required acceptance - check('agreement') + click_link 'Add existing works' + find('input#check_all').click + click_button "Add to collection" + expect(page).to have_selector "#member_of_collection_ids[value=\"#{collection1.id}\"]", visible: false + expect(page).to have_selector "#member_of_collection_label[value=\"#{collection1.title.first}\"]" - click_on('Save') + visit "/dashboard/collections/#{collection2.id}" + click_link 'Add existing works' + find('input#check_all').click + click_button "Add to collection" + expect(page).to have_selector "#member_of_collection_ids[value=\"#{collection2.id}\"]", visible: false + expect(page).to have_selector "#member_of_collection_label[value=\"#{collection2.title.first}\"]" - # verify new work was added to collection1 - visit "/dashboard/collections/#{collection1.id}" - expect(page).to have_content("New Work for Collection") + click_button "Save changes" + expect(page).to have_content(work1.title.first) + expect(page).to have_content(work2.title.first) end end @@ -702,11 +665,10 @@ def get_url_fragment(type) it "preselects the collection we are adding works to and adds the new work" do visit "/dashboard/collections/#{collection1.id}" click_link 'Deposit new work through this collection' - - # verify the collection is pre-selected - expect(page).to have_content "Select type of work" choose "payload_concern", option: "GenericWork" click_button 'Create work' + + # verify the collection is pre-selected click_link "Relationships" # switch tab expect(page).to have_selector("table tr", text: collection1.title.first) expect(page).not_to have_selector("table tr", text: collection2.title.first) @@ -714,25 +676,14 @@ def get_url_fragment(type) # add required file click_link "Files" # switch tab within('span#addfiles') do - attach_file("files[]", "#{fixture_path}/image.jp2", visible: false) + attach_file("files[]", "#{Hyrax::Engine.root}/spec/fixtures/image.jp2", visible: false) end # set required metadata - click_link "Metadata" # switch tab - - title_element = find_by_id("generic_work_title") - title_element.set("New Work for Collection") - - select 'Attribution-ShareAlike 4.0 International', from: 'generic_work_license' - + click_link "Descriptions" # switch tab + fill_in('Title', with: 'New Work for Collection') fill_in('Creator', with: 'Doe, Jane') - - college_element = find_by_id("generic_work_college") - college_element.select("Business") - - fill_in('Program or Department', with: 'University Department') - fill_in('Description', with: 'This is a description.') - - choose('generic_work_visibility_open') + fill_in('Keyword', with: 'testing') + select('In Copyright', from: 'Rights statement') # check required acceptance check('agreement') @@ -760,7 +711,7 @@ def get_url_fragment(type) sign_in user end - let(:collection) { create(:named_collection, user: user, create_access: true) } + let(:collection) { create(:named_collection_lw, user: user, with_permission_template: true) } it "shows a collection with a listing of Descriptive Metadata and catalog-style search results" do visit '/dashboard/my/collections' @@ -815,7 +766,7 @@ def get_url_fragment(type) end describe 'edit collection' do - let(:collection) { create(:named_collection, user: user, create_access: true) } + let(:collection) { build(:named_collection_lw, user: user, with_permission_template: true) } let!(:work1) { create(:work, title: ["King Louie"], member_of_collections: [collection], user: user) } let!(:work2) { create(:work, title: ["King Kong"], member_of_collections: [collection], user: user) } @@ -858,10 +809,8 @@ def get_url_fragment(type) click_link('Edit collection') end # URL: /dashboard/collections/collection-id/edit + expect(page).to have_selector('h1', text: "Edit User Collection: #{collection.title.first}") - skip 'problem encountered with :en locale prefix' do - expect(page).to have_selector('h1', text: "Edit User Collection: #{collection.title.first}") - end expect(page).to have_field('collection_title', with: collection.title.first) expect(page).to have_field('collection_description', with: collection.description.first) @@ -870,7 +819,7 @@ def get_url_fragment(type) creators = ["Dorje Trollo", "Vajrayogini"] fill_in('Title', with: new_title) - fill_in('Description', with: new_description) + fill_in('Abstract or Summary', with: new_description) fill_in('Creator', with: creators.first) within('.panel-footer') do click_button('Save changes') @@ -911,8 +860,8 @@ def get_url_fragment(type) end context 'with brandable set' do - let(:brandable_collection_id) { create(:collection, user: user, collection_type_settings: [:brandable], create_access: true).id } - let(:not_brandable_collection_id) { create(:collection, user: user, collection_type_settings: [:not_brandable], create_access: true).id } + let(:brandable_collection_id) { create(:collection_lw, user: user, collection_type_settings: [:brandable], with_permission_template: true).id } + let(:not_brandable_collection_id) { create(:collection_lw, user: user, collection_type_settings: [:not_brandable], with_permission_template: true).id } it 'to true, it shows Branding tab' do visit "/dashboard/collections/#{brandable_collection_id}/edit" @@ -926,23 +875,23 @@ def get_url_fragment(type) end context 'with discoverable set' do - let(:discoverable_collection_id) { create(:collection, user: user, collection_type_settings: [:discoverable], create_access: true).id } - let(:not_discoverable_collection_id) { create(:collection, user: user, collection_type_settings: [:not_discoverable], create_access: true).id } + let(:discoverable_collection_id) { create(:collection_lw, user: user, collection_type_settings: [:discoverable], with_permission_template: true).id } + let(:not_discoverable_collection_id) { create(:collection_lw, user: user, collection_type_settings: [:not_discoverable], with_permission_template: true).id } it 'to true, it shows Discovery tab' do visit "/dashboard/collections/#{discoverable_collection_id}/edit" - expect(page).to have_link('Visibility', href: '#discovery') + expect(page).to have_link('Discovery', href: '#discovery') end it 'to false, it hides Discovery tab' do visit "/dashboard/collections/#{not_discoverable_collection_id}/edit" - expect(page).not_to have_link('Visibility', href: '#discovery') + expect(page).not_to have_link('Discovery', href: '#discovery') end end context 'with sharable set' do - let(:sharable_collection_id) { create(:collection, user: user, collection_type_settings: [:sharable], create_access: true).id } - let(:not_sharable_collection_id) { create(:collection, user: user, collection_type_settings: [:not_sharable], create_access: true).id } + let(:sharable_collection_id) { create(:collection_lw, user: user, collection_type_settings: [:sharable], with_permission_template: true).id } + let(:not_sharable_collection_id) { create(:collection_lw, user: user, collection_type_settings: [:not_sharable], with_permission_template: true).id } it 'to true, it shows Sharable tab' do visit "/dashboard/collections/#{sharable_collection_id}/edit" @@ -951,7 +900,6 @@ def get_url_fragment(type) context "to true, limits available users", js: true do let(:user2) { create(:user) } - it "to system users filted by select2" do visit "/dashboard/collections/#{sharable_collection_id}/edit" expect(page).to have_link('Sharing', href: '#sharing') diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index f9547cfcc..26784cbf3 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -91,6 +91,10 @@ def ci_build? Capybara.default_driver = :rack_test # This is a faster driver Capybara.javascript_driver = :selenium_chrome_headless_sandboxless # This is slower +Capybara.default_max_wait_time = 10 + +# Redis improvements +# Redlock::Client.new(['redis://localhost:6379']).reset! if defined?(Redlock) # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are diff --git a/spec/services/work_loader_spec.rb b/spec/services/work_loader_spec.rb deleted file mode 100644 index 963770fd0..000000000 --- a/spec/services/work_loader_spec.rb +++ /dev/null @@ -1,221 +0,0 @@ -# frozen_string_literal: true -require "rails_helper" - -describe WorkLoader, :clean_repo do - let(:user) { create(:user) } - let!(:role1) { Sipity::Role.create(name: 'depositing') } - let(:work_pid) { nil } - let(:file_pid1) { nil } - let(:file_pid2) { nil } - let(:collection_ids) { [] } - let(:member_work_ids) { nil } - let(:editors) { nil } - let(:readers) { nil } - let!(:admin_set_id) { AdminSet.find_or_create_default_admin_set_id } - let!(:permission_template) { Hyrax::PermissionTemplate.find_or_create_by!(source_id: admin_set_id) } - let!(:workflow) { Sipity::Workflow.create!(active: true, name: 'test-workflow', permission_template: permission_template) } - - before do - allow(CharacterizeJob).to receive(:perform_later) - - # Create a single action that can be taken - Sipity::WorkflowAction.create!(name: 'submit', workflow: workflow) - - # Grant the user access to deposit into the admin set. - Hyrax::PermissionTemplateAccess.create!( - permission_template_id: permission_template.id, - agent_type: 'user', - agent_id: user.user_key, - access: 'deposit' - ) - end - let(:files) do - [ - { - path: "#{fixture_path}/world.png", - title: "World!", - visibility: "restricted", - embargo_date: "", - uri: "", - pid: file_pid1 - }, - { - path: "#{fixture_path}/test_file.txt", - title: "Test file", - visibility: "open", - embargo_date: "", - uri: "", - pid: file_pid2 - } - ] - end - - let(:default_attributes) do - { - pid: work_pid, - work_type: "generic_work", - submitter_email: user.email, - files: files, - title: ["My title"], - creator: ["Creator"], - description: ["My description"], - college: "Libraries", - department: "Digital Collections", - license: ["http://creativecommons.org/licenses/by-nc/4.0/"], - rights_statement: ["These are your rights, all three of them"], - geo_subject: ["Dayton, Ohio"], - visibility: "open", - edit_access: editors, - read_access: readers, - member_of_collection_ids: collection_ids, - member_work_ids: member_work_ids - } - end - - shared_examples_for "batch upload object" do - subject(:work_loader) { described_class.new(attributes) } - - it "instantiates the class with minimal required attributes" do - expect(work_loader).to be_an_instance_of(described_class) - end - - it "instantiates a new curation_concern" do - expect(work_loader.curation_concern).to be_an_instance_of(work_type.constantize) - end - - it "builds an actor" do - expect(work_loader.ability).to be_an_instance_of(Ability) - end - - it "creates uploaded files" do - expect(work_loader.file_attributes).to be_an_instance_of(Array) - expect(work_loader.file_attributes.length).to be(2) - expect(work_loader.file_attributes.first[:uploaded_file]).to be_an_instance_of(Hyrax::UploadedFile) - end - - describe "#create" do - before { work_loader.create } - it "saves the curation concern" do - expect(work_loader.curation_concern.id).not_to be_nil - end - - it "includes specified attributes" do - expect(work_loader.curation_concern).to have_attributes(attributes) - end - - it "attaches the files" do - expect(work_loader.curation_concern.file_sets.length).to be(2) - expect(work_loader.curation_concern.file_sets.map(&:id)).not_to include(nil) - end - - context "when collection membership is specified" do - let!(:collection) { create(:collection) } - let(:collection_ids) { [collection.id] } - - it "adds the curation concern to the collection" do - expect(work_loader.curation_concern.member_of_collections.first.id).to eq collection.id - end - end - - context "when file pid is specified" do - let(:file_pid2) { "bez2345" } - it "saves with the specified pid " do - expect(work_loader.curation_concern.file_sets.map(&:id)).to include(file_pid2) - end - end - - context "when work pid is specified" do - let(:work_pid) { "foo1234" } - it "saves with the specified pid " do - expect(work_loader.curation_concern.id).to eq(work_pid) - end - end - - context "when file visibility is specified" do - it "saves the file with the specified visibility" do - expect(work_loader.curation_concern.file_sets.map(&:visibility)).to include("restricted") - end - end - - context "when editors are set" do - let(:editors) { [create(:user).email, create(:user).email] } - it "saves the work with the readers" do - expect(work_loader.curation_concern.edit_users).to include(editors[0]) - expect(work_loader.curation_concern.edit_users).to include(editors[1]) - end - end - - context "when a reader is set" do - let(:readers) { create(:user).email } - it "saves the work with the reader" do - expect(work_loader.curation_concern.read_users).to include(readers) - end - end - - context "when a related work is set" do - let(:related_work) { create(:article) } - let(:member_work_ids) { related_work.id } - it "saves the work with the relationship" do - expect(work_loader.curation_concern.member_ids).to include(related_work.id) - end - end - end - end - - describe "Generic Work" do - it_behaves_like "batch upload object" do - let(:work_type) { "GenericWork" } - let(:attributes) { default_attributes.merge(work_type: 'generic_work') } - end - end - - describe "Article" do - it_behaves_like "batch upload object" do - let(:work_type) { "Article" } - let(:attributes) { default_attributes.merge(work_type: 'article') } - end - end - - describe "Document" do - it_behaves_like "batch upload object" do - let(:work_type) { "Document" } - let(:attributes) { default_attributes.merge(work_type: 'document') } - end - end - - describe "Media" do - it_behaves_like "batch upload object" do - let(:work_type) { "Medium" } - let(:attributes) { default_attributes.merge(work_type: 'medium') } - end - end - - describe "Image" do - it_behaves_like "batch upload object" do - let(:work_type) { "Image" } - let(:attributes) { default_attributes.merge(work_type: 'image') } - end - end - - describe "Dataset" do - it_behaves_like "batch upload object" do - let(:work_type) { "Dataset" } - let(:attributes) { default_attributes.merge(work_type: 'dataset', required_software: 'Minesweeper') } - end - end - - describe "Etd" do - it_behaves_like "batch upload object" do - let(:work_type) { "Etd" } - # Use only 1 committee_member since fedora doesn't preserve the order - let(:attributes) { default_attributes.merge(work_type: 'etd', advisor: ["Some advisor"], committee_member: ["member 1"]) } - end - end - - describe "Student Work" do - it_behaves_like "batch upload object" do - let(:work_type) { "StudentWork" } - let(:attributes) { default_attributes.merge(work_type: 'student_work', advisor: ["Some advisor"]) } - end - end -end