dor-workflow-service 1.7.6 → 1.7.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: cfa9ff38ab7893369b2e7c762f0a3d76725c8c9e
4
- data.tar.gz: c0196de5226f2771b595efd265d210f5bdfc8949
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NGMyMjcyOTg4YjEzYzAwYjFiMTI1NjMzYzNmYjk1YjlhZDVmNTZhYw==
5
+ data.tar.gz: !binary |-
6
+ YzA4M2RjMDc0MWFmOGU2NDBkZjUxZDU3M2M4MTgzOWQ5YmMyY2IxMw==
5
7
  SHA512:
6
- metadata.gz: 1713dda989aed538147756b8a0b51df5a54adee310302b76e609037e4dc8629aac1602a423dc951a92014eda8cedf59ede51f34916cddaf07c86d35e862ac8b1
7
- data.tar.gz: b7d671e2973e6ce7267180caf74a436266f8fb7c9237c7d2191f31e2cf529b8c5a1e0b852ca41646150457d13a90a7e7bb0e6aad4204df2b00833830a6a88008
8
+ metadata.gz: !binary |-
9
+ N2Y3Y2JlN2Q0MzYwZWQ5MWE4NDZiNzM1MWY2NzJkYTM3OWMzNzUwMzRjZGFj
10
+ Y2FmNjA3ZjY3YjFjODliMDhkMTYzNzIzMmUzZjhlMzkzYzcxMWU3MDEzZDVk
11
+ Y2Q0OGM1OWNmYzIwNmJlODk0OWQ4ZWE2YmY5M2Q5OTNlY2IxMzU=
12
+ data.tar.gz: !binary |-
13
+ YTUzYTVkOTQ4Y2NmMTc0MjRjMzM2NzdkOWRkMDM2ZjY2OTkzNTcyYWZmY2I3
14
+ YTE1NzhjOWUyNzY2ODc4ZmFhNzlmZTZiYjVmN2VmYjgxM2YxOTVjNzVhM2Y2
15
+ ZmRhYTIzMTgzYWE1MGIzMjlkZTc2YWUzNWYwYTkyZGJkYTg4NzQ=
data/.rubocop_todo.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  # This configuration was generated by `rubocop --auto-gen-config`
2
- # on 2015-06-26 11:52:46 -0700 using RuboCop version 0.31.0.
2
+ # on 2015-08-28 15:56:06 -0700 using RuboCop version 0.32.1.
3
3
  # The point is for the user to remove these configuration records
4
4
  # one by one as the offenses are removed from the code base.
5
5
  # Note that changes in the inspected code, or installation of new
@@ -60,12 +60,6 @@ Style/ClassVars:
60
60
  Style/Documentation:
61
61
  Enabled: false
62
62
 
63
- # Offense count: 7
64
- # Cop supports --auto-correct.
65
- # Configuration parameters: EnforcedStyle, SupportedStyles.
66
- Style/EmptyLinesAroundBlockBody:
67
- Enabled: false
68
-
69
63
  # Offense count: 2
70
64
  # Cop supports --auto-correct.
71
65
  # Configuration parameters: EnforcedStyle, SupportedStyles.
@@ -94,20 +88,15 @@ Style/HashSyntax:
94
88
  Style/LeadingCommentSpace:
95
89
  Enabled: false
96
90
 
97
- # Offense count: 1
98
- # Cop supports --auto-correct.
99
- Style/MethodCallParentheses:
100
- Enabled: false
101
-
102
91
  # Offense count: 2
103
92
  # Cop supports --auto-correct.
104
- # Configuration parameters: EnforcedStyle, SupportedStyles.
105
- Style/MethodDefParentheses:
93
+ Style/MultilineBlockLayout:
106
94
  Enabled: false
107
95
 
108
- # Offense count: 2
96
+ # Offense count: 6
109
97
  # Cop supports --auto-correct.
110
- Style/MultilineBlockLayout:
98
+ # Configuration parameters: AllowSafeAssignment.
99
+ Style/ParenthesesAroundCondition:
111
100
  Enabled: false
112
101
 
113
102
  # Offense count: 10
@@ -121,39 +110,12 @@ Style/PercentLiteralDelimiters:
121
110
  Style/RaiseArgs:
122
111
  Enabled: false
123
112
 
124
- # Offense count: 10
125
- # Cop supports --auto-correct.
126
- # Configuration parameters: AllowMultipleReturnValues.
127
- Style/RedundantReturn:
128
- Enabled: false
129
-
130
- # Offense count: 3
131
- # Cop supports --auto-correct.
132
- Style/RedundantSelf:
133
- Enabled: false
134
-
135
113
  # Offense count: 5
