Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes the deletion of collections 500 (#6362). #6367

Merged
merged 7 commits into from
Oct 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/controllers/hyrax/batch_edits_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ def destroy_collection
batch.each do |doc_id|
resource = Hyrax.query_service.find_by(id: Valkyrie::ID.new(doc_id))
transactions['collection_resource.destroy']
.with_step_args('collection_resource.delete' => { user: current_user })
.call(resource)
.value!
.with_step_args('collection_resource.delete' => { user: current_user },
'collection_resource.remove_from_membership' => { user: current_user })
.call(resource).value!
end
flash[:notice] = "Batch delete complete"
after_destroy_collection
Expand Down
5 changes: 4 additions & 1 deletion app/controllers/hyrax/dashboard/collections_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,10 @@ def update_valkyrie_collection
end

def valkyrie_destroy
if transactions['collection_resource.destroy'].call(@collection).success?
if transactions['collection_resource.destroy']
.with_step_args('collection_resource.delete' => { user: current_user },
'collection_resource.remove_from_membership' => { user: current_user })
.call(@collection).success?
after_destroy(params[:id])
else
after_destroy_error(params[:id])
Expand Down
15 changes: 1 addition & 14 deletions app/services/hyrax/listeners/member_cleanup_listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,7 @@ def on_object_deleted(event); end
# Called when 'collection.deleted' event is published
# @param [Dry::Events::Event] event
# @return [void]
def on_collection_deleted(event)
return unless event.payload.key?(:collection) # legacy callback
return if event[:collection].is_a?(ActiveFedora::Base) # handled by legacy code

Hyrax.custom_queries.find_members_of(collection: event[:collection]).each do |resource|
resource.member_of_collection_ids -= [event[:collection].id]
Hyrax.persister.save(resource: resource)
Hyrax.publisher
.publish('collection.membership.updated', collection: event[:collection], user: event[:user])
rescue StandardError
Hyrax.logger.warn "Failed to remove collection reference from #{work.class}:#{work.id} " \
"during cleanup for collection: #{event[:collection]}. "
end
end
def on_collection_deleted(event); end
end
end
end
5 changes: 3 additions & 2 deletions lib/hyrax/transactions/collection_destroy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ module Transactions
# @since 3.4.0
class CollectionDestroy < Transaction
# TODO: Add step that checks if collection is empty for collections of types that require it
DEFAULT_STEPS = ['collection_resource.delete',
'collection_resource.delete_acl'].freeze
DEFAULT_STEPS = ['collection_resource.delete_acl',
'collection_resource.remove_from_membership',
'collection_resource.delete'].freeze

##
# @see Hyrax::Transactions::Transaction
Expand Down
5 changes: 5 additions & 0 deletions lib/hyrax/transactions/container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class Container # rubocop:disable Metrics/ClassLength
require 'hyrax/transactions/steps/file_metadata_delete'
require 'hyrax/transactions/steps/set_collection_type_gid'
require 'hyrax/transactions/steps/remove_file_set_from_work'
require 'hyrax/transactions/steps/remove_from_membership'
require 'hyrax/transactions/steps/save'
require 'hyrax/transactions/steps/save_access_control'
require 'hyrax/transactions/steps/save_collection_banner'
Expand Down Expand Up @@ -215,6 +216,10 @@ class Container # rubocop:disable Metrics/ClassLength
Steps::DeleteAccessControl.new
end

ops.register 'remove_from_membership' do
Steps::RemoveFromMembership.new
end

ops.register 'save_acl' do
Steps::SaveAccessControl.new
end
Expand Down
45 changes: 45 additions & 0 deletions lib/hyrax/transactions/steps/remove_from_membership.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# frozen_string_literal: true
require 'dry/monads'

module Hyrax
module Transactions
module Steps
##
# Removes a collection from its members, returning a `Dry::Monads::Result`
# (`Success`|`Failure`).
#
# @see https://dry-rb.org/gems/dry-monads/1.0/result/
class RemoveFromMembership
include Dry::Monads[:result]

##
# @params [#save] persister
def initialize(query_service: Hyrax.custom_queries, persister: Hyrax.persister, publisher: Hyrax.publisher)
@persister = persister
@query_service = query_service
@publisher = publisher
end

##
# @param [Valkyrie::Resource] resource
# @param [::User] the user resposible for the delete action
#
# @return [Dry::Monads::Result]
def call(collection, user: nil)
return Failure(:resource_not_persisted) unless collection.persisted?
return Failure(:user_not_present) if user.blank?

@query_service.find_members_of(collection: collection).each do |member|
member.member_of_collection_ids -= [collection.id]
@persister.save(resource: member)
@publisher.publish('collection.membership.updated', collection: collection, user: user)
rescue StandardError
nil
end

Success(collection)
end
end
end
end
end
8 changes: 7 additions & 1 deletion spec/hyrax/transactions/collection_destroy_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@
RSpec.describe Hyrax::Transactions::CollectionDestroy, valkyrie_adapter: :test_adapter do
subject(:tx) { described_class.new }
let(:resource) { FactoryBot.valkyrie_create(:hyrax_collection) }
let(:user) { FactoryBot.create(:user) }

describe '#call' do
it 'is a success' do
expect(tx.call(resource)).to be_success
expect(
tx
.with_step_args('collection_resource.delete' => { user: user },
'collection_resource.remove_from_membership' => { user: user })
.call(resource)
).to be_success
end
end
end
41 changes: 41 additions & 0 deletions spec/hyrax/transactions/steps/remove_from_membership_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# frozen_string_literal: true
require 'spec_helper'
require 'hyrax/transactions'
require 'hyrax/specs/spy_listener'

RSpec.describe Hyrax::Transactions::Steps::RemoveFromMembership, valkyrie_adapter: :test_adapter do
subject(:step) { described_class.new }
let(:user) { FactoryBot.create(:user) }
let(:collection) { FactoryBot.valkyrie_create(:hyrax_collection) }
let(:work) { FactoryBot.valkyrie_create(:monograph, member_of_collection_ids: [collection.id]) }
let(:spy_listener) { Hyrax::Specs::SpyListener.new }

describe '#call' do
before do
work
Hyrax.publisher.subscribe(spy_listener)
end
after { Hyrax.publisher.unsubscribe(spy_listener) }

it 'fails without a user' do
expect(step.call(collection)).to be_failure
end

it 'gives success' do
expect(step.call(collection, user: user)).to be_success
end

it 'removes collection references from member objects' do
expect { step.call(collection, user: user) }
.to change { Hyrax.custom_queries.find_members_of(collection: collection).size }
.from(1)
.to(0)
end

it 'publishes events' do
expect { step.call(collection, user: user) }
.to change { spy_listener.collection_membership_updated&.payload }
.to match(collection: collection, user: user)
end
end
end
4 changes: 2 additions & 2 deletions spec/services/hyrax/listeners/member_cleanup_listener_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,14 @@
work
end

it 'removes collection references from member objects' do
xit 'removes collection references from member objects' do
expect { listener.on_collection_deleted(event) }
.to change { Hyrax.custom_queries.find_members_of(collection: event[:collection]).size }
.from(1)
.to(0)
end

it 'publishes events' do
xit 'publishes events' do
listener.on_collection_deleted(event)
expect(spy_listener.collection_membership_updated&.payload)
.to include(collection: collection, user: user)
Expand Down