dor-workflow-service 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MDZjYmEwY2M5NzNiNjE0MjFlNjk4NmFlOTM3NDZjMmZmOTNkN2M2ZQ==
4
+ Mzg4NWZmMjc0OWU3NGRjNWJlZTRlNTYyNzA4NTU4ZDJjNGQzMDE4NA==
5
5
  data.tar.gz: !binary |-
6
- MjcyNmQxNjU1ZjU2ZmQ3NDA4M2U0NjNkOTU4NjVmOTUzNDU5YjhmOA==
6
+ OWQ2ZWQ3NThiODc1MWU2ODY4ZWY0MDY5ZmI3MTI4YmU0YjE5Mjg3MA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- MjBhYmM4NzBjZDQwMmY5NzFhYWU2NzdjNzI0YmU5N2U3YWRlZGEwYzQyMzc2
10
- ZjE0Mjk1ZWU2NzZmNGQ0MmRhM2ExZGUxYzJjNzk1MGE0ZWNjY2Q3YjllYmI2
11
- ZTRmODI3Yjg4YjUzZjAxYjk3ODA0MWUwMjQyNDYwY2VlZmMyMTk=
9
+ Mzg0MzkzMmI3OWVmMzZhOGI4ZjFjN2QxMTE1ZmQxMDRlMGU5ZTVlODQwMjU0
10
+ NTYzYWY0YmU0MTc4ZDQzZmM0NDIyYTc3OWFmNjZmNTc2NjQ4NWJhMTEzMGU3
11
+ ZDZjOGUyYzZkY2U5MmFkNDg4NzYxMTg5M2MxOGY5OGY5NGE3YTE=
12
12
  data.tar.gz: !binary |-
13
- NzYzZjM1NzFlZmI1MjkyNWI5MzkzZmZmNGNhODZkMDY4NDM4YzFhYTUwMDUw
14
- MzJlMWFjODIwZDJjNmNjOGEyMjU0MTQzN2JjNGMyZTU4NDY2NGZjOGQ2YmU4
15
- OTVmMzViMWQ0MDk5MmZkM2ZjYTNhYTE1N2Q0YzZiNGY1MmJmMDQ=
13
+ ZGRjZTE2NzUxMDc3NTM4NzY1NjVhNDc5ZGIyY2M3Zjc1NDdjYTFkMjEzMmM0
14
+ OTA5YjlmNmJhNzI5MGYxYzA4YWNkNDNiMzBkZGQxZjkyYTQyZWRmOGQwMTI2
15
+ N2E4YmJlM2ExY2M0NGQxN2M3YzU1NmEwMDllYWVhNjU2OTUzNWU=
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --markup markdown
data/README.md CHANGED
@@ -1,11 +1,13 @@
1
+
1
2
  # dor-workflow-service gem
2
3
 
3
- Provides Ruby convenience methods to work with the DOR Workflow REST Service. The REST API is defined here:
4
+ Provides Ruby convenience methods to work with the DOR Workflow REST Service. The REST API is defined here:
4
5
  https://consul.stanford.edu/display/DOR/DOR+services#DORservices-initializeworkflow
5
6
 
6
7
  ## Usage
7
8
 
8
- To initialize usage of the service, you need to call Dor::WorkflowService.configure, like in a bootup or startup method, e.g.:
9
+ To initialize usage of the service, you need to call `Dor::WorkflowService.configure`, like in a bootup or startup method,
10
+ e.g.:
9
11
 
10
12
  Dor::WorkflowService.configure('https://test-server.edu/workflow/')
11
13
 
@@ -13,4 +15,5 @@ If you plan to archive workflows, then you need to set the URL to the Dor REST s
13
15
 
14
16
  Dor::WorkflowService.configure('https://test-server.edu/workflow/', :dor_services_url => 'https://sul-lyberservices-dev.stanford.edu/dor')
15
17
 
16
- There's no need to call Dor::WorkflowService.configure if using the dor-services gem and using the Dor::Config object. The latest versions of dor-services will configure the workflow service for you.
18
+ There's no need to call `Dor::WorkflowService.configure` if using the `dor-services` gem and using the `Dor::Config`
19
+ object. The latest versions of `dor-services` will configure the workflow service for you.
@@ -6,7 +6,7 @@ require 'dor/workflow_version'
6
6
  Gem::Specification.new do |gem|
7
7
  gem.name = "dor-workflow-service"
8
8
  gem.version = Dor::Workflow::Service::VERSION
9
- gem.authors = ["Willy Mene"]
9
+ gem.authors = ["Willy Mene", "Darren Hardy"]
10
10
  gem.email = ["wmene@stanford.edu"]
