Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ESSI-2062] fix iiif urls unaware of external_storage #648

Merged
merged 5 commits into from
Feb 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/controllers/purl_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def rescue_url

def jp2_url(solr_hit)
begin
Hyrax.config.iiif_image_url_builder.call(solr_hit['original_file_id_ssi'], nil, '!600,600')
IIIFFileSetPathService.new(solr_hit).iiif_image_url(size: '!600,600')
rescue StandardError
nil
end
Expand Down
8 changes: 3 additions & 5 deletions app/helpers/catalog_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ def thumbnail_url document
else
representative_document = ::SolrDocument.find(document.thumbnail_id)
end

thumbnail_file_id = representative_document&.content_location
thumbnail_file_id ||= representative_document.original_file_id
if thumbnail_file_id
Hyrax.config.iiif_image_url_builder.call(thumbnail_file_id, nil, '250,')
iiif_path_service = IIIFFileSetPathService.new(representative_document)
if iiif_path_service.lookup_id
iiif_path_service.iiif_image_url(size: '250,')
else
raise 'thumbnail_file_id is nil'
end
Expand Down
19 changes: 6 additions & 13 deletions app/models/collection_branding_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,26 +95,19 @@ def find_local_dir_name(collection_id, role)
File.join(Hyrax.config.branding_path, collection_id.to_s, role.to_s)
end

# this passes a nil value for request base_url, as our custom url builder
# does not use that argument, and the model also doesn't have a request
def generate_image_path!
if image_path.blank? && file_set_versions.any?
original_uri = file_set_versions.all.last.uri
uri_to_id = ActiveFedora::File.uri_to_id(original_uri.sub(/\/fcr.versions.*/,''))
self.image_path = \
Hyrax.config.iiif_image_url_builder.call(uri_to_id,
nil,
ESSI.config.dig(:essi, :collection_banner_size) || Hyrax.config.iiif_image_size_default)
if image_path.blank? && iiif_path_service.lookup_id.present?
self.image_path = iiif_path_service.iiif_image_url(size: ESSI.config.dig(:essi, :collection_banner_size))
save
end
end

def file_set_versions
file_set&.reload&.original_file&.versions || []
end

def uploaded_files(uploaded_file_ids)
return [] if uploaded_file_ids.empty?
Hyrax::UploadedFile.find(uploaded_file_ids)
end

def iiif_path_service
@iiif_path_service ||= IIIFFileSetPathService.new(file_set || {})
end
end
5 changes: 5 additions & 0 deletions app/models/file_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@ def ocr_language
ESSI.config.dig(:essi, :ocr_language),
'eng'].map { |l| Tesseract.try_languages(l) }.select(&:present?).first
end

# @todo revisit after Hyrax 3.x upgrade
def original_file_id
@original_file_id ||= self.original_file&.id
end
end
47 changes: 8 additions & 39 deletions app/presenters/hyrax/displays_image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,62 +8,31 @@ module Hyrax
module DisplaysImage
extend ActiveSupport::Concern

delegate :content_location, to: :solr_document

# Creates a display image only where FileSet is an image.
#
# @return [IIIFManifest::DisplayImage] the display image required by the manifest builder.
def display_image
return nil unless solr_document.image? && current_ability.can?(:read, id)

latest_file_id = lookup_original_file_id

return nil unless latest_file_id

url = Hyrax.config.iiif_image_url_builder.call(
latest_file_id,
request.base_url,
Hyrax.config.iiif_image_size_default
)
return nil unless iiif_path_service.lookup_id

# @see https://github.com/samvera-labs/iiif_manifest
IIIFManifest::DisplayImage.new(url,
IIIFManifest::DisplayImage.new(iiif_path_service.iiif_image_url,
width: width,
height: height,
iiif_endpoint: iiif_endpoint(latest_file_id))
end

def lookup_original_file_id
return content_location if content_location&.start_with?('s3://')
result = original_file_id
if result.blank?
Rails.logger.warn "original_file_id for #{id} not found, falling back to Fedora."
# result = Hyrax::VersioningService.versioned_file_id ::FileSet.find(id).original_file
result = versioned_file_id ::FileSet.find(id).original_file
end
result
iiif_endpoint: iiif_endpoint)
end

