Skip to content

Commit 70b31d3

Browse files
committed
Better Runtime error detection for bad paths
1 parent ced1f19 commit 70b31d3

File tree

17 files changed

+44
-35
lines changed

17 files changed

+44
-35
lines changed

lib/floe/validation_mixin.rb

Lines changed: 6 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

lib/floe/workflow/choice_rule/data.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def true?(context, input)
5151
rhs = compare_value(context, input)
5252
lhs = variable_value(context, input)
5353
send(OPERATIONS[operator], lhs, rhs)
54-
rescue Floe::PathError
54+
rescue Floe::ExecutionError
5555
# For IsPresent, we can expect the lhs to not be present in some cases,
5656
# This throws a PathError. We handle that special case here.
5757
# Example:
@@ -188,7 +188,7 @@ def parse_field(field_name, data_type)
188188

189189
# fetch a path at runtime
190190
def fetch_path(field_name, field_path, context, input)
191-
value = field_path.value(context, input)
191+
value = wrap_runtime_error(field_name, field_path.to_s) { field_path.value(context, input) }
192192
# if this is an operation (GreaterThanPath), ensure the value is the correct type
193193
return value if type.nil? || correct_type?(value, type)
194194

lib/floe/workflow/intrinsic_function/transformer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ def check_types(args, function, signature)
7575

7676
rule(:string => simple(:v)) { v.to_s[1..-2] }
7777
rule(:number => simple(:v)) { v.match(/[eE.]/) ? Float(v) : Integer(v) }
78-
rule(:jsonpath => simple(:v)) { Floe::Workflow::Path.value(v.to_s, context, input) }
78+
rule(:jsonpath => simple(:v)) { Floe::Workflow::Path.value(v.to_s, context, input) } # improve error here
7979

8080
STATES_FORMAT_PLACEHOLDER = /(?<!\\)\{\}/
8181

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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ def initialize(workflow, name, payload)
1818
end
1919

2020
def finish(context)
21-
input = input_path.value(context, context.input)
22-
output = output_path.value(context, input)
21+
input = wrap_runtime_error("InputPath", input_path.to_s) { input_path.value(context, context.input) }
22+
output = wrap_runtime_error("OutputPath", output_path.to_s) { output_path.value(context, input) }
2323
next_state = choices.detect { |choice| choice.true?(context, output) }&.next || default
2424

2525
runtime_field_error!("Default", nil, "not defined and no match found", :floe_error => "States.NoChoiceMatched") if next_state.nil?

lib/floe/workflow/states/fail.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +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.match?(/^\$\$\.Credentials\b/)
19-
context.credentials.merge!(result_path.set(context.credentials, results))
19+
credentials = wrap_runtime_error("ResultPath", result_path.to_s) { result_path.set(context.credentials, results) }
20+
context.credentials.merge!(credentials) if credentials
2021
output = context.input.dup
2122
else
2223
output = result_path.set(context.input.dup, results)
2324
end
2425

25-
output_path.value(context, output)
26+
wrap_runtime_error("OutputPath", output_path.to_s) { output_path.value(context, output) }
2627
end
2728
end
2829
end

lib/floe/workflow/states/map.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def initialize(workflow, name, payload)
4343

4444
def process_input(context)
4545
input = super
46-
input = items_path.value(context, input)
46+
input = wrap_runtime_error("ItemPath", items_path.to_s) { items_path.value(context, input) }
4747
input = item_batcher.value(context, input, context.state["Input"]) if item_batcher
4848
input
4949
end

lib/floe/workflow/states/retry_catch_mixin.rb

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

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

4444
true

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

0 commit comments

Comments
 (0)