Skip to content

Commit

Permalink
expand the EmbargoManager interface to handle releasing embargos
Browse files Browse the repository at this point in the history
for Valkyrie models, we provide `Hyrax::EmbargoManager` to make the embargo
lifecycle easy to work with. to date, this has only supported applying
embargoes. this adds the ability to release an embargo after its period has expired.

a concept of 'enforcement' is added, represented by `#enforced?`. this addresses
the issue of embargoes which are not current (their period is past) but have not
yet been released.

the terminology used by the `EmbargoManager` is documented.

see also: samvera/hydra-head#511.
  • Loading branch information
tom johnson committed May 7, 2020
1 parent f63b1f1 commit dd75bf7
Show file tree
Hide file tree
Showing 2 changed files with 177 additions and 5 deletions.
110 changes: 107 additions & 3 deletions app/services/hyrax/embargo_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,24 @@ module Hyrax
# Provides utilities for managing the lifecycle of an `Hyrax::Embargo` on a
# `Hyrax::Resource`.
#
# This can do things like
# The embargo terminology used here is as follows:
#
# - "Release Date" is the day an embargo is scheduled to be released.
# - "Under Embargo" means the embargo is "active"; i.e. that its release
# date is today or later.
# - "Applied" means the embargo's pre-release visibility has been set on
# the resource.
# - "Released" means the embargo's post-release visibility has been set on
# the resource.
# - "Enforced" means the object's visibility matches the pre-release
# visibility of the embargo; i.e. the embargo has been applied,
# but not released.
#
# Note that an resource may be `#under_embargo?` even if the embargo is not
# be `#enforced?` (in this case, the application should seek to apply the
# embargo, e.g. via a scheduled job). Additionally, an embargo may be
# `#enforced?` after its release date (in this case, the application should
# seek to release the embargo).
#
# @example check whether a resource is under an active embargo
# manager = EmbargoManager.new(resource: my_resource)
Expand All @@ -21,9 +38,42 @@ module Hyrax
#
# manager = EmbargoManager.new(resource: resource)
#
# manager.apply
# manager.apply!
# manager.enforced? => true
# resource.visibility # => 'restricted'
#
# @example releasing an embargo
# embargo = Hyrax::Embargo.new(visibility_during_embargo: 'restricted',
# visibility_after_embargo: 'open',
# embargo_release_date: Time.zone.today + 1000)
#
# @example releasing an embargo
# embargo = Hyrax::Embargo.new(visibility_during_embargo: 'restricted',
# visibility_after_embargo: 'open',
# embargo_release_date: Time.zone.today + 1)
#
# resource = Hyrax::Resource.new(embargo: embargo)
# manager = EmbargoManager.new(resource: resource)
#
# manager.under_embargo? => true
# manager.enforced? => false
#
# manager.apply!
#
# resource.visibility # => 'restricted'
# manager.enforced? => true
#
# manager.release! # => NotReleasableError
#
# # <spongebob narrator>ONE DAY LATER</spongebob narrator>
# manager.under_embargo? => false
# manager.enforced? => true
#
# manager.release!
#
# resource.visibility # => 'open'
# manager.enforced? => false
#
class EmbargoManager
##
# @!attribute [rw] resource
Expand All @@ -48,10 +98,25 @@ def apply_embargo_for(resource:, query_service: Hyrax.query_service)
.apply
end

def apply_embargo_for!(resource:, query_service: Hyrax.query_service)
new(resource: resource, query_service: query_service)
.apply!
end

def embargo_for(resource:, query_service: Hyrax.query_service)
new(resource: resource, query_service: query_service)
.embargo
end

def release_embargo_for(resource:, query_service: Hyrax.query_service)
new(resource: resource, query_service: query_service)
.release
end

def release_embargo_for!(resource:, query_service: Hyrax.query_service)
new(resource: resource, query_service: query_service)
.release!
end
end

##
Expand All @@ -68,25 +133,64 @@ def copy_embargo_to(target:)
end

##
# Sets the visibility of the resource to the embargo's visibility condition
#
# @return [Boolean]
def apply
return false unless under_embargo?

resource.visibility = embargo.visibility_during_embargo
end

##
# @return [void]
# @raise [NotEnforcableError] when trying to apply an embargo that isn't active
def apply!
apply || raise(NotEnforcableError)
end

##
# @return [Boolean]
def enforced?
embargo.visibility_during_embargo.to_s == resource.visibility
end

##
# @return [Valkyrie::Resource]
def embargo
resource.embargo || Embargo.new
end

##
# @return [Boolean]
# Sets the visibility of the resource to the embargo's visibility condition.
# no-op if the embargo period is current.
#
# @return [Boolean] truthy if the embargo has been applied
def release
return false if under_embargo?
return true if embargo.visibility_after_embargo.nil?

resource.visibility = embargo.visibility_after_embargo
end

##
# @return [void]
# @raise [NotEnforcableError] when trying to release an embargo that
# is currently active
def release!
release || raise(NotReleasableError)
end

##
# @return [Boolean] indicates whether the date range for the embargo's
# applicability includes the present date.
def under_embargo?
embargo.active?
end

class NotEnforcableError < RuntimeError; end
class NotReleasableError < RuntimeError; end

private

def clone_attributes
Expand Down
72 changes: 70 additions & 2 deletions spec/services/hyrax/embargo_manager_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
end
end

context 'with expried embargo' do
context 'with expired embargo' do
include_context 'with expired embargo'

it 'is a no-op' do
Expand Down Expand Up @@ -52,7 +52,7 @@
.from nil
end

context 'with expried embargo' do
context 'with expired embargo' do
include_context 'with expired embargo'

it 'does not copy the embargo' do
Expand Down Expand Up @@ -83,6 +83,36 @@
end
end

describe '#enforced' do
it { is_expected.not_to be_enforced }

context 'when under embargo' do
include_context 'when under embargo'

it { is_expected.not_to be_enforced }

context 'and it is applied' do
before { manager.apply! }

it { is_expected.to be_enforced }
end
end

context 'with expired embargo' do
include_context 'with expired embargo'

it { is_expected.not_to be_enforced }
end

context 'with an embargo that is in force, but expired' do
include_context 'with expired embargo'

before { resource.visibility = embargo.visibility_during_embargo }

it { is_expected.to be_enforced }
end
end

describe '#embargo' do
it 'gives an inactive embargo' do
expect(manager.embargo).not_to be_active
Expand All @@ -105,6 +135,44 @@
end
end

describe '#release' do
context 'with no embargo' do
it 'is a no-op' do
expect { manager.release }
.not_to change { resource.visibility }
end
end

context 'with expired embargo' do
include_context 'with expired embargo'

it 'ensures the post-embargo visibility is set' do
manager.release
expect(resource.visibility).to eq embargo.visibility_after_embargo
end

context 'and embargo was applied' do
before { resource.visibility = embargo.visibility_during_embargo }

it 'ensures the post-embargo visibility is set' do
expect { manager.release }
.to change { resource.visibility }
.from(embargo.visibility_during_embargo)
.to embargo.visibility_after_embargo
end
end
end

context 'when under embargo' do
include_context 'when under embargo'

it 'is a no-op' do
expect { manager.release }
.not_to change { resource.visibility }
end
end
end

describe '#under_embargo?' do
it { is_expected.not_to be_under_embargo }

Expand Down

0 comments on commit dd75bf7

Please sign in to comment.