Skip to content

Commit cf60580

Browse files
committed
Better Runtime error detection for bad paths
1 parent f8b256e commit cf60580

File tree

14 files changed

+39
-29
lines changed

14 files changed

+39
-29
lines changed

lib/floe/validation_mixin.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ def runtime_field_error!(field_name, field_value, comment, floe_error: "States.R
2222
raise Floe::ExecutionError.new(self.class.field_error_text(name, field_name, field_value, comment), floe_error)
2323
end
2424

25+
def wrap_runtime_error(field_name, field_value)
26+
yield
27+
rescue Floe::ExecutionError => e
28+
runtime_field_error!(field_name, field_value, e.message)
29+
end
30+
2531
def workflow_state?(field_value, workflow)
2632
workflow.payload["States"] ? workflow.payload["States"].include?(field_value) : true
2733
end
@@ -38,6 +44,8 @@ def parser_error!(name, comment)
3844
raise Floe::InvalidWorkflowError, "#{name} #{comment}"
3945
end
4046

47+
# these are here for State.build!
48+
4149
def missing_field_error!(name, field_name)
4250
parser_error!(name, "does not have required field \"#{field_name}\"")
4351
end

lib/floe/workflow/choice_rule/data.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ def presence_check(context, input)
6868

6969
# path found the variable_value, (so if they said true, return true)
7070
rhs
71-
rescue Floe::PathError
71+
rescue Floe::ExecutionError
7272
# variable_value (path) threw an error
7373
# it was not found (so if they said false, return true)
7474
!rhs
@@ -117,7 +117,7 @@ def compare_value(context, input)
117117
end
118118

119119
def fetch_path(field_name, field_path, context, input)
120-
ret_value = field_path.value(context, input)
120+
ret_value = wrap_runtime_error(field_name, field_path.to_s) { field_path.value(context, input) }
121121
runtime_field_error!(field_name, field_path.to_s, "must point to a #{type}") if type && !correct_type?(ret_value)
122122
ret_value
123123
end

lib/floe/workflow/path.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def value(context, input = {})
3636
results = JsonPath.on(obj, path)
3737
case results.count
3838
when 0
39-
raise Floe::PathError, "Path [#{payload}] references an invalid value"
39+
raise Floe::PathError, "references an invalid value"
4040
when 1
4141
results.first
4242
else

lib/floe/workflow/states/choice.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ def initialize(workflow, name, payload)
1919
end
2020

2121
def finish(context)
22-
input = input_path.value(context, context.input)
23-
output = output_path.value(context, input)
22+
input = wrap_runtime_error("InputPath", input_path.to_s) { input_path.value(context, context.input) }
23+
output = wrap_runtime_error("OutputPath", output_path.to_s) { output_path.value(context, input) }
24+
# For a bad path, throws an ExecutionError
2425
next_state = choices.detect { |choice| choice.true?(context, output) }&.next || default
2526

2627
runtime_field_error!("Default", nil, "not defined and no match found", :floe_error => "States.NoChoiceMatched") if next_state.nil?
2728
context.next_state = next_state
2829
context.output = output
30+
2931
super
3032
end
3133

lib/floe/workflow/states/fail.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module Floe
44
class Workflow
55
module States
66
class Fail < Floe::Workflow::State
7-
attr_reader :cause, :error
7+
attr_reader :cause, :error, :cause_path, :error_path
88

99
def initialize(workflow, name, payload)
1010
super
@@ -21,8 +21,8 @@ def finish(context)
2121
# see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-fail-state.html
2222
# https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html#asl-intrsc-func-generic
2323
context.output = {
24-
"Error" => @error_path ? @error_path.value(context, context.input) : error,
25-
"Cause" => @cause_path ? @cause_path.value(context, context.input) : cause
24+
"Error" => error_path ? wrap_runtime_error("ErrorPath", error_path.to_s) { @error_path.value(context, context.input) } : error,
25+
"Cause" => cause_path ? wrap_runtime_error("CausePath", cause_path.to_s) { @cause_path.value(context, context.input) } : cause
2626
}.compact
2727
super
2828
end

lib/floe/workflow/states/input_output_mixin.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,25 @@ class Workflow
55
module States
66
module InputOutputMixin
77
def process_input(context)
8-
input = input_path.value(context, context.input)
9-
input = parameters.value(context, input) if parameters
8+
input = wrap_runtime_error("InputPath", input_path.to_s) { input_path.value(context, context.input) }
9+
input = wrap_runtime_error("Parameters", parameters.to_s) { parameters.value(context, input) } if parameters
1010
input
1111
end
1212

1313
def process_output(context, results)
1414
return context.input.dup if results.nil?
1515
return if output_path.nil?
1616

17-
results = result_selector.value(context, results) if @result_selector
17+
results = wrap_runtime_error("ResultSelector", @result_selector.to_s) { result_selector.value(context, results) } if @result_selector
1818
if result_path.payload.start_with?("$.Credentials")
19-
credentials = result_path.set(context.credentials, results)["Credentials"]
19+
credentials = wrap_runtime_error("ResultPath", result_path.to_s) { result_path.set(context.credentials, results)["Credentials"] }
2020
context.credentials.merge!(credentials)
2121
output = context.input.dup
2222
else
2323
output = result_path.set(context.input.dup, results)
2424
end
2525

26-
output_path.value(context, output)
26+
wrap_runtime_error("OutputPath", output_path.to_s) { output_path.value(context, output) }
2727
end
2828
end
2929
end

lib/floe/workflow/states/succeed.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ def initialize(workflow, name, payload)
1414
end
1515

1616
def finish(context)
17-
input = input_path.value(context, context.input)
18-
context.output = output_path.value(context, input)
17+
input = wrap_runtime_error("InputPath", input_path.to_s) { input_path.value(context, context.input) }
18+
context.output = wrap_runtime_error("OutputPath", output_path.to_s) { output_path.value(context, input) }
1919
context.next_state = nil
2020

2121
super

lib/floe/workflow/states/task.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ def catch_error!(context, error)
116116
return if catcher.nil?
117117

118118
context.next_state = catcher.next
119-
context.output = catcher.result_path.set(context.input, error)
119+
context.output = wrap_runtime_error("ResultPath", result_path.to_s) { catcher.result_path.set(context.input, error) }
120120
logger.info("Running state: [#{long_name}] with input [#{context.json_input}]...CatchError - next state: [#{context.next_state}] output: [#{context.json_output}]")
121121

122122
true

lib/floe/workflow/states/wait.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def initialize(workflow, name, payload)
2828
def start(context)
2929
super
3030

31-
input = input_path.value(context, context.input)
31+
input = wrap_runtime_error("InputPath", input_path.to_s) { input_path.value(context, context.input) }
3232

3333
wait_until!(
3434
context,
@@ -38,8 +38,8 @@ def start(context)
3838
end
3939

4040
def finish(context)
41-
input = input_path.value(context, context.input)
42-
context.output = output_path.value(context, input)
41+
input = wrap_runtime_error("InputPath", input_path.to_s) { input_path.value(context, context.input) }
42+
context.output = wrap_runtime_error("OutputPath", input_path.to_s) { output_path.value(context, input) }
4343
super
4444
end
4545

spec/workflow/choice_rule_spec.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@
133133
let(:input) { {} }
134134

135135
it "raises an exception" do
136-
expect { subject }.to raise_exception(Floe::PathError, "Path [$.foo] references an invalid value")
136+
expect { subject }.to raise_exception(Floe::ExecutionError, "States.Choice1.Choices.0.Data field \"Variable\" value \"$.foo\" references an invalid value")
137137
end
138138
end
139139

@@ -396,7 +396,7 @@
396396

397397
context "with path not found" do
398398
let(:input) { {"foo" => 2} }
399-
it { expect { subject }.to raise_error(Floe::PathError, "Path [$.bar] references an invalid value") }
399+
it { expect { subject }.to raise_exception(Floe::ExecutionError, "States.Choice1.Choices.0.Data field \"NumericEqualsPath\" value \"$.bar\" references an invalid value") }
400400
end
401401
end
402402

@@ -581,7 +581,7 @@
581581

582582
context "that does not exist" do
583583
let(:input) { {} }
584-
it { expect { subject }.to raise_exception(Floe::PathError, "Path [$.foo] references an invalid value") }
584+
it { expect { subject }.to raise_exception(Floe::ExecutionError, "States.Choice1.Choices.0.Data field \"Variable\" value \"$.foo\" references an invalid value") }
585585
end
586586

587587
context "that references a number" do

0 commit comments

Comments
 (0)