Skip to content

Commit

Permalink
Implementing support for ARK validation by extending the Ark Class (#84)
Browse files Browse the repository at this point in the history
* Implementing support for ARK validation by extending the `Ark` Class; Ensuring that WebMock is enabled but does not conflict wit h test suites which request data from the EZID API endpoint

* Increasing the test coverage for the `User` Model
  • Loading branch information
jrgriffiniii committed Apr 26, 2022
1 parent bfb3269 commit 727c8ca
Show file tree
Hide file tree
Showing 11 changed files with 306 additions and 8 deletions.
40 changes: 40 additions & 0 deletions app/models/ark.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,46 @@ def self.mint
identifier.id
end

def self.find(ezid)
Ezid::Identifier.find(ezid)
rescue Net::HTTPServerException
nil
rescue StandardError => error
Rails.logger.error("Failed to find the EZID #{ezid}: #{error.message}")
nil
end

# Determines whether or not a given EZID string is a valid ARK
# @param [ezid] [String] the EZID being validated
# @return [Boolean]
def self.valid?(ezid)
resolved = find(ezid)
!resolved.nil?
end

def initialize(ezid)
@object = self.class.find(ezid)
raise(ArgumentError, "Invalid EZID provided for an ARK: #{ezid}") if @object.nil?
end

def object
@object ||= self.class.find(ezid)
end

delegate :id, :metadata, to: :object

def target
metadata[Ezid::Metadata::TARGET]
end

def target=(value)
metadata[Ezid::Metadata::TARGET] = value
end

def save!
object.modify(id, metadata)
end

# ======================
# If in the future we want to update the information of the ARK we can
# implement a few methods as follow:
Expand Down
28 changes: 28 additions & 0 deletions app/models/dataset.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,36 @@
# frozen_string_literal: true

class Dataset < ApplicationRecord
include Rails.application.routes.url_helpers

belongs_to :work

delegate :title, to: :work
delegate :created_by_user, to: :work
delegate :state, to: :work

validate do |ds|
if ds.ark.present?
ds.errors.add(:base, "Invalid ARK provided for the Dataset: #{ds.ark}") unless Ark.valid?(ds.ark)
end
end

before_update do |ds|
if ds.ark.blank?
ds.ark = Ark.mint
end
end

after_save do |ds|
if ds.ark.present?
# Ensure that the ARK metadata is updated for the new URL
if ark_object.target != ds.url
ark_object.target = ds.url
ark_object.save!
end
end
end

def self.my_datasets(user)
datasets = []
Work.where(created_by_user_id: user).find_each do |work|
Expand Down Expand Up @@ -71,4 +89,14 @@ def self.create_skeleton(title, user_id, collection_id)
def ark_url
"https://ezid.cdlib.org/id/#{ark}"
end

def ark_object
@ark_object ||= Ark.new(ark)
end

def url
return unless persisted?

@url ||= url_for(self)
end
end
8 changes: 3 additions & 5 deletions app/views/datasets/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@
<%= form.collection_select :collection_id, current_user.submitter_collections, :id, :title, {:include_blank => true}, {:class => 'form-control'} %>
</div>

<% if form.object.ark.present? %>
<div class="field">
ARK: <%= form.object.ark %>
</div>
<% end %>
<div class="field">
ARK: <%= form.object.ark %>
</div>
<br />

<div class="actions">
Expand Down
2 changes: 2 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,6 @@

# Uncomment if you wish to allow Action Cable access from any origin.
# config.action_cable.disable_request_forgery_protection = true

routes.default_url_options[:host] = "www.example.com"
end
2 changes: 2 additions & 0 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,6 @@

# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true

routes.default_url_options[:host] = "www.example.com"
end
29 changes: 28 additions & 1 deletion spec/controllers/datasets_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,36 @@
require "rails_helper"

RSpec.describe DatasetsController do
before { Collection.create_defaults }
before do
Collection.create_defaults
user

# this is a work-around due to an issue with webmock
allow(Ezid::Identifier).to receive(:find).and_return(identifier)

allow(identifier).to receive(:metadata).and_return(ezid_metadata)
allow(identifier).to receive(:id).and_return(ezid)
allow(identifier).to receive(:modify)
end
let(:identifier) { double(Ezid::Identifier) }
let(:ezid_metadata_values) do
{
"_updated" => "1611860047",
"_target" => "http://arks.princeton.edu/ark:/88435/dsp01zc77st047",
"_profile" => "erc",
"_export" => "yes",
"_owner" => "pudiglib",
"_ownergroup" => "pudiglib",
"_created" => "1611860047",
"_status" => "public"
}
end
let(:ezid_metadata) do
Ezid::Metadata.new(ezid_metadata_values)
end
let(:user) { FactoryBot.create(:user) }
let(:ds) { Dataset.create_skeleton("test dataset", user.id, Collection.first.id) }
let(:ezid) { ds.ark }

context "valid user login" do
it "handles the index page" do
Expand Down
26 changes: 26 additions & 0 deletions spec/controllers/users_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,30 @@
get :show, params: { id: user_other.friendly_id }
expect(response).to render_template("show")
end

describe "#edit" do
context "when authenticated and the current user is authorized" do
before do
sign_in user
end

it "renders the edit view" do
get :edit, params: { id: user.friendly_id }
expect(response).to render_template("edit")
end
end

context "when authenticated and the current user is not authorized" do
before do
sign_in user
end
it "logs a warning and redirects the client to the show view" do
allow(Rails.logger).to receive(:warn)

get :edit, params: { id: user_other.friendly_id }
expect(response).to redirect_to(user_path(user_other))
expect(Rails.logger).to have_received(:warn).with("Unauthorized to edit user #{user_other.id} (current user: #{user.id})")
end
end
end
end
47 changes: 47 additions & 0 deletions spec/models/ark_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# frozen_string_literal: true
require "rails_helper"

RSpec.describe Ark, type: :model do
describe ".valid?" do
let(:id) { "id" }

context "when the ARK references a non-existent EZID" do
before do
stub_request(:get, "https://ezid.cdlib.org/id/#{id}").to_return(status: 400, body: "error: bad request - invalid identifier")

# This is a work-around for WebMock
allow(Ezid::Identifier).to receive(:find).and_raise(Net::HTTPServerException, '400 "Bad Request"')
end

it "returns false" do
expect(described_class.valid?(id)).to be false
end
end

context "when the ARK references an existing EZID" do
let(:response_body) do
%(
success: ark:/99999/fk4cz3dh0
_created: 1300812337
_updated: 1300913550
_target: http://www.gutenberg.org/ebooks/7178
_profile: erc
erc.who: Proust, Marcel
erc.what: Remembrance of Things Past
erc.when: 1922
)
end
let(:identifier) { instance_double(Ezid::Identifier) }

before do
stub_request(:get, "https://ezid.cdlib.org/id/#{id}").to_return(status: 200, body: response_body)
# This is a work-around for WebMock
allow(Ezid::Identifier).to receive(:find).and_return(identifier)
end

it "returns true" do
expect(described_class.valid?(id)).to be true
end
end
end
end
74 changes: 74 additions & 0 deletions spec/models/dataset_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,33 @@
let(:user_other) { FactoryBot.create :user }
let(:superadmin_user) { User.from_cas(OmniAuth::AuthHash.new(provider: "cas", uid: "fake1", extra: { mail: "fake@princeton.edu" })) }
let(:doi) { "https://doi.org/10.34770/0q6b-cj27" }
# This is not `instance_double` given that the `#modify` must be stubbed as is private
let(:identifier) { double(Ezid::Identifier) }
let(:ezid_metadata_values) do
{
"_updated" => "1611860047",
"_target" => "https://dataspace.princeton.edu/handle/88435/dsp01qb98mj541",
"_profile" => "erc",
"_export" => "yes",
"_owner" => "pudiglib",
"_ownergroup" => "pudiglib",
"_created" => "1611860047",
"_status" => "public"
}
end
let(:ezid_metadata) do
Ezid::Metadata.new(ezid_metadata_values)
end
let(:ezid) { "ark:/88435/dsp01qb98mj541" }

before do
# This is a work-around due to an issue with WebMock
allow(Ezid::Identifier).to receive(:find).and_return(identifier)

allow(identifier).to receive(:metadata).and_return(ezid_metadata)
allow(identifier).to receive(:id).and_return(ezid)
allow(identifier).to receive(:modify)
end

it "creates a skeleton dataset and links it to a new work" do
ds = described_class.create_skeleton("test title", user.id, collection.id)
Expand All @@ -19,6 +46,9 @@
end

it "mints an ARK on save (and only when needed)" do
# This is a work-around due to an issue with WebMock
allow(Ezid::Identifier).to receive(:find).and_return(identifier)

ds = described_class.create_skeleton("test title", user.id, collection.id)
expect(ds.ark).to be_blank
ds.save
Expand All @@ -28,6 +58,47 @@
expect(ds.ark).to eq original_ark
end

context "when created with an existing ARK" do
subject(:data_set) { described_class.create_skeleton("test title", user.id, collection.id) }

context "and when the ARK is valid" do
before do
# stub_request(:get, "https://ezid.cdlib.org/id/#{ezid}").to_return(status: 200, body: response_body)
end

it "does not mint a new ARK" do
expect(data_set.persisted?).not_to be false
data_set.ark = ezid
data_set.save

expect(data_set.persisted?).to be true
expect(data_set.ark).to eq(ezid)
end

it "updates the ARK metadata" do
data_set = described_class.create_skeleton("test title", user.id, collection.id)

data_set.ark = ezid
data_set.save

expect(identifier).to have_received(:modify)
end
end

context "and when the ARK is invalid" do
before do
# This is a work-around due to an issue with WebMock
allow(Ezid::Identifier).to receive(:find).and_raise(Net::HTTPServerException, '400 "Bad Request"')
end

it "raises an error" do
expect(data_set.persisted?).not_to be false
data_set.ark = ezid
expect { data_set.save! }.to raise_error("Validation failed: Invalid ARK provided for the Dataset: #{ezid}")
end
end
end

it "returns datasets waiting for approval depending on the user" do
described_class.create_skeleton("test title", user.id, collection.id)
described_class.create_skeleton("test title", user_other.id, collection.id)
Expand All @@ -44,6 +115,9 @@
context "linked to a work" do
let(:dataset) { FactoryBot.create(:shakespeare_and_company_dataset) }
it "has a DOI" do
# This is a work-around due to an issue with WebMock
allow(Ezid::Identifier).to receive(:find).and_return(identifier)

expect(dataset.title).to eq "Shakespeare and Company Project Dataset: Lending Library Members, Books, Events"
expect(dataset.doi).to eq "https://doi.org/10.34770/pe9w-x904"
end
Expand Down
29 changes: 28 additions & 1 deletion spec/system/dataset_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,39 @@
hash = OmniAuth::AuthHash.new(provider: "cas", uid: "who", extra: { mail: "who@princeton.edu", departmentnumber: "31000" })
User.from_cas(hash)
end
let(:identifier) { double(Ezid::Identifier) }
let(:ezid_metadata_values) do
{
"_updated" => "1611860047",
"_target" => "http://arks.princeton.edu/ark:/88435/dsp01zc77st047",
"_profile" => "erc",
"_export" => "yes",
"_owner" => "pudiglib",
"_ownergroup" => "pudiglib",
"_created" => "1611860047",
"_status" => "public"
}
end
let(:ezid_metadata) do
Ezid::Metadata.new(ezid_metadata_values)
end
let(:ezid) { "ark:/88435/dsp01zc77st047" }

before do
# this is a work-around due to an issue with webmock
allow(Ezid::Identifier).to receive(:find).and_return(identifier)

allow(identifier).to receive(:metadata).and_return(ezid_metadata)
allow(identifier).to receive(:id).and_return(ezid)
allow(identifier).to receive(:modify)
end

it "Creates ARK when a new dataset is saved", js: true do
sign_in user
visit new_dataset_path
expect(page).to_not have_content "ARK"
expect(page).to have_content "ARK"
click_on "Update Dataset"
expect(page).to have_content "ARK"
expect(page).to have_content Dataset.last.ark
end
end
Loading

0 comments on commit 727c8ca

Please sign in to comment.