dor-workflow-service 1.5.1 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +8 -8
- data/.yardopts +1 -0
- data/README.md +6 -3
- data/dor-workflow-service.gemspec +6 -6
- data/lib/dor/services/workflow_service.rb +173 -39
- data/lib/dor/workflow_version.rb +1 -1
- data/spec/workflow_service_spec.rb +54 -1
- metadata +25 -22
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
Mzg4NWZmMjc0OWU3NGRjNWJlZTRlNTYyNzA4NTU4ZDJjNGQzMDE4NA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OWQ2ZWQ3NThiODc1MWU2ODY4ZWY0MDY5ZmI3MTI4YmU0YjE5Mjg3MA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
Mzg0MzkzMmI3OWVmMzZhOGI4ZjFjN2QxMTE1ZmQxMDRlMGU5ZTVlODQwMjU0
|
10
|
+
NTYzYWY0YmU0MTc4ZDQzZmM0NDIyYTc3OWFmNjZmNTc2NjQ4NWJhMTEzMGU3
|
11
|
+
ZDZjOGUyYzZkY2U5MmFkNDg4NzYxMTg5M2MxOGY5OGY5NGE3YTE=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
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.
|
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
|
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
|
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]
|
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
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
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
|
-
|
68
|
-
|
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 =
|
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
|
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
|
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
|
-
#
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
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
|
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
|
-
|
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
|
-
|
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 [
|
230
|
-
# @param [
|
231
|
-
# @param [
|
303
|
+
# @param [String] workflow name
|
304
|
+
# @param [String] step name
|
305
|
+
# @param [String] repository -- optional, default=dor
|
232
306
|
#
|
233
|
-
# @return [
|
234
|
-
#
|
235
|
-
#
|
236
|
-
#
|
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
|
-
#
|
280
|
-
#
|
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
|
data/lib/dor/workflow_version.rb
CHANGED
@@ -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="
|
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.
|
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-
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|