sul-dlss/argo

View on GitHub
app/services/apply_mods_metadata.rb

Summary

Maintainability
A
0 mins
Test Coverage
B
88%
# frozen_string_literal: true

# Updates the metadata of an object with the given MODS
class ApplyModsMetadata
  # @param [String] apo_druid
  # @param [String] mods A string containing MODS XML.
  # @param [Cocina::Models::DRO] cocina the item to be updated
  # @param [String] original_filename the filename these updates came from
  # @param [Ability] ability the abilities of the acting user
  # @param [#puts] log
  def initialize(apo_druid:, mods:, cocina:, original_filename:, ability:, log:)
    @apo_druid = apo_druid
    @mods = mods
    @cocina = cocina
    @original_filename = original_filename
    @ability = ability
    @log = log
  end

  def can_apply?
    # Only update objects that are governed by the correct APO
    unless cocina.administrative.hasAdminPolicy == apo_druid
      log.puts("argo.bulk_metadata.bulk_log_apo_fail #{cocina.externalIdentifier}")
      return false
    end

    return false unless updatable?

    return false unless ability.can? :update, cocina

    # We only update objects if the cocina is different
    if cocina.description == cocina_description
      log.puts("argo.bulk_metadata.bulk_log_skipped_mods #{cocina.externalIdentifier}")
      return false
    end

    errors = ModsValidator.validate(mods_ng)
    if errors.present?
      log.puts "argo.bulk_metadata.bulk_log_validation_error #{cocina.externalIdentifier} #{errors.join(';')}"
      return false
    end

    true
  end

  def apply
    return unless can_apply?

    version_object
    begin
      update_metadata

      log.puts("argo.bulk_metadata.bulk_log_job_save_success #{cocina.externalIdentifier}")
    rescue Dor::Services::Client::UnexpectedResponse => e
      log.puts("argo.bulk_metadata.bulk_log_unexpected_response #{cocina.externalIdentifier} #{e.message}")
    end
  rescue Cocina::Models::ValidationError => e
    log.puts("argo.bulk_metadata.bulk_log_validation_error #{cocina.externalIdentifier} #{e.message}")
  rescue StandardError => e
    log_error!(e)
  end

  private

  attr_reader :apo_druid, :mods, :cocina, :original_filename, :ability, :log

  # Log the error
  def log_error!(exception)
    log.puts("argo.bulk_metadata.bulk_log_error_exception #{cocina.externalIdentifier}")
    log.puts(exception.message.to_s)
    log.puts(exception.backtrace.to_s)
  end

  # Open a new version for the given object if not already open
  def version_object
    return if version_service.open?

    @cocina = version_service.open(druid: cocina.externalIdentifier,
                                   description: "Descriptive metadata upload from #{original_filename}",
                                   opening_user_name: ability.current_user.sunetid)
  end

  def update_metadata
    object_client = Dor::Services::Client.object(cocina.externalIdentifier)
    object_client.update(params: cocina.new(description: cocina_description))
  end

  # Item must be open or openable? to be updated
  def updatable?
    return true if version_service.open? || version_service.openable?

    log.puts("argo.bulk_metadata.bulk_log_skipped_mods #{cocina.externalIdentifier}")
    false
  end

  def version_service
    @version_service ||= VersionService.new(druid: cocina.externalIdentifier)
  end

  def cocina_description
    @cocina_description ||= Cocina::Models::Description.new(Cocina::Models::Mapping::FromMods::Description.props(mods: mods_ng, druid: cocina.externalIdentifier,
                                                                                                                 label: cocina.label))
  end

  def mods_ng
    @mods_ng ||= Nokogiri::XML(mods)
  end
end