Skip to content

Commit

Permalink
Merge pull request #1182 from sul-dlss/extract-cocina
Browse files Browse the repository at this point in the history
  • Loading branch information
mjgiarlo authored Jun 27, 2024
2 parents 4b64193 + d693909 commit cf9026c
Show file tree
Hide file tree
Showing 21 changed files with 104 additions and 105 deletions.
3 changes: 2 additions & 1 deletion app/controllers/object_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ class ObjectController < ApplicationController
# Return a zip of all the files if they have access to all the files.
# This will force a login if any of the files is not access=world
def show
files = Purl.files(druid)
cocina = Cocina.find(druid)
files = cocina.files
raise ActionController::RoutingError, 'No downloadable files' if files.none?

files.each do |file|
Expand Down
68 changes: 68 additions & 0 deletions app/models/cocina.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# frozen_string_literal: true

# Retrieves the public JSON (cocina) and extracts data from it.
class Cocina
extend ActiveSupport::Benchmarkable

THUMBNAIL_MIME_TYPE = 'image/jp2'

def self.find(druid)
Rails.cache.fetch("purl/#{druid}.json", expires_in: 10.minutes) do
benchmark "Fetching public json for #{druid}" do
response = Faraday.get(public_json_url(druid))
raise Purl::Exception, response.status unless response.success?

new(JSON.parse(response.body))
end
end
end

def self.public_json_url(druid)
"#{Settings.purl.url}#{druid}.json"
end

def self.logger
Rails.logger
end

def initialize(data)
@data = data
end

attr_accessor :data

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

file_sets.lazy.flat_map { |file_set| file_set.dig('structural', 'contains') }
.find { |file| file['filename'] == file_name } || raise(ActionController::MissingFile, "File not found '#{file_name}'")
end

def thumbnail_file
data.dig('structural', 'contains')
.lazy.flat_map { |file_set| file_set.dig('structural', 'contains') }
.find { |file| file['hasMimeType'] == THUMBNAIL_MIME_TYPE }
end

def embargo_release_date
data.dig('access', 'embargo', 'releaseDate')
end

def files(&)
return to_enum(:files) unless block_given?

files_from_json(&)
end

private

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'])
yield file
end
end
end
end
48 changes: 0 additions & 48 deletions app/models/purl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,5 @@

# PURL API service
class Purl
include ActiveSupport::Benchmarkable

class Exception < ::RuntimeError; end

def self.instance
@instance ||= new
end

class << self
delegate :public_json, :files, to: :instance
end

def public_json(druid)
Rails.cache.fetch("purl/#{druid}.json", expires_in: 10.minutes) do
benchmark "Fetching public json for #{druid}" do
response = Faraday.get(public_json_url(druid))
raise Purl::Exception, response.status unless response.success?

JSON.parse(response.body)
end
end
end

def files(druid, &)
return to_enum(:files, druid) unless block_given?

files_from_json(druid, &)
end

def files_from_json(druid)
doc = public_json(druid)

doc.dig('structural', 'contains').each do |fileset|
fileset.dig('structural', 'contains').each do |file|
file = StacksFile.new(id: druid, file_name: file['filename'])
yield file
end
end
end

private

def public_json_url(druid)
"#{Settings.purl.url}#{druid}.json"
end

def logger
Rails.logger
end
end
3 changes: 2 additions & 1 deletion app/models/stacks_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ def storage_root
end

def stacks_rights
@stacks_rights ||= StacksRights.new(id:, file_name:)
@stacks_rights ||= StacksRights.new(cocina: Cocina.find(id), file_name:)
end

delegate :rights, :restricted_by_location?, :stanford_restricted?, :embargoed?,
:embargo_release_date, :location, to: :stacks_rights

Expand Down
3 changes: 2 additions & 1 deletion app/models/stacks_image.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ def info_service
end

def stacks_rights
@stacks_rights ||= StacksRights.new(id:, file_name:)
@stacks_rights ||= StacksRights.new(cocina: Cocina.find(id), file_name:)
end

delegate :rights, :maybe_downloadable?, :object_thumbnail?,
:stanford_restricted?, :restricted_by_location?, to: :stacks_rights
end
2 changes: 1 addition & 1 deletion app/models/stacks_media_stream.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def file
delegate :etag, :mtime, to: :file

