diff --git a/lib/rails_ext/active_storage_analyze_job_skip_detached.rb b/lib/rails_ext/active_storage_analyze_job_skip_detached.rb new file mode 100644 index 000000000..0df49f0cf --- /dev/null +++ b/lib/rails_ext/active_storage_analyze_job_skip_detached.rb @@ -0,0 +1,14 @@ +# Skip analysis for blobs whose attachments have already been destroyed. +# e.g. when a user uploads a file but deletes it before the analysis runs. +# Avoids `Aws::S3::Errors::NoSuchKey` when an upload is deleted before AnalyzeJob runs. +module ActiveStorageAnalyzeJobSkipDetached + def perform(blob) + return unless blob.attachments.exists? + + super + end +end + +ActiveSupport.on_load :active_storage_blob do + ActiveStorage::AnalyzeJob.prepend ActiveStorageAnalyzeJobSkipDetached +end diff --git a/test/lib/rails_ext/active_storage_analyze_job_skip_detached_test.rb b/test/lib/rails_ext/active_storage_analyze_job_skip_detached_test.rb new file mode 100644 index 000000000..5ab5edaff --- /dev/null +++ b/test/lib/rails_ext/active_storage_analyze_job_skip_detached_test.rb @@ -0,0 +1,23 @@ +require "test_helper" + +class ActiveStorageAnalyzeJobSkipDetachedTest < ActiveSupport::TestCase + test "skips analysis when blob has no attachments" do + blob = ActiveStorage::Blob.create_and_upload!( + io: StringIO.new("x" * 1024), filename: "orphan.txt", content_type: "text/plain" + ) + + blob.expects(:analyze).never + + ActiveStorage::AnalyzeJob.perform_now(blob) + end + + test "performs analysis when blob has attachments" do + card = cards(:logo) + card.image.attach io: StringIO.new("x" * 1024), filename: "test.png", content_type: "image/png" + blob = card.image.blob + + blob.expects(:analyze).once + + ActiveStorage::AnalyzeJob.perform_now(blob) + end +end