136
114
  # Cop supports --auto-correct.
137
115
  # Configuration parameters: EnforcedStyle, SupportedStyles.
138
116
  Style/SignalException:
139
117
  Enabled: false
140
118
 
141
- # Offense count: 14
142
- # Cop supports --auto-correct.
143
- Style/SpaceAfterComma:
144
- Enabled: false
145
-
146
- # Offense count: 6
147
- # Cop supports --auto-correct.
148
- Style/SpaceAfterControlKeyword:
149
- Enabled: false
150
-
151
- # Offense count: 7
152
- # Cop supports --auto-correct.
153
- # Configuration parameters: EnforcedStyle, SupportedStyles.
154
- Style/SpaceAroundEqualsInParameterDefault:
155
- Enabled: false
156
-
157
119
  # Offense count: 11
158
120
  # Cop supports --auto-correct.
159
121
  # Configuration parameters: MultiSpaceAllowedForOperators.
@@ -188,17 +150,6 @@ Style/SpaceInsideHashLiteralBraces:
188
150
  Style/SpaceInsideParens:
189
151
  Enabled: false
190
152
 
191
- # Offense count: 1
192
- # Cop supports --auto-correct.
193
- Style/SpecialGlobalVars:
194
- Enabled: false
195
-
196
- # Offense count: 128
197
- # Cop supports --auto-correct.
198
- # Configuration parameters: EnforcedStyle, SupportedStyles.
199
- Style/StringLiterals:
200
- Enabled: false
201
-
202
153
  # Offense count: 1
203
154
  # Cop supports --auto-correct.
204
155
  # Configuration parameters: IgnoredMethods.
data/.travis.yml CHANGED
@@ -1,3 +1,5 @@
1
+ language: ruby
2
+ sudo: false
1
3
  notifications:
2
4
  email: false
3
5
 
data/Gemfile CHANGED
@@ -4,5 +4,5 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  group :development do
7
- gem "debugger", :platform => :ruby_19
7
+ gem 'debugger', :platform => :ruby_19
8
8
  end
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
- require "bundler/setup"
1
+ require 'bundler/setup'
2
2
  require 'bundler/gem_tasks'
3
3
 
4
4
  require 'rspec/core/rake_task'
@@ -4,27 +4,28 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'dor/workflow_version'
5
5
 
6
6
  Gem::Specification.new do |gem|
7
- gem.name = "dor-workflow-service"
7
+ gem.name = 'dor-workflow-service'
8
8
  gem.version = Dor::Workflow::Service::VERSION
9
- gem.authors = ["Willy Mene", "Darren Hardy"]
10
- gem.email = ["wmene@stanford.edu"]
11
- gem.description = "Enables Ruby manipulation of the DOR Workflow Service via its REST API"
12
- gem.summary = "Provides convenience methods to work with the DOR Workflow Service"
13
- gem.homepage = "https://consul.stanford.edu/display/DOR/DOR+services#DORservices-initializeworkflow"
9
+ gem.authors = ['Willy Mene', 'Darren Hardy']
10
+ gem.email = ['wmene@stanford.edu']
11
+ gem.description = 'Enables Ruby manipulation of the DOR Workflow Service via its REST API'
12
+ gem.summary = 'Provides convenience methods to work with the DOR Workflow Service'
13
+ gem.homepage = 'https://consul.stanford.edu/display/DOR/DOR+services#DORservices-initializeworkflow'
14
14
 
15
- gem.files = `git ls-files`.split($/)
15
+ gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
17
  gem.test_files = gem.files.grep(%r{^(spec)/})
18
- gem.require_paths = ["lib"]
18
+ gem.require_paths = ['lib']
19
19
 
20
- gem.add_dependency "activesupport", '>= 3.2.1', '< 5'
21
- gem.add_dependency "nokogiri", '~> 1.6.0'
22
- gem.add_dependency "rest-client", '~> 1.7'
23
- gem.add_dependency "confstruct", '>= 0.2.7', '< 2'
20
+ gem.add_dependency 'activesupport', '>= 3.2.1', '< 5'
21
+ gem.add_dependency 'nokogiri', '~> 1.6.0'
22
+ gem.add_dependency 'rest-client', '~> 1.7'
23
+ gem.add_dependency 'retries'
24
+ gem.add_dependency 'confstruct', '>= 0.2.7', '< 2'
24
25
 
