diff --git a/app/controllers/file_controller.rb b/app/controllers/file_controller.rb index f41392db..d8130c4d 100644 --- a/app/controllers/file_controller.rb +++ b/app/controllers/file_controller.rb @@ -70,7 +70,7 @@ def cache_headers end def current_file - @file ||= StacksFile.new(id: params[:id], file_name: params[:file_name], cocina:) + @file ||= StacksFile.new(file_name: params[:file_name], cocina:) end def cocina diff --git a/app/controllers/iiif/auth/v2/probe_service_controller.rb b/app/controllers/iiif/auth/v2/probe_service_controller.rb index 719d070c..551b659a 100644 --- a/app/controllers/iiif/auth/v2/probe_service_controller.rb +++ b/app/controllers/iiif/auth/v2/probe_service_controller.rb @@ -13,9 +13,12 @@ def show # rubocop:disable Metrics:AbcSize stacks_uri = params[:id] # this is a fully qualified URI to the resource on the stacks that the user is requesting access to parsed_uri = parse_uri(stacks_uri) - file = StacksFile.new(id: parsed_uri[:druid], file_name: parsed_uri[:file_name], cocina: Cocina.find(parsed_uri[:druid])) - json = { '@context': 'http://iiif.io/api/auth/2/context.json', type: 'AuthProbeResult2' } + begin + file = StacksFile.new(file_name: parsed_uri[:file_name], cocina: Cocina.find(parsed_uri[:druid])) + rescue Purl::Exception + return render json: json.merge(status: 404, note: { en: ["Unable to find #{parsed_uri[:druid]}"] }) + end if !file.valid? json[:status] = 400 diff --git a/app/controllers/iiif_controller.rb b/app/controllers/iiif_controller.rb index f4b44503..89d3488f 100644 --- a/app/controllers/iiif_controller.rb +++ b/app/controllers/iiif_controller.rb @@ -153,7 +153,6 @@ def canonical_url def stacks_file StacksFile.new( - id: identifier_params[:id], file_name: "#{identifier_params[:file_name]}.jp2", cocina: Cocina.find(identifier_params[:id]) ) diff --git a/app/controllers/legacy_image_service_controller.rb b/app/controllers/legacy_image_service_controller.rb index 7dfea079..ced816f4 100644 --- a/app/controllers/legacy_image_service_controller.rb +++ b/app/controllers/legacy_image_service_controller.rb @@ -31,7 +31,7 @@ def load_image end def stacks_file - StacksFile.new(id:, file_name:, cocina: Cocina.find(id)) + StacksFile.new(file_name:, cocina: Cocina.find(id)) end def iiif_params diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb index 5531aeac..2a2abb71 100644 --- a/app/controllers/media_controller.rb +++ b/app/controllers/media_controller.rb @@ -70,7 +70,7 @@ def load_media end def stacks_file - StacksFile.new(id: params[:id], file_name: params[:file_name], cocina: Cocina.find(params[:id])) + StacksFile.new(file_name: params[:file_name], cocina: Cocina.find(params[:id])) end def current_media diff --git a/app/models/cocina.rb b/app/models/cocina.rb index 92175ea6..6492748c 100644 --- a/app/models/cocina.rb +++ b/app/models/cocina.rb @@ -32,6 +32,10 @@ def initialize(data) attr_accessor :data + def druid + @druid ||= data.fetch('externalIdentifier').delete_prefix('druid:') + end + def find_file(file_name) file_sets = data.dig('structural', 'contains') raise(ActionController::MissingFile, "File not found '#{file_name}'") unless file_sets # Trap for Collections @@ -61,7 +65,7 @@ def files(&) def files_from_json data.dig('structural', 'contains').each do |fileset| fileset.dig('structural', 'contains').each do |file| - file = StacksFile.new(id: data.fetch('externalIdentifier').delete_prefix('druid:'), file_name: file['filename'], cocina: self) + file = StacksFile.new(file_name: file['filename'], cocina: self) yield file end end diff --git a/app/models/stacks_file.rb b/app/models/stacks_file.rb index 7911bd6b..76b2897f 100644 --- a/app/models/stacks_file.rb +++ b/app/models/stacks_file.rb @@ -6,13 +6,16 @@ class StacksFile include ActiveModel::Validations - def initialize(id:, file_name:, cocina:) - @id = id + def initialize(file_name:, cocina:) @file_name = file_name @cocina = cocina end - attr_reader :id, :file_name, :cocina + attr_reader :file_name, :cocina + + def id + cocina.druid + end validates :id, format: { with: StorageRoot::DRUID_PARTS_PATTERN } @@ -42,7 +45,7 @@ def treeified_path end def storage_root - @storage_root ||= StorageRoot.new(druid: id, file_name:) + @storage_root ||= StorageRoot.new(cocina:, file_name:) end def stacks_rights diff --git a/app/models/stacks_rights.rb b/app/models/stacks_rights.rb index 26677464..df27ac94 100644 --- a/app/models/stacks_rights.rb +++ b/app/models/stacks_rights.rb @@ -5,6 +5,8 @@ class StacksRights attr_reader :cocina_file, :cocina + # @param [String] file_name + # @param [Cocina] cocina def initialize(file_name:, cocina:) @cocina = cocina @cocina_file = cocina.find_file(file_name) diff --git a/app/models/storage_root.rb b/app/models/storage_root.rb index dc2965ad..6e4d6052 100644 --- a/app/models/storage_root.rb +++ b/app/models/storage_root.rb @@ -4,10 +4,17 @@ class StorageRoot DRUID_PARTS_PATTERN = /\A([b-df-hjkmnp-tv-z]{2})([0-9]{3})([b-df-hjkmnp-tv-z]{2})([0-9]{4})\z/i - def initialize(druid:, file_name:) - @druid = druid - @druid_parts = druid.match(DRUID_PARTS_PATTERN) + # @param [String] file_name + # @param [Cocina] cocina + def initialize(file_name:, cocina:) @file_name = file_name + @cocina = cocina + end + + delegate :druid, to: :cocina + + def druid_parts + @druid_parts ||= druid.match(DRUID_PARTS_PATTERN) end def absolute_path @@ -28,7 +35,7 @@ def treeified_id private - attr_reader :druid, :druid_parts, :file_name + attr_reader :cocina, :file_name def path_finder @path_finder ||= path_finder_class.new(treeified_id:, druid:, file_name:) diff --git a/spec/abilities/cocina_ability_spec.rb b/spec/abilities/cocina_ability_spec.rb index 8d166e30..2b36196e 100644 --- a/spec/abilities/cocina_ability_spec.rb +++ b/spec/abilities/cocina_ability_spec.rb @@ -15,6 +15,7 @@ let(:public_json) do { + 'externalIdentifier' => 'yx350pf4616', 'structural' => { 'contains' => [ { @@ -56,13 +57,13 @@ let(:cocina) { Cocina.new(public_json) } let(:file) do - StacksFile.new(id: 'xxxxxxx', file_name: 'file.csv', cocina:) + StacksFile.new(file_name: 'file.csv', cocina:) end let(:image) do - StacksImage.new(stacks_file: StacksFile.new(id: 'yx350pf4616', file_name: 'image.jpg', cocina:)) + StacksImage.new(stacks_file: StacksFile.new(file_name: 'image.jpg', cocina:)) end let(:media) do - StacksMediaStream.new(stacks_file: StacksFile.new(id: 'xxxxxxx', file_name: 'movie.mp4', cocina:)) + StacksMediaStream.new(stacks_file: StacksFile.new(file_name: 'movie.mp4', cocina:)) end let(:thumbnail_transformation) { IIIF::Image::OptionDecoder.decode(region: 'full', size: '!400,400') } diff --git a/spec/controllers/file_controller_spec.rb b/spec/controllers/file_controller_spec.rb index 165dcd75..8dd1fa51 100644 --- a/spec/controllers/file_controller_spec.rb +++ b/spec/controllers/file_controller_spec.rb @@ -9,6 +9,7 @@ let(:public_json) do { + 'externalIdentifier' => druid, 'structural' => { 'contains' => [ { diff --git a/spec/models/iiif_image_spec.rb b/spec/models/iiif_image_spec.rb index 736639f4..4da51ad8 100644 --- a/spec/models/iiif_image_spec.rb +++ b/spec/models/iiif_image_spec.rb @@ -7,7 +7,7 @@ let(:druid) { 'nr349ct7889' } let(:file_name) { 'image.jp2' } let(:transformation) { IIIF::Image::Transformation.new(size: 'full', region: 'full') } - let(:stacks_file) { StacksFile.new(id: druid, file_name:, cocina: Cocina.new({})) } + let(:stacks_file) { StacksFile.new(file_name:, cocina: Cocina.new({ "externalIdentifier" => druid })) } let(:instance) { described_class.new(stacks_file:, base_uri:, transformation:) } describe "#remote_id" do diff --git a/spec/models/projection_spec.rb b/spec/models/projection_spec.rb index 32642eb7..01b3900b 100644 --- a/spec/models/projection_spec.rb +++ b/spec/models/projection_spec.rb @@ -99,10 +99,10 @@ describe '#response' do let(:druid) { 'nr349ct7889' } let(:file_name) { 'image.jp2' } - let(:cocina) { Cocina.new({}) } + let(:cocina) { Cocina.new({ 'externalIdentifier' => druid }) } context 'for an image' do - let(:image) { StacksImage.new(stacks_file: StacksFile.new(id: druid, file_name:, cocina:)) } + let(:image) { StacksImage.new(stacks_file: StacksFile.new(file_name:, cocina:)) } subject(:projection) { described_class.new(image, transformation) } @@ -130,7 +130,7 @@ end context 'for a restricted image' do - let(:image) { RestrictedImage.new(stacks_file: StacksFile.new(id: druid, file_name:, cocina:)) } + let(:image) { RestrictedImage.new(stacks_file: StacksFile.new(file_name:, cocina:)) } subject(:projection) { described_class.new(image, transformation) } diff --git a/spec/models/stacks_file_spec.rb b/spec/models/stacks_file_spec.rb index 01ddee07..cca03399 100644 --- a/spec/models/stacks_file_spec.rb +++ b/spec/models/stacks_file_spec.rb @@ -5,9 +5,10 @@ RSpec.describe StacksFile do let(:druid) { 'nr349ct7889' } let(:file_name) { 'image.jp2' } - let(:instance) { described_class.new(id: druid, file_name:, cocina: Cocina.new({})) } + let(:cocina) { Cocina.new({ 'externalIdentifier' => druid }) } + let(:instance) { described_class.new(file_name:, cocina:) } let(:path) { storage_root.absolute_path } - let(:storage_root) { StorageRoot.new(druid:, file_name:) } + let(:storage_root) { StorageRoot.new(cocina:, file_name:) } describe '#path' do subject { instance.path } diff --git a/spec/models/stacks_image_spec.rb b/spec/models/stacks_image_spec.rb index c470f1f6..7e5365d9 100644 --- a/spec/models/stacks_image_spec.rb +++ b/spec/models/stacks_image_spec.rb @@ -12,7 +12,7 @@ describe "#info_service" do subject { instance.send(:info_service) } - let(:stacks_file) { StacksFile.new(id: 'nr349ct7889', file_name: 'image.jp2', cocina: Cocina.new({})) } + let(:stacks_file) { StacksFile.new(file_name: 'image.jp2', cocina: Cocina.new({ 'externalIdentifier' => 'nr349ct7889' })) } let(:instance) { described_class.new(stacks_file:) } it { is_expected.to be_a IiifMetadataService } diff --git a/spec/requests/file_auth_request_spec.rb b/spec/requests/file_auth_request_spec.rb index f50af36c..c3ee7412 100644 --- a/spec/requests/file_auth_request_spec.rb +++ b/spec/requests/file_auth_request_spec.rb @@ -11,9 +11,9 @@ let(:druid) { 'nr349ct7889' } let(:file_name) { 'image.jp2' } let(:path) { storage_root.absolute_path } - let(:storage_root) { StorageRoot.new(druid:, file_name:) } + let(:storage_root) { StorageRoot.new(cocina:, file_name:) } let(:perms) { nil } - let(:stacks_file) { StacksFile.new(id: druid, file_name:, cocina:) } + let(:stacks_file) { StacksFile.new(file_name:, cocina:) } let(:cocina) { Cocina.new(public_json) } before do @@ -26,6 +26,7 @@ context 'stanford only (no location qualifications)' do let(:public_json) do { + 'externalIdentifier' => druid, 'structural' => { 'contains' => [ { @@ -69,6 +70,7 @@ context 'not stanford qualified in any way' do let(:public_json) do { + 'externalIdentifier' => druid, 'structural' => { 'contains' => [ { diff --git a/spec/requests/file_spec.rb b/spec/requests/file_spec.rb index 2c7f9086..4e1a3a3c 100644 --- a/spec/requests/file_spec.rb +++ b/spec/requests/file_spec.rb @@ -11,6 +11,7 @@ let(:file_name) { 'image.jp2' } let(:public_json) do { + 'externalIdentifier' => druid, 'structural' => { 'contains' => [ { @@ -44,6 +45,7 @@ let(:file_name) { 'path/to/image.jp2' } let(:public_json) do { + 'externalIdentifier' => druid, 'structural' => { 'contains' => [ { diff --git a/spec/requests/iiif/auth/v2/probe_service_spec.rb b/spec/requests/iiif/auth/v2/probe_service_spec.rb index 47b516bd..87d6682c 100644 --- a/spec/requests/iiif/auth/v2/probe_service_spec.rb +++ b/spec/requests/iiif/auth/v2/probe_service_spec.rb @@ -7,7 +7,7 @@ let(:file_name) { 'image.jp2' } let(:stacks_uri) { "https://stacks-uat.stanford.edu/file/druid:#{id}/#{URI.encode_uri_component(file_name)}" } let(:stacks_uri_param) { URI.encode_uri_component(stacks_uri) } - let(:public_json) { '{}' } + let(:public_json) { { "externalIdentifier" => "druid:nr349ct7889" } } # NOTE: For any unauthorized responses, the status from the service is OK...the access status of the resource is in the response body @@ -46,14 +46,15 @@ let(:id) { '111' } before do + allow(Cocina).to receive(:find).and_raise(Purl::Exception) get "/iiif/auth/v2/probe?id=#{stacks_uri_param}" end it 'returns a success response' do expect(response).to have_http_status :ok expect(response.parsed_body).to eq("@context" => "http://iiif.io/api/auth/2/context.json", - "note" => { "en" => ["Id is invalid"] }, - "status" => 400, + "note" => { "en" => ["Unable to find 111"] }, + "status" => 404, "type" => "AuthProbeResult2") end end @@ -73,6 +74,7 @@ context 'when the user has access to the resource because it is world accessible' do let(:public_json) do { + "externalIdentifier" => "druid:nr349ct7889", 'structural' => { 'contains' => [ { @@ -100,9 +102,6 @@ context 'when druid has a prefix' do it 'returns a success response' do expect(response).to have_http_status :ok - # Ensure the druid doesn't have a prefix: - expect(StacksFile).to have_received(:new).with(hash_including(id: "nr349ct7889")) - expect(response.parsed_body).to include({ "@context" => "http://iiif.io/api/auth/2/context.json", "type" => "AuthProbeResult2", @@ -116,9 +115,6 @@ it 'returns a success response' do expect(response).to have_http_status :ok - # Ensure the druid doesn't have a prefix: - expect(StacksFile).to have_received(:new).with(hash_including(id: "nr349ct7889")) - expect(response.parsed_body).to include({ "@context" => "http://iiif.io/api/auth/2/context.json", "type" => "AuthProbeResult2", @@ -145,6 +141,7 @@ let(:file_name) { 'SC0193_1982-013_b06_f01_1981-09-29.mp4' } let(:public_json) do { + "externalIdentifier" => "druid:nr349ct7889", 'structural' => { 'contains' => [ { @@ -183,8 +180,6 @@ end context 'when the requested file does not exist' do - let(:public_json) { {} } - before do allow_any_instance_of(StacksFile).to receive(:readable?).and_return(nil) get "/iiif/auth/v2/probe?id=#{stacks_uri_param}" @@ -203,6 +198,7 @@ context 'when a Stanford only resource' do let(:public_json) do { + "externalIdentifier" => "druid:nr349ct7889", 'structural' => { 'contains' => [ { @@ -283,6 +279,7 @@ context 'when the user does not have access to a location restricted resource' do let(:public_json) do { + "externalIdentifier" => "druid:nr349ct7889", 'structural' => { 'contains' => [ { @@ -346,6 +343,7 @@ context 'when the user does not have access to a stanford restricted embargoed resource' do let(:public_json) do { + "externalIdentifier" => "druid:nr349ct7889", 'access' => { 'embargo' => { "releaseDate" => Time.parse('2099-05-15').getlocal.as_json @@ -393,6 +391,7 @@ context 'when the user does not have access to an embargoed resource' do let(:public_json) do { + "externalIdentifier" => "druid:nr349ct7889", 'access' => { 'embargo' => { "releaseDate" => Time.parse('2099-05-15').getlocal.as_json diff --git a/spec/requests/iiif_auth_request_spec.rb b/spec/requests/iiif_auth_request_spec.rb index 78deab19..cd01694d 100644 --- a/spec/requests/iiif_auth_request_spec.rb +++ b/spec/requests/iiif_auth_request_spec.rb @@ -19,7 +19,8 @@ let(:transformation) { IIIF::Image::Transformation.new region:, size:, rotation:, quality:, format: } let(:druid) { 'nr349ct7889' } let(:file_name) { 'image.jp2' } - let(:stacks_file) { StacksFile.new(id: druid, file_name:, cocina: Cocina.new(public_json)) } + let(:cocina) { Cocina.new(public_json) } + let(:stacks_file) { StacksFile.new(file_name:, cocina:) } let(:current_image) { StacksImage.new(stacks_file:, transformation:) } let(:http_client) { instance_double(HTTP::Client) } @@ -35,6 +36,7 @@ context 'with a public item' do let(:public_json) do { + 'externalIdentifier' => druid, 'structural' => { 'contains' => [ { @@ -69,6 +71,7 @@ context 'with a stanford only item' do let(:public_json) do { + 'externalIdentifier' => druid, 'structural' => { 'contains' => [ { @@ -121,6 +124,7 @@ context 'with a location-restricted item' do let(:public_json) do { + 'externalIdentifier' => druid, 'structural' => { 'contains' => [ { diff --git a/spec/requests/iiif_spec.rb b/spec/requests/iiif_spec.rb index 30b43ff6..971803e0 100644 --- a/spec/requests/iiif_spec.rb +++ b/spec/requests/iiif_spec.rb @@ -16,6 +16,7 @@ let(:public_json) do { + 'externalIdentifier' => 'druid:nr349ct7889', 'structural' => { 'contains' => [ { @@ -71,6 +72,7 @@ context 'for location-restricted documents' do let(:public_json) do { + 'externalIdentifier' => 'druid:nr349ct7889', 'structural' => { 'contains' => [ { @@ -101,6 +103,7 @@ context 'for a thumbnail' do let(:public_json) do { + 'externalIdentifier' => 'druid:nr349ct7889', 'structural' => { 'contains' => [ { @@ -147,6 +150,7 @@ context 'for stanford-restricted documents' do let(:public_json) do { + 'externalIdentifier' => 'druid:nr349ct7889', 'structural' => { 'contains' => [ { @@ -187,6 +191,7 @@ context 'where no one can download' do let(:public_json) do { + 'externalIdentifier' => 'druid:nr349ct7889', 'structural' => { 'contains' => [ { @@ -223,6 +228,7 @@ context 'where stanford only no download rights' do let(:public_json) do { + 'externalIdentifier' => 'druid:nr349ct7889', 'structural' => { 'contains' => [ { diff --git a/spec/requests/media_auth_request_spec.rb b/spec/requests/media_auth_request_spec.rb index 724bec57..7bf9e5b6 100644 --- a/spec/requests/media_auth_request_spec.rb +++ b/spec/requests/media_auth_request_spec.rb @@ -8,6 +8,7 @@ let(:public_json) do { + 'externalIdentifier' => druid, 'structural' => { 'contains' => [ { @@ -30,8 +31,7 @@ let(:mock_media) do StacksMediaStream.new( - stacks_file: StacksFile.new(id: 'bb582xs1304', - file_name: 'file', + stacks_file: StacksFile.new(file_name: 'file', cocina:) ) end diff --git a/spec/requests/remote_iiif_image_delivery_spec.rb b/spec/requests/remote_iiif_image_delivery_spec.rb index bbb5c0df..fff015ee 100644 --- a/spec/requests/remote_iiif_image_delivery_spec.rb +++ b/spec/requests/remote_iiif_image_delivery_spec.rb @@ -14,6 +14,7 @@ end let(:public_json) do { + 'externalIdentifier' => 'druid:nr349ct7889', 'structural' => { 'contains' => [ { diff --git a/spec/services/iiif_info_service_spec.rb b/spec/services/iiif_info_service_spec.rb index e141be6b..c91783de 100644 --- a/spec/services/iiif_info_service_spec.rb +++ b/spec/services/iiif_info_service_spec.rb @@ -23,7 +23,7 @@ let(:image) { StacksImage.new(stacks_file:) } let(:cocina) { Cocina.new(public_json) } let(:public_json) { {} } - let(:stacks_file) { StacksFile.new(id: 'nr349ct7889', file_name: 'nr349ct7889_00_0001', cocina:) } + let(:stacks_file) { StacksFile.new(file_name: 'nr349ct7889_00_0001', cocina:) } let(:source_info) { {} } before do @@ -149,6 +149,7 @@ let(:location_service) { image_info['service'] } let(:public_json) do { + 'externalIdentifier' => 'nr349ct7889', 'structural' => { 'contains' => [ { diff --git a/spec/services/iiif_metadata_service_spec.rb b/spec/services/iiif_metadata_service_spec.rb index f2c067b2..a8a1c655 100644 --- a/spec/services/iiif_metadata_service_spec.rb +++ b/spec/services/iiif_metadata_service_spec.rb @@ -6,7 +6,7 @@ let(:base_uri) { 'https://sul-imageserver-uat.stanford.edu/cantaloupe/iiif/2/' } # 'image-server-path' let(:druid) { 'nr349ct7889' } let(:file_name) { 'image.jp2' } - let(:stacks_file) { StacksFile.new(id: druid, file_name:, cocina: Cocina.new({})) } + let(:stacks_file) { StacksFile.new(file_name:, cocina: Cocina.new({ 'externalIdentifier' => "druid:#{druid}" })) } let(:service) { described_class.new(stacks_file:, canonical_url: 'foo', base_uri:) } let(:http_client) { instance_double(HTTP::Client) } diff --git a/spec/support/image_server_path.rb b/spec/support/image_server_path.rb index 51d19be3..76ba1794 100644 --- a/spec/support/image_server_path.rb +++ b/spec/support/image_server_path.rb @@ -5,7 +5,8 @@ # reading from OCFL or not module ImageServerPath def image_server_path(druid, file_name) - CGI.escape(StorageRoot.new(druid:, file_name:).relative_path) + cocina = instance_double(Cocina, druid:) + CGI.escape(StorageRoot.new(cocina:, file_name:).relative_path) end end