11
11
  gem.description = "Enables Ruby manipulation of the DOR Workflow Service via its REST API"
12
12
  gem.summary = "Provides convenience methods to work with the DOR Workflow Service"
@@ -17,14 +17,14 @@ Gem::Specification.new do |gem|
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
- gem.add_dependency "activesupport"
21
- gem.add_dependency "nokogiri"
22
- gem.add_dependency "rest-client"
23
- gem.add_dependency "confstruct"
20
+ gem.add_dependency "activesupport", '~> 4.0.4'
21
+ gem.add_dependency "nokogiri", '~> 1.6.0'
22
+ gem.add_dependency "rest-client", '~> 1.6.7'
23
+ gem.add_dependency "confstruct", '~> 0.2.7'
24
24
 
25
25
  gem.add_development_dependency "rake"
26
26
  gem.add_development_dependency "rspec"
27
27
  gem.add_development_dependency "yard"
28
28
  gem.add_development_dependency "redcarpet"
29
- gem.add_development_dependency "equivalent-xml"
29
+ gem.add_development_dependency "equivalent-xml", '~> 0.3.0'
30
30
  end
@@ -11,11 +11,13 @@ module Dor
11
11
  @@resource = nil
12
12
  @@dor_services_url = nil
13
13
 
14
+ # From Workflow Service's admin/Process.java
15
+ VALID_STATUS = %w{waiting completed error queued skipped hold}
16
+
14
17
  # Creates a workflow for a given object in the repository. If this particular workflow for this objects exists,
15
18
  # it will replace the old workflow with wf_xml passed to this method. You have the option of creating a datastream or not.
16
19
  # Returns true on success. Caller must handle any exceptions
17
20
  #
18
- # == Parameters
19
21
  # @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
20
22
  # @param [String] druid The id of the object
21
23
  # @param [String] workflow_name The name of the workflow you want to create
@@ -24,6 +26,7 @@ module Dor
24
26
  # @option opts [Boolean] :create_ds if true, a workflow datastream will be created in Fedora. Set to false if you do not want a datastream to be created
25
27
  # If you do not pass in an <b>opts</b> Hash, then :create_ds is set to true by default
26
28
  # @option opts [Integer] :priority adds priority to all process elements in the wf_xml workflow xml
29
+ # @return [Boolean] always true
27
30
  #
28
31
  def create_workflow(repo, druid, workflow_name, wf_xml, opts = {:create_ds => true})
29
32
  xml = wf_xml
@@ -51,29 +54,42 @@ module Dor
51
54
  # @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
52
55
  # @param [String] druid The id of the object
53
56
  # @param [String] workflow The name of the workflow
54
- # @param [String] status The status that you want to set. Typical statuses are 'waiting', 'completed', 'error', but could be any string
57
+ # @param [String] process The name of the process step
58
+ # @param [String] status The status that you want to set -- using one of the values in VALID_STATUS
55
59
  # @param [Hash] opts optional values for the workflow step
56
60
  # @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.
57
61
  # @option opts [String] :lifecycle Bookeeping label for this particular workflow step. Examples are: 'registered', 'shelved'
58
62
  # @option opts [String] :note Any kind of string annotation that you want to attach to the workflow
59
- # @option opts [Integer] :priority 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
60
- # == Http Call
61
- # The method does an HTTP PUT to the URL defined in Dor::WF_URI. As an example:
62
- # PUT "/dor/objects/pid:123/workflows/GoogleScannedWF/convert"
63
- # <process name=\"convert\" status=\"completed\" />"
63
+ # @option opts [Integer] :priority Processing priority. Recommended range is -100..100, 100 being the highest priority, and -100 being the lowest priority. Workflow queues are returned in order of highest to lowest priority value. Default is 0.
64
+ # @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
65
+ # @return [Boolean] always true
66
+ # Http Call
67
+ # ==
68
+ # The method does an HTTP PUT to the URL defined in `Dor::WF_URI`. As an example:
69
+ #
70
+ # PUT "/dor/objects/pid:123/workflows/GoogleScannedWF/convert"
71
+ # <process name=\"convert\" status=\"completed\" />"
64
72
  def update_workflow_status(repo, druid, workflow, process, status, opts = {})
73
+ raise ArgumentError, "Unknown status value #{status}" unless VALID_STATUS.include?(status.downcase)
65
74
  opts = {:elapsed => 0, :lifecycle => nil, :note => nil}.merge!(opts)
66
75
  opts[:elapsed] = opts[:elapsed].to_s
