Skip to content

Commit

Permalink
Remove current children from virtual object before initializing conte…
Browse files Browse the repository at this point in the history
…nt metadata

This prevents a situation where lots of manual clean-up is required to remove old children from a parent.

Fixes #409
  • Loading branch information
mjgiarlo committed Sep 17, 2019
1 parent f06d64f commit 060c813
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 5 deletions.
28 changes: 23 additions & 5 deletions app/services/reset_content_metadata_service.rb
@@ -1,15 +1,33 @@
# frozen_string_literal: true

# Clears the contentMetadata datastream to the default, wiping out any members.
# item MUST allow modification, or save! will raise error
# Clears the contentMetadata datastream to the default, wiping out any members,
# and severing relationships bidirectionally.
#
# NOTE: item MUST allow modification, or `save!` will raise error
class ResetContentMetadataService
def initialize(item:, type: 'image')
DEFAULT_ITEM_TYPE = 'image'

attr_reader :item, :type

def initialize(item:, type: DEFAULT_ITEM_TYPE)
@item = item
@type = type
end

def reset
@item.contentMetadata.content = "<contentMetadata objectId='#{@item.id}' type='#{@type}'/>"
@item.save!
disown_current_children!

item.contentMetadata.content = "<contentMetadata objectId='#{item.id}' type='#{type}'/>"
item.save!
end

private

def disown_current_children!
item.contentMetadata.ng_xml.xpath('//resource/relationship/@objectId').map(&:content).each do |child_id|
child = Dor::Item.find(child_id)
child.clear_relationship(:is_constituent_of)
child.save! if child.relationships_are_dirty?
end
end
end
109 changes: 109 additions & 0 deletions spec/services/reset_content_metadata_service_spec.rb
@@ -0,0 +1,109 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe ResetContentMetadataService do
subject(:service) { described_class.new(args) }

let(:args) { { item: parent } }
let(:child1) do
instance_double(Dor::Item,
id: 'druid:child1',
relationships_are_dirty?: true,
save!: true,
clear_relationship: nil)
end
let(:child2) do
instance_double(Dor::Item,
id: 'druid:child2',
relationships_are_dirty?: true,
save!: true,
clear_relationship: nil)
end
let(:content_metadata) do
instance_double(Dor::ContentMetadataDS,
ng_xml: xml,
'content=': nil)
end
let(:default_content_xml) { "<contentMetadata objectId='druid:parent' type='image'/>" }
let(:parent) do
instance_double(Dor::Item,
id: 'druid:parent',
contentMetadata: content_metadata,
save!: true)
end
let(:predicate) { :is_constituent_of }

describe '.new' do
let(:xml) { '<notUsed/>' }

context 'when only the `item:` argument is included' do
it 'uses the supplied `item` attribute' do
expect(service.item).to eq(parent)
end

it 'uses the default `type` attribute' do
expect(service.type).to eq(described_class::DEFAULT_ITEM_TYPE)
end
end

context 'when `item:` and `type:` arguments are included' do
let(:args) { { item: parent, type: custom_type } }
let(:custom_type) { 'book' }

it 'uses the supplied `type` attribute' do
expect(service.type).to eq(custom_type)
end
end
end

describe '#reset' do
context 'without child relationships recorded in content metadata' do
let(:xml) { Nokogiri::XML(default_content_xml) }

before do
allow(Dor::Item).to receive(:find)
end

it 'resets content metadata without touching children' do
service.reset
expect(content_metadata).to have_received(:content=).with(default_content_xml).once
expect(parent).to have_received(:save!).once
expect(Dor::Item).not_to have_received(:find)
end
end

context 'with child relationships recorded in content metadata' do
let(:xml) do
Nokogiri::XML(
<<~XML
<contentMetadata objectId="druid:parent1" type="image">
<resource sequence="1" id="druid:child1_1" type="image">
<relationship type="alsoAvailableAs" objectId="druid:child1"/>
</resource>
<resource sequence="2" id="druid:child2_2" type="image">
<relationship type="alsoAvailableAs" objectId="druid:child2"/>
</resource>
</contentMetadata>
XML
)
end

before do
allow(Dor::Item).to receive(:find).with(child1.id).and_return(child1)
allow(Dor::Item).to receive(:find).with(child2.id).and_return(child2)
end

it 'severs the child relationships and resets content metadata' do
service.reset
expect(content_metadata).to have_received(:content=).with(default_content_xml).once
expect(parent).to have_received(:save!).once
expect(Dor::Item).to have_received(:find).exactly(2).times
expect(child1).to have_received(:clear_relationship).with(predicate).once
expect(child2).to have_received(:clear_relationship).with(predicate).once
expect(child1).to have_received(:save!)
expect(child2).to have_received(:save!)
end
end
end
end

0 comments on commit 060c813

Please sign in to comment.