ntswf 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -9,6 +9,7 @@ AWS Simple Workflow.
9
9
  [![Gem Version](https://badge.fury.io/rb/ntswf.png)](http://badge.fury.io/rb/ntswf)
10
10
  [![Code Climate](https://codeclimate.com/github/infopark/ntswf.png)](https://codeclimate.com/github/infopark/ntswf)
11
11
  [![Dependency Status](https://gemnasium.com/infopark/ntswf.png)](https://gemnasium.com/infopark/ntswf)
12
+ [![Build Status](https://travis-ci.org/infopark/ntswf.png)](https://travis-ci.org/infopark/ntswf)
12
13
 
13
14
  Usage
14
15
  -----
@@ -69,4 +70,4 @@ See {Ntswf::Utils}
69
70
 
70
71
  License
71
72
  -------
72
- [LPGLv3](http://www.gnu.org/licenses/lgpl-3.0.html)
73
+ [LPGLv3](http://www.gnu.org/licenses/lgpl-3.0.html)
data/lib/ntswf/client.rb CHANGED
@@ -10,12 +10,18 @@ module Ntswf
10
10
  # The options configure the control flow of the task.
11
11
  # Excluding *:execution_id* they will be stored in the *input* argument of the task as JSON.
12
12
  # @param options [Hash] The task's options. Keys with special meaning:
13
- # @option options [String] :execution_id Mandatory workflow ID suffix, allowed IDs are documented at docs.amazonwebservices.com (WorkflowId Property)
14
- # @option options [Numeric] :interval Optional, in seconds. Enforces periodic re-run of the task, even in case of failure
13
+ # @option options [String] :execution_id
14
+ # Mandatory workflow ID suffix, allowed IDs are documented at docs.amazonwebservices.com
15
+ # (WorkflowId Property)
16
+ # @option options [Numeric] :interval
17
+ # Optional, in seconds. Enforces periodic start of
18
+ # new executions, even in case of failure
15
19
  # @option options [String] :name Identifies the kind of task for the executing unit
16
20
  # @option options [Hash] :params Custom task parameters passed on to the executing unit
17
- # @option options [String] :unit The executing unit's key, a corresponding activity task list must be configured
18
- # @option options [Numeric] :version Optional minimum version of the client. The task may be rescheduled by older clients.
21
+ # @option options [String] :unit
22
+ # The executing unit's key, a corresponding activity task list must be configured
23
+ # @option options [Numeric] :version
24
+ # Optional minimum version of the client. The task may be rescheduled by older clients.
19
25
  # @return [AWS::SimpleWorkflow::WorkflowExecution]
20
26
  # @raise [AWS::SimpleWorkflow::Errors::WorkflowExecutionAlreadyStartedFault]
21
27
  def start_execution(options)
@@ -11,7 +11,9 @@ module Ntswf
11
11
  # reason:: reschedule if {RETRY}
12
12
  # result:: Interpreted as {Hash}, see below for keys
13
13
  # Result keys
14
- # :seconds_until_retry:: Planned reschedule after task completion
14
+ # :seconds_until_retry::
15
+ # Planned re-schedule after task completion. Please note that
16
+ # given an *:interval* option the behaviour of this key is undefined
15
17
  def process_decision_task
16
18
  announce("polling for decision task #{decision_task_list}")
17
19
  domain.decision_tasks.poll_for_single_task(decision_task_list) do |task|
@@ -33,17 +35,7 @@ module Ntswf
33
35
  when 'WorkflowExecutionStarted'
34
36
  schedule(task, event)
35
37
  when 'TimerFired'
36
- start_event = task.events.first
37
- keys = [
38
- :child_policy,
39
- :execution_start_to_close_timeout,
40
- :input,
41
- :tag_list,
42
- :task_list,
43
- :task_start_to_close_timeout,
44
- ]
45
- attributes = start_event.attributes.to_h.keep_if {|k| keys.include? k}
46
- task.continue_as_new_workflow_execution(attributes)
38
+ retry_or_continue_as_new(task, task.events.first)
47
39
  when 'ActivityTaskCompleted'
48
40
  result = parse_result(event.attributes.result)
49
41
  start_timer(task, result["seconds_until_retry"]) or task.complete_workflow_execution(
@@ -53,7 +45,7 @@ module Ntswf
53
45
  schedule(task, task.events.first)
54
46
  else
55
47
  start_timer(task) or task.fail_workflow_execution(
56
- event.attributes.to_h.slice(:details, :reason))
48
+ event.attributes.to_h.keep_if {|k| [:details, :reason].include? k})
57
49
  end
58
50
  when 'ActivityTaskTimedOut'
59
51
  notify("Timeout in Simple Workflow. Possible cause: all workers busy",
@@ -72,6 +64,24 @@ module Ntswf
72
64
  interval
73
65
  end
74
66
 
67
+ def retry_or_continue_as_new(task, original_event)
68
+ options = parse_input(original_event.attributes.input)
69
+ if options['interval']
70
+ keys = [
71
+ :child_policy,
72
+ :execution_start_to_close_timeout,
73
+ :input,
74
+ :tag_list,
75
+ :task_list,
76
+ :task_start_to_close_timeout,
77
+ ]
78
+ attributes = original_event.attributes.to_h.keep_if {|k| keys.include? k}
79
+ task.continue_as_new_workflow_execution(attributes)
80
+ else
81
+ schedule(task, original_event)
82
+ end
83
+ end
84
+
75
85
  def schedule(task, data_providing_event)
76
86
  input = data_providing_event.attributes.input
77
87
  options = parse_input(input)
@@ -0,0 +1,214 @@
1
+ require "ntswf"
2
+ require "json"
3
+
4
+ describe Ntswf::DecisionWorker do
5
+ class Worker
6
+ include Ntswf::DecisionWorker
7
+ end
8
+
9
+ let(:atl_config) { { "test" => "atl" } }
10
+ let(:dtl_config) { "dtl" }
11
+ let(:unit) { "testt" }
12
+ let(:config) { { unit: unit, decision_task_list: dtl_config, activity_task_lists: atl_config } }
13
+ let(:worker) { Worker.new config }
14
+
15
+ let(:options) { {} }
16
+ let(:input) { options.merge('params' => {'test' => 'value'}).to_json }
17
+ let(:reason) { nil }
18
+ let(:result) { nil }
19
+ let(:attributes_hash) { { input: input, reason: reason, result: result } }
20
+ let(:attributes) { double("Attributes", attributes_hash.merge(to_h: attributes_hash)) }
21
+ let(:workflow_execution) { double("Workflow Execution", workflow_type: double(name: 'test-wf')) }
22
+ let(:event) do
23
+ double("Event", attributes: attributes, event_type: event_type, workflow_execution:
24
+ workflow_execution)
25
+ end
26
+ let(:task) { double("Task", new_events: [event], events: [event]).as_null_object }
27
+
28
+ before { worker.stub(announce: nil, log: nil, activity_type: "test_activity") }
29
+
30
+ describe "processing a decision task" do
31
+ it "should only query for the configured task list" do
32
+ AWS::SimpleWorkflow::DecisionTaskCollection.any_instance.
33
+ should_receive(:poll_for_single_task).with("dtl")
34
+ worker.process_decision_task
35
+ end
36
+
37
+ describe "handling event" do
38
+ before do
39
+ AWS::SimpleWorkflow::DecisionTaskCollection.any_instance.stub(
40
+ :poll_for_single_task).and_yield(task)
41
+ end
42
+
43
+ describe "ActivityTaskTimedOut" do
44
+ let(:event_type) {"ActivityTaskTimedOut"}
45
+
46
+ it "should cancel the execution" do
47
+ task.should_receive :cancel_workflow_execution
48
+ worker.process_decision_task
49
+ end
50
+
51
+ it "should notify" do
52
+ worker.should_receive :notify
53
+ worker.process_decision_task
54
+ end
55
+ end
56
+
57
+ describe "ActivityTaskCompleted" do
58
+ let(:event_type) {"ActivityTaskCompleted"}
59
+
60
+ context "when requesting re-execution per seconds_until_retry" do
61
+ let(:result) { {seconds_until_retry: 321}.to_json }
62
+
63
+ it "schedules a timer event" do
64
+ task.should_receive(:start_timer).with(321)
65
+ worker.process_decision_task
66
+ end
67
+ end
68
+
69
+ context "when not requesting re-execution" do
70
+ let(:result) { {outcome: "some_data"}.to_json }
71
+
72
+ it "schedules a workflow completed event" do
73
+ task.should_receive(:complete_workflow_execution).with(result: result)
74
+ worker.process_decision_task
75
+ end
76
+ end
77
+ end
78
+
79
+ describe "WorkflowExecutionStarted" do
80
+ let(:event_type) {"WorkflowExecutionStarted"}
81
+
82
+ it "should schedule an activity task avoiding defaults" do
83
+ task.should_receive(:schedule_activity_task).with("test_activity", hash_including(
84
+ heartbeat_timeout: :none,
85
+ input: anything,
86
+ schedule_to_close_timeout: anything,
87
+ schedule_to_start_timeout: anything,
88
+ start_to_close_timeout: anything,
89
+ task_list: "atl",
90
+ ))
91
+ worker.process_decision_task
92
+ end
93
+
94
+ context "given no app in charge" do
95
+ let(:input) { ["legacy_stuff", {}].to_json }
96
+
97
+ it "should schedule an activity task for a guessed task list" do
98
+ task.should_receive(:schedule_activity_task).with("test_activity", hash_including(
99
+ task_list: "atl"))
100
+ worker.process_decision_task
101
+ end
102
+ end
103
+ end
104
+
105
+ describe "ActivityTaskFailed" do
106
+ let(:event_type) {"ActivityTaskFailed"}
107
+
108
+ context "without retry" do
109
+ let(:reason) { "Error" }
110
+
111
+ it "should fail" do
112
+ task.should_receive(:fail_workflow_execution)
113
+ worker.process_decision_task
114
+ end
115
+
116
+ it "should not re-schedule the task" do
117
+ task.should_not_receive(:schedule_activity_task)
118
+ worker.process_decision_task
119
+ end
120
+ end
121
+
122
+ context "with retry" do
123
+ let(:reason) { "Retry" }
124
+
125
+ it "should not fail" do
126
+ task.should_not_receive(:fail_workflow_execution)
127
+ worker.process_decision_task
128
+ end
129
+
130
+ it "should re-schedule the task" do
131
+ task.should_receive(:schedule_activity_task).with("test_activity", hash_including(
132
+ heartbeat_timeout: :none,
133
+ input: input,
134
+ schedule_to_close_timeout: anything,
135
+ schedule_to_start_timeout: anything,
136
+ start_to_close_timeout: anything,
137
+ task_list: "atl",
138
+ ))
139
+ worker.process_decision_task
140
+ end
141
+ end
142
+ end
143
+
144
+ describe "TimerFired" do
145
+ let(:event_type) {"TimerFired"}
146
+ let(:attributes_hash) do
147
+ {
148
+ child_policy: 1,
149
+ execution_start_to_close_timeout: 2,
150
+ input: input,
151
+ tag_list: ["tag"],
152
+ task_list: "list",
153
+ task_start_to_close_timeout: 3,
154
+ }
155
+ end
156
+
157
+ context "given an interval option" do
158
+ let(:options) { {interval: 1234} }
159
+
160
+ it "should continue wiht mandatory attributes" do
161
+ task.should_receive(:continue_as_new_workflow_execution).with(hash_including(
162
+ attributes_hash))
163
+ worker.process_decision_task
164
+ end
165
+ end
166
+
167
+ context "given no interval" do
168
+ it "should re-schedule, assuming seconds_until_retry was set" do
169
+ task.should_receive(:schedule_activity_task).with("test_activity", hash_including(
170
+ heartbeat_timeout: :none,
171
+ input: input,
172
+ schedule_to_close_timeout: anything,
173
+ schedule_to_start_timeout: anything,
174
+ start_to_close_timeout: anything,
175
+ task_list: "atl",
176
+ ))
177
+ worker.process_decision_task
178
+ end
179
+ end
180
+ end
181
+
182
+ context "given an interval" do
183
+ let(:options) { {interval: 1234} }
184
+
185
+ events = %w(
186
+ ActivityTaskCompleted
187
+ ActivityTaskFailed
188
+ ActivityTaskTimedOut
189
+ )
190
+
191
+ events.each do |event|
192
+ describe event do
193
+ let(:event_type) { event }
194
+
195
+ it "should start a timer" do
196
+ task.should_receive(:start_timer).with(1234)
197
+ worker.process_decision_task
198
+ end
199
+ end
200
+ end
201
+
202
+ describe "string options for compatibility" do
203
+ let(:event_type) { "ActivityTaskCompleted" }
204
+ let(:input) { ["interval", {}].to_json }
205
+
206
+ it "should not be interpreted" do
207
+ task.should_not_receive :start_timer
208
+ worker.process_decision_task
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end
214
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ntswf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
4
+ version: 1.0.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-01-22 00:00:00.000000000 Z
12
+ date: 2014-01-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: aws-sdk
@@ -58,6 +58,7 @@ files:
58
58
  - lib/ntswf/client.rb
59
59
  - lib/ntswf/base.rb
60
60
  - lib/ntswf.rb
61
+ - spec/decision_worker_spec.rb
61
62
  - spec/activity_worker_spec.rb
62
63
  homepage: https://github.com/infopark/ntswf
63
64
  licenses:
@@ -85,5 +86,6 @@ signing_key:
85
86
  specification_version: 3
86
87
  summary: Not That Simple Workflow
87
88
  test_files:
89
+ - spec/decision_worker_spec.rb
88
90
  - spec/activity_worker_spec.rb
89
91
  has_rdoc: