Skip to content

Commit

Permalink
Merge pull request #5051 from sul-dlss/republish_constituents
Browse files Browse the repository at this point in the history
Republish constituents after virtual object.
  • Loading branch information
jcoyne authored May 30, 2024
2 parents 5ead346 + 4298915 commit 67c4a73
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 10 deletions.
6 changes: 1 addition & 5 deletions app/services/member_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,7 @@ def only_published_members(members)
return members unless only_published

members.select do |member|
workflow_client.lifecycle(druid: member.external_identifier, milestone_name: 'published', version: member.version).present?
WorkflowStateService.published?(druid: member.external_identifier, version: member.version)
end
end

def workflow_client
@workflow_client ||= WorkflowClientFactory.build
end
end
11 changes: 9 additions & 2 deletions app/services/publish/metadata_transfer_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ def initialize(cocina_object, workflow:)

# Appends contentMetadata file resources from the source objects to this object
def publish
republish_members!
republish_collection_members!
return unpublish unless discoverable?

if Settings.enabled_features.publish_shelve
publish_shelve
republish_virtual_object_constituents!
else
transfer_metadata
publish_notify_on_success
Expand Down Expand Up @@ -58,7 +59,7 @@ def discoverable?
cocina_object.access.view != 'dark'
end

def republish_members!
def republish_collection_members!
return unless cocina_object&.collection?