def stacks_rights
@stacks_rights ||= StacksRights.new(id:, file_name:)
@stacks_rights ||= StacksRights.new(cocina: Cocina.find(id), file_name:)
end

delegate :rights, :restricted_by_location?, :stanford_restricted?, :embargoed?,
Expand Down
43 changes: 7 additions & 36 deletions app/models/stacks_rights.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
##
# RightsMetadata interpretation
class StacksRights
attr_reader :id, :file_name
attr_reader :cocina_file, :cocina

THUMBNAIL_MIME_TYPE = 'image/jp2'

def initialize(id:, file_name:)
@id = id
@file_name = file_name
def initialize(file_name:, cocina:)
@cocina = cocina
@cocina_file = cocina.find_file(file_name)
end

delegate :embargo_release_date, :thumbnail_file, to: :cocina

def maybe_downloadable?
%w[world stanford].include?(rights.download)
end
Expand All @@ -26,23 +26,12 @@ def restricted_by_location?
rights.view == 'location-based' || rights.download == 'location-based'
end

def embargo_release_date
cocina_embargo_release_date
end

def embargoed?
cocina_embargo_release_date && Time.parse(cocina_embargo_release_date).getlocal > Time.now.getlocal
end

def cocina_embargo_release_date
@cocina_embargo_release_date ||= public_json.dig('access', 'embargo', 'releaseDate')
embargo_release_date && Time.parse(embargo_release_date).getlocal > Time.now.getlocal
end

# Based on implementation of ThumbnailService in DSA
def object_thumbnail?
thumbnail_file = public_json.dig('structural', 'contains')
.lazy.flat_map { |file_set| file_set.dig('structural', 'contains') }
.find { |file| file['hasMimeType'] == THUMBNAIL_MIME_TYPE }
thumbnail_file == cocina_file
end

Expand All @@ -51,22 +40,4 @@ def rights
end

delegate :location, to: :rights

private

def cocina_file
@cocina_file ||= find_file
end

def find_file
file_sets = public_json.dig('structural', 'contains')
raise(ActionController::MissingFile, "File not found '#{file_name}'") unless file_sets # Trap for Collections

file_sets.lazy.flat_map { |file_set| file_set.dig('structural', 'contains') }
.find { |file| file['filename'] == file_name } || raise(ActionController::MissingFile, "File not found '#{file_name}'")
end

def public_json
@public_json ||= Purl.public_json(id)
end
end
2 changes: 1 addition & 1 deletion spec/abilities/cocina_ability_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
subject(:ability) { described_class.new(user) }

before do
allow(Purl).to receive(:public_json).and_return(public_json)
allow(Cocina).to receive(:find).and_return(Cocina.new(public_json))
allow(image).to receive_messages(image_width: 11_957, image_height: 15_227)
end

Expand Down
2 changes: 1 addition & 1 deletion spec/controllers/file_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
RSpec.describe FileController do
before do
allow(StacksFile).to receive(:new).and_return(file)
allow(Purl).to receive(:public_json).and_return(public_json)
allow(Cocina).to receive(:find).and_return(Cocina.new(public_json))
end

