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

#acquire_lock_for, #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