67
- xml = create_process_xml({:name => process, :status => status}.merge!(opts))
68
- workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}/#{process}"].put(xml, :content_type => 'application/xml')
76
+ current_status = opts.delete(:current_status)
77
+ xml = create_process_xml({:name => process, :status => status.downcase}.merge!(opts))
78
+ uri = "#{repo}/objects/#{druid}/workflows/#{workflow}/#{process}"
79
+ uri << "?current-status=#{current_status.downcase}" if current_status
80
+ workflow_resource[uri].put(xml, :content_type => 'application/xml')
69
81
  return true
70
82
  end
71
83
 
72
84
  #
73
85
  # Retrieves the process status of the given workflow for the given object identifier
74
- #
86
+ # @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
87
+ # @param [String] druid The id of the object
88
+ # @param [String] workflow The name of the workflow
89
+ # @param [String] process The name of the process step
90
+ # @return [String] status for repo-workflow-process-druid
75
91
  def get_workflow_status(repo, druid, workflow, process)
76
- workflow_md = workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}"].get
92
+ workflow_md = get_workflow_xml(repo, druid, workflow)
77
93
  doc = Nokogiri::XML(workflow_md)
78
94
  raise Exception.new("Unable to parse response:\n#{workflow_md}") if(doc.root.nil?)
79
95
 
@@ -84,6 +100,12 @@ module Dor
84
100
  return status
85
101
  end
86
102
 
103
+ #
104
+ # Retrieves the raw XML for the given workflow
105
+ # @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
106
+ # @param [String] druid The id of the object
107
+ # @param [String] workflow The name of the workflow
108
+ # @return [String] XML of the workflow
87
109
  def get_workflow_xml(repo, druid, workflow)
88
110
  workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}"].get
89
111
  end
@@ -93,7 +115,8 @@ module Dor
93
115
  #
94
116
  # @param [String] pid of druid
95
117
  # @return [Array<String>] list of worklows
96
- # @example Dor::WorkflowService.get_workflows('druid:sr100hp0609')
118
+ # @example
119
+ # Dor::WorkflowService.get_workflows('druid:sr100hp0609')
97
120
  # => ["accessionWF", "assemblyWF", "disseminationWF"]
98
121
  def get_workflows(pid)
99
122
  xml_doc=Nokogiri::XML(get_workflow_xml('dor',pid,''))
@@ -106,7 +129,8 @@ module Dor
106
129
  # @param [String] repo repository of the object
107
130
  # @param [String] pid id of object
108
131
  # @return [Array<String>] list of active worklows. Returns an empty Array if none are found
109
- # @example Dor::WorkflowService.get_workflows('dor', 'druid:sr100hp0609')
132
+ # @example
133
+ # Dor::WorkflowService.get_workflows('dor', 'druid:sr100hp0609')
110
134
  # => ["accessionWF", "assemblyWF", "disseminationWF"]
111
135
  def get_active_workflows(repo, pid)
112
136
  doc = Nokogiri::XML(get_workflow_xml(repo,pid,''))
@@ -122,11 +146,14 @@ module Dor
122
146
  # @param [String] error_msg The error message. Ideally, this is a brief message describing the error
123
147
  # @param [Hash] opts optional values for the workflow step
124
148
  # @option opts [String] :error_txt A slot to hold more information about the error, like a full stacktrace
149
+ # @return [Boolean] always true
125
150
  #
126
- # == Http Call
127
- # The method does an HTTP PUT to the URL defined in Dor::WF_URI. As an example:
128
- # PUT "/dor/objects/pid:123/workflows/GoogleScannedWF/convert"
129
- # <process name=\"convert\" status=\"error\" />"
151
+ # Http Call
152
+ # ==
153
+ # The method does an HTTP PUT to the URL defined in `Dor::WF_URI`.
154
+ #
155
+ # PUT "/dor/objects/pid:123/workflows/GoogleScannedWF/convert"
156
+ # <process name=\"convert\" status=\"error\" />"
130
157
  def update_workflow_error_status(repo, druid, workflow, process, error_msg, opts = {})
131
158
  opts = {:error_txt => nil}.merge!(opts)
132
159
  xml = create_process_xml({:name => process, :status => 'error', :errorMessage => error_msg}.merge!(opts))
@@ -138,13 +165,14 @@ module Dor
138
165
  # @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
139
166
  # @param [String] druid The id of the object to delete the workflow from
140
167
  # @param [String] workflow The name of the workflow to be deleted
168
+ # @return [Boolean] always true
141
169
  def delete_workflow(repo, druid, workflow)
142
170
  workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}"].delete
143
171
  return true
