Skip to content

Commit

Permalink
Adds new route for checking if a version can be opened for an object.
Browse files Browse the repository at this point in the history
closes #282
  • Loading branch information
justinlittman committed May 22, 2019
1 parent 72ebc5b commit cbe4ed2
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 32 deletions.
4 changes: 4 additions & 0 deletions .rubocop.yml
Expand Up @@ -31,3 +31,7 @@ RSpec/MultipleExpectations:
RSpec/ExpectActual:
Exclude:
- 'spec/routing/**'

RSpec/AnyInstance:
Exclude:
- 'spec/**/*'
4 changes: 4 additions & 0 deletions app/controllers/versions_controller.rb
Expand Up @@ -21,6 +21,10 @@ def close_current
render build_error('Unable to close version', e)
end

def openeable
render plain: VersionService.can_open?(@item, open_params).to_s
end

private

# JSON-API error response
Expand Down
61 changes: 46 additions & 15 deletions app/services/version_service.rb
Expand Up @@ -6,6 +6,10 @@ def self.open(work, opts = {})
new(work).open(opts)
end

def self.can_open?(work, opts = {})
new(work).can_open?(opts)
end

def self.close(work, opts = {})
new(work).close(opts)
end
Expand All @@ -20,17 +24,7 @@ def initialize(work)
# @option opts [Hash] :vers_md_upd_info If present, used to add to the events datastream and set the desc and significance on the versionMetadata datastream
# @raise [Dor::Exception] if the object hasn't been accessioned, or if a version is already opened
def open(opts = {})
# Raised when the object has never been accessioned.
# The accessioned milestone is the last step of the accessionWF.
# During local development, we need a way to open a new version even if the object has not been accessioned.
raise(Dor::Exception, 'Object net yet accessioned') unless
opts[:assume_accessioned] || Dor::Config.workflow.client.lifecycle('dor', work.pid, 'accessioned')
# Raised when the current version has any incomplete wf steps and there is a versionWF.
# The open milestone is part of the versioningWF.
raise Dor::VersionAlreadyOpenError, 'Object already opened for versioning' if open?
# Raised when the current version has any incomplete wf steps and there is an accessionWF.
# The submitted milestone is part of the accessionWF.
raise Dor::Exception, 'Object currently being accessioned' if Dor::Config.workflow.client.active_lifecycle('dor', work.pid, 'submitted')
raise_for_open(opts[:assume_accessioned])

sdr_version = SdrClient.current_version work.pid

Expand All @@ -48,6 +42,17 @@ def open(opts = {})
work.save
end

# Determines whether a new version can be opened for an object.
# @param [Hash] opts optional params
# @option opts [Boolean] :assume_accessioned If true, does not check whether object has been accessioned.
# @return [Boolean] true if a new version can be opened.
def can_open?(opts = {})
raise_for_open(opts[:assume_accessioned])
true
rescue Dor::Exception
false
end

# Sets versioningWF:submit-version to completed and initiates accessionWF for the object
# @param [Hash] opts optional params
# @option opts [String] :description describes the version change
Expand All @@ -64,18 +69,44 @@ def close(opts = {})
end

raise Dor::Exception, 'latest version in versionMetadata requires tag and description before it can be closed' unless work.versionMetadata.current_version_closeable?
raise Dor::Exception, 'Trying to close version on an object not opened for versioning' unless open?
raise Dor::Exception, 'accessionWF already created for versioned object' if Dor::Config.workflow.client.active_lifecycle('dor', work.pid, 'submitted')
raise Dor::Exception, 'Trying to close version on an object not opened for versioning' unless open_for_versioning?
raise Dor::Exception, 'accessionWF already created for versioned object' if accessioning?

Dor::Config.workflow.client.close_version 'dor', work.pid, opts.fetch(:start_accession, true) # Default to creating accessionWF when calling close_version
end

# @return [Boolean] true if 'opened' lifecycle is active, false otherwise
def open?
# Performs checks on whether a new version can be opened for an object
# @param [Boolean] :assume_accessioned If true, does not check whether object has been accessioned.
# @raise [Dor::Exception] if the object hasn't been accessioned, or if a version is already opened
def raise_for_open(assume_accessioned = false)
# Raised when the object has never been accessioned.
# The accessioned milestone is the last step of the accessionWF.
# During local development, we need a way to open a new version even if the object has not been accessioned.
raise(Dor::Exception, 'Object net yet accessioned') unless
assume_accessioned || Dor::Config.workflow.client.lifecycle('dor', work.pid, 'accessioned')
# Raised when the current version has any incomplete wf steps and there is a versionWF.
# The open milestone is part of the versioningWF.
raise Dor::VersionAlreadyOpenError, 'Object already opened for versioning' if open_for_versioning?
# Raised when the current version has any incomplete wf steps and there is an accessionWF.
# The submitted milestone is part of the accessionWF.
raise Dor::Exception, 'Object currently being accessioned' if accessioning?
end

# Checks if current version has any incomplete wf steps and there is a versionWF
# @return [Boolean] true if object is open for versioning
def open_for_versioning?
return true if Dor::Config.workflow.client.active_lifecycle('dor', work.pid, 'opened')

false
end

# Checks if the current version has any incomplete wf steps and there is an accessionWF.
# @return [Boolean] true if object is currently being accessioned
def accessioning?
return true if Dor::Config.workflow.client.active_lifecycle('dor', work.pid, 'submitted')

false
end

attr_reader :work
end
1 change: 1 addition & 0 deletions config/routes.rb
Expand Up @@ -48,6 +48,7 @@