private
def iiif_path_service
@iiif_path_service ||= IIIFFileSetPathService.new(solr_document)
end

def iiif_endpoint(file_id)
def iiif_endpoint
return unless Hyrax.config.iiif_image_server?
IIIFManifest::IIIFEndpoint.new(
Hyrax.config.iiif_info_url_builder.call(file_id, request.base_url),
iiif_path_service.iiif_info_url(request.base_url),
profile: Hyrax.config.iiif_image_compliance_level_uri
)
end

# @todo remove after upgrade to Hyrax 3.x
# cherry-picked from Hyrax 3.x VersioningService
# @param [ActiveFedora::File | Hyrax::FileMetadata] content
def versioned_file_id(file)
versions = file.versions.all
if versions.present?
ActiveFedora::File.uri_to_id versions.last.uri
else
file.id
end
end
end
end
4 changes: 3 additions & 1 deletion app/services/essi/generate_pdf_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ def create_tmp_files(pdf)
fs_solr = SolrDocument.find(fs)
image_width = get_image_width(fs_solr).to_i
raise StandardError, 'Image width unavailable' unless image_width > 0 # IIIF server call requires a positive integer value
uri = Hyrax.config.iiif_image_url_builder.call(fs_solr.original_file_id, nil, render_dimensions(image_width))
iiif_path_service = IIIFFileSetPathService.new(fs_solr)
raise StandardError, 'Source image file unavailable' unless iiif_path_service.lookup_id
uri = iiif_path_service.iiif_image_url(size: render_dimensions(image_width))
URI.open(uri) do |file|
page_size = [CoverPageGenerator::LETTER_WIDTH, CoverPageGenerator::LETTER_HEIGHT]
file.binmode
Expand Down
2 changes: 1 addition & 1 deletion app/services/iiif_collection_thumbnail_path_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ class << self
# @return the network path to the thumbnail
# @param [FileSet] thumbnail the object that is the thumbnail
def thumbnail_path(thumbnail)
Hyrax.config.iiif_image_url_builder.call(thumbnail.original_file.id, nil, '250,')
IIIFFileSetPathService.new(thumbnail).iiif_image_url(size: '250,')
# Hyrax::Engine.routes.url_helpers.download_path(thumbnail.id,
# file: 'thumbnail')
end
Expand Down
76 changes: 76 additions & 0 deletions app/services/iiif_file_set_path_service.rb
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pulling these IIIF behaviors into this service presents an opportunity to make some spec tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dlpierce spec tests added, which did indeed lead to some revisions!

Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
class IIIFFileSetPathService
attr_reader :file_set

# @param [ActiveFedora::SolrHit, FileSet, Hash, SolrDocument, Hyrax::FileSetPresenter] file_set
def initialize(file_set)
file_set = SolrDocument.new(file_set) if file_set.class.in? [ActiveFedora::SolrHit, Hash]
if [:id, :content_location, :original_file_id].map { |m| file_set.respond_to?(m) }.all?
@file_set = file_set
else
raise StandardError, 'Provided file_set does not meet interface requirements'
end
end

def lookup_id
@lookup_id ||= versioned_lookup_id
end

# @return [String] a URL that resolves to an image provided by a IIIF image server
def iiif_image_url(base_url: nil, size: nil)
return unless lookup_id
Hyrax.config.iiif_image_url_builder.call(lookup_id, base_url, size || Hyrax.config.iiif_image_size_default)
end

# @return [String] a URL that resolves to an info.json file provided by a IIIF image server
def iiif_info_url(base_url)
return unless lookup_id
Hyrax.config.iiif_info_url_builder.call(lookup_id, base_url)
end

private
def versioned_lookup_id
result = file_set.content_location || file_set.original_file_id
if result.blank?
Rails.logger.warn "original_file_id for #{file_set.id} not found, falling back to Fedora."
# result = Hyrax::VersioningService.versioned_file_id(original_file)
result = versioned_file_id(original_file) if original_file
end
if result.blank?
Rails.logger.warn "original_file for #{file_set.id} not found, versioned_lookup_id failed."
nil
else
result
end
end