144
172
  end
145
173
 
146
174
  # Returns the Date for a requested milestone from workflow lifecycle
147
- # @param [String] repo epository name
175
+ # @param [String] repo repository name
148
176
  # @param [String] druid object id
149
177
  # @param [String] milestone name of the milestone being queried for
150
178
  # @return [Time] when the milestone was achieved. Returns nil if the milestone does not exist
@@ -185,6 +213,7 @@ module Dor
185
213
  nil
186
214
  end
187
215
 
216
+ # @return [Hash]
188
217
  def get_milestones(repo, druid)
189
218
  doc = self.query_lifecycle(repo, druid)
190
219
  doc.xpath("//lifecycle/milestone").collect do |node|
@@ -192,6 +221,16 @@ module Dor
192
221
  end
193
222
  end
194
223
 
224
+ # Converts repo-workflow-step into repo:workflow:step
225
+ # @param [String] default_repository
226
+ # @param [String] default_workflow
227
+ # @param [String] step if contains colon :, then uses
228
+ # the value for workflow and/or workflow/repository.
229
+ # for example, jp2-create, or assemblyWF:jp2-create,
230
+ # or dor:assemblyWF:jp2-create
231
+ # @return [String] repo:workflow:step
232
+ # @example
233
+ # dor:assemblyWF:jp2-create
195
234
  def qualify_step(default_repository, default_workflow, step)
196
235
  current = step.split(/:/,3)
197
236
  current.unshift(default_workflow) if current.length < 3
@@ -199,41 +238,77 @@ module Dor
199
238
  current.join(':')
200
239
  end
201
240
 
202
-
203
241
  # Returns a list of druids from the WorkflowService that meet the criteria of the passed in completed and waiting params
204
242
  #
205
- # @param [Array<String>, String] completed An array or single String of the completed steps, should use the qualified format:
206
- # repository:workflow:step-name
243
+ # @param [Array<String>, String] completed An array or single String of the completed steps, should use the qualified format: `repository:workflow:step-name`
207
244
  # @param [String] waiting name of the waiting step
208
245
  # @param [String] repository default repository to use if it isn't passed in the qualified-step-name
209
246
  # @param [String] workflow default workflow to use if it isn't passed in the qualified-step-name
210
- def get_objects_for_workstep completed, waiting, repository=nil, workflow=nil
247
+ # @param [Hash] options
248
+ # @option options [Boolean] :with_priority include the priority with each druid
249
+ # @option options [Integer] :limit maximum number of druids to return (nil for no limit)
250
+ # @return [Array<String>, Hash] if with_priority, hash with druids as keys with their Integer priority as value; else Array of druids
251
+ #
252
+ # @example
253
+ # get_objects_for_workstep(...)
254
+ # => [
255
+ # "druid:py156ps0477",
256
+ # "druid:tt628cb6479",
257
+ # "druid:ct021wp7863"
258
+ # ]
259
+ #
260
+ # @example
261
+ # get_objects_for_workstep(..., with_priority: true)
262
+ # => {
263
+ # "druid:py156ps0477" => 100,
264
+ # "druid:tt628cb6479" => 0,
265
+ # "druid:ct021wp7863" => -100
266
+ # }
267
+ #
268
+ def get_objects_for_workstep completed, waiting, repository=nil, workflow=nil, options = {}
211
269
  result = nil
270
+ uri_string = "workflow_queue?waiting=#{qualify_step(repository,workflow,waiting)}"
212
271
  if(completed)
213
- uri_string = "workflow_queue?waiting=#{qualify_step(repository,workflow,waiting)}"
214
272
  Array(completed).each do |step|
215
273
  uri_string << "&completed=#{qualify_step(repository,workflow,step)}"
216
274
  end
217
- else
218
- uri_string = "workflow_queue?waiting=#{qualify_step(repository,workflow,waiting)}"
219
275
  end
276
+
277
+ if options[:limit] and options[:limit].to_i > 0
278
+ uri_string << "&limit=#{options[:limit].to_i}"
279
+ end
280
+
220
281
  workflow_resource.options[:timeout] = 5 * 60 unless(workflow_resource.options.include?(:timeout))
221
282
  resp = workflow_resource[uri_string].get
222
- result = Nokogiri::XML(resp).xpath('//object[@id]').collect { |node| node['id'] }
283
+ #
284
+ # response looks like:
285
+ # <objects count="2">
286
+ # <object id="druid:ab123de4567" priority="2"/>
287
+ # <object id="druid:ab123de9012" priority="1"/> #
288
+ # </objects>
289
+ #
290
+ # convert into:
291
+ # { 'druid:ab123de4567' => 2, 'druid:ab123de9012' => 1}
292
+ #
293
+ result = Nokogiri::XML(resp).xpath('//object[@id]').inject({}) do |h, node|
294
+ h[node['id']] = node['priority'] ? node['priority'].to_i : 0
295
+ h
296
+ end
223
297
 
