Module: Dor::WorkflowService

Defined in:
lib/dor/services/workflow_service.rb

Overview

Methods to create and update workflow

Constant Summary collapse

@@resource =
nil
@@dor_services_url =
nil

Class Method Summary collapse

Class Method Details

.add_priority_to_workflow_xml(priority, wf_xml) ⇒ String

Adds priority attributes to each process of workflow xml

Parameters:

  • priority (Integer)

    value to add to each process element

  • wf_xml (String)

    the workflow xml

Returns:

  • (String)

    wf_xml with priority attributes



41
42
43
44
45
46
# File 'lib/dor/services/workflow_service.rb', line 41

def add_priority_to_workflow_xml(priority, wf_xml)
  return wf_xml if(priority.to_i == 0)
  doc = Nokogiri::XML(wf_xml)
  doc.xpath('/workflow/process').each { |proc| proc['priority'] = priority }
  doc.to_xml
end

.archive_active_workflow(repo, druid) ⇒ Object



262
263
264
265
266
267
# File 'lib/dor/services/workflow_service.rb', line 262

def archive_active_workflow(repo, druid)
  workflows = get_active_workflows(repo, druid)
  workflows.each do |wf|
    archive_workflow(repo, druid, wf)
  end
end

.archive_workflow(repo, druid, wf_name, version_num = nil) ⇒ Object



269
270
271
272
273
274
275
276
# File 'lib/dor/services/workflow_service.rb', line 269

def archive_workflow(repo, druid, wf_name, version_num=nil)
  raise "Please call Dor::WorkflowService.configure(workflow_service_url, :dor_services_url => DOR_SERVIES_URL) once before archiving workflow" if(@@dor_services_url.nil?)

  dor_services = RestClient::Resource.new(@@dor_services_url)
  url = "/v1/objects/#{druid}/workflows/#{wf_name}/archive"
  url << "/#{version_num}" if(version_num)
  dor_services[url].post ''
end

.close_version(repo, druid, create_accession_wf = true) ⇒ Object

Calls the versionClose endpoint of the WorkflowService:

- completes the versioningWF:submit-version and versioningWF:start-accession steps
- initiates accesssionWF

Parameters:

  • repo (String)

    The repository the object resides in. The service recoginzes “dor” and “sdr” at the moment

  • druid (String)

    The id of the object to delete the workflow from

  • create_accession_wf (Boolean) (defaults to: true)

    Option to create accessionWF when closing a version. Defaults to true



284
285
286
287
288
289
# File 'lib/dor/services/workflow_service.rb', line 284

def close_version(repo, druid, create_accession_wf = true)
  uri = "#{repo}/objects/#{druid}/versionClose"
  uri << "?create-accession=false" if(!create_accession_wf)
  workflow_resource[uri].post ''
  return true
end

.configure(url, opts = {}) ⇒ Object

Parameters:

  • url (String)

    points to the workflow service

  • opts (Hash) (defaults to: {})

    optional params

Options Hash (opts):

  • :client_cert_file (String)

    path to an SSL client certificate

  • :client_key_file (String)

    path to an SSL key file

  • :client_key_pass (String)

    password for the key file

  • :dor_services_uri (String)

    uri to the DOR REST service



302
303
304
305
306
307
308
# File 'lib/dor/services/workflow_service.rb', line 302

def configure(url, opts={})
  params = {}
  @@dor_services_url = opts[:dor_services_url] if opts[:dor_services_url]
  #params[:ssl_client_cert] = OpenSSL::X509::Certificate.new(File.read(opts[:client_cert_file])) if opts[:client_cert_file]
  #params[:ssl_client_key]  = OpenSSL::PKey::RSA.new(File.read(opts[:client_key_file]), opts[:client_key_pass]) if opts[:client_key_file]
  @@resource = RestClient::Resource.new(url, params)
end

.create_process_xml(params) ⇒ Object



247
248
249
250
251
252
253
# File 'lib/dor/services/workflow_service.rb', line 247

def create_process_xml(params)
  builder = Nokogiri::XML::Builder.new do |xml|
    attrs = params.reject { |k,v| v.nil? }
    xml.process(attrs)
  end
  return builder.to_xml
end

.create_workflow(repo, druid, workflow_name, wf_xml, opts = {:create_ds => true}) ⇒ Object

Creates a workflow for a given object in the repository. If this particular workflow for this objects exists, it will replace the old workflow with wf_xml passed to this method. You have the option of creating a datastream or not. Returns true on success. Caller must handle any exceptions