# @return [Hydra::PCDM::File, nil]
def original_file
@original_file ||=
case file_set
when FileSet
file_set.original_file
else
begin
FileSet.find(file_set.id).original_file
rescue => error
Rails.logger.error "original_file lookup for #{file_set.id} raised error: #{error.inspect}"
nil
end
end
end

# @todo remove after upgrade to Hyrax 3.x
# cherry-picked from Hyrax 3.x VersioningService
# @param [ActiveFedora::File | Hyrax::FileMetadata] content
def versioned_file_id(file)
@versioned_file_id ||= begin
raise StandardError, 'No original_file available for versioning' if file.nil?
versions = file.versions.all
if versions.present?
ActiveFedora::File.uri_to_id versions.last.uri
else
file.id
end
end
end
end
4 changes: 1 addition & 3 deletions app/services/iiif_thumbnail_path_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ class << self
# @return the network path to the thumbnail
# @param [FileSet] thumbnail the object that is the thumbnail
def thumbnail_path(thumbnail)
return unless thumbnail.original_file
id = thumbnail.content_location || thumbnail.original_file.id
Hyrax.config.iiif_image_url_builder.call(id, nil, '250,')
IIIFFileSetPathService.new(thumbnail).iiif_image_url(size: '250,')
# Hyrax::Engine.routes.url_helpers.download_path(thumbnail.id,
# file: 'thumbnail')
end
Expand Down
2 changes: 1 addition & 1 deletion config/essi_config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ test:
rails:
secret_key_base: c9dd75fe2cce941807d14e04c09aa1f9ae41b6e1f7ba9d2f33142659acf9491f4a7835aad0c4110bf2fa40f2a1e6f7a62048b5a1e1a32c361c8c16d772e40bf0
cantaloupe:
iiif_server_url: /iiif/2/
iiif_server_url: https://localhost:3000/iiif/2/
browse_everything:
file_system:
:home: <%= Rails.root.join("spec/fixtures") %>
Expand Down
3 changes: 3 additions & 0 deletions lib/extensions/extensions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -188,3 +188,6 @@ def attribute_will_change!(attr)

# support for rendering an orphan FileSet
Hyrax::FileSetsController.prepend Extensions::Hyrax::FileSetsController::RenderOrphanFileSet

# support for FileSetPresenter#content_location
Hyrax::FileSetPresenter.include Extensions::Hyrax::FileSetPresenter::ContentLocation
9 changes: 9 additions & 0 deletions lib/extensions/hyrax/file_set_presenter/content_location.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module Extensions
module Hyrax
module FileSetPresenter
module ContentLocation
delegate :content_location, to: :solr_document
end
end
end
end
15 changes: 4 additions & 11 deletions spec/models/collection_branding_info_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@
RSpec.describe CollectionBrandingInfo, type: :model do
let(:banner) { FactoryBot.build(:collection_branding_banner) }
let(:file_set) { FactoryBot.create(:file_set, id: 'file_set_id', uri: 'file_set_uri') }
let(:version) { double(uri: 'version_uri') }
let(:versions) { double(any?: true, all: self, last: version) }

let(:local_file) { File.open(RSpec.configuration.fixture_path + '/world.png') }
let(:local_file_set) { FactoryBot.create(:file_set, content: local_file) }

describe '#initialize' do
context 'with local_path value provided' do
Expand Down Expand Up @@ -101,24 +100,18 @@
banner.send(:image_path=, nil)
end
context 'and unable to generate one' do
before do
allow(banner).to receive(:file_set_versions).and_return([])
end
it 'returns nil' do
expect(banner.file_set_image_path).to be_nil
end
end
context 'but able to generate one' do
before do
allow(banner).to receive(:file_set_versions).and_return(versions)
allow(versions).to receive(:all).and_return(versions)
end
let(:banner) { FactoryBot.build(:collection_branding_banner, file_set_id: local_file_set.id) }
it 'sets the image_path value' do
expect(banner).to receive(:image_path=)
banner.file_set_image_path
end
it 'returns the image_path value' do
expect(banner.file_set_image_path).to match version.uri
expect(banner.file_set_image_path).to match /^http/
end
end
end
Expand Down
Loading