224
- result || []
298
+ options[:with_priority] ? result : result.keys
225
299
  end
226
300
 
227
301
  # Get a list of druids that have errored out in a particular workflow and step
228
302
  #
229
- # @param [string] workflow name
230
- # @param [string] step name
231
- # @param [string] repository -- optional, default=dor
303
+ # @param [String] workflow name
304
+ # @param [String] step name
305
+ # @param [String] repository -- optional, default=dor
232
306
  #
233
- # @return [hash] hash of results, with key has a druid, and value as the error message
234
- # e.g.
235
- # Dor::WorkflowService.get_errored_objects_for_workstep('accessionWF','content-metadata')
236
- # => {"druid:qd556jq0580"=>"druid:qd556jq0580 - Item error; caused by #<Rubydora::FedoraInvalidRequest: Error modifying datastream contentMetadata for druid:qd556jq0580. See logger for details>"}
307
+ # @return [Hash] hash of results, with key has a druid, and value as the error message
308
+ # @example
309
+ # Dor::WorkflowService.get_errored_objects_for_workstep('accessionWF','content-metadata')
310
+ # => {"druid:qd556jq0580"=>"druid:qd556jq0580 - Item error; caused by
311
+ # #<Rubydora::FedoraInvalidRequest: Error modifying datastream contentMetadata for druid:qd556jq0580. See logger for details>"}
237
312
  def get_errored_objects_for_workstep workflow, step, repository='dor'
238
313
  result = {}
239
314
  uri_string = "workflow_queue?repository=#{repository}&workflow=#{workflow}&error=#{step}"
@@ -244,6 +319,34 @@ module Dor
244
319
  result
245
320
  end
246
321
 
322
+ # Gets all of the workflow steps that have a status of 'queued' that have a last-updated timestamp older than the number of hours passed in
323
+ # This will enable re-queueing of jobs that have been lost by the job manager
324
+ # @param [String] repository name of the repository you want to query, like 'dor' or 'sdr'
325
+ # @param [Hash] opts optional values for query
326
+ # @option opts [Integer] :hours_ago steps older than this value will be returned by the query. If not passed in, the service defaults to 24 hours
327
+ # @option opts [Integer] :limit sets the maximum number of workflow steps that can be returned. Defaults to no limit
328
+ # @return [Array[Hash]] each Hash represents a workflow step. It will have the following keys:
329
+ # :workflow, :step, :druid, :priority
330
+ def get_stale_queued_workflows(repository, opts = {})
331
+ uri_string = build_queued_uri(repository, opts)
332
+ xml = workflow_resource[uri_string].get
333
+ parse_queued_workflows_response xml
334
+ end
335
+
336
+ # Returns a count of workflow steps that have a status of 'queued' that have a last-updated timestamp older than the number of hours passed in
337
+ # @param [String] repository name of the repository you want to query, like 'dor' or 'sdr'
338
+ # @param [Hash] opts optional values for query
339
+ # @option opts [Integer] :hours_ago steps older than this value will be returned by the query. If not passed in, the service defaults to 24 hours
340
+ # @return [Integer] number of stale, queued steps if the :count_only option was set to true
341
+ def count_stale_queued_workflows(repository, opts = {})
342
+ uri_string = build_queued_uri(repository, opts)
343
+ uri_string << "&count-only=true"
344
+ xml = workflow_resource[uri_string].get
345
+ doc = Nokogiri::XML(xml)
346
+ return doc.at_xpath('/objects/@count').value.to_i
347
+ end
348
+
349
+ # @return [String]
247
350
  def create_process_xml(params)
248
351
  builder = Nokogiri::XML::Builder.new do |xml|
249
352
  attrs = params.reject { |k,v| v.nil? }
@@ -252,6 +355,7 @@ module Dor
252
355
  return builder.to_xml
253
356
  end
254
357
 
358
+ # @return [Nokogiri::XML::Document]
255
359
  def query_lifecycle(repo, druid, active_only = false)
256
360
  req = "#{repo}/objects/#{druid}/lifecycle"
257
361
  req << '?active-only=true' if active_only
@@ -276,8 +380,10 @@ module Dor
276
380
  end
277
381
 
278
382
  # Calls the versionClose endpoint of the WorkflowService:
