From a2d1d36baf9bb680cd573cb5a7cc4697e7876753 Mon Sep 17 00:00:00 2001 From: Jeremy Friesen <jeremy.n.friesen@gmail.com> Date: Tue, 28 Nov 2023 15:17:22 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=81=20=20=20Add=20conditional=20PDF=20?= =?UTF-8?q?split=20or=20PDF.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a preliminary commit to add conditional logic for either rendering PDF.js or IIIF viewer for split pages. There is a particular case where QA might appear to fail: 1. Set the Tenant to use UV 2. Create a work with a PDF (this will split the page) 3. Go to the work's page, you'll see the UV 4. Set the Tenant to use PDF.js 5. Refresh the work page, and you should see PDF.js but instead will see the UV. To work around this (and realistically how most people will experience this): 1. Set the Tenant to use UV 2. Create a work with a PDF 3. Go to the Dashboard 4. Click the work's URL 5. You should see a UV for the PDF 6. Close the work's page 7. Change the tenant to use PDF.js 8. Go to the Dashboard 9. Click the work's URL The conjecture is that there's some iframe and/or turbolinks caching. Given that this is an edge case regarding toggling on and off a tenant feature, we think the work around is adequate. Related to: - https://github.com/scientist-softserv/palni-palci/issues/666 - https://github.com/scientist-softserv/palni-palci/issues/675 - https://github.com/scientist-softserv/palni-palci/issues/704 - https://github.com/scientist-softserv/palni-palci/issues/705 - https://github.com/scientist-softserv/atla-hyku/issues/162 Co-authored-by: Shana Moore <shana@scientist.com> Co-authored-by: Kirk Wang <kirk.wang@scientist.com> --- Gemfile.lock | 22 +- app/helpers/iiif_print_helper.rb | 5 + app/models/concerns/pdf_behavior.rb | 5 + app/presenters/hyku/work_show_presenter.rb | 25 +-- app/services/iiif_print/tenant_config.rb | 202 +++++++++++++++++ config/application.rb | 16 ++ ..._print_pending_relationships.iiif_print.rb | 8 + db/schema.rb | 5 +- docker-compose.bundle.yml | 9 + .../displays_content_decorator_spec.rb | 15 ++ .../hyku/work_show_presenter_spec.rb | 74 ++++++- .../services/iiif_print/tenant_config_spec.rb | 208 ++++++++++++++++++ 12 files changed, 566 insertions(+), 28 deletions(-) create mode 100644 app/helpers/iiif_print_helper.rb create mode 100644 app/services/iiif_print/tenant_config.rb create mode 100644 db/migrate/20231128191136_add_model_details_to_iiif_print_pending_relationships.iiif_print.rb create mode 100644 docker-compose.bundle.yml create mode 100644 spec/presenters/concerns/hyrax/iiif_av/displays_content_decorator_spec.rb create mode 100644 spec/services/iiif_print/tenant_config_spec.rb diff --git a/Gemfile.lock b/Gemfile.lock index f970628a..adda38e2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -43,17 +43,15 @@ GIT GIT remote: https://github.com/scientist-softserv/iiif_print.git - revision: 8fdf56e151b5adb7e6aab1cd29d39b74554ed48a + revision: 50b2659f4584ac85f468c7fcec56398f6221a412 branch: main specs: iiif_print (1.0.0) - blacklight_iiif_search (~> 1.0) - dry-monads (~> 1.4.0) - hyrax (>= 2.5, < 4.0) + blacklight_iiif_search (>= 1.0, < 3.0) + derivative-rodeo (~> 0.5) + hyrax (>= 2.5, < 6) nokogiri (>= 1.13.2) - rails (~> 5.0) rdf-vocab (~> 3.0) - reform-rails (= 0.2.3) GIT remote: https://github.com/tawan/active-elastic-job.git @@ -313,6 +311,15 @@ GEM declarative-option (0.1.0) deprecation (1.1.0) activesupport + derivative-rodeo (0.5.2) + activesupport (>= 5) + aws-sdk-s3 + aws-sdk-sqs + httparty + marcel + mime-types + mini_magick + nokogiri devise (4.8.1) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -469,6 +476,9 @@ GEM hiredis (0.6.3) htmlentities (4.3.4) http_logger (0.7.0) + httparty (0.21.0) + mini_mime (>= 1.0.0) + multi_xml (>= 0.5.2) httpclient (2.8.3) hydra-access-controls (11.0.7) active-fedora (>= 10.0.0) diff --git a/app/helpers/iiif_print_helper.rb b/app/helpers/iiif_print_helper.rb new file mode 100644 index 00000000..0c1859cf --- /dev/null +++ b/app/helpers/iiif_print_helper.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +module IiifPrintHelper + include IiifPrint::IiifPrintHelperBehavior +end diff --git a/app/models/concerns/pdf_behavior.rb b/app/models/concerns/pdf_behavior.rb index ca2ede58..bba7a8af 100644 --- a/app/models/concerns/pdf_behavior.rb +++ b/app/models/concerns/pdf_behavior.rb @@ -25,6 +25,11 @@ module PdfBehavior end after_initialize :set_default_show_pdf_viewer, :set_default_show_pdf_download_button + + include IiifPrint.model_configuration( + pdf_split_child_model: GenericWork, + pdf_splitter_service: IiifPrint::TenantConfig::PdfSplitter + ) end private diff --git a/app/presenters/hyku/work_show_presenter.rb b/app/presenters/hyku/work_show_presenter.rb index 3a3f3e49..21796933 100644 --- a/app/presenters/hyku/work_show_presenter.rb +++ b/app/presenters/hyku/work_show_presenter.rb @@ -11,6 +11,12 @@ class WorkShowPresenter < Hyrax::WorkShowPresenter include Hyrax::IiifAv::DisplaysIiifAv Hyrax::MemberPresenterFactory.file_presenter_class = Hyrax::IiifAv::IiifFileSetPresenter + ## + # NOTE: IIIF Print prepends a IiifPrint::WorkShowPresenterDecorator to Hyrax::WorkShowPresenter + # However, with the above `include Hyrax::IiifAv::DisplaysIiifAv` we obliterate that logic. So + # we need to re-introduce that logic. + prepend IiifPrint::TenantConfig::WorkShowPresenterDecorator + delegate :title_or_label, :extent, :show_pdf_viewer, :show_pdf_download_button, to: :solr_document # OVERRIDE Hyrax v2.9.0 here to make featured collections work @@ -58,15 +64,6 @@ def user_can_feature_collection? end # End Featured Collections Methods - # @return [Boolean] render a IIIF viewer - def iiif_viewer? - Hyrax.config.iiif_image_server? && - representative_id.present? && - representative_presenter.present? && - iiif_media? && - members_include_viewable? - end - def video_embed_viewer? extract_video_embed_presence end @@ -96,16 +93,6 @@ def extract_video_embed_presence solr_document[:video_embed_tesim]&.all?(&:present?) end - def iiif_media?(presenter: representative_presenter) - presenter.image? || presenter.video? || presenter.audio? - end - - def members_include_viewable? - file_set_presenters.any? do |presenter| - iiif_media?(presenter: presenter) && current_ability.can?(:read, presenter.id) - end - end - def extract_from_identifier(rgx) if solr_document['identifier_tesim'].present? ref = solr_document['identifier_tesim'].map do |str| diff --git a/app/services/iiif_print/tenant_config.rb b/app/services/iiif_print/tenant_config.rb new file mode 100644 index 00000000..96bbaf95 --- /dev/null +++ b/app/services/iiif_print/tenant_config.rb @@ -0,0 +1,202 @@ +# frozen_string_literal: true + +# rubocop:disable Metrics/LineLength +module IiifPrint + ## + # This module encapsulates the logic for whether or not we'll use the IIIF Print services for the + # current tenant/account. The IIIF Print services does the following: + # + # - Skipping IIIF Print based derivative generation + # - Skipping PDF Splitting + # - Ignoring showing PDFs in the UV + # + # @note I am specifically isolating as much of this code into one module as possible, so that it + # it is hopefully easier to understand the configuration requirements and scope to this + # change. At some point, this might make sense to bring into IIIF Print directly. + # + # @see https://github.com/scientist-softserv/palni-palci/issues/656 palni-palci#656 + # @see https://github.com/scientist-softserv/palni-palci/issues/657 palni-palci#657 + # @see https://github.com/scientist-softserv/palni-palci/issues/658 palni-palci#658 + # @see https://github.com/scientist-softserv/palni-palci/issues/659 palni-palci#659 + module TenantConfig + ## + # When we were not planning on calling the underlying IiifPrint service but did due to some kind + # of faulty programming logic. + # + # @note This is raised as a guard to say "Hey, you thought you weren't using IIIF Print but your + # code's logic paths say otherwise." + class LeakyAbstractionError < StandardError + def initialize(klass:, method_name:) + super("Called #{klass}##{method_name} when we had said that #{klass} was not valid because we weren't using IIIF Print") + end + end + + ## + # If the default PDF viewer (PDF.js) is enabled, this method returns false, + # meaning the application should not use IIIF Print. If the default viewer is + # disabled, this method returns true, meaning the application should use IIIF Print. + def self.use_iiif_print? + !::Flipflop.default_pdf_viewer? + end + + ## + # This class implements the interface of the Hyrax::DerivativeService. It is responsible for + # negotiating whether or not the DerivativeService is "on" for the current tenant. + # + # @see https://github.com/samvera/hyrax/blob/08ef6c9a4fac489972eea9be53403e173f4ffb29/app/services/hyrax/derivative_service.rb Hyrax::DerivativeService + class DerivativeService + ## + # This allows you to specify the IIIF derivative service to use when the tenant has chosen to + # use IIIF Print for processing PDFs. + # + # If you are using the DerivativeRodeo, you'd specify something else. + class_attribute :iiif_service_class, default: ::IiifPrint::PluggableDerivativeService + + def initialize(file_set) + @file_set = file_set + end + + delegate :use_iiif_print?, to: TenantConfig + + def valid? + return false unless use_iiif_print? + + iiif_print_service_instance.valid? + end + + %i[create_derivatives cleanup_derivatives].each do |method_name| + define_method(method_name) do |*args| + raise LeakyAbstractionError.new(klass: self.class, method_name: method_name) unless use_iiif_print? + + iiif_print_service_instance.public_send(method_name, *args) + end + end + + ## + # @api private + # + # @note Public to ease testing. + def iiif_print_service_instance + @iiif_print_service_instance ||= iiif_service_class.new(@file_set) + end + end + + ## + # This is the pdf_splitter_service that will be used. If the tenant does not allow PDF splitting + # we will return an empty array. + # + # @example + # + # class MyWork + # include IiifPrint.model_configuration( + # pdf_split_child_model: Attachment, + # pdf_splitter_service: IiifPrint::TenantConfig::PdfSplitter, + # derivative_service_plugins: [ IiifPrint::TextExtractionDerivativeService ]) + # end + # + # @see https://github.com/scientist-softserv/iiif_print/blob/9e7837ce4bd08bf8fff9126455d0e0e2602f6018/lib/iiif_print.rb#L86-L138 Documentation for configuring + # @see https://github.com/scientist-softserv/adventist-dl/blob/d7676bdac2c672f09b28086d7145b68306978950/app/models/image.rb#L14-L20 Example implementation + module PdfSplitter + mattr_accessor :iiif_print_splitter + self.iiif_print_splitter = ::IiifPrint::SplitPdfs::PagesToJpgsSplitter + + ## + # @api public + def self.call(*args) + return [] unless TenantConfig.use_iiif_print? + + iiif_print_splitter.call(*args) + end + end + + ## + # @see https://github.com/scientist-softserv/iiif_print/blob/9e7837ce4bd08bf8fff9126455d0e0e2602f6018/lib/iiif_print/split_pdfs/child_work_creation_from_pdf_service.rb#L10-L46 Interface of FileSetActor#service + module SkipSplittingPdfService + ## + # @return [Symbol] Always :tenant_does_not_split_pdfs + def self.conditionally_enqueue(*_args) + :tenant_does_not_split_pdfs + end + end + + ## + # This decorator should ensure that we don't call model configured :pdf_splitter_service as + # documented in {TenantConfig::PdfSplitter} and the IIIF Print gem. It avoids the potentially + # expensive conditionally enqueue logic of the super class. + # + # Why not make an `app/actors/hyrax/actors/file_set_actor_decorator.rb`? It would be lost in that + # it is decorating the decoration of the IIIF Print gem. Beside, in bringing this here, we have + # a relatively singular place for all of the configurations. + module FileSetActorDecorator + ## + # @see https://github.com/scientist-softserv/iiif_print/blob/9e7837ce4bd08bf8fff9126455d0e0e2602f6018/app/actors/iiif_print/actors/file_set_actor_decorator.rb#L33-L35 Method we're overriding + def service + return TenantConfig::SkipSplittingPdfService unless TenantConfig.use_iiif_print? + + super + end + end + + ## + # OVERRIDE IiifPrint::WorkShowPresenterDecorator + # OVERRIDE Hyrax::WorkShowPresenter + # + # In IiifPrint we overrided #members_include_viewable_image? to query for both file sets and + # child works. (Child works being the pages split off of a PDF) + # + # In Hyrax::WorkShowPresenter we're only looking at the underlying file_sets. But IiifPrint + # needs to look at multiple places. + module WorkShowPresenterDecorator + ## + # @return [Array<Symbol>] predicate methods (e.g. ending in "?") that reflect the types + # of files we want to consider for showing in the IIIF Viewer. + def iiif_media_predicates + if TenantConfig.use_iiif_print? + %i[image? audio? video? pdf?] + else + %i[image? audio? video?] + end + end + + def iiif_media?(presenter: representative_presenter) + iiif_media_predicates.any? { |predicate| presenter.try(predicate) || presenter.try(:solr_document).try(predicate) } + end + + ## + # @return [Boolean] render a IIIF viewer + # + # OVERRIDE Hyrax::WorkShowPresenter; this override introduces behavior to handle over-rides. + def iiif_viewer? + Hyrax.config.iiif_image_server? && + representative_id.present? && + representative_presenter.present? && + iiif_media? && + members_include_iiif_viewable? + end + + def members_include_iiif_viewable? + iiif_presentable_member_presenters.any? do |presenter| + iiif_media?(presenter: presenter) && current_ability.can?(:read, presenter.id) + end + end + + ## + # @return [Array<Object>] An array of presenter objects + # + # In a non-IIIF Print using scenario, we use the file_set_presenters value; that is for + # objects that are very specifically file_sets. + # + # In a IIIF Print using scenario, we use the ill-named 'file_set_ids_ssim', because a + # long-standing decision is that this field will have both file_set IDs and child work IDs. + def iiif_presentable_member_presenters + if TenantConfig.use_iiif_print? + presentable_member_ids = Array.wrap(solr_document.try(:file_set_ids) || solr_document.try(:[], 'file_set_ids_ssim')) + member_presenters_for(presentable_member_ids) + else + file_set_presenters + end + end + end + end +end +# rubocop:enable Metrics/LineLength diff --git a/config/application.rb b/config/application.rb index f858fd46..80c8c780 100644 --- a/config/application.rb +++ b/config/application.rb @@ -60,5 +60,21 @@ class Application < Rails::Application Object.include(AccountSwitch) end + + config.after_initialize do + ## + # The first "#valid?" service is the one that we'll use for generating derivatives. + Hyrax::DerivativeService.services = [ + IiifPrint::TenantConfig::DerivativeService, + Hyrax::FileSetDerivativesService + ] + + ## + # This needs to be in the after initialize so that the IiifPrint gem can do it's decoration. + # + # @see https://github.com/scientist-softserv/iiif_print/blob/9e7837ce4bd08bf8fff9126455d0e0e2602f6018/lib/iiif_print/engine.rb#L54 Where we do the override. + Hyrax::Actors::FileSetActor.prepend(IiifPrint::TenantConfig::FileSetActorDecorator) + Hyrax::WorkShowPresenter.prepend(IiifPrint::TenantConfig::WorkShowPresenterDecorator) + end end end diff --git a/db/migrate/20231128191136_add_model_details_to_iiif_print_pending_relationships.iiif_print.rb b/db/migrate/20231128191136_add_model_details_to_iiif_print_pending_relationships.iiif_print.rb new file mode 100644 index 00000000..23437715 --- /dev/null +++ b/db/migrate/20231128191136_add_model_details_to_iiif_print_pending_relationships.iiif_print.rb @@ -0,0 +1,8 @@ +# This migration comes from iiif_print (originally 20231110163052) +class AddModelDetailsToIiifPrintPendingRelationships < ActiveRecord::Migration[5.2] + def change + add_column :iiif_print_pending_relationships, :parent_model, :string + add_column :iiif_print_pending_relationships, :child_model, :string + add_column :iiif_print_pending_relationships, :file_id, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index efdb5619..31243d54 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2023_06_08_153601) do +ActiveRecord::Schema.define(version: 2023_11_28_191136) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -382,6 +382,9 @@ t.string "child_order", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "parent_model" + t.string "child_model" + t.string "file_id" t.index ["parent_id"], name: "index_iiif_print_pending_relationships_on_parent_id" end diff --git a/docker-compose.bundle.yml b/docker-compose.bundle.yml new file mode 100644 index 00000000..d3f73cdf --- /dev/null +++ b/docker-compose.bundle.yml @@ -0,0 +1,9 @@ +# Copy file to docker-compose.override.yml to override docker-compose.yml +# Only use for local development +version: '3.8' +services: + web: + command: sh -l -c "bundle && bundle exec puma -v -b tcp://0.0.0.0:3000" + worker: + command: sh -l -c "bundle && bundle exec sidekiq" + diff --git a/spec/presenters/concerns/hyrax/iiif_av/displays_content_decorator_spec.rb b/spec/presenters/concerns/hyrax/iiif_av/displays_content_decorator_spec.rb new file mode 100644 index 00000000..1ede624a --- /dev/null +++ b/spec/presenters/concerns/hyrax/iiif_av/displays_content_decorator_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Hyrax::IiifAv::DisplaysContentDecorator do + # We're prepending the DisplaysContentDecorator to the Hyrax::IiifAv::DisplaysContent + describe Hyrax::IiifAv::DisplaysContent do + describe '.public_instance_methods' do + subject { Hyrax::IiifAv::DisplaysContent.public_instance_methods } + + it { is_expected.to include(:solr_document) } + it { is_expected.to include(:current_ability) } + end + end +end diff --git a/spec/presenters/hyku/work_show_presenter_spec.rb b/spec/presenters/hyku/work_show_presenter_spec.rb index 75c948f6..5985071c 100644 --- a/spec/presenters/hyku/work_show_presenter_spec.rb +++ b/spec/presenters/hyku/work_show_presenter_spec.rb @@ -22,10 +22,36 @@ before do allow(solr_document).to receive(:representative_id).and_return(solr_document.member_ids.first) allow(ability).to receive(:can?).and_return true - allow_any_instance_of(Hyrax::IiifAv::IiifFileSetPresenter).to receive(:image?).and_return true + allow_any_instance_of(Hyrax::IiifAv::IiifFileSetPresenter).to receive(:image?).and_return false + allow_any_instance_of(Hyrax::IiifAv::IiifFileSetPresenter).to receive(:pdf?).and_return false + allow_any_instance_of(Hyrax::IiifAv::IiifFileSetPresenter).to receive(:video?).and_return false + allow_any_instance_of(Hyrax::IiifAv::IiifFileSetPresenter).to receive(:audio?).and_return false end - it { is_expected.to be true } + context 'method owner' do + # I was noticing load logic issues, so I'm adding this spec for verification + subject { presenter.method(:iiif_viewer?).owner } + + it { is_expected.to eq(IiifPrint::TenantConfig::WorkShowPresenterDecorator) } + end + + context "for a PDF file" do + let!(:test_strategy) { Flipflop::FeatureSet.current.test! } + + before { allow_any_instance_of(Hyrax::IiifAv::IiifFileSetPresenter).to receive(:pdf?).and_return true } + + context 'when the tenant is not configured to use IIIF Print' do + before { test_strategy.switch!(:default_pdf_viewer, true) } + + it { is_expected.to be false } + end + + context 'when the tenant is configured to use IIIF Print' do + before { test_strategy.switch!(:default_pdf_viewer, false) } + + it { is_expected.to be true } + end + end context "for an audio file" do before do @@ -35,6 +61,14 @@ it { is_expected.to be true } end + context "for an image file" do + before do + allow_any_instance_of(Hyrax::IiifAv::IiifFileSetPresenter).to receive(:image?).and_return true + end + + it { is_expected.to be true } + end + context "for a video file" do before do allow_any_instance_of(Hyrax::IiifAv::IiifFileSetPresenter).to receive(:video?).and_return true @@ -120,5 +154,41 @@ expect(presenter.isbns).to be_empty end end + + describe "#parent_works" do + let(:public_doc) { double(SolrDocument, public?: true) } + let(:non_public_doc) { double(SolrDocument, public?: false) } + let(:parent_docs) { [public_doc, non_public_doc] } + let(:current_user) { double(User, ability: double) } + + before do + allow(solr_document).to receive(:load_parent_docs).and_return(parent_docs) + end + + it 'returns the parent works of the solr document' do + parent_docs.each do |doc| + allow(doc).to receive(:public?).and_return(true) # Assumes all parent docs are public + end + + expect(presenter.parent_works).to eq(parent_docs) + end + + context 'when a public doc is not public' do + it 'excludes non-public documents' do + allow(non_public_doc).to receive(:public?).and_return(false) + + expect(presenter.parent_works).to eq([public_doc]) + end + end + + context 'with a current user and their ability' do + it 'filters based on user ability' do + allow(current_user.ability).to receive(:can?).with(:read, public_doc).and_return(false) + allow(current_user.ability).to receive(:can?).with(:read, non_public_doc).and_return(true) + + expect(presenter.parent_works(current_user)).to eq([non_public_doc]) + end + end + end end end diff --git a/spec/services/iiif_print/tenant_config_spec.rb b/spec/services/iiif_print/tenant_config_spec.rb new file mode 100644 index 00000000..148ec31d --- /dev/null +++ b/spec/services/iiif_print/tenant_config_spec.rb @@ -0,0 +1,208 @@ +# frozen_string_literal: true + +require 'spec_helper' + +## +# Yes good reader this is a lot of modules that have specs; however, they are a cohesive and atomic +# unit. That is to say they each build towards the feature of "whether or not we use IIIF Print". +# +# So instead of sprinkling these all around, I opted to compartmentalize them in the +# IiifPrint::TenantConfig module. +# +# rubocop:disable RSpec/DescribeClass +RSpec.describe 'Tenant Config for IIIF Print' do + let!(:test_strategy) { Flipflop::FeatureSet.current.test! } + + describe IiifPrint::TenantConfig do + describe '.use_iiif_print?' do + subject { described_class.use_iiif_print? } + + context 'by default' do + it { is_expected.to be_falsey } + end + + context 'when the feature is flipped to false' do + before { test_strategy.switch!(:default_pdf_viewer, true) } + + it { is_expected.to be_falsey } + end + + context 'when the feature is flipped to true' do + before { test_strategy.switch!(:default_pdf_viewer, false) } + + it { is_expected.to be_truthy } + end + end + end + + describe IiifPrint::TenantConfig::DerivativeService do + let(:fake_service_class) do + # Creating a class that inherits from the configured service but doesn't do all the antics of + # that service. For testing and implementation purposes we must assume the underlying thing + # works as intended, but are instead testing that we're properly calling things. + Class.new(described_class.iiif_service_class) do + def initialize(file_set); end + end + end + + let(:file_set) { double(FileSet) } + + let(:instance) { described_class.new(file_set).tap { |i| i.iiif_service_class = fake_service_class } } + + describe '#iiif_service_class' do + subject { described_class.iiif_service_class } + + it { is_expected.to eq(::IiifPrint::PluggableDerivativeService) } + end + + describe '#valid?' do + subject { instance.valid? } + + context 'when the feature is flipped to false' do + before { test_strategy.switch!(:default_pdf_viewer, true) } + + it { is_expected.to be_falsey } + end + + context 'when the feature is flipped to true' do + before { test_strategy.switch!(:default_pdf_viewer, false) } + + it 'delegates to the configured iiif_service' do + expect(instance.iiif_print_service_instance).to receive(:valid?) + subject + end + end + end + + describe '#create_derivatives' do + subject { instance.create_derivatives("filename") } + + context 'when the feature is flipped to false' do + before { test_strategy.switch!(:default_pdf_viewer, true) } + + it 'raises an error' do + expect { subject }.to raise_error(IiifPrint::TenantConfig::LeakyAbstractionError) + end + end + + context 'when the feature is flipped to true' do + before { test_strategy.switch!(:default_pdf_viewer, false) } + it 'delegates to the configured iiif_service' do + expect(instance.iiif_print_service_instance).to receive(:create_derivatives) + subject + end + end + end + + describe '#cleanup_derivatives' do + subject { instance.cleanup_derivatives } + + context 'when the feature is flipped to false' do + before { test_strategy.switch!(:default_pdf_viewer, true) } + + it 'raises an error' do + expect { subject }.to raise_error(IiifPrint::TenantConfig::LeakyAbstractionError) + end + end + + context 'when the feature is flipped to true' do + before { test_strategy.switch!(:default_pdf_viewer, false) } + it 'delegates to the configured iiif_service' do + expect(instance.iiif_print_service_instance).to receive(:cleanup_derivatives) + subject + end + end + end + end + + describe IiifPrint::TenantConfig::PdfSplitter do + describe '.iiif_print_splitter' do + subject { described_class.iiif_print_splitter } + + it { is_expected.to eq(::IiifPrint::SplitPdfs::PagesToJpgsSplitter) } + end + + describe '.call' do + subject { described_class.call(:arg) } + + context 'when the feature is flipped to false' do + before { test_strategy.switch!(:default_pdf_viewer, true) } + + it { is_expected.to eq([]) } + end + + context 'when the feature is flipped to true' do + before { test_strategy.switch!(:default_pdf_viewer, false) } + + it 'delegates to the configured .iiif_print_splitter' do + expect(described_class.iiif_print_splitter).to receive(:call).with(:arg) + subject + end + end + end + end + + describe IiifPrint::TenantConfig::SkipSplittingPdfService do + describe '.conditionally_enqueue' do + subject { described_class.conditionally_enqueue } + + it { is_expected.to eq(:tenant_does_not_split_pdfs) } + end + end + + describe Hyrax::Actors::FileSetActor do + let(:instance) { described_class.new(:file_set, :user) } + + ## + # The purpose of this spec is to demonstrate that load sequence of modules correctly evaluates + describe '#service' do + subject { instance.service } + + context 'when the feature is flipped to false' do + before { test_strategy.switch!(:default_pdf_viewer, true) } + + it { is_expected.to eq(IiifPrint::TenantConfig::SkipSplittingPdfService) } + end + + context 'when the feature is flipped to true' do + before { test_strategy.switch!(:default_pdf_viewer, false) } + + it { is_expected.to eq(IiifPrint::SplitPdfs::ChildWorkCreationFromPdfService) } + end + end + end + + ## + # Much like the Hyrax::Actors::FileSetActor, we need to ensure that we've registered derivatives + # in the correct manner. + # + # see config/application.rb + describe Hyrax::DerivativeService do + describe '.services' do + subject { described_class.services } + + it { is_expected.to match_array([IiifPrint::TenantConfig::DerivativeService, Hyrax::FileSetDerivativesService]) } + end + end + + describe Hyrax::WorkShowPresenter do + let(:instance) { described_class.new(:solr_doc, :ability) } + + describe '#iiif_media_predicates' do + subject { instance.iiif_media_predicates } + + context 'when the feature is flipped to false' do + before { test_strategy.switch!(:default_pdf_viewer, true) } + + it { is_expected.to eq(%i[image? audio? video?]) } + end + + context 'when the feature is flipped to true' do + before { test_strategy.switch!(:default_pdf_viewer, false) } + + it { is_expected.to eq(%i[image? audio? video? pdf?]) } + end + end + end +end +# rubocop:enable RSpec/DescribeClass