25
- gem.add_development_dependency "rake"
26
- gem.add_development_dependency "rspec", "~> 3.3"
27
- gem.add_development_dependency "yard"
28
- gem.add_development_dependency "redcarpet"
29
- gem.add_development_dependency "equivalent-xml", '~> 0.5.1'
26
+ gem.add_development_dependency 'rake'
27
+ gem.add_development_dependency 'rspec', '~> 3.3'
28
+ gem.add_development_dependency 'yard'
29
+ gem.add_development_dependency 'redcarpet'
30
+ gem.add_development_dependency 'equivalent-xml', '~> 0.5.1'
30
31
  end
@@ -1,2 +1,2 @@
1
- require "dor/workflow_version"
1
+ require 'dor/workflow_version'
2
2
  require 'dor/services/workflow_service'
@@ -2,13 +2,24 @@ require 'rest-client'
2
2
  require 'active_support'
3
3
  require 'active_support/core_ext'
4
4
  require 'nokogiri'
5
+ require 'retries'
5
6
 
6
7
  module Dor
7
8
 
8
- # Methods to create and update workflow
9
- module WorkflowService
9
+ # TODO: major version revision: change pattern of usage to be normal non-singleton class
10
+ # TODO: convert @@class_vars to regular attributes
11
+ # TODO: create normal initalize method, deprecate configure
12
+ # TODO: hardcoded 'true' returns are dumb, instead return the response object where possible
13
+ # TODO: VALID_STATUS should be just another attribute w/ default
14
+ # TODO: allow constructor/initalizer to receive RestClient::Resource object(s), not just URLs (solves SSL/proxy config problem)
15
+ # TODO: allow constructor/initalizer to receive logger
16
+
17
+ # Create and update workflows
18
+ class WorkflowService
10
19
  class << self
11
20
 
21
+ @@handler = nil
22
+ @@logger = nil
12
23
  @@resource = nil
13
24
  @@dor_services_url = nil
14
25
 
@@ -32,9 +43,12 @@ module Dor
32
43
  def create_workflow(repo, druid, workflow_name, wf_xml, opts = {:create_ds => true})
33
44
  lane_id = opts.fetch(:lane_id, 'default')
34
45
  xml = add_lane_id_to_workflow_xml(lane_id, wf_xml)
35
- workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow_name}"].put(xml, :content_type => 'application/xml',
36
- :params => {'create-ds' => opts[:create_ds] })
37
- return true
46
+ workflow_resource_method "#{repo}/objects/#{druid}/workflows/#{workflow_name}", 'put', xml,
47
+ {
48
+ :content_type => 'application/xml',
49
+ :params => { 'create-ds' => opts[:create_ds] }
50
+ }
51
+ true
38
52
  end
39
53
 
40
54
  # Updates the status of one step in a workflow.
@@ -60,19 +74,19 @@ module Dor
60
74
  # <process name=\"convert\" status=\"completed\" />"
61
75
  def update_workflow_status(repo, druid, workflow, process, status, opts = {})
62
76
  raise ArgumentError, "Unknown status value #{status}" unless VALID_STATUS.include?(status.downcase)
63
- opts = {:elapsed => 0, :lifecycle => nil, :note => nil}.merge!(opts)
77
+ opts = { :elapsed => 0, :lifecycle => nil, :note => nil }.merge!(opts)
64
78
  opts[:elapsed] = opts[:elapsed].to_s
65
79
  current_status = opts.delete(:current_status)
66
- xml = create_process_xml({:name => process, :status => status.downcase}.merge!(opts))
80
+ xml = create_process_xml({ :name => process, :status => status.downcase }.merge!(opts))
67
81
  uri = "#{repo}/objects/#{druid}/workflows/#{workflow}/#{process}"
68
82
  uri << "?current-status=#{current_status.downcase}" if current_status
69
- workflow_resource[uri].put(xml, :content_type => 'application/xml')
70
- return true
83
+ workflow_resource_method(uri, 'put', xml, { :content_type => 'application/xml' })
84
+ true
71
85
  end
72
86
 
73
87
  #
74
88
  # Retrieves the process status of the given workflow for the given object identifier
75
- # @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
89
+ # @param [String] repo The repository the object resides in. Currently recoginzes "dor" and "sdr".
76
90
  # @param [String] druid The id of the object
77
91
  # @param [String] workflow The name of the workflow
78
92
  # @param [String] process The name of the process step
@@ -80,35 +94,34 @@ module Dor
80
94
  def get_workflow_status(repo, druid, workflow, process)
