Skip to content

Commit

Permalink
Merge pull request #95 from pulibrary/complete
Browse files Browse the repository at this point in the history
ARK minting
  • Loading branch information
Trey Pendragon committed Aug 9, 2017
2 parents bad484c + b3e5359 commit c97fc1d
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 21 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ gem 'aasm'
gem 'browse-everything'
gem 'coffee-rails'
gem 'devise'
gem 'ezid-client'
gem 'hydra-head'
gem 'hydra-role-management'
gem 'iiif_manifest', github: "samvera-labs/iiif_manifest"
Expand Down
5 changes: 4 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ GEM
sxp (~> 1.0)
erubi (1.6.1)
execjs (2.7.0)
ezid-client (1.7.1)
hashie (~> 3.4, >= 3.4.3)
factory_girl (4.8.0)
activesupport (>= 3.0.0)
factory_girl_rails (4.8.0)
Expand Down Expand Up @@ -708,6 +710,7 @@ DEPENDENCIES
devise
devise-guests!
dotenv-rails
ezid-client
factory_girl_rails
flutie
formulaic
Expand Down Expand Up @@ -755,4 +758,4 @@ DEPENDENCIES
webmock

BUNDLED WITH
1.15.2
1.15.3
6 changes: 6 additions & 0 deletions app/change_set_persisters/plum_change_set_persister.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,18 @@ def append(append_id:, updated_resource:)
end

def apply_remote_metadata(change_set:)
IdentifierService.mint_or_update(persister: persister, resource: change_set.model) if mint_ark?(change_set)
return unless change_set.respond_to?(:source_metadata_identifier)
return unless change_set.apply_remote_metadata?
attributes = RemoteRecord.retrieve(change_set.source_metadata_identifier).attributes
change_set.model.imported_metadata = ImportedMetadata.new(attributes)
end

def mint_ark?(change_set)
return false unless change_set.try(:new_state) == 'complete'
change_set.try(:state_changed?) || change_set.apply_remote_metadata?
end

def clean_up_collection_associations(change_set:)
resources = query_service.find_inverse_references_by(resource: change_set.resource, property: :member_of_collection_ids)
resources.each do |resource|
Expand Down
14 changes: 13 additions & 1 deletion app/change_sets/scanned_resource_change_set.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

# frozen_string_literal: true
class ScannedResourceChangeSet < Valkyrie::ChangeSet
delegate :human_readable_type, to: :model
Expand Down Expand Up @@ -87,4 +86,17 @@ def workflow
def workflow_class
BookWorkflow
end

def state_changed?
# conditional assignment makes this true if it has ever been true, to allow seeing the change after sync
@state_changed ||= changed?(:state) && !old_state.nil? && old_state != new_state
end

def new_state
Array.wrap(state).first
end

def old_state
Array.wrap(model.state).first
end
end
43 changes: 43 additions & 0 deletions app/services/identifier_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true
class IdentifierService
def self.mint_or_update(persister:, resource:)
if resource.identifier.present?
update_metadata resource
else
mint_identifier resource
persister.save resource: resource
end
end

private_class_method def self.update_metadata(resource)
return if minter_user == "apitest"
minter.modify(Array.wrap(resource.identifier).first, metadata(resource))
end

private_class_method def self.mint_identifier(resource)
resource.identifier = minter.mint(metadata(resource)).id
end

private_class_method def self.metadata(resource)
{
dc_publisher: 'Princeton University Library',
dc_title: resource.title.join('; '),
dc_type: 'Text',
target: url_for(resource)
}
end

private_class_method def self.url_for(resource)
return Rails.application.routes.url_helpers.scanned_resource_url(resource) if resource.try(:source_metadata_identifier).blank?
return "https://pulsearch.princeton.edu/catalog/#{resource.source_metadata_identifier.first}#view" if PulMetadataServices::Client.bibdata?(resource.source_metadata_identifier.first)
"http://findingaids.princeton.edu/collections/#{resource.source_metadata_identifier.first.tr('_', '/')}"
end