let(:public_json) do
Expand Down
3 changes: 3 additions & 0 deletions spec/controllers/object_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
context "with downloadable files" do
let(:json) do
{
'externalIdentifier' => 'druid:fd063dh3727',
'structural' => {
'contains' => [
{
Expand Down Expand Up @@ -102,6 +103,7 @@
context "with a stanford access file" do
let(:json) do
{
'externalIdentifier' => "druid:bb142ws0723",
'structural' => {
'contains' => [
{
Expand Down Expand Up @@ -165,6 +167,7 @@
context "with a stanford access file" do
let(:json) do
{
'externalIdentifier' => "druid:bb142ws0723",
'structural' => {
'contains' => [
{
Expand Down
11 changes: 6 additions & 5 deletions spec/models/purl_spec.rb → spec/models/cocina_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@

require 'rails_helper'

RSpec.describe Purl do
RSpec.describe Cocina do
before do
Rails.cache.clear
end

describe '.files' do
describe '#files' do
let(:json) do
{
'externalIdentifier' => 'abc',
'structural' => {
'contains' => [
{
Expand Down Expand Up @@ -44,12 +45,12 @@
end

before do
allow(Faraday).to receive(:get).with('https://purl.stanford.edu/abc.json')
.and_return(double(success?: true, body: json))
stub_request(:get, "https://purl.stanford.edu/abc.json")
.to_return(status: 200, body: json)
end

it 'gets all the files for a resource' do
actual = described_class.files('abc').map { |file| "#{file.id}/#{file.file_name}" }
actual = described_class.find('abc').files.map { |file| "#{file.id}/#{file.file_name}" }

expect(actual).to contain_exactly('abc/26855.jp2', 'abc/123.jp2')
end
Expand Down
2 changes: 1 addition & 1 deletion spec/requests/file_auth_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
let(:sf) { StacksFile.new(id: druid, file_name:) }

before(:each) do
allow(Purl).to receive(:public_json).and_return(public_json)
allow(Cocina).to receive(:find).and_return(Cocina.new(public_json))
allow(File).to receive(:world_readable?).with(path).and_return(perms)
end

Expand Down
2 changes: 1 addition & 1 deletion spec/requests/file_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

RSpec.describe "File requests" do
before do
allow(Purl).to receive_messages(public_json:)
allow(Cocina).to receive(:find).and_return(Cocina.new(public_json))
end

let(:druid) { 'nr349ct7889' }
Expand Down
2 changes: 1 addition & 1 deletion spec/requests/iiif/auth/v2/probe_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

# rubocop:disable RSpec/AnyInstance
before do
allow(Purl).to receive(:public_json).and_return(public_json)
allow(Cocina).to receive(:find).and_return(Cocina.new(public_json))
allow(File).to receive(:world_readable?).and_return('420')
allow(StacksFile).to receive(:new).and_call_original
end
Expand Down
2 changes: 1 addition & 1 deletion spec/requests/iiif_auth_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
allow(http_client).to receive(:get).and_return(instance_double(HTTP::Response, status: 200, body: StringIO.new))
allow_any_instance_of(IiifController).to receive(:current_user).and_return(current_user)
allow_any_instance_of(IiifController).to receive(:current_image).and_return(current_image)
allow(Purl).to receive(:public_json).and_return(public_json)
allow(Cocina).to receive(:find).and_return(Cocina.new(public_json))
end

context 'with a public item' do
Expand Down
2 changes: 1 addition & 1 deletion spec/requests/iiif_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
end

before do
allow(Purl).to receive(:public_json).and_return(public_json)
allow(Cocina).to receive(:find).and_return(Cocina.new(public_json))

# stubbing Rails.cache.fetch is required because you can't dump a singleton (double)
# which is what happens when writing to the cache.
Expand Down
2 changes: 1 addition & 1 deletion spec/requests/media_auth_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
end

before do
allow(Purl).to receive(:public_json).and_return(public_json)
allow(Cocina).to receive(:find).and_return(Cocina.new(public_json))
allow_any_instance_of(MediaController).to receive(:current_user).and_return(user)
allow_any_instance_of(MediaController).to receive(:current_media).and_return(mock_media)
end
Expand Down
2 changes: 1 addition & 1 deletion spec/requests/media_headers_request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

RSpec.describe "CORS headers for Media requests" do
before do
allow(Purl).to receive(:public_json).and_return(public_json)
allow(Cocina).to receive(:find).and_return(Cocina.new(public_json))
end

let(:public_json) do
Expand Down
1 change: 1 addition & 0 deletions spec/requests/metrics_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
let(:file_name) { 'image.jp2' }
let(:json) do
{
'externalIdentifier' => "druid:#{druid}",
'structural' => {
'contains' => [
{
Expand Down
2 changes: 1 addition & 1 deletion spec/requests/remote_iiif_image_delivery_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
end

before do
allow(Purl).to receive(:public_json).and_return(public_json)
allow(Cocina).to receive(:find).and_return(Cocina.new(public_json))
stub_request(:get, info_request)
.to_return(status: 200, body: info_response)
stub_request(:get, image_response)
Expand Down
Loading

0 comments on commit cf9026c

Please sign in to comment.