Parameters

Parameters:

  • repo (String)

    The repository the object resides in. The service recoginzes “dor” and “sdr” at the moment

  • druid (String)

    The id of the object

  • workflow_name (String)

    The name of the workflow you want to create

  • wf_xml (String)

    The xml that represents the workflow

  • opts (Hash) (defaults to: {:create_ds => true})

    optional params

Options Hash (opts):

  • :create_ds (Boolean)

    if true, a workflow datastream will be created in Fedora. Set to false if you do not want a datastream to be created If you do not pass in an opts Hash, then :create_ds is set to true by default

  • :priority (Integer)

    adds priority to all process elements in the wf_xml workflow xml



28
29
30
31
32
33
34
# File 'lib/dor/services/workflow_service.rb', line 28

def create_workflow(repo, druid, workflow_name, wf_xml, opts = {:create_ds => true})
  xml = wf_xml
  xml = add_priority_to_workflow_xml(opts[:priority], wf_xml) if(opts[:priority])
  workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow_name}"].put(xml, :content_type => 'application/xml',
                                                                               :params => {'create-ds' => opts[:create_ds] })
  return true
end

.delete_workflow(repo, druid, workflow) ⇒ Object

Deletes a workflow from a particular repository and druid

Parameters:

  • repo (String)

    The repository the object resides in. The service recoginzes “dor” and “sdr” at the moment

  • druid (String)

    The id of the object to delete the workflow from

  • workflow (String)

    The name of the workflow to be deleted



141
142
143
144
# File 'lib/dor/services/workflow_service.rb', line 141

def delete_workflow(repo, druid, workflow)
  workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}"].delete
  return true
end

.get_active_lifecycle(repo, druid, milestone) ⇒ Time

Returns the Date for a requested milestone ONLY FROM THE ACTIVE workflow table

Examples:

An example lifecycle xml from the workflow service.

<lifecycle objectId="druid:ct011cv6501">
  <milestone date="2010-04-27T11:34:17-0700">registered</milestone>
  <milestone date="2010-04-29T10:12:51-0700">inprocess</milestone>
  <milestone date="2010-06-15T16:08:58-0700">released</milestone>
</lifecycle>

Parameters:

  • repo (String)

    epository name

  • druid (String)

    object id

  • milestone (String)

    name of the milestone being queried for

Returns:

  • (Time)

    when the milestone was achieved. Returns nil if the milestone does not exist



178
179
180
181
182
183
184
185
186
# File 'lib/dor/services/workflow_service.rb', line 178

def get_active_lifecycle(repo, druid, milestone)
  doc = self.query_lifecycle(repo, druid, true)
  milestone = doc.at_xpath("//lifecycle/milestone[text() = '#{milestone}']")
  if(milestone)
    return Time.parse(milestone['date'])
  end

  nil
end

.get_active_workflows(repo, pid) ⇒ Array<String>

Get active 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

Examples:

Dor::WorkflowService.get_workflows(‘dor’, ‘druid:sr100hp0609’)

=> ["accessionWF", "assemblyWF", "disseminationWF"]

Parameters:

  • repo (String)

    repository of the object

  • pid (String)

    id of object

Returns:

  • (Array<String>)

    list of active worklows. Returns an empty Array if none are found



111
112
113
114
# File 'lib/dor/services/workflow_service.rb', line 111