private_class_method def self.minter_user
Ezid::Client.config.user
end

private_class_method def self.minter
Ezid::Identifier
end
end
20 changes: 6 additions & 14 deletions app/validators/state_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,17 @@ def validate(record)
private

def validate_state(record)
return if workflow(record).valid_states.include?(new_state(record))
record.errors.add :state, "#{new_state(record)} is not a valid state"
return if workflow(record).valid_states.include?(record.new_state)
record.errors.add :state, "#{record.new_state} is not a valid state"
end

def validate_transition(record)
return unless record.changed?(:state) && !old_state(record).nil? && old_state(record) != new_state(record)
return if workflow(record).valid_transitions.include?(new_state(record))
record.errors.add :state, "Cannot transition from #{old_state(record)} to #{new_state(record)}"
return unless record.state_changed?
return if workflow(record).valid_transitions.include?(record.new_state)
record.errors.add :state, "Cannot transition from #{record.old_state} to #{record.new_state}"
end

def workflow(record)
record.workflow_class.new(old_state(record))
end

def new_state(record)
Array.wrap(record.state).first
end

def old_state(record)
Array.wrap(record.model.state).first
record.workflow_class.new(record.old_state)
end
end
6 changes: 6 additions & 0 deletions config/initializers/ezid.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true
Ezid::Client.configure do |conf|
conf.default_shoulder = 'ark:/99999/fk4' unless ENV['EZID_DEFAULT_SHOULDER']
conf.user = 'apitest' unless ENV['EZID_USER']
conf.password = 'apitest' unless ENV['EZID_PASSWORD']
end
19 changes: 19 additions & 0 deletions spec/change_set_persisters/plum_change_set_persister_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,25 @@
expect(output.primary_imported_metadata.call_number).to eq ["BL980.G7 B66 1982"]
end
end
context "when a resource is completed" do
let(:shoulder) { '99999/fk4' }
let(:blade) { '123456' }

before do
stub_bibdata(bib_id: '123456')
stub_ezid(shoulder: shoulder, blade: blade)
end

it "mints an ARK" do
resource = FactoryGirl.create(:scanned_resource, title: [], source_metadata_identifier: '123456', state: 'final_review')
change_set = change_set_class.new(resource)
change_set.prepopulate!
change_set.validate(state: 'complete')
change_set.sync
output = change_set_persister.save(change_set: change_set)
expect(output.identifier.first).to eq "ark:/#{shoulder}#{blade}"
end
end
context "when a source_metadata_identifier is set and it's from PULFA" do
before do
stub_pulfa(pulfa_id: "MC016_c9616")
Expand Down
99 changes: 99 additions & 0 deletions spec/services/identifier_service_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# frozen_string_literal: true
require 'rails_helper'

RSpec.describe IdentifierService do
let(:ark) { 'ark:88435/x1234567' }
let(:minter) { class_double('Ezid::Identifier') }
let(:identifier) { instance_double('Ezid::Identifier') }
let(:base_metadata) { { dc_publisher: 'Princeton University Library', dc_title: 'Title', dc_type: 'Text' } }
let(:persister) { Valkyrie.config.metadata_adapter.persister }

before do
Rails.application.routes.default_url_options[:host] = 'example.com'
end

context "when there is an existing identifier" do
let(:metadata) { base_metadata.merge(target: "https://pulsearch.princeton.edu/catalog/123456#{obj.id}#view") }
let(:obj) { FactoryGirl.build :scanned_resource, source_metadata_identifier: '123456', identifier: ark }

before do
stub_bibdata(bib_id: '123456')
allow(described_class).to receive(:minter).and_return(minter)
allow(described_class).to receive(:minter_user).and_return('pudiglib')
allow(minter).to receive(:modify)
end

