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.
@@ -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
- end
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
- current = []
102
- # only get steps that are part of accessioning and part of the current version. That can mean they were archived with the current version
103
- # number, or they might be active (no version number).
104
- milestones.each do |m|
105
- if STEPS.key?(m[:milestone]) && (m[:version].nil? || m[:version] == version)
106
- current << m unless m[:milestone] == 'registered' && version.to_i > 1
107
- end
108
- end
109
- current
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 formating utc date/time to human readable
113
- # XXX: bad form to hardcode TZ here.
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)
@@ -3,7 +3,7 @@
3
3
  module Dor
4
4
  module Workflow
5
5
  class Client
6
- VERSION = '5.1.0'
6
+ VERSION = '7.0.0'
7
7
  end
8
8
  end
9
9
  end
@@ -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 = all_workflows_xml(pid)
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
@@ -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, :all_workflows_xml, :workflows,
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, :stale_queued_workflows, :count_stale_queued_workflows,
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(mock_logger).to receive(:warn)
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 receive(:warn)
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
- context 'for gv054hp4128' do
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
- context 'for bd504dj1946' do
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 'for an accessioned step with the exact same timestamp as the deposited step' do
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 'for an accessioned step with an ealier timestamp than the deposited step' do
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 'for a deposited step for a non-accessioned object' do
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
- before do
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 'it returns the workflows' do
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