dor-workflow-service 1.7.6 → 1.7.7

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