resources :versions, only: [:create] do
collection do
get 'openeable', to: 'openeable'
get 'current'
post 'current/close', action: 'close_current'
end
Expand Down
28 changes: 27 additions & 1 deletion spec/controllers/versions_controller_spec.rb
Expand Up @@ -76,7 +76,7 @@
allow(item).to receive(:current_version).and_return('2')
end

context 'when opening a version succeedes' do
context 'when opening a version succeeds' do
before do
# Do not test version service side effects in dor-services-app; that is dor-services' responsibility
allow(VersionService).to receive(:open)
Expand Down Expand Up @@ -109,4 +109,30 @@
end
end
end

describe '/versions/openeable' do
context 'when a new version can be opened' do
before do
allow(VersionService).to receive(:can_open?).and_return(true)
end

it 'returns true' do
get :openeable, params: { object_id: item.pid }
expect(response.body).to eq('true')
expect(response).to be_successful
end
end

context 'when a new version cannot be opened' do
before do
allow(VersionService).to receive(:can_open?).and_return(false)
end

it 'returns true' do
get :openeable, params: { object_id: item.pid }
expect(response.body).to eq('false')
expect(response).to be_successful
end
end
end
end
74 changes: 58 additions & 16 deletions spec/services/version_service_spec.rb
Expand Up @@ -70,37 +70,79 @@
end
end

context 'when the object has not been accessioned' do
context 'when a new version cannot be opened' do
before do
allow_any_instance_of(described_class).to receive(:raise_for_open).and_raise(Dor::Exception, 'Object net yet accessioned')
end

it 'raises an exception' do
expect(Dor::Config.workflow.client).to receive(:lifecycle).with('dor', druid, 'accessioned').and_return(false)
expect { open }.to raise_error(Dor::Exception, 'Object net yet accessioned')
end
end

context 'when the object has already been opened' do
context "SDR's current version is greater than the current version" do
it 'raises an exception' do
expect(Dor::Config.workflow.client).to receive(:lifecycle).with('dor', druid, 'accessioned').and_return(true)
expect(Dor::Config.workflow.client).to receive(:active_lifecycle).with('dor', druid, 'opened').and_return(Time.new)
expect { open }.to raise_error(Dor::VersionAlreadyOpenError, 'Object already opened for versioning')
expect(Dor::Config.workflow.client).to receive(:active_lifecycle).with('dor', druid, 'opened').and_return(nil)
expect(Dor::Config.workflow.client).to receive(:active_lifecycle).with('dor', druid, 'submitted').and_return(nil)
expect(SdrClient).to receive(:current_version).and_return(3)
expect { open }.to raise_error(Dor::Exception, 'Cannot sync to a version greater than current: 1, requested 3')
end
end
end

describe '.can_open?' do
subject(:can_open?) { described_class.can_open?(obj) }

before do
allow(Dor::Config.workflow).to receive(:client).and_return(workflow_client)
end

let(:workflow_client) do
instance_double(Dor::Workflow::Client,
lifecycle: true,
active_lifecycle: nil)
end

context 'when a new version can be opened' do
it 'returns true' do
expect(can_open?).to be true
expect(workflow_client).to have_received(:lifecycle).with('dor', druid, 'accessioned')
expect(workflow_client).to have_received(:active_lifecycle).with('dor', druid, 'opened')
expect(workflow_client).to have_received(:active_lifecycle).with('dor', druid, 'submitted')
end
end

context 'when the object is still being accessioned' do
context 'when the object has not been accessioned' do
before do
allow(Dor::Config.workflow.client).to receive(:lifecycle).with('dor', druid, 'accessioned').and_return(false)
end

it 'returns false' do
expect(can_open?).to be false
expect(Dor::Config.workflow.client).to have_received(:lifecycle).with('dor', druid, 'accessioned')
end
end

context 'when the object has already been opened' do
before do
allow(Dor::Config.workflow.client).to receive(:active_lifecycle).with('dor', druid, 'opened').and_return(Time.new)
end

it 'raises an exception' do
expect(Dor::Config.workflow.client).to receive(:lifecycle).with('dor', druid, 'accessioned').and_return(true)
expect(Dor::Config.workflow.client).to receive(:active_lifecycle).with('dor', druid, 'opened').and_return(nil)
expect(Dor::Config.workflow.client).to receive(:active_lifecycle).with('dor', druid, 'submitted').and_return(Time.new)
expect { open }.to raise_error(Dor::Exception, 'Object currently being accessioned')
expect(can_open?).to be false
expect(Dor::Config.workflow.client).to have_received(:active_lifecycle).with('dor', druid, 'opened')
end
end

context "SDR's current version is greater than the current version" do
context 'when the object is still being accessioned' do
before do
allow(Dor::Config.workflow.client).to receive(:active_lifecycle).with('dor', druid, 'submitted').and_return(Time.new)
end

it 'raises an exception' do
expect(Dor::Config.workflow.client).to receive(:lifecycle).with('dor', druid, 'accessioned').and_return(true)
expect(Dor::Config.workflow.client).to receive(:active_lifecycle).with('dor', druid, 'opened').and_return(nil)
expect(Dor::Config.workflow.client).to receive(:active_lifecycle).with('dor', druid, 'submitted').and_return(nil)
expect(SdrClient).to receive(:current_version).and_return(3)
expect { open }.to raise_error(Dor::Exception, 'Cannot sync to a version greater than current: 1, requested 3')
expect(can_open?).to be false
expect(Dor::Config.workflow.client).to have_received(:active_lifecycle).with('dor', druid, 'submitted')
end
end
end
Expand Down

0 comments on commit cbe4ed2

Please sign in to comment.