Array.wrap(
Expand All @@ -68,6 +69,12 @@ def republish_members!
end
end

def republish_virtual_object_constituents!
VirtualObjectService.constituents(cocina_object, exclude_opened: true, only_published: true).each do |druid|
PublishJob.set(queue: :publish_low).perform_later(druid:, background_job_result: BackgroundJobResult.create, workflow:, log_success: false)
end
end

# Create a file inside the content directory under the stacks.local_document_cache_root
# @param [String] content The contents of the file to be created
# @param [String] filename The name of the file to be created
Expand Down
49 changes: 49 additions & 0 deletions app/services/virtual_object_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

# Finds the constituents of a virtual object.
class VirtualObjectService
def self.constituents(...)
new(...).constituents
end

# @param [Cocina::Models::DRO] cocina_dro the cocina model for the virtual object
# @param [Boolean] only_published when true, restrict to only published items
# @param [Boolean] exclude_opened when true, exclude opened items
# @return [Array<String>] the druids for the constituents of this virtual object
def initialize(cocina_dro, only_published: false, exclude_opened: false)
@cocina_dro = cocina_dro
@only_published = only_published
@exclude_opened = exclude_opened
end

def constituents
return [] unless cocina_dro.dro?

RepositoryObject.joins(:head_version).where(external_identifier: constituent_druids).select(:external_identifier, :version, :head_version_id, :opened_version_id)
.then { |constituents| exclude_opened_constituents(constituents) }
.then { |constituents| only_published_constituents(constituents) }
.map(&:external_identifier)
end

private

attr_reader :cocina_dro, :only_published, :exclude_opened

def constituent_druids
cocina_dro.structural.hasMemberOrders.flat_map(&:members).uniq
end

def exclude_opened_constituents(constituents)
return constituents unless exclude_opened

constituents.reject(&:open?)
end

def only_published_constituents(constituents)
return constituents unless only_published

constituents.select do |constituent|
WorkflowStateService.published?(druid: constituent.external_identifier, version: constituent.version)
end
end
end
9 changes: 9 additions & 0 deletions app/services/workflow_state_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ def self.accessioned?(...)
new(...).accessioned?
end

def self.published?(...)
new(...).published?
end

def initialize(druid:, version:)
@druid = druid
@version = version
Expand Down Expand Up @@ -53,6 +57,11 @@ def accessioned?
false
end

# @return [Boolean] true if the object has previously been published for the version.
def published?
workflow_client.lifecycle(druid:, milestone_name: 'published', version:).present?
end

private

attr_reader :druid, :version
Expand Down
25 changes: 22 additions & 3 deletions spec/services/publish/metadata_transfer_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
let(:cocina_collection) { build(:collection, id: 'druid:xh235dd9059') }
let(:thumbnail_service) { ThumbnailService.new(cocina_object) }
let(:service) { described_class.new(cocina_object, workflow:) }
let(:fake_publish_job) { class_double(PublishJob, perform_later: nil) }

describe '#publish' do
before do
Expand All @@ -33,7 +34,6 @@
access: { view: 'world' }
)
end
let(:fake_publish_job) { class_double(PublishJob, perform_later: nil) }
let(:member_druid) { 'druid:hx532dd9509' }
let(:member_item) do
build(:dro, id: member_druid).new(
Expand Down Expand Up @@ -141,7 +141,7 @@
allow(service).to receive(:transfer_to_document_store)
allow(service).to receive(:publish_notify_on_success)
allow(service).to receive(:release_tags_on_success)
allow(service).to receive(:republish_members!)
allow(service).to receive(:republish_collection_members!)
end

it 'ignores missing data' do
Expand All @@ -150,7 +150,7 @@
expect(service).to have_received(:transfer_to_document_store).with(/<publicObject/, 'public')
expect(service).to have_received(:publish_notify_on_success)
expect(service).to have_received(:release_tags_on_success)
expect(service).to have_received(:republish_members!).with(no_args)
expect(service).to have_received(:republish_collection_members!).with(no_args)
end
end

Expand All @@ -159,6 +159,7 @@
allow(Settings.enabled_features).to receive(:publish_shelve).and_return(true)
allow(service).to receive(:publish_shelve)
allow(service).to receive(:release_tags_on_success)
allow(service).to receive(:republish_virtual_object_constituents!)
end

let(:access) { { view: 'citation-only', download: 'none' } }
Expand All @@ -167,6 +168,7 @@
service.publish
expect(service).to have_received(:publish_shelve)
expect(service).to have_received(:release_tags_on_success)
expect(service).to have_received(:republish_virtual_object_constituents!)
end
end
end
Expand Down Expand Up @@ -289,6 +291,23 @@
end
end

describe '#republish_virtual_object_constituents' do
subject(:republish) { service.send(:republish_virtual_object_constituents!) }

let(:constituent_druid) { 'druid:bc123df4567' }

before do
allow(VirtualObjectService).to receive(:constituents).and_return([constituent_druid])
allow(PublishJob).to receive(:set).with(queue: :publish_low).and_return(fake_publish_job)
end

it 'republishes the virtual object constituents' do
republish
expect(VirtualObjectService).to have_received(:constituents).with(cocina_object, exclude_opened: true, only_published: true)
expect(fake_publish_job).to have_received(:perform_later).once.with(druid: constituent_druid, background_job_result: BackgroundJobResult.last, workflow:, log_success: false)
end
end

describe '.publish' do
let(:service) { instance_double(described_class, publish: nil) }

Expand Down
69 changes: 69 additions & 0 deletions spec/services/virtual_object_service_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe VirtualObjectService do
describe '#constituents' do
let(:constituents) { described_class.constituents(cocina_object, only_published:, exclude_opened:) }
let(:only_published) { false }
let(:exclude_opened) { false }

context 'when the virtual object is a DRO' do
let(:cocina_object) do
build(:dro).new(structural: {
contains: [],
hasMemberOrders: [
{
members: [
'druid:dj321gm8879', # published and closed
'druid:vs491yc7072', # published and open
'druid:bc778pm9866' # unpublished and closed
],
viewingDirection: 'left-to-right'
}
],
isMemberOf: []
})
end

before do
create(:repository_object, :closed, external_identifier: 'druid:dj321gm8879')
create(:repository_object, external_identifier: 'druid:vs491yc7072')
create(:repository_object, :closed, external_identifier: 'druid:bc778pm9866')
allow(WorkflowStateService).to receive(:published?).with(druid: 'druid:dj321gm8879', version: 1).and_return(true)
allow(WorkflowStateService).to receive(:published?).with(druid: 'druid:vs491yc7072', version: 1).and_return(true)
allow(WorkflowStateService).to receive(:published?).with(druid: 'druid:bc778pm9866', version: 1).and_return(false)
end

context 'when not limited' do
it 'returns the druids of the constituent objects' do
expect(constituents).to eq(['druid:dj321gm8879', 'druid:vs491yc7072', 'druid:bc778pm9866'])
end
end

context 'when only published' do
let(:only_published) { true }

it 'returns the druids of the constituent objects' do
expect(constituents).to eq(['druid:dj321gm8879', 'druid:vs491yc7072'])
end
end

context 'when only closed' do
let(:exclude_opened) { true }

it 'returns the druids of the constituent objects' do
expect(constituents).to eq(['druid:dj321gm8879', 'druid:bc778pm9866'])
end
end
end

context 'when the virtual object is a collection' do
let(:cocina_object) { build(:collection) }

it 'returns an empty array' do
expect(constituents).to eq([])
end
end
end
end

0 comments on commit 67c4a73

Please sign in to comment.