def get_active_workflows(repo, pid)
  doc = Nokogiri::XML(get_workflow_xml(repo,pid,''))
  doc.xpath( %(//workflow[not(process/@archived)]/@id ) ).map {|n| n.value}
end

.get_errored_objects_for_workstep(workflow, step, repository = 'dor') ⇒ hash

Get a list of druids that have errored out in a particular workflow and step

e.g. Dor::WorkflowService.get_errored_objects_for_workstep(‘accessionWF’,‘content-metadata’)

> - Item error; caused by #<Rubydora::FedoraInvalidRequest: Error modifying datastream contentMetadata for druid:qd556jq0580. See logger for details>”

Parameters:

  • workflow (string)

    name

  • step (string)

    name

  • repository (string) (defaults to: 'dor')

    – optional, default=dor

Returns:

  • (hash)

    hash of results, with key has a druid, and value as the error message



237
238
239
240
241
242
243
244
245
# File 'lib/dor/services/workflow_service.rb', line 237

def get_errored_objects_for_workstep workflow, step, repository='dor'
  result = {}
  uri_string = "workflow_queue?repository=#{repository}&workflow=#{workflow}&error=#{step}"
  resp = workflow_resource[uri_string].get
  objs = Nokogiri::XML(resp).xpath('//object').collect do |node|
    result.merge!(node['id'] => node['errorMessage'])
  end
  result
end

.get_lifecycle(repo, druid, milestone) ⇒ Time

Returns the Date for a requested milestone from workflow lifecycle

Examples:

An example lifecycle xml from the workflow service.

<lifecycle objectId="druid:ct011cv6501">
  <milestone date="2010-04-27T11:34:17-0700">registered</milestone>
  <milestone date="2010-04-29T10:12:51-0700">inprocess</milestone>
  <milestone date="2010-06-15T16:08:58-0700">released</milestone>
</lifecycle>

Parameters:

  • repo (String)

    epository name

  • druid (String)

    object id

  • milestone (String)

    name of the milestone being queried for

Returns:

  • (Time)

    when the milestone was achieved. Returns nil if the milestone does not exist



157
158
159
160
161
162
163
164
165
# File 'lib/dor/services/workflow_service.rb', line 157

def get_lifecycle(repo, druid, milestone)
  doc = self.query_lifecycle(repo, druid)
  milestone = doc.at_xpath("//lifecycle/milestone[text() = '#{milestone}']")
  if(milestone)
    return Time.parse(milestone['date'])
  end

  nil
end

.get_milestones(repo, druid) ⇒ Object



188
189
190
191
192
193
# File 'lib/dor/services/workflow_service.rb', line 188

def get_milestones(repo, druid)
  doc = self.query_lifecycle(repo, druid)
  doc.xpath("//lifecycle/milestone").collect do |node|
    { :milestone => node.text, :at => Time.parse(node['date']), :version => node['version'] }
  end
end

.get_objects_for_workstep(completed, waiting, repository = nil, workflow = nil) ⇒ Object

Returns a list of druids from the WorkflowService that meet the criteria of the passed in completed and waiting params

Parameters:

  • completed (Array<String>, String)

    An array or single String of the completed steps, should use the qualified format: repository:workflow:step-name

  • waiting (String)

    name of the waiting step

  • repository (String) (defaults to: nil)

    default repository to use if it isn’t passed in the qualified-step-name

  • workflow (String) (defaults to: nil)

    default workflow to use if it isn’t passed in the qualified-step-name



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/dor/services/workflow_service.rb', line 210

def get_objects_for_workstep completed, waiting, repository=nil, workflow=nil
  result = nil
  if(completed)
    uri_string = "workflow_queue?waiting=#{qualify_step(repository,workflow,waiting)}"
    Array(completed).each do |step|
      uri_string << "&completed=#{qualify_step(repository,workflow,step)}"
    end
  else
    uri_string = "workflow_queue?waiting=#{qualify_step(repository,workflow,waiting)}"
  end
  workflow_resource.options[:timeout] = 5 * 60 unless(workflow_resource.options.include?(:timeout))
  resp = workflow_resource[uri_string].get
  result = Nokogiri::XML(resp).xpath('//object[@id]').collect { |node| node['id'] }

  result || []
end

.get_workflow_status(repo, druid, workflow, process) ⇒ Object

Retrieves the process status of the given workflow for the given object identifier

Raises:

  • (Exception)


75
76
77
78
79
80
81
82
83
84
85
# File 'lib/dor/services/workflow_service.rb', line 75

def get_workflow_status(repo, druid, workflow, process)
  workflow_md = workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}"].get
  doc = Nokogiri::XML(workflow_md)
  raise Exception.new("Unable to parse response:\n#{workflow_md}") if(doc.root.nil?)

  status = doc.root.at_xpath("//process[@name='#{process}']/@status")
  if status
    status=status.content
  end
  return status
end

.get_workflow_xml(repo, druid, workflow) ⇒ Object



87
88
89
# File 'lib/dor/services/workflow_service.rb', line 87

def get_workflow_xml(repo, druid, workflow)
  workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}"].get
end

.get_workflows(pid) ⇒ Array<String>

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

Examples:

Dor::WorkflowService.get_workflows(‘druid:sr100hp0609’)

=> ["accessionWF", "assemblyWF", "disseminationWF"]

Parameters:

  • pid (String)

    of druid

Returns:

  • (Array<String>)

    list of worklows



98
99
100
101
# File 'lib/dor/services/workflow_service.rb', line 98

