Class: Hyrax::WorkUploadsHandler

Inherits:
Object
  • Object
show all
Includes:
Lockable
Defined in:
app/services/hyrax/work_uploads_handler.rb

Overview

TODO:

make this genuinely retry-able. if we fail after creating some of the file_sets, but not attaching them to works, we should resolve that incomplete work on subsequent runs.

Mediates uploads to a work.

Given an existing Work object, #add some set of files, then call #attach to handle creation/attachment of File Sets, and trigger persistence of files to the storage backend.

This class provides both an interface and a concrete implementation. Applications that want to handle file attachment differently (e.g. by making them completely synchronous, by attaching file sets to a different work attribute, by supporting another file/IO class, etc…) can use a different Handler implementation. The only guarantee made by the interface is that the process of persisting the relationship between work and the provided files will start when #attach is called.

This base implementation accepts only Hyrax::UploadedFile instances and, for each one, creates a Hyrax::FileSet with permissions matching those on work, and appends that FileSet to member_ids. The FileSet will be added in the order that the UploadedFiles are passed in. If the work has a nil representative_id and/or thumbnail_id, the first FileSet will be set to that value. An IngestJob will be equeued, for each FileSet. When all of the files have been processed, the work will be saved with the added members. While this is happening, we take a lock on the work via Lockable (Redis/Redlock).

This also publishes events as required by Hyrax.publisher.

Examples:

Hyrax::WorkUploadsHandler.new(work: my_work)
  .add(files: [file1, file2])
  .attach

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Lockable

#lock_manager

Constructor Details

#initialize(work:, persister: Hyrax.persister) ⇒ WorkUploadsHandler

Returns a new instance of WorkUploadsHandler.

Parameters:

  • work (Hyrax::Work)
  • persister (#save) (defaults to: Hyrax.persister)

    the valkyrie persister to use



58
59
60
61
# File 'app/services/hyrax/work_uploads_handler.rb', line 58

def initialize(work:, persister: Hyrax.persister)
  @work = work
  @persister = persister
end

Instance Attribute Details

#file_set_paramsObject (readonly)



53
# File 'app/services/hyrax/work_uploads_handler.rb', line 53

attr_reader :files, :work, :file_set_params

#filesEnumberable<Hyrax::UploadedFile> (readonly)

Returns:



53
54
55
# File 'app/services/hyrax/work_uploads_handler.rb', line 53

def files
  @files
end

#workHyrax::Work (readonly)

Returns:



53
# File 'app/services/hyrax/work_uploads_handler.rb', line 53

attr_reader :files, :work, :file_set_params

Instance Method Details

#add(files:, file_set_params: []) ⇒ WorkFileSetManager

Note:

we immediately and silently discard uploads with an existing file_set_uri, in a half-considered attempt at supporting idempotency (for job retries). this is for legacy/AttachFilesToWorkJob compatibility, but could stand for a robust reimplementation.

Returns self.

Parameters:

Returns:

  • (WorkFileSetManager)

    self

Raises:

  • (ArgumentError)

    if any of the uploaded files are not an UploadedFile



76
77
78
79
80
81
# File 'app/services/hyrax/work_uploads_handler.rb', line 76

def add(files:, file_set_params: [])
  validate_files(files) &&
    @files = Array.wrap(files).reject { |f| f.file_set_uri.present? }
  @file_set_params = file_set_params
  self
end

#attachBoolean

Create filesets for each added file

Returns:

  • (Boolean)

    true if all requested files were attached



89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'app/services/hyrax/work_uploads_handler.rb', line 89

def attach
  return true if Array.wrap(files).empty? # short circuit to avoid aquiring a lock we won't use

  acquire_lock_for(work.id) do
    event_payloads = files.each_with_object([]) { |file, arry| arry << make_file_set_and_ingest(file) }
    @persister.save(resource: work)
    Hyrax.publisher.publish('object.metadata.updated', object: work, user: files.first.user)
    event_payloads.each do |payload|
      payload.delete(:job).enqueue
      Hyrax.publisher.publish('file.set.attached', payload)
    end
  end
end