From 9152c15e8418edfeb6cc92bac369007a7fc99dfc Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Thu, 16 Oct 2025 11:08:23 -0400 Subject: [PATCH 001/116] upgrade hyrax to 4.0 (tests fail atm) --- Gemfile | 16 +- Gemfile.lock | 831 +++++++++--------- app/assets/javascripts/application.js | 13 +- app/assets/stylesheets/hyrax.scss | 3 +- app/controllers/application_controller.rb | 3 - .../spot/additional_formats_for_controller.rb | 2 +- .../concerns/spot/solr_document_attributes.rb | 12 - app/models/solr_document.rb | 2 - .../spot/exporters/work_metadata_exporter.rb | 2 +- app/services/spot/{iso6391.rb => iso_6391.rb} | 0 app/services/spot/rdf_authority_parser.rb | 6 +- app/services/spot/work_csv_service.rb | 2 +- app/values/blacklight/types.rb | 34 - config/application.rb | 10 +- config/boot.rb | 1 + config/database.yml | 15 +- config/initializers/hyrax.rb | 27 +- config/initializers/inflections.rb | 17 +- config/initializers/spot_overrides.rb | 2 +- config/initializers/zeitwerk.rb | 12 + lib/tasks/spot/import_rdf_terms.rake | 2 +- .../spot/rdf_authority_parser_spec.rb | 2 +- spec/services/spot/work_csv_service_spec.rb | 2 +- 23 files changed, 509 insertions(+), 507 deletions(-) rename app/services/spot/{iso6391.rb => iso_6391.rb} (100%) delete mode 100644 app/values/blacklight/types.rb create mode 100644 config/initializers/zeitwerk.rb diff --git a/Gemfile b/Gemfile index db585bb3a..88058c632 100644 --- a/Gemfile +++ b/Gemfile @@ -9,13 +9,13 @@ end # # the base rails stack (installed with 'rails new spot') # -gem 'rails', '~> 5.2.7' +gem 'rails', '~> 6.1' # use Puma as the app server gem 'puma', '~> 6.4.0' # Use SCSS for stylesheets -gem 'sass-rails', '~> 5.1.0' +gem 'sass-rails', '~> 6.0' # Use Uglifier as compressor for JavaScript assets gem 'uglifier', '~> 4.2.0' @@ -29,7 +29,7 @@ gem 'jbuilder', '~> 2.11.5' # # the hyrax/spot stack # -gem 'hyrax', '~> 3.6.0' +gem 'hyrax', '~> 4.0.0' # modularize our javascripts gem 'almond-rails', '~> 0.3.0' @@ -44,13 +44,15 @@ gem 'aws-sdk-s3', '~> 1.142.0' gem 'bagit', '~> 0.6.0' # blacklight plugins for enhanced searching -gem 'blacklight_advanced_search', '~> 6.4.1' -gem 'blacklight_oai_provider', '~> 6.0.0' -gem 'blacklight_range_limit', '~> 6.3.3' +gem 'blacklight_advanced_search', '~> 7.0.0' +gem 'blacklight_oai_provider', '~> 7.0.2' +gem 'blacklight_range_limit', '~> 9.1.0' # start up the server faster gem 'bootsnap', '~> 1.17', require: false +gem 'bootstrap', '~> 4.0' + # Bulkrax for batch ingesting objects gem 'browse-everything', '~> 1.1.2' gem 'bulkrax', '~> 9.0.2' @@ -73,7 +75,7 @@ gem 'edtf-humanize', '~> 2.1.0' # a bunch of samvera gems rely on Faraday already, but we'll # require it as we're explicitly using it. -gem 'faraday', '~> 0.17.6' +gem 'faraday' #, '~> 0.17.6' # video file resource for getting information on video derivatives gem 'ffprober' diff --git a/Gemfile.lock b/Gemfile.lock index f10d88aa4..eff5d7b7c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,35 +1,50 @@ GEM remote: https://rubygems.org/ specs: - actioncable (5.2.8.1) - actionpack (= 5.2.8.1) + actioncable (6.1.7.10) + actionpack (= 6.1.7.10) + activesupport (= 6.1.7.10) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailer (5.2.8.1) - actionpack (= 5.2.8.1) - actionview (= 5.2.8.1) - activejob (= 5.2.8.1) + actionmailbox (6.1.7.10) + actionpack (= 6.1.7.10) + activejob (= 6.1.7.10) + activerecord (= 6.1.7.10) + activestorage (= 6.1.7.10) + activesupport (= 6.1.7.10) + mail (>= 2.7.1) + actionmailer (6.1.7.10) + actionpack (= 6.1.7.10) + actionview (= 6.1.7.10) + activejob (= 6.1.7.10) + activesupport (= 6.1.7.10) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.2.8.1) - actionview (= 5.2.8.1) - activesupport (= 5.2.8.1) - rack (~> 2.0, >= 2.0.8) + actionpack (6.1.7.10) + actionview (= 6.1.7.10) + activesupport (= 6.1.7.10) + rack (~> 2.0, >= 2.0.9) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.2.8.1) - activesupport (= 5.2.8.1) + rails-html-sanitizer (~> 1.0, >= 1.2.0) + actiontext (6.1.7.10) + actionpack (= 6.1.7.10) + activerecord (= 6.1.7.10) + activestorage (= 6.1.7.10) + activesupport (= 6.1.7.10) + nokogiri (>= 1.8.5) + actionview (6.1.7.10) + activesupport (= 6.1.7.10) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.0.3) - active-fedora (13.3.0) + rails-html-sanitizer (~> 1.1, >= 1.2.0) + active-fedora (14.0.1) active-triples (>= 0.11.0, < 2.0.0) activemodel (>= 5.1) activesupport (>= 5.1) deprecation - faraday (~> 0.12) + faraday (>= 1.0) faraday-encoding (>= 0.0.5) ldp (>= 0.7.0, < 2) rsolr (>= 1.1.2, < 3) @@ -42,32 +57,35 @@ GEM active_encode (0.8.2) rails sprockets (< 4) - activejob (5.2.8.1) - activesupport (= 5.2.8.1) + activejob (6.1.7.10) + activesupport (= 6.1.7.10) globalid (>= 0.3.6) - activemodel (5.2.8.1) - activesupport (= 5.2.8.1) - activemodel-serializers-xml (1.0.2) - activemodel (> 5.x) - activesupport (> 5.x) + activemodel (6.1.7.10) + activesupport (= 6.1.7.10) + activemodel-serializers-xml (1.0.3) + activemodel (>= 5.0.0.a) + activesupport (>= 5.0.0.a) builder (~> 3.1) - activerecord (5.2.8.1) - activemodel (= 5.2.8.1) - activesupport (= 5.2.8.1) - arel (>= 9.0) + activerecord (6.1.7.10) + activemodel (= 6.1.7.10) + activesupport (= 6.1.7.10) activerecord-import (2.2.0) activerecord (>= 4.2) - activestorage (5.2.8.1) - actionpack (= 5.2.8.1) - activerecord (= 5.2.8.1) - marcel (~> 1.0.0) - activesupport (5.2.8.1) + activestorage (6.1.7.10) + actionpack (= 6.1.7.10) + activejob (= 6.1.7.10) + activerecord (= 6.1.7.10) + activesupport (= 6.1.7.10) + marcel (~> 1.0) + mini_mime (>= 1.1.0) + activesupport (6.1.7.10) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - addressable (2.8.5) - public_suffix (>= 2.0.2, < 6.0) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + zeitwerk (~> 2.3) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) almond-rails (0.3.0) rails (>= 4.2) anystyle (1.4.2) @@ -76,27 +94,29 @@ GEM namae (~> 1.0) wapiti (~> 2.1) anystyle-data (1.3.0) - arel (9.0.0) - ast (2.4.2) - autoprefixer-rails (10.4.13.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.3.0) - aws-partitions (1.887.0) - aws-sdk-core (3.191.0) + awesome_nested_set (3.8.0) + activerecord (>= 4.0.0, < 8.1) + aws-eventstream (1.4.0) + aws-partitions (1.1172.0) + aws-sdk-core (3.233.0) aws-eventstream (~> 1, >= 1.3.0) - aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-partitions (~> 1, >= 1.992.0) + aws-sigv4 (~> 1.9) + base64 + bigdecimal jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.77.0) - aws-sdk-core (~> 3, >= 3.191.0) - aws-sigv4 (~> 1.1) + logger + aws-sdk-kms (1.113.0) + aws-sdk-core (~> 3, >= 3.231.0) + aws-sigv4 (~> 1.5) aws-sdk-s3 (1.142.0) aws-sdk-core (~> 3, >= 3.189.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.8) - aws-sigv4 (1.8.0) + aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) babel-source (5.8.35) babel-transpiler (0.7.0) @@ -108,50 +128,51 @@ GEM base64 (0.3.0) bcp47 (0.3.3) i18n - bcrypt (3.1.19) - bibtex-ruby (6.0.0) + bcrypt (3.1.20) + bibtex-ruby (6.2.0) latex-decode (~> 0.0) + logger (~> 1.7) + racc (~> 1.7) + bigdecimal (3.3.1) bixby (5.0.2) rubocop (= 1.28.2) rubocop-ast rubocop-performance rubocop-rails rubocop-rspec - blacklight (6.25.0) - bootstrap-sass (~> 3.2) + blacklight (7.41.0) deprecation globalid + hashdiff + i18n (>= 1.7.0) jbuilder (~> 2.7) kaminari (>= 0.15) - nokogiri (~> 1.6) - rails (>= 4.2, < 6) - rsolr (>= 1.0.6, < 3) - twitter-typeahead-rails (= 0.11.1.pre.corejavascript) - blacklight-access_controls (0.6.2) - blacklight (~> 6.0) - cancancan (~> 1.8) + ostruct (>= 0.3.2) + rails (>= 6.1, < 8.1) + view_component (>= 2.74, < 4) + zeitwerk + blacklight-access_controls (6.1.0) + blacklight (> 6.0, < 9) + cancancan (>= 1.8) deprecation (~> 1.0) - blacklight-gallery (0.12.0) - blacklight (~> 6.3) - bootstrap-sass (~> 3.0) - openseadragon (>= 0.2.0) - rails - blacklight_advanced_search (6.4.1) - blacklight (~> 6.0, >= 6.0.1) + blacklight-gallery (4.9.0) + blacklight (>= 7.17, < 9) + rails (>= 6.1, < 9) + blacklight_advanced_search (7.0.0) + blacklight (~> 7.0) parslet - blacklight_oai_provider (6.0.0) - blacklight (~> 6.0) - oai (~> 0.4) - rails (>= 4.2, < 6) - blacklight_range_limit (6.3.3) - blacklight (>= 6.0.2) - jquery-rails - rails (>= 3.0) - bootsnap (1.17.0) + blacklight_oai_provider (7.0.2) + blacklight (~> 7.0) + oai (~> 1.2) + rexml + blacklight_range_limit (9.1.0) + blacklight (>= 7.25.2, < 9) + view_component (>= 2.54, < 5) + bootsnap (1.18.6) msgpack (~> 1.2) - bootstrap-sass (3.4.1) - autoprefixer-rails (>= 5.2.1) - sassc (>= 2.0.0) + bootstrap (4.6.2.1) + autoprefixer-rails (>= 9.1.0) + popper_js (>= 1.16.1, < 2) bootstrap_form (5.1.0) actionpack (>= 5.2) activemodel (>= 5.2) @@ -167,7 +188,7 @@ GEM ruby-box signet (~> 0.8) typhoeus - builder (3.2.4) + builder (3.3.0) bulkrax (9.0.2) bagit (~> 0.6.0) coderay @@ -185,7 +206,7 @@ GEM rubyzip simple_form byebug (11.1.3) - cancancan (1.17.0) + cancancan (3.6.1) capybara (3.39.2) addressable matrix @@ -198,11 +219,13 @@ GEM capybara-screenshot (1.0.26) capybara (>= 1.0, < 4) launchy - carrierwave (1.3.3) + carrierwave (1.3.4) activemodel (>= 4.0.0) activesupport (>= 4.0.0) mime-types (>= 1.16) ssrf_filter (~> 1.0, < 1.1.0) + childprocess (5.1.0) + logger (~> 1.5) clipboard-rails (1.7.1) coderay (1.1.3) coffee-rails (5.0.0) @@ -212,52 +235,47 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.2.3) - connection_pool (2.4.1) - crack (0.4.5) + concurrent-ruby (1.3.5) + connection_pool (2.5.4) + crack (1.0.0) + bigdecimal rexml crass (1.0.6) csv (3.3.5) database_cleaner (2.0.2) database_cleaner-active_record (>= 2, < 3) - database_cleaner-active_record (2.1.0) + 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.3.4) + date (3.4.1) declarative (0.0.20) - declarative-builder (0.1.0) - declarative-option (< 0.2.0) - declarative-option (0.1.0) denormalize_fields (1.3.0) activerecord (>= 4.1.14, < 8.0.0) deprecation (1.1.0) activesupport - devise (4.9.2) + devise (4.9.4) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) responders warden (~> 1.2.3) - devise-guests (0.8.1) + devise-guests (0.8.3) devise devise_cas_authenticatable (2.0.2) devise (>= 4.0.0) rack-cas - diff-lcs (1.5.0) - disposable (0.4.7) + diff-lcs (1.6.2) + disposable (0.6.3) declarative (>= 0.0.9, < 1.0.0) - declarative-builder (< 0.2.0) - declarative-option (< 0.2.0) - representable (>= 2.4.0, <= 3.1.0) - uber (< 0.2.0) + representable (>= 3.1.1, < 4) docile (1.4.1) docopt (0.5.0) dotenv (2.7.6) dotenv-rails (2.7.6) dotenv (= 2.7.6) railties (>= 3.2) - draper (4.0.2) + draper (4.0.4) actionpack (>= 5.0) activemodel (>= 5.0) activemodel-serializers-xml (>= 1.0) @@ -286,11 +304,10 @@ GEM concurrent-ruby (~> 1.0) 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) + dry-monads (1.5.0) concurrent-ruby (~> 1.0) - dry-core (~> 0.7) + dry-core (~> 0.9, >= 0.9) + zeitwerk (~> 2.6) dry-schema (1.11.3) concurrent-ruby (~> 1.0) dry-configurable (~> 0.16, >= 0.16) @@ -304,11 +321,6 @@ GEM dry-types (~> 1.6) ice_nine (~> 0.11) zeitwerk (~> 2.6) - 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.6.1) concurrent-ruby (~> 1.0) dry-container (~> 0.3) @@ -337,47 +349,71 @@ GEM roman (~> 0.2.0) equivalent-xml (0.6.0) nokogiri (>= 1.4.3) - erubi (1.12.0) - et-orbi (1.2.7) + erubi (1.13.1) + et-orbi (1.4.0) tzinfo - ethon (0.16.0) + ethon (0.17.0) ffi (>= 1.15.0) - execjs (2.8.1) + execjs (2.10.0) factory_bot (6.4.5) activesupport (>= 5.0.0) factory_bot_rails (6.4.3) factory_bot (~> 6.4) railties (>= 5.0.0) - faraday (0.17.6) - multipart-post (>= 1.2, < 3) - faraday-encoding (0.0.5) + faraday (1.10.4) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.1) + faraday-encoding (0.0.6) faraday - faraday_middleware (0.14.0) - faraday (>= 0.7.4, < 1.0) - ffi (1.15.5) + faraday-excon (1.1.0) + faraday-follow_redirects (0.4.0) + faraday (>= 1, < 3) + faraday-httpclient (1.0.1) + faraday-multipart (1.1.1) + multipart-post (~> 2.0) + faraday-net_http (1.0.2) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.1) + faraday (~> 1.0) + ffi (1.17.2) ffprober (1.0) sorbet-runtime - flipflop (2.7.1) + 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) - fugit (1.8.1) - et-orbi (~> 1, >= 1.2.7) + font-awesome-rails (4.7.0.9) + railties (>= 3.2, < 9.0) + fugit (1.12.1) + et-orbi (~> 1.4) raabro (~> 1.4) gems (1.2.0) - geocoder (1.8.5) + geocoder (1.8.6) base64 (>= 0.1.0) csv (>= 3.0.0) - gli (2.21.0) - globalid (1.1.0) - activesupport (>= 5.0) + gli (2.22.2) + ostruct + globalid (1.3.0) + activesupport (>= 6.1) google-api-client (0.53.0) google-apis-core (~> 0.1) google-apis-generator (~> 0.1) - google-apis-core (0.11.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) @@ -385,10 +421,9 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.a) rexml - webrick google-apis-discovery_v1 (0.14.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-drive_v3 (0.42.0) + google-apis-drive_v3 (0.46.0) google-apis-core (>= 0.11.0, < 2.a) google-apis-generator (0.12.0) activesupport (>= 5.0) @@ -396,7 +431,7 @@ GEM google-apis-core (>= 0.11.0, < 2.a) google-apis-discovery_v1 (~> 0.5) thor (>= 0.20, < 2.a) - google-apis-sheets_v4 (0.24.0) + 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) @@ -414,48 +449,50 @@ GEM temple (>= 0.8.2) thor tilt - hashdiff (1.0.1) + hashdiff (1.2.1) hashie (5.0.0) hiredis (0.6.3) honeybadger (4.12.2) htmlentities (4.3.4) - http_logger (0.7.0) - httpclient (2.8.3) - hydra-access-controls (11.0.7) + http_logger (1.0.1) + httpclient (2.9.0) + mutex_m + hydra-access-controls (12.1.0) active-fedora (>= 10.0.0) - activesupport (>= 4, < 6) - blacklight (>= 5.16) - blacklight-access_controls (~> 0.6.0) - cancancan (~> 1.8) + activesupport (>= 5.2, < 7.1) + blacklight-access_controls (~> 6.0) + cancancan (>= 1.8, < 4) deprecation (~> 1.0) - hydra-core (11.0.7) - hydra-access-controls (= 11.0.7) - railties (>= 4.0.0, < 6) - hydra-derivatives (3.7.0) - active-fedora (>= 11.5.6, != 13.2.1, != 13.2.0, != 13.1.3, != 13.1.2, != 13.1.1, != 13.1.0, != 13.0.0, != 12.2.1, != 12.2.0, != 12.1.1, != 12.1.0, != 12.0.3, != 12.0.2, != 12.0.1, != 12.0.0) + hydra-core (12.1.0) + hydra-access-controls (= 12.1.0) + railties (>= 5.2, < 7.1) + hydra-derivatives (3.8.0) + active-fedora (>= 14.0) + active-triples (>= 1.2) active_encode (~> 0.1) - activesupport (>= 4.0, < 7) + activesupport (>= 4.0, < 7.1) addressable (~> 2.5) deprecation mime-types (> 2.0, < 4.0) mini_magick (>= 3.2, < 5) - hydra-editor (5.0.5) + hydra-editor (6.3.0) active-fedora (>= 9.0.0) - activerecord (~> 5.0) + activerecord (>= 5.2, < 8.0) almond-rails (~> 0.1) - cancancan (~> 1.8) - rails (>= 5, < 6) - simple_form (>= 4.1.0, < 6.0) - sprockets (~> 3.7) + cancancan + psych (~> 3.3, < 4) + rails (>= 5.2, < 8.0) + simple_form (>= 4.1.0, < 5.2) + 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.3.0) - active-fedora (>= 10, < 15) + hydra-head (12.1.0) + hydra-access-controls (= 12.1.0) + hydra-core (= 12.1.0) + rails (>= 5.2, < 7.1) + hydra-pcdm (1.4.0) + active-fedora (>= 10) mime-types (>= 1) rdf-vocab hydra-role-management (1.1.0) @@ -465,54 +502,49 @@ GEM cancancan json (>= 1.8) psych (~> 3.0) - hydra-works (2.1.0) - activesupport (>= 5.2, < 7.1) - hydra-derivatives (~> 3.6) + hydra-works (2.2.0) + activesupport (>= 5.2, < 8.0) + hydra-derivatives (>= 3.6) hydra-file_characterization (~> 1.0) hydra-pcdm (>= 0.9) - hyrax (3.6.0) - active-fedora (~> 13.1, >= 13.1.2) + hyrax (4.0.0) + active-fedora (~> 14.0) almond-rails (~> 0.1) awesome_nested_set (~> 3.1) - blacklight (~> 6.14) - blacklight-gallery (~> 0.7) + blacklight (~> 7.29) + blacklight-gallery (~> 4.0) breadcrumbs_on_rails (~> 3.0) browse-everything (>= 0.16, < 2.0) carrierwave (~> 1.0) clipboard-rails (~> 1.5) + connection_pool (~> 2.4) draper (~> 4.0) dry-equalizer (~> 0.2) dry-events (~> 0.2.0) - dry-monads (< 1.5) + dry-monads (~> 1.5) dry-struct (~> 1.0) - dry-transaction (~> 0.11) dry-validation (~> 1.3) flipflop (~> 2.3) flot-rails (~> 0.0.6) font-awesome-rails (~> 4.2) hydra-derivatives (~> 3.3) - hydra-editor (~> 5.0, >= 5.0.4) - hydra-file_characterization (~> 1.1.2) - hydra-head (~> 11.0, >= 11.0.1) + hydra-editor (~> 6.0) + hydra-file_characterization (~> 1.1) + hydra-head (~> 12.0) hydra-works (>= 0.16) iiif_manifest (>= 0.3, < 2.0) - jquery-datatables-rails (~> 3.4) - jquery-ui-rails (~> 6.0) - json-ld (< 3.2) json-schema - kaminari_route_prefix (~> 0.1.1) legato (~> 0.3) linkeddata mailboxer (~> 0.12) nest (~> 3.1) - noid-rails (~> 3.0.0) + noid-rails (~> 3.0) oauth oauth2 (~> 1.2) + openseadragon posix-spawn - power_converter (~> 0.1, >= 0.1.2) - psych (~> 3.3) qa (~> 5.5, >= 5.5.1) - rails (~> 5.0) + rails (~> 6.0) rails_autolink (~> 1.1) rdf-rdfxml rdf-vocab (~> 3.0) @@ -522,51 +554,48 @@ GEM reform (~> 2.3) reform-rails (~> 0.2.0) retriable (>= 2.9, < 4.0) - samvera-nesting_indexer (~> 2.0) - sass-rails (~> 5.0) + sass-rails (~> 6.0) select2-rails (~> 3.5) signet + sprockets (~> 3.7) tinymce-rails (~> 5.10) - valkyrie (~> 2, >= 2.1.1) + valkyrie (~> 3.0.1) + view_component (~> 2.74.1) hyrax-spec (0.3.2) rspec (~> 3.6) - i18n (1.14.4) + i18n (1.14.7) concurrent-ruby (~> 1.0) ice_nine (0.11.2) - iiif_manifest (1.3.1) + iiif_manifest (1.6.0) activesupport (>= 4) - iso-639 (0.3.6) + iso-639 (0.3.8) + csv iso8601 (0.9.1) jbuilder (2.11.5) actionview (>= 5.0.0) activesupport (>= 5.0.0) jmespath (1.6.2) - jquery-datatables-rails (3.4.0) - actionpack (>= 3.1) - jquery-rails - railties (>= 3.1) - sass-rails 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.3) + json (2.15.1) json-canonicalization (0.4.0) - json-ld (3.1.10) + json-ld (3.2.5) htmlentities (~> 4.3) - json-canonicalization (~> 0.2) + json-canonicalization (~> 0.3, >= 0.3.2) link_header (~> 0.0, >= 0.0.8) - multi_json (~> 1.14) - rack (~> 2.0) - rdf (~> 3.1) - json-ld-preloaded (3.1.6) - json-ld (~> 3.1) - rdf (~> 3.1) - json-schema (4.0.0) - addressable (>= 2.8) - jwt (2.7.1) + multi_json (~> 1.15) + 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 (5.2.2) + addressable (~> 2.8) + bigdecimal (~> 3.1) + jwt (2.10.2) + base64 kaminari (1.2.2) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.2) @@ -579,25 +608,26 @@ GEM activerecord kaminari-core (= 1.2.2) kaminari-core (1.2.2) - kaminari_route_prefix (0.1.1) - kaminari (~> 1.0) language_list (1.2.1) latex-decode (0.4.0) - launchy (2.5.2) + launchy (3.1.1) addressable (~> 2.8) - ld-patch (3.2.0) - ebnf (~> 2.2) + childprocess (~> 5.0) + logger (~> 1.6) + ld-patch (3.2.2) + ebnf (~> 2.3) rdf (~> 3.2) rdf-xsd (~> 3.2) sparql (~> 3.2) sxp (~> 1.2) - ldp (1.0.3) + ldp (1.2.0) deprecation - faraday + faraday (>= 1) http_logger - json-ld - rdf (>= 1.1) + json-ld (~> 3.2) + rdf (~> 3.2) rdf-isomorphic + rdf-ldp rdf-turtle rdf-vocab (>= 0.8) slop @@ -608,7 +638,7 @@ GEM rdf-vocab (~> 3.0) legato (0.7.0) multi_json - libxml-ruby (5.0.3) + libxml-ruby (5.0.5) link_header (0.0.8) linkeddata (3.1.6) equivalent-xml (~> 0.6) @@ -640,13 +670,13 @@ GEM listen (3.7.1) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) - logger (1.5.3) + logger (1.7.0) lograge (0.14.0) actionpack (>= 4) activesupport (>= 4) railties (>= 4) request_store (~> 1.0) - loofah (2.22.0) + loofah (2.24.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -657,78 +687,84 @@ GEM mailboxer (0.15.1) carrierwave (>= 0.5.8) rails (>= 5.0.0) - marcel (1.0.4) - matrix (0.4.2) + marcel (1.1.0) + 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.2023.0218.1) - mini_magick (4.12.0) + method_source (1.1.0) + mime-types (3.7.0) + logger + mime-types-data (~> 3.2025, >= 3.2025.0507) + mime-types-data (3.2025.0924) + mini_magick (4.13.2) mini_mime (1.1.5) - mini_portile2 (2.8.5) - minitest (5.22.3) - msgpack (1.7.1) + mini_portile2 (2.8.9) + minitest (5.26.0) + msgpack (1.8.0) multi_json (1.15.0) multi_xml (0.6.0) - multipart-post (2.4.0) - namae (1.1.1) + multipart-post (2.4.1) + mutex_m (0.3.0) + namae (1.2.0) + racc (~> 1.7) nest (3.2.0) redic - net-http-persistent (4.0.2) - connection_pool (~> 2.2) - net-imap (0.4.10) + 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-smtp (0.4.0.1) + net-smtp (0.5.1) net-protocol - nio4r (2.7.0) + nio4r (2.7.4) noid (0.9.0) - noid-rails (3.0.3) - actionpack (>= 5.0.0, < 7) + noid-rails (3.3.0) + actionpack (>= 5.0.0, < 9) noid (~> 0.9) - nokogiri (1.15.6) + nokogiri (1.15.7) mini_portile2 (~> 2.8.2) racc (~> 1.4) non-digest-assets (2.2.0) activesupport (>= 5.2, < 7.1) sprockets (>= 2.0, < 5.0) - oai (0.4.0) + oai (1.3.0) builder (>= 3.1.0) - faraday - faraday_middleware - oauth (1.1.0) - oauth-tty (~> 1.0, >= 1.0.1) + faraday (< 3) + faraday-follow_redirects (>= 0.3.0, < 2) + rexml + oauth (1.1.2) + oauth-tty (~> 1.0, >= 1.0.6) snaky_hash (~> 2.0) - version_gem (~> 1.1) - oauth-tty (1.0.5) - version_gem (~> 1.1, >= 1.1.1) + version_gem (~> 1.1, >= 1.1.9) + oauth-tty (1.0.6) + version_gem (~> 1.1, >= 1.1.9) 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, < 4) - okcomputer (1.18.5) - openseadragon (0.6.0) - rails (> 3.2.0) + okcomputer (1.18.6) + openseadragon (1.0.17) + rails (> 6.1.0) orm_adapter (0.5.0) os (1.1.4) - parallel (1.23.0) - parser (3.2.2.3) + ostruct (0.6.3) + parallel (1.27.0) + parser (3.3.9.0) ast (~> 2.4.1) racc parslet (2.0.0) - pg (1.5.4) + pg (1.5.9) + popper_js (1.16.1) posix-spawn (0.3.15) - power_converter (0.1.2) + prism (1.5.2) psych (3.3.4) - public_suffix (5.0.3) - puma (6.4.2) + public_suffix (5.1.1) + puma (6.4.3) nio4r (~> 2.0) qa (5.15.0) activerecord-import @@ -740,56 +776,59 @@ GEM rails (>= 5.0, < 8.1) rdf raabro (1.4.0) - racc (1.7.3) - rack (2.2.8.1) + racc (1.8.1) + rack (2.2.20) rack-cas (0.16.1) addressable (~> 2.3) nokogiri (~> 1.5) rack (>= 1.3) - rack-protection (3.0.6) - rack - rack-test (2.1.0) + rack-protection (3.2.0) + base64 (>= 0.1.0) + rack (~> 2.2, >= 2.2.4) + rack-test (2.2.0) rack (>= 1.3) - rails (5.2.8.1) - actioncable (= 5.2.8.1) - actionmailer (= 5.2.8.1) - actionpack (= 5.2.8.1) - actionview (= 5.2.8.1) - activejob (= 5.2.8.1) - activemodel (= 5.2.8.1) - activerecord (= 5.2.8.1) - activestorage (= 5.2.8.1) - activesupport (= 5.2.8.1) - bundler (>= 1.3.0) - railties (= 5.2.8.1) + rails (6.1.7.10) + actioncable (= 6.1.7.10) + actionmailbox (= 6.1.7.10) + actionmailer (= 6.1.7.10) + actionpack (= 6.1.7.10) + actiontext (= 6.1.7.10) + actionview (= 6.1.7.10) + activejob (= 6.1.7.10) + activemodel (= 6.1.7.10) + activerecord (= 6.1.7.10) + activestorage (= 6.1.7.10) + activesupport (= 6.1.7.10) + bundler (>= 1.15.0) + railties (= 6.1.7.10) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) activesupport (>= 5.0.1.rc1) - rails-dom-testing (2.2.0) + rails-dom-testing (2.3.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.2) loofah (~> 2.21) - nokogiri (~> 1.14) + 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.8.1) - actionpack (= 5.2.8.1) - activesupport (= 5.2.8.1) + railties (6.1.7.10) + actionpack (= 6.1.7.10) + activesupport (= 6.1.7.10) method_source - rake (>= 0.8.7) - thor (>= 0.19.0, < 2.0) + rake (>= 12.2) + thor (~> 1.0) rainbow (3.1.1) - rake (13.1.0) + 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.11) + rdf (3.2.12) link_header (~> 0.0, >= 0.0.8) rdf-aggregate-repo (3.2.1) rdf (~> 3.2) @@ -797,6 +836,9 @@ GEM rdf (~> 3.2) rdf-json (3.2.0) rdf (~> 3.2) + rdf-ldp (0.1.0) + deprecation + rdf rdf-microdata (3.2.1) htmlentities (~> 4.3) nokogiri (~> 1.13) @@ -812,7 +854,7 @@ GEM rdf (~> 3.2) rdf-ordered-repo (3.2.1) rdf (~> 3.2, >= 3.2.1) - rdf-rdfa (3.2.2) + rdf-rdfa (3.2.3) haml (>= 5.2, < 7) htmlentities (~> 4.3) rdf (~> 3.2) @@ -827,13 +869,13 @@ GEM rdf-reasoner (0.8.0) rdf (~> 3.2) rdf-xsd (~> 3.2) - rdf-tabular (3.1.1) - addressable (~> 2.3) + rdf-tabular (3.2.1) + addressable (~> 2.8) bcp47 (~> 0.3, >= 0.3.3) - json-ld (~> 3.1) - rdf (~> 3.1) - rdf-vocab (~> 3.1) - rdf-xsd (~> 3.1) + json-ld (~> 3.2) + rdf (~> 3.2, >= 3.2.7) + rdf-vocab (~> 3.2) + rdf-xsd (~> 3.2) rdf-trig (3.2.0) ebnf (~> 2.2) rdf (~> 3.2) @@ -856,44 +898,44 @@ GEM redis (>= 4) redlock (1.3.2) redis (>= 3.0.0, < 6.0) - reform (2.5.0) - disposable (>= 0.4.2, < 0.5.0) - representable (>= 2.4.0, < 3.1.0) + reform (2.6.2) + disposable (>= 0.5.0, < 1.0.0) + representable (>= 3.1.1, < 4) uber (< 0.2.0) - reform-rails (0.2.5) + reform-rails (0.2.6) activemodel (>= 5.0) reform (>= 2.3.1, < 3.0.0) - regexp_parser (2.8.1) - representable (3.0.4) + regexp_parser (2.11.3) + representable (3.2.0) declarative (< 0.1.0) - declarative-option (< 0.2.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.1.0) + responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) retriable (3.1.2) - rexml (3.2.6) + rexml (3.4.4) roman (0.2.0) rsolr (2.5.0) builder (>= 2.1.2) faraday (>= 0.9, < 3, != 2.0.0) - rspec (3.12.0) - rspec-core (~> 3.12.0) - rspec-expectations (~> 3.12.0) - rspec-mocks (~> 3.12.0) - rspec-core (3.12.2) - rspec-support (~> 3.12.0) - rspec-expectations (3.12.3) + rspec (3.13.1) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.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.12.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.12.6) + rspec-mocks (3.13.6) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.12.0) + rspec-support (~> 3.13.0) rspec-rails (5.1.2) actionpack (>= 5.2) activesupport (>= 5.2) @@ -902,7 +944,7 @@ GEM rspec-expectations (~> 3.10) rspec-mocks (~> 3.10) rspec-support (~> 3.10) - rspec-support (3.12.1) + rspec-support (3.13.6) rspec_junit_formatter (0.4.1) rspec-core (>= 2, < 4, != 2.12.0) rubocop (1.28.2) @@ -914,9 +956,10 @@ GEM rubocop-ast (>= 1.17.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.29.0) - parser (>= 3.2.1.0) - rubocop-performance (1.18.0) + rubocop-ast (1.47.1) + 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) @@ -933,41 +976,36 @@ GEM ruby-progressbar (1.13.0) ruby2_keywords (0.0.5) rubyzip (2.3.2) - samvera-nesting_indexer (2.0.0) - dry-equalizer - sass (3.7.4) - sass-listen (~> 4.0.0) - sass-listen (4.0.0) - rb-fsevent (~> 0.9, >= 0.9.4) - rb-inotify (~> 0.9, >= 0.9.7) - sass-rails (5.1.0) - railties (>= 5.2.0) - sass (~> 3.1) - sprockets (>= 2.8, < 4.0) - sprockets-rails (>= 2.0, < 4.0) - tilt (>= 1.1, < 3) + sass-rails (6.0.0) + sassc-rails (~> 2.1, >= 2.1.1) sassc (2.4.0) ffi (~> 1.9) + sassc-rails (2.1.2) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt scanf (1.0.0) select2-rails (3.5.11) selenium-webdriver (4.9.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) - shacl (0.1.1) - json-ld (~> 3.1, >= 3.1.7) - rdf (~> 3.1, >= 3.1.8) - sparql (~> 3.1) - sxp (~> 1.1) - shex (0.6.4) - ebnf (~> 2.1, >= 2.2) + shacl (0.3.0) + json-ld (~> 3.2) + rdf (~> 3.2, >= 3.2.8) + sparql (~> 3.2, >= 3.2.4) + sxp (~> 1.2) + shex (0.7.1) + ebnf (~> 2.2) htmlentities (~> 4.3) - json-ld (~> 3.1) - json-ld-preloaded (~> 3.1) - rdf (~> 3.1) - rdf-xsd (~> 3.1) - sparql (~> 3.1) - sxp (~> 1.1) + json-ld (~> 3.2) + json-ld-preloaded (~> 3.2) + rdf (~> 3.2) + rdf-xsd (~> 3.2) + sparql (~> 3.2) + sxp (~> 1.2) shoulda-matchers (4.5.1) activesupport (>= 4.2.0) sidekiq (5.2.10) @@ -978,7 +1016,7 @@ GEM sidekiq-cron (1.9.1) fugit (~> 1.8) sidekiq (>= 4.2.1) - signet (0.17.0) + signet (0.19.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) @@ -993,7 +1031,7 @@ GEM simplecov-cobertura (2.1.0) rexml simplecov (~> 0.19) - simplecov-html (0.12.3) + simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) slack-ruby-client (0.14.6) activesupport @@ -1003,36 +1041,37 @@ GEM hashie websocket-driver slop (4.10.1) - snaky_hash (2.0.1) - hashie - version_gem (~> 1.1, >= 1.1.1) - sorbet-runtime (0.5.11463) - sparql (3.2.5) - builder (~> 3.2) - ebnf (~> 2.2, >= 2.3.1) + snaky_hash (2.0.3) + hashie (>= 0.1.0, < 6) + version_gem (>= 1.1.8, < 3) + sorbet-runtime (0.5.12443) + 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) + 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) spring (2.1.1) spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) - sprockets (3.7.2) + sprockets (3.7.5) + base64 concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-es6 (0.9.2) babel-source (>= 5.8.11) babel-transpiler sprockets (>= 3.0.0) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) ssrf_filter (1.0.8) stub_env (1.0.4) @@ -1040,40 +1079,35 @@ GEM sxp (1.2.4) matrix (~> 0.4) rdf (~> 3.2) - temple (0.10.3) - terminal-table (3.0.2) - unicode-display_width (>= 1.1.1, < 3) - thor (1.3.1) - thread_safe (0.3.6) - tilt (2.3.0) - timeout (0.4.1) - tinymce-rails (5.10.7.1) + temple (0.10.4) + terminal-table (4.0.0) + unicode-display_width (>= 1.1.1, < 4) + thor (1.4.0) + tilt (2.6.1) + timeout (0.4.3) + tinymce-rails (5.10.9) railties (>= 3.1.1) + trailblazer-option (0.1.2) turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) - twitter-typeahead-rails (0.11.1.pre.corejavascript) - actionpack (>= 3.1) - jquery-rails - railties (>= 3.1) - typhoeus (1.4.0) + typhoeus (1.4.1) ethon (>= 0.9.0) - tzinfo (1.2.11) - thread_safe (~> 0.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) uber (0.1.0) - uglifier (4.2.0) + uglifier (4.2.1) execjs (>= 0.3.0, < 3) - unicode-display_width (2.4.2) - unicode-types (1.8.0) + unicode-display_width (2.6.0) + unicode-types (1.11.0) validatable (1.6.7) - valkyrie (2.2.0) + valkyrie (3.0.3) activemodel activesupport - disposable (~> 0.4.5) - draper dry-struct dry-types (~> 1.0) - faraday (< 1.0) + faraday (>= 0.9, < 3, != 2.0.0) + faraday-multipart json json-ld railties @@ -1081,24 +1115,28 @@ GEM rdf-vocab reform (~> 2.2) reform-rails - version_gem (1.1.3) + version_gem (1.1.9) + view_component (2.74.1) + activesupport (>= 5.0.0, < 8.0) + concurrent-ruby (~> 1.0) + method_source (~> 1.0) wapiti (2.1.0) builder (~> 3.2) rexml (~> 3.0) warden (1.2.9) rack (>= 2.0.9) - webmock (3.19.1) + webmock (3.25.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.8.1) - websocket (1.2.10) - websocket-driver (0.7.6) + websocket (1.2.11) + websocket-driver (0.8.0) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.13) + zeitwerk (2.6.18) PLATFORMS ruby @@ -1109,10 +1147,11 @@ DEPENDENCIES aws-sdk-s3 (~> 1.142.0) bagit (~> 0.6.0) bixby (~> 5.0.1) - blacklight_advanced_search (~> 6.4.1) - blacklight_oai_provider (~> 6.0.0) - blacklight_range_limit (~> 6.3.3) + blacklight_advanced_search (~> 7.0.0) + blacklight_oai_provider (~> 7.0.2) + blacklight_range_limit (~> 9.1.0) bootsnap (~> 1.17) + bootstrap (~> 4.0) browse-everything (~> 1.1.2) bulkrax (~> 9.0.2) byebug (~> 11.1.3) @@ -1128,11 +1167,11 @@ DEPENDENCIES edtf-humanize (~> 2.1.0) equivalent-xml (~> 0.6.0) factory_bot_rails (~> 6) - faraday (~> 0.17.6) + faraday ffprober honeybadger (~> 4.12.1) hydra-role-management (~> 1.1.0) - hyrax (~> 3.6.0) + hyrax (~> 4.0.0) hyrax-spec (~> 0.3.2) iso-639 (~> 0.3.6) jbuilder (~> 2.11.5) @@ -1146,7 +1185,7 @@ DEPENDENCIES okcomputer (~> 1.18.5) pg (~> 1.5.4) puma (~> 6.4.0) - rails (~> 5.2.7) + rails (~> 6.1) rails-controller-testing (~> 1.0.5) rdf-vocab (~> 3.2.7) redlock (>= 0.1.2, < 2.0) @@ -1156,7 +1195,7 @@ DEPENDENCIES rspec-rails (~> 5.1) rspec_junit_formatter (~> 0.4.1) rubyzip (~> 2.3.2) - sass-rails (~> 5.1.0) + sass-rails (~> 6.0) selenium-webdriver shoulda-matchers (~> 4) sidekiq (~> 5.2.9) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 7e6b2f8dc..31278a8d0 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,12 +13,17 @@ //= require turbolinks // Required by Blacklight -//= require jquery +//= require jquery3 //= require blacklight_advanced_search //= require blacklight_range_limit -//= require jquery_ujs -//= require dataTables/jquery.dataTables -//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap +//= require rails-ujs + +//= require popper +//= require twitter/typeahead +//= require bootstrap + +//= require jquery.dataTables +//= require dataTables.bootstrap4 //= require blacklight/blacklight // local require that in turn calls a require diff --git a/app/assets/stylesheets/hyrax.scss b/app/assets/stylesheets/hyrax.scss index 2baaa74e2..6d3b65ea2 100644 --- a/app/assets/stylesheets/hyrax.scss +++ b/app/assets/stylesheets/hyrax.scss @@ -2,13 +2,12 @@ @import "spot/variables"; @import "bootstrap-compass"; -@import "bootstrap-sprockets"; @import "bootstrap-default-overrides"; @import 'bootstrap'; @import 'blacklight/blacklight'; @import "font-awesome"; -@import "blacklight_gallery/gallery"; @import "blacklight_gallery/masonry"; @import "blacklight_gallery/slideshow"; @import "blacklight_gallery/osd_viewer"; +@import "blacklight_gallery/gallery"; @import 'hyrax/hyrax'; diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index fa502e1f0..3d71e09ec 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -13,9 +13,6 @@ class ApplicationController < ActionController::Base before_action :store_user_location!, if: :storable_location? - # from Blacklight: 'Discarding flash messages on XHR requests is deprecated.' - skip_after_action :discard_flash_if_xhr - protect_from_forgery with: :exception # @return [Hash] diff --git a/app/controllers/concerns/spot/additional_formats_for_controller.rb b/app/controllers/concerns/spot/additional_formats_for_controller.rb index 25f3ccfea..ca2ef684e 100644 --- a/app/controllers/concerns/spot/additional_formats_for_controller.rb +++ b/app/controllers/concerns/spot/additional_formats_for_controller.rb @@ -22,7 +22,7 @@ def csv_filename # @return [String] def work_as_csv - Spot::WorkCSVService.new(presenter.solr_document).csv + Spot::WorkCsvService.new(presenter.solr_document).csv end end end diff --git a/app/models/concerns/spot/solr_document_attributes.rb b/app/models/concerns/spot/solr_document_attributes.rb index 84d37f80f..9f462933d 100644 --- a/app/models/concerns/spot/solr_document_attributes.rb +++ b/app/models/concerns/spot/solr_document_attributes.rb @@ -3,10 +3,6 @@ module Spot # Various model attributes for the single SolrDocument model. Since that model wraps # every kind of Solr document, we need to ensure that _all_ of the models attributes # are available. - # - # - # - # @todo Remove {.attribute} definition when Hyrax upgrades to Blacklight >=7 module SolrDocumentAttributes extend ActiveSupport::Concern @@ -107,13 +103,5 @@ module SolrDocumentAttributes attribute :citation_firstpage, ::Blacklight::Types::String, 'citation_firstpage_ss' attribute :citation_lastpage, ::Blacklight::Types::String, 'citation_lastpage_ss' end - - module ClassMethods - def attribute(name, type, field) - define_method name do - type.coerce(self[field]) - end - end - end end end diff --git a/app/models/solr_document.rb b/app/models/solr_document.rb index ad4845652..6dc7f5af6 100644 --- a/app/models/solr_document.rb +++ b/app/models/solr_document.rb @@ -1,6 +1,4 @@ # frozen_string_literal: true -# -# Generated from +rails generate hyrax:install+ class SolrDocument include Blacklight::Solr::Document include BlacklightOaiProvider::SolrDocument diff --git a/app/services/spot/exporters/work_metadata_exporter.rb b/app/services/spot/exporters/work_metadata_exporter.rb index 196bfc427..8b193f338 100644 --- a/app/services/spot/exporters/work_metadata_exporter.rb +++ b/app/services/spot/exporters/work_metadata_exporter.rb @@ -55,7 +55,7 @@ def export_for_format(format) # @return [String] def generate_csv_content - Spot::WorkCSVService.new(solr_document).csv + Spot::WorkCsvService.new(solr_document).csv end # @return [RDF::Graph] diff --git a/app/services/spot/iso6391.rb b/app/services/spot/iso_6391.rb similarity index 100% rename from app/services/spot/iso6391.rb rename to app/services/spot/iso_6391.rb diff --git a/app/services/spot/rdf_authority_parser.rb b/app/services/spot/rdf_authority_parser.rb index 46e9c5791..b476e11ad 100644 --- a/app/services/spot/rdf_authority_parser.rb +++ b/app/services/spot/rdf_authority_parser.rb @@ -9,14 +9,14 @@ # # @example # -# class FrenchRDFAuthorityParser < Spot::RDFAuthorityParser +# class FrenchRdfAuthorityParser < Spot::RdfAuthorityParser # self.preferred_language = :fr # end # -# FrenchRDFAuthorityParser.load_rdf('languages_fr', ['http://id.loc.gov/vocabulary/iso639-1.nt']) +# FrenchRdfAuthorityParser.load_rdf('languages_fr', ['http://id.loc.gov/vocabulary/iso639-1.nt']) # module Spot - class RDFAuthorityParser < ::Qa::Services::RDFAuthorityParser + class RdfAuthorityParser < ::Qa::Services::RDFAuthorityParser class_attribute :preferred_language self.preferred_language = :en diff --git a/app/services/spot/work_csv_service.rb b/app/services/spot/work_csv_service.rb index a0eeae1f4..63e280c70 100644 --- a/app/services/spot/work_csv_service.rb +++ b/app/services/spot/work_csv_service.rb @@ -6,7 +6,7 @@ require 'csv' module Spot - class WorkCSVService + class WorkCsvService attr_reader :work, :terms, :multi_value_separator, :include_headers # @param [ActiveFedora::Base,SolrDocument] work diff --git a/app/values/blacklight/types.rb b/app/values/blacklight/types.rb deleted file mode 100644 index b0751d1fb..000000000 --- a/app/values/blacklight/types.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true -# -# This is copied whole-hog from Blacklight@master and gives us types to use -# for -# @todo remove this file when Hyrax upgrades to Blacklight 7. -module Blacklight - # These are data types that blacklight can use to coerce values from the index - module Types - class Array - def self.coerce(input) - ::Array.wrap(input) - end - end - - class String - def self.coerce(input) - ::Array.wrap(input).first - end - end - - class Date - def self.coerce(input) - field = String.coerce(input) - return if field.blank? - - begin - ::Date.parse(field) - rescue ArgumentError - Rails.logger.info "Unable to parse date: #{field.first.inspect}" - end - end - end - end -end diff --git a/config/application.rb b/config/application.rb index 93921061d..fa1a7efc0 100644 --- a/config/application.rb +++ b/config/application.rb @@ -13,11 +13,9 @@ if ActiveModel::Type::Boolean.new.cast(ENV.fetch('SPOT_IGNORE_DEPRECATIONS', false)) require 'deprecation' - ActiveSupport::Deprecation.silence do - Deprecation.default_deprecation_behavior = :silence - - Bundler.require(*Rails.groups) - end + ActiveSupport::Deprecation.silenced = true + Deprecation.default_deprecation_behavior = :silence + Bundler.require(*Rails.groups) else # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. @@ -27,7 +25,7 @@ module Spot class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 5.1 + config.load_defaults 6.0 # use sidekiq for async jobs config.active_job.queue_adapter = :sidekiq diff --git a/config/boot.rb b/config/boot.rb index b3e08ed18..8b2631ad7 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -2,4 +2,5 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) require 'bundler/setup' # Set up gems listed in the Gemfile. +require 'logger' require 'bootsnap/setup' # Load our app faster w/ Bootsnap diff --git a/config/database.yml b/config/database.yml index f5381cfa9..c799f8977 100644 --- a/config/database.yml +++ b/config/database.yml @@ -3,18 +3,27 @@ default: &default pool: <%= ENV.fetch('RAILS_MAX_THREADS', 5) %> timeout: 5000 encoding: unicode - url: "postgres://<%= ENV.fetch('PSQL_USER', 'spot_dev_user') %>:<%= ENV.fetch('PSQL_PASSWORD', 'spot_dev_pw') %>@<%= ENV.fetch('PSQL_HOST', 'localhost') %>/<%= ENV.fetch('PSQL_DATABASE', 'spot_development') %>" development: <<: *default + username: <%= ENV.fetch('PSQL_USER', 'spot_dev_user') %> + password: <%= ENV.fetch('PSQL_PASSWORD', 'spot_dev_pw') %> + host: <%= ENV.fetch('PSQL_HOST', 'localhost') %> + database: <%= ENV.fetch('PSQL_DATABASE', 'spot_development') %> # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: <<: *default - url: "postgres://<%= ENV.fetch('PSQL_USER', 'spot_dev_user') %>:<%= ENV.fetch('PSQL_PASSWORD', 'spot_dev_pw') %>@<%= ENV.fetch('PSQL_HOST', 'localhost') %>/<%= ENV.fetch('PSQL_TEST_DATABASE', 'spot_test') %>" + username: <%= ENV.fetch('PSQL_USER', 'spot_dev_user') %> + password: <%= ENV.fetch('PSQL_PASSWORD', 'spot_dev_pw') %> + host: <%= ENV.fetch('PSQL_HOST', 'localhost') %> + database: <%= ENV.fetch('PSQL_DATABASE', 'spot_test') %> production: <<: *default - + username: <%= ENV.fetch('PSQL_USER') %> + password: <%= ENV.fetch('PSQL_PASSWORD') %> + host: <%= ENV.fetch('PSQL_HOST') %> + database: <%= ENV.fetch('PSQL_DATABASE') %> \ No newline at end of file diff --git a/config/initializers/hyrax.rb b/config/initializers/hyrax.rb index 0bebd4fc0..97f85694a 100644 --- a/config/initializers/hyrax.rb +++ b/config/initializers/hyrax.rb @@ -52,9 +52,6 @@ # Requires a Google Analytics id and OAuth2 keyfile. See README for more info # config.analytics = false - # Google Analytics tracking ID to gather usage statistics - config.google_analytics_id = ENV.fetch('GOOGLE_ANALYTICS_ID', nil) - # Date you wish to start collecting Google Analytic statistics for # Leaving it blank will set the start date to when ever the file was uploaded by # NOTE: if you have always sent analytics to GA for downloads and page views leave this commented out @@ -299,7 +296,7 @@ # ingest files from the file system that are not part of the BrowseEverything # mount point. # - config.whitelisted_ingest_dirs = [ + config.registered_ingest_dirs = [ Rails.root.join('tmp', 'ingest').to_s, Rails.root.to_s ] @@ -307,15 +304,17 @@ config.branding_path = ENV.fetch('HYRAX_COLLECTION_BRANDING_PATH', Rails.root.join('public', 'branding')) end -Date::DATE_FORMATS[:standard] = "%m/%d/%Y" - -# Hyrax v4 adds a helper method on the Hyrax constant that Bulkrax v9 depends on, -# so we'll patch it in if it doesn't exist yet. This came up while having issues -# with Bulkrax exports. -unless Hyrax.respond_to?(:index_field_mapper) - module Hyrax - def self.index_field_mapper - config.index_field_mapper +Rails.application.reloader.to_prepare do + Date::DATE_FORMATS[:standard] = "%m/%d/%Y" + + # Hyrax v4 adds a helper method on the Hyrax constant that Bulkrax v9 depends on, + # so we'll patch it in if it doesn't exist yet. This came up while having issues + # with Bulkrax exports. + unless Hyrax.respond_to?(:index_field_mapper) + module Hyrax + def self.index_field_mapper + config.index_field_mapper + end end end -end +end \ No newline at end of file diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index aa7435fbc..caf3256f5 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -1,17 +1,6 @@ # frozen_string_literal: true # Be sure to restart your server when you modify this file. - -# Add new inflection rules using the following format. Inflections -# are locale specific, and you may define rules for as many different -# locales as you wish. All of these examples are active by default: # ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.plural /^(ox)$/i, '\1en' -# inflect.singular /^(ox)en/i, '\1' -# inflect.irregular 'person', 'people' -# inflect.uncountable %w( fish sheep ) -# end - -# These inflection rules are supported but not enabled by default: -# ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.acronym 'RESTful' -# end +# inflect.acronym 'CSV' +# inflect.acronym 'ISO' +# end \ No newline at end of file diff --git a/config/initializers/spot_overrides.rb b/config/initializers/spot_overrides.rb index 8f0d4117c..0a4af80c9 100644 --- a/config/initializers/spot_overrides.rb +++ b/config/initializers/spot_overrides.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # # Class attribute updates + monkey-patching customizations for Hyrax. -Rails.application.config.to_prepare do +Rails.application.reloader.to_prepare do # Bump start the Noid minter in development: # Using Bulkrax on a brand-new Hyrax application will wreak havoc with # multiple async jobs running MinterState.create! with the same "unique" diff --git a/config/initializers/zeitwerk.rb b/config/initializers/zeitwerk.rb new file mode 100644 index 000000000..f7bf74147 --- /dev/null +++ b/config/initializers/zeitwerk.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +ActiveSupport::Inflector.inflections(:en) do |inflect| + inflect.acronym 'ISO' +end + +Rails.autoloaders.each do |autoloader| + autoloader.inflector.inflect( + 'spot/iso_6391' => 'Spot::ISO6391', + 'spot/rdf_authority_parser' => 'Spot::RDFAuthorityParser', + 'spot/work_csv_service' => 'Spot::WorkCSVService' + ) +end diff --git a/lib/tasks/spot/import_rdf_terms.rake b/lib/tasks/spot/import_rdf_terms.rake index 90cd83ee3..b361f1720 100644 --- a/lib/tasks/spot/import_rdf_terms.rake +++ b/lib/tasks/spot/import_rdf_terms.rake @@ -4,7 +4,7 @@ namespace :spot do desc 'Loads remote RDF terms locally' task :load, [:name, :uri] => [:environment] do |_t, args| abort 'Need to provide [name, uri] parameters' unless args[:name] && args[:uri] - Spot::RDFAuthorityParser.import_rdf(args[:name], Array(args[:uri])) + Spot::RdfAuthorityParser.import_rdf(args[:name], Array(args[:uri])) end end end diff --git a/spec/services/spot/rdf_authority_parser_spec.rb b/spec/services/spot/rdf_authority_parser_spec.rb index eb94b5b29..97f27409d 100644 --- a/spec/services/spot/rdf_authority_parser_spec.rb +++ b/spec/services/spot/rdf_authority_parser_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true # borrows heavily from # samvera/questioning_authority:spec/lib/services/rdf_authority_parser_spec.rb -RSpec.describe Spot::RDFAuthorityParser do +RSpec.describe Spot::RdfAuthorityParser do let(:source) { [Rails.root.join('spec', 'fixtures', 'iso639-1-en.nt')] } let(:format) { :ntriples } let(:predicate) { ::RDF::Vocab::SKOS.prefLabel } diff --git a/spec/services/spot/work_csv_service_spec.rb b/spec/services/spot/work_csv_service_spec.rb index 22546fc4b..8a0c1bb21 100644 --- a/spec/services/spot/work_csv_service_spec.rb +++ b/spec/services/spot/work_csv_service_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require 'tmpdir' -RSpec.describe Spot::WorkCSVService do +RSpec.describe Spot::WorkCsvService do let(:service) { described_class.new(work, terms: terms) } let(:work) { build(:publication, id: 'abc123', title: ['one title', 'two titles']) } let(:terms) { %i[id title creator] } From 0525cc31df34333a8862e9eda242f081b16fcb4c Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Thu, 16 Oct 2025 11:09:05 -0400 Subject: [PATCH 002/116] introduce hyrax_valkyrie matrix to github actions just 'false' for right now until tests pass --- .github/workflows/lint-and-test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml index 4ee18cca9..0c6baff24 100644 --- a/.github/workflows/lint-and-test.yml +++ b/.github/workflows/lint-and-test.yml @@ -26,6 +26,9 @@ jobs: volumes: - solr:/tmp/solr needs: lint + strategy: + matrix: + hyrax_valkyrie_enabled: [false] services: database: image: postgres:13-alpine @@ -62,6 +65,7 @@ jobs: CAS_BASE_URL: '' CI: 1 FEDORA_TEST_URL: http://fedora:8080/rest + HYRAX_VALKYRIE: ${{ matrix.hyrax_valkyrie_enabled }} IIIF_BASE_URL: http://localhost/iiif/2 NOKOGIRI_USE_SYSTEM_LIBRARIES: true PSQL_PASSWORD: spot_test_pw From 6d6484726de7d20d938a9d0bbd1b06c95bac2e56 Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Fri, 17 Oct 2025 11:29:12 -0400 Subject: [PATCH 003/116] ruby to 3.2.9 --- .ruby-version | 2 +- Dockerfile | 21 +++++++++------------ docker-compose.yml | 2 +- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/.ruby-version b/.ruby-version index 6a81b4c83..e650c01d9 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.8 +3.2.9 diff --git a/Dockerfile b/Dockerfile index 1d78fa408..3640d5117 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,14 +3,12 @@ # !! This is a builder image. Not for general use !! # Use this as the base image for the Rails / Sidekiq services. ## -FROM ruby:2.7.8-slim-bullseye AS spot-base +FROM ruby:3.2.9-slim-bookworm AS spot-base RUN apt-get clean && \ - apt-get update && \ - apt-get install -y --no-install-recommends ca-certificates curl gnupg && \ - mkdir -p /etc/apt/keyrings && \ - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ - echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ + apt-get update -y && \ + apt-get install -y curl && \ + curl -fsSL https://deb.nodesource.com/setup_22.x | bash && \ apt-get update -y && apt-get install -y --no-install-recommends \ awscli \ build-essential \ @@ -24,7 +22,7 @@ RUN apt-get clean && \ netcat-openbsd \ nodejs \ openssl \ - postgresql-13 \ + postgresql \ ruby-dev \ tzdata \ zip @@ -38,10 +36,9 @@ ENV HYRAX_CACHE_PATH=/spot/tmp/cache \ RUN corepack enable -COPY Gemfile.lock /spot/ +COPY Gemfile Gemfile.lock /spot/ RUN gem install bundler:$(tail -n 1 Gemfile.lock | sed -e 's/\s*//') -COPY Gemfile /spot/ RUN bundle config unset with && \ bundle config unset without && \ bundle config set without "development:test" && \ @@ -137,14 +134,14 @@ ENV MALLOC_ARENA_MAX=2 # We don't need the entrypoint script to generate an SSL cert ENV SKIP_SSL_CERT=true -RUN apt-get update && apt-get install -y --no-install-recommends \ - bash \ +RUN apt-get update -y && \ + apt-get install -y \ ffmpeg \ ghostscript \ imagemagick \ libreoffice \ mediainfo \ - openjdk-11-jre \ + openjdk-17-jre \ perl \ python3 \ unzip diff --git a/docker-compose.yml b/docker-compose.yml index 20f86f87e..70b113b0a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -39,7 +39,7 @@ services: restart: unless-stopped db: - image: postgres:13-alpine + image: postgres:16-alpine volumes: - db:/var/lib/postgresql/data - ./bin/create-dev-dbs.sql:/docker-entrypoint-initdb.d/10-create-dev-dbs.sql From b1a23e58c03d4611af3d220d139987507673b053 Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Fri, 17 Oct 2025 11:29:39 -0400 Subject: [PATCH 004/116] fix js/css assets --- Gemfile | 10 ++++++++-- Gemfile.lock | 17 ++++++++++++----- app/assets/config/manifest.js | 2 ++ app/assets/javascripts/application.js | 7 +++++-- app/assets/javascripts/blacklight_gallery.js | 1 - app/assets/javascripts/openseadragon.js | 2 -- app/assets/stylesheets/application.scss | 4 ++-- app/assets/stylesheets/hyrax.scss | 4 ++-- config/application.rb | 9 ++++----- config/blacklight.yml | 4 ++-- config/initializers/hyrax.rb | 2 +- config/initializers/inflections.rb | 2 +- 12 files changed, 39 insertions(+), 25 deletions(-) delete mode 100644 app/assets/javascripts/blacklight_gallery.js delete mode 100644 app/assets/javascripts/openseadragon.js diff --git a/Gemfile b/Gemfile index 88058c632..c86cb7c53 100644 --- a/Gemfile +++ b/Gemfile @@ -46,7 +46,7 @@ gem 'bagit', '~> 0.6.0' # blacklight plugins for enhanced searching gem 'blacklight_advanced_search', '~> 7.0.0' gem 'blacklight_oai_provider', '~> 7.0.2' -gem 'blacklight_range_limit', '~> 9.1.0' +gem 'blacklight_range_limit', '~> 8.5.0' # start up the server faster gem 'bootsnap', '~> 1.17', require: false @@ -75,7 +75,7 @@ gem 'edtf-humanize', '~> 2.1.0' # a bunch of samvera gems rely on Faraday already, but we'll # require it as we're explicitly using it. -gem 'faraday' #, '~> 0.17.6' +gem 'faraday', '~> 1.10.4' # video file resource for getting information on video derivatives gem 'ffprober' @@ -133,6 +133,9 @@ gem 'sidekiq-cron', '~> 1.9.1' # using Slack for some of our messaging gem 'slack-ruby-client', '~> 0.14.6' +# used in the Hyrax 4 upgrade but not a dependency?? +gem 'twitter-typeahead-rails', '~> 0.11.1' + # now that we're writing es6 javascript of our own (+ not just using the hyrax js) # we need to compile it in sprockets. # @@ -150,6 +153,9 @@ gem 'redlock', '>= 0.1.2', '< 2.0' # that throws off how forms are built in Hyrax. gem 'simple_form', '< 5.2' +# sprockets support gets cagey after 1.0 +gem 'openseadragon', '~> 0.9.0' + # development dependencies (not as necessary to lock down versions here) group :development do # Seed data diff --git a/Gemfile.lock b/Gemfile.lock index eff5d7b7c..2c6038c44 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -165,9 +165,10 @@ GEM blacklight (~> 7.0) oai (~> 1.2) rexml - blacklight_range_limit (9.1.0) + blacklight_range_limit (8.5.0) blacklight (>= 7.25.2, < 9) - view_component (>= 2.54, < 5) + deprecation + view_component (>= 2.54, < 4) bootsnap (1.18.6) msgpack (~> 1.2) bootstrap (4.6.2.1) @@ -748,7 +749,7 @@ GEM multi_xml (~> 0.5) rack (>= 1.2, < 4) okcomputer (1.18.6) - openseadragon (1.0.17) + openseadragon (0.9.0) rails (> 6.1.0) orm_adapter (0.5.0) os (1.1.4) @@ -1091,6 +1092,10 @@ GEM turbolinks (5.2.1) turbolinks-source (~> 5.2) turbolinks-source (5.2.0) + twitter-typeahead-rails (0.11.1) + actionpack (>= 3.1) + jquery-rails + railties (>= 3.1) typhoeus (1.4.1) ethon (>= 0.9.0) tzinfo (2.0.6) @@ -1149,7 +1154,7 @@ DEPENDENCIES bixby (~> 5.0.1) blacklight_advanced_search (~> 7.0.0) blacklight_oai_provider (~> 7.0.2) - blacklight_range_limit (~> 9.1.0) + blacklight_range_limit (~> 8.5.0) bootsnap (~> 1.17) bootstrap (~> 4.0) browse-everything (~> 1.1.2) @@ -1167,7 +1172,7 @@ DEPENDENCIES edtf-humanize (~> 2.1.0) equivalent-xml (~> 0.6.0) factory_bot_rails (~> 6) - faraday + faraday (~> 1.10.4) ffprober honeybadger (~> 4.12.1) hydra-role-management (~> 1.1.0) @@ -1183,6 +1188,7 @@ DEPENDENCIES mini_magick (~> 4.11) non-digest-assets (~> 2.2.0) okcomputer (~> 1.18.5) + openseadragon (~> 0.9.0) pg (~> 1.5.4) puma (~> 6.4.0) rails (~> 6.1) @@ -1209,6 +1215,7 @@ DEPENDENCIES sprockets-es6 (~> 0.9.2) stub_env (~> 1.0.4) turbolinks (~> 5.2.1) + twitter-typeahead-rails (~> 0.11.1) uglifier (~> 4.2.0) webmock (~> 3.8) diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index b16e53d6d..75466dd08 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -1,3 +1,5 @@ //= link_tree ../images //= link_directory ../javascripts .js //= link_directory ../stylesheets .css + +//= link blacklight_gallery/manifest.js \ No newline at end of file diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 31278a8d0..31b93dc5d 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -27,9 +27,12 @@ //= require blacklight/blacklight // local require that in turn calls a require -//= require blacklight_gallery +//= require blacklight_gallery/blacklight-gallery + +// require openseadragon +//= require openseadragon/openseadragon +//= require openseadragon/rails -//= require openseadragon //= require hyrax //= require almond diff --git a/app/assets/javascripts/blacklight_gallery.js b/app/assets/javascripts/blacklight_gallery.js deleted file mode 100644 index 992a7d4df..000000000 --- a/app/assets/javascripts/blacklight_gallery.js +++ /dev/null @@ -1 +0,0 @@ -//= require blacklight_gallery/default \ No newline at end of file diff --git a/app/assets/javascripts/openseadragon.js b/app/assets/javascripts/openseadragon.js deleted file mode 100644 index 2ab91a9f1..000000000 --- a/app/assets/javascripts/openseadragon.js +++ /dev/null @@ -1,2 +0,0 @@ -//= require openseadragon/openseadragon -//= require openseadragon/rails \ No newline at end of file diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 5e6f25936..771a689b2 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -15,8 +15,8 @@ // //= require hyrax //= require openseadragon -//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap -//= require 'bulkrax/application' +//= require dataTables.bootstrap4 +//= require bulkrax/application // //= require spot //= require_self diff --git a/app/assets/stylesheets/hyrax.scss b/app/assets/stylesheets/hyrax.scss index 6d3b65ea2..bfec41942 100644 --- a/app/assets/stylesheets/hyrax.scss +++ b/app/assets/stylesheets/hyrax.scss @@ -1,7 +1,7 @@ // our own overrides are called _before_ we import hyrax/bootstrap @import "spot/variables"; -@import "bootstrap-compass"; +// @import "bootstrap-compass"; @import "bootstrap-default-overrides"; @import 'bootstrap'; @import 'blacklight/blacklight'; @@ -9,5 +9,5 @@ @import "blacklight_gallery/masonry"; @import "blacklight_gallery/slideshow"; @import "blacklight_gallery/osd_viewer"; -@import "blacklight_gallery/gallery"; +@import "hyrax/blacklight_gallery"; @import 'hyrax/hyrax'; diff --git a/config/application.rb b/config/application.rb index fa1a7efc0..5e2a4058e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -15,13 +15,12 @@ ActiveSupport::Deprecation.silenced = true Deprecation.default_deprecation_behavior = :silence - Bundler.require(*Rails.groups) -else - # Require the gems listed in Gemfile, including any gems - # you've limited to :test, :development, or :production. - Bundler.require(*Rails.groups) end +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(*Rails.groups) + module Spot class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. diff --git a/config/blacklight.yml b/config/blacklight.yml index c8fb47fd2..56ec67947 100644 --- a/config/blacklight.yml +++ b/config/blacklight.yml @@ -2,7 +2,7 @@ development: adapter: solr url: <%= ENV['SOLR_URL'] || "http://127.0.0.1:#{ENV.fetch('SOLR_DEVELOPMENT_PORT', 8983)}/solr/hydra-development" %> ssl: - :verify: false + verify: false test: adapter: solr url: <%= ENV['SOLR_TEST_URL'] || "http://127.0.0.1:#{ENV.fetch('SOLR_TEST_PORT', 8985)}/solr/hydra-test" %> @@ -10,4 +10,4 @@ production: adapter: solr url: <%= ENV['SOLR_URL'] %> ssl: - :verify: false + verify: false diff --git a/config/initializers/hyrax.rb b/config/initializers/hyrax.rb index 97f85694a..cd2a70d8a 100644 --- a/config/initializers/hyrax.rb +++ b/config/initializers/hyrax.rb @@ -317,4 +317,4 @@ def self.index_field_mapper end end end -end \ No newline at end of file +end diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index caf3256f5..02780b836 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -3,4 +3,4 @@ # ActiveSupport::Inflector.inflections(:en) do |inflect| # inflect.acronym 'CSV' # inflect.acronym 'ISO' -# end \ No newline at end of file +# end From 2616be7e35bc508b575a0b19d8022a0d37582a8b Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Fri, 17 Oct 2025 11:35:51 -0400 Subject: [PATCH 005/116] add valkyrie status to gh test name --- .github/workflows/lint-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml index 0c6baff24..934df1aa3 100644 --- a/.github/workflows/lint-and-test.yml +++ b/.github/workflows/lint-and-test.yml @@ -19,7 +19,7 @@ jobs: run: bundle exec rubocop --format github test: - name: Test + name: "Test (Valkyrie enabled: ${{ github.matrix.hyrax_valkyrie_enabled }})" runs-on: ubuntu-latest container: image: ruby:2.7.8-slim-bullseye From a8a04cfa5a1815a3bf60caa18bdc37406c8f5487 Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Tue, 21 Oct 2025 14:53:12 -0400 Subject: [PATCH 006/116] hyrax to 5.2.0 --- .github/workflows/lint-and-test.yml | 2 +- Dockerfile | 4 +- Gemfile | 10 +- Gemfile.lock | 317 +++++++++++++------------- config/initializers/spot_overrides.rb | 17 ++ config/storage.yml | 26 +++ 6 files changed, 211 insertions(+), 165 deletions(-) create mode 100644 config/storage.yml diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml index 934df1aa3..06159ea1b 100644 --- a/.github/workflows/lint-and-test.yml +++ b/.github/workflows/lint-and-test.yml @@ -22,7 +22,7 @@ jobs: name: "Test (Valkyrie enabled: ${{ github.matrix.hyrax_valkyrie_enabled }})" runs-on: ubuntu-latest container: - image: ruby:2.7.8-slim-bullseye + image: ruby:3.2.9-slim-bookworm volumes: - solr:/tmp/solr needs: lint diff --git a/Dockerfile b/Dockerfile index 3640d5117..1e77d05d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,8 +8,8 @@ FROM ruby:3.2.9-slim-bookworm AS spot-base RUN apt-get clean && \ apt-get update -y && \ apt-get install -y curl && \ - curl -fsSL https://deb.nodesource.com/setup_22.x | bash && \ - apt-get update -y && apt-get install -y --no-install-recommends \ + curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \ + apt-get update -y --fix-missing && apt-get install -y --no-install-recommends \ awscli \ build-essential \ coreutils \ diff --git a/Gemfile b/Gemfile index c86cb7c53..5092a44ba 100644 --- a/Gemfile +++ b/Gemfile @@ -29,7 +29,7 @@ gem 'jbuilder', '~> 2.11.5' # # the hyrax/spot stack # -gem 'hyrax', '~> 4.0.0' +gem 'hyrax', '~> 5.2.0' # modularize our javascripts gem 'almond-rails', '~> 0.3.0' @@ -54,7 +54,7 @@ gem 'bootsnap', '~> 1.17', require: false gem 'bootstrap', '~> 4.0' # Bulkrax for batch ingesting objects -gem 'browse-everything', '~> 1.1.2' +gem 'browse-everything', '~> 1.6.0' gem 'bulkrax', '~> 9.0.2' # This needs to be here if we want to compile our own JS @@ -75,7 +75,7 @@ gem 'edtf-humanize', '~> 2.1.0' # a bunch of samvera gems rely on Faraday already, but we'll # require it as we're explicitly using it. -gem 'faraday', '~> 1.10.4' +gem 'faraday', '~> 2.0' # video file resource for getting information on video derivatives gem 'ffprober' @@ -131,7 +131,7 @@ gem 'sidekiq', '~> 5.2.9' gem 'sidekiq-cron', '~> 1.9.1' # using Slack for some of our messaging -gem 'slack-ruby-client', '~> 0.14.6' +gem 'slack-ruby-client' # used in the Hyrax 4 upgrade but not a dependency?? gem 'twitter-typeahead-rails', '~> 0.11.1' @@ -161,7 +161,7 @@ group :development do # Seed data # gem 'ldr-development-seeds', github: 'LafayetteCollegeLibraries/ldr-development-seeds', branch: 'main' - gem 'listen', '>= 3.0.5', '< 3.8' + gem 'listen', '~> 3.9.0' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring', '~> 2.1.1' gem 'spring-watcher-listen', '~> 2.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index 2c6038c44..c20cefcb3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,14 +39,15 @@ GEM erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - active-fedora (14.0.1) + active-fedora (15.0.1) active-triples (>= 0.11.0, < 2.0.0) - activemodel (>= 5.1) - activesupport (>= 5.1) + activemodel (>= 6.1) + activesupport (>= 6.1) deprecation faraday (>= 1.0) faraday-encoding (>= 0.0.5) ldp (>= 0.7.0, < 2) + mutex_m rsolr (>= 1.1.2, < 3) ruby-progressbar (~> 1.0) active-triples (1.2.0) @@ -54,9 +55,9 @@ GEM activesupport (>= 3.0.0) rdf (>= 2.0.2, < 4.0) rdf-vocab (>= 2.0, < 4.0) - active_encode (0.8.2) + active_encode (1.3.0) + addressable (~> 2.8) rails - sprockets (< 4) activejob (6.1.7.10) activesupport (= 6.1.7.10) globalid (>= 0.3.6) @@ -155,7 +156,7 @@ GEM blacklight (> 6.0, < 9) cancancan (>= 1.8) deprecation (~> 1.0) - blacklight-gallery (4.9.0) + blacklight-gallery (4.6.4) blacklight (>= 7.17, < 9) rails (>= 6.1, < 9) blacklight_advanced_search (7.0.0) @@ -178,17 +179,16 @@ GEM actionpack (>= 5.2) activemodel (>= 5.2) breadcrumbs_on_rails (3.0.1) - browse-everything (1.1.2) + browse-everything (1.6.0) addressable (~> 2.5) aws-sdk-s3 - dropbox_api (>= 0.1.10) - google-api-client (~> 0.23) - google_drive (>= 2.1, < 4) - googleauth (>= 0.6.6, < 1.0) - rails (>= 4.2, < 7.0) + dropbox_api (>= 0.1.20) + faraday (~> 2.0) + google-apis-drive_v3 + googleauth (>= 0.6.6, < 2.0) + rails (>= 4.2, < 8.1) ruby-box signet (~> 0.8) - typhoeus builder (3.3.0) bulkrax (9.0.2) bagit (~> 0.6.0) @@ -236,7 +236,7 @@ GEM coffee-script-source execjs coffee-script-source (1.12.2) - concurrent-ruby (1.3.5) + concurrent-ruby (1.3.4) connection_pool (2.5.4) crack (1.0.0) bigdecimal @@ -286,55 +286,54 @@ GEM dropbox_api (0.1.21) faraday (< 3.0) oauth2 (~> 1.1) - dry-configurable (0.16.1) - dry-core (~> 0.6) + dry-configurable (1.3.0) + dry-core (~> 1.1) zeitwerk (~> 2.6) dry-container (0.11.0) concurrent-ruby (~> 1.0) - dry-core (0.9.1) + dry-core (1.1.0) concurrent-ruby (~> 1.0) + logger zeitwerk (~> 2.6) - dry-equalizer (0.3.0) - dry-events (0.2.0) + dry-events (1.1.0) concurrent-ruby (~> 1.0) - dry-core (~> 0.4) - dry-equalizer (~> 0.2) - dry-inflector (0.3.0) - dry-initializer (3.1.1) - dry-logic (1.3.0) + dry-core (~> 1.1) + dry-inflector (1.2.0) + dry-initializer (3.2.0) + dry-logic (1.6.0) + bigdecimal concurrent-ruby (~> 1.0) - dry-core (~> 0.9, >= 0.9) + dry-core (~> 1.1) zeitwerk (~> 2.6) - dry-monads (1.5.0) + dry-monads (1.9.0) concurrent-ruby (~> 1.0) - dry-core (~> 0.9, >= 0.9) + dry-core (~> 1.1) zeitwerk (~> 2.6) - dry-schema (1.11.3) + dry-schema (1.14.1) concurrent-ruby (~> 1.0) - dry-configurable (~> 0.16, >= 0.16) - dry-core (~> 0.9, >= 0.9) - dry-initializer (~> 3.0) - dry-logic (~> 1.3) - dry-types (~> 1.6) + dry-configurable (~> 1.0, >= 1.0.1) + dry-core (~> 1.1) + dry-initializer (~> 3.2) + dry-logic (~> 1.5) + dry-types (~> 1.8) zeitwerk (~> 2.6) - dry-struct (1.5.2) - dry-core (~> 0.9, >= 0.9) - dry-types (~> 1.6) + dry-struct (1.8.0) + dry-core (~> 1.1) + dry-types (~> 1.8, >= 1.8.2) ice_nine (~> 0.11) zeitwerk (~> 2.6) - dry-types (1.6.1) + dry-types (1.8.3) + bigdecimal (~> 3.0) concurrent-ruby (~> 1.0) - dry-container (~> 0.3) - dry-core (~> 0.9, >= 0.9) - dry-inflector (~> 0.1, >= 0.1.2) - dry-logic (~> 1.3, >= 1.3) + dry-core (~> 1.0) + dry-inflector (~> 1.0) + dry-logic (~> 1.4) zeitwerk (~> 2.6) - dry-validation (1.9.0) + dry-validation (1.11.1) concurrent-ruby (~> 1.0) - dry-container (~> 0.7, >= 0.7.1) - dry-core (~> 0.9, >= 0.9) - dry-initializer (~> 3.0) - dry-schema (~> 1.11, >= 1.11.0) + dry-core (~> 1.1) + dry-initializer (~> 3.2) + dry-schema (~> 1.14) zeitwerk (~> 2.6) ebnf (2.3.5) htmlentities (~> 4.3) @@ -353,43 +352,29 @@ GEM erubi (1.13.1) et-orbi (1.4.0) tzinfo - ethon (0.17.0) - ffi (>= 1.15.0) execjs (2.10.0) factory_bot (6.4.5) activesupport (>= 5.0.0) factory_bot_rails (6.4.3) factory_bot (~> 6.4) railties (>= 5.0.0) - faraday (1.10.4) - faraday-em_http (~> 1.0) - faraday-em_synchrony (~> 1.0) - faraday-excon (~> 1.1) - faraday-httpclient (~> 1.0) - faraday-multipart (~> 1.0) - faraday-net_http (~> 1.0) - faraday-net_http_persistent (~> 1.0) - faraday-patron (~> 1.0) - faraday-rack (~> 1.0) - faraday-retry (~> 1.0) - ruby2_keywords (>= 0.0.4) - faraday-em_http (1.0.0) - faraday-em_synchrony (1.0.1) + faraday (2.14.0) + faraday-net_http (>= 2.0, < 3.5) + json + logger faraday-encoding (0.0.6) faraday - faraday-excon (1.1.0) faraday-follow_redirects (0.4.0) faraday (>= 1, < 3) - faraday-httpclient (1.0.1) + faraday-mashify (1.0.0) + faraday (~> 2.0) + hashie faraday-multipart (1.1.1) multipart-post (~> 2.0) - faraday-net_http (1.0.2) - faraday-net_http_persistent (1.2.0) - faraday-patron (1.0.0) - faraday-rack (1.0.0) - faraday-retry (1.0.3) - faraday_middleware (1.2.1) - faraday (~> 1.0) + faraday-net_http (3.4.1) + net-http (>= 0.5.0) + faraday-retry (2.3.2) + faraday (~> 2.0) ffi (1.17.2) ffprober (1.0) sorbet-runtime @@ -403,7 +388,16 @@ GEM fugit (1.12.1) et-orbi (~> 1.4) raabro (~> 1.4) - gems (1.2.0) + gapic-common (1.2.0) + faraday (>= 1.9, < 3.a) + faraday-retry (>= 1.0, < 3.a) + google-cloud-env (~> 2.2) + google-logging-utils (~> 0.1) + google-protobuf (~> 4.26) + googleapis-common-protos (~> 1.6) + googleapis-common-protos-types (~> 1.15) + googleauth (~> 1.12) + grpc (~> 1.66) geocoder (1.8.6) base64 (>= 0.1.0) csv (>= 3.0.0) @@ -411,41 +405,50 @@ GEM ostruct globalid (1.3.0) activesupport (>= 6.1) - google-api-client (0.53.0) - google-apis-core (~> 0.1) - google-apis-generator (~> 0.1) - google-apis-core (0.11.3) - addressable (~> 2.5, >= 2.5.1) - googleauth (>= 0.16.2, < 2.a) - httpclient (>= 2.8.1, < 3.a) - mini_mime (~> 1.0) + google-analytics-data (0.7.2) + google-analytics-data-v1beta (>= 0.11, < 2.a) + google-cloud-core (~> 1.6) + google-analytics-data-v1beta (0.19.0) + gapic-common (~> 1.2) + google-cloud-errors (~> 1.0) + google-apis-core (1.0.2) + addressable (~> 2.8, >= 2.8.7) + faraday (~> 2.13) + faraday-follow_redirects (~> 0.3) + googleauth (~> 1.14) + mini_mime (~> 1.1) representable (~> 3.0) - retriable (>= 2.0, < 4.a) - rexml - 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.11.0, < 2.a) - google-apis-discovery_v1 (~> 0.5) - thor (>= 0.20, < 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) - googleauth (>= 0.5.0, < 1.0.0) - nokogiri (>= 1.5.3, < 2.0.0) - googleauth (0.17.1) - faraday (>= 0.17.3, < 2.0) - jwt (>= 1.4, < 3.0) - memoist (~> 0.16) + retriable (~> 3.1) + google-apis-drive_v3 (0.72.0) + google-apis-core (>= 0.15.0, < 2.a) + google-cloud-core (1.8.0) + google-cloud-env (>= 1.0, < 3.a) + google-cloud-errors (~> 1.0) + google-cloud-env (2.3.1) + base64 (~> 0.2) + faraday (>= 1.0, < 3.a) + google-cloud-errors (1.5.0) + google-logging-utils (0.2.0) + google-protobuf (4.33.0) + bigdecimal + rake (>= 13) + googleapis-common-protos (1.9.0) + google-protobuf (~> 4.26) + googleapis-common-protos-types (~> 1.21) + grpc (~> 1.41) + googleapis-common-protos-types (1.22.0) + google-protobuf (~> 4.26) + googleauth (1.15.1) + faraday (>= 1.0, < 3.a) + google-cloud-env (~> 2.2) + google-logging-utils (~> 0.1) + jwt (>= 1.4, < 4.0) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.15) + signet (>= 0.16, < 2.a) + grpc (1.75.0) + google-protobuf (>= 3.25, < 5.0) + googleapis-common-protos-types (~> 1.0) haml (6.3.0) temple (>= 0.8.2) thor @@ -456,31 +459,30 @@ GEM honeybadger (4.12.2) htmlentities (4.3.4) http_logger (1.0.1) - httpclient (2.9.0) - mutex_m - hydra-access-controls (12.1.0) + hydra-access-controls (13.1.0) active-fedora (>= 10.0.0) - activesupport (>= 5.2, < 7.1) + activesupport (>= 6.1, < 8.1) blacklight-access_controls (~> 6.0) cancancan (>= 1.8, < 4) deprecation (~> 1.0) - hydra-core (12.1.0) - hydra-access-controls (= 12.1.0) - railties (>= 5.2, < 7.1) - hydra-derivatives (3.8.0) + hydra-core (13.1.0) + hydra-access-controls (= 13.1.0) + railties (>= 6.1, < 8.1) + hydra-derivatives (4.0.0) active-fedora (>= 14.0) active-triples (>= 1.2) - active_encode (~> 0.1) - activesupport (>= 4.0, < 7.1) + active_encode (~> 1.2) + activesupport (>= 4.0, < 8.0) addressable (~> 2.5) deprecation mime-types (> 2.0, < 4.0) mini_magick (>= 3.2, < 5) - hydra-editor (6.3.0) + hydra-editor (7.0.0) active-fedora (>= 9.0.0) activerecord (>= 5.2, < 8.0) almond-rails (~> 0.1) cancancan + concurrent-ruby (= 1.3.4) psych (~> 3.3, < 4) rails (>= 5.2, < 8.0) simple_form (>= 4.1.0, < 5.2) @@ -488,10 +490,10 @@ GEM sprockets-es6 hydra-file_characterization (1.2.0) activesupport (>= 3.0.0) - hydra-head (12.1.0) - hydra-access-controls (= 12.1.0) - hydra-core (= 12.1.0) - rails (>= 5.2, < 7.1) + hydra-head (13.1.0) + hydra-access-controls (= 13.1.0) + hydra-core (= 13.1.0) + rails (>= 6.1, < 8.1) hydra-pcdm (1.4.0) active-fedora (>= 10) mime-types (>= 1) @@ -508,44 +510,46 @@ GEM hydra-derivatives (>= 3.6) hydra-file_characterization (~> 1.0) hydra-pcdm (>= 0.9) - hyrax (4.0.0) - active-fedora (~> 14.0) + hyrax (5.2.0) + active-fedora (~> 15.0) almond-rails (~> 0.1) awesome_nested_set (~> 3.1) blacklight (~> 7.29) - blacklight-gallery (~> 4.0) + blacklight-gallery (~> 4.6.4) breadcrumbs_on_rails (~> 3.0) browse-everything (>= 0.16, < 2.0) carrierwave (~> 1.0) clipboard-rails (~> 1.5) + concurrent-ruby (= 1.3.4) connection_pool (~> 2.4) draper (~> 4.0) - dry-equalizer (~> 0.2) - dry-events (~> 0.2.0) - dry-monads (~> 1.5) - dry-struct (~> 1.0) - dry-validation (~> 1.3) + dry-container (~> 0.11) + dry-events (~> 1.0, >= 1.0.1) + dry-logic (~> 1.5) + dry-monads (~> 1.6) + dry-validation (~> 1.10) flipflop (~> 2.3) flot-rails (~> 0.0.6) font-awesome-rails (~> 4.2) - hydra-derivatives (~> 3.3) - hydra-editor (~> 6.0) + google-analytics-data (~> 0.6) + hydra-derivatives (~> 4.0) + hydra-editor (~> 7.0) hydra-file_characterization (~> 1.1) - hydra-head (~> 12.0) + hydra-head (~> 13.0) hydra-works (>= 0.16) iiif_manifest (>= 0.3, < 2.0) json-schema legato (~> 0.3) linkeddata + listen (~> 3.9) mailboxer (~> 0.12) nest (~> 3.1) noid-rails (~> 3.0) oauth oauth2 (~> 1.2) - openseadragon - posix-spawn + openseadragon (~> 0.9) qa (~> 5.5, >= 5.5.1) - rails (~> 6.0) + rails (> 6.1, < 8.0) rails_autolink (~> 1.1) rdf-rdfxml rdf-vocab (~> 3.0) @@ -558,9 +562,9 @@ GEM sass-rails (~> 6.0) select2-rails (~> 3.5) signet - sprockets (~> 3.7) + sprockets (= 3.7.2) tinymce-rails (~> 5.10) - valkyrie (~> 3.0.1) + valkyrie (~> 3.5) view_component (~> 2.74.1) hyrax-spec (0.3.2) rspec (~> 3.6) @@ -592,7 +596,7 @@ GEM json-ld-preloaded (3.2.2) json-ld (~> 3.2) rdf (~> 3.2) - json-schema (5.2.2) + json-schema (6.0.0) addressable (~> 2.8) bigdecimal (~> 3.1) jwt (2.10.2) @@ -668,7 +672,7 @@ GEM shex (~> 0.6, >= 0.6.4) sparql (~> 3.1, >= 3.1.8) sparql-client (~> 3.1, >= 3.1.2) - listen (3.7.1) + listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) logger (1.7.0) @@ -690,7 +694,6 @@ GEM rails (>= 5.0.0) marcel (1.1.0) matrix (0.4.3) - memoist (0.16.2) method_source (1.1.0) mime-types (3.7.0) logger @@ -702,13 +705,16 @@ GEM minitest (5.26.0) msgpack (1.8.0) multi_json (1.15.0) - multi_xml (0.6.0) + multi_xml (0.7.2) + bigdecimal (~> 3.1) multipart-post (2.4.1) mutex_m (0.3.0) namae (1.2.0) racc (~> 1.7) nest (3.2.0) redic + net-http (0.6.0) + uri net-http-persistent (4.0.6) connection_pool (~> 2.2, >= 2.2.4) net-imap (0.4.22) @@ -761,7 +767,6 @@ GEM parslet (2.0.0) pg (1.5.9) popper_js (1.16.1) - posix-spawn (0.3.15) prism (1.5.2) psych (3.3.4) public_suffix (5.1.1) @@ -1017,10 +1022,10 @@ GEM sidekiq-cron (1.9.1) fugit (~> 1.8) sidekiq (>= 4.2.1) - signet (0.19.0) + signet (0.21.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) - jwt (>= 1.5, < 3.0) + jwt (>= 1.5, < 4.0) multi_json (~> 1.10) simple_form (5.1.0) actionpack (>= 5.2) @@ -1034,13 +1039,13 @@ GEM simplecov (~> 0.19) simplecov-html (0.13.2) simplecov_json_formatter (0.1.4) - slack-ruby-client (0.14.6) - activesupport - faraday (>= 0.9) - faraday_middleware + slack-ruby-client (3.0.0) + faraday (>= 2.0.1) + faraday-mashify + faraday-multipart gli hashie - websocket-driver + logger slop (4.10.1) snaky_hash (2.0.3) hashie (>= 0.1.0, < 6) @@ -1062,8 +1067,7 @@ GEM spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) - sprockets (3.7.5) - base64 + sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-es6 (0.9.2) @@ -1096,8 +1100,6 @@ GEM actionpack (>= 3.1) jquery-rails railties (>= 3.1) - typhoeus (1.4.1) - ethon (>= 0.9.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) @@ -1105,8 +1107,9 @@ GEM execjs (>= 0.3.0, < 3) unicode-display_width (2.6.0) unicode-types (1.11.0) + uri (1.0.4) validatable (1.6.7) - valkyrie (3.0.3) + valkyrie (3.5.0) activemodel activesupport dry-struct @@ -1157,7 +1160,7 @@ DEPENDENCIES blacklight_range_limit (~> 8.5.0) bootsnap (~> 1.17) bootstrap (~> 4.0) - browse-everything (~> 1.1.2) + browse-everything (~> 1.6.0) bulkrax (~> 9.0.2) byebug (~> 11.1.3) capybara (~> 3.38) @@ -1172,18 +1175,18 @@ DEPENDENCIES edtf-humanize (~> 2.1.0) equivalent-xml (~> 0.6.0) factory_bot_rails (~> 6) - faraday (~> 1.10.4) + faraday (~> 2.0) ffprober honeybadger (~> 4.12.1) hydra-role-management (~> 1.1.0) - hyrax (~> 4.0.0) + hyrax (~> 5.2.0) hyrax-spec (~> 0.3.2) iso-639 (~> 0.3.6) jbuilder (~> 2.11.5) jquery-rails (~> 4.6.0) kaminari (~> 1.2.2) linkeddata (~> 3.1.6) - listen (>= 3.0.5, < 3.8) + listen (~> 3.9.0) lograge mini_magick (~> 4.11) non-digest-assets (~> 2.2.0) @@ -1209,7 +1212,7 @@ DEPENDENCIES simple_form (< 5.2) simplecov (~> 0.22.0) simplecov-cobertura (~> 2.1) - slack-ruby-client (~> 0.14.6) + slack-ruby-client spring (~> 2.1.1) spring-watcher-listen (~> 2.0.0) sprockets-es6 (~> 0.9.2) diff --git a/config/initializers/spot_overrides.rb b/config/initializers/spot_overrides.rb index 0a4af80c9..9de7857b8 100644 --- a/config/initializers/spot_overrides.rb +++ b/config/initializers/spot_overrides.rb @@ -304,4 +304,21 @@ def authorize_download! send_file unauthorized_image end end + + # Encountering an issue where Hyrax::PersistDirectlyContainedOutputFileService.retrieve_file_set requires + # Hyrax::UploadedFile#file_set_uri to be an URI but querying for that URI throws an error (ActiveFedora + # is appending the base root to the full uri, resulting in errors like: + # Ldp::BadRequest: Path contains empty element! /dev/ht/tp/:/http://fedora:8080/rest/dev/2v/23/vt/36/2v23vt362") + Hyrax::UploadedFile.class_eval do + def add_file_set!(file_set) + uri = case file_set + when ActiveFedora::Base + file_set.uri + when Hyrax::Resource + file_set.id.is_a?(URI::HTTP) ? file_set.id : Hyrax::Base.id_to_uri(file_set.id.to_s) + end + + update!(file_set_uri: uri) if uri.present? + end + end end diff --git a/config/storage.yml b/config/storage.yml new file mode 100644 index 000000000..74490077e --- /dev/null +++ b/config/storage.yml @@ -0,0 +1,26 @@ +# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket + +# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] From 12ae27ec1ff5da5f4dd32a5ed6428e1e894d0c2e Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Tue, 21 Oct 2025 16:38:41 -0400 Subject: [PATCH 007/116] bundle update --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c20cefcb3..e48629174 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -717,7 +717,7 @@ GEM uri net-http-persistent (4.0.6) connection_pool (~> 2.2, >= 2.2.4) - net-imap (0.4.22) + net-imap (0.5.12) date net-protocol net-pop (0.1.2) @@ -731,7 +731,7 @@ GEM noid-rails (3.3.0) actionpack (>= 5.0.0, < 9) noid (~> 0.9) - nokogiri (1.15.7) + nokogiri (1.18.10) mini_portile2 (~> 2.8.2) racc (~> 1.4) non-digest-assets (2.2.0) @@ -1144,7 +1144,7 @@ GEM websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.18) + zeitwerk (2.7.3) PLATFORMS ruby From f8f96c3ba3ce33c67884922bfcb16ce1a04f5a4a Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Tue, 21 Oct 2025 16:38:51 -0400 Subject: [PATCH 008/116] controller specs pass --- app/assets/javascripts/application.js | 4 ++-- app/controllers/handle_controller.rb | 12 +++++------- app/controllers/spot/redirect_controller.rb | 5 ++--- app/views/hyrax/base/_items_table.html.erb | 2 +- config/initializers/hyrax.rb | 3 +++ spec/controllers/handle_controller_spec.rb | 6 ++++-- spec/controllers/spot/redirect_controller_spec.rb | 12 ++++++++---- 7 files changed, 25 insertions(+), 19 deletions(-) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 31b93dc5d..b6845e497 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -26,8 +26,8 @@ //= require dataTables.bootstrap4 //= require blacklight/blacklight -// local require that in turn calls a require -//= require blacklight_gallery/blacklight-gallery +//= require blacklight_gallery/default +//= require blacklight_gallery/osd_viewer // require openseadragon //= require openseadragon/openseadragon diff --git a/app/controllers/handle_controller.rb b/app/controllers/handle_controller.rb index e43aca06a..b42390ce0 100644 --- a/app/controllers/handle_controller.rb +++ b/app/controllers/handle_controller.rb @@ -9,12 +9,11 @@ class HandleController < ApplicationController # Displays a 404 (via raised +Blacklight::Exceptions::RecordNotFound+ # that is handled with +Hydra::Catalog+) if no item is found. def show - query = query_for_identifier(Spot::Identifier.new('hdl', params[:id])) - result, _documents = repository.search(query) - - raise Blacklight::Exceptions::RecordNotFound if result.response['numFound'].zero? - document = result.response['docs'].first + query_opts = query_for_identifier(Spot::Identifier.new('hdl', params[:id])) + service = Hyrax::SolrQueryService.new(query: [query_opts.delete(:q)]) + document = service.get(**query_opts)['response']['docs'].first + raise Blacklight::Exceptions::RecordNotFound if document.nil? redirect_to redirect_params_for(solr_document: document) end @@ -32,7 +31,6 @@ def identifier_solr_field # @param id [Spot::Identifier, #to_s] the identifier (with prefix) # @return [Hash String>] def query_for_identifier(id) - { q: "{!terms f=#{identifier_solr_field}}#{id}", - defType: 'lucene' } + { q: "{!terms f=identifier_ssim}#{id}", defType: 'lucene' } end end diff --git a/app/controllers/spot/redirect_controller.rb b/app/controllers/spot/redirect_controller.rb index 08c293ed9..4b373d4e3 100644 --- a/app/controllers/spot/redirect_controller.rb +++ b/app/controllers/spot/redirect_controller.rb @@ -28,9 +28,8 @@ def show private def document_params - # The only fields we'll need to generate a URL (or url_helper params) is are id and has_model_ssim. - result, _documents = repository.search(q: "{!terms f=identifier_ssim}url:#{http_uri}", fl: ['id', 'has_model_ssim'], defType: 'lucene') - document = result.response['docs']&.first + service = Hyrax::SolrQueryService.new(query: ["{!terms f=identifier_ssim}url:#{http_uri}"]) + document = service.query_result(fl: ['id', 'has_model_ssim'], defType: 'lucene')['response']['docs'].first raise Blacklight::Exceptions::RecordNotFound if document.nil? redirect_params_for(solr_document: document) diff --git a/app/views/hyrax/base/_items_table.html.erb b/app/views/hyrax/base/_items_table.html.erb index c88194ced..146613d43 100644 --- a/app/views/hyrax/base/_items_table.html.erb +++ b/app/views/hyrax/base/_items_table.html.erb @@ -1,4 +1,4 @@ -<% members = presenter.member_presenters_for(presenter.list_of_item_ids_to_display) %> +<% members = presenter.member_presenters %>
diff --git a/config/initializers/hyrax.rb b/config/initializers/hyrax.rb index cd2a70d8a..3ef23d532 100644 --- a/config/initializers/hyrax.rb +++ b/config/initializers/hyrax.rb @@ -5,6 +5,9 @@ # Can't define this within the Bulkrax initializer as it runs _before_ this Bulkrax.default_work_type = Hyrax.config.curation_concerns.first.name + config.collection_model = '::Collection' + config.admin_set_model = '::AdminSet' + # Register roles that are expected by your implementation. # @see Hyrax::RoleRegistry for additional details. # @note there are magical roles as defined in Hyrax::RoleRegistry::MAGIC_ROLES diff --git a/spec/controllers/handle_controller_spec.rb b/spec/controllers/handle_controller_spec.rb index 1baaa22cc..70eb6d746 100644 --- a/spec/controllers/handle_controller_spec.rb +++ b/spec/controllers/handle_controller_spec.rb @@ -2,7 +2,7 @@ RSpec.describe HandleController do describe '#show' do - subject { get :show, params: { id: handle } } + subject(:make_request) { get :show, params: { id: handle } } let(:solr_service) { ActiveFedora::SolrService } @@ -86,7 +86,9 @@ let(:solr_data) { { id: 'unrelated' } } - it { is_expected.to have_http_status :not_found } + it 'raises a Blacklight::Exceptions::RecordNotFound error' do + expect { make_request }.to raise_error Blacklight::Exceptions::RecordNotFound + end end end end diff --git a/spec/controllers/spot/redirect_controller_spec.rb b/spec/controllers/spot/redirect_controller_spec.rb index 277e040dc..dee27a9fa 100644 --- a/spec/controllers/spot/redirect_controller_spec.rb +++ b/spec/controllers/spot/redirect_controller_spec.rb @@ -3,9 +3,9 @@ routes { Rails.application.routes } describe '#show' do - subject { get :show, params: { url: url } } + subject(:make_request) { get :show, params: { url: url } } - let(:solr_service) { ActiveFedora::SolrService } + let(:solr_service) { Hyrax::SolrService } let(:solr_data) { { id: 'solr-object' } } before do @@ -54,7 +54,9 @@ } end - it { is_expected.to have_http_status :not_found } + it 'raises a Blacklight::Exceptions::RecordNotFound error' do + expect { make_request }.to raise_error Blacklight::Exceptions::RecordNotFound + end end context 'when the item is a collection' do @@ -73,7 +75,9 @@ context 'when the URL is an old Islandora search' do let(:url) { 'http://digital.lafayette.edu/collections/browsef[0]=cdm.Relation.IsPartOf%3A%22East%20Asia%20Image%20Collection%22&f[1]=cdm.Relation.IsPartOf%3A%22Japanese%20History%20Study%20Cards%22&f[2]=eastasia.Subject.OCM%3A%22870%20EDUCATION%22' } - it { is_expected.to have_http_status :not_found } + it 'raises a Blacklight::Exceptions::RecordNotFound error' do + expect { make_request }.to raise_error Blacklight::Exceptions::RecordNotFound + end end end end From bce2b0a8826b5d291c25ddf9ed3fdf7fe7064d76 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Wed, 22 Oct 2025 11:15:50 -0400 Subject: [PATCH 009/116] copy over some valkyrie work to use query service --- app/models/audio_visual_resource.rb | 7 + .../concerns/spot/base_resource_behavior.rb | 32 ++++ app/models/image_resource.rb | 5 + app/models/publication_resource.rb | 8 + app/models/student_work_resource.rb | 6 + app/services/spot/embargo_lease_service.rb | 73 +++++--- app/services/spot/simple_schema_loader.rb | 53 ++++++ config/metadata/audio_visual_metadata.yaml | 67 +++++++ config/metadata/base_metadata.yaml | 173 ++++++++++++++++++ config/metadata/core_metadata.yaml | 22 +++ config/metadata/image_metadata.yaml | 85 +++++++++ config/metadata/institutional_metadata.yaml | 28 +++ config/metadata/publication_metadata.yaml | 42 +++++ config/metadata/student_work_metadata.yaml | 43 +++++ 14 files changed, 623 insertions(+), 21 deletions(-) create mode 100644 app/models/audio_visual_resource.rb create mode 100644 app/models/concerns/spot/base_resource_behavior.rb create mode 100644 app/models/image_resource.rb create mode 100644 app/models/publication_resource.rb create mode 100644 app/models/student_work_resource.rb create mode 100644 app/services/spot/simple_schema_loader.rb create mode 100644 config/metadata/audio_visual_metadata.yaml create mode 100644 config/metadata/base_metadata.yaml create mode 100644 config/metadata/core_metadata.yaml create mode 100644 config/metadata/image_metadata.yaml create mode 100644 config/metadata/institutional_metadata.yaml create mode 100644 config/metadata/publication_metadata.yaml create mode 100644 config/metadata/student_work_metadata.yaml diff --git a/app/models/audio_visual_resource.rb b/app/models/audio_visual_resource.rb new file mode 100644 index 000000000..3cf3507f3 --- /dev/null +++ b/app/models/audio_visual_resource.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +class AudioVisualResource < ::Hyrax::Work + include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:audio_visual_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + + attribute :stored_derivatives, Valkyrie::Types::String +end diff --git a/app/models/concerns/spot/base_resource_behavior.rb b/app/models/concerns/spot/base_resource_behavior.rb new file mode 100644 index 000000000..ad7ef36fe --- /dev/null +++ b/app/models/concerns/spot/base_resource_behavior.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true +module Spot + module BaseResourceBehavior + # Wraps stored :identifier values (strings) in our {Spot::Identifier} interface + # + # @see app/models/spot/identifier.rb + # @return [Array] + def identifier + Array.wrap(self[:identifier]).map { |id| Spot::Identifier.from_string(id) } + end + + # Wraps stored :location values (themselves wrapped in RDF::URI via Hyrax/Valkyrie) in + # our {Spot::ControlledVocabularies::Location} interface. This conforms to the previous + # ActiveFedora interface and gives us an entrypoint to fetch labels during indexing. + # + # @see app/models/spot/controlled_vocabularies/location.rb + # @return [Array] + def location + Array.wrap(self[:location]).map { |uri| Spot::ControlledVocabularies::Location.new(uri) } + end + + # Wraps stored :subject values (themselves wrapped in RDF::URI via Valkyrie) in + # our {Spot::ControlledVocabularies::AssignFastSubject} interface. This conforms to the previous + # ActiveFedora interface and gives us an entrypoint to fetch labels during indexing. + # + # @see app/models/spot/controlled_vocabularies/assign_fast_subject.rb + # @return [Array] + def subject + Array.wrap(self[:subject]).map { |uri| Spot::ControlledVocabularies::AssignFastSubject.new(uri) } + end + end +end diff --git a/app/models/image_resource.rb b/app/models/image_resource.rb new file mode 100644 index 000000000..9de64fee0 --- /dev/null +++ b/app/models/image_resource.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true +class ImageResource < ::Hyrax::Work + include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:image_metadata, schema_loader: Spot::SimpleSchemaLoader.new) +end diff --git a/app/models/publication_resource.rb b/app/models/publication_resource.rb new file mode 100644 index 000000000..2111b0dd3 --- /dev/null +++ b/app/models/publication_resource.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +class PublicationResource < ::Hyrax::Work + include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:institutional_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:publication_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + + include Spot::BaseResourceBehavior +end diff --git a/app/models/student_work_resource.rb b/app/models/student_work_resource.rb new file mode 100644 index 000000000..316197ce9 --- /dev/null +++ b/app/models/student_work_resource.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +class StudentWorkResource < ::Hyrax::Work + include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:institutional_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:student_work_metadata, schema_loader: Spot::SimpleSchemaLoader.new) +end diff --git a/app/services/spot/embargo_lease_service.rb b/app/services/spot/embargo_lease_service.rb index 061262742..232425588 100644 --- a/app/services/spot/embargo_lease_service.rb +++ b/app/services/spot/embargo_lease_service.rb @@ -3,15 +3,15 @@ module Spot # A service for dealing with embargoes and leases. Right now, we're just # using this to clear out expired items. # - # @example Clear out all expired values at once - # Spot::EmbargoLeaseService.clear_all_expired + # @example Clear out all expired values at once and regenerate thumbnails + # Spot::EmbargoLeaseService.clear_all_expired(regenerate_thumbnails: true) # - # @example Clear out expired embargoes (and update +date_available+ values) + # @example Clear out expired embargoes # Spot::EmbargoLeaseService.clear_expired_embargoes # # @example Clear out expired leases # Spot::EmbargoLeaseService.clear_expired_leases - # + class EmbargoLeaseService class << self # Convenience method to clear both embargoes and leases @@ -28,18 +28,7 @@ def clear_all_expired(regenerate_thumbnails: false) # @return [void] def clear_expired_embargoes(regenerate_thumbnails: false) ::Hyrax::EmbargoService.assets_with_expired_embargoes.each do |presenter| - item = ActiveFedora::Base.find(presenter.id) - - next if item.under_embargo? - - ::Hyrax::Actors::EmbargoActor.new(item).destroy - - next if item.is_a? FileSet - - item.copy_visibility_to_files - item.save! - - RegenerateThumbnailJob.perform_later(item) if regenerate_thumbnails == true + release_and_save_for_id(presenter.id, type: :embargo, regenerate_thumbnails: regenerate_thumbnails) end end @@ -48,15 +37,57 @@ def clear_expired_embargoes(regenerate_thumbnails: false) # @return [void] def clear_expired_leases(regenerate_thumbnails: false) ::Hyrax::LeaseService.assets_with_expired_leases.each do |presenter| - item = ActiveFedora::Base.find(presenter.id) + release_and_save_for_id(presenter.id, type: :lease, regenerate_thumbnails: regenerate_thumbnails) + end + end + + private - next if item.active_lease? + # Embargos and Leases behave very similarly and are managed through similar interfaces + # in Hyrax, so we'll do the bulk of that work here. + # + # @param [String] id + # @option [:embargo, :lease] :type + # @option [true/false] :regenerate_thumbnails + # @return [void] + def release_and_save_for_id(id, type:, regenerate_thumbnails: false) + resource = release_and_save_acl_for(type: type, resource: Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: id)) + return if resource.nil? + + copy_visibility_to_members!(resource: resource) + + RegenerateThumbnailJob.perform_later(resource) if regenerate_thumbnails == true + end - ::Hyrax::Actors::LeaseActor.new(item).destroy + # calling #release! on the managers will raise a +NotReleaseableError+ if + # the embargo/lease isn't ready to be deactivated. Since the resource's + # visibility hasn't changed, we won't bother to resave the object or enqueue + # a thumbnail regeneration job. + # + # @todo how can we test the `nil` return value for coverage? + def release_and_save_acl_for(type:, resource:) + manager_class = manager_class_for(type) + return if manager_class.nil? + + manager_class.new(resource: resource).release! + resource.permission_manager.acl.save + resource + rescue Hyrax::EmbargoManager::NotReleasableError, Hyrax::LeaseManager::NotReleasableError + nil + end - item.copy_visibility_to_files unless item.is_a? FileSet + def manager_class_for(type) + case type + when :embargo + Hyrax::EmbargoManager + when :lease + Hyrax::LeaseManager + end + end - RegenerateThumbnailJob.perform_later(item) if regenerate_thumbnails == true + def copy_visibility_to_members!(resource:) + Hyrax.query_service.find_members(resource: resource).each do |member| + Hyrax::AccessControlList.copy_permissions(source: resource, target: member) end end end diff --git a/app/services/spot/simple_schema_loader.rb b/app/services/spot/simple_schema_loader.rb new file mode 100644 index 000000000..889a8c782 --- /dev/null +++ b/app/services/spot/simple_schema_loader.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true +module Spot + # Using our own SchemaLoader for the time being to bypass an issue with the Hyrax loader + # where 'type: uri' and 'multiple: true' fields were passing the Dry::Struct interface default + # value of +Dry::Types::Unknown+ through, as it responds true to #present?, which was resulting + # in new resources having default URI values of 'Unknown' + # + # @example + # # config/metadata/cool_metadata.yml + # attributes: + # subject: + # type: uri + # multiple: true + # + # # app/models/cool_resource.rb + # class CoolResource < Hyrax::Resource + # include Hyrax::Schema(:cool_metadata) + # end + # + # CoolResource.new.subject + # => [#] + # + # # app/models/better_resource.rb + # class BetterResource < Hyrax::Resource + # include Hyrax::Schema(:cool_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + # end + # + # BetterResource.new.subject + # => [] + # + class SimpleSchemaLoader < Hyrax::SimpleSchemaLoader + class AttributeDefinition < Hyrax::SimpleSchemaLoader::AttributeDefinition + def type + collection_type = if config['multiple'] + Valkyrie::Types::Array.constructor { |v| Array(v).select { |x| x.present? && x != Dry::Types::Undefined } } + else + Identity + end + collection_type.of(type_for(config['type'])) + end + end + + private + + # Map the definitions to use our overloaded AttributeDefinition class. + # + # @param [#to_s] schema_name + # @return [Enumerable Date: Mon, 27 Oct 2025 09:10:49 -0400 Subject: [PATCH 010/116] quick and dirty postresql upgrade --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 1e77d05d0..41d4b67b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,7 @@ RUN apt-get clean && \ netcat-openbsd \ nodejs \ openssl \ - postgresql \ + postgresql-14 \ ruby-dev \ tzdata \ zip From 7c7cc4af2a3a5e6bf3a2585a689440a9761c9ccd Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Mon, 27 Oct 2025 09:15:57 -0400 Subject: [PATCH 011/116] github actions was looking for 13, that was the problem --- .github/workflows/lint-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml index 06159ea1b..6e06c6edd 100644 --- a/.github/workflows/lint-and-test.yml +++ b/.github/workflows/lint-and-test.yml @@ -98,7 +98,7 @@ jobs: echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ apt-get update -y && apt-get install -y \ build-essential coreutils curl git libxml2 libxml2-dev libxslt-dev \ - netcat-openbsd nodejs openssl postgresql-13 ruby-dev tzdata zip \ + netcat-openbsd nodejs openssl postgresql-14 ruby-dev tzdata zip \ libasound2 libdbus-glib-1-dev libgtk-3-0 libpq-dev unzip libx11-xcb1 - name: Setup Firefox From d2dbb237b61c26e9a9d6fd27693504c128f3bfe7 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Mon, 27 Oct 2025 10:29:07 -0400 Subject: [PATCH 012/116] reconstitute action from dockerfile --- .github/workflows/lint-and-test.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml index 6e06c6edd..53cb9ca87 100644 --- a/.github/workflows/lint-and-test.yml +++ b/.github/workflows/lint-and-test.yml @@ -92,13 +92,11 @@ jobs: - name: Install system dependencies from Dockerfile run: | - apt-get update -y && apt-get install -y --no-install-recommends ca-certificates curl gnupg && \ - mkdir -p /etc/apt/keyrings && \ - curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \ - echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \ - apt-get update -y && apt-get install -y \ - build-essential coreutils curl git libxml2 libxml2-dev libxslt-dev \ - netcat-openbsd nodejs openssl postgresql-14 ruby-dev tzdata zip \ + apt-get update -y && apt-get install -y curl && \ + curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \ + apt-get update -y --fix-missing && apt-get install -y --no-install-recommends \ + awscli build-essential coreutils cron git libpq-dev libxml2 libxml2-dev \ + libxslt-dev netcat-openbsd nodejs openssl postgresql-14 ruby-dev tzdata zip \ libasound2 libdbus-glib-1-dev libgtk-3-0 libpq-dev unzip libx11-xcb1 - name: Setup Firefox From 30ae7654b5db415619c0d6c76074ebf1c91fc84d Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Mon, 27 Oct 2025 10:47:43 -0400 Subject: [PATCH 013/116] maybe unlock the version? --- .github/workflows/lint-and-test.yml | 2 +- Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml index 53cb9ca87..aa150d243 100644 --- a/.github/workflows/lint-and-test.yml +++ b/.github/workflows/lint-and-test.yml @@ -96,7 +96,7 @@ jobs: curl -fsSL https://deb.nodesource.com/setup_22.x | bash - && \ apt-get update -y --fix-missing && apt-get install -y --no-install-recommends \ awscli build-essential coreutils cron git libpq-dev libxml2 libxml2-dev \ - libxslt-dev netcat-openbsd nodejs openssl postgresql-14 ruby-dev tzdata zip \ + libxslt-dev netcat-openbsd nodejs openssl postgresql ruby-dev tzdata zip \ libasound2 libdbus-glib-1-dev libgtk-3-0 libpq-dev unzip libx11-xcb1 - name: Setup Firefox diff --git a/Dockerfile b/Dockerfile index 41d4b67b4..1e77d05d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -22,7 +22,7 @@ RUN apt-get clean && \ netcat-openbsd \ nodejs \ openssl \ - postgresql-14 \ + postgresql \ ruby-dev \ tzdata \ zip From 0cfe2104eca964503e52e84605ddadbe9f236570 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Mon, 27 Oct 2025 15:10:33 -0400 Subject: [PATCH 014/116] remove valkyrization --- app/models/audio_visual_resource.rb | 7 - .../concerns/spot/base_resource_behavior.rb | 32 ---- app/models/image_resource.rb | 5 - app/models/publication_resource.rb | 8 - app/models/student_work_resource.rb | 6 - app/services/spot/embargo_lease_service.rb | 73 +++----- config/metadata/audio_visual_metadata.yaml | 67 ------- config/metadata/base_metadata.yaml | 173 ------------------ config/metadata/core_metadata.yaml | 22 --- config/metadata/image_metadata.yaml | 85 --------- config/metadata/institutional_metadata.yaml | 28 --- config/metadata/publication_metadata.yaml | 42 ----- config/metadata/student_work_metadata.yaml | 43 ----- 13 files changed, 21 insertions(+), 570 deletions(-) delete mode 100644 app/models/audio_visual_resource.rb delete mode 100644 app/models/concerns/spot/base_resource_behavior.rb delete mode 100644 app/models/image_resource.rb delete mode 100644 app/models/publication_resource.rb delete mode 100644 app/models/student_work_resource.rb delete mode 100644 config/metadata/audio_visual_metadata.yaml delete mode 100644 config/metadata/base_metadata.yaml delete mode 100644 config/metadata/core_metadata.yaml delete mode 100644 config/metadata/image_metadata.yaml delete mode 100644 config/metadata/institutional_metadata.yaml delete mode 100644 config/metadata/publication_metadata.yaml delete mode 100644 config/metadata/student_work_metadata.yaml diff --git a/app/models/audio_visual_resource.rb b/app/models/audio_visual_resource.rb deleted file mode 100644 index 3cf3507f3..000000000 --- a/app/models/audio_visual_resource.rb +++ /dev/null @@ -1,7 +0,0 @@ -# frozen_string_literal: true -class AudioVisualResource < ::Hyrax::Work - include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) - include Hyrax::Schema(:audio_visual_metadata, schema_loader: Spot::SimpleSchemaLoader.new) - - attribute :stored_derivatives, Valkyrie::Types::String -end diff --git a/app/models/concerns/spot/base_resource_behavior.rb b/app/models/concerns/spot/base_resource_behavior.rb deleted file mode 100644 index ad7ef36fe..000000000 --- a/app/models/concerns/spot/base_resource_behavior.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true -module Spot - module BaseResourceBehavior - # Wraps stored :identifier values (strings) in our {Spot::Identifier} interface - # - # @see app/models/spot/identifier.rb - # @return [Array] - def identifier - Array.wrap(self[:identifier]).map { |id| Spot::Identifier.from_string(id) } - end - - # Wraps stored :location values (themselves wrapped in RDF::URI via Hyrax/Valkyrie) in - # our {Spot::ControlledVocabularies::Location} interface. This conforms to the previous - # ActiveFedora interface and gives us an entrypoint to fetch labels during indexing. - # - # @see app/models/spot/controlled_vocabularies/location.rb - # @return [Array] - def location - Array.wrap(self[:location]).map { |uri| Spot::ControlledVocabularies::Location.new(uri) } - end - - # Wraps stored :subject values (themselves wrapped in RDF::URI via Valkyrie) in - # our {Spot::ControlledVocabularies::AssignFastSubject} interface. This conforms to the previous - # ActiveFedora interface and gives us an entrypoint to fetch labels during indexing. - # - # @see app/models/spot/controlled_vocabularies/assign_fast_subject.rb - # @return [Array] - def subject - Array.wrap(self[:subject]).map { |uri| Spot::ControlledVocabularies::AssignFastSubject.new(uri) } - end - end -end diff --git a/app/models/image_resource.rb b/app/models/image_resource.rb deleted file mode 100644 index 9de64fee0..000000000 --- a/app/models/image_resource.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -class ImageResource < ::Hyrax::Work - include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) - include Hyrax::Schema(:image_metadata, schema_loader: Spot::SimpleSchemaLoader.new) -end diff --git a/app/models/publication_resource.rb b/app/models/publication_resource.rb deleted file mode 100644 index 2111b0dd3..000000000 --- a/app/models/publication_resource.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -class PublicationResource < ::Hyrax::Work - include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) - include Hyrax::Schema(:institutional_metadata, schema_loader: Spot::SimpleSchemaLoader.new) - include Hyrax::Schema(:publication_metadata, schema_loader: Spot::SimpleSchemaLoader.new) - - include Spot::BaseResourceBehavior -end diff --git a/app/models/student_work_resource.rb b/app/models/student_work_resource.rb deleted file mode 100644 index 316197ce9..000000000 --- a/app/models/student_work_resource.rb +++ /dev/null @@ -1,6 +0,0 @@ -# frozen_string_literal: true -class StudentWorkResource < ::Hyrax::Work - include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) - include Hyrax::Schema(:institutional_metadata, schema_loader: Spot::SimpleSchemaLoader.new) - include Hyrax::Schema(:student_work_metadata, schema_loader: Spot::SimpleSchemaLoader.new) -end diff --git a/app/services/spot/embargo_lease_service.rb b/app/services/spot/embargo_lease_service.rb index 232425588..061262742 100644 --- a/app/services/spot/embargo_lease_service.rb +++ b/app/services/spot/embargo_lease_service.rb @@ -3,15 +3,15 @@ module Spot # A service for dealing with embargoes and leases. Right now, we're just # using this to clear out expired items. # - # @example Clear out all expired values at once and regenerate thumbnails - # Spot::EmbargoLeaseService.clear_all_expired(regenerate_thumbnails: true) + # @example Clear out all expired values at once + # Spot::EmbargoLeaseService.clear_all_expired # - # @example Clear out expired embargoes + # @example Clear out expired embargoes (and update +date_available+ values) # Spot::EmbargoLeaseService.clear_expired_embargoes # # @example Clear out expired leases # Spot::EmbargoLeaseService.clear_expired_leases - + # class EmbargoLeaseService class << self # Convenience method to clear both embargoes and leases @@ -28,7 +28,18 @@ def clear_all_expired(regenerate_thumbnails: false) # @return [void] def clear_expired_embargoes(regenerate_thumbnails: false) ::Hyrax::EmbargoService.assets_with_expired_embargoes.each do |presenter| - release_and_save_for_id(presenter.id, type: :embargo, regenerate_thumbnails: regenerate_thumbnails) + item = ActiveFedora::Base.find(presenter.id) + + next if item.under_embargo? + + ::Hyrax::Actors::EmbargoActor.new(item).destroy + + next if item.is_a? FileSet + + item.copy_visibility_to_files + item.save! + + RegenerateThumbnailJob.perform_later(item) if regenerate_thumbnails == true end end @@ -37,57 +48,15 @@ def clear_expired_embargoes(regenerate_thumbnails: false) # @return [void] def clear_expired_leases(regenerate_thumbnails: false) ::Hyrax::LeaseService.assets_with_expired_leases.each do |presenter| - release_and_save_for_id(presenter.id, type: :lease, regenerate_thumbnails: regenerate_thumbnails) - end - end - - private + item = ActiveFedora::Base.find(presenter.id) - # Embargos and Leases behave very similarly and are managed through similar interfaces - # in Hyrax, so we'll do the bulk of that work here. - # - # @param [String] id - # @option [:embargo, :lease] :type - # @option [true/false] :regenerate_thumbnails - # @return [void] - def release_and_save_for_id(id, type:, regenerate_thumbnails: false) - resource = release_and_save_acl_for(type: type, resource: Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: id)) - return if resource.nil? - - copy_visibility_to_members!(resource: resource) - - RegenerateThumbnailJob.perform_later(resource) if regenerate_thumbnails == true - end + next if item.active_lease? - # calling #release! on the managers will raise a +NotReleaseableError+ if - # the embargo/lease isn't ready to be deactivated. Since the resource's - # visibility hasn't changed, we won't bother to resave the object or enqueue - # a thumbnail regeneration job. - # - # @todo how can we test the `nil` return value for coverage? - def release_and_save_acl_for(type:, resource:) - manager_class = manager_class_for(type) - return if manager_class.nil? - - manager_class.new(resource: resource).release! - resource.permission_manager.acl.save - resource - rescue Hyrax::EmbargoManager::NotReleasableError, Hyrax::LeaseManager::NotReleasableError - nil - end + ::Hyrax::Actors::LeaseActor.new(item).destroy - def manager_class_for(type) - case type - when :embargo - Hyrax::EmbargoManager - when :lease - Hyrax::LeaseManager - end - end + item.copy_visibility_to_files unless item.is_a? FileSet - def copy_visibility_to_members!(resource:) - Hyrax.query_service.find_members(resource: resource).each do |member| - Hyrax::AccessControlList.copy_permissions(source: resource, target: member) + RegenerateThumbnailJob.perform_later(item) if regenerate_thumbnails == true end end end diff --git a/config/metadata/audio_visual_metadata.yaml b/config/metadata/audio_visual_metadata.yaml deleted file mode 100644 index b7690b6f5..000000000 --- a/config/metadata/audio_visual_metadata.yaml +++ /dev/null @@ -1,67 +0,0 @@ -attributes: - date: - predicate: http://purl.org/dc/terms/date - type: string - multiple: true - required: true - form: - multiple: true - index_keys: - - date_ssim - date_associated: - predicate: https://d-nb.info/standards/elementset/gnd#associatedDate - type: string - multiple: true - form: - multiple: true - index_keys: - - date_associated_ssim - - date_associated_tesim - inscription: - predicate: http://dbpedia.org/ontology/inscription - type: string - multiple: true - form: - multiple: true - index_keys: - - inscription_tesim - original_item_extent: - predicate: http://purl.org/dc/terms/extent - type: string - multiple: true - form: - multiple: true - index_keys: - - original_item_extent_tesim - repository_location: - predicate: http://purl.org/vra/placeOfRepository - type: string - multiple: true - form: - multiple: true - index_keys: - - repository_location_ssim - research_assistance: - predicate: http://www.rdaregistry.info/Elements/a/#P50265 - type: string - multiple: true - form: - multiple: true - index_keys: - - research_assistance_ssim - provenance: - predicate: http://purl.org/dc/terms/provenance - type: string - multiple: true - form: - multiple: true - index_keys: - - provenance_tesim - barcode: - predicate: https://schema.org/Barcode - type: string - multiple: true - form: - multiple: true - index_keys: - - barcode_ssim diff --git a/config/metadata/base_metadata.yaml b/config/metadata/base_metadata.yaml deleted file mode 100644 index 7e86872ab..000000000 --- a/config/metadata/base_metadata.yaml +++ /dev/null @@ -1,173 +0,0 @@ -attributes: - bibliographic_citation: - predicate: http://purl.org/dc/terms/bibliographicCitation - type: string - multiple: true - form: - multiple: true - index_keys: - - bibliographic_citation_tesim - contributor: - predicate: http://purl.org/dc/elements/1.1/contributor - type: string - multiple: true - form: - multiple: true - index_keys: - - contributor_tesim - - contributor_sim - creator: - predicate: http://purl.org/dc/elements/1.1/creator - type: string - multiple: true - form: - multiple: true - index_keys: - - creator_tesim - - creator_sim - description: - predicate: http://purl.org/dc/elements/1.1/description - type: string - multiple: true - form: - multiple: true - primary: true - index_keys: - - description_tesim - identifier: - predicate: http://purl.org/dc/terms/identifier - type: string - multiple: true - form: - multiple: true - index_keys: - - identifier_ssim - keyword: - predicate: http://schema.org/keywords - type: string - multiple: true - form: - multiple: true - index_keys: - - keyword_tesim - - keyword_sim - # @see IndexesLanguageAndLabel mixin for indexing info, uses - language: - predicate: http://purl.org/dc/elements/1.1/language - type: string - multiple: true - form: - multiple: true - # @note RDF indexing for :location is set up in app/models/base_resource.rb - # and uses "location_ssim" and "location_label_ssim" keys - location: - predicate: http://purl.org/dc/terms/spatial - type: uri - multiple: true - form: - multiple: true - note: - predicate: http://www.w3.org/2004/02/skos/core#note - type: string - multiple: true - form: - multiple: true - index_keys: - - note_tesim - physical_medium: - predicate: http://purl.org/dc/terms/PhysicalMedium - type: string - multiple: true - form: - multiple: true - index_keys: - - physical_medium_tesim - - physical_medium_sim - publisher: - predicate: http://purl.org/dc/elements/1.1/publisher - type: string - multiple: true - form: - multiple: true - index_keys: - - publisher_tesim - - publisher_sim - related_resource: - predicate: http://www.w3.org/2000/01/rdf-schema#seeAlso - type: string - multiple: true - form: - multiple: true - index_keys: - - related_resource_tesim - - related_resource_sim - resource_type: - predicate: 'http://purl.org/dc/terms/type' - type: string - multiple: true - form: - multiple: true - required: true - index_keys: - - resource_type_ssim - rights_holder: - predicate: http://purl.org/dc/terms/rightsHolder - type: string - multiple: true - form: - multiple: true - index_keys: - - rights_holder_tesim - - rights_holder_sim - rights_statement: - predicate: http://www.europeana.eu/schemas/edm/rights - type: uri - multiple: true - form: - multiple: false - required: true - index_keys: - - rights_statement_ssim - source: - predicate: http://purl.org/dc/terms/source - type: string - multiple: true - form: - multiple: true - index_keys: - - source_tesim - - source_sim - source_identifier: - predicate: http://ldr.lafayette.edu/ns#source_identifier - type: string - multiple: true - index_keys: - - source_identifier_ssim - # @note RDF indexing for :subject is set up in app/models/base_resource.rb - # and uses "subject_ssim" and "subject_label_ssim" keys - subject: - predicate: http://purl.org/dc/elements/1.1/subject - type: uri - multiple: true - form: - multiple: true - primary: true - default: [] - subtitle: - predicate: http://purl.org/spar/doco/Subtitle - type: string - multiple: true - form: - multiple: true - index_keys: - - subtitle_tesim - - subtitle_sim - title_alternative: - predicate: http://purl.org/dc/terms/alternative - type: string - multiple: true - form: - multiple: true - index_keys: - - title_alternative_tesim - - title_alternative_sim \ No newline at end of file diff --git a/config/metadata/core_metadata.yaml b/config/metadata/core_metadata.yaml deleted file mode 100644 index 9af3d81a0..000000000 --- a/config/metadata/core_metadata.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Included in Hyrax::Work + copied from Hyrax source -# @see https://github.com/samvera/hyrax/blob/hyrax-v3.6.0/config/metadata/core_metadata.yaml -attributes: - title: - predicate: http://purl.org/dc/terms/title - type: string - multiple: true - index_keys: - - "title_sim" - - "title_tesim" - form: - multiple: false - required: true - date_modified: - predicate: http://purl.org/dc/terms/modified - type: date_time - date_uploaded: - precicate: http://purl.org/dc/terms/dateSubmitted - type: date_time - depositor: - predicate: http://id.loc.gov/vocabulary/relators/dpt - type: string \ No newline at end of file diff --git a/config/metadata/image_metadata.yaml b/config/metadata/image_metadata.yaml deleted file mode 100644 index 9e409f5b7..000000000 --- a/config/metadata/image_metadata.yaml +++ /dev/null @@ -1,85 +0,0 @@ -attributes: - # @todo date type? edtf type? - date: - predicate: http://purl.org/dc/terms/date - type: string - multiple: true - required: true - form: - multiple: true - index_keys: - - date_ssim - date_associated: - predicate: https://d-nb.info/standards/elementset/gnd#associatedDate - type: string - multiple: true - form: - multiple: true - index_keys: - - date_associated_ssim - - date_associated_tesim - date_scope_note: - predicate: http://www.w3.org/2004/02/skos/core#scopeNote - type: string - multiple: true - form: - multiple: true - index_keys: - - date_scope_note_tesim - donor: - predicate: http://purl.org/dc/terms/provenance - type: string - multiple: true - form: - multiple: true - index_keys: - - donor_ssim - inscription: - predicate: http://dbpedia.org/ontology/inscription - type: string - multiple: true - form: - multiple: true - index_keys: - - inscription_tesim - original_item_extent: - predicate: http://purl.org/dc/terms/extent - type: string - multiple: true - form: - multiple: true - index_keys: - - original_item_extent_tesim - repository_location: - predicate: http://purl.org/vra/placeOfRepository - type: string - multiple: true - form: - multiple: true - index_keys: - - repository_location_ssim - requested_by: - predicate: http://rdf.myexperiment.org/ontologies/base/has-requester - type: string - multiple: true - form: - multiple: true - index_keys: - - requested_by_ssim - research_assistance: - predicate: http://www.rdaregistry.info/Elements/a/#P50265 - type: string - multiple: true - form: - multiple: true - index_keys: - - research_assistance_ssim - subject_ocm: - predicate: https://hraf.yale.edu/resources/reference/outline-of-cultural-materials - type: string - multiple: true - form: - multiple: true - index_keys: - - subject_ocm_tesim - - subject_ocm_ssim diff --git a/config/metadata/institutional_metadata.yaml b/config/metadata/institutional_metadata.yaml deleted file mode 100644 index 5e3e9ea44..000000000 --- a/config/metadata/institutional_metadata.yaml +++ /dev/null @@ -1,28 +0,0 @@ -attributes: - academic_department: - predicate: http://vivoweb.org/ontology/core#AcademicDepartment - type: string - multiple: true - form: - multiple: true - index_keys: - - academic_department_tesim - - academic_department_sim - division: - predicate: http://vivoweb.org/ontology/core#Division - type: string - multiple: true - form: - multiple: true - index_keys: - - division_tesim - - division_sim - organization: - predicate: http://vivoweb.org/ontology/core#Organization - type: string - multiple: true - form: - multiple: true - index_keys: - - organization_tesim - - organization_sim \ No newline at end of file diff --git a/config/metadata/publication_metadata.yaml b/config/metadata/publication_metadata.yaml deleted file mode 100644 index d4c6fe2fc..000000000 --- a/config/metadata/publication_metadata.yaml +++ /dev/null @@ -1,42 +0,0 @@ -attributes: - abstract: - predicate: http://purl.org/dc/terms/abstract - type: string - multiple: true - form: - multiple: true - index_keys: - - abstract_tesim - date_issued: - predicate: http://purl.org/dc/terms/issued - type: string - multiple: true - form: - multiple: false - required: true - index_keys: - - date_issued_ssim - # @todo should this be a date field? - date_available: - predicate: http://purl.org/dc/terms/available - type: string - multiple: true - form: - multiple: false - index_keys: - - date_available_ssim - editor: - predicate: http://purl.org/ontology/bibo/editor - type: string - multiple: true - form: - multiple: true - index_keys: - - editor_sim - - editor_tesim - license: - predicate: http://purl.org/dc/terms/license - type: string - multiple: true - index_keys: - - license_tsm diff --git a/config/metadata/student_work_metadata.yaml b/config/metadata/student_work_metadata.yaml deleted file mode 100644 index 39c697708..000000000 --- a/config/metadata/student_work_metadata.yaml +++ /dev/null @@ -1,43 +0,0 @@ -attributes: - abstract: - predicate: http://purl.org/dc/terms/abstract - type: string - multiple: true - form: - multiple: true - index_keys: - - abstract_tesim - access_note: - predicate: http://purl.org/dc/terms/accessRights - type: string - multiple: true - form: - multiple: true - index_keys: - - access_note_tesim - advisor: - predicate: http://id.loc.gov/vocabulary/relators/ths - type: string - multiple: true - form: - multiple: true - index_keys: - - advisor_ssim - # dates are edtf values, is there a way we can make this a type? - date: - predicate: http://purl.org/dc/terms/date - type: string - multiple: true - required: true - form: - multiple: true - index_keys: - - date_ssim - date_available: - predicate: http://purl.org/dc/terms/available - type: string - multiple: true - form: - multiple: true - index_keys: - - date_available_ssim From f0aaeb3808e4f4e6d3a5dae249413c4d0d457815 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Mon, 27 Oct 2025 17:04:34 -0400 Subject: [PATCH 015/116] undo remove valkyrization (: --- app/models/audio_visual_resource.rb | 7 + app/models/concerns/base_resource_behavior.rb | 32 +++ app/models/image_resource.rb | 5 + app/models/publication_resource.rb | 8 + app/models/student_work_resource.rb | 6 + app/services/spot/embargo_lease_service.rb | 73 +++++-- config/metadata/audio_visual_metadata.yaml | 67 ++++++ config/metadata/base_metadata.yaml | 173 +++++++++++++++ config/metadata/core_metadata.yaml | 22 ++ config/metadata/image_metadata.yaml | 85 ++++++++ config/metadata/institutional_metadata.yaml | 28 +++ config/metadata/publication_metadata.yaml | 42 ++++ config/metadata/student_work_metadata.yaml | 43 ++++ .../spot/embargo_lease_service_spec.rb | 201 ++++++++---------- 14 files changed, 659 insertions(+), 133 deletions(-) create mode 100644 app/models/audio_visual_resource.rb create mode 100644 app/models/concerns/base_resource_behavior.rb create mode 100644 app/models/image_resource.rb create mode 100644 app/models/publication_resource.rb create mode 100644 app/models/student_work_resource.rb create mode 100644 config/metadata/audio_visual_metadata.yaml create mode 100644 config/metadata/base_metadata.yaml create mode 100644 config/metadata/core_metadata.yaml create mode 100644 config/metadata/image_metadata.yaml create mode 100644 config/metadata/institutional_metadata.yaml create mode 100644 config/metadata/publication_metadata.yaml create mode 100644 config/metadata/student_work_metadata.yaml diff --git a/app/models/audio_visual_resource.rb b/app/models/audio_visual_resource.rb new file mode 100644 index 000000000..3cf3507f3 --- /dev/null +++ b/app/models/audio_visual_resource.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true +class AudioVisualResource < ::Hyrax::Work + include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:audio_visual_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + + attribute :stored_derivatives, Valkyrie::Types::String +end diff --git a/app/models/concerns/base_resource_behavior.rb b/app/models/concerns/base_resource_behavior.rb new file mode 100644 index 000000000..ad7ef36fe --- /dev/null +++ b/app/models/concerns/base_resource_behavior.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true +module Spot + module BaseResourceBehavior + # Wraps stored :identifier values (strings) in our {Spot::Identifier} interface + # + # @see app/models/spot/identifier.rb + # @return [Array] + def identifier + Array.wrap(self[:identifier]).map { |id| Spot::Identifier.from_string(id) } + end + + # Wraps stored :location values (themselves wrapped in RDF::URI via Hyrax/Valkyrie) in + # our {Spot::ControlledVocabularies::Location} interface. This conforms to the previous + # ActiveFedora interface and gives us an entrypoint to fetch labels during indexing. + # + # @see app/models/spot/controlled_vocabularies/location.rb + # @return [Array] + def location + Array.wrap(self[:location]).map { |uri| Spot::ControlledVocabularies::Location.new(uri) } + end + + # Wraps stored :subject values (themselves wrapped in RDF::URI via Valkyrie) in + # our {Spot::ControlledVocabularies::AssignFastSubject} interface. This conforms to the previous + # ActiveFedora interface and gives us an entrypoint to fetch labels during indexing. + # + # @see app/models/spot/controlled_vocabularies/assign_fast_subject.rb + # @return [Array] + def subject + Array.wrap(self[:subject]).map { |uri| Spot::ControlledVocabularies::AssignFastSubject.new(uri) } + end + end +end diff --git a/app/models/image_resource.rb b/app/models/image_resource.rb new file mode 100644 index 000000000..9de64fee0 --- /dev/null +++ b/app/models/image_resource.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true +class ImageResource < ::Hyrax::Work + include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:image_metadata, schema_loader: Spot::SimpleSchemaLoader.new) +end diff --git a/app/models/publication_resource.rb b/app/models/publication_resource.rb new file mode 100644 index 000000000..2111b0dd3 --- /dev/null +++ b/app/models/publication_resource.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +class PublicationResource < ::Hyrax::Work + include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:institutional_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:publication_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + + include Spot::BaseResourceBehavior +end diff --git a/app/models/student_work_resource.rb b/app/models/student_work_resource.rb new file mode 100644 index 000000000..316197ce9 --- /dev/null +++ b/app/models/student_work_resource.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true +class StudentWorkResource < ::Hyrax::Work + include Hyrax::Schema(:base_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:institutional_metadata, schema_loader: Spot::SimpleSchemaLoader.new) + include Hyrax::Schema(:student_work_metadata, schema_loader: Spot::SimpleSchemaLoader.new) +end diff --git a/app/services/spot/embargo_lease_service.rb b/app/services/spot/embargo_lease_service.rb index 061262742..232425588 100644 --- a/app/services/spot/embargo_lease_service.rb +++ b/app/services/spot/embargo_lease_service.rb @@ -3,15 +3,15 @@ module Spot # A service for dealing with embargoes and leases. Right now, we're just # using this to clear out expired items. # - # @example Clear out all expired values at once - # Spot::EmbargoLeaseService.clear_all_expired + # @example Clear out all expired values at once and regenerate thumbnails + # Spot::EmbargoLeaseService.clear_all_expired(regenerate_thumbnails: true) # - # @example Clear out expired embargoes (and update +date_available+ values) + # @example Clear out expired embargoes # Spot::EmbargoLeaseService.clear_expired_embargoes # # @example Clear out expired leases # Spot::EmbargoLeaseService.clear_expired_leases - # + class EmbargoLeaseService class << self # Convenience method to clear both embargoes and leases @@ -28,18 +28,7 @@ def clear_all_expired(regenerate_thumbnails: false) # @return [void] def clear_expired_embargoes(regenerate_thumbnails: false) ::Hyrax::EmbargoService.assets_with_expired_embargoes.each do |presenter| - item = ActiveFedora::Base.find(presenter.id) - - next if item.under_embargo? - - ::Hyrax::Actors::EmbargoActor.new(item).destroy - - next if item.is_a? FileSet - - item.copy_visibility_to_files - item.save! - - RegenerateThumbnailJob.perform_later(item) if regenerate_thumbnails == true + release_and_save_for_id(presenter.id, type: :embargo, regenerate_thumbnails: regenerate_thumbnails) end end @@ -48,15 +37,57 @@ def clear_expired_embargoes(regenerate_thumbnails: false) # @return [void] def clear_expired_leases(regenerate_thumbnails: false) ::Hyrax::LeaseService.assets_with_expired_leases.each do |presenter| - item = ActiveFedora::Base.find(presenter.id) + release_and_save_for_id(presenter.id, type: :lease, regenerate_thumbnails: regenerate_thumbnails) + end + end + + private - next if item.active_lease? + # Embargos and Leases behave very similarly and are managed through similar interfaces + # in Hyrax, so we'll do the bulk of that work here. + # + # @param [String] id + # @option [:embargo, :lease] :type + # @option [true/false] :regenerate_thumbnails + # @return [void] + def release_and_save_for_id(id, type:, regenerate_thumbnails: false) + resource = release_and_save_acl_for(type: type, resource: Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: id)) + return if resource.nil? + + copy_visibility_to_members!(resource: resource) + + RegenerateThumbnailJob.perform_later(resource) if regenerate_thumbnails == true + end - ::Hyrax::Actors::LeaseActor.new(item).destroy + # calling #release! on the managers will raise a +NotReleaseableError+ if + # the embargo/lease isn't ready to be deactivated. Since the resource's + # visibility hasn't changed, we won't bother to resave the object or enqueue + # a thumbnail regeneration job. + # + # @todo how can we test the `nil` return value for coverage? + def release_and_save_acl_for(type:, resource:) + manager_class = manager_class_for(type) + return if manager_class.nil? + + manager_class.new(resource: resource).release! + resource.permission_manager.acl.save + resource + rescue Hyrax::EmbargoManager::NotReleasableError, Hyrax::LeaseManager::NotReleasableError + nil + end - item.copy_visibility_to_files unless item.is_a? FileSet + def manager_class_for(type) + case type + when :embargo + Hyrax::EmbargoManager + when :lease + Hyrax::LeaseManager + end + end - RegenerateThumbnailJob.perform_later(item) if regenerate_thumbnails == true + def copy_visibility_to_members!(resource:) + Hyrax.query_service.find_members(resource: resource).each do |member| + Hyrax::AccessControlList.copy_permissions(source: resource, target: member) end end end diff --git a/config/metadata/audio_visual_metadata.yaml b/config/metadata/audio_visual_metadata.yaml new file mode 100644 index 000000000..b7690b6f5 --- /dev/null +++ b/config/metadata/audio_visual_metadata.yaml @@ -0,0 +1,67 @@ +attributes: + date: + predicate: http://purl.org/dc/terms/date + type: string + multiple: true + required: true + form: + multiple: true + index_keys: + - date_ssim + date_associated: + predicate: https://d-nb.info/standards/elementset/gnd#associatedDate + type: string + multiple: true + form: + multiple: true + index_keys: + - date_associated_ssim + - date_associated_tesim + inscription: + predicate: http://dbpedia.org/ontology/inscription + type: string + multiple: true + form: + multiple: true + index_keys: + - inscription_tesim + original_item_extent: + predicate: http://purl.org/dc/terms/extent + type: string + multiple: true + form: + multiple: true + index_keys: + - original_item_extent_tesim + repository_location: + predicate: http://purl.org/vra/placeOfRepository + type: string + multiple: true + form: + multiple: true + index_keys: + - repository_location_ssim + research_assistance: + predicate: http://www.rdaregistry.info/Elements/a/#P50265 + type: string + multiple: true + form: + multiple: true + index_keys: + - research_assistance_ssim + provenance: + predicate: http://purl.org/dc/terms/provenance + type: string + multiple: true + form: + multiple: true + index_keys: + - provenance_tesim + barcode: + predicate: https://schema.org/Barcode + type: string + multiple: true + form: + multiple: true + index_keys: + - barcode_ssim diff --git a/config/metadata/base_metadata.yaml b/config/metadata/base_metadata.yaml new file mode 100644 index 000000000..7e86872ab --- /dev/null +++ b/config/metadata/base_metadata.yaml @@ -0,0 +1,173 @@ +attributes: + bibliographic_citation: + predicate: http://purl.org/dc/terms/bibliographicCitation + type: string + multiple: true + form: + multiple: true + index_keys: + - bibliographic_citation_tesim + contributor: + predicate: http://purl.org/dc/elements/1.1/contributor + type: string + multiple: true + form: + multiple: true + index_keys: + - contributor_tesim + - contributor_sim + creator: + predicate: http://purl.org/dc/elements/1.1/creator + type: string + multiple: true + form: + multiple: true + index_keys: + - creator_tesim + - creator_sim + description: + predicate: http://purl.org/dc/elements/1.1/description + type: string + multiple: true + form: + multiple: true + primary: true + index_keys: + - description_tesim + identifier: + predicate: http://purl.org/dc/terms/identifier + type: string + multiple: true + form: + multiple: true + index_keys: + - identifier_ssim + keyword: + predicate: http://schema.org/keywords + type: string + multiple: true + form: + multiple: true + index_keys: + - keyword_tesim + - keyword_sim + # @see IndexesLanguageAndLabel mixin for indexing info, uses + language: + predicate: http://purl.org/dc/elements/1.1/language + type: string + multiple: true + form: + multiple: true + # @note RDF indexing for :location is set up in app/models/base_resource.rb + # and uses "location_ssim" and "location_label_ssim" keys + location: + predicate: http://purl.org/dc/terms/spatial + type: uri + multiple: true + form: + multiple: true + note: + predicate: http://www.w3.org/2004/02/skos/core#note + type: string + multiple: true + form: + multiple: true + index_keys: + - note_tesim + physical_medium: + predicate: http://purl.org/dc/terms/PhysicalMedium + type: string + multiple: true + form: + multiple: true + index_keys: + - physical_medium_tesim + - physical_medium_sim + publisher: + predicate: http://purl.org/dc/elements/1.1/publisher + type: string + multiple: true + form: + multiple: true + index_keys: + - publisher_tesim + - publisher_sim + related_resource: + predicate: http://www.w3.org/2000/01/rdf-schema#seeAlso + type: string + multiple: true + form: + multiple: true + index_keys: + - related_resource_tesim + - related_resource_sim + resource_type: + predicate: 'http://purl.org/dc/terms/type' + type: string + multiple: true + form: + multiple: true + required: true + index_keys: + - resource_type_ssim + rights_holder: + predicate: http://purl.org/dc/terms/rightsHolder + type: string + multiple: true + form: + multiple: true + index_keys: + - rights_holder_tesim + - rights_holder_sim + rights_statement: + predicate: http://www.europeana.eu/schemas/edm/rights + type: uri + multiple: true + form: + multiple: false + required: true + index_keys: + - rights_statement_ssim + source: + predicate: http://purl.org/dc/terms/source + type: string + multiple: true + form: + multiple: true + index_keys: + - source_tesim + - source_sim + source_identifier: + predicate: http://ldr.lafayette.edu/ns#source_identifier + type: string + multiple: true + index_keys: + - source_identifier_ssim + # @note RDF indexing for :subject is set up in app/models/base_resource.rb + # and uses "subject_ssim" and "subject_label_ssim" keys + subject: + predicate: http://purl.org/dc/elements/1.1/subject + type: uri + multiple: true + form: + multiple: true + primary: true + default: [] + subtitle: + predicate: http://purl.org/spar/doco/Subtitle + type: string + multiple: true + form: + multiple: true + index_keys: + - subtitle_tesim + - subtitle_sim + title_alternative: + predicate: http://purl.org/dc/terms/alternative + type: string + multiple: true + form: + multiple: true + index_keys: + - title_alternative_tesim + - title_alternative_sim \ No newline at end of file diff --git a/config/metadata/core_metadata.yaml b/config/metadata/core_metadata.yaml new file mode 100644 index 000000000..9af3d81a0 --- /dev/null +++ b/config/metadata/core_metadata.yaml @@ -0,0 +1,22 @@ +# Included in Hyrax::Work + copied from Hyrax source +# @see https://github.com/samvera/hyrax/blob/hyrax-v3.6.0/config/metadata/core_metadata.yaml +attributes: + title: + predicate: http://purl.org/dc/terms/title + type: string + multiple: true + index_keys: + - "title_sim" + - "title_tesim" + form: + multiple: false + required: true + date_modified: + predicate: http://purl.org/dc/terms/modified + type: date_time + date_uploaded: + precicate: http://purl.org/dc/terms/dateSubmitted + type: date_time + depositor: + predicate: http://id.loc.gov/vocabulary/relators/dpt + type: string \ No newline at end of file diff --git a/config/metadata/image_metadata.yaml b/config/metadata/image_metadata.yaml new file mode 100644 index 000000000..9e409f5b7 --- /dev/null +++ b/config/metadata/image_metadata.yaml @@ -0,0 +1,85 @@ +attributes: + # @todo date type? edtf type? + date: + predicate: http://purl.org/dc/terms/date + type: string + multiple: true + required: true + form: + multiple: true + index_keys: + - date_ssim + date_associated: + predicate: https://d-nb.info/standards/elementset/gnd#associatedDate + type: string + multiple: true + form: + multiple: true + index_keys: + - date_associated_ssim + - date_associated_tesim + date_scope_note: + predicate: http://www.w3.org/2004/02/skos/core#scopeNote + type: string + multiple: true + form: + multiple: true + index_keys: + - date_scope_note_tesim + donor: + predicate: http://purl.org/dc/terms/provenance + type: string + multiple: true + form: + multiple: true + index_keys: + - donor_ssim + inscription: + predicate: http://dbpedia.org/ontology/inscription + type: string + multiple: true + form: + multiple: true + index_keys: + - inscription_tesim + original_item_extent: + predicate: http://purl.org/dc/terms/extent + type: string + multiple: true + form: + multiple: true + index_keys: + - original_item_extent_tesim + repository_location: + predicate: http://purl.org/vra/placeOfRepository + type: string + multiple: true + form: + multiple: true + index_keys: + - repository_location_ssim + requested_by: + predicate: http://rdf.myexperiment.org/ontologies/base/has-requester + type: string + multiple: true + form: + multiple: true + index_keys: + - requested_by_ssim + research_assistance: + predicate: http://www.rdaregistry.info/Elements/a/#P50265 + type: string + multiple: true + form: + multiple: true + index_keys: + - research_assistance_ssim + subject_ocm: + predicate: https://hraf.yale.edu/resources/reference/outline-of-cultural-materials + type: string + multiple: true + form: + multiple: true + index_keys: + - subject_ocm_tesim + - subject_ocm_ssim diff --git a/config/metadata/institutional_metadata.yaml b/config/metadata/institutional_metadata.yaml new file mode 100644 index 000000000..5e3e9ea44 --- /dev/null +++ b/config/metadata/institutional_metadata.yaml @@ -0,0 +1,28 @@ +attributes: + academic_department: + predicate: http://vivoweb.org/ontology/core#AcademicDepartment + type: string + multiple: true + form: + multiple: true + index_keys: + - academic_department_tesim + - academic_department_sim + division: + predicate: http://vivoweb.org/ontology/core#Division + type: string + multiple: true + form: + multiple: true + index_keys: + - division_tesim + - division_sim + organization: + predicate: http://vivoweb.org/ontology/core#Organization + type: string + multiple: true + form: + multiple: true + index_keys: + - organization_tesim + - organization_sim \ No newline at end of file diff --git a/config/metadata/publication_metadata.yaml b/config/metadata/publication_metadata.yaml new file mode 100644 index 000000000..d4c6fe2fc --- /dev/null +++ b/config/metadata/publication_metadata.yaml @@ -0,0 +1,42 @@ +attributes: + abstract: + predicate: http://purl.org/dc/terms/abstract + type: string + multiple: true + form: + multiple: true + index_keys: + - abstract_tesim + date_issued: + predicate: http://purl.org/dc/terms/issued + type: string + multiple: true + form: + multiple: false + required: true + index_keys: + - date_issued_ssim + # @todo should this be a date field? + date_available: + predicate: http://purl.org/dc/terms/available + type: string + multiple: true + form: + multiple: false + index_keys: + - date_available_ssim + editor: + predicate: http://purl.org/ontology/bibo/editor + type: string + multiple: true + form: + multiple: true + index_keys: + - editor_sim + - editor_tesim + license: + predicate: http://purl.org/dc/terms/license + type: string + multiple: true + index_keys: + - license_tsm diff --git a/config/metadata/student_work_metadata.yaml b/config/metadata/student_work_metadata.yaml new file mode 100644 index 000000000..39c697708 --- /dev/null +++ b/config/metadata/student_work_metadata.yaml @@ -0,0 +1,43 @@ +attributes: + abstract: + predicate: http://purl.org/dc/terms/abstract + type: string + multiple: true + form: + multiple: true + index_keys: + - abstract_tesim + access_note: + predicate: http://purl.org/dc/terms/accessRights + type: string + multiple: true + form: + multiple: true + index_keys: + - access_note_tesim + advisor: + predicate: http://id.loc.gov/vocabulary/relators/ths + type: string + multiple: true + form: + multiple: true + index_keys: + - advisor_ssim + # dates are edtf values, is there a way we can make this a type? + date: + predicate: http://purl.org/dc/terms/date + type: string + multiple: true + required: true + form: + multiple: true + index_keys: + - date_ssim + date_available: + predicate: http://purl.org/dc/terms/available + type: string + multiple: true + form: + multiple: true + index_keys: + - date_available_ssim diff --git a/spec/services/spot/embargo_lease_service_spec.rb b/spec/services/spot/embargo_lease_service_spec.rb index 26ed38460..480942316 100644 --- a/spec/services/spot/embargo_lease_service_spec.rb +++ b/spec/services/spot/embargo_lease_service_spec.rb @@ -1,137 +1,114 @@ # frozen_string_literal: true -RSpec.describe Spot::EmbargoLeaseService do - let(:publication) { create(:publication) } - let(:attributes) do - { title: ['example item with an embargo'], date_issued: ['2020-01'], - rights_statement: ['http://creativecommons.org/publicdomain/mark/1.0/'], - resource_type: ['Other'], admin_set_id: AdminSet.find_or_create_default_admin_set_id } - end - - describe '.clear_expired_embargoes' do - let(:visibility_during_embargo) { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE } - let(:visibility_after_embargo) { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PUBLIC } - - before do - publication.apply_embargo(Date.tomorrow.to_s, visibility_during_embargo, visibility_after_embargo) - publication.embargo_release_date = release_date +RSpec.describe Spot::EmbargoLeaseService, valkyrization: true do + # testing on a Publication, but theoretically this should behave the same on all resources. + # since we're not validating the resources, we don't really need metadata. + let(:original_resource) { FactoryBot.valkyrie_create(:publication_resource_with_required_fields_only) } + let(:resource) { Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: original_resource.id) } + let(:embargo) { FactoryBot.valkyrie_create(:embargo, embargo_release_date: embargo_release_date) } + let(:lease) { FactoryBot.valkyrie_create(:lease, lease_expiration_date: lease_expiration_date) } + let(:embargo_release_date) { DateTime.now.utc + 1.day } + let(:lease_expiration_date) { DateTime.now.utc + 1.day } - # need to skip validation so that we can back-date the embargo - publication.save(validate: false) - end - - context 'with an expired embargo' do - let(:release_date) { Date.yesterday.to_s } + describe '.clear_all_expired' do + describe 'embargoes' do + # @note In Hyrax < 5.0.1 there's not a way to use the Wings persister without triggering + # ActiveFedora validations (which will ultimately not allow an Embargo to be applied + # with a date in the past), so instead we'll cast the Resource + Embargo/Leases to + # ActiveFedora objects and assign the Embargo/Lease that way, allowing us to call + # #save(validate: false). + # + # @todo When we upgrade to Hyrax 5.0.1, we should be able to replace this block with: + # before do + # resource.embargo = embargo + # Hyrax.persister.save(resource: resource, perform_af_validation: false) + # end + before do + af_resource = Hyrax.persister.resource_factory.from_resource(resource: resource) + af_embargo = Hyrax.persister.resource_factory.from_resource(resource: embargo) + af_resource.visibility = af_embargo.visibility_during_embargo + af_resource.embargo = af_embargo + af_resource.save(validate: false) + end - it "updates the item's visibility" do - expect(publication.visibility).to eq visibility_during_embargo + after do + Hyrax.persister.delete(resource: original_resource) + end - described_class.clear_expired_embargoes + context 'with an active embargo' do + let(:embargo_release_date) { DateTime.now.utc + 1.day } - # the same as +publication.reload+ but should be compatible with Wings - expect(Publication.find(publication.id).visibility).to eq visibility_after_embargo + it 'does nothing' do + expect { described_class.clear_all_expired } + .not_to change { Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: resource.id).visibility } + end end - end - context 'with an active embargo' do - let(:release_date) { Date.tomorrow.to_s } + context 'with an expired embargo' do + let(:embargo_release_date) { DateTime.now.utc - 1.day } - it 'does nothing' do - expect(publication.visibility).to eq visibility_during_embargo + it "updates the item's visibility" do + expect { described_class.clear_all_expired } + .to change { Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: resource.id).visibility } + .from(embargo.visibility_during_embargo) + .to(embargo.visibility_after_embargo) + end - described_class.clear_expired_embargoes + context 'for a resource with children' do + let(:resource) { Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: original_resource.id) } + let(:child) { FactoryBot.valkyrie_create(:publication_resource_with_required_fields_only) } + let(:child_resource) { Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: child.id) } - pub = Publication.find(publication.id) - expect(pub.visibility).not_to eq visibility_after_embargo - expect(pub.visibility).to eq visibility_during_embargo - end - end - end + before do + allow(Hyrax::AccessControlList).to receive(:copy_permissions) - describe '.clear_expired_leases' do - let(:visibility_during_lease) { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PUBLIC } - let(:visibility_after_lease) { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE } + resource.member_ids << child_resource.id + Hyrax.persister.save(resource: resource) + end - before do - publication.apply_lease(Date.tomorrow.to_s, visibility_during_lease, visibility_after_lease) - publication.lease_expiration_date = release_date - publication.save(validate: false) - end - - context 'with an expired embargo' do - let(:release_date) { Date.yesterday.to_s } - - it "updates the item's visibility" do - expect(publication.visibility).to eq visibility_during_lease + # @todo having difficulty getting children to save reliably to test? + skip 'calls Hyrax::AccessControlList.copy_permissions for each child' do + expect(Hyrax.query_service.find_members(resource: resource).count).not_to be_zero - described_class.clear_expired_leases + described_class.clear_all_expired - expect(Publication.find(publication.id).visibility).to eq visibility_after_lease + expect(Hyrax::AccessControlList) + .to have_received(:copy_permissions) + .with(source: resource, target: child_resource) + .exactly(1).time + end + end end end - context "when a lease isn't expired yet" do - let(:release_date) { Date.tomorrow.to_s } - - it 'does nothing' do - expect(publication.visibility).to eq visibility_during_lease + describe 'leases' do + # @see above before block + before do + af_resource = Hyrax.persister.resource_factory.from_resource(resource: resource) + af_lease = Hyrax.persister.resource_factory.from_resource(resource: lease) + af_resource.lease = af_lease + af_resource.visibility = af_lease.visibility_during_lease + af_resource.save(validate: false) + end - described_class.clear_expired_leases + context 'with an expired lease' do + let(:lease_expiration_date) { DateTime.now.utc - 1.day } - pub = Publication.find(publication.id) - expect(pub.visibility).not_to eq visibility_after_lease - expect(pub.visibility).to eq visibility_during_lease + it "updates the item's visibility" do + expect { described_class.clear_all_expired } + .to change { Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: resource.id).visibility } + .from(lease.visibility_during_lease) + .to(lease.visibility_after_lease) + end end - end - end - # we just want to be sure that the methods are being called, so - # let's just mock everything - describe '.clear_all_expired' do - let(:embargoed_presenter) { instance_double(Hyrax::WorkShowPresenter, id: 'abc123def') } - let(:leased_presenter) { instance_double(Hyrax::WorkShowPresenter, id: 'def456ghi') } - let(:publication_double) { instance_double(Publication, copy_visibility_to_files: true, save!: true) } - let(:embargoed_actor_double) { instance_double(Hyrax::Actors::EmbargoActor, destroy: true) } - let(:leased_actor_double) { instance_double(Hyrax::Actors::LeaseActor, destroy: true) } - let(:todays_date) { Time.zone.now.strftime('%Y-%m-%d') } - - before do - allow(Hyrax::EmbargoService) - .to receive(:assets_with_expired_embargoes) - .and_return([embargoed_presenter]) - - allow(Hyrax::LeaseService) - .to receive(:assets_with_expired_leases) - .and_return([leased_presenter]) - - allow(ActiveFedora::Base) - .to receive(:find) - .with(embargoed_presenter.id) - .and_return(publication_double) - - allow(ActiveFedora::Base) - .to receive(:find) - .with(leased_presenter.id) - .and_return(publication_double) - - allow(publication_double).to receive(:under_embargo?) - allow(publication_double).to receive(:active_lease?) - - allow(Hyrax::Actors::EmbargoActor) - .to receive(:new) - .with(publication_double) - .and_return(embargoed_actor_double) - - allow(Hyrax::Actors::LeaseActor) - .to receive(:new) - .with(publication_double) - .and_return(leased_actor_double) - end + context 'with an active lease' do + let(:lease_expiration_date) { DateTime.now.utc + 1.day } - it 'calls destroy on both the embargo + lease actors' do - described_class.clear_all_expired - - expect(embargoed_actor_double).to have_received(:destroy).exactly(1).time - expect(leased_actor_double).to have_received(:destroy).exactly(1).time + it 'does nothing' do + expect { described_class.clear_all_expired } + .not_to change { Hyrax.query_service.find_by_alternate_identifier(alternate_identifier: resource.id).visibility } + end + end end end end From bea510dc3b0195c53743b7f783193246daecccd7 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Tue, 28 Oct 2025 11:27:48 -0400 Subject: [PATCH 016/116] factories embargo-lease mostly passes now --- .../{ => spot}/base_resource_behavior.rb | 0 spec/factories/audio_visual_resource.rb | 12 ++ spec/factories/base_resource.rb | 53 ++++++++ spec/factories/base_work_resource.rb | 49 ++++++++ spec/factories/embargo_and_lease.rb | 14 +++ spec/factories/hyrax/admin_sets.rb | 37 ++++++ spec/factories/hyrax/administrative_sets.rb | 58 +++++++++ spec/factories/hyrax/object_id.rb | 13 ++ .../hyrax/permission_template_accesses.rb | 22 ++++ spec/factories/hyrax/permission_templates.rb | 116 ++++++++++++++++++ spec/factories/hyrax/workflow_actions.rb | 12 ++ spec/factories/hyrax/workflow_states.rb | 12 ++ spec/factories/hyrax/workflows.rb | 12 ++ spec/factories/image_resource.rb | 12 ++ spec/factories/publication_resource.rb | 13 ++ spec/factories/schema_traits.rb | 84 +++++++++++++ spec/factories/student_work_resource.rb | 17 +++ spec/spec_helper.rb | 5 + 18 files changed, 541 insertions(+) rename app/models/concerns/{ => spot}/base_resource_behavior.rb (100%) create mode 100644 spec/factories/audio_visual_resource.rb create mode 100644 spec/factories/base_resource.rb create mode 100644 spec/factories/base_work_resource.rb create mode 100644 spec/factories/embargo_and_lease.rb create mode 100644 spec/factories/hyrax/admin_sets.rb create mode 100644 spec/factories/hyrax/administrative_sets.rb create mode 100644 spec/factories/hyrax/object_id.rb create mode 100644 spec/factories/hyrax/permission_template_accesses.rb create mode 100644 spec/factories/hyrax/permission_templates.rb create mode 100644 spec/factories/hyrax/workflow_actions.rb create mode 100644 spec/factories/hyrax/workflow_states.rb create mode 100644 spec/factories/hyrax/workflows.rb create mode 100644 spec/factories/image_resource.rb create mode 100644 spec/factories/publication_resource.rb create mode 100644 spec/factories/schema_traits.rb create mode 100644 spec/factories/student_work_resource.rb diff --git a/app/models/concerns/base_resource_behavior.rb b/app/models/concerns/spot/base_resource_behavior.rb similarity index 100% rename from app/models/concerns/base_resource_behavior.rb rename to app/models/concerns/spot/base_resource_behavior.rb diff --git a/spec/factories/audio_visual_resource.rb b/spec/factories/audio_visual_resource.rb new file mode 100644 index 000000000..82e9fa428 --- /dev/null +++ b/spec/factories/audio_visual_resource.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +FactoryBot.define do + factory :audio_visual_resource_with_required_fields_only, parent: :base_work_resource, class: 'AudioVisualResource' do + date { [Time.zone.now.strftime('%Y-%m-%d')] } + resource_type { ['Other'] } + rights_statement { ['http://creativecommons.org/publicdomain/mark/1.0/'] } + end + + factory :audio_visual_resource, + parent: :audio_visual_resource_with_required_fields_only, + traits: [:base_metadata, :audio_visual_metadata] +end diff --git a/spec/factories/base_resource.rb b/spec/factories/base_resource.rb new file mode 100644 index 000000000..d4b3ec837 --- /dev/null +++ b/spec/factories/base_resource.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true +# +# Abstracts common resource traits into a separate factory for inheritance, namely visibility +# settings. Works should inherit from :base_work_resource (@see spec/factories/base_work_resource.rb) +# which adds support for attaching `Hyrax::FileSet`s to works. +# +# @see https://github.com/samvera/hyrax/blob/hyrax-v3.6.0/spec/factories/hyrax_work.rb +FactoryBot.define do + factory :base_resource, traits: [:core_metadata], class: 'Hyrax::Resource' do + transient do + with_index { true } + visibility_setting { nil } + end + + after :build do |work, evaluator| + work.visibility = evaluator.visibility_setting if evaluator.visibility_setting + end + + after :create do |work, evaluator| + if evaluator.visibility_setting + work.visibility = evaluator.visibility_setting + work.permission_manager.acl.save + end + + Hyrax.index_adapter.save(resource: work) if evaluator.with_index + end + + trait :public do + transient do + visibility_setting { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PUBLIC } + end + end + + trait :lafayette_only do + transient do + visibility_setting { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_AUTHENTICATED } + end + end + + # @todo does this work? + trait :metadata_only do + transient do + visibility_setting { 'metadata' } + end + end + + trait :private do + transient do + visibility_setting { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE } + end + end + end +end diff --git a/spec/factories/base_work_resource.rb b/spec/factories/base_work_resource.rb new file mode 100644 index 000000000..df9dc75de --- /dev/null +++ b/spec/factories/base_work_resource.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true +# +# Common traits for work-classes (file_set handling in particular). It's recommended to +# inherit work resource factories from this factory, rather than :base_resource. Exposes +# an :include_file_sets trait that can either be `true` or an +# +# +# @example +# FactoryBot.define do +# factory :book_resource, parent: :base_work_resource, class: 'BookResource' do +# # ... +# end +# end +# +# @example Creating a work with a file_set (depositor is new user) +# RSpec.describe do +# let(:work_with_file_set) { FactoryBot.valkyrie_create(:work_resource, include_file_sets: true) } +# end +# +# @example Creating a work with a specific file +# RSpec.describe do +# let(:fixture_path) { Rails.root.join('spec', 'fixtures', 'a_fixture.pdf') } +# let(:work_with_file_set) { FactoryBot.valkryie_create(:work_resource, include_file_sets: [fixture_path]) } +# end +# +FactoryBot.define do + factory :base_work_resource, parent: :base_resource, class: 'Hyrax::Work' do + transient do + include_file_sets { nil } + end + + trait :with_file_set do + include_file_sets { true } + end + + after(:create) do |work, evaluator| + next unless evaluator.include_file_sets + + fixture_path = Rails.root.join('spec', 'fixtures', 'image_transcript.vtt') # @todo we need a more universal name for this file + files = evaluator.include_file_sets == true ? Array.wrap(fixture_path) : Array.wrap(evaluator.include_file_sets) + depositor = User.find_by(email: work.depositor) || FactoryBot.create(:user) + + files.each do |path| + uploaded_file = Hyrax::UploadedFile.create!(file: File.open(path), user: depositor) + AttachFilesToWorkJob.perform_now(work, [uploaded_file]) + end + end + end +end diff --git a/spec/factories/embargo_and_lease.rb b/spec/factories/embargo_and_lease.rb new file mode 100644 index 000000000..4ce99f78e --- /dev/null +++ b/spec/factories/embargo_and_lease.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true +FactoryBot.define do + factory :embargo, class: 'Hyrax::Embargo' do + visibility_during_embargo { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE } + visibility_after_embargo { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PUBLIC } + embargo_release_date { DateTime.now.utc + 1.day } + end + + factory :lease, class: 'Hyrax::Lease' do + visibility_during_lease { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PUBLIC } + visibility_after_lease { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE } + lease_expiration_date { DateTime.now.utc + 1.day } + end +end diff --git a/spec/factories/hyrax/admin_sets.rb b/spec/factories/hyrax/admin_sets.rb new file mode 100644 index 000000000..b59784939 --- /dev/null +++ b/spec/factories/hyrax/admin_sets.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true +# +# Borrowing factories from Hyrax source, as the files aren't available in the gem. +# +# @todo When upgrading to Hyrax 5.1.0, remove the files in spec/factories/hyrax and replace them +# with relevant calls to `require 'hyrax/specs/shared_specs/factories/factory.rb` in spec_helper.rb +FactoryBot.define do + factory :admin_set do + sequence(:title) { |n| ["Title #{n}"] } + + # Given the relationship between permission template and admin set, when + # an admin set is created via a factory, I believe it is appropriate to go ahead and + # create the corresponding permission template + # + # This way, we can go ahead + after(:create) do |admin_set, evaluator| + if evaluator.with_permission_template + attributes = { source_id: admin_set.id } + attributes = evaluator.with_permission_template.merge(attributes) if evaluator.with_permission_template.respond_to?(:merge) + # There is a unique constraint on permission_templates.source_id; I don't want to + # create a permission template if one already exists for this admin_set + create(:permission_template, attributes) unless Hyrax::PermissionTemplate.find_by(source_id: admin_set.id) + end + end + + transient do + # false, true, or Hash with keys for permission_template + with_permission_template { false } + end + + factory :complete_admin_set do + alternative_title { ['alternative admin set title'] } + creator { ['moomin', 'snufkin'] } + description { ['Before a revolution happens', 'it is perceived as impossible'] } + end + end +end diff --git a/spec/factories/hyrax/administrative_sets.rb b/spec/factories/hyrax/administrative_sets.rb new file mode 100644 index 000000000..52ac00236 --- /dev/null +++ b/spec/factories/hyrax/administrative_sets.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true +# +# Borrowing factories from Hyrax source, as the files aren't available in the gem. +# +# @todo When upgrading to Hyrax 5.1.0, remove the files in spec/factories/hyrax and replace them +# with relevant calls to `require 'hyrax/specs/shared_specs/factories/factory.rb` in spec_helper.rb +FactoryBot.define do + factory :hyrax_admin_set, class: 'Hyrax::AdministrativeSet' do + title { ['My Admin Set'] } + + transient do + with_permission_template { false } + user { create(:user) } + access_grants { [] } + with_index { true } + end + + after(:build) do |adminset, evaluator| + adminset.creator = [evaluator.user.user_key] + end + + after(:create) do |admin_set, evaluator| + if evaluator.with_permission_template + template = Hyrax::PermissionTemplate.find_or_create_by(source_id: admin_set.id.to_s) + evaluator.access_grants.each do |grant| + Hyrax::PermissionTemplateAccess.find_or_create_by(permission_template_id: template.id, + agent_type: grant[:agent_type], + agent_id: grant[:agent_id], + access: grant[:access]) + end + Hyrax::PermissionTemplateAccess.find_or_create_by(permission_template_id: template.id, + agent_type: Hyrax::PermissionTemplateAccess::USER, + agent_id: evaluator.user.user_key, + access: Hyrax::PermissionTemplateAccess::MANAGE) + template.reset_access_controls_for(collection: admin_set) + end + Hyrax.index_adapter.save(resource: admin_set) if evaluator.with_index + end + end + + factory :invalid_hyrax_admin_set, class: 'Hyrax::AdministrativeSet' do + # Title is required. Without title, the admin set is invalid. + end + + factory :default_hyrax_admin_set, class: 'Hyrax::AdministrativeSet' do + id { Hyrax::AdminSetCreateService::DEFAULT_ID } + title { Hyrax::AdminSetCreateService::DEFAULT_TITLE } + + transient do + with_persisted_default_id { true } + end + + after(:create) do |admin_set, evaluator| + Hyrax::DefaultAdministrativeSet.update(default_admin_set_id: admin_set.id) if + evaluator.with_persisted_default_id + end + end +end diff --git a/spec/factories/hyrax/object_id.rb b/spec/factories/hyrax/object_id.rb new file mode 100644 index 000000000..56a408aa9 --- /dev/null +++ b/spec/factories/hyrax/object_id.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true +# +# Borrowing factories from Hyrax source, as the files aren't available in the gem. +# +# @todo When upgrading to Hyrax 5.1.0, remove the files in spec/factories/hyrax and replace them +# with relevant calls to `require 'hyrax/specs/shared_specs/factories/factory.rb` in spec_helper.rb +# +# Defines a new sequence +FactoryBot.define do + sequence :object_id do |n| + "object_id_#{n}" + end +end diff --git a/spec/factories/hyrax/permission_template_accesses.rb b/spec/factories/hyrax/permission_template_accesses.rb new file mode 100644 index 000000000..b6f1adc94 --- /dev/null +++ b/spec/factories/hyrax/permission_template_accesses.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true +# +# Borrowing factories from Hyrax source, as the files aren't available in the gem. +# +# @todo When upgrading to Hyrax 5.1.0, remove the files in spec/factories/hyrax and replace them +# with relevant calls to `require 'hyrax/specs/shared_specs/factories/factory.rb` in spec_helper.rb +FactoryBot.define do + factory :permission_template_access, class: Hyrax::PermissionTemplateAccess do + permission_template + trait :manage do + access { 'manage' } + end + + trait :deposit do + access { 'deposit' } + end + + trait :view do + access { 'view' } + end + end +end diff --git a/spec/factories/hyrax/permission_templates.rb b/spec/factories/hyrax/permission_templates.rb new file mode 100644 index 000000000..f9c768ac1 --- /dev/null +++ b/spec/factories/hyrax/permission_templates.rb @@ -0,0 +1,116 @@ +# frozen_string_literal: true +# +# Borrowing factories from Hyrax source, as the files aren't available in the gem. +# +# @todo When upgrading to Hyrax 5.1.0, remove the files in spec/factories/hyrax and replace them +# with relevant calls to `require 'hyrax/specs/shared_specs/factories/factory.rb` in spec_helper.rb +FactoryBot.define do + factory :permission_template, class: Hyrax::PermissionTemplate do + # Given that there is a one to one strong relation between permission_template and admin_set, + # with a unique index on the source_id, I don't want to have duplication in source_id + sequence(:source_id) { |n| format("%010d", n) } + + trait :with_immediate_release do + release_period { Hyrax::PermissionTemplate::RELEASE_TEXT_VALUE_NO_DELAY } + end + + trait :with_delayed_release do + release_period { Hyrax::PermissionTemplate::RELEASE_TEXT_VALUE_6_MONTHS } + end + + before(:create) do |permission_template, evaluator| + if evaluator.with_admin_set + source_id = permission_template.source_id + admin_set = SourceFinder.find_or_create_admin_set(source_id) + permission_template.source_id = admin_set.id + elsif evaluator.with_collection + source_id = permission_template.source_id + collection = + if source_id.present? + begin + Collection.find(source_id) + rescue ActiveFedora::ObjectNotFoundError + create(:collection, id: source_id) + end + else + create(:collection) + end + permission_template.source_id = collection.id + end + end + + after(:create) do |permission_template, evaluator| + if evaluator.with_workflows + Hyrax::Workflow::WorkflowImporter.load_workflow_for(permission_template: permission_template) + Sipity::Workflow.activate!(permission_template: permission_template, workflow_id: permission_template.available_workflows.pick(:id)) + end + if evaluator.with_active_workflow + workflow = create(:workflow, active: true, permission_template: permission_template) + create(:workflow_action, workflow: workflow) # Need to create a single action that can be taken + end + AccessHelper.create_access(permission_template, 'user', :manage, evaluator.manage_users) if evaluator.manage_users.present? + AccessHelper.create_access(permission_template, 'group', :manage, evaluator.manage_groups) if evaluator.manage_groups.present? + AccessHelper.create_access(permission_template, 'user', :deposit, evaluator.deposit_users) if evaluator.deposit_users.present? + AccessHelper.create_access(permission_template, 'group', :deposit, evaluator.deposit_groups) if evaluator.deposit_groups.present? + AccessHelper.create_access(permission_template, 'user', :view, evaluator.view_users) if evaluator.view_users.present? + AccessHelper.create_access(permission_template, 'group', :view, evaluator.view_groups) if evaluator.view_groups.present? + end + + transient do + with_admin_set { false } + with_collection { false } + with_workflows { false } + with_active_workflow { false } + manage_users { nil } + manage_groups { nil } + deposit_users { nil } + deposit_groups { nil } + view_users { nil } + view_groups { nil } + end + end + + class AccessHelper + def self.create_access(permission_template_id, agent_type, access, agent_ids) + agent_ids.each do |agent_id| + FactoryBot.create(:permission_template_access, + access, + permission_template: permission_template_id, + agent_type: agent_type, + agent_id: agent_id) + end + end + end + + class SourceFinder + def self.find_or_create_admin_set(source_id) + Hyrax.config.use_valkyrie? ? find_or_create_admin_set_valkyrie(source_id) : find_or_create_admin_set_active_fedora(source_id) + end + + def self.find_or_create_admin_set_active_fedora(source_id) + if source_id.present? + begin + AdminSet.find(source_id) + rescue ActiveFedora::ObjectNotFoundError + FactoryBot.create(:admin_set, id: source_id) + end + else + FactoryBot.create(:admin_set) + end + end + + def self.find_or_create_admin_set_valkyrie(source_id) + if source_id.present? + begin + Hyrax.query_service.find_by(id: source_id) + rescue Valkyrie::Persistence::ObjectNotFoundError + # Creating an Administrative set with a pre-determined id will not work for all adapters + # so we're letting the adapter assign the id + FactoryBot.valkyrie_create(:hyrax_admin_set) + end + else + FactoryBot.valkyrie_create(:hyrax_admin_set) + end + end + end +end diff --git a/spec/factories/hyrax/workflow_actions.rb b/spec/factories/hyrax/workflow_actions.rb new file mode 100644 index 000000000..4132584cd --- /dev/null +++ b/spec/factories/hyrax/workflow_actions.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +# +# Borrowing factories from Hyrax source, as the files aren't available in the gem. +# +# @todo When upgrading to Hyrax 5.1.0, remove the files in spec/factories/hyrax and replace them +# with relevant calls to `require 'hyrax/specs/shared_specs/factories/factory.rb` in spec_helper.rb +FactoryBot.define do + factory :workflow_action, class: Sipity::WorkflowAction do + workflow + name { 'submit' } + end +end diff --git a/spec/factories/hyrax/workflow_states.rb b/spec/factories/hyrax/workflow_states.rb new file mode 100644 index 000000000..823130363 --- /dev/null +++ b/spec/factories/hyrax/workflow_states.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +# +# Borrowing factories from Hyrax source, as the files aren't available in the gem. +# +# @todo When upgrading to Hyrax 5.1.0, remove the files in spec/factories/hyrax and replace them +# with relevant calls to `require 'hyrax/specs/shared_specs/factories/factory.rb` in spec_helper.rb +FactoryBot.define do + factory :workflow_state, class: Sipity::WorkflowState do + workflow + name { 'initial' } + end +end diff --git a/spec/factories/hyrax/workflows.rb b/spec/factories/hyrax/workflows.rb new file mode 100644 index 000000000..2dc0775f8 --- /dev/null +++ b/spec/factories/hyrax/workflows.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +# +# Borrowing factories from Hyrax source, as the files aren't available in the gem. +# +# @todo When upgrading to Hyrax 5.1.0, remove the files in spec/factories/hyrax and replace them +# with relevant calls to `require 'hyrax/specs/shared_specs/factories/factory.rb` in spec_helper.rb +FactoryBot.define do + factory :workflow, class: Sipity::Workflow do + sequence(:name) { |n| "generic_work-#{n}" } + permission_template + end +end diff --git a/spec/factories/image_resource.rb b/spec/factories/image_resource.rb new file mode 100644 index 000000000..dcfd84441 --- /dev/null +++ b/spec/factories/image_resource.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true +FactoryBot.define do + factory :image_resource_with_required_fields_only, parent: :base_work_resource, class: 'ImageResource' do + date { [Time.zone.now.strftime('%Y-%m-%d')] } + resource_type { ['Other'] } + rights_statement { ['http://creativecommons.org/publicdomain/mark/1.0/'] } + end + + factory :image_resource, + parent: :image_resource_with_required_fields_only, + traits: [:base_metadata, :image_metadata] +end diff --git a/spec/factories/publication_resource.rb b/spec/factories/publication_resource.rb new file mode 100644 index 000000000..5ed74868a --- /dev/null +++ b/spec/factories/publication_resource.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true +FactoryBot.define do + factory :publication_resource_with_required_fields_only, parent: :base_work_resource, class: 'PublicationResource' do + date_issued { [Time.zone.now.strftime('%Y-%m-%d')] } + resource_type { ['Other'] } + rights_statement { ['http://creativecommons.org/publicdomain/mark/1.0/'] } + end + + factory :publication_resource, + parent: :publication_resource_with_required_fields_only, + traits: [:base_metadata, :institutional_metadata, :publication_metadata] do + end +end diff --git a/spec/factories/schema_traits.rb b/spec/factories/schema_traits.rb new file mode 100644 index 000000000..6ab8d5ea7 --- /dev/null +++ b/spec/factories/schema_traits.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true +# +# Mixin traits to use when building factories for resources. +# Each trait mirrors the attributes of the respective schema +# in `config/metadata/{schema}.yaml` +FactoryBot.define do + trait :base_metadata do + bibliographic_citation { ['Lastname, First. Title of piece.'] } + contributor { ['Contributor, First-Name', 'Person, Another'] } + creator { ['Creator, Anne'] } + description { ['An account of the resource'] } + identifier { ['hdl:10385/abc123'] } + keyword { ['photo'] } + language { ['en'] } + location { ['http://sws.geonames.org/5188140/'] } + note { ['Some staff-side information'] } + physical_medium { ['Photograph'] } + publisher { ['Lafayette College'] } + related_resource { ['http://another-resource.com'] } + resource_type { ['Other'] } + rights_holder { ['Holder, R.'] } + rights_statement { ['http://creativecommons.org/publicdomain/mark/1.0/'] } + source { ['Lafayette College'] } + source_identifier { [] } + subject { ['http://id.worldcat.org/fast/1061714'] } + subtitle { ['An object to view'] } + title { ['A Fabulous Work'] } + title_alternative { ['An alternative title for the work.'] } + end + + trait :core_metadata do + title { |n| ["Title of work (#{n})"] } + date_modified { Time.now.utc } + date_uploaded { Time.now.utc } + depositor { 'repository@lafayette.edu' } + alternate_ids { [] } + end + + trait :image_metadata do + date { ['2024-11'] } + date_associated { ['2024'] } + date_scope_note { ['Printed information on the backs of postcards that provides information relevent to dating the postcard itself (not the image).'] } + donor { ['Alumnus, Anne Esteemed'] } + inscription { ['hey look over here'] } + original_item_extent { ['24 x 19.5 cm.'] } + repository_location { ['On that one shelf in the back'] } + requested_by { ['Requester, Jennifer Q.'] } + research_assistance { ['Student, Ashley'] } + subject_ocm { ['000 VALUE'] } + end + + trait :institutional_metadata do + academic_department { ['Art'] } + division { ['Humanities'] } + organization { ['Lafayette College'] } + end + + trait :publication_metadata do + abstract { ['A short description of the thing'] } + date_issued { [Time.zone.now.strftime('%Y-%m-%d')] } + date_available { ['2024-11-08'] } + editor { ['Sweeney, Mary'] } + license { ['This is some licensing text'] } + end + + trait :student_work_metadata do + abstract { ['A short description of the thing'] } + access_note { ['Here is how to access the thing'] } + advisor { ['Smartfellow, Jane'] } + date { ['2024-11-08'] } + date_available { ['2024-11-08'] } + end + + trait :audio_visual_metadata do + date { ['2024-11'] } + date_associated { ['2024'] } + inscription { ['hey look over here'] } + original_item_extent { ['24 x 19.5 cm.'] } + repository_location { ['On that one shelf in the back'] } + research_assistance { ['Student, Ashley'] } + provenance { ['Owner Information'] } + barcode { ['abc123'] } + end +end diff --git a/spec/factories/student_work_resource.rb b/spec/factories/student_work_resource.rb new file mode 100644 index 000000000..e79f7ceef --- /dev/null +++ b/spec/factories/student_work_resource.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true +FactoryBot.define do + factory :student_work_resource_with_required_fields_only, parent: :base_resource, class: 'StudentWorkResource' do + academic_department { ['Libraries'] } + advisor { ['Professor, Anne Esteemed'] } + creator { ['Creator, A.'] } + description { ['Description of StudentWork'] } + date { [Time.zone.now.strftime('%Y-%m-%d')] } + resource_type { ['Other'] } + rights_statement { ['http://creativecommons.org/publicdomain/mark/1.0/'] } + end + + factory :student_work_resource, + parent: :student_work_resource_with_required_fields_only, + traits: [:base_metadata, :institutional_metadata, :student_work_metadata] do + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9976ad1c0..8ef6e47ed 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -37,6 +37,11 @@ require 'equivalent-xml/rspec_matchers' require 'mail' +# FactoryBot setup borrowed from Hyrax +# @see https://github.com/samvera/hyrax/blob/hyrax-v3.6.0/spec/spec_helper.rb#L83-L88 +require 'hyrax/specs/shared_specs/factories/strategies/valkyrie_resource' +FactoryBot.register_strategy(:valkyrie_create, ValkyrieCreateStrategy) + Capybara.register_driver :selenium_firefox_headless do |app| browser_options = ::Selenium::WebDriver::Firefox::Options.new browser_options.binary = ENV['FIREFOX_BINARY_PATH'] if ENV['FIREFOX_BINARY_PATH'].present? From 38adf055dd2a59c2a56a308139ea818130231d15 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Tue, 28 Oct 2025 12:25:49 -0400 Subject: [PATCH 017/116] work_metadata_exporter --- app/services/spot/exporters/work_metadata_exporter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/spot/exporters/work_metadata_exporter.rb b/app/services/spot/exporters/work_metadata_exporter.rb index 8b193f338..6c5c24124 100644 --- a/app/services/spot/exporters/work_metadata_exporter.rb +++ b/app/services/spot/exporters/work_metadata_exporter.rb @@ -60,7 +60,7 @@ def generate_csv_content # @return [RDF::Graph] def graph - @graph ||= Hyrax::GraphExporter.new(solr_document, request).fetch + @graph ||= Hyrax::GraphExporter.new(solr_document, hostname: request.host).fetch end end end From e6a26a38d9274df65b642848f343fbde290750e3 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Tue, 28 Oct 2025 16:40:42 -0400 Subject: [PATCH 018/116] rdf upgrade solves a lot of testing issues --- Gemfile | 2 +- Gemfile.lock | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index 5092a44ba..4a68e4009 100644 --- a/Gemfile +++ b/Gemfile @@ -118,7 +118,7 @@ gem 'pg', '~> 1.5.4' # this is bundled somewhere within the hyrax stack, but since we're # calling it within our code we shouldn't expect it to always be there -gem 'rdf-vocab', '~> 3.2.7' +gem 'rdf-vocab', '~> 3.3.3' # solr client for interacting with rails (installed w/ hyrax) gem 'rsolr', '~> 2.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index e48629174..dafbe524e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -129,6 +129,7 @@ GEM base64 (0.3.0) bcp47 (0.3.3) i18n + bcp47_spec (0.2.1) bcrypt (3.1.20) bibtex-ruby (6.2.0) latex-decode (~> 0.0) @@ -573,6 +574,7 @@ GEM ice_nine (0.11.2) iiif_manifest (1.6.0) activesupport (>= 4) + io-console (0.8.1) iso-639 (0.3.8) csv iso8601 (0.9.1) @@ -834,8 +836,13 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rdf (3.2.12) + rdf (3.3.4) + bcp47_spec (~> 0.2) + bigdecimal (~> 3.1, >= 3.1.5) link_header (~> 0.0, >= 0.0.8) + logger (~> 1.5) + ostruct (~> 0.6) + readline (~> 0.0) rdf-aggregate-repo (3.2.1) rdf (~> 3.2) rdf-isomorphic (3.2.1) @@ -892,11 +899,13 @@ GEM rdf-turtle (3.2.1) ebnf (~> 2.3) rdf (~> 3.2) - rdf-vocab (3.2.7) - rdf (~> 3.2, >= 3.2.4) + rdf-vocab (3.3.3) + rdf (~> 3.3) rdf-xsd (3.2.1) rdf (~> 3.2) rexml (~> 3.2) + readline (0.0.4) + reline redic (1.5.3) hiredis redis (4.5.1) @@ -912,6 +921,8 @@ GEM activemodel (>= 5.0) reform (>= 2.3.1, < 3.0.0) regexp_parser (2.11.3) + reline (0.6.2) + io-console (~> 0.5) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) @@ -1196,7 +1207,7 @@ DEPENDENCIES puma (~> 6.4.0) rails (~> 6.1) rails-controller-testing (~> 1.0.5) - rdf-vocab (~> 3.2.7) + rdf-vocab (~> 3.3.3) redlock (>= 0.1.2, < 2.0) rsolr (~> 2.5.0) rspec (~> 3.10) From 609649d2255af0cff054ccaded0d8e5d2e5815d7 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Thu, 30 Oct 2025 11:13:47 -0400 Subject: [PATCH 019/116] partial fix for workflow --- .../services/spot/workflow/grant_sipity_role_to_advisor_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/services/spot/workflow/grant_sipity_role_to_advisor_spec.rb b/spec/services/spot/workflow/grant_sipity_role_to_advisor_spec.rb index 99bfd1621..c5335e1b0 100644 --- a/spec/services/spot/workflow/grant_sipity_role_to_advisor_spec.rb +++ b/spec/services/spot/workflow/grant_sipity_role_to_advisor_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Spot::Workflow::GrantSipityRoleToAdvisor do let(:workflow_role) { Sipity::WorkflowRole.find_or_create_by!(role: Sipity::Role[:advising], workflow: permission_template.active_workflow) } let(:advisors) { [create(:user), create(:user)] } - let(:admin_set) { AdminSet.find(AdminSet.find_or_create_default_admin_set_id) } + let(:admin_set) { Hyrax::AdminSetCreateService.find_or_create_default_admin_set } let(:default_access_grants) do [{ agent_type: 'group', agent_id: ::Ability.admin_group_name, access: Hyrax::PermissionTemplateAccess::MANAGE }] end From e66c3735bfccececaf953b6564d8b517b4219867 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Thu, 30 Oct 2025 13:28:09 -0400 Subject: [PATCH 020/116] should be all services but handle --- .../spot/characterization_service_spec.rb | 12 ++++++------ spec/services/spot/video_processor_spec.rb | 18 +++++++++--------- .../grant_sipity_role_to_advisor_spec.rb | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/spec/services/spot/characterization_service_spec.rb b/spec/services/spot/characterization_service_spec.rb index 80bb2166c..bcdad7d0f 100644 --- a/spec/services/spot/characterization_service_spec.rb +++ b/spec/services/spot/characterization_service_spec.rb @@ -9,7 +9,7 @@ before do allow(described_class).to receive(:run).with(proxy, filename).and_call_original - allow(described_class).to receive(:new).with(proxy, filename, ch12n_tool: tool).and_return(service_double) + allow(described_class).to receive(:new).with(proxy, filename, { ch12n_tool: tool }).and_return(service_double) end it 'is set to be the service for CharacterizeJob' do @@ -24,7 +24,7 @@ it 'passes the :fits_servlet tool to the initializer' do described_class.run(proxy, filename) - expect(described_class).to have_received(:new).with(proxy, filename, ch12n_tool: tool) + expect(described_class).to have_received(:new).with(proxy, filename, { ch12n_tool: tool }) end end @@ -34,7 +34,7 @@ it 'passes the :fits tool to the initializer' do described_class.run(proxy, filename) - expect(described_class).to have_received(:new).with(proxy, filename, ch12n_tool: tool) + expect(described_class).to have_received(:new).with(proxy, filename, { ch12n_tool: tool }) end end @@ -42,13 +42,13 @@ let(:tool) { :some_advanced_tool } before do - allow(described_class).to receive(:run).with(proxy, filename, ch12n_tool: tool).and_call_original + allow(described_class).to receive(:run).with(proxy, filename, { ch12n_tool: tool }).and_call_original end it 'uses the provided tool' do - described_class.run(proxy, filename, ch12n_tool: tool) + described_class.run(proxy, filename, { ch12n_tool: tool }) - expect(described_class).to have_received(:new).with(proxy, filename, ch12n_tool: tool) + expect(described_class).to have_received(:new).with(proxy, filename, { ch12n_tool: tool }) end end end diff --git a/spec/services/spot/video_processor_spec.rb b/spec/services/spot/video_processor_spec.rb index 964c85cec..79e52af5f 100644 --- a/spec/services/spot/video_processor_spec.rb +++ b/spec/services/spot/video_processor_spec.rb @@ -17,8 +17,8 @@ expect(subject) .to receive(:encode_file) .with("mp4", - Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec mpeg4 -acodec aac -strict -2 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "") + { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec mpeg4 -acodec aac -strict -2 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) subject.process end end @@ -31,8 +31,8 @@ expect(subject) .to receive(:encode_file) .with("webm", - Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec libvpx -acodec libvorbis -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "") + { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec libvpx -acodec libvorbis -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) subject.process end end @@ -41,7 +41,7 @@ let(:directives) { { label: :thumb, format: 'jpg', url: 'http://localhost:8983/fedora/rest/dev/1234/thumbnail' } } it "creates a fedora resource and infers the name" do - expect(subject).to receive(:encode_file).with("jpg", output_options: "-s 320x240 -vcodec mjpeg -vframes 1 -an -f rawvideo", input_options: " -itsoffset -2") + expect(subject).to receive(:encode_file).with("jpg", { output_options: "-s 320x240 -vcodec mjpeg -vframes 1 -an -f rawvideo", input_options: " -itsoffset -2" }) subject.process end end @@ -54,8 +54,8 @@ expect(subject) .to receive(:encode_file) .with("mkv", - Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec ffv1 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "test_input_options") + { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec ffv1 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "test_input_options" }) subject.process end end @@ -67,8 +67,8 @@ expect(subject) .to receive(:encode_file) .with("mkv", - Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec ffv1 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "") + { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec ffv1 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) subject.process end end diff --git a/spec/services/spot/workflow/grant_sipity_role_to_advisor_spec.rb b/spec/services/spot/workflow/grant_sipity_role_to_advisor_spec.rb index c5335e1b0..b86212c62 100644 --- a/spec/services/spot/workflow/grant_sipity_role_to_advisor_spec.rb +++ b/spec/services/spot/workflow/grant_sipity_role_to_advisor_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Spot::Workflow::GrantSipityRoleToAdvisor do let(:workflow_role) { Sipity::WorkflowRole.find_or_create_by!(role: Sipity::Role[:advising], workflow: permission_template.active_workflow) } let(:advisors) { [create(:user), create(:user)] } - let(:admin_set) { Hyrax::AdminSetCreateService.find_or_create_default_admin_set } + let(:admin_set) { AdminSet.find(Hyrax::AdminSetCreateService.find_or_create_default_admin_set.id) } let(:default_access_grants) do [{ agent_type: 'group', agent_id: ::Ability.admin_group_name, access: Hyrax::PermissionTemplateAccess::MANAGE }] end From e89bf51a6568fa778a9cbe0430b29f3f05aad493 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Thu, 30 Oct 2025 13:40:27 -0400 Subject: [PATCH 021/116] rubo --- spec/services/spot/video_processor_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/services/spot/video_processor_spec.rb b/spec/services/spot/video_processor_spec.rb index 79e52af5f..c4d15f4c8 100644 --- a/spec/services/spot/video_processor_spec.rb +++ b/spec/services/spot/video_processor_spec.rb @@ -18,7 +18,7 @@ .to receive(:encode_file) .with("mp4", { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec mpeg4 -acodec aac -strict -2 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) subject.process end end @@ -32,7 +32,7 @@ .to receive(:encode_file) .with("webm", { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec libvpx -acodec libvorbis -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) subject.process end end @@ -55,7 +55,7 @@ .to receive(:encode_file) .with("mkv", { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec ffv1 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "test_input_options" }) + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "test_input_options" }) subject.process end end @@ -68,7 +68,7 @@ .to receive(:encode_file) .with("mkv", { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec ffv1 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) subject.process end end From 4ef76ede4b4ba138d5a330a9b95ee2997b5efdcf Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Fri, 31 Oct 2025 14:17:42 -0400 Subject: [PATCH 022/116] use hdl.handle.net for handle service specs --- spec/services/spot/handle_service_spec.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spec/services/spot/handle_service_spec.rb b/spec/services/spot/handle_service_spec.rb index 5ced89292..b646843c0 100644 --- a/spec/services/spot/handle_service_spec.rb +++ b/spec/services/spot/handle_service_spec.rb @@ -2,9 +2,14 @@ RSpec.describe Spot::HandleService do subject(:service) { described_class.new(work) } + # @note This isn't a real-world example of a working Handle server URL, + # but WebMock requires a mocked URL to resolve to a real host/port + # combination, and this will get tests passing. + # @see https://github.com/bblimke/webmock/issues/955 + let(:handle_server_url) { 'http://hdl.handle.net' } + let(:work) { instance_double(Publication, id: 'abc123def', identifier: identifiers) } let(:identifiers) { [] } - let(:handle_server_url) { 'http://handle-service:8000' } let(:handle_prefix) { '10385' } before do From 7af1860e97c98cf92ead24b765c25da378e8e35d Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Tue, 4 Nov 2025 08:02:21 -0500 Subject: [PATCH 023/116] fix call to deprecated gid method --- spec/models/collection_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb index 4cfea7f52..8561122c2 100644 --- a/spec/models/collection_spec.rb +++ b/spec/models/collection_spec.rb @@ -7,7 +7,7 @@ let(:params) { base_params } let(:base_params) { { title: ['ok'], collection_type_gid: collection_type_gid } } let(:collection_type) { Hyrax::CollectionType.find_or_create_by(title: 'a cool collection type') } - let(:collection_type_gid) { collection_type.gid } + let(:collection_type_gid) { collection_type.to_global_id } it_behaves_like 'a model with hyrax core metadata' From 9050d441f2bd0697beb0a25a4ec64b5b0c3bd29c Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Tue, 4 Nov 2025 09:38:26 -0500 Subject: [PATCH 024/116] drop reindex_extent --- app/actors/spot/actors/collections_membership_actor.rb | 2 -- app/models/concerns/spot/nested_collection_behavior.rb | 1 - 2 files changed, 3 deletions(-) diff --git a/app/actors/spot/actors/collections_membership_actor.rb b/app/actors/spot/actors/collections_membership_actor.rb index 22f414f76..da6102090 100644 --- a/app/actors/spot/actors/collections_membership_actor.rb +++ b/app/actors/spot/actors/collections_membership_actor.rb @@ -26,8 +26,6 @@ def add(env, id) col = collection_stack.shift next if collection_ids.include?(col.id) - col.reindex_extent = Hyrax::Adapters::NestingIndexAdapter::LIMITED_REINDEX - collection_ids << col.id env.curation_concern.member_of_collections << col collection_stack += col.member_of_collections diff --git a/app/models/concerns/spot/nested_collection_behavior.rb b/app/models/concerns/spot/nested_collection_behavior.rb index 631ddb6ba..437695860 100644 --- a/app/models/concerns/spot/nested_collection_behavior.rb +++ b/app/models/concerns/spot/nested_collection_behavior.rb @@ -58,7 +58,6 @@ def gather_collections_to_add [].tap do |collections| until collections_to_check.size.zero? col = collections_to_check.shift - col.reindex_extent = Hyrax::Adapters::NestingIndexAdapter::LIMITED_REINDEX collections << col unless collections.include?(col) collections_to_check += col.member_of_collections From 4c2cde5d3cc3221d60302bfdf5a5479c1a161003 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Tue, 4 Nov 2025 14:58:00 -0500 Subject: [PATCH 025/116] fix controlled vocab models --- spec/models/spot/controlled_vocabularies/base_spec.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/spec/models/spot/controlled_vocabularies/base_spec.rb b/spec/models/spot/controlled_vocabularies/base_spec.rb index de08b405d..1324350ab 100644 --- a/spec/models/spot/controlled_vocabularies/base_spec.rb +++ b/spec/models/spot/controlled_vocabularies/base_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe Spot::ControlledVocabularies::Base do let(:resource) { described_class.new(uri) } - let(:uri) { RDF::URI('http://id.loc.gov/authorities/subjects/sh85062079') } + let(:uri) { RDF::URI('http://id.loc.gov/authorities/subjects/sh85062079').to_s } let(:label_en) { RDF::Literal('Horror in art', language: :en) } let(:label_de) { RDF::Literal('Schrecken ', language: :de) } let(:labels) { [label_en, label_de] } @@ -34,8 +34,6 @@ # 3 tries, minus the first try expect(resource).to have_received(:sleep).exactly(2).times end - - it_behaves_like 'it logs a warning' end context 'when a label has not been cached but others exist' do From 4fb639c69d38eb0fcba96fd9a40c6076094dcc0d Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Wed, 5 Nov 2025 10:51:31 -0500 Subject: [PATCH 026/116] development host and minor feature spec fixes --- config/environments/development.rb | 2 ++ spec/features/create_audio_visual_spec.rb | 2 +- spec/features/create_image_spec.rb | 2 +- spec/features/create_publication_spec.rb | 2 +- spec/features/create_student_work_spec.rb | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/config/environments/development.rb b/config/environments/development.rb index 6ec30402a..8b21e8e69 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true Rails.application.configure do + config.hosts << "ldr-future.lafayette.edu" + config.public_file_server.enabled = true # Settings specified here will take precedence over those in config/application.rb. diff --git a/spec/features/create_audio_visual_spec.rb b/spec/features/create_audio_visual_spec.rb index b2eb37cf2..75328d8b4 100644 --- a/spec/features/create_audio_visual_spec.rb +++ b/spec/features/create_audio_visual_spec.rb @@ -8,7 +8,7 @@ # (h/t: https://github.com/curationexperts/mahonia/blob/89b036c/spec/features/access_etd_spec.rb#L9-L10) ActiveJob::Base.queue_adapter.filter = [IngestJob] - AdminSet.find_or_create_default_admin_set_id + Hyrax::AdminSetCreateService.find_or_create_default_admin_set login_as user end diff --git a/spec/features/create_image_spec.rb b/spec/features/create_image_spec.rb index fe03c14e5..6197dfc73 100644 --- a/spec/features/create_image_spec.rb +++ b/spec/features/create_image_spec.rb @@ -9,7 +9,7 @@ # (h/t: https://github.com/curationexperts/mahonia/blob/89b036c/spec/features/access_etd_spec.rb#L9-L10) ActiveJob::Base.queue_adapter.filter = [IngestJob] - AdminSet.find_or_create_default_admin_set_id + Hyrax::AdminSetCreateService.find_or_create_default_admin_set login_as user end diff --git a/spec/features/create_publication_spec.rb b/spec/features/create_publication_spec.rb index c0bcb8633..30c818b8e 100644 --- a/spec/features/create_publication_spec.rb +++ b/spec/features/create_publication_spec.rb @@ -8,7 +8,7 @@ # (h/t: https://github.com/curationexperts/mahonia/blob/89b036c/spec/features/access_etd_spec.rb#L9-L10) ActiveJob::Base.queue_adapter.filter = [IngestJob] - AdminSet.find_or_create_default_admin_set_id + Hyrax::AdminSetCreateService.find_or_create_default_admin_set login_as user end diff --git a/spec/features/create_student_work_spec.rb b/spec/features/create_student_work_spec.rb index a53526b10..d884abe42 100644 --- a/spec/features/create_student_work_spec.rb +++ b/spec/features/create_student_work_spec.rb @@ -8,7 +8,7 @@ # (h/t: https://github.com/curationexperts/mahonia/blob/89b036c/spec/features/access_etd_spec.rb#L9-L10) ActiveJob::Base.queue_adapter.filter = [IngestJob] - AdminSet.find_or_create_default_admin_set_id + Hyrax::AdminSetCreateService.find_or_create_default_admin_set login_as user end From 9bb3b61476afb5f7b7989848affa9e9113e26057 Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Tue, 4 Nov 2025 12:45:37 -0500 Subject: [PATCH 027/116] pull 'require's from app/spec files --- app/controllers/spot/redirect_controller.rb | 2 - app/models/spot/oai_collection_solr_set.rb | 2 - app/services/rdf_literal_serializer.rb | 2 - .../derivatives/audio_derivative_service.rb | 5 -- .../audio_visual_base_derivative_service.rb | 5 -- .../derivatives/iiif_access_copy_service.rb | 4 -- .../derivatives/video_derivative_service.rb | 5 -- .../spot/exporters/zipped_work_exporter.rb | 3 -- app/services/spot/iiif_service.rb | 3 -- .../spot/transcript_download_service.rb | 3 -- app/services/zip_service.rb | 4 +- app/validators/spot/only_urls_validator.rb | 4 -- .../exporters/work_members_exporter_spec.rb | 3 +- .../exporters/work_metadata_exporter_spec.rb | 2 - .../exporters/zipped_work_exporter_spec.rb | 2 - spec/services/spot/work_csv_service_spec.rb | 2 - spec/values/blacklight/types_spec.rb | 50 ------------------- 17 files changed, 2 insertions(+), 99 deletions(-) delete mode 100644 spec/values/blacklight/types_spec.rb diff --git a/app/controllers/spot/redirect_controller.rb b/app/controllers/spot/redirect_controller.rb index 4b373d4e3..d70c17005 100644 --- a/app/controllers/spot/redirect_controller.rb +++ b/app/controllers/spot/redirect_controller.rb @@ -1,6 +1,4 @@ # frozen_string_literal: true -require 'uri' - module Spot # Controller responsible for redirecting requests from legacy services # (in particular, our Islandora instance) to their migrated counterparts. diff --git a/app/models/spot/oai_collection_solr_set.rb b/app/models/spot/oai_collection_solr_set.rb index bf04ed476..56588a824 100644 --- a/app/models/spot/oai_collection_solr_set.rb +++ b/app/models/spot/oai_collection_solr_set.rb @@ -1,6 +1,4 @@ # frozen_string_literal: true -require 'cgi' - module Spot # Subclass of +BlacklightOaiProvider::SolrSet+ that translates # spaces to underscores, making the following assumptions: diff --git a/app/services/rdf_literal_serializer.rb b/app/services/rdf_literal_serializer.rb index 093d2640e..4ddcc6370 100644 --- a/app/services/rdf_literal_serializer.rb +++ b/app/services/rdf_literal_serializer.rb @@ -1,6 +1,4 @@ # frozen_string_literal: true -require 'rdf/ntriples' - class RdfLiteralSerializer # @return [String] def serialize(literal) diff --git a/app/services/spot/derivatives/audio_derivative_service.rb b/app/services/spot/derivatives/audio_derivative_service.rb index 6901c6c31..ffdbafa8d 100644 --- a/app/services/spot/derivatives/audio_derivative_service.rb +++ b/app/services/spot/derivatives/audio_derivative_service.rb @@ -1,9 +1,4 @@ # frozen_string_literal: true -require 'aws-sdk-s3' -require 'digest/md5' -require 'fileutils' -require 'ffprober' - module Spot module Derivatives # Checks the 'premade_derivatives' property on the associated work. If the property is empty, diff --git a/app/services/spot/derivatives/audio_visual_base_derivative_service.rb b/app/services/spot/derivatives/audio_visual_base_derivative_service.rb index aafa15c66..7dfc290b7 100644 --- a/app/services/spot/derivatives/audio_visual_base_derivative_service.rb +++ b/app/services/spot/derivatives/audio_visual_base_derivative_service.rb @@ -1,9 +1,4 @@ # frozen_string_literal: true -require 'aws-sdk-s3' -require 'digest/md5' -require 'fileutils' -require 'ffprober' - module Spot module Derivatives # Base file that audio and video derivative services inherit from. Contains functionality common to both. diff --git a/app/services/spot/derivatives/iiif_access_copy_service.rb b/app/services/spot/derivatives/iiif_access_copy_service.rb index 704b01329..0484ac792 100644 --- a/app/services/spot/derivatives/iiif_access_copy_service.rb +++ b/app/services/spot/derivatives/iiif_access_copy_service.rb @@ -1,8 +1,4 @@ # frozen_string_literal: true -require 'aws-sdk-s3' -require 'digest/md5' -require 'fileutils' - module Spot module Derivatives # Creates pyramidal TIFF copies of Images for serving via IIIF. Pyramidal TIFFs contain diff --git a/app/services/spot/derivatives/video_derivative_service.rb b/app/services/spot/derivatives/video_derivative_service.rb index 7fb752836..5a2d799c0 100644 --- a/app/services/spot/derivatives/video_derivative_service.rb +++ b/app/services/spot/derivatives/video_derivative_service.rb @@ -1,9 +1,4 @@ # frozen_string_literal: true -require 'aws-sdk-s3' -require 'digest/md5' -require 'fileutils' -require 'ffprober' - module Spot module Derivatives # Checks the 'premade_derivatives' property on the associated work. If the property is empty, diff --git a/app/services/spot/exporters/zipped_work_exporter.rb b/app/services/spot/exporters/zipped_work_exporter.rb index 2b38caa5f..873bcc215 100644 --- a/app/services/spot/exporters/zipped_work_exporter.rb +++ b/app/services/spot/exporters/zipped_work_exporter.rb @@ -1,7 +1,4 @@ # frozen_string_literal: true -require 'tmpdir' -require 'fileutils' - module Spot module Exporters class ZippedWorkExporter diff --git a/app/services/spot/iiif_service.rb b/app/services/spot/iiif_service.rb index 46fac8ee3..c0488e906 100644 --- a/app/services/spot/iiif_service.rb +++ b/app/services/spot/iiif_service.rb @@ -1,7 +1,4 @@ # frozen_string_literal: true -require 'cgi' -require 'uri' - module Spot # Service for generating IIIF urls (via file_ids) for an external Cantaloupe image server. # Really, this could be used for _any_ external image server, save for the #download_url diff --git a/app/services/spot/transcript_download_service.rb b/app/services/spot/transcript_download_service.rb index 83b96f60f..47b90f0d8 100644 --- a/app/services/spot/transcript_download_service.rb +++ b/app/services/spot/transcript_download_service.rb @@ -1,7 +1,4 @@ # frozen_string_literal: true -require 'aws-sdk-s3' -require 'fileutils' - module Spot # Service to download transcript files and clean them up after attachment. # Calls the FileSetTranscriptAttachmentService. diff --git a/app/services/zip_service.rb b/app/services/zip_service.rb index d3ad8ba15..57447578e 100644 --- a/app/services/zip_service.rb +++ b/app/services/zip_service.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'fileutils' -require 'zip' - +# # Abstracting out our zipping/unzipping into its own service so that it's not # lumped in with another one. # diff --git a/app/validators/spot/only_urls_validator.rb b/app/validators/spot/only_urls_validator.rb index d4ee56447..a45ec3ce9 100644 --- a/app/validators/spot/only_urls_validator.rb +++ b/app/validators/spot/only_urls_validator.rb @@ -1,8 +1,4 @@ # frozen_string_literal: true -# -# -require 'uri' - module Spot class OnlyUrlsValidator < ActiveModel::Validator # @param record [ActiveModel::Base] diff --git a/spec/services/spot/exporters/work_members_exporter_spec.rb b/spec/services/spot/exporters/work_members_exporter_spec.rb index ac2210d7f..bbc89f6a0 100644 --- a/spec/services/spot/exporters/work_members_exporter_spec.rb +++ b/spec/services/spot/exporters/work_members_exporter_spec.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require 'fileutils' - +# # Is this too much mocking? I didn't want to go down the rabbit hole # of generating a full item just for this spec. RSpec.describe Spot::Exporters::WorkMembersExporter, perform_enqueued: true do diff --git a/spec/services/spot/exporters/work_metadata_exporter_spec.rb b/spec/services/spot/exporters/work_metadata_exporter_spec.rb index 2e0846616..0a2c56004 100644 --- a/spec/services/spot/exporters/work_metadata_exporter_spec.rb +++ b/spec/services/spot/exporters/work_metadata_exporter_spec.rb @@ -1,6 +1,4 @@ # frozen_string_literal: true -require 'fileutils' - RSpec.describe Spot::Exporters::WorkMetadataExporter do let(:exporter) { described_class.new(solr_document, request) } let(:work_id) { 'spot-work_metadata_exporter_spec-obj' } diff --git a/spec/services/spot/exporters/zipped_work_exporter_spec.rb b/spec/services/spot/exporters/zipped_work_exporter_spec.rb index 812934bc4..80b228ff9 100644 --- a/spec/services/spot/exporters/zipped_work_exporter_spec.rb +++ b/spec/services/spot/exporters/zipped_work_exporter_spec.rb @@ -1,6 +1,4 @@ # frozen_string_literal: true -require 'fileutils' - RSpec.describe Spot::Exporters::ZippedWorkExporter do let(:exporter) { described_class.new(work, request) } let(:path_to_file) { Rails.root.join('spec', 'fixtures', 'image.png') } diff --git a/spec/services/spot/work_csv_service_spec.rb b/spec/services/spot/work_csv_service_spec.rb index 8a0c1bb21..a9303a5d7 100644 --- a/spec/services/spot/work_csv_service_spec.rb +++ b/spec/services/spot/work_csv_service_spec.rb @@ -1,6 +1,4 @@ # frozen_string_literal: true -require 'tmpdir' - RSpec.describe Spot::WorkCsvService do let(:service) { described_class.new(work, terms: terms) } let(:work) { build(:publication, id: 'abc123', title: ['one title', 'two titles']) } diff --git a/spec/values/blacklight/types_spec.rb b/spec/values/blacklight/types_spec.rb deleted file mode 100644 index 420257808..000000000 --- a/spec/values/blacklight/types_spec.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true -RSpec.describe Blacklight::Types do - describe 'Array' do - subject { described_class::Array.coerce(value) } - - context 'when an Array' do - let(:value) { ['a value'] } - - it { is_expected.to eq value } - end - - context 'when another value' do - let(:value) { 'a value' } - - it { is_expected.to eq [value] } - end - end - - describe 'String' do - subject { described_class::String.coerce(value) } - - context 'when a single value' do - let(:value) { 'a single value' } - - it { is_expected.to eq value } - end - - context 'when an array' do - let(:value) { ['one', 'two'] } - - it { is_expected.to eq value.first } - end - end - - describe 'Date' do - subject { described_class::Date.coerce(value) } - - context 'when a parseable date' do - let(:value) { Time.now.utc.to_s } - - it { is_expected.to be_a Date } - end - - context 'when not parseable' do - let(:value) { 'not today' } - - it { is_expected.to be true } - end - end -end From 0123240d367a83d5be0d044df3755c07c1a4f69e Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Tue, 4 Nov 2025 12:46:06 -0500 Subject: [PATCH 028/116] some hyrax defaults --- config/application.rb | 2 +- config/initializers/hyrax.rb | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/config/application.rb b/config/application.rb index 5e2a4058e..c15683875 100644 --- a/config/application.rb +++ b/config/application.rb @@ -24,7 +24,7 @@ module Spot class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 6.0 + config.load_defaults 6.1 # use sidekiq for async jobs config.active_job.queue_adapter = :sidekiq diff --git a/config/initializers/hyrax.rb b/config/initializers/hyrax.rb index 3ef23d532..5487c8a9f 100644 --- a/config/initializers/hyrax.rb +++ b/config/initializers/hyrax.rb @@ -5,8 +5,11 @@ # Can't define this within the Bulkrax initializer as it runs _before_ this Bulkrax.default_work_type = Hyrax.config.curation_concerns.first.name - config.collection_model = '::Collection' config.admin_set_model = '::AdminSet' + config.collection_model = '::Collection' + config.file_set_model = '::FileSet' + + config.solr_default_method = :post # Register roles that are expected by your implementation. # @see Hyrax::RoleRegistry for additional details. From 27e9b7bf29c9aefbec25658bda2704078b6f24ee Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Tue, 4 Nov 2025 16:10:42 -0500 Subject: [PATCH 029/116] wip test work --- app/jobs/spot/repository_fixity_check_job.rb | 7 +- app/services/spot/characterization_service.rb | 2 +- config/initializers/spot_overrides.rb | 336 +++++++++++------- .../deserializes_rdf_literals_spec.rb | 137 +++---- spec/helpers/spot/facet_helper_spec.rb | 2 +- spec/models/collection_spec.rb | 2 +- spec/services/spot/video_processor_spec.rb | 47 ++- .../support/shared_examples/logs_a_warning.rb | 37 -- .../support/shared_examples/spot_presenter.rb | 4 +- 9 files changed, 328 insertions(+), 246 deletions(-) delete mode 100644 spec/support/shared_examples/logs_a_warning.rb diff --git a/app/jobs/spot/repository_fixity_check_job.rb b/app/jobs/spot/repository_fixity_check_job.rb index 7d94d349f..442e2e1ea 100644 --- a/app/jobs/spot/repository_fixity_check_job.rb +++ b/app/jobs/spot/repository_fixity_check_job.rb @@ -18,15 +18,12 @@ class RepositoryFixityCheckJob < ApplicationJob around_perform :wrap_check # @param [true, false] :force Ignore the 'max days between check' parameter - def perform(force: false) - opts = { async_jobs: false } - opts[:max_days_between_fixity_checks] = -1 if force - + def perform @count = 0 ::FileSet.find_each do |file_set| @count += 1 - Hyrax::FileSetFixityCheckService.new(file_set, opts).fixity_check + Hyrax::FileSetFixityCheckService.new(file_set, async_jobs: false, max_days_between_fixity_checks: -1).fixity_check end end diff --git a/app/services/spot/characterization_service.rb b/app/services/spot/characterization_service.rb index 53de286d6..74e5e9c64 100644 --- a/app/services/spot/characterization_service.rb +++ b/app/services/spot/characterization_service.rb @@ -12,7 +12,7 @@ def self.run(characterization_proxy, filepath, opts = {}) tool = ENV['FITS_SERVLET_URL'].present? ? :fits_servlet : :fits opts = { ch12n_tool: tool }.merge(opts) - super(characterization_proxy, filepath, opts) + super(characterization_proxy, filepath, **opts) end end end diff --git a/config/initializers/spot_overrides.rb b/config/initializers/spot_overrides.rb index 9de7857b8..e03df4901 100644 --- a/config/initializers/spot_overrides.rb +++ b/config/initializers/spot_overrides.rb @@ -43,44 +43,68 @@ # "translation missing" message being provided as a fall-back label. this should # prevent that error from appearing by replacing the +translate+ calls with a symbolized # I18n key (see also 0717dee, + catalog_controller.rb) - [Hyrax::My::CollectionsController, Hyrax::Dashboard::CollectionsController].each do |klass| - klass.class_eval do - def self.update_facet_labels! - blacklight_config.facet_fields['visibility_ssi'].label = :'hyrax.dashboard.my.heading.visibility' - blacklight_config.facet_fields[Hyrax.config.collection_type_index_field].label = :'hyrax.dashboard.my.heading.collection_type' - blacklight_config.facet_fields['has_model_ssim'].label = :'hyrax.dashboard.my.heading.collection_type' + module Spot + module CollectionsControllerFacetDecorator + extend ActiveSupport::Concern + + class_methods do + def update_facet_labels! + blacklight_config.facet_fields['visibility_ssi'].label = :'hyrax.dashboard.my.heading.visibility' + blacklight_config.facet_fields[Hyrax.config.collection_type_index_field].label = :'hyrax.dashboard.my.heading.collection_type' + blacklight_config.facet_fields['has_model_ssim'].label = :'hyrax.dashboard.my.heading.collection_type' + end + end + + included do + update_facet_labels! end - update_facet_labels! end end - # same as previous: updating facet labels for dashboard works controller - [Hyrax::My::WorksController, Hyrax::Dashboard::WorksController].each do |klass| - klass.class_eval do - def self.update_facet_labels! - blacklight_config.facet_fields['visibility_ssi'].label = :'hyrax.dashboard.my.heading.visibility' + Hyrax::My::CollectionsController.prepend(Spot::CollectionsControllerFacetDecorator) + Hyrax::Dashboard::CollectionsController.prepend(Spot::CollectionsControllerFacetDecorator) + + module Spot + module WorksControllerFacetDecorator + extend ActiveSupport::Concern + + class_methods do + def update_facet_labels! + blacklight_config.facet_fields['visibility_ssi'].label = :'hyrax.dashboard.my.heading.visibility' + end + end + + included do + update_facet_labels! end - update_facet_labels! end end + # same as previous: updating facet labels for dashboard works controller + Hyrax::My::WorksController.prepend(Spot::WorksControllerFacetDecorator) + Hyrax::Dashboard::WorksController.prepend(Spot::WorksControllerFacetDecorator) + # We're using an older version of the FITSServlet tool (1.1.3 as of 2019-12-03, # anything higher throws an exception that I can't nail down) that predates # a change to set the response encoding to UTF-8. So we need to do this as # early as possible within the Characterization tool. - require 'hydra-file_characterization' + module Spot + module FitsServletCharacterizerDecorator + extend ActiveSupport::Concern - Hydra::FileCharacterization::Characterizers::FitsServlet.class_eval do - # Wrap the datafile= param in quotes to handle filenames with spaces - def command - %(curl -k -F datafile=@"#{filename}" #{ENV['FITS_SERVLET_URL']}/examine) - end + # Wrap the datafile= param in quotes to handle filenames with spaces + def command + %(curl -k -F datafile=@"#{filename}" #{ENV['FITS_SERVLET_URL']}/examine) + end - def output - super.encode('UTF-8', invalid: :replace) + def output + super.encode('UTF-8', invalid: :replace) + end end end + Hydra::FileCharacterization::Characterizers::FitsServlet.prepend(Spot::FitsServletCharacterizerDecorator) + # Add our SolrSuggestActor to the front of the default actor-stack. This will # trigger a build of all of the Solr suggestion dictionaries at the end of # each create, update, destroy process (each method calls the next actor and _then_ @@ -99,26 +123,41 @@ def output # # => false # # @see https://github.com/samvera/hydra-head/blob/v10.7.0/hydra-access-controls/app/models/hydra/access_controls/embargo.rb#L13-L15 - Hydra::AccessControls::Embargo.class_eval do - def active? - embargo_release_date.present? && DateTime.current < embargo_release_date + module Spot + module EmbargoActiveDecorator + extend ActiveSupport::Concern + + def active? + embargo_release_date.present? && DateTime.current < embargo_release_date + end end - end - Hydra::AccessControls::Lease.class_eval do - def active? - lease_expiration_date.present? && DateTime.current < lease_expiration_date + module LeaseActiveDecorator + extend ActiveSupport::Concern + + def active? + lease_expiration_date.present? && DateTime.current < lease_expiration_date + end end end + Hydra::AccessControls::Embargo.prepend(Spot::EmbargoActiveDecorator) + Hydra::AccessControls::Lease.prepend(Spot::LeaseActiveDecorator) + # Updating how SimpleForm generates labels so that we can use the same locales # for the form as those for the metadata display. - SimpleForm::Inputs::Base.class_eval do - protected def raw_label_text - options[:label] || I18n.t("blacklight.search.fields.#{attribute_name}", default: label_translation) + module Spot + module SimpleFormBaseInputDecorator + extend ActiveSupport::Concern + + protected def raw_label_text + options[:label] || I18n.t("blacklight.search.fields.#{attribute_name}", default: label_translation) + end end end + SimpleForm::Inputs::Base.prepend(Spot::SimpleFormBaseInputDecorator) + # Adding label support for metadata-only records Hyrax::PermissionBadge.class_eval do old_visibility_label_class = Hyrax::PermissionBadge::VISIBILITY_LABEL_CLASS.dup @@ -145,13 +184,19 @@ class << self # wonky one-liner seems to preserve user-selected sorting. ¯\_(ツ)_/¯ # # @see https://github.com/samvera/hyrax/blob/main/app/search_builders/hyrax/collection_search_builder.rb#L36-L42 - Hyrax::CollectionMemberSearchBuilder.class_eval do - def add_sorting_to_solr(solr_parameters) - return if solr_parameters[:q] - solr_parameters[:sort] ||= (sort || "title_sort_si asc") + module Spot + module CollectionMemberSearchBuilderDecorator + extend ActiveSupport::Concern + + def add_sorting_to_solr(solr_parameters) + return if solr_parameters[:q] + solr_parameters[:sort] ||= (sort || "title_sort_si asc") + end end end + Hyrax::CollectionMemberSearchBuilder.prepend(Spot::CollectionMemberSearchBuilderDecorator) + # Override to fix Hyrax bug where calling Hyrax::AdminSetCreateService.find_or_create_default_admin_set # will try to load an AdminSet's entire set of members when called. # @@ -173,79 +218,114 @@ def find_default_admin_set # # @see https://github.com/biola/rack-cas/blob/v0.16.1/lib/rack/cas.rb#L96-L102 # rubocop:disable Style/IfUnlessModifier - require 'rack/cas' - Rack::CAS.class_eval do - def store_session(request, user, ticket, extra_attrs = {}) - if RackCAS.config.extra_attributes_filter? - extra_attrs.select! { |key, _val| RackCAS.config.extra_attributes_filter.map(&:to_s).include?(key.to_s) } - end + module Spot + module RackCasEntitlementDecorator + extend ActiveSupport::Concern - if extra_attrs['eduPersonEntitlement'].present? - extra_attrs['eduPersonEntitlement'] = Array.wrap(extra_attrs['eduPersonEntitlement']).select do |val| - URI.parse(val).host == Spot::CasUserRolesService.entitlement_host + def store_session(request, user, ticket, extra_attrs = {}) + if RackCAS.config.extra_attributes_filter? + extra_attrs.select! { |key, _val| RackCAS.config.extra_attributes_filter.map(&:to_s).include?(key.to_s) } end - end - request.session['cas'] = { 'user' => user, 'ticket' => ticket, 'extra_attributes' => extra_attrs } + if extra_attrs['eduPersonEntitlement'].present? + extra_attrs['eduPersonEntitlement'] = Array.wrap(extra_attrs['eduPersonEntitlement']).select do |val| + URI.parse(val).host == Spot::CasUserRolesService.entitlement_host + end + end + + request.session['cas'] = { 'user' => user, 'ticket' => ticket, 'extra_attributes' => extra_attrs } + end end end + require 'rack/cas' unless Object.const_defined?('Rack::RAS') + + Rack::CAS.prepend(Spot::RackCasEntitlementDecorator) + # Modifying how Questiong Authority returns AssignFAST results by # converting fst ids into URLs - require 'qa/authorities/assign_fast' - Qa::Authorities::AssignFast::GenericAuthority.class_eval do - private - - def parse_authority_response(raw_response) - raw_response['response']['docs'].map do |doc| - index = Qa::Authorities::AssignFast.index_for_authority(subauthority) - term = doc[index].first - term += " (USE #{doc['auth']})" if doc['type'] == 'alt' - fast_id = Array.wrap(doc['idroot']).first - - { - fast_id: fast_id, - id: "http://id.worldcat.org/fast/#{fast_id.gsub(/^fst/, '')}", - label: term, - type: doc['type'], - value: doc['auth'] - } + module Spot + module QaAssignFastGenericAuthorityDecorator + extend ActiveSupport::Concern + + private + + def parse_authority_response(raw_response) + raw_response['response']['docs'].map do |doc| + index = Qa::Authorities::AssignFast.index_for_authority(subauthority) + term = doc[index].first + term += " (USE #{doc['auth']})" if doc['type'] == 'alt' + fast_id = Array.wrap(doc['idroot']).first + + { + fast_id: fast_id, + id: "http://id.worldcat.org/fast/#{fast_id.gsub(/^fst/, '')}", + label: term, + type: doc['type'], + value: doc['auth'] + } + end end end end - # In order for us to search assignFAST by FAST IDs, we need to - # add the 'idroot' searchIndex as a valid subauthority for AssignFast - Qa::Authorities::AssignFastSubauthority.module_eval do - def index_for_authority(authority) - return authority if authority == 'idroot' + Qa::Authorities::AssignFast::GenericAuthority.prepend(Spot::QaAssignFastGenericAuthorityDecorator) - Qa::Authorities::AssignFastSubauthority::SUBAUTHORITIES[authority] - end + module Spot + module QaAssignFastSubauthorityDecorator + extend ActiveSupport::Concern + + def index_for_authority(authority) + return authority if authority == 'idroot' - def subauthorities - Qa::Authorities::AssignFastSubauthority::SUBAUTHORITIES.keys + ['idroot'] + Qa::Authorities::AssignFastSubauthority::SUBAUTHORITIES[authority] + end + + def subauthorities + Qa::Authorities::AssignFastSubauthority::SUBAUTHORITIES.keys + ['idroot'] + end end end + # In order for us to search assignFAST by FAST IDs, we need to + # add the 'idroot' searchIndex as a valid subauthority for AssignFast + Qa::Authorities::AssignFastSubauthority.prepend(Spot::QaAssignFastSubauthorityDecorator) + # Modifying the Video Runner for Hydra to use a customized Processor # which backports changes from 3.8.0 # # @see https://github.com/samvera/hydra-derivatives/blob/v3.8.0/lib/hydra/derivatives/runners/video_derivatives.rb - Hydra::Derivatives::VideoDerivatives.class_eval do - def self.processor_class - Spot::VideoProcessor + + module Spot + module VideoProcessorClassDecorator + extend ActiveSupport::Concern + + class_methods do + def processor_class + Spot::VideoProcessor + end + end end end + Hydra::Derivatives::VideoDerivatives.prepend(Spot::VideoProcessorClassDecorator) + # Add original file names and the transcript flag to presenter for file sets # # @see https://github.com/samvera/hyrax/blob/e4f8a06aaf1c9ec378f87764da59f73a8adf06d7/app/presenters/hyrax/file_set_presenter.rb - Hyrax::FileSetPresenter.class_eval do - delegate :original_filenames, :transcript_name, - :stored_derivatives, to: :solr_document + module Spot + module FileSetPresenterDecorator + extend ActiveSupport::Concern + + included do + delegate :original_filenames, :transcript_name, + :stored_derivatives, to: :solr_document + end + end end + Hyrax::FileSetPresenter.prepend(Spot::FileSetPresenterDecorator) + # Add support for downloading file_set transcripts Hyrax::DownloadsController.prepend(Spot::DownloadsControllerBehavior) @@ -253,31 +333,36 @@ def self.processor_class # # @see https://github.com/samvera/bulkrax/blob/v5.5.1/app/parsers/bulkrax/csv_parser.rb#L258 # - Bulkrax::ImporterJob.class_eval do - # checks the file sizes of the download files to match the original files - def all_files_completed?(importer) - cloud_files = importer.parser_fields['cloud_file_paths'] - original_files = importer.parser_fields['original_file_paths'] - return true unless cloud_files.present? && original_files.present? - - imported_file_sizes = cloud_files.map { |_, v| get_file_size_from_s3(v['url']) } - original_file_sizes = original_files.map { |imported_file| File.size(imported_file) } - - original_file_sizes == imported_file_sizes - end + module Spot + module BulkraxImporterJobDecorator + extend ActiveSupport::Concern + # checks the file sizes of the download files to match the original files + def all_files_completed?(importer) + cloud_files = importer.parser_fields['cloud_file_paths'] + original_files = importer.parser_fields['original_file_paths'] + return true unless cloud_files.present? && original_files.present? + + imported_file_sizes = cloud_files.map { |_, v| get_file_size_from_s3(v['url']) } + original_file_sizes = original_files.map { |imported_file| File.size(imported_file) } + + original_file_sizes == imported_file_sizes + end - # s3 file size fetch - # @todo should we add handling for other types of cloud files? - def get_file_size_from_s3(url) - uri_parsed = ::Addressable::URI.parse(url) - return unless uri_parsed.scheme == 's3' + # s3 file size fetch + # @todo should we add handling for other types of cloud files? + def get_file_size_from_s3(url) + uri_parsed = ::Addressable::URI.parse(url) + return unless uri_parsed.scheme == 's3' - client = Aws::S3::Client.new - resp = client.head_object(bucket: uri_parsed.host, key: uri_parsed.path[1..-1]) - resp.content_length + client = Aws::S3::Client.new + resp = client.head_object(bucket: uri_parsed.host, key: uri_parsed.path[1..-1]) + resp.content_length + end end end + Bulkrax::ImporterJob.prepend(Spot::BulkraxImporterJobDecorator) + # Bulkrax's parser replaces any whitespace character with a space, which kills paragraph breaks # and newlines. Our patch modifies the base #result method to only replace tabs with # spaces and strip lead/trailing spaces. @@ -290,35 +375,44 @@ def get_file_size_from_s3(url) # The unauthorized status breaks the laf only thumbnail. # # @see https://github.com/samvera/hyrax/blob/hyrax-v3.6.0/app/controllers/hyrax/downloads_controller.rb#L52-L63 - Hyrax::DownloadsController.class_eval do - # Customize the :read ability in your Ability class, or override this method. - # Hydra::Ability#download_permissions can't be used in this case because it assumes - # that files are in a LDP basic container, and thus, included in the asset's uri. - def authorize_download! - authorize! :download, params[asset_param_key] - # Deny access if the work containing this file is restricted by a workflow - return unless workflow_restriction?(file_set_parent(params[asset_param_key]), ability: current_ability) - raise Hyrax::WorkflowAuthorizationException - rescue CanCan::AccessDenied, Hyrax::WorkflowAuthorizationException - unauthorized_image = Rails.root.join("app", "assets", "images", "unauthorized.png") - send_file unauthorized_image + module Spot + module HyraxDownloadsControllerDecorator + extend ActiveSupport::Concern + # Customize the :read ability in your Ability class, or override this method. + # Hydra::Ability#download_permissions can't be used in this case because it assumes + # that files are in a LDP basic container, and thus, included in the asset's uri. + def authorize_download! + authorize! :download, params[asset_param_key] + # Deny access if the work containing this file is restricted by a workflow + return unless workflow_restriction?(file_set_parent(params[asset_param_key]), ability: current_ability) + raise Hyrax::WorkflowAuthorizationException + rescue CanCan::AccessDenied, Hyrax::WorkflowAuthorizationException + unauthorized_image = Rails.root.join("app", "assets", "images", "unauthorized.png") + send_file unauthorized_image + end end end + Hyrax::DownloadsController.prepend(Spot::HyraxDownloadsControllerDecorator) + # Encountering an issue where Hyrax::PersistDirectlyContainedOutputFileService.retrieve_file_set requires # Hyrax::UploadedFile#file_set_uri to be an URI but querying for that URI throws an error (ActiveFedora # is appending the base root to the full uri, resulting in errors like: # Ldp::BadRequest: Path contains empty element! /dev/ht/tp/:/http://fedora:8080/rest/dev/2v/23/vt/36/2v23vt362") - Hyrax::UploadedFile.class_eval do - def add_file_set!(file_set) - uri = case file_set - when ActiveFedora::Base - file_set.uri - when Hyrax::Resource - file_set.id.is_a?(URI::HTTP) ? file_set.id : Hyrax::Base.id_to_uri(file_set.id.to_s) - end - - update!(file_set_uri: uri) if uri.present? + module Spot + module HyraxUploadedFileDecorator + def add_file_set!(file_set) + uri = case file_set + when ActiveFedora::Base + file_set.uri + when Hyrax::Resource + file_set.id.is_a?(URI::HTTP) ? file_set.id : Hyrax::Base.id_to_uri(file_set.id.to_s) + end + + update!(file_set_uri: uri) if uri.present? + end end end + + Hyrax::UploadedFile.prepend(Spot::HyraxUploadedFileDecorator) end diff --git a/spec/actors/concerns/deserializes_rdf_literals_spec.rb b/spec/actors/concerns/deserializes_rdf_literals_spec.rb index e56d8787d..5a6e3279c 100644 --- a/spec/actors/concerns/deserializes_rdf_literals_spec.rb +++ b/spec/actors/concerns/deserializes_rdf_literals_spec.rb @@ -1,36 +1,35 @@ # frozen_string_literal: true RSpec.describe DeserializesRdfLiterals do - before do - class WorkType < ActiveFedora::Base - property :title, predicate: ::RDF::Vocab::DC.title, multiple: false - property :description, predicate: ::RDF::Vocab::DC.description - end + class TestWorkType < ActiveFedora::Base + property :title, predicate: ::RDF::Vocab::DC.title, multiple: false + property :description, predicate: ::RDF::Vocab::DC.description + end - # need to define this to be able to access the `language_tagged_fields` array - class Hyrax::WorkTypeForm < Hyrax::Forms::WorkForm - include LanguageTaggedFormFields - transforms_language_tags_for :title, :description - end + class TestWorkTypeActor < Hyrax::Actors::AbstractActor + include DeserializesRdfLiterals + end - class WorkTypeActor < Hyrax::Actors::AbstractActor - include DeserializesRdfLiterals - end + # This is all incredibly messy because the Actor mixin is relying on a class attribute + # for a form based on naming conventions, hence this silly unused Form class and exposing + # the test classes to the Object namespace (as opposed to `let(:work_type_class) { Class.new(ActiveFedora::Base) }`) + class Hyrax::TestWorkTypeForm < ::Spot::Forms::WorkForm + include LanguageTaggedFormFields + transforms_language_tags_for :title, :description end - after do - Object.send(:remove_const, :WorkType) - Object.send(:remove_const, :WorkTypeActor) - Hyrax.send(:remove_const, :WorkTypeForm) + after(:suite) do + Object.remove_const(:TestWorkType) + Object.remove_const(:TestWorkTypeActor) + Hyrax.remove_const(:TestWorkTypeForm) end - let(:work) { WorkType.new } - let(:ability) { Ability.new(build(:user)) } - let(:env) { Hyrax::Actors::Environment.new(work, ability, attributes) } - let(:actor) { WorkTypeActor.new(Hyrax::Actors::Terminator.new) } + let(:work) { TestWorkType.new } + let(:ability) { Ability.new(create(:user)) } + let(:actor) { TestWorkTypeActor.new(Hyrax::Actors::Terminator.new) } # when the actor receives the form attributes, it should have already # been run through the serializer - let(:attributes) do + let(:string_attributes) do { title: '"Cool Beans"@en', description: ['"A work of importance"'] @@ -44,57 +43,71 @@ class WorkTypeActor < Hyrax::Actors::AbstractActor } end - shared_examples 'it transforms fields' do - it 'transforms single fields' do - expect(env.attributes[:title]).to eq RDF::Literal('Cool Beans', language: :en) - end - - it 'transforms multiple fields' do - expect(env.attributes[:description]).to eq [RDF::Literal('A work of importance')] - end - end - describe '#create' do - before { actor.create(env) } + context 'with string values' do + let(:env) { Hyrax::Actors::Environment.new(work, ability, string_attributes) } + + it 'transforms single fields' do + expect { actor.create(env) } + .to change { env.attributes[:title] } + .from(string_attributes[:title]) + .to(literal_attributes[:title]) + end + + it 'transforms multiple fields' do + expect { actor.create(env) } + .to change { env.attributes[:title] } + .from(string_attributes[:title]) + .to(literal_attributes[:title]) + end + end - it_behaves_like 'it transforms fields' + context 'with literal values' do + let(:env) { Hyrax::Actors::Environment.new(work, ability, literal_attributes) } - context 'when attributes are already RDF::Literals (from ingest)' do - let(:attributes) { literal_attributes } + it 'transforms single fields' do + expect { actor.create(env) } + .not_to change { env.attributes[:title] } + end - it_behaves_like 'it transforms fields' + it 'transforms multiple fields' do + expect { actor.create(env) } + .not_to change { env.attributes[:title] } + end end end describe '#update' do - before { actor.update(env) } - - it_behaves_like 'it transforms fields' - - context 'when attributes are already RDF::Literals (from ingest)' do - let(:attributes) { literal_attributes } - - it_behaves_like 'it transforms fields' + context 'with string values' do + let(:env) { Hyrax::Actors::Environment.new(work, ability, string_attributes) } + + it 'transforms single fields' do + expect { actor.update(env) } + .to change { env.attributes[:title] } + .from(string_attributes[:title]) + .to(literal_attributes[:title]) + end + + it 'transforms multiple fields' do + expect { actor.update(env) } + .to change { env.attributes[:title] } + .from(string_attributes[:title]) + .to(literal_attributes[:title]) + end end - end - # because of how the the create/update tests are set up, testing this case in each - # causes a race between their +before+ blocks, which call the methods, and this - # +before+ block, which invalidates the fields method. having it run as a shared_example - # causes the parent +before+ block to run first, which runs the attributes through - # the transformation, and then invalidates the method, which then does nothing. - # we can make the logical jump from this test -- when there's no method on the - # form, are we getting back an empty array of fields? -- to the inference that - # no forms are iterated through. - describe '#tagged_fields (private)' do - subject { actor.send(:tagged_fields) } - - before do - allow(Hyrax::WorkTypeForm).to receive(:language_tagged_fields).and_raise(NoMethodError) - end + context 'with literal values' do + let(:env) { Hyrax::Actors::Environment.new(work, ability, literal_attributes) } + + it 'transforms single fields' do + expect { actor.update(env) } + .not_to change { env.attributes[:title] } + end - context 'when language tagged fields are not defined on the form' do - it { is_expected.to be_empty } + it 'transforms multiple fields' do + expect { actor.update(env) } + .not_to change { env.attributes[:title] } + end end end end diff --git a/spec/helpers/spot/facet_helper_spec.rb b/spec/helpers/spot/facet_helper_spec.rb index 790412190..8a58a491d 100644 --- a/spec/helpers/spot/facet_helper_spec.rb +++ b/spec/helpers/spot/facet_helper_spec.rb @@ -56,6 +56,6 @@ let(:visibility) { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PUBLIC } - it { is_expected.to eq 'Public' } + it { is_expected.to eq 'Public' } end end diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb index 8561122c2..06fc04b79 100644 --- a/spec/models/collection_spec.rb +++ b/spec/models/collection_spec.rb @@ -37,7 +37,7 @@ context 'when the collection does not exist' do it 'raises an ObjectNotFoundError' do expect { described_class.find(param) } - .to raise_error(ActiveFedora::ObjectNotFoundError, %r{'id'=#{param}$}) + .to raise_error(Hyrax::ObjectNotFoundError, %r{'id'=#{param}$}) end end diff --git a/spec/services/spot/video_processor_spec.rb b/spec/services/spot/video_processor_spec.rb index c4d15f4c8..3d5c026d5 100644 --- a/spec/services/spot/video_processor_spec.rb +++ b/spec/services/spot/video_processor_spec.rb @@ -12,13 +12,17 @@ after { described_class.config = @original_config } let(:directives) { { label: :thumb, format: "mp4", url: 'http://localhost:8983/fedora/rest/dev/1234/thumbnail' } } - + let(:opts) do + { + Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec mpeg4 -acodec aac -strict -2 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" + } + end it "is configurable" do expect(subject) .to receive(:encode_file) - .with("mp4", - { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec mpeg4 -acodec aac -strict -2 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) + .with("mp4", **opts) + subject.process end end @@ -26,13 +30,16 @@ context "when arguments are passed as a hash" do context "when a video format is requested" do let(:directives) { { label: :thumb, format: 'webm', url: 'http://localhost:8983/fedora/rest/dev/1234/thumbnail' } } - + let(:opts) do + { + Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec libvpx -acodec libvorbis -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" + } + end it "creates a fedora resource and infers the name" do expect(subject) .to receive(:encode_file) - .with("webm", - { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec libvpx -acodec libvorbis -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) + .with("webm", **opts) subject.process end end @@ -49,26 +56,34 @@ context "when an mkv is requested" do context "input options present" do let(:directives) { { label: :thumb, format: 'mkv', url: 'http://localhost:8983/fedora/rest/dev/1234/thumbnail', input_options: 'test_input_options' } } - + let(:opts) do + { + Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec ffv1 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "test_input_options" + } + end it "creates a fedora resource and infers the name" do expect(subject) .to receive(:encode_file) - .with("mkv", - { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec ffv1 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "test_input_options" }) + .with("mkv", **opts) + subject.process end end context "input options not present" do let(:directives) { { label: :thumb, format: 'mkv', url: 'http://localhost:8983/fedora/rest/dev/1234/thumbnail' } } - + let(:opts) do + { + Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec ffv1 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", + Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" + } + end it "creates a fedora resource and infers the name" do expect(subject) .to receive(:encode_file) - .with("mkv", - { Hydra::Derivatives::Processors::Ffmpeg::OUTPUT_OPTIONS => "-s 320x240 -vcodec ffv1 -g 30 -b:v 345k -ac 2 -ab 96k -ar 44100", - Hydra::Derivatives::Processors::Ffmpeg::INPUT_OPTIONS => "" }) + .with("mkv", **opts) + subject.process end end diff --git a/spec/support/shared_examples/logs_a_warning.rb b/spec/support/shared_examples/logs_a_warning.rb deleted file mode 100644 index f076279c2..000000000 --- a/spec/support/shared_examples/logs_a_warning.rb +++ /dev/null @@ -1,37 +0,0 @@ -# frozen_string_literal: true -shared_examples 'it logs a warning' do - before do - @original_logger = Rails.logger - - class FakeLogger < Logger - attr_reader :messages - def initialize(*) - super(File::NULL) - @messages = [] - end - - def add(*args) - @messages << args - super(*args) - end - end - - Rails.logger = logger - end - - after do - Rails.logger = @original_logger - Object.send(:remove_const, :FakeLogger) - end - - let(:logger) { FakeLogger.new(File::NULL) } - - it do - # we need to call the subject, otherwise nothing will happen - # and +logger.messages+ will be empty - subject - - expect(logger.messages).not_to be_empty - expect(logger.messages.first.first).to eq Logger::WARN - end -end diff --git a/spec/support/shared_examples/spot_presenter.rb b/spec/support/shared_examples/spot_presenter.rb index def44d7ae..35de46d42 100644 --- a/spec/support/shared_examples/spot_presenter.rb +++ b/spec/support/shared_examples/spot_presenter.rb @@ -90,8 +90,8 @@ let(:_solr_data) { {} } # `metadata_only?` calls `can?(:read, solr_document)` so the data needs to be persisted - before { ActiveFedora::SolrService.add(solr_data, commit: true) } - after { ActiveFedora::SolrService.delete(solr_data[:id]) } + before { Hyrax::SolrService.add(solr_data, commit: true) } + after { Hyrax::SolrService.delete(solr_data[:id]) } context 'when the ability is admin' do let(:ability) { admin_ability } From 41e7e36b085d14ef6d32c20132b3aed9f357ed23 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Wed, 5 Nov 2025 11:09:07 -0500 Subject: [PATCH 030/116] rubo --- config/environments/development.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/development.rb b/config/environments/development.rb index 8b21e8e69..f0e771fc7 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true Rails.application.configure do config.hosts << "ldr-future.lafayette.edu" - + config.public_file_server.enabled = true # Settings specified here will take precedence over those in config/application.rb. From 54d003a6a495da1194fb2b60842306ad89cccb05 Mon Sep 17 00:00:00 2001 From: Jennifer Wellnitz Date: Wed, 5 Nov 2025 13:30:40 -0500 Subject: [PATCH 031/116] tie host to env --- config/environments/development.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/environments/development.rb b/config/environments/development.rb index f0e771fc7..85563e35f 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true Rails.application.configure do - config.hosts << "ldr-future.lafayette.edu" + config.hosts << ENV['URL_HOST'] if ENV['URL_HOST'].present? config.public_file_server.enabled = true From 5c4982bd03211e6b68cf69bcc6e0eca378795fbf Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Wed, 5 Nov 2025 13:37:14 -0500 Subject: [PATCH 032/116] patches for passing tests (#1196) * copy collections_membership_actor_spec test rewrite * validator deprecations + collection error class * add/subtract hosts for tests * rubOOOO * everything but feature specs should pass * correct gh matrix variable --- .github/workflows/lint-and-test.yml | 2 +- app/helpers/audio_visual_helper.rb | 2 +- app/jobs/spot/repository_fixity_check_job.rb | 6 ++-- .../spot/catalog_search_builder.rb | 2 +- .../hyrax/has_one_title_validator.rb | 2 +- app/validators/spot/date_issued_validator.rb | 6 ++-- app/validators/spot/only_urls_validator.rb | 2 +- .../required_local_authority_validator.rb | 5 ++- app/validators/spot/slug_validator.rb | 4 +-- config/application.rb | 5 +++ config/environments/development.rb | 2 -- config/initializers/spot_overrides.rb | 2 +- .../deserializes_rdf_literals_spec.rb | 36 ++++++++++--------- .../collections_membership_actor_spec.rb | 7 ++-- spec/factories/file_set.rb | 1 + spec/helpers/audio_visual_helper_spec.rb | 5 ++- spec/indexers/spot/file_set_indexer_spec.rb | 2 +- .../spot/repository_fixity_check_job_spec.rb | 19 +++------- spec/models/collection_spec.rb | 2 +- .../spot/renderers/attribute_renderer_spec.rb | 4 +-- .../spot/catalog_search_builder_spec.rb | 3 +- .../work_and_file_set_search_builder_spec.rb | 9 +++-- spec/services/spot/handle_service_spec.rb | 17 ++++++--- spec/services/spot/video_processor_spec.rb | 9 ++--- 24 files changed, 83 insertions(+), 71 deletions(-) diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml index aa150d243..baab85071 100644 --- a/.github/workflows/lint-and-test.yml +++ b/.github/workflows/lint-and-test.yml @@ -19,7 +19,7 @@ jobs: run: bundle exec rubocop --format github test: - name: "Test (Valkyrie enabled: ${{ github.matrix.hyrax_valkyrie_enabled }})" + name: "Test (Valkyrie enabled: ${{ matrix.hyrax_valkyrie_enabled }})" runs-on: ubuntu-latest container: image: ruby:3.2.9-slim-bookworm diff --git a/app/helpers/audio_visual_helper.rb b/app/helpers/audio_visual_helper.rb index 2c7fb9cb6..acb673b91 100644 --- a/app/helpers/audio_visual_helper.rb +++ b/app/helpers/audio_visual_helper.rb @@ -56,6 +56,6 @@ def get_derivative_res(derivative) # @return the network path to the thumbnail # @param [FileSet] file_set of the video def transcript_path(file_set) - Hyrax::Engine.routes.url_helpers.download_path(file_set.id, file: 'transcript') + Hyrax::Engine.routes.url_helpers.download_path(id: file_set.id, file: 'transcript') end end diff --git a/app/jobs/spot/repository_fixity_check_job.rb b/app/jobs/spot/repository_fixity_check_job.rb index 442e2e1ea..726553e4c 100644 --- a/app/jobs/spot/repository_fixity_check_job.rb +++ b/app/jobs/spot/repository_fixity_check_job.rb @@ -18,12 +18,14 @@ class RepositoryFixityCheckJob < ApplicationJob around_perform :wrap_check # @param [true, false] :force Ignore the 'max days between check' parameter - def perform + def perform(force: false) @count = 0 + opts = { async_jobs: false } + opts[:max_days_between_fixity_checks] = -1 if force ::FileSet.find_each do |file_set| @count += 1 - Hyrax::FileSetFixityCheckService.new(file_set, async_jobs: false, max_days_between_fixity_checks: -1).fixity_check + Hyrax::FileSetFixityCheckService.new(file_set, **opts).fixity_check end end diff --git a/app/search_builders/spot/catalog_search_builder.rb b/app/search_builders/spot/catalog_search_builder.rb index b6f5b53d5..400134116 100644 --- a/app/search_builders/spot/catalog_search_builder.rb +++ b/app/search_builders/spot/catalog_search_builder.rb @@ -5,7 +5,7 @@ module Spot # the Hyrax join query for extracted text, and conditionally (for now) # display highlight matches (where present). class CatalogSearchBuilder < ::Hyrax::CatalogSearchBuilder - include BlacklightAdvancedSearch::AdvancedSearchBuilder + # include BlacklightAdvancedSearch::AdvancedSearchBuilder include BlacklightRangeLimit::RangeLimitBuilder self.default_processor_chain -= [:show_works_or_works_that_contain_files] diff --git a/app/validators/hyrax/has_one_title_validator.rb b/app/validators/hyrax/has_one_title_validator.rb index 81efaa485..f82d69cff 100644 --- a/app/validators/hyrax/has_one_title_validator.rb +++ b/app/validators/hyrax/has_one_title_validator.rb @@ -8,7 +8,7 @@ module Hyrax class HasOneTitleValidator < ActiveModel::Validator def validate(record) return unless record.title.map(&:to_s).reject(&:empty?).empty? - record.errors[:title] << "You must provide a title" + record.errors.add(:title, :invalid, message: 'You must provide a title') end end end diff --git a/app/validators/spot/date_issued_validator.rb b/app/validators/spot/date_issued_validator.rb index 542c9b710..2b1f6371e 100644 --- a/app/validators/spot/date_issued_validator.rb +++ b/app/validators/spot/date_issued_validator.rb @@ -15,11 +15,11 @@ class DateIssuedValidator < ::ActiveModel::Validator # @param [ActiveFedora::Base] record # @return [void] def validate(record) - record.errors[:date_issued] << 'Date Issued may not be blank' if record.date_issued.empty? - record.errors[:date_issued] << 'Date Issued may only contain one value' if record.date_issued.size > 1 + record.errors.add(:date_issued, :invalid, message: 'Date Issued may not be blank') if record.date_issued.empty? + record.errors.add(:date_issued, :invalid, message: 'Date Issued may only contain one value') if record.date_issued.size > 1 record.date_issued.each do |date| - record.errors[:date_issued] << 'Date Issued must be in YYYY-MM-DD, YYYY-MM, or YYYY format' unless + record.errors.add(:date_issued, :invalid, message: 'Date Issued must be in YYYY-MM-DD, YYYY-MM, or YYYY format') unless date.match?(/\A\d{4}(-\d{2}){0,2}\z/) end end diff --git a/app/validators/spot/only_urls_validator.rb b/app/validators/spot/only_urls_validator.rb index a45ec3ce9..94b46d536 100644 --- a/app/validators/spot/only_urls_validator.rb +++ b/app/validators/spot/only_urls_validator.rb @@ -11,7 +11,7 @@ def validate(record) next if values.empty? values.each do |val| - record.errors[field] << "#{val} is not a valid URL" unless val.match?(uri_regex) + record.errors.add(field, :invalid, message: "#{val} is not a valid URL") unless val.match?(uri_regex) end end end diff --git a/app/validators/spot/required_local_authority_validator.rb b/app/validators/spot/required_local_authority_validator.rb index 0296f7898..1448b7eab 100644 --- a/app/validators/spot/required_local_authority_validator.rb +++ b/app/validators/spot/required_local_authority_validator.rb @@ -16,13 +16,12 @@ def validate(record) authority_name = options[:authority] field = options[:field] authority = authority_for(authority_name) - values = record.send(field) - values = Array.wrap(values) unless values.respond_to?(:each) + values = Array.wrap(values) unless values.class < Enumerable values.each do |v| value = v.is_a?(ActiveTriples::Resource) ? v.id : v.to_s - record.errors[field] << %("#{value}" is not a valid #{field.to_s.titleize}.) if authority.find(value).empty? + record.errors.add(field, :invalid, message: %("#{value}" is not a valid #{field.to_s.titleize}.)) if authority.find(value).empty? end end diff --git a/app/validators/spot/slug_validator.rb b/app/validators/spot/slug_validator.rb index aef053501..2d9da5633 100644 --- a/app/validators/spot/slug_validator.rb +++ b/app/validators/spot/slug_validator.rb @@ -13,8 +13,8 @@ def validate(record) slugs = record.send(field).select { |id| id.start_with? 'slug:' } next if slugs.empty? - record.errors[field] << single_slug_message unless slugs.size == 1 - record.errors[field] << slug_regex_message unless slug_valid?(slugs.first) + record.errors.add(field, :invalid, message: single_slug_message) unless slugs.size == 1 + record.errors.add(field, :invalid, message: slug_regex_message) unless slug_valid?(slugs.first) end end diff --git a/config/application.rb b/config/application.rb index c15683875..37309d086 100644 --- a/config/application.rb +++ b/config/application.rb @@ -45,6 +45,11 @@ class Application < Rails::Application config.rack_cas.service = ENV['URL_HOST'].present? ? "#{ENV['URL_HOST']}/users/service" : '/users/service' config.rack_cas.extra_attributes_filter = %w[uid email givenName surname lnumber eduPersonEntitlement] + if Rails.env.test? + config.hosts << '127.0.0.1' + config.hosts << 'localhost' + end + # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. diff --git a/config/environments/development.rb b/config/environments/development.rb index 85563e35f..6ec30402a 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true Rails.application.configure do - config.hosts << ENV['URL_HOST'] if ENV['URL_HOST'].present? - config.public_file_server.enabled = true # Settings specified here will take precedence over those in config/application.rb. diff --git a/config/initializers/spot_overrides.rb b/config/initializers/spot_overrides.rb index e03df4901..d62f895aa 100644 --- a/config/initializers/spot_overrides.rb +++ b/config/initializers/spot_overrides.rb @@ -317,7 +317,7 @@ module Spot module FileSetPresenterDecorator extend ActiveSupport::Concern - included do + prepended do delegate :original_filenames, :transcript_name, :stored_derivatives, to: :solr_document end diff --git a/spec/actors/concerns/deserializes_rdf_literals_spec.rb b/spec/actors/concerns/deserializes_rdf_literals_spec.rb index 5a6e3279c..a4a9a2624 100644 --- a/spec/actors/concerns/deserializes_rdf_literals_spec.rb +++ b/spec/actors/concerns/deserializes_rdf_literals_spec.rb @@ -1,26 +1,28 @@ # frozen_string_literal: true RSpec.describe DeserializesRdfLiterals do - class TestWorkType < ActiveFedora::Base - property :title, predicate: ::RDF::Vocab::DC.title, multiple: false - property :description, predicate: ::RDF::Vocab::DC.description - end + before do + class TestWorkType < ActiveFedora::Base + property :title, predicate: ::RDF::Vocab::DC.title, multiple: false + property :description, predicate: ::RDF::Vocab::DC.description + end - class TestWorkTypeActor < Hyrax::Actors::AbstractActor - include DeserializesRdfLiterals - end + class TestWorkTypeActor < Hyrax::Actors::AbstractActor + include DeserializesRdfLiterals + end - # This is all incredibly messy because the Actor mixin is relying on a class attribute - # for a form based on naming conventions, hence this silly unused Form class and exposing - # the test classes to the Object namespace (as opposed to `let(:work_type_class) { Class.new(ActiveFedora::Base) }`) - class Hyrax::TestWorkTypeForm < ::Spot::Forms::WorkForm - include LanguageTaggedFormFields - transforms_language_tags_for :title, :description + # This is all incredibly messy because the Actor mixin is relying on a class attribute + # for a form based on naming conventions, hence this silly unused Form class and exposing + # the test classes to the Object namespace (as opposed to `let(:work_type_class) { Class.new(ActiveFedora::Base) }`) + class Hyrax::TestWorkTypeForm < ::Spot::Forms::WorkForm + include LanguageTaggedFormFields + transforms_language_tags_for :title, :description + end end - after(:suite) do - Object.remove_const(:TestWorkType) - Object.remove_const(:TestWorkTypeActor) - Hyrax.remove_const(:TestWorkTypeForm) + after do + Object.send(:remove_const, :TestWorkType) + Object.send(:remove_const, :TestWorkTypeActor) + Hyrax.send(:remove_const, :TestWorkTypeForm) end let(:work) { TestWorkType.new } diff --git a/spec/actors/spot/actors/collections_membership_actor_spec.rb b/spec/actors/spot/actors/collections_membership_actor_spec.rb index 99882397f..3c71ade00 100644 --- a/spec/actors/spot/actors/collections_membership_actor_spec.rb +++ b/spec/actors/spot/actors/collections_membership_actor_spec.rb @@ -1,16 +1,17 @@ # frozen_string_literal: true -RSpec.describe Spot::Actors::CollectionsMembershipActor do +RSpec.describe Spot::Actors::CollectionsMembershipActor, actor_stack: true do before do allow(Collection).to receive(:find).with(parent_collection.id).and_return(parent_collection) allow(Collection).to receive(:find).with(child_collection.id).and_return(child_collection) - allow(parent_collection).to receive(:share_applies_to_new_works?).and_return true - allow(child_collection).to receive(:share_applies_to_new_works?).and_return true + allow(Hyrax::CollectionType).to receive(:for).with(collection: parent_collection).and_return(mock_collection_type) + allow(Hyrax::CollectionType).to receive(:for).with(collection: child_collection).and_return(mock_collection_type) work.member_of_collections.clear child_collection.member_of_collections.clear end + let(:mock_collection_type) { instance_double(Hyrax::CollectionType, share_applies_to_new_works?: true) } let(:stack) { described_class.new(Hyrax::Actors::Terminator.new) } let(:env) { Hyrax::Actors::Environment.new(work, ability, attributes) } diff --git a/spec/factories/file_set.rb b/spec/factories/file_set.rb index 66b75b04e..c567d1d62 100644 --- a/spec/factories/file_set.rb +++ b/spec/factories/file_set.rb @@ -7,6 +7,7 @@ transient do user { create(:user) } content { nil } + original_file { nil } visibility { Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE } end diff --git a/spec/helpers/audio_visual_helper_spec.rb b/spec/helpers/audio_visual_helper_spec.rb index 7100b0fc0..665c8c04d 100644 --- a/spec/helpers/audio_visual_helper_spec.rb +++ b/spec/helpers/audio_visual_helper_spec.rb @@ -152,7 +152,10 @@ before do allow(file_set).to receive(:id).and_return('0') - allow(Hyrax::Engine.routes.url_helpers).to receive(:download_path).with(id: '0', file: 'transcript') + allow(Hyrax::Engine.routes.url_helpers) + .to receive(:download_path) + .with(id: '0', file: 'transcript') + .and_call_original end it { is_expected.to eq "/downloads/0?file=transcript" } diff --git a/spec/indexers/spot/file_set_indexer_spec.rb b/spec/indexers/spot/file_set_indexer_spec.rb index 22a2d2a33..0ab8844df 100644 --- a/spec/indexers/spot/file_set_indexer_spec.rb +++ b/spec/indexers/spot/file_set_indexer_spec.rb @@ -4,7 +4,7 @@ let(:indexer) { described_class.new(file_set) } let(:file_set) { build(:file_set, label: 'file_set_object') } - let(:mock_file) { instance_double(Hydra::PCDM::File) } + let(:mock_file) { instance_double(Hydra::PCDM::File, id: 'file_id', versions: []) } before do allow(file_set).to receive(:extracted_text).and_return(mock_file) diff --git a/spec/jobs/spot/repository_fixity_check_job_spec.rb b/spec/jobs/spot/repository_fixity_check_job_spec.rb index ce6ecc503..b34f79963 100644 --- a/spec/jobs/spot/repository_fixity_check_job_spec.rb +++ b/spec/jobs/spot/repository_fixity_check_job_spec.rb @@ -1,39 +1,30 @@ # frozen_string_literal: true RSpec.describe Spot::RepositoryFixityCheckJob do - subject(:perform_job!) { described_class.perform_now(job_opts) } - let(:service_double) { instance_double(Hyrax::FileSetFixityCheckService) } let(:fs) { instance_double(FileSet, id: 'abc123') } let(:job_opts) { {} } before do - allow(Hyrax::FileSetFixityCheckService).to receive(:new).and_return(service_double) + allow(Hyrax::FileSetFixityCheckService).to receive(:new).with(fs, **fixity_opts).and_return(service_double) allow(service_double).to receive(:fixity_check) allow(FileSet).to receive(:find_each).and_yield(fs) - perform_job! + + described_class.perform_now(**job_opts) end context 'default implementation' do - let(:opts) { { async_jobs: false } } + let(:fixity_opts) { { async_jobs: false } } it 'calls the Hyrax::FileSetFixityCheckService without async_jobs' do - expect(Hyrax::FileSetFixityCheckService) - .to have_received(:new) - .with(fs, opts) - expect(service_double).to have_received(:fixity_check).at_least(1).times end end context 'with force: true' do - let(:opts) { { async_jobs: false, max_days_between_fixity_checks: -1 } } + let(:fixity_opts) { { async_jobs: false, max_days_between_fixity_checks: -1 } } let(:job_opts) { { force: true } } it do - expect(Hyrax::FileSetFixityCheckService) - .to have_received(:new) - .with(fs, opts) - expect(service_double).to have_received(:fixity_check).at_least(1).times end end diff --git a/spec/models/collection_spec.rb b/spec/models/collection_spec.rb index 06fc04b79..8561122c2 100644 --- a/spec/models/collection_spec.rb +++ b/spec/models/collection_spec.rb @@ -37,7 +37,7 @@ context 'when the collection does not exist' do it 'raises an ObjectNotFoundError' do expect { described_class.find(param) } - .to raise_error(Hyrax::ObjectNotFoundError, %r{'id'=#{param}$}) + .to raise_error(ActiveFedora::ObjectNotFoundError, %r{'id'=#{param}$}) end end diff --git a/spec/renderers/spot/renderers/attribute_renderer_spec.rb b/spec/renderers/spot/renderers/attribute_renderer_spec.rb index ecfaf0eaa..2f84b2555 100644 --- a/spec/renderers/spot/renderers/attribute_renderer_spec.rb +++ b/spec/renderers/spot/renderers/attribute_renderer_spec.rb @@ -57,10 +57,10 @@ context 'options[:show_help_text]' do before do allow(I18n).to receive(:translate) - .with(:'simple_form.hints.defaults.title', default: [], raise: true) + .with(:'simple_form.hints.defaults.title', anything) .and_return(help_text) allow(I18n).to receive(:translate) - .with(:'blacklight.search.fields.default.show.title', raise: true) + .with(:'blacklight.search.fields.default.show.title', anything) .and_return('Title') end diff --git a/spec/search_builders/spot/catalog_search_builder_spec.rb b/spec/search_builders/spot/catalog_search_builder_spec.rb index b7194a9d1..567354dab 100644 --- a/spec/search_builders/spot/catalog_search_builder_spec.rb +++ b/spec/search_builders/spot/catalog_search_builder_spec.rb @@ -11,7 +11,8 @@ describe '#add_full_text_context' do subject(:context_query) { builder.add_full_text_context(params) } - let(:builder) { described_class.new([]).with(blacklight_params) } + let(:builder) { described_class.new([], scope_double).with(blacklight_params) } + let(:scope_double) { double('scope', blacklight_config: {}) } let(:blacklight_params) { { q: 'a cool query' } } let(:feature_available) { true } let(:params) { {} } diff --git a/spec/search_builders/spot/work_and_file_set_search_builder_spec.rb b/spec/search_builders/spot/work_and_file_set_search_builder_spec.rb index fe3a95345..cd0a7dd7c 100644 --- a/spec/search_builders/spot/work_and_file_set_search_builder_spec.rb +++ b/spec/search_builders/spot/work_and_file_set_search_builder_spec.rb @@ -1,17 +1,16 @@ # frozen_string_literal: true RSpec.describe Spot::WorkAndFileSetSearchBuilder do - let(:builder) { described_class.new([]) } + let(:builder) { described_class.new(processor_chain, scope) } + let(:processor_chain) { [] } + let(:scope) { double('scope', blacklight_config: {}) } describe '#filter_models' do subject(:params) { {} } before { builder.filter_models(params) } - it 'adds a :fq key' do + it 'adds a :fq key that is an array' do expect(params).to include :fq - end - - it 'is an array' do expect(params[:fq]).to be_an Array end diff --git a/spec/services/spot/handle_service_spec.rb b/spec/services/spot/handle_service_spec.rb index b646843c0..ee8ad3482 100644 --- a/spec/services/spot/handle_service_spec.rb +++ b/spec/services/spot/handle_service_spec.rb @@ -11,6 +11,14 @@ let(:work) { instance_double(Publication, id: 'abc123def', identifier: identifiers) } let(:identifiers) { [] } let(:handle_prefix) { '10385' } + let(:stubbed_env) do + { + 'HANDLE_SERVER_URL' => handle_server_url, + 'HANDLE_PREFIX' => handle_prefix, + 'HANDLE_CLIENT_CERT_PEM' => 'client_cert', + 'HANDLE_CLIENT_KEY_PEM' => 'client_key' + } + end before do stub_env('URL_HOST', 'http://localhost') @@ -21,16 +29,17 @@ context 'when all values are provided' do before do - stub_env('HANDLE_SERVER_URL', handle_server_url) - stub_env('HANDLE_PREFIX', handle_prefix) - stub_env('HANDLE_CLIENT_CERT_PEM', 'client_cert') - stub_env('HANDLE_CLIENT_KEY_PEM', 'client_key') + stubbed_env.each_pair { |(key, val)| stub_env(key, val) } end it { is_expected.to be true } end context 'when not all values are provided' do + before do + stubbed_env.keys.each { |key| stub_env(key, nil) } + end + it { is_expected.to be false } end end diff --git a/spec/services/spot/video_processor_spec.rb b/spec/services/spot/video_processor_spec.rb index 3d5c026d5..46e191ec5 100644 --- a/spec/services/spot/video_processor_spec.rb +++ b/spec/services/spot/video_processor_spec.rb @@ -11,6 +11,7 @@ end after { described_class.config = @original_config } + let(:directives) { { label: :thumb, format: "mp4", url: 'http://localhost:8983/fedora/rest/dev/1234/thumbnail' } } let(:opts) do { @@ -21,7 +22,7 @@ it "is configurable" do expect(subject) .to receive(:encode_file) - .with("mp4", **opts) + .with("mp4", opts) subject.process end @@ -39,7 +40,7 @@ it "creates a fedora resource and infers the name" do expect(subject) .to receive(:encode_file) - .with("webm", **opts) + .with("webm", opts) subject.process end end @@ -65,7 +66,7 @@ it "creates a fedora resource and infers the name" do expect(subject) .to receive(:encode_file) - .with("mkv", **opts) + .with("mkv", opts) subject.process end @@ -82,7 +83,7 @@ it "creates a fedora resource and infers the name" do expect(subject) .to receive(:encode_file) - .with("mkv", **opts) + .with("mkv", opts) subject.process end From 54b232c8c9da1ff10c9014707018fbe1f5a7fa07 Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Fri, 7 Nov 2025 15:56:02 -0500 Subject: [PATCH 033/116] add host to rails from environment --- config/application.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/application.rb b/config/application.rb index 37309d086..28bdda1cf 100644 --- a/config/application.rb +++ b/config/application.rb @@ -45,6 +45,9 @@ class Application < Rails::Application config.rack_cas.service = ENV['URL_HOST'].present? ? "#{ENV['URL_HOST']}/users/service" : '/users/service' config.rack_cas.extra_attributes_filter = %w[uid email givenName surname lnumber eduPersonEntitlement] + hostname = ENV['APPLICATION_FQDN'] || URI.parse(ENV['URL_HOST'] || '').hostname + config.hosts << hostname if hostname.present? + if Rails.env.test? config.hosts << '127.0.0.1' config.hosts << 'localhost' From 6a00c69230c81b0de46c046c00559bb968134c31 Mon Sep 17 00:00:00 2001 From: Anna Malantonio Date: Mon, 10 Nov 2025 10:11:21 -0500 Subject: [PATCH 034/116] update views --- app/assets/stylesheets/spot.scss | 11 +++++-- app/views/_controls.html.erb | 31 ++++++++------------ app/views/_masthead.html.erb | 28 +++++++----------- app/views/_user_util_links.html.erb | 39 ++++++++++++------------- app/views/catalog/_search_form.html.erb | 14 ++++----- config/initializers/spot_overrides.rb | 17 +---------- 6 files changed, 56 insertions(+), 84 deletions(-) diff --git a/app/assets/stylesheets/spot.scss b/app/assets/stylesheets/spot.scss index 7d6e538b3..89dc6a3c5 100644 --- a/app/assets/stylesheets/spot.scss +++ b/app/assets/stylesheets/spot.scss @@ -24,12 +24,12 @@ } [role="main"] { - margin-top: 1rem; + margin-top: 2rem; padding-bottom: 10rem; } .dashboard > [role="main"] { - margin-top: 0; + margin-top: 0.6rem; } .thumbnail.file-set { @@ -76,7 +76,8 @@ .site-footer { background-color: $tan-light; - height: 20rem; + font-size: .85rem; + min-height: 180px; } // duplicating styles from #facet-panel-collapse @@ -138,3 +139,7 @@ select[multiple] { } } } + +#metadata .form-group label { + font-weight: 700; +} diff --git a/app/views/_controls.html.erb b/app/views/_controls.html.erb index a3695e84b..3714fef68 100644 --- a/app/views/_controls.html.erb +++ b/app/views/_controls.html.erb @@ -1,20 +1,13 @@ - + \ No newline at end of file diff --git a/app/views/_masthead.html.erb b/app/views/_masthead.html.erb index 83c8a9494..c800d77dc 100644 --- a/app/views/_masthead.html.erb +++ b/app/views/_masthead.html.erb @@ -1,20 +1,12 @@ -
-
\ No newline at end of file diff --git a/app/views/_user_util_links.html.erb b/app/views/_user_util_links.html.erb index c2da425d3..ae4323a91 100644 --- a/app/views/_user_util_links.html.erb +++ b/app/views/_user_util_links.html.erb @@ -1,4 +1,4 @@ -
+ <% else %> -
  • - <%= link_to main_app.new_user_session_path do %> - - <%= t('spot.log_in') %> +
  • <% end %> diff --git a/app/views/catalog/_search_form.html.erb b/app/views/catalog/_search_form.html.erb index 531df7cb1..f63e27360 100644 --- a/app/views/catalog/_search_form.html.erb +++ b/app/views/catalog/_search_form.html.erb @@ -1,16 +1,16 @@ -<%= form_tag search_form_action, method: :get, class: "form-horizontal search-form", id: "search-form-header", role: "search" do %> - <%= render_hash_as_hidden_fields(search_state.params_for_search.except(:q, :search_field, :qt, :page, :utf8)) %> +<%= form_tag search_form_action, method: :get, class: "search-form", id: "search-form-header", role: "search" do %> + <%= render Blacklight::HiddenSearchStateComponent.new(params: search_state.params_for_search.except(:q, :search_field, :qt, :page, :utf8)) %> <%= hidden_field_tag :search_field, 'all_fields' %> -
    +
    -