81
95
  workflow_md = get_workflow_xml(repo, druid, workflow)
82
96
  doc = Nokogiri::XML(workflow_md)
83
- raise Exception.new("Unable to parse response:\n#{workflow_md}") if(doc.root.nil?)
84
-
97
+ raise Exception.new("Unable to parse response:\n#{workflow_md}") if doc.root.nil?
85
98
  status = doc.root.at_xpath("//process[@name='#{process}']/@status")
86
- status=status.content if status
87
- return status
99
+ status = status.content if status
100
+ status
88
101
  end
89
102
 
90
103
  #
91
104
  # Retrieves the raw XML for the given workflow
92
- # @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
105
+ # @param [String] repo The repository the object resides in. Currently recoginzes "dor" and "sdr".
93
106
  # @param [String] druid The id of the object
94
107
  # @param [String] workflow The name of the workflow
95
108
  # @return [String] XML of the workflow
96
109
  def get_workflow_xml(repo, druid, workflow)
97
- workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}"].get
110
+ workflow_resource_method "#{repo}/objects/#{druid}/workflows/#{workflow}"
98
111
  end
99
112
 
100
113
  # Get workflow names into an array for given PID
101
114
  # This method only works when this gem is used in a project that is configured to connect to DOR
102
115
  #
103
116
  # @param [String] pid of druid
104
- # @param [String] repo repository for the object. Defaults to 'dor'
117
+ # @param [String] repo repository for the object
105
118
  # @return [Array<String>] list of worklows
106
119
  # @example
107
120
  # Dor::WorkflowService.get_workflows('druid:sr100hp0609')
108
121
  # => ["accessionWF", "assemblyWF", "disseminationWF"]
109
- def get_workflows(pid, repo='dor')
110
- xml_doc=Nokogiri::XML(get_workflow_xml(repo,pid,''))
111
- return xml_doc.xpath('//workflow').collect {|workflow| workflow['id']}
122
+ def get_workflows(pid, repo = 'dor')
123
+ xml_doc = Nokogiri::XML(get_workflow_xml(repo, pid, ''))
124
+ xml_doc.xpath('//workflow').collect {|workflow| workflow['id']}
112
125
  end
113
126
 
114
127
  # Get active workflow names into an array for given PID
@@ -121,7 +134,7 @@ module Dor
121
134
  # Dor::WorkflowService.get_workflows('dor', 'druid:sr100hp0609')
122
135
  # => ["accessionWF", "assemblyWF", "disseminationWF"]
123
136
  def get_active_workflows(repo, pid)
