dor-workflow-client 3.19.0 → 3.23.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1fdb8fc01a2ed982c1fd0e0d2e0aaaf58e9ac4dd89171c879c491fccaf02a83d
4
- data.tar.gz: 50503e224f5c8c0e23cd9111de9c889b0f2773697a9acc1eb5985d8bca916111
3
+ metadata.gz: 7b6a0ed1dac951da79aabe808df201cd4064ef5e1699d803673e1675d4b9dc0f
4
+ data.tar.gz: 8ada5b31dda45a87efaad45a420a300fd907d376cbe85f52b93a099eb5859d27
5
5
  SHA512:
6
- metadata.gz: ed1e22bad5aebd865bff1c03ab340b37a73eb7b4d6671436ad4fa2b5375c68667deb26fdd630800dad6d1392a4f27877c9c32611e54495947488f0d7ad0ab0de
7
- data.tar.gz: 604a0981194c0d857b93ddc7925a3c4036eb560141e1ba61226367e8324ee26206cd92f2abe61ce6710ac89493c1ce563d001e64bc206605aad6af25544ad652
6
+ metadata.gz: 47ad0a88123584d64c7324adc24d19c597475edb884510faca16ee4b2abb8c2f18ef6f1a70ca74008de4e9ce4b6aa2af6fe52f94e92febd72667c2d02ed25838
7
+ data.tar.gz: 4d19c76451e137c4a8c7a4cbc443115324f65b722ee747168b75d2898c278abfec3ee3ece4d7e0e81cafd154bea5cff5ec945d5b09061d1f7105ed791f0a960e
@@ -0,0 +1,35 @@
1
+ version: 2.1
2
+
3
+ jobs:
4
+ build:
5
+ docker:
6
+ - image: circleci/ruby:2.7.1
7
+ environment:
8
+ CC_TEST_REPORTER_ID: 3853bc688a88a4de1a5568eaa8163d20db8637fe988540a935aff028190cff6a
9
+ RAILS_ENV: test
10
+ steps:
11
+ - checkout
12
+ - run:
13
+ name: Install Bundler
14
+ command: gem install bundler
15
+ - run:
16
+ name: Which bundler?
17
+ command: bundle -v
18
+ - run:
19
+ name: Bundle Install
20
+ command: bundle check || bundle install
21
+ - run:
22
+ name: Lint using rubocop
23
+ command: bundle exec rubocop
24
+ - run:
25
+ name: Setup Code Climate test-reporter
26
+ command: |
27
+ curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
28
+ chmod +x ./cc-test-reporter
29
+ ./cc-test-reporter before-build
30
+ - run:
31
+ name: rspec
32
+ command: bundle exec rspec
33
+ - run:
34
+ name: upload test coverage report to Code Climate
35
+ command: ./cc-test-reporter after-build --coverage-input-type simplecov --exit-code $?
@@ -0,0 +1,12 @@
1
+ ## Why was this change made?
2
+
3
+
4
+
5
+ ## How was this change tested?
6
+
7
+
8
+
9
+ ## Which documentation and/or configurations were updated?
10
+
11
+
12
+
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://travis-ci.org/sul-dlss/dor-workflow-client.svg?branch=master)](https://travis-ci.org/sul-dlss/dor-workflow-client)
1
+ [![CircleCI](https://circleci.com/gh/sul-dlss/dor-workflow-client.svg?style=svg)](https://circleci.com/gh/sul-dlss/dor-workflow-client)
2
2
  [![Test Coverage](https://api.codeclimate.com/v1/badges/fba77ff479c468f8510f/test_coverage)](https://codeclimate.com/github/sul-dlss/dor-services-client/test_coverage)
3
3
  [![Maintainability](https://api.codeclimate.com/v1/badges/fba77ff479c468f8510f/maintainability)](https://codeclimate.com/github/sul-dlss/dor-services-client/maintainability)
4
4
  [![Gem Version](https://badge.fury.io/rb/dor-workflow-client.svg)](https://badge.fury.io/rb/dor-workflow-client)
@@ -37,7 +37,7 @@ client.update_status(druid: 'druid:bc123df4567',
37
37
 
38
38
  Show "milestones" for an object
39
39
  ```ruby
40
- client.milestones('dor', 'druid:gv054hp4128')
40
+ client.milestones(druid: 'druid:gv054hp4128')
41
41
  #=> [{version: '1', milestone: 'published'}]
42
42
  ```
43
43
 
@@ -1,4 +1,4 @@
1
- # frozen_string_literal: true
1
+ # frozen_string_literal: true
2
2
 
3
3
  lib = File.expand_path('lib', __dir__)
4
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
@@ -27,7 +27,7 @@ Gem::Specification.new do |gem|
27
27
  gem.add_development_dependency 'rake'
28
28
  gem.add_development_dependency 'rspec', '~> 3.3'
29
29
  gem.add_development_dependency 'rubocop', '~> 0.63.1'
30
- gem.add_development_dependency 'simplecov'
30
+ gem.add_development_dependency 'simplecov', '~> 0.17.0' # CodeClimate cannot use SimpleCov >= 0.18.0 for generating test coverage
31
31
  gem.add_development_dependency 'webmock'
32
32
  gem.add_development_dependency 'yard'
33
33
  end
@@ -82,7 +82,7 @@ module Dor
82
82
  end
83
83
 
84
84
  def status(druid:, version:)
85
- @status ||= Status.new(druid: druid, version: version, lifecycle_routes: lifecycle_routes)
85
+ Status.new(druid: druid, version: version, lifecycle_routes: lifecycle_routes)
86
86
  end
87
87
 
88
88
  private
@@ -10,37 +10,102 @@ module Dor
10
10
  end
11
11
 
12
12
  # Returns the Date for a requested milestone from workflow lifecycle
13
- # @param [String] repo repository name
13
+ #
14
+ # @param [String] repo The repository the object resides in. This parameter is deprecated
14
15
  # @param [String] druid object id
15
16
  # @param [String] milestone_name the name of the milestone being queried for
16
17
  # @param [Number] version the version to query for
17
18
  # @param [Boolean] active_only (false) if true, return only lifecycle steps for versions that have all processes complete
18
19
  # @return [Time] when the milestone was achieved. Returns nil if the milestone does not exist
19
20
  #
20
- def lifecycle(repo, druid, milestone_name, version: nil, active_only: false)
21
- filter_milestone(query_lifecycle(repo, druid, version: version, active_only: active_only), milestone_name)
21
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
22
+ def lifecycle(*args)
23
+ case args.size
24
+ when 4
25
+ Deprecation.warn(self, 'you provided 4 args, but lifecycle now takes kwargs')
26
+ (repo, druid, milestone_name) = args[0..2]
27
+ version = args[3][:version]
28
+ active_only = args[3][:active_only] || false
29
+ when 3
30
+ Deprecation.warn(self, 'you provided 3 args, but lifecycle now takes kwargs')
31
+ (repo, druid, milestone_name) = args
32
+ version = nil
33
+ active_only = false
34
+ when 1
35
+ opts = args.first
36
+ repo = opts[:repo]
37
+ druid = opts[:druid]
38
+ milestone_name = opts[:milestone_name]
39
+ version = opts[:version]
40
+ active_only = opts[:active_only] || false
41
+ else
42
+ raise ArgumentError, 'wrong number of arguments, must be 1, or 3-5'
43
+ end
44
+
45
+ Deprecation.warn(self, 'passing the repo parameter to lifecycle is no longer necessary. This will raise an error in dor-workflow-client version 4') if repo
46
+
47
+ filter_milestone(query_lifecycle(druid, version: version, active_only: active_only), milestone_name)
22
48
  end
49
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
23
50
 
24
51
  # Returns the Date for a requested milestone ONLY for the current version.
25
52
  # This is slow as the workflow server will query dor-services-app for the version.
26
53
  # A better approach is #lifecycle with the version tag.
27
- # @param [String] repo repository name
28
54
  # @param [String] druid object id
29
55
  # @param [String] milestone_name the name of the milestone being queried for
30
56
  # @param [Number] version the version to query for
31
57
  # @return [Time] when the milestone was achieved. Returns nil if the milestone does not exis
32
58
  #
33
- def active_lifecycle(repo, druid, milestone_name, version: nil)
34
- lifecycle(repo, druid, milestone_name, version: version, active_only: true)
59
+ # rubocop:disable Metrics/MethodLength
60
+ def active_lifecycle(*args)
61
+ case args.size
62
+ when 4
63
+ Deprecation.warn(self, 'you provided 4 args, but active_lifecycle now takes kwargs')
64
+ (repo, druid, milestone_name) = args[0..2]
65
+ version = args[3][:version]
66
+ when 3
67
+ Deprecation.warn(self, 'you provided 3 args, but active_lifecycle now takes kwargs')
68
+ (repo, druid, milestone_name) = args
69
+ version = nil
70
+ when 1
71
+ opts = args.first
72
+ repo = opts[:repo]
73
+ druid = opts[:druid]
74
+ milestone_name = opts[:milestone_name]
75
+ version = opts[:version]
76
+ else
77
+ raise ArgumentError, 'wrong number of arguments, must be 1, 3, or 4'
78
+ end
79
+
80
+ Deprecation.warn(self, 'passing the repo parameter to active_lifecycle is no longer necessary. This will raise an error in dor-workflow-client version 4') if repo
81
+
82
+ lifecycle(druid: druid, milestone_name: milestone_name, version: version, active_only: true)
35
83
  end
84
+ # rubocop:enable Metrics/MethodLength
85
+
86
+ # @return [Array<Hash>]
87
+ # rubocop:disable Metrics/MethodLength
88
+ def milestones(*args)
89
+ case args.size
90
+ when 2
91
+ Deprecation.warn(self, 'you provided 2 args, but active_lifecycle now takes kwargs')
92
+ (repo, druid) = args
93
+ when 1
94
+ opts = args.first
95
+ repo = opts[:repo]
96
+ druid = opts.fetch(:druid)
97
+ else
98
+ raise ArgumentError, 'wrong number of arguments, must be 1-2'
99
+ end
100
+
101
+ Deprecation.warn(self, 'passing the repo parameter to active_lifecycle is no longer necessary. This will raise an error in dor-workflow-client version 4') if repo
36
102
 
37
- # @return [Hash]
38
- def milestones(repo, druid)
39
- doc = query_lifecycle(repo, druid, active_only: false)
103
+ doc = query_lifecycle(druid, active_only: false)
40
104
  doc.xpath('//lifecycle/milestone').collect do |node|
41
105
  { milestone: node.text, at: Time.parse(node['date']), version: node['version'] }
42
106
  end
43
107
  end
108
+ # rubocop:enable Metrics/MethodLength
44
109
 
45
110
  private
46
111
 
@@ -51,7 +116,6 @@ module Dor
51
116
  Time.parse(milestone['date'])
52
117
  end
53
118
 
54
- # @param [String] repo repository name
55
119
  # @param [String] druid object id
56
120
  # @param [Boolean] active_only (false) if true, return only lifecycle steps for versions that have all processes complete
57
121
  # @param [Number] version the version to query for
@@ -63,8 +127,8 @@ module Dor
63
127
  # <milestone date="2010-06-15T16:08:58-0700">released</milestone>
64
128
  # </lifecycle>
65
129
  #
66
- def query_lifecycle(repo, druid, active_only:, version: nil)
67
- req = "#{repo}/objects/#{druid}/lifecycle"
130
+ def query_lifecycle(druid, active_only:, version: nil)
131
+ req = "objects/#{druid}/lifecycle"
68
132
  params = []
69
133
  params << "version=#{version}" if version
70
134
  params << 'active-only=true' if active_only
@@ -11,39 +11,59 @@ module Dor
11
11
 
12
12
  # Returns all the distinct laneIds for a given workflow step
13
13
  #
14
- # @param [String] repo The repository the object resides in. The service recoginzes "dor" and "sdr" at the moment
14
+ # @param [String] repo -- deprecated, ignored by workflow service
15
15
  # @param [String] workflow name
16
16
  # @param [String] process name
17
17
  # @return [Array<String>] all of the distinct laneIds. Array will be empty if no lane ids were found
18
- def lane_ids(repo, workflow, process)
19
- uri = "workflow_queue/lane_ids?step=#{repo}:#{workflow}:#{process}"
18
+ def lane_ids(*args)
19
+ if args.count == 3
20
+ Deprecation.warn(
21
+ self,
22
+ '`#lane_ids` only takes two args: workflow name, & process/step name. This will raise an exception in Dor::Workflow::Client 4.0.0'
23
+ )
24
+ args.shift # ditch the `repo` argument
25
+ end
26
+ uri = "workflow_queue/lane_ids?step=#{args.first}:#{args.second}"
20
27
  doc = Nokogiri::XML(requestor.request(uri))
21
- nodes = doc.xpath('/lanes/lane')
22
- nodes.map { |n| n['id'] }
28
+ doc.xpath('/lanes/lane').map { |n| n['id'] }
23
29
  end
24
30
 
25
31
  # 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
26
32
  # This will enable re-queueing of jobs that have been lost by the job manager
27
- # @param [String] repository name of the repository you want to query, like 'dor' or 'sdr'
33
+ # @param [String] repository -- deprecated, ignored by workflow service
28
34
  # @param [Hash] opts optional values for query
29
35
  # @option opts [Integer] :hours_ago steps older than this value will be returned by the query. If not passed in, the service defaults to 0 hours,
30
36
  # meaning you will get all queued workflows
31
37
  # @option opts [Integer] :limit sets the maximum number of workflow steps that can be returned. Defaults to no limit
32
38
  # @return [Array[Hash]] each Hash represents a workflow step. It will have the following keys:
33
39
  # :workflow, :step, :druid, :lane_id
34
- def stale_queued_workflows(repository, opts = {})
35
- uri_string = build_queued_uri(repository, opts)
40
+ def stale_queued_workflows(*args)
41
+ if args.count == 2
42
+ Deprecation.warn(
43
+ self,
44
+ '`#stale_queued_workflows` only takes one arg: a hash. This will raise an exception in Dor::Workflow::Client 4.0.0'
45
+ )
46
+ args.shift # ditch the `repo` argument
47
+ end
48
+ uri_string = build_queued_uri(args.first)
36
49
  parse_queued_workflows_response requestor.request(uri_string)
37
50
  end
38
51
 
39
52
  # 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
40
- # @param [String] repository name of the repository you want to query, like 'dor' or 'sdr'
53
+ # @param [String] repository -- deprecated, ignored by workflow service
41
54
  # @param [Hash] opts optional values for query
42
55
  # @option opts [Integer] :hours_ago steps older than this value will be returned by the query. If not passed in, the service defaults to 0 hours,
43
56
  # meaning you will get all queued workflows
44
57
  # @return [Integer] number of stale, queued steps if the :count_only option was set to true
45
- def count_stale_queued_workflows(repository, opts = {})
46
- uri_string = build_queued_uri(repository, opts) + '&count-only=true'
58
+ def count_stale_queued_workflows(*args)
59
+ if args.count == 2
60
+ Deprecation.warn(
61
+ self,
62
+ '`#count_stale_queued_workflows` only takes one arg: a hash. This will raise an exception in Dor::Workflow::Client 4.0.0'
63
+ )
64
+ args.shift # ditch the `repo` argument
65
+ end
66
+ uri_string = build_queued_uri(args.first) + '&count-only=true'
47
67
  doc = Nokogiri::XML(requestor.request(uri_string))
48
68
  doc.at_xpath('/objects/@count').value.to_i
49
69
  end
@@ -51,13 +71,11 @@ module Dor
51
71
  # Returns a list of druids from the workflow service that meet the criteria
52
72
  # of the passed in completed and waiting params
53
73
  #
54
- # @param [Array<String>, String] completed An array or single String of the completed steps, should use the qualified format: `repository:workflow:step-name`
74
+ # @param [Array<String>, String] completed An array or single String of the completed steps, should use the qualified format: `workflow:step-name`
55
75
  # @param [String] waiting name of the waiting step
56
- # @param [String] repository default repository to use if it isn't passed in the qualified-step-name
57
76
  # @param [String] workflow default workflow to use if it isn't passed in the qualified-step-name
58
77
  # @param [String] lane_id issue a query for a specific lane_id for the waiting step
59
78
  # @param [Hash] options
60
- # @param options [String] :default_repository repository to query for if not using the qualified format
61
79
  # @param options [String] :default_workflow workflow to query for if not using the qualified format
62
80
  # @option options [Integer] :limit maximum number of druids to return (nil for no limit)
63
81
  # @return [Array<String>] Array of druids
@@ -84,11 +102,12 @@ module Dor
84
102
  # }
85
103
  #
86
104
  def objects_for_workstep(completed, waiting, lane_id = 'default', options = {})
87
- waiting_param = qualify_step(options[:default_repository], options[:default_workflow], waiting)
105
+ Deprecation.warn(self, 'the `:default_repository` option in `#objects_for_workstep` is unused and will go away in Dor::Workflow::Client 4.0.0. omit argument to silence.') if options[:default_repository]
106
+ waiting_param = qualify_step(options[:default_workflow], waiting)
88
107
  uri_string = "workflow_queue?waiting=#{waiting_param}"
89
108
  if completed
90
109
  Array(completed).each do |step|
91
- completed_param = qualify_step(options[:default_repository], options[:default_workflow], step)
110
+ completed_param = qualify_step(options[:default_workflow], step)
92
111
  uri_string += "&completed=#{completed_param}"
93
112
  end
94
113
  end
@@ -107,33 +126,39 @@ module Dor
107
126
  # convert into:
108
127
  # ['druid:ab123de4567', 'druid:ab123de9012']
109
128
  #
110
- result = Nokogiri::XML(resp).xpath('//object[@id]')
111
- result.map { |n| n[:id] }
129
+ Nokogiri::XML(resp).xpath('//object[@id]').map { |n| n[:id] }
112
130
  end
113
131
 
114
132
  # Get a list of druids that have errored out in a particular workflow and step
115
133
  #
116
134
  # @param [String] workflow name
117
135
  # @param [String] step name
118
- # @param [String] repository -- optional, default=dor
136
+ # @param [String] repository -- deprecated, ignored by workflow service
119
137
  #
120
138
  # @return [Hash] hash of results, with key has a druid, and value as the error message
121
139
  # @example
122
140
  # client.errored_objects_for_workstep('accessionWF','content-metadata')
123
141
  # => {"druid:qd556jq0580"=>"druid:qd556jq0580 - Item error; caused by
124
142
  # #<Rubydora::FedoraInvalidRequest: Error modifying datastream contentMetadata for druid:qd556jq0580. See logger for details>"}
125
- def errored_objects_for_workstep(workflow, step, repository = 'dor')
126
- resp = requestor.request "workflow_queue?repository=#{repository}&workflow=#{workflow}&error=#{step}"
127
- result = {}
128
- Nokogiri::XML(resp).xpath('//object').collect do |node|
129
- result.merge!(node['id'] => node['errorMessage'])
130
- end
131
- result
143
+ def errored_objects_for_workstep(workflow, step, repository = nil)
144
+ Deprecation.warn(self, 'the third argument to `#errored_objects_for_workstep` is unused and will go away in Dor::Workflow::Client 4.0.0. omit argument to silence.') unless repository.nil?
145
+ resp = requestor.request "workflow_queue?workflow=#{workflow}&error=#{step}"
146
+ Nokogiri::XML(resp).xpath('//object').map do |node|
147
+ [node['id'], node['errorMessage']]
148
+ end.to_h
132
149
  end
133
150
 
134
151
  # Used by preservation robots stats reporter
135
- def count_objects_in_step(workflow, step, type, repo)
136
- resp = requestor.request "workflow_queue?repository=#{repo}&workflow=#{workflow}&#{type}=#{step}"
152
+ #
153
+ # @param [String] workflow name
154
+ # @param [String] step name
155
+ # @param [String] type
156
+ # @param [String] repo -- deprecated, ignored by workflow service
157
+ #
158
+ # @return [Hash] hash of results, with key has a druid, and value as the error message
159
+ def count_objects_in_step(workflow, step, type, repo = nil)
160
+ Deprecation.warn(self, 'the fourth argument to `#count_objects_in_step` is unused and will go away in Dor::Workflow::Client 4.0.0. omit argument to silence.') unless repo.nil?
161
+ resp = requestor.request "workflow_queue?workflow=#{workflow}&#{type}=#{step}"
137
162
  extract_object_count(resp)
138
163
  end
139
164
 
@@ -141,52 +166,50 @@ module Dor
141
166
  #
142
167
  # @param [String] workflow name
143
168
  # @param [String] step name
144
- # @param [String] repository -- optional, default=dor
169
+ # @param [String] repository -- deprecated, ignored by workflow service
145
170
  #
146
171
  # @return [Integer] Number of objects with this repository:workflow:step that have a status of 'error'
147
- def count_errored_for_workstep(workflow, step, repository = 'dor')
148
- count_objects_in_step(workflow, step, 'error', repository)
172
+ def count_errored_for_workstep(workflow, step, repository = nil)
173
+ Deprecation.warn(self, 'the third argument to `#count_errored_for_workstep` is unused and will go away in Dor::Workflow::Client 4.0.0. omit argument to silence.') unless repository.nil?
174
+ count_objects_in_step(workflow, step, 'error')
149
175
  end
150
176
 
151
177
  # Returns the number of objects that have a status of 'queued' in a particular workflow and step
152
178
  #
153
179
  # @param [String] workflow name
154
180
  # @param [String] step name
155
- # @param [String] repository -- optional, default=dor
181
+ # @param [String] repository -- deprecated, ignored by workflow service
156
182
  #
157
183
  # @return [Integer] Number of objects with this repository:workflow:step that have a status of 'queued'
158
- def count_queued_for_workstep(workflow, step, repository = 'dor')
159
- count_objects_in_step(workflow, step, 'queued', repository)
184
+ def count_queued_for_workstep(workflow, step, repository = nil)
185
+ Deprecation.warn(self, 'the third argument to `#count_queued_for_workstep` is unused and will go away in Dor::Workflow::Client 4.0.0. omit argument to silence.') unless repository.nil?
186
+ count_objects_in_step(workflow, step, 'queued')
160
187
  end
161
188
 
162
189
  private
163
190
 
164
191
  attr_reader :requestor
165
192
 
166
- def build_queued_uri(repository, opts = {})
167
- uri_string = "workflow_queue/all_queued?repository=#{repository}"
168
- uri_string += "&hours-ago=#{opts[:hours_ago]}" if opts[:hours_ago]
169
- uri_string += "&limit=#{opts[:limit]}" if opts[:limit]
170
- uri_string
193
+ def build_queued_uri(opts = {})
194
+ query_hash = opts.slice(:hours_ago, :limit).transform_keys { |key| key.to_s.tr('_', '-') }
195
+ query_string = URI.encode_www_form(query_hash)
196
+ "workflow_queue/all_queued?#{query_string}"
171
197
  end
172
198
 
173
- # Converts repo-workflow-step into repo:workflow:step
174
- # @param [String] default_repository
199
+ # Converts workflow-step into workflow:step
175
200
  # @param [String] default_workflow
176
201
  # @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'
177
- # @return [String] repo:workflow:step
202
+ # @return [String] workflow:step
178
203
  # @example
179
- # dor:assemblyWF:jp2-create
180
- def qualify_step(default_repository, default_workflow, step)
181
- current = step.split(/:/, 3)
182
- current.unshift(default_workflow) if current.length < 3
183
- current.unshift(default_repository) if current.length < 3
204
+ # assemblyWF:jp2-create
205
+ def qualify_step(default_workflow, step)
206
+ current = step.split(':').last(2)
207
+ current.unshift(default_workflow) if current.length < 2
184
208
  current.join(':')
185
209
  end
186
210
 
187
211
  def parse_queued_workflows_response(xml)
188
- doc = Nokogiri::XML(xml)
189
- doc.xpath('/workflows/workflow').collect do |wf_node|
212
+ Nokogiri::XML(xml).xpath('/workflows/workflow').collect do |wf_node|
190
213
  {
191
214
  workflow: wf_node['name'],
192
215
  step: wf_node['process'],