it "updates the ark" do
described_class.mint_or_update(resource: obj, persister: persister)
expect(minter).to have_received(:modify).with(ark, metadata)
expect(obj.identifier.first).to eq(ark)
end
end

context "when the identifier is blank" do
before do
allow(described_class).to receive(:minter).and_return(minter)
allow(minter).to receive(:mint).and_return(identifier)
allow(identifier).to receive(:id).and_return(ark)
end

context "with a bibdata source_metadata_identifier" do
let(:bib) { '123456' }
let(:metadata) { base_metadata.merge(target: "https://pulsearch.princeton.edu/catalog/#{bib}#view") }
let(:obj) { FactoryGirl.build :scanned_resource, source_metadata_identifier: bib }

before do
stub_bibdata(bib_id: '123456')
end

it "links to OrangeLight" do
described_class.mint_or_update(resource: obj, persister: persister)
expect(minter).to have_received(:mint).with(metadata)
end
end

context "with a pulfa source_metadata_identifier" do
let(:cid) { 'MC016/c9616' }
let(:metadata) { base_metadata.merge(target: "http://findingaids.princeton.edu/collections/#{cid}") }
let(:obj) { FactoryGirl.build :scanned_resource, source_metadata_identifier: cid }

before do
stub_pulfa(pulfa_id: 'MC016/c9616')
end

it "links to OrangeLight" do
described_class.mint_or_update(resource: obj, persister: persister)
expect(minter).to have_received(:mint).with(metadata)
end
end

context "without a source_metadata_identifier" do
let(:metadata) { base_metadata.merge(target: "http://example.com/concern/scanned_resources/#{obj.id}") }
let(:obj) { FactoryGirl.create :scanned_resource, id: '1234567', source_metadata_identifier: nil }
it "links to OrangeLight" do
described_class.mint_or_update(resource: obj, persister: persister)
expect(minter).to have_received(:mint).with(metadata)
end
end
end

context "integration test" do
let(:metadata) { base_metadata.merge(target: "http://example.com/concern/scanned_resources/#{obj.id}") }
let(:obj) { FactoryGirl.create :scanned_resource, id: '1234567', source_metadata_identifier: nil }
let(:shoulder) { '99999/fk4' }
let(:blade) { '123456' }

before do
stub_ezid(shoulder: shoulder, blade: blade)
end

it "mints an ARK" do
described_class.mint_or_update(resource: obj, persister: persister)
expect(obj.identifier.first).to eq("ark:/#{shoulder}#{blade}")
end

it "uses a test account" do
expect(described_class.send(:minter_user)).to eq('apitest')
end
end
end
12 changes: 12 additions & 0 deletions spec/support/stub_ezid.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true
module EzidStubbing
def stub_ezid(shoulder:, blade:)
stub_request(:post, "https://ezid.cdlib.org/shoulder/ark:/#{shoulder}")
.to_return(status: 200, body: "id: ark:/#{shoulder}#{blade}", headers: {})
end
end

RSpec.configure do |config|
config.include EzidStubbing
end
Ezid::Client.configure do |conf| conf.logger = Logger.new(File::NULL); end
8 changes: 3 additions & 5 deletions spec/validators/state_validator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,11 @@

def build_record(old_state:, new_state:)
record = instance_double ScannedResourceChangeSet
model = instance_double ScannedResource
allow(record).to receive(:workflow_class).and_return(BookWorkflow)
allow(record).to receive(:changed?).and_return(true)
allow(record).to receive(:errors).and_return(errors)
allow(record).to receive(:state).and_return(new_state)
allow(record).to receive(:model).and_return(model)
allow(model).to receive(:state).and_return(old_state)
allow(record).to receive(:old_state).and_return(old_state)
allow(record).to receive(:new_state).and_return(new_state)
allow(record).to receive(:state_changed?).and_return(old_state != new_state)
record
end
end

0 comments on commit c97fc1d

Please sign in to comment.