124
- doc = Nokogiri::XML(get_workflow_xml(repo,pid,''))
137
+ doc = Nokogiri::XML(get_workflow_xml(repo, pid, ''))
125
138
  doc.xpath( %(//workflow[not(process/@archived)]/@id ) ).map {|n| n.value}
126
139
  end
127
140
 
@@ -145,8 +158,8 @@ module Dor
145
158
  def update_workflow_error_status(repo, druid, workflow, process, error_msg, opts = {})
146
159
  opts = {:error_text => nil}.merge!(opts)
147
160
  xml = create_process_xml({:name => process, :status => 'error', :errorMessage => error_msg}.merge!(opts))
148
- workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}/#{process}"].put(xml, :content_type => 'application/xml')
149
- return true
161
+ workflow_resource_method "#{repo}/objects/#{druid}/workflows/#{workflow}/#{process}", 'put', xml, {:content_type => 'application/xml'}
162
+ true
150
163
  end
151
164
 
152
165
  # Deletes a workflow from a particular repository and druid
@@ -155,8 +168,8 @@ module Dor
155
168
  # @param [String] workflow The name of the workflow to be deleted
156
169
  # @return [Boolean] always true
157
170
  def delete_workflow(repo, druid, workflow)
158
- workflow_resource["#{repo}/objects/#{druid}/workflows/#{workflow}"].delete
159
- return true
171
+ workflow_resource_method "#{repo}/objects/#{druid}/workflows/#{workflow}", 'delete'
172
+ true
160
173
  end
161
174
 
162
175
  # Returns the Date for a requested milestone from workflow lifecycle
@@ -171,10 +184,9 @@ module Dor
171
184
  # <milestone date="2010-06-15T16:08:58-0700">released</milestone>
172
185
  # </lifecycle>
173
186
  def get_lifecycle(repo, druid, milestone)
174
- doc = self.query_lifecycle(repo, druid)
187
+ doc = query_lifecycle(repo, druid)
175
188
  milestone = doc.at_xpath("//lifecycle/milestone[text() = '#{milestone}']")
176
189
  return Time.parse(milestone['date']) if milestone
177
-
178
190
  nil
179
191
  end
180
192
 
@@ -190,17 +202,16 @@ module Dor
190
202
  # <milestone date="2010-06-15T16:08:58-0700">released</milestone>
191
203
  # </lifecycle>
192
204
  def get_active_lifecycle(repo, druid, milestone)
193
- doc = self.query_lifecycle(repo, druid, true)
205
+ doc = query_lifecycle(repo, druid, true)
194
206
  milestone = doc.at_xpath("//lifecycle/milestone[text() = '#{milestone}']")
195
207
  return Time.parse(milestone['date']) if milestone
196
-
197
208
  nil
198
209
  end
199
210
 
200
211
  # @return [Hash]
201
212
  def get_milestones(repo, druid)
202
- doc = self.query_lifecycle(repo, druid)
203
- doc.xpath("//lifecycle/milestone").collect do |node|
213
+ doc = query_lifecycle(repo, druid)
214
+ doc.xpath('//lifecycle/milestone').collect do |node|
204
215
  { :milestone => node.text, :at => Time.parse(node['date']), :version => node['version'] }
205
216
  end
206
217
  end
@@ -208,16 +219,13 @@ module Dor
208
219
  # Converts repo-workflow-step into repo:workflow:step
209
220
  # @param [String] default_repository
210
221
  # @param [String] default_workflow
211
- # @param [String] step if contains colon :, then uses
212
- # the value for workflow and/or workflow/repository.
213
- # for example, jp2-create, or assemblyWF:jp2-create,
214
- # or dor:assemblyWF:jp2-create
222
+ # @param [String] step if contains colon :, then the value for workflow and/or workflow/repository. For example: 'jp2-create', 'assemblyWF:jp2-create' or 'dor:assemblyWF:jp2-create'
215
223
  # @return [String] repo:workflow:step
216
224
  # @example
217
225
  # dor:assemblyWF:jp2-create
218
226
  def qualify_step(default_repository, default_workflow, step)
219
- current = step.split(/:/,3)
220
- current.unshift(default_workflow) if current.length < 3
227
+ current = step.split(/:/, 3)
228
+ current.unshift(default_workflow) if current.length < 3
221
229
  current.unshift(default_repository) if current.length < 3
222
230
  current.join(':')
223
231
  end
@@ -256,24 +264,21 @@ module Dor
256
264
  # "druid:py156ps0477",
257
265
  # }
258
266
  #
259
- def get_objects_for_workstep completed, waiting, lane_id='default', options = {}
260
- waiting_param = qualify_step(options[:default_repository],options[:default_workflow],waiting)
267
+ def get_objects_for_workstep(completed, waiting, lane_id = 'default', options = {})
268
+ waiting_param = qualify_step(options[:default_repository], options[:default_workflow], waiting)
261
269
  uri_string = "workflow_queue?waiting=#{waiting_param}"
262
- if(completed)
270
+ if completed
263
271
  Array(completed).each do |step|
264
- completed_param = qualify_step(options[:default_repository],options[:default_workflow],step)
272
+ completed_param = qualify_step(options[:default_repository], options[:default_workflow], step)
265
273
  uri_string << "&completed=#{completed_param}"
266
274
  end
267
275
  end
268
276
 
269
- if options[:limit] && options[:limit].to_i > 0
270
- uri_string << "&limit=#{options[:limit].to_i}"
271
- end
272
-
277
+ uri_string << "&limit=#{options[:limit].to_i}" if options[:limit] && options[:limit].to_i > 0
273
278
  uri_string << "&lane-id=#{lane_id}"
274
279
 
275
- workflow_resource.options[:timeout] = 5 * 60 unless(workflow_resource.options.include?(:timeout))
276
- resp = workflow_resource[uri_string].get
280
+ workflow_resource.options[:timeout] = 5 * 60 unless workflow_resource.options.include?(:timeout)
281
+ resp = workflow_resource_method uri_string
277
282
  #
278
283
  # response looks like:
279
284
  # <objects count="2">
@@ -299,10 +304,9 @@ module Dor
299
304
  # Dor::WorkflowService.get_errored_objects_for_workstep('accessionWF','content-metadata')
300
305
  # => {"druid:qd556jq0580"=>"druid:qd556jq0580 - Item error; caused by
301
306
  # #<Rubydora::FedoraInvalidRequest: Error modifying datastream contentMetadata for druid:qd556jq0580. See logger for details>"}
302
- def get_errored_objects_for_workstep workflow, step, repository='dor'
307
+ def get_errored_objects_for_workstep(workflow, step, repository = 'dor')
308
+ resp = workflow_resource_method "workflow_queue?repository=#{repository}&workflow=#{workflow}&error=#{step}"
303
309
  result = {}
304
- uri_string = "workflow_queue?repository=#{repository}&workflow=#{workflow}&error=#{step}"
305
- resp = workflow_resource[uri_string].get
306
310
  Nokogiri::XML(resp).xpath('//object').collect do |node|
307
311
  result.merge!(node['id'] => node['errorMessage'])
308
312
  end
@@ -316,7 +320,7 @@ module Dor
316
320
  # @param [String] repository -- optional, default=dor
317
321
  #
318
322
  # @return [Integer] Number of objects with this repository:workflow:step that have a status of 'error'
319
- def count_errored_for_workstep(workflow, step, repository='dor')
323
+ def count_errored_for_workstep(workflow, step, repository = 'dor')
320
324
  count_objects_in_step(workflow, step, repository, 'error')
321
325
  end
322
326
 
@@ -327,7 +331,7 @@ module Dor
327
331
  # @param [String] repository -- optional, default=dor
328
332
  #
329
333
  # @return [Integer] Number of objects with this repository:workflow:step that have a status of 'queued'
330
- def count_queued_for_workstep(workflow, step, repository='dor')
334
+ def count_queued_for_workstep(workflow, step, repository = 'dor')
331
335
  count_objects_in_step(workflow, step, repository, 'queued')
332
336
  end
333
337
 
@@ -342,8 +346,7 @@ module Dor
342
346
  # :workflow, :step, :druid, :lane_id
343
347
  def get_stale_queued_workflows(repository, opts = {})
344
348
  uri_string = build_queued_uri(repository, opts)
345
- xml = workflow_resource[uri_string].get
346
- parse_queued_workflows_response xml
349
+ parse_queued_workflows_response workflow_resource_method(uri_string)
347
350
  end
348
351
 
349
352
  # 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
@@ -353,31 +356,31 @@ module Dor
353
356
  # meaning you will get all queued workflows
354
357
  # @return [Integer] number of stale, queued steps if the :count_only option was set to true
355
358
  def count_stale_queued_workflows(repository, opts = {})
356
- uri_string = build_queued_uri(repository, opts)
357
- uri_string << "&count-only=true"
358
- xml = workflow_resource[uri_string].get
359
- doc = Nokogiri::XML(xml)
360
- return doc.at_xpath('/objects/@count').value.to_i
359
+ uri_string = build_queued_uri(repository, opts) + '&count-only=true'
360
+ doc = Nokogiri::XML(workflow_resource_method uri_string)
361
+ doc.at_xpath('/objects/@count').value.to_i
361
362
  end
362
363
 
364
+ # @param [Hash] params
363
365
  # @return [String]
364
366
  def create_process_xml(params)
365
367
  builder = Nokogiri::XML::Builder.new do |xml|
366
- attrs = params.reject { |k,v| v.nil? }
367
- attrs = Hash[ attrs.map {|k,v| [k.to_s.camelize(:lower), v]}] # camelize all the keys in the attrs hash
368
+ attrs = params.reject { |k, v| v.nil? }
369
+ attrs = Hash[ attrs.map {|k, v| [k.to_s.camelize(:lower), v]}] # camelize all the keys in the attrs hash
368
370
  xml.process(attrs)
369
371
  end
370
- return builder.to_xml
372
+ builder.to_xml
371
373
  end
372
374
 
373
375
  # @return [Nokogiri::XML::Document]
374
376
  def query_lifecycle(repo, druid, active_only = false)
375
377
  req = "#{repo}/objects/#{druid}/lifecycle"
376
378
  req << '?active-only=true' if active_only
377
- lifecycle_xml = workflow_resource[req].get
378
- return Nokogiri::XML(lifecycle_xml)
379
+ Nokogiri::XML(workflow_resource_method req)
379
380
  end
380
381
 
382
+ # @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
383
+ # @param [String] druid The id of the object to archive the workflows from
381
384
  def archive_active_workflow(repo, druid)
382
385
  workflows = get_active_workflows(repo, druid)
383
386
  workflows.each do |wf|
@@ -385,12 +388,13 @@ module Dor
385
388
  end
386
389
  end
387
390
 
388
- def archive_workflow(repo, druid, wf_name, version_num=nil)
389
- raise "Please call Dor::WorkflowService.configure(workflow_service_url, :dor_services_url => DOR_SERVIES_URL) once before archiving workflow" if(@@dor_services_url.nil?)
390
-
391
+ # @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
392
+ # @param [String] druid The id of the object to delete the workflow from
393
+ def archive_workflow(repo, druid, wf_name, version_num = nil)
394
+ raise 'Please call Dor::WorkflowService.configure(workflow_service_url, :dor_services_url => DOR_SERVIES_URL) once before archiving workflow' if @@dor_services_url.nil?
391
395
  dor_services = RestClient::Resource.new(@@dor_services_url)
392
396
  url = "/v1/objects/#{druid}/workflows/#{wf_name}/archive"
393
- url << "/#{version_num}" if(version_num)
397
+ url << "/#{version_num}" if version_num
394
398
  dor_services[url].post ''
395
399
  end
396
400
 
@@ -404,9 +408,9 @@ module Dor
404
408
  # @param [Boolean] create_accession_wf Option to create accessionWF when closing a version. Defaults to true
405
409
  def close_version(repo, druid, create_accession_wf = true)
406
410
  uri = "#{repo}/objects/#{druid}/versionClose"
407
- uri << "?create-accession=false" unless create_accession_wf
408
- workflow_resource[uri].post ''
409
- return true
411
+ uri << '?create-accession=false' unless create_accession_wf
412
+ workflow_resource_method(uri, 'post', '')
413
+ true
410
414
  end
411
415
 
412
416
  # Returns all the distinct laneIds for a given workflow step
@@ -417,57 +421,74 @@ module Dor
417
421
  # @return [Array<String>] all of the distinct laneIds. Array will be empty if no lane ids were found
418
422
  def get_lane_ids(repo, workflow, process)
419
423
  uri = "workflow_queue/lane_ids?step=#{repo}:#{workflow}:#{process}"
420
- doc = Nokogiri::XML(workflow_resource[uri].get)
424
+ doc = Nokogiri::XML(workflow_resource_method uri)
421
425
  nodes = doc.xpath('/lanes/lane')
422
- nodes.map {|n| n['id']}
426
+ nodes.map { |n| n['id'] }
423
427
  end
424
428
 
425
- # @return [RestClient::Resource] the REST client resource
429
+ ### MIMICKING ATTRIBUTE READER
430
+ # @return [RestClient::Resource] the REST client resource created during configure()
426
431
  def workflow_resource
427
- raise "Please call Dor::WorkflowService.configure(url) once before calling any WorkflowService methods" if(@@resource.nil?)
432
+ raise 'Please call Dor::WorkflowService.configure(url) once before calling any WorkflowService methods' if @@resource.nil?
428
433
  @@resource
429
434
  end
430
435
 
436
+ # Among other things, a distinct method helps tests mock default logger
437
+ # @param [String, IO] logdev The log device. This is a filename (String) or IO object (typically STDOUT, STDERR, or an open file).
438
+ # @param [String, Integer] shift_age Number of old log files to keep, or frequency of rotation (daily, weekly or monthly).
439
+ # @return [Logger] default logger object
440
+ def default_logger(logdev = 'workflow_service.log', shift_age = 'weekly')
441
+ Logger.new(logdev, shift_age)
442
+ end
443
+
444
+ def workflow_service_exceptions_to_catch
445
+ [RestClient::Exception]
446
+ end
447
+
431
448
  # Configure the workflow service
432
- #
449
+ # TODO: replace with initialize
433
450
  # @param [String] url points to the workflow service
434
451
  # @param [Hash] opts optional params
435
- # @option opts [String] :dor_services_uri uri to the DOR REST service
452
+ # @option opts [Logger] :logger defaults writing to workflow_service.log with weekly rotation
453
+ # @option opts [String] :dor_services_url uri to the DOR REST service
436
454
  # @option opts [Integer] :timeout number of seconds for RestClient timeout
437
455
  # @option opts [String] :client_cert_file path to an SSL client certificate (deprecated)
438
456
  # @option opts [String] :client_key_file path to an SSL key file (deprecated)
439
457
  # @option opts [String] :client_key_pass password for the key file (deprecated)
440
458
  # @return [RestClient::Resource] the REST client resource
441
- def configure(url, opts={})
459
+ def configure(url, opts = {})
442
460
  params = {}
443
- params[:timeout] = opts[:timeout] if opts[:timeout]
461
+ params[:timeout] = opts[:timeout] if opts[:timeout]
462
+ @@logger = opts[:logger] || default_logger
444
463
  @@dor_services_url = opts[:dor_services_url] if opts[:dor_services_url]
445
- #params[:ssl_client_cert] = OpenSSL::X509::Certificate.new(File.read(opts[:client_cert_file])) if opts[:client_cert_file]
446
- #params[:ssl_client_key] = OpenSSL::PKey::RSA.new(File.read(opts[:client_key_file]), opts[:client_key_pass]) if opts[:client_key_file]
464
+ # params[:ssl_client_cert] = OpenSSL::X509::Certificate.new(File.read(opts[:client_cert_file])) if opts[:client_cert_file]
465
+ # params[:ssl_client_key] = OpenSSL::PKey::RSA.new(File.read(opts[:client_key_file]), opts[:client_key_pass]) if opts[:client_key_file]
466
+ @@handler = Proc.new do |exception, attempt_number, total_delay|
467
+ @@logger.warn "[Attempt #{attempt_number}] #{exception.class}: #{exception.message}; #{total_delay} seconds elapsed."
468
+ end
447
469
  @@resource = RestClient::Resource.new(url, params)
448
470
  end
449
471
 
472
+
450
473
  protected
451
474
 
452
475
  def build_queued_uri(repository, opts = {})
453
476
  uri_string = "workflow_queue/all_queued?repository=#{repository}"
454
477
  uri_string << "&hours-ago=#{opts[:hours_ago]}" if opts[:hours_ago]
455
- uri_string << "&limit=#{opts[:limit]}" if opts[:limit]
478
+ uri_string << "&limit=#{opts[:limit]}" if opts[:limit]
456
479
  uri_string
457
480
  end
458
481
 
459
482
  def parse_queued_workflows_response(xml)
460
- res = []
461
483
  doc = Nokogiri::XML(xml)
462
- doc.xpath('/workflows/workflow').each do |wf_node|
463
- wf = {}
464
- wf[:workflow] = wf_node['name']
465
- wf[:step] = wf_node['process']
466
- wf[:druid] = wf_node['druid']
467
- wf[:lane_id] = wf_node['laneId']
468
- res << wf
484
+ doc.xpath('/workflows/workflow').collect do |wf_node|
485
+ {
486
+ :workflow => wf_node['name'],
487
+ :step => wf_node['process'],
488
+ :druid => wf_node['druid'],
489
+ :lane_id => wf_node['laneId']
490
+ }
469
491
  end
470
- res
471
492
  end
472
493
 
473
494
  # Adds laneId attributes to each process of workflow xml
@@ -482,13 +503,32 @@ module Dor
482
503
  end
483
504
 
484
505
  def count_objects_in_step(workflow, step, type, repo)
485
- uri_string = "workflow_queue?repository=#{repo}&workflow=#{workflow}&#{type}=#{step}"
486
- resp = @workflow_resource[uri_string].get
506
+ resp = workflow_resource_method "workflow_queue?repository=#{repo}&workflow=#{workflow}&#{type}=#{step}"
487
507
  node = Nokogiri::XML(resp).at_xpath('/objects')
488
- raise "Unable to determine count from response" if node.nil?
508
+ raise 'Unable to determine count from response' if node.nil?
489
509
  node['count'].to_i
490
510
  end
491
511
 
512
+ # calls workflow_resource[uri_string]."#{meth}" with variable number of optional arguments
513
+ # The point of this is to wrap ALL remote calls with consistent error handling and logging
514
+ # @param [String] uri_string resource to request
515
+ # @param [String] meth REST method to use on resource (get, put, post, delete, etc.)
516
+ # @param [String] payload body for (e.g. put) request
517
+ # @param [Hash] opts addtional headers options
518
+ # @return [Object] response from method
519
+ def workflow_resource_method(uri_string, meth = 'get', payload = '', opts = {})
520
+ with_retries(:max_tries => 2, :handler => @@handler, :rescue => workflow_service_exceptions_to_catch) do |attempt|
521
+ @@logger.info "[Attempt #{attempt}] #{meth} #{workflow_resource.url}/#{uri_string}"
522
+ if %w[get delete].include?(meth)
523
+ workflow_resource[uri_string].send(meth, opts)
524
+ elsif opts.size == 0 # the right number of args allows existing test expect/with statements to continue working
525
+ workflow_resource[uri_string].send(meth, payload)
526
+ else
527
+ workflow_resource[uri_string].send(meth, payload, opts)
528
+ end
529
+ end
530
+ end
531
+
492
532
  end
493
533
  end
494
534
  end