dor-workflow-client 5.1.0 → 7.0.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 +4 -4
- data/.circleci/config.yml +1 -1
- data/.rubocop.yml +159 -4
- data/.rubocop_todo.yml +24 -42
- data/Gemfile +11 -0
- data/Gemfile.lock +75 -54
- data/dor-workflow-client.gemspec +1 -9
- data/lib/dor/workflow/client/queues.rb +0 -97
- data/lib/dor/workflow/client/status.rb +41 -41
- data/lib/dor/workflow/client/version.rb +1 -1
- data/lib/dor/workflow/client/workflow_routes.rb +1 -9
- data/lib/dor/workflow/client/workflow_template.rb +0 -9
- data/lib/dor/workflow/client.rb +2 -8
- data/spec/{workflow → dor/workflow}/client/connection_factory_spec.rb +7 -5
- data/spec/{workflow → dor/workflow}/client/status_spec.rb +13 -11
- data/spec/{workflow → dor/workflow}/client/workflow_routes_spec.rb +7 -16
- data/spec/{workflow → dor/workflow}/client/workflow_template_spec.rb +1 -10
- data/spec/{workflow → dor/workflow}/client_spec.rb +38 -185
- data/spec/{models → dor/workflow}/response/process_spec.rb +6 -1
- data/spec/{models → dor/workflow}/response/workflow_spec.rb +7 -0
- data/spec/{models → dor/workflow}/response/workflows_spec.rb +4 -3
- metadata +14 -126
- data/spec/{workflow → dor/workflow}/client/lifecycle_routes_spec.rb +2 -2
- /data/spec/{workflow → dor/workflow}/client/requestor_spec.rb +0 -0
- /data/spec/{workflow → dor/workflow}/client/version_routes_spec.rb +0 -0
@@ -32,6 +32,8 @@ module Dor
|
|
32
32
|
'opened' => 9
|
33
33
|
}.freeze
|
34
34
|
|
35
|
+
attr_reader :status_code
|
36
|
+
|
35
37
|
# @param [String] druid the object identifier
|
36
38
|
# @param [String|Integer] version the version identifier
|
37
39
|
# @param [LifecycleRoutes] lifecycle_routes the lifecycle client
|
@@ -39,40 +41,12 @@ module Dor
|
|
39
41
|
@druid = druid
|
40
42
|
@version = version.to_s
|
41
43
|
@lifecycle_routes = lifecycle_routes
|
42
|
-
|
43
|
-
|
44
|
-
# @return [Hash{Symbol => Object}] including :status_code and :status_time
|
45
|
-
def info
|
46
|
-
@info ||= begin
|
47
|
-
# if we have an accessioned milestone, this is the last possible step and should be the status regardless of time stamp
|
48
|
-
accessioned_milestones = current_milestones.select { |m| m[:milestone] == 'accessioned' }
|
49
|
-
return { status_code: STEPS['accessioned'], status_time: accessioned_milestones.last[:at].utc.xmlschema } unless accessioned_milestones.empty?
|
50
|
-
|
51
|
-
status_code = 0
|
52
|
-
status_time = nil
|
53
|
-
# for each milestone in the current version, see if it comes at the same time or after the current 'last' step, if so, make it the last and record the date/time
|
54
|
-
current_milestones.each do |m|
|
55
|
-
m_name = m[:milestone]
|
56
|
-
m_time = m[:at].utc.xmlschema
|
57
|
-
next unless STEPS.key?(m_name) && (!status_time || m_time >= status_time)
|
58
|
-
|
59
|
-
status_code = STEPS[m_name]
|
60
|
-
status_time = m_time
|
61
|
-
end
|
62
|
-
|
63
|
-
{ status_code: status_code, status_time: status_time }
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def status_code
|
68
|
-
info.fetch(:status_code)
|
44
|
+
@status_code, @status_time = status_from_latest_current_milestone
|
69
45
|
end
|
70
46
|
|
71
47
|
# @param [Boolean] include_time
|
72
48
|
# @return [String] single composed status from status_info
|
73
49
|
def display(include_time: false)
|
74
|
-
status_time = info[:status_time]
|
75
|
-
|
76
50
|
# use the translation table to get the appropriate verbage for the latest step
|
77
51
|
result = "v#{version} #{STATUS_CODE_DISP_TXT[status_code]}"
|
78
52
|
result += " #{format_date(status_time)}" if include_time
|
@@ -89,7 +63,19 @@ module Dor
|
|
89
63
|
|
90
64
|
private
|
91
65
|
|
92
|
-
attr_reader :druid, :version, :lifecycle_routes
|
66
|
+
attr_reader :druid, :version, :lifecycle_routes, :status_time
|
67
|
+
|
68
|
+
def status_from_latest_current_milestone
|
69
|
+
# if we have an accessioned milestone, this is the last possible step and should be the status regardless of timestamp
|
70
|
+
return [STEPS['accessioned'], latest_accessioned_milestone[:at].utc.xmlschema] if currently_accessioned?
|
71
|
+
|
72
|
+
return [0, nil] if latest_current_milestone.nil?
|
73
|
+
|
74
|
+
[
|
75
|
+
STEPS.fetch(latest_current_milestone.fetch(:milestone), 0),
|
76
|
+
latest_current_milestone[:at].utc.xmlschema
|
77
|
+
]
|
78
|
+
end
|
93
79
|
|
94
80
|
# @return [String] text translation of the status code, minus any trailing parenthetical explanation
|
95
81
|
# e.g. 'In accessioning (described)' and 'In accessioning (described, published)' both return 'In accessioning'
|
@@ -98,19 +84,33 @@ module Dor
|
|
98
84
|
end
|
99
85
|
|
100
86
|
def current_milestones
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
87
|
+
milestones
|
88
|
+
# milestone name must be in list of known steps
|
89
|
+
.select { |m| STEPS.key?(m[:milestone]) }
|
90
|
+
# registered milestone is only valid for v1
|
91
|
+
.reject { |m| m[:milestone] == 'registered' && version.to_i > 1 }
|
92
|
+
# Two possible ways the version can indicate the milestone is part of the current version:
|
93
|
+
# if m[:version] is nil, then the milestone is active (version 0 becoming version 1)
|
94
|
+
# if m[:version] is matches the current version, then the milestone is archived with the current version
|
95
|
+
.select { |m| m[:version].nil? || m[:version] == version }
|
96
|
+
end
|
97
|
+
|
98
|
+
def latest_current_milestone
|
99
|
+
current_milestones.max_by { |m| m[:at].utc.xmlschema }
|
100
|
+
end
|
101
|
+
|
102
|
+
def currently_accessioned?
|
103
|
+
current_milestones.any? { |m| m[:milestone] == 'accessioned' }
|
104
|
+
end
|
105
|
+
|
106
|
+
def latest_accessioned_milestone
|
107
|
+
current_milestones
|
108
|
+
.select { |m| m[:milestone] == 'accessioned' }
|
109
|
+
.max_by { |m| m[:at].utc.xmlschema }
|
110
110
|
end
|
111
111
|
|
112
|
-
# handles
|
113
|
-
#
|
112
|
+
# handles formatting UTC date/time to human readable
|
113
|
+
# TODO: bad form to hardcode TZ here.
|
114
114
|
def format_date(datetime)
|
115
115
|
d =
|
116
116
|
if datetime.is_a?(Time)
|
@@ -98,19 +98,11 @@ module Dor
|
|
98
98
|
Workflow::Response::Update.new(json: response)
|
99
99
|
end
|
100
100
|
|
101
|
-
#
|
102
|
-
# Retrieves the raw XML for all the workflows for the the given object
|
103
|
-
# @param [String] druid The id of the object
|
104
|
-
# @return [String] XML of the workflow
|
105
|
-
def all_workflows_xml(druid)
|
106
|
-
requestor.request "objects/#{druid}/workflows"
|
107
|
-
end
|
108
|
-
|
109
101
|
# Retrieves all workflows for the given object
|
110
102
|
# @param [String] pid The id of the object
|
111
103
|
# @return [Workflow::Response::Workflows]
|
112
104
|
def all_workflows(pid:)
|
113
|
-
xml =
|
105
|
+
xml = requestor.request "objects/#{pid}/workflows"
|
114
106
|
Workflow::Response::Workflows.new(xml: xml)
|
115
107
|
end
|
116
108
|
|
@@ -21,15 +21,6 @@ module Dor
|
|
21
21
|
JSON.parse(body)
|
22
22
|
end
|
23
23
|
|
24
|
-
# Retrieves a list of workflow template name
|
25
|
-
#
|
26
|
-
# @return [Array<String>] the list of templates
|
27
|
-
#
|
28
|
-
def all
|
29
|
-
body = requestor.request 'workflow_templates'
|
30
|
-
JSON.parse(body)
|
31
|
-
end
|
32
|
-
|
33
24
|
private
|
34
25
|
|
35
26
|
attr_reader :requestor
|
data/lib/dor/workflow/client.rb
CHANGED
@@ -40,15 +40,13 @@ module Dor
|
|
40
40
|
@requestor = Requestor.new(connection: connection || ConnectionFactory.build_connection(url, timeout: timeout, logger: logger))
|
41
41
|
end
|
42
42
|
|
43
|
-
delegate :create_workflow_by_name, :workflow_status, :
|
43
|
+
delegate :create_workflow_by_name, :workflow_status, :workflows,
|
44
44
|
:workflow, :process, :delete_workflow, :delete_all_workflows, :update_status, :update_error_status,
|
45
45
|
to: :workflow_routes
|
46
46
|
|
47
47
|
delegate :lifecycle, :active_lifecycle, :milestones, to: :lifecycle_routes
|
48
48
|
|
49
|
-
delegate :lane_ids, :
|
50
|
-
:objects_for_workstep, :errored_objects_for_workstep, :count_objects_in_step,
|
51
|
-
:count_errored_for_workstep, :count_queued_for_workstep,
|
49
|
+
delegate :lane_ids, :objects_for_workstep,
|
52
50
|
to: :queues
|
53
51
|
|
54
52
|
delegate :close_version, to: :version_routes
|
@@ -73,10 +71,6 @@ module Dor
|
|
73
71
|
templates.retrieve(name)
|
74
72
|
end
|
75
73
|
|
76
|
-
def workflow_templates
|
77
|
-
templates.all
|
78
|
-
end
|
79
|
-
|
80
74
|
def templates
|
81
75
|
WorkflowTemplate.new(requestor: requestor)
|
82
76
|
end
|
@@ -5,24 +5,26 @@ require 'spec_helper'
|
|
5
5
|
# This test can take up to 15s to run because it does retries with exponential backoff
|
6
6
|
RSpec.describe Dor::Workflow::Client::ConnectionFactory do
|
7
7
|
let(:mock_logger) { double('Logger', info: true, debug: true, warn: true) }
|
8
|
+
let(:client) { Dor::Workflow::Client.new url: 'http://example.com', logger: mock_logger }
|
8
9
|
|
9
10
|
let(:druid) { 'druid:123' }
|
10
11
|
let(:request_url) { "http://example.com/objects/#{druid}/workflows/httpException?lane-id=default&version=1" }
|
12
|
+
|
11
13
|
before do
|
12
14
|
stub_request(:post, request_url)
|
13
15
|
.to_return(status: 500, body: 'Internal error', headers: {})
|
16
|
+
allow(mock_logger).to receive(:warn)
|
14
17
|
end
|
15
18
|
|
16
|
-
let(:client) { Dor::Workflow::Client.new url: 'http://example.com', logger: mock_logger }
|
17
|
-
|
18
19
|
describe '#create_workflow_by_name' do
|
19
20
|
subject(:request) { client.create_workflow_by_name(druid, 'httpException', version: '1') }
|
21
|
+
|
20
22
|
it 'logs an error and retry upon a targeted Faraday exception' do
|
21
|
-
expect
|
23
|
+
expect { request }.to raise_error Dor::WorkflowException
|
24
|
+
expect(mock_logger).to have_received(:warn)
|
22
25
|
.with("retrying connection (1) to #{request_url}: (Faraday::RetriableResponse) 500")
|
23
|
-
expect(mock_logger).to
|
26
|
+
expect(mock_logger).to have_received(:warn)
|
24
27
|
.with("retrying connection (2) to #{request_url}: (Faraday::RetriableResponse) 500")
|
25
|
-
expect { request }.to raise_error Dor::WorkflowException
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
@@ -6,6 +6,7 @@ RSpec.describe Dor::Workflow::Client::Status do
|
|
6
6
|
subject(:instance) do
|
7
7
|
described_class.new(druid: druid, version: version, lifecycle_routes: lifecycle_routes)
|
8
8
|
end
|
9
|
+
|
9
10
|
let(:druid) { 'druid:ab123cd4567' }
|
10
11
|
let(:version) { '2' }
|
11
12
|
let(:lifecycle_routes) { Dor::Workflow::Client::LifecycleRoutes.new(requestor: requestor) }
|
@@ -14,7 +15,7 @@ RSpec.describe Dor::Workflow::Client::Status do
|
|
14
15
|
describe '#display' do
|
15
16
|
subject(:status) { instance.display }
|
16
17
|
|
17
|
-
|
18
|
+
describe 'for gv054hp4128' do
|
18
19
|
context 'when current version is published, but does not have a version attribute' do
|
19
20
|
let(:xml) do
|
20
21
|
'<?xml version="1.0" encoding="UTF-8"?>
|
@@ -51,7 +52,7 @@ RSpec.describe Dor::Workflow::Client::Status do
|
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
54
|
-
|
55
|
+
describe 'for bd504dj1946' do
|
55
56
|
let(:xml) do
|
56
57
|
'<?xml version="1.0"?>
|
57
58
|
<lifecycle objectId="druid:bd504dj1946">
|
@@ -100,7 +101,9 @@ RSpec.describe Dor::Workflow::Client::Status do
|
|
100
101
|
end
|
101
102
|
end
|
102
103
|
|
103
|
-
context '
|
104
|
+
context 'with an accessioned step with the exact same timestamp as the deposited step' do
|
105
|
+
subject(:status) { instance.display(include_time: true) }
|
106
|
+
|
104
107
|
let(:xml) do
|
105
108
|
'<?xml version="1.0"?>
|
106
109
|
<lifecycle objectId="druid:bd504dj1946">
|
@@ -120,14 +123,14 @@ RSpec.describe Dor::Workflow::Client::Status do
|
|
120
123
|
</lifecycle>'
|
121
124
|
end
|
122
125
|
|
123
|
-
subject(:status) { instance.display(include_time: true) }
|
124
|
-
|
125
126
|
it 'has the correct status of accessioned (v2) object' do
|
126
127
|
expect(status).to eq('v2 Accessioned 2013-10-01 07:10PM')
|
127
128
|
end
|
128
129
|
end
|
129
130
|
|
130
|
-
context '
|
131
|
+
context 'with an accessioned step with an ealier timestamp than the deposited step' do
|
132
|
+
subject(:status) { instance.display(include_time: true) }
|
133
|
+
|
131
134
|
let(:xml) do
|
132
135
|
'<?xml version="1.0"?>
|
133
136
|
<lifecycle objectId="druid:bd504dj1946">
|
@@ -147,14 +150,14 @@ RSpec.describe Dor::Workflow::Client::Status do
|
|
147
150
|
</lifecycle>'
|
148
151
|
end
|
149
152
|
|
150
|
-
subject(:status) { instance.display(include_time: true) }
|
151
|
-
|
152
153
|
it 'has the correct status of accessioned (v2) object' do
|
153
154
|
expect(status).to eq('v2 Accessioned 2013-09-01 07:10PM')
|
154
155
|
end
|
155
156
|
end
|
156
157
|
|
157
|
-
context '
|
158
|
+
context 'with a deposited step for a non-accessioned object' do
|
159
|
+
subject(:status) { instance.display(include_time: true) }
|
160
|
+
|
158
161
|
let(:xml) do
|
159
162
|
'<?xml version="1.0"?>
|
160
163
|
<lifecycle objectId="druid:bd504dj1946">
|
@@ -173,8 +176,6 @@ RSpec.describe Dor::Workflow::Client::Status do
|
|
173
176
|
</lifecycle>'
|
174
177
|
end
|
175
178
|
|
176
|
-
subject(:status) { instance.display(include_time: true) }
|
177
|
-
|
178
179
|
it 'has the correct status of deposited (v2) object' do
|
179
180
|
expect(status).to eq('v2 In accessioning (described, published, deposited) 2013-10-01 07:10PM')
|
180
181
|
end
|
@@ -183,6 +184,7 @@ RSpec.describe Dor::Workflow::Client::Status do
|
|
183
184
|
|
184
185
|
describe '#display_simplified' do
|
185
186
|
subject(:status) { instance.display_simplified }
|
187
|
+
|
186
188
|
let(:xml) do
|
187
189
|
'<?xml version="1.0" encoding="UTF-8"?>
|
188
190
|
<lifecycle objectId="druid:gv054hp4128">
|
@@ -86,6 +86,7 @@ RSpec.describe Dor::Workflow::Client::WorkflowRoutes do
|
|
86
86
|
before do
|
87
87
|
allow(mock_requestor).to receive(:request).and_raise(Dor::WorkflowException, 'status 400')
|
88
88
|
end
|
89
|
+
|
89
90
|
it 'raises an exception' do
|
90
91
|
expect { routes.update_status(druid: druid, workflow: 'errorWF', process: 'registrar-approval', status: 'completed') }.to raise_error(Dor::WorkflowException, /status 400/)
|
91
92
|
end
|
@@ -128,6 +129,7 @@ RSpec.describe Dor::Workflow::Client::WorkflowRoutes do
|
|
128
129
|
before do
|
129
130
|
allow(mock_requestor).to receive(:request).and_raise(Dor::WorkflowException, 'status 400')
|
130
131
|
end
|
132
|
+
|
131
133
|
it 'raises an exception' do
|
132
134
|
expect { routes.update_error_status(druid: druid, workflow: 'errorWF', process: 'registrar-approval', error_msg: 'broken') }.to raise_error(Dor::WorkflowException, /status 400/)
|
133
135
|
end
|
@@ -138,19 +140,7 @@ RSpec.describe Dor::Workflow::Client::WorkflowRoutes do
|
|
138
140
|
subject(:delete_all_workflows) do
|
139
141
|
routes.delete_all_workflows(pid: 'druid:mw971zk1113')
|
140
142
|
end
|
141
|
-
let(:mock_requestor) { instance_double(Dor::Workflow::Client::Requestor, request: nil) }
|
142
143
|
|
143
|
-
it 'sends a delete request' do
|
144
|
-
delete_all_workflows
|
145
|
-
expect(mock_requestor).to have_received(:request)
|
146
|
-
.with('objects/druid:mw971zk1113/workflows', 'delete')
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
describe '#delete_all_workflows' do
|
151
|
-
subject(:delete_all_workflows) do
|
152
|
-
routes.delete_all_workflows(pid: 'druid:mw971zk1113')
|
153
|
-
end
|
154
144
|
let(:mock_requestor) { instance_double(Dor::Workflow::Client::Requestor, request: nil) }
|
155
145
|
|
156
146
|
it 'sends a delete request' do
|
@@ -171,15 +161,16 @@ RSpec.describe Dor::Workflow::Client::WorkflowRoutes do
|
|
171
161
|
XML
|
172
162
|
end
|
173
163
|
|
174
|
-
|
175
|
-
allow(routes).to receive(:all_workflows_xml) { xml }
|
176
|
-
end
|
164
|
+
let(:mock_requestor) { instance_double(Dor::Workflow::Client::Requestor, request: xml) }
|
177
165
|
|
178
|
-
it '
|
166
|
+
it 'returns the workflows' do
|
179
167
|
expect(routes.all_workflows(pid: 'druid:mw971zk1113')).to be_kind_of Dor::Workflow::Response::Workflows
|
180
168
|
end
|
181
169
|
end
|
182
170
|
|
183
171
|
describe '#create_workflow_by_name' do
|
172
|
+
it 'need to write these specs', skip: 'need to write specs' do
|
173
|
+
# something
|
174
|
+
end
|
184
175
|
end
|
185
176
|
end
|
@@ -9,6 +9,7 @@ RSpec.describe Dor::Workflow::Client::WorkflowTemplate do
|
|
9
9
|
|
10
10
|
describe '#retrieve' do
|
11
11
|
subject(:workflow_template) { routes.retrieve('accessionWF') }
|
12
|
+
|
12
13
|
let(:data) { '{"processes":[{"name":"start-assembly"},{"name":"content-metadata-create"}]}' }
|
13
14
|
|
14
15
|
it 'returns a workflow template' do
|
@@ -17,14 +18,4 @@ RSpec.describe Dor::Workflow::Client::WorkflowTemplate do
|
|
17
18
|
expect(mock_requestor).to have_received(:request).with('workflow_templates/accessionWF')
|
18
19
|
end
|
19
20
|
end
|
20
|
-
|
21
|
-
describe '#all' do
|
22
|
-
subject(:workflow_templates) { routes.all }
|
23
|
-
let(:data) { '["assemblyWF","registrationWF"]' }
|
24
|
-
|
25
|
-
it 'returns a list of templates' do
|
26
|
-
expect(workflow_templates).to eq %w[assemblyWF registrationWF]
|
27
|
-
expect(mock_requestor).to have_received(:request).with('workflow_templates')
|
28
|
-
end
|
29
|
-
end
|
30
21
|
end
|