# frozen_string_literal: true
module Dor
module Workflow
class Client
# Makes requests relating to a workflow
# rubocop:disable Metrics/ClassLength
class WorkflowRoutes
extend Deprecation
def initialize(requestor:)
@requestor = requestor
end
# This method is deprecated and calls create_workflow_by_name.
#
# @param [String] repo Ignored
# @param [String] druid The id of the object
# @param [String] workflow_name The name of the workflow you want to create
# @param [String] wf_xml Ignored
# @param [Hash] opts optional params
# @option opts [String] :lane_id adds laneId attribute to all process elements in the wf_xml workflow xml. Defaults to a value of 'default'
# @return [Boolean] always true
#
def create_workflow(_repo, druid, workflow_name, _wf_xml, opts = {})
create_workflow_by_name(druid, workflow_name, opts)
end
deprecation_deprecate create_workflow: 'use create_workflow_by_name instead'
# Creates a workflow for a given object in the repository. If this particular workflow for this objects exists,
# it will replace the old workflow. You have the option of creating a datastream or not.
# Returns true on success. Caller must handle any exceptions.
#
# @param [String] druid The id of the object
# @param [String] workflow_name The name of the workflow you want to create. This must correspond with a workflow
# name that is known by the workflow service.
# @param [String] lane_id adds laneId attribute to all process elements in the wf_xml workflow xml. Defaults to a value of 'default'
# @param [Integer] version specifies the version so that workflow service doesn't need to query dor-services.
# @return [Boolean] always true
#
def create_workflow_by_name(druid, workflow_name, version: nil, lane_id: 'default')
params = { 'lane-id' => lane_id }
if version
params['version'] = version
else
Deprecation.warn(self, 'Calling create_workflow_by_name without passing version is deprecated and will result in an error in dor-workflow-client 4.0')
end
requestor.request "objects/#{druid}/workflows/#{workflow_name}", 'post', '',
content_type: 'application/xml',
params: params
true
end
# Updates the status of one step in a workflow.
# Returns true on success. Caller must handle any exceptions
#
# @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
# @param [String] druid The id of the object
# @param [String] workflow The name of the workflow
# @param [String] process The name of the process step
# @param [String] status The status that you want to set -- using one of the values in VALID_STATUS
# @param [Float] :elapsed The number of seconds it took to complete this step. Can have a decimal. Is set to 0 if not passed in.
# @param [String] :lifecycle Bookeeping label for this particular workflow step. Examples are: 'registered', 'shelved'
# @param [String] :note Any kind of string annotation that you want to attach to the workflow
# @param [String] :current_status Setting this string tells the workflow service to compare the current status to this value. If the current value does not match this value, the update is not performed
# @return [Boolean] always true
# Http Call
# ==
# The method does an HTTP PUT to the base URL. As an example:
#
# PUT "/objects/pid:123/workflows/GoogleScannedWF/convert"
# "
def update_status(druid:, workflow:, process:, status:, elapsed: 0, lifecycle: nil, note: nil, current_status: nil)
raise ArgumentError, "Unknown status value #{status}" unless VALID_STATUS.include?(status)
raise ArgumentError, "Unknown current_status value #{current_status}" if current_status && !VALID_STATUS.include?(current_status)
xml = create_process_xml(name: process, status: status, elapsed: elapsed, lifecycle: lifecycle, note: note)
uri = "objects/#{druid}/workflows/#{workflow}/#{process}"
uri += "?current-status=#{current_status}" if current_status
response = requestor.request(uri, 'put', xml, content_type: 'application/xml')
Workflow::Response::Update.new(json: response)
end
# Updates the status of one step in a workflow.
# Returns true on success. Caller must handle any exceptions
#
# @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
# @param [String] druid The id of the object
# @param [String] workflow The name of the workflow
# @param [String] process The name of the process step
# @param [String] status The status that you want to set -- using one of the values in VALID_STATUS
# @param [Hash] opts optional values for the workflow step
# @option opts [Float] :elapsed The number of seconds it took to complete this step. Can have a decimal. Is set to 0 if not passed in.
# @option opts [String] :lifecycle Bookeeping label for this particular workflow step. Examples are: 'registered', 'shelved'
# @option opts [String] :note Any kind of string annotation that you want to attach to the workflow
# @option opts [String] :lane_id Id of processing lane used by the job manager. Can convey priority or name of an applicaiton specific processing lane (e.g. 'high', 'critical', 'hydrus')
# @option opts [String] :current_status Setting this string tells the workflow service to compare the current status to this value. If the current value does not match this value, the update is not performed
# @return [Boolean] always true
# Http Call
# ==
# The method does an HTTP PUT to the base URL. As an example:
#
# PUT "/dor/objects/pid:123/workflows/GoogleScannedWF/convert"
# "
def update_workflow_status(repo, druid, workflow, process, status, opts = {})
raise ArgumentError, "Unknown status value #{status}" unless VALID_STATUS.include?(status.downcase)
opts = { elapsed: 0, lifecycle: nil, note: nil }.merge!(opts)
opts[:elapsed] = opts[:elapsed].to_s
current_status = opts.delete(:current_status)
xml = create_process_xml({ name: process, status: status.downcase }.merge!(opts))
uri = "#{repo}/objects/#{druid}/workflows/#{workflow}/#{process}"
uri += "?current-status=#{current_status.downcase}" if current_status
response = requestor.request(uri, 'put', xml, content_type: 'application/xml')
Workflow::Response::Update.new(json: response)
end
deprecation_deprecate update_workflow_status: 'use update_status instead.'
#
# Retrieves the process status of the given workflow for the given object identifier
# @param [String] repo The repository the object resides in. Currently recoginzes "dor" and "sdr".
# @param [String] druid The id of the object
# @param [String] workflow The name of the workflow
# @param [String] process The name of the process step
# @return [String] status for repo-workflow-process-druid
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
def workflow_status(*args)
case args.length
when 4
Deprecation.warn(self, 'Calling workflow_status with positional args is deprecated, use kwargs instead')
(_repo, druid, workflow, process) = *args
when 1
opts = args.first
repo = opts[:repo]
Deprecation.warn(self, 'Passing `:repo` to workflow_status is deprecated and can be omitted') if repo
druid = opts[:druid]
workflow = opts[:workflow]
process = opts[:process]
end
workflow_md = fetch_workflow(druid: druid, workflow: workflow)
doc = Nokogiri::XML(workflow_md)
raise Dor::WorkflowException, "Unable to parse response:\n#{workflow_md}" if doc.root.nil?
processes = doc.root.xpath("//process[@name='#{process}']")
process = processes.max { |a, b| a.attr('version').to_i <=> b.attr('version').to_i }
process&.attr('status')
end
# rubocop:enable Metrics/AbcSize
# rubocop:enable Metrics/MethodLength
#
# Retrieves the raw XML for the given workflow
# @param [String] repo The repository the object resides in. Currently recoginzes "dor" and "sdr".
# @param [String] druid The id of the object
# @param [String] workflow The name of the workflow
# @return [String] XML of the workflow
# rubocop:disable Metrics/MethodLength
def workflow_xml(*args)
case args.length
when 3
Deprecation.warn(self, 'Calling workflow_xml with positional args is deprecated, use kwargs instead')
(repo, druid, workflow) = *args
when 1
opts = args.first
repo = opts[:repo]
Deprecation.warn(self, 'Passing `:repo` to workflow_xml is deprecated and can be omitted') if repo
druid = opts[:druid]
workflow = opts[:workflow]
end
raise ArgumentError, 'missing workflow' unless workflow
return requestor.request "#{repo}/objects/#{druid}/workflows/#{workflow}" if repo
fetch_workflow(druid: druid, workflow: workflow)
end
deprecation_deprecate workflow_xml: 'workflow_xml will not be replaced'
# rubocop:enable Metrics/MethodLength
# Updates the status of one step in a workflow to error.
# Returns true on success. Caller must handle any exceptions
#
# @param [String] _repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
# @param [String] druid The id of the object
# @param [String] workflow The name of the workflow
# @param [String] process The name of the workflow step
# @param [String] error_msg The error message. Ideally, this is a brief message describing the error
# @param [Hash] opts optional values for the workflow step
# @option opts [String] :error_text A slot to hold more information about the error, like a full stacktrace
# @return [Boolean] always true
#
# Http Call
# ==
# The method does an HTTP PUT to the base URL.
#
# PUT "/objects/pid:123/workflows/GoogleScannedWF/convert"
# "
def update_workflow_error_status(_repo, druid, workflow, process, error_msg, opts = {})
update_error_status(druid: druid, workflow: workflow, process: process, error_msg: error_msg, error_text: opts[:error_text])
true
end
deprecation_deprecate update_workflow_error_status: 'use update_error_status instead.'
# Updates the status of one step in a workflow to error.
# Returns true on success. Caller must handle any exceptions
#
# @param [String] druid The id of the object
# @param [String] workflow The name of the workflow
# @param [String] process The name of the workflow step
# @param [String] error_msg The error message. Ideally, this is a brief message describing the error
# @param [String] error_text A slot to hold more information about the error, like a full stacktrace
# @return [Workflow::Response::Update]
#
# Http Call
# ==
# The method does an HTTP PUT to the base URL.
#
# PUT "/objects/pid:123/workflows/GoogleScannedWF/convert"
# "
def update_error_status(druid:, workflow:, process:, error_msg:, error_text: nil)
xml = create_process_xml(name: process, status: 'error', errorMessage: error_msg, error_text: error_text)
response = requestor.request "objects/#{druid}/workflows/#{workflow}/#{process}", 'put', xml, content_type: 'application/xml'
Workflow::Response::Update.new(json: response)
end
#
# Retrieves the raw XML for all the workflows for the the given object
# @param [String] druid The id of the object
# @return [String] XML of the workflow
def all_workflows_xml(druid)
requestor.request "objects/#{druid}/workflows"
end
# Retrieves all workflows for the given object
# @param [String] pid The id of the object
# @return [Workflow::Response::Workflows]
def all_workflows(pid:)
xml = all_workflows_xml(pid)
Workflow::Response::Workflows.new(xml: xml)
end
# Get workflow names into an array for given PID
# This method only works when this gem is used in a project that is configured to connect to DOR
#
# @param [String] pid of druid
# @param [String] repo repository for the object
# @return [Array] list of worklows
# @example
# client.workflows('druid:sr100hp0609')
# => ["accessionWF", "assemblyWF", "disseminationWF"]
def workflows(pid, repo = nil)
Deprecation.warn(self, 'Passing the second argument (repo) to workflows is deprecated and can be omitted') if repo
xml_doc = Nokogiri::XML(fetch_workflow(druid: pid, workflow: ''))
xml_doc.xpath('//workflow').collect { |workflow| workflow['id'] }
end
# @param [String] repo repository of the object
# @param [String] pid id of object
# @param [String] workflow_name The name of the workflow
# @return [Workflow::Response::Workflow]
def workflow(repo: nil, pid:, workflow_name:)
Deprecation.warn(self, 'passing the repo parameter is deprecated and will be removed in the next major versions') if repo
xml = fetch_workflow(druid: pid, workflow: workflow_name)
Workflow::Response::Workflow.new(xml: xml)
end
# @param [String] pid id of object
# @param [String] workflow_name The name of the workflow
# @param [String] process The name of the workflow step
# @return [Workflow::Response::Process]
def process(pid:, workflow_name:, process:)
workflow(pid: pid, workflow_name: workflow_name).process_for_recent_version(name: process)
end
# Deletes a workflow from a particular repository and druid
# @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
# @param [String] druid The id of the object to delete the workflow from
# @param [String] workflow The name of the workflow to be deleted
# @return [Boolean] always true
def delete_workflow(repo, druid, workflow)
requestor.request "#{repo}/objects/#{druid}/workflows/#{workflow}", 'delete'
true
end
# Deletes all workflow steps for a particular druid
# @param [String] pid The id of the object to delete the workflow from
# @return [Boolean] always true
def delete_all_workflows(pid:)
requestor.request "objects/#{pid}/workflows", 'delete'
true
end
private
attr_reader :requestor
def fetch_workflow(druid:, workflow:)
raise ArgumentError, 'missing workflow' unless workflow
requestor.request "objects/#{druid}/workflows/#{workflow}"
end
# @param [Hash] params
# @return [String]
def create_process_xml(params)
builder = Nokogiri::XML::Builder.new do |xml|
attrs = params.reject { |_k, v| v.nil? }
attrs = Hash[attrs.map { |k, v| [k.to_s.camelize(:lower), v] }] # camelize all the keys in the attrs hash
xml.process(attrs)
end
builder.to_xml
end
end
# rubocop:enable Metrics/ClassLength
end
end
end