Skip to content

Commit

Permalink
Choice state detect data type errors
Browse files Browse the repository at this point in the history
  • Loading branch information
kbrock committed Aug 23, 2024
1 parent 6983db1 commit 9696565
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 13 deletions.
30 changes: 24 additions & 6 deletions lib/floe/workflow/choice_rule/data.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ module Floe
class Workflow
class ChoiceRule
class Data < Floe::Workflow::ChoiceRule
TYPES = ["String", "Numeric", "Boolean", "Timestamp", "Present", "Null"].freeze
TYPES = {"String" => :is_string?, "Numeric" => :is_numeric?, "Boolean" => :is_boolean?, "Timestamp" => :is_timestamp?, "Present" => :is_present?, "Null" => :is_null?}.freeze
COMPARES = ["Equals", "LessThan", "GreaterThan", "LessThanEquals", "GreaterThanEquals", "Matches"].freeze
# e.g.: (Is)(String), (Is)(Present)
TYPE_CHECK = /^(Is)(#{TYPES.join("|")})$/.freeze
TYPE_CHECK = /^(Is)(#{TYPES.keys.join("|")})$/.freeze
# e.g.: (String)(LessThan)(Path), (Numeric)(GreaterThanEquals)()
OPERATION = /^(#{(TYPES - %w[Null Present]).join("|")})(#{COMPARES.join("|")})(Path)?$/.freeze
OPERATION = /^(#{(TYPES.keys - %w[Null Present]).join("|")})(#{COMPARES.join("|")})(Path)?$/.freeze

attr_reader :variable, :compare_key, :type, :compare_predicate, :path

Expand Down Expand Up @@ -135,18 +135,18 @@ def parse_compare_key(payload)
# parse predicate at initilization time
# @return the right predicate attached to the compare key
def parse_predicate(payload)
path ? parse_path(compare_key, payload) : payload[compare_key]
path ? parse_path(compare_key, payload) : parse_value(compare_key, payload)
end

# @return right hand predicate - input path or static payload value)
def compare_value(context, input)
path ? compare_predicate.value(context, input) : compare_predicate
path ? fetch_path(compare_key, compare_predicate, context, input) : compare_predicate
end

# feth the variable value at runtime
# @return variable value (left hand side )
def variable_value(context, input)
variable.value(context, input)
fetch_path("Variable", variable, context, input)
end

# parse path at initilization time
Expand All @@ -156,6 +156,24 @@ def parse_path(field_name, payload)
missing_field_error!(field_name) unless value
wrap_parser_error(field_name, value) { Path.new(value) }
end

def parse_value(field_name, payload)
value = payload[field_name]
invalid_field_error!(field_name, value, "required to be a #{type || "Boolean"}") unless correct_type?(value)
value
end

# fetch a path at runtime
# @ the value at a path
def fetch_path(field_name, field_path, context, input)
ret_value = field_path.value(context, input)
runtime_field_error!(field_name, field_path.to_s, "required to point to a #{type}") if type && !correct_type?(ret_value)
ret_value
end

def correct_type?(val)
send(TYPES[type || "Boolean"], val)
end
end
end
end
Expand Down
41 changes: 34 additions & 7 deletions spec/workflow/choice_rule_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,13 @@
end
end

context "with an invalid predicate" do
let(:choices) { [{"Variable" => "$.foo", "IsNull" => 5, "Next" => "FirstMatchState"}] }
it "fails" do
expect { subject }.to raise_exception(Floe::InvalidWorkflowError, "States.Choice1.Choices.0.Data field \"IsNull\" value \"5\" required to be a Boolean")
end
end

context "with IsNull" do
let(:predicate) { true }
let(:choices) { [{"Variable" => "$.foo", "IsNull" => predicate, "Next" => "FirstMatchState"}] }
Expand Down Expand Up @@ -384,6 +391,16 @@
end
end

context "with wrong match type on the left" do
let(:input) { {"foo" => "abc", "bar" => 2} }
it { expect { subject }.to raise_exception(Floe::ExecutionError, "States.Choice1.Choices.0.Data field \"Variable\" value \"$.foo\" required to point to a Numeric") }
end

context "with wrong match type on the right" do
let(:input) { {"foo" => 2, "bar" => "xyz"} }
it { expect { subject }.to raise_exception(Floe::ExecutionError, "States.Choice1.Choices.0.Data field \"NumericEqualsPath\" value \"$.bar\" required to point to a Numeric") }
end

context "with path not found" do
let(:input) { {"foo" => 2} }
it { expect { subject }.to raise_error(Floe::PathError, "Path [$.bar] references an invalid value") }
Expand Down Expand Up @@ -468,6 +485,12 @@
expect(subject).to eq(false)
end
end

context "with invalid Path" do
let(:choices) { [{"Variable" => "$.foo", "NumericGreaterThanPath" => "bogus", "Next" => "FirstMatchState"}] }
let(:input) { {} }
it { expect { subject }.to raise_exception(Floe::InvalidWorkflowError, "States.Choice1.Choices.0.Data field \"NumericGreaterThanPath\" value \"bogus\" Path [bogus] must start with \"$\"") }
end
end

context "with a NumericLessThanEquals" do
Expand Down Expand Up @@ -555,18 +578,22 @@

context "that is true" do
let(:input) { {"foo" => "audit.log"} }

it "returns true" do
expect(subject).to eq(true)
end
it { expect(subject).to eq(true) }
end

context "that is false" do
let(:input) { {"foo" => "audit"} }
it { expect(subject).to eq(false) }
end

it "returns false" do
expect(subject).to eq(false)
end
context "that does not exist" do
let(:input) { {} }
it { expect { subject }.to raise_exception(Floe::PathError, "Path [$.foo] references an invalid value") }
end

context "that references a number" do
let(:input) { {"foo" => 5} }
it { expect { subject }.to raise_exception(Floe::ExecutionError, "States.Choice1.Choices.0.Data field \"Variable\" value \"$.foo\" required to point to a String") }
end
end
end
Expand Down

0 comments on commit 9696565

Please sign in to comment.