279
- # - completes the versioningWF:submit-version and versioningWF:start-accession steps
280
- # - initiates accesssionWF
383
+ #
384
+ # - completes the versioningWF:submit-version and versioningWF:start-accession steps
385
+ # - initiates accesssionWF
386
+ #
281
387
  # @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
282
388
  # @param [String] druid The id of the object to delete the workflow from
283
389
  # @param [Boolean] create_accession_wf Option to create accessionWF when closing a version. Defaults to true
@@ -288,17 +394,22 @@ module Dor
288
394
  return true
289
395
  end
290
396
 
397
+ # @return [RestClient::Resource] the REST client resource
291
398
  def workflow_resource
292
399
  raise "Please call Dor::WorkflowService.configure(url) once before calling any WorkflowService methods" if(@@resource.nil?)
293
400
  @@resource
294
401
  end
295
402
 
403
+ # Configure the workflow service
404
+ #
296
405
  # @param [String] url points to the workflow service
297
406
  # @param [Hash] opts optional params
298
- # @option opts [String] :client_cert_file path to an SSL client certificate
299
- # @option opts [String] :client_key_file path to an SSL key file
300
- # @option opts [String] :client_key_pass password for the key file
301
407
  # @option opts [String] :dor_services_uri uri to the DOR REST service
408
+ # @option opts [Integer] :timeout number of seconds for RestClient timeout
409
+ # @option opts [String] :client_cert_file path to an SSL client certificate (deprecated)
410
+ # @option opts [String] :client_key_file path to an SSL key file (deprecated)
411
+ # @option opts [String] :client_key_pass password for the key file (deprecated)
412
+ # @return [RestClient::Resource] the REST client resource
302
413
  def configure(url, opts={})
303
414
  params = {}
304
415
  params[:timeout] = opts[:timeout] if opts[:timeout]
@@ -308,6 +419,29 @@ module Dor
308
419
  @@resource = RestClient::Resource.new(url, params)
309
420
  end
310
421
 
422
+ protected
423
+
424
+ def build_queued_uri(repository, opts = {})
425
+ uri_string = "workflow_queue/all_queued?repository=#{repository}"
426
+ uri_string << "&hours-ago=#{opts[:hours_ago]}" if opts[:hours_ago]
427
+ uri_string << "&limit=#{opts[:limit]}" if opts[:limit]
428
+ uri_string
429
+ end
430
+
431
+ def parse_queued_workflows_response(xml)
432
+ res = []
433
+ doc = Nokogiri::XML(xml)
434
+ doc.xpath('/workflows/workflow').each do |wf_node|
435
+ wf = {}
436
+ wf[:workflow] = wf_node['name']
437
+ wf[:step] = wf_node['process']
438
+ wf[:druid] = wf_node['druid']
439
+ wf[:priority] = wf_node['priority'].to_i
440
+ res << wf
441
+ end
442
+ res
443
+ end
444
+
311
445
  end
312
446
  end
313
447
  end
@@ -1,7 +1,7 @@
1
1
  module Dor
2
2
  module Workflow
3
3
  module Service
4
- VERSION = "1.5.1"
4
+ VERSION = "1.6.0"
5
5
  end
6
6
  end
7
7
  end
@@ -85,6 +85,12 @@ describe Dor::WorkflowService do
85
85
  @mock_resource.should_receive(:put).with(@xml_re, { :content_type => 'application/xml' }).and_raise(ex)
86
86
  lambda{ Dor::WorkflowService.update_workflow_status(@repo, @druid, "etdSubmitWF", "reader-approval", "completed") }.should raise_error(Exception, "exception thrown")
87
87
  end
88
+
89
+ it "performs a conditional update when current-status is passed as a parameter" do
90
+ @mock_resource.should_receive(:[]).with("dor/objects/druid:123/workflows/etdSubmitWF/reader-approval?current-status=queued")
91
+ @mock_resource.should_receive(:put).with(@xml_re, { :content_type => 'application/xml' }).and_return('')
92
+ Dor::WorkflowService.update_workflow_status(@repo, @druid, "etdSubmitWF", "reader-approval", "completed", :version => 2, :note => 'annotation', :priority => 34, :current_status => 'queued').should be_true
93
+ end
88
94
  end
89
95
 
90
96
  describe "#update_workflow_error_status" do
@@ -188,10 +194,24 @@ describe Dor::WorkflowService do
188
194
  qualified_completed2 = "#{repo2}:#{workflow2}:#{completed2}"
189
195
  qualified_completed3 = "#{repo2}:#{workflow2}:#{completed3}"
