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

Removes FileSet related objects when work deleted (#6334). #6357

Merged
merged 16 commits into from
Oct 17, 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/concerns/hyrax/works_controller_behavior.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ def destroy
Hyrax.config.callback.run(:after_destroy, curation_concern.id, current_user, warn: false)
else
transactions['work_resource.destroy']
.with_step_args('work_resource.delete' => { user: current_user })
.call(curation_concern)
.value!
.with_step_args('work_resource.delete' => { user: current_user },
'work_resource.delete_all_file_sets' => { user: current_user })
.call(curation_concern).value!

title = Array(curation_concern.title).first
end
Expand Down
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 @@ -108,9 +108,9 @@ def destroy_batch
batch.each do |id|
resource = Hyrax.query_service.find_by(id: Valkyrie::ID.new(id))
transactions['work_resource.destroy']
.with_step_args('work_resource.delete' => { user: current_user })
.call(resource)
.value!
.with_step_args('work_resource.delete' => { user: current_user },
'work_resource.delete_all_file_sets' => { user: current_user })
.call(resource).value!
end
after_update
end
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 @@ -8,20 +8,7 @@ class MemberCleanupListener
# Called when 'object.deleted' event is published
# @param [Dry::Events::Event] event
# @return [void]
def on_object_deleted(event)
return unless event.payload.key?(:object) # legacy callback
return if event[:object].is_a?(ActiveFedora::Base) # handled by legacy code

Hyrax.custom_queries.find_child_file_sets(resource: event[:object]).each do |file_set|
Hyrax.persister.delete(resource: file_set)
Hyrax.publisher
.publish('object.deleted', object: file_set, id: file_set.id, user: event[:user])
rescue StandardError # we don't uncaught errors looping filesets
Hyrax.logger.warn "Failed to delete #{file_set.class}:#{file_set.id} " \
"during cleanup for resource: #{event[:object]}. " \
'This member may now be orphaned.'
end
end
def on_object_deleted(event); end

# Called when 'collection.deleted' event is published
# @param [Dry::Events::Event] event
Expand Down
11 changes: 11 additions & 0 deletions app/services/hyrax/listeners/metadata_index_listener.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ def on_collection_deleted(event)
Hyrax.index_adapter.delete(resource: event[:collection])
end

##
# Remove the resource from the index.
#
# Called when 'file.metadata.deleted' event is published
# @param [Dry::Events::Event] event
# @return [void]
def on_file_metadata_deleted(event)
return unless resource? event.payload[:metadata]
Hyrax.index_adapter.delete(resource: event[:metadata])
end

private

def resource?(resource)
Expand Down
4 changes: 4 additions & 0 deletions lib/hyrax/publisher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ class Publisher
# a system user.
register_event('file.metadata.updated')

# @since 5.0.0
# @macro a_registered_event
register_event('file.metadata.deleted')

# @since 5.0.0
# @macro a_registered_event
register_event('file.uploaded')
Expand Down
17 changes: 13 additions & 4 deletions lib/hyrax/transactions/container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Container # rubocop:disable Metrics/ClassLength
require 'hyrax/transactions/collection_create'
require 'hyrax/transactions/collection_destroy'
require 'hyrax/transactions/collection_update'
require 'hyrax/transactions/file_metadata_destroy'
require 'hyrax/transactions/file_set_destroy'
require 'hyrax/transactions/file_set_update'
require 'hyrax/transactions/work_create'
Expand All @@ -38,8 +39,11 @@ class Container # rubocop:disable Metrics/ClassLength
require 'hyrax/transactions/steps/change_depositor'
require 'hyrax/transactions/steps/check_for_empty_admin_set'
require 'hyrax/transactions/steps/delete_access_control'
require 'hyrax/transactions/steps/delete_all_file_metadata'
require 'hyrax/transactions/steps/delete_all_file_sets'
require 'hyrax/transactions/steps/delete_resource'
require 'hyrax/transactions/steps/ensure_admin_set'
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/save'
Expand All @@ -53,9 +57,6 @@ class Container # rubocop:disable Metrics/ClassLength
require 'hyrax/transactions/steps/set_user_as_depositor'
require 'hyrax/transactions/steps/update_work_members'
require 'hyrax/transactions/steps/validate'
require 'hyrax/transactions/steps/delete_all_file_metadata'
require 'hyrax/transactions/steps/file_metadata_delete'
require 'hyrax/transactions/file_metadata_destroy'

extend Dry::Container::Mixin

Expand Down Expand Up @@ -147,7 +148,7 @@ class Container # rubocop:disable Metrics/ClassLength
end

ops.register 'delete_all_file_metadata' do
Steps::DeleteAllFileMetadata.new(property: :file_ids)
Steps::DeleteAllFileMetadata.new
end

ops.register 'destroy' do
Expand All @@ -158,6 +159,10 @@ class Container # rubocop:disable Metrics/ClassLength
Steps::RemoveFileSetFromWork.new
end

ops.register 'delete_acl' do
Steps::DeleteAccessControl.new
end

ops.register 'save_acl' do
Steps::SaveAccessControl.new
end
Expand Down Expand Up @@ -244,6 +249,10 @@ class Container # rubocop:disable Metrics/ClassLength
Steps::DeleteResource.new
end

ops.register 'delete_all_file_sets' do
Steps::DeleteAllFileSets.new
end

ops.register 'destroy' do
WorkDestroy.new
end
Expand Down
7 changes: 4 additions & 3 deletions lib/hyrax/transactions/file_set_destroy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ module Transactions
#
# @since 3.1.0
class FileSetDestroy < Transaction
DEFAULT_STEPS = ['file_set.remove_from_work',
'file_set.delete',
'file_set.delete_all_file_metadata'].freeze
DEFAULT_STEPS = ['file_set.delete_all_file_metadata',
'file_set.remove_from_work',
'file_set.delete_acl',
'file_set.delete'].freeze

##
# @see Hyrax::Transactions::Transaction
Expand Down
7 changes: 5 additions & 2 deletions lib/hyrax/transactions/steps/delete_all_file_metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class DeleteAllFileMetadata

##
# @params [#save] persister
def initialize(property:, query_service: Hyrax.query_service, persister: Hyrax.persister, publisher: Hyrax.publisher)
def initialize(property: :file_ids, query_service: Hyrax.query_service, persister: Hyrax.persister, publisher: Hyrax.publisher)
@property = property
@persister = persister
@query_service = query_service
Expand All @@ -30,7 +30,10 @@ def call(resource)
return Failure(:resource_not_persisted) unless resource.persisted?

resource[@property].each do |file_id|
Hyrax::Transactions::Container['file_metadata.destroy'].call(@query_service.custom_queries.find_file_metadata_by(id: file_id))
return Failure[:failed_to_delete_file_metadata, file_id] unless
Hyrax::Transactions::Container['file_metadata.destroy']
.call(@query_service.custom_queries.find_file_metadata_by(id: file_id))
.success?
rescue ::Ldp::Gone
nil
end
Expand Down
46 changes: 46 additions & 0 deletions lib/hyrax/transactions/steps/delete_all_file_sets.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true
require 'dry/monads'

module Hyrax
module Transactions
module Steps
##
# Deletes a resource's member FileSets from the persister, returning a `Dry::Monads::Result`
# (`Success`|`Failure`).
#
# @see https://dry-rb.org/gems/dry-monads/1.0/result/
class DeleteAllFileSets
include Dry::Monads[:result]

##
# @params [#save] persister
def initialize(query_service: Hyrax.query_service, 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(resource, user: nil)
return Failure(:resource_not_persisted) unless resource.persisted?

@query_service.custom_queries.find_child_file_sets(resource: resource).each do |file_set|
return Failure[:failed_to_delete_file_set, file_set] unless
Hyrax::Transactions::Container['file_set.destroy']
.with_step_args('file_set.remove_from_work' => { user: user },
'file_set.delete' => { user: user })
.call(file_set).success?
rescue ::Ldp::Gone
nil
end

Success(resource)
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/hyrax/transactions/steps/file_metadata_delete.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def call(resource)
return Failure(:resource_not_persisted) unless resource.persisted?

@persister.delete(resource: resource)
@publisher.publish('file.metadata.deleted', metadata: resource)
Valkyrie::StorageAdapter.delete(id: resource.file_identifier)

Success(resource)
Expand Down
3 changes: 2 additions & 1 deletion lib/hyrax/transactions/work_destroy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ module Transactions
#
# @since 3.0.0
class WorkDestroy < Transaction
DEFAULT_STEPS = ['work_resource.delete_acl',
DEFAULT_STEPS = ['work_resource.delete_all_file_sets',
'work_resource.delete_acl',
'work_resource.delete'].freeze

##
Expand Down
13 changes: 12 additions & 1 deletion spec/hyrax/transactions/file_set_destroy_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'spec_helper'
require 'hyrax/transactions'
require 'dry/container/stub'

RSpec.describe Hyrax::Transactions::FileSetDestroy do
subject(:transaction) { described_class.new }
Expand All @@ -10,6 +9,11 @@
describe '#call' do
let(:user) { FactoryBot.create(:user) }

before do
file_set.permission_manager.read_users = [user.user_key]
file_set.permission_manager.acl.save
end

context 'without a user' do
it 'is a failure' do
expect(transaction.call(file_set)).to be_failure
Expand All @@ -28,6 +32,13 @@
.to raise_error Valkyrie::Persistence::ObjectNotFoundError
end

it 'deletes the access control resource' do
expect { transaction.with_step_args('file_set.remove_from_work' => { user: user }).call(file_set) }
.to change { Hyrax::AccessControl.for(resource: file_set).persisted? }
.from(true)
.to(false)
end

context "with attached files" do
let(:work) { FactoryBot.valkyrie_create(:hyrax_work, uploaded_files: [FactoryBot.create(:uploaded_file)], edit_users: [user]) }
let(:file_set) { query_service.find_members(resource: work).first }
Expand Down
30 changes: 30 additions & 0 deletions spec/hyrax/transactions/steps/delete_all_file_metadata_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true
require 'spec_helper'
require 'hyrax/transactions'

RSpec.describe Hyrax::Transactions::Steps::DeleteAllFileMetadata, valkyrie_adapter: :test_adapter, storage_adapter: :test_disk do
subject(:step) { described_class.new }
let(:file_set) { FactoryBot.valkyrie_create(:hyrax_file_set, :with_files) }

describe '#call' do
it 'gives success' do
expect(step.call(file_set)).to be_success
end

it 'destroys each file_set' do
file_ids = file_set.file_ids
step.call(file_set)
file_ids.each do |id|
expect { Hyrax.query_service.find_by(id: id) }.to raise_error Valkyrie::Persistence::ObjectNotFoundError
end
end

context 'with a resource that is not saved' do
let(:file_set) { FactoryBot.build(:hyrax_file_set) }

it 'is a failure' do
expect(step.call(file_set)).to be_failure
end
end
end
end
35 changes: 35 additions & 0 deletions spec/hyrax/transactions/steps/delete_all_file_sets_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true
require 'spec_helper'
require 'hyrax/transactions'

RSpec.describe Hyrax::Transactions::Steps::DeleteAllFileSets, valkyrie_adapter: :test_adapter do
subject(:step) { described_class.new }
let(:user) { FactoryBot.create(:user) }
let(:work) { FactoryBot.valkyrie_create(:hyrax_work, :with_member_file_sets) }

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

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

it 'destroys each file_set' do
member_ids = work.member_ids
step.call(work, user: user)
member_ids.each do |id|
expect { Hyrax.query_service.find_by(id: id) }.to raise_error Valkyrie::Persistence::ObjectNotFoundError
end
end

context 'with a resource that is not saved' do
let(:work) { FactoryBot.build(:hyrax_work) }

it 'is a failure' do
expect(step.call(work)).to be_failure
end
end
end
end
60 changes: 60 additions & 0 deletions spec/hyrax/transactions/work_destroy_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# frozen_string_literal: true
require 'spec_helper'
require 'hyrax/transactions'

RSpec.describe Hyrax::Transactions::WorkDestroy do
subject(:transaction) { described_class.new }
let(:work) { FactoryBot.valkyrie_create(:hyrax_work, read_users: [user], members: [file_set]) }
let(:file_set) { FactoryBot.valkyrie_create(:hyrax_file_set) }

describe '#call' do
let(:user) { FactoryBot.create(:user) }

context 'without a user' do
it 'is a failure' do
expect(transaction.call(work)).to be_failure
end
end

it 'succeeds' do
expect(transaction.with_step_args('work_resource.delete_all_file_sets' => { user: user }).call(work))
.to be_success
end

it 'deletes the work' do
transaction.with_step_args('work_resource.delete_all_file_sets' => { user: user }).call(work)

expect { Hyrax.query_service.find_by(id: work.id) }
.to raise_error Valkyrie::Persistence::ObjectNotFoundError
end

it 'deletes the file set' do
transaction.with_step_args('work_resource.delete_all_file_sets' => { user: user }).call(work)

expect { Hyrax.query_service.find_by(id: file_set.id) }
.to raise_error Valkyrie::Persistence::ObjectNotFoundError
end

it 'deletes the access control resource' do
expect { transaction.with_step_args('work_resource.delete_all_file_sets' => { user: user }).call(work) }
.to change { Hyrax::AccessControl.for(resource: work).persisted? }
.from(true).to(false)
end

context "with attached files" do
let(:work) { FactoryBot.valkyrie_create(:hyrax_work, uploaded_files: [FactoryBot.create(:uploaded_file)], edit_users: [user]) }
let(:file_set) { query_service.find_members(resource: work).first }
let(:file_metadata) { query_service.custom_queries.find_file_metadata_by(id: file_set.file_ids.first) }
let(:uploaded) { storage_adapter.find_by(id: file_metadata.file_identifier) }
let(:storage_adapter) { Hyrax.storage_adapter }
let(:query_service) { Hyrax.query_service }
it "deletes them" do
file_metadata
transaction.with_step_args('work_resource.delete_all_file_sets' => { user: user }).call(work)

expect { Hyrax.query_service.find_by(id: file_metadata.id) }.to raise_error Valkyrie::Persistence::ObjectNotFoundError
expect { storage_adapter.find_by(id: uploaded.id) }.to raise_error Valkyrie::StorageAdapter::FileNotFound
end
end
end
end
Loading