ntswf 1.0.5 → 1.0.6

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