190
196
  @mock_resource.should_receive(:[]).with("workflow_queue?waiting=#{qualified_waiting}&completed=#{qualified_completed}&completed=#{qualified_completed2}&completed=#{qualified_completed3}")
191
- @mock_resource.should_receive(:get).and_return(%{<objects count="2"><object id="druid:ab123de4567" priority="0"/><object id="druid:ab123de9012" priority="0"/></objects>})
197
+ @mock_resource.should_receive(:get).and_return(%{<objects count="2"><object id="druid:ab123de4567" priority="1"/><object id="druid:ab123de9012" priority="0"/></objects>})
192
198
  Dor::WorkflowService.get_objects_for_workstep([qualified_completed, qualified_completed2, qualified_completed3], qualified_waiting).should == ['druid:ab123de4567', 'druid:ab123de9012']
193
199
  end
194
200
 
201
+ it "same but with priority" do
202
+ qualified_waiting = "#{@repository}:#{@workflow}:#{@waiting}"
203
+ qualified_completed = "#{@repository}:#{@workflow}:#{@completed}"
204
+ repo2 = "sdr"
205
+ workflow2 = "sdrIngestWF"
206
+ completed2="complete-deposit"
207
+ completed3="ingest-transfer"
208
+ qualified_completed2 = "#{repo2}:#{workflow2}:#{completed2}"
209
+ qualified_completed3 = "#{repo2}:#{workflow2}:#{completed3}"
210
+ @mock_resource.should_receive(:[]).with("workflow_queue?waiting=#{qualified_waiting}&completed=#{qualified_completed}&completed=#{qualified_completed2}&completed=#{qualified_completed3}")
211
+ @mock_resource.should_receive(:get).and_return(%{<objects count="2"><object id="druid:ab123de4567" priority="2"/><object id="druid:ab123de9012" priority="1"/></objects>})
212
+ Dor::WorkflowService.get_objects_for_workstep([qualified_completed, qualified_completed2, qualified_completed3], qualified_waiting, nil, nil, with_priority: true).should == { 'druid:ab123de4567' => 2, 'druid:ab123de9012' => 1}
213
+ end
214
+
195
215
  it "creates the URI string with only one completed step passed in as a String" do
196
216
  qualified_waiting = "#{@repository}:#{@workflow}:#{@waiting}"
197
217
  qualified_completed = "#{@repository}:#{@workflow}:#{@completed}"
@@ -209,6 +229,14 @@ describe Dor::WorkflowService do
209
229
  @mock_resource.should_receive(:get).and_return(%{<objects count="1"><object id="druid:ab123de4567"/></objects>})
210
230
  Dor::WorkflowService.get_objects_for_workstep(nil, qualified_waiting).should == ['druid:ab123de4567']
211
231
  end
232
+
233
+ it "same but with priority" do
234
+ qualified_waiting = "#{@repository}:#{@workflow}:#{@waiting}"
235
+
236
+ @mock_resource.should_receive(:[]).with("workflow_queue?waiting=#{qualified_waiting}")
237
+ @mock_resource.should_receive(:get).and_return(%{<objects count="1"><object id="druid:ab123de4567" priority="33"/></objects>})
238
+ Dor::WorkflowService.get_objects_for_workstep(nil, qualified_waiting, nil, nil, with_priority: true).should == { 'druid:ab123de4567' => 33 }
239
+ end
212
240
  end
213
241
  end
214
242
 
@@ -275,4 +303,29 @@ describe Dor::WorkflowService do
275
303
  end
276
304
  end
277
305
 
306
+ describe ".parse_queued_workflows_response" do
307
+ it "returns an Array of Hashes containing each workflow step" do
308
+ xml = <<-XML
309
+ <workflows>
310
+ <workflow priority="30" note="annotation" lifecycle="in-process" errorText="stacktrace" errorMessage="NullPointerException" elapsed="1.173" repository="dor" attempts="0" datetime="2008-11-15T13:30:00-0800" status="waiting" process="content-metadata" name="accessionWF" druid="dr:123"/>
311
+ <workflow priority="30" note="annotation" lifecycle="in-process" errorText="stacktrace" errorMessage="NullPointerException" elapsed="1.173" repository="dor" attempts="0" datetime="2008-11-15T13:30:00-0800" status="waiting" process="jp2-create" name="assemblyWF" druid="dr:456"/>
312
+ </workflows>
313
+ XML
314
+ expected = [ { :workflow => 'accessionWF', :step => 'content-metadata', :druid => 'dr:123', :priority => 30},
315
+ { :workflow => 'assemblyWF', :step => 'jp2-create', :druid => 'dr:456', :priority => 30} ]
316
+
317
+ ah = Dor::WorkflowService.parse_queued_workflows_response(xml)
318
+ ah.should eql(expected)
319
+ end
320
+ end
321
+
322
+ describe ".count_stale_queued_workflows" do
323
+ it "returns the number of queued workflow steps" do
324
+ @mock_resource.should_receive(:[]).with("workflow_queue/all_queued?repository=dor&hours-ago=48&count-only=true")
325
+ @mock_resource.should_receive(:get).and_return(%{<objects count="10"/>})
326
+
327
+ Dor::WorkflowService.count_stale_queued_workflows('dor', :hours_ago => 48).should == 10
328
+ end
329
+ end
330
+
278
331
  end
metadata CHANGED
@@ -1,71 +1,72 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dor-workflow-service
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Willy Mene
8
+ - Darren Hardy
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2014-03-13 00:00:00.000000000 Z
12
+ date: 2014-04-02 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: activesupport
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - ! '>='
18
+ - - ~>
18
19
  - !ruby/object:Gem::Version
19
- version: '0'
20
+ version: 4.0.4
20
21
  type: :runtime
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
- - - ! '>='
25
+ - - ~>
25
26
  - !ruby/object:Gem::Version
26
- version: '0'
27
+ version: 4.0.4
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: nokogiri
29
30
  requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
- - - ! '>='
32
+ - - ~>
32
33
  - !ruby/object:Gem::Version
33
- version: '0'
34
+ version: 1.6.0
34
35
  type: :runtime
35
36
  prerelease: false
36
37
  version_requirements: !ruby/object:Gem::Requirement
37
38
  requirements:
38
- - - ! '>='
39
+ - - ~>
39
40
  - !ruby/object:Gem::Version
40
- version: '0'
41
+ version: 1.6.0
41
42
  - !ruby/object:Gem::Dependency
42
43
  name: rest-client
43
44
  requirement: !ruby/object:Gem::Requirement
44
45
  requirements:
45
- - - ! '>='
46
+ - - ~>
46
47
  - !ruby/object:Gem::Version
47
- version: '0'
48
+ version: 1.6.7
48
49
  type: :runtime
49
50
  prerelease: false
50
51
  version_requirements: !ruby/object:Gem::Requirement
51
52
  requirements:
52
- - - ! '>='
53
+ - - ~>
53
54
  - !ruby/object:Gem::Version
54
- version: '0'
55
+ version: 1.6.7
55
56
  - !ruby/object:Gem::Dependency
56
57
  name: confstruct
57
58
  requirement: !ruby/object:Gem::Requirement
58
59
  requirements:
59
- - - ! '>='
60
+ - - ~>
60
61
  - !ruby/object:Gem::Version
61
- version: '0'
62
+ version: 0.2.7
62
63
  type: :runtime
63
64
  prerelease: false
64
65
  version_requirements: !ruby/object:Gem::Requirement
65
66
  requirements:
66
- - - ! '>='
67
+ - - ~>
67
68
  - !ruby/object:Gem::Version
68
- version: '0'
69
+ version: 0.2.7
69
70
  - !ruby/object:Gem::Dependency
70
71
  name: rake
71
72
  requirement: !ruby/object:Gem::Requirement
@@ -126,16 +127,16 @@ dependencies:
126
127
  name: equivalent-xml
127
128
  requirement: !ruby/object:Gem::Requirement
128
129
  requirements:
129
- - - ! '>='
130
+ - - ~>
130
131
  - !ruby/object:Gem::Version
131
- version: '0'
132
+ version: 0.3.0
132
133
  type: :development
133
134
  prerelease: false
134
135
  version_requirements: !ruby/object:Gem::Requirement
135
136
  requirements:
136
- - - ! '>='
137
+ - - ~>
137
138
  - !ruby/object:Gem::Version
138
- version: '0'
139
+ version: 0.3.0
139
140
  description: Enables Ruby manipulation of the DOR Workflow Service via its REST API
140
141
  email:
141
142
  - wmene@stanford.edu
@@ -146,6 +147,7 @@ extra_rdoc_files: []
146
147
  files:
147
148
  - .gitignore
148
149
  - .travis.yml
150
+ - .yardopts
149
151
  - Gemfile
150
152
  - LICENSE.txt
151
153
  - README.md
@@ -183,3 +185,4 @@ summary: Provides convenience methods to work with the DOR Workflow Service
183
185
  test_files:
184
186
  - spec/spec_helper.rb
185
187
  - spec/workflow_service_spec.rb
188
+ has_rdoc: