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

🧹 Make CreateRelationshipJob work for Valkyrie #908

Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion app/factories/bulkrax/object_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def find
return found if found.present?
rescue Valkyrie::Persistence::ObjectNotFoundError
false
ensure
else
search_by_identifier if attributes[work_identifier].present?
end

Expand Down
25 changes: 22 additions & 3 deletions app/jobs/bulkrax/create_relationships_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,13 @@ def perform(parent_identifier:, importer_run_id:) # rubocop:disable Metrics/AbcS
if @parent_record_members_added
parent_record.save!
# Ensure that the new relationship gets indexed onto the children
@child_members_added.each(&:update_index)
if parent_record.is_a?(Valkyrie::Resource)
@child_members_added.each do |child|
Hyrax.index_adapter.save(resource: child)
end
else
@child_members_added.each(&:update_index)
end
end
end
else
Expand Down Expand Up @@ -165,13 +171,26 @@ def add_to_collection(child_record, parent_record)
end

def add_to_work(child_record, parent_record)
return true if parent_record.ordered_members.to_a.include?(child_record)
parent_record.is_a?(Valkyrie::Resource) ? add_to_valkyrie_work(child_record, parent_record) : add_to_af_work(child_record, parent_record)

parent_record.ordered_members << child_record
@parent_record_members_added = true
@child_members_added << child_record
end

def add_to_valkyrie_work(child_record, parent_record)
return true if parent_record.member_ids.include?(child_record.id)

parent_record.member_ids << child_record.id
change_set = Hyrax::ChangeSet.for(parent_record)
Hyrax::Transactions::Container['change_set.update_work'].call(change_set)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm curious about this choice. won't this ensure that the entire update work process (the same one that happens via the controller action) takes place just to change the work membership?

Hyrax::Publisher provides an object.membership.updated event, and i wonder whether it wouldn't be better to save the work with the updated membership and publish that? https://github.com/samvera/hyrax/blob/fdcabe651850b1885d7bd3e1b36777bf5f336ee8/lib/hyrax/publisher.rb#L183-L192

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@no-reply, for our uses, we're trying to hit this transaction, so you're suggesting we put a publisher here and then that means this can become a listener?

Copy link
Contributor Author

@kirkkwang kirkkwang Feb 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here was the original idea of the listener

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kirkkwang yes. i think what i'm suggesting (and i'm just popping in as an outsider here), is that you want this behavior to trigger whenever an object's membership changes, not only when a given transaction runs.

making it a listener means you'll trigger these changes when outside callers (in Hyrax or in the downstream application) change membership, as long as they dutifully publish the expected message.

i think a helpful line to draw around transactions and listeners is:

  • use a transaction step when you want to EXPLICITLY implement some behavior as part of a given operation, but you might want to (explicitly) implement different behavior in a different context.
    • e.g. i might want to set the current_user as the "depositor" every time a create action happens in the hyrax works controller, but handle this matter differently if i create an object through an API endpoint.
    • it would be normal for the Hyrax engine, Bulkrax's own code, or the downstream application to call different transactions that may or may not include steps the others might use.
  • use a listener when you want to ensure some behavior always runs in response to some event, regardless of that event's context.
    • e.g. whenever metadata updates for an object, i want to reindex that object.

another way of thinking about it is:

  • if in a naive MVC architecture i would have just written the code in the controller, or as a model method, like do_this; do_that; do_other, then you want that to be a transaction step.
  • if in a naive MVC architecture i would have written a callback, you want a listener. (it might be helpful to think of a listener as a multiplexed callback, decoupled from the model layer).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @no-reply that's super helpful!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI I made a ticket here scientist-softserv/iiif_print#330

Copy link
Contributor Author

@kirkkwang kirkkwang Feb 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@no-reply should this publish 'object.membership.updated'

end

def add_to_af_work(child_record, parent_record)
return true if parent_record.ordered_members.to_a.include?(child_record)

parent_record.ordered_members << child_record
end

def reschedule(parent_identifier:, importer_run_id:)
CreateRelationshipsJob.set(wait: 10.minutes).perform_later(
parent_identifier: parent_identifier,
Expand Down
2 changes: 1 addition & 1 deletion lib/bulkrax/persistence_layer/active_fedora_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class ActiveFedoraAdapter < AbstractAdapter
def self.find(id)
ActiveFedora::Base.find(id)
rescue ActiveFedora::ObjectNotFoundError => e
raise PersistenceLayer::RecordNotFound, e.message
raise PersistenceLayer::ObjectNotFoundError, e.message
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

end

def self.query(q, **kwargs)
Expand Down
Loading