def get_workflows(pid)
  xml_doc=Nokogiri::XML(get_workflow_xml('dor',pid,''))
  return xml_doc.xpath('//workflow').collect {|workflow| workflow['id']}
end

.qualify_step(default_repository, default_workflow, step) ⇒ Object



195
196
197
198
199
200
# File 'lib/dor/services/workflow_service.rb', line 195

def qualify_step(default_repository, default_workflow, step)
  current = step.split(/:/,3)
  current.unshift(default_workflow) if current.length < 3
  current.unshift(default_repository) if current.length < 3
  current.join(':')
end

.query_lifecycle(repo, druid, active_only = false) ⇒ Object



255
256
257
258
259
260
# File 'lib/dor/services/workflow_service.rb', line 255

def query_lifecycle(repo, druid, active_only = false)
  req = "#{repo}/objects/#{druid}/lifecycle"
  req << '?active-only=true' if active_only
  lifecycle_xml = workflow_resource[req].get
  return Nokogiri::XML(lifecycle_xml)
end

.update_workflow_error_status(repo, druid, workflow, process, error_msg, opts = {}) ⇒ Object

Updates the status of one step in a workflow to error. Returns true on success. Caller must handle any exceptions

Http Call

The method does an HTTP PUT to the URL defined in Dor::WF_URI. As an example:

PUT "/dor/objects/pid:123/workflows/GoogleScannedWF/convert"
<process name=\"convert\" status=\"error\" />"

Parameters:

  • repo (String)

    The repository the object resides in. The service recoginzes “dor” and “sdr” at the moment

  • druid (String)

    The id of the object

  • workflow (String)

    The name of the workflow

  • error_msg (String)

    The error message. Ideally, this is a brief message describing the error

  • opts (Hash) (defaults to: {})

    optional values for the workflow step

Options Hash (opts):

  • :error_txt (String)

    A slot to hold more information about the error, like a full stacktrace



130
131
132
133
134
135
# File 'lib/dor/services/workflow_service.rb', line 130

def update_workflow_error_status(repo, druid, workflow, process, error_msg, opts = {})
  opts = {:error_txt => nil}.merge!(opts)
  xml = create_process_xml({:name => process, :status => 'error', :errorMessage => error_msg}.merge!(opts))
  workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}/#{process}"].put(xml, :content_type => 'application/xml')
  return true
end

.update_workflow_status(repo, druid, workflow, process, status, opts = {}) ⇒ Object

Updates the status of one step in a workflow. Returns true on success. Caller must handle any exceptions

Http Call

The method does an HTTP PUT to the URL defined in Dor::WF_URI. As an example:

PUT "/dor/objects/pid:123/workflows/GoogleScannedWF/convert"
<process name=\"convert\" status=\"completed\" />"

Parameters:

  • repo (String)

    The repository the object resides in. The service recoginzes “dor” and “sdr” at the moment

  • druid (String)

    The id of the object

  • workflow (String)

    The name of the workflow

  • status (String)

    The status that you want to set. Typical statuses are ‘waiting’, ‘completed’, ‘error’, but could be any string

  • opts (Hash) (defaults to: {})

    optional values for the workflow step

Options Hash (opts):

  • :elapsed (Float)

    The number of seconds it took to complete this step. Can have a decimal. Is set to 0 if not passed in.

  • :lifecycle (String)

    Bookeeping label for this particular workflow step. Examples are: ‘registered’, ‘shelved’

  • :note (String)

    Any kind of string annotation that you want to attach to the workflow

  • :priority (Integer)

    Processing priority, 0-100, 100 being the highest priority. Workflow queues are returned in order of highest to lowest priority. Stored in the system as 0 by default



64
65
66
67
68
69
70
# File 'lib/dor/services/workflow_service.rb', line 64

def update_workflow_status(repo, druid, workflow, process, status, opts = {})
  opts = {:elapsed => 0, :lifecycle => nil, :note => nil}.merge!(opts)
  opts[:elapsed] = opts[:elapsed].to_s
  xml = create_process_xml({:name => process, :status => status}.merge!(opts))
  workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}/#{process}"].put(xml, :content_type => 'application/xml')
  return true
end

.workflow_resourceObject



291
292
293
294
# File 'lib/dor/services/workflow_service.rb', line 291

def workflow_resource
  raise "Please call Dor::WorkflowService.configure(url) once before calling any WorkflowService methods" if(@@resource.nil?)
  @@resource
end