-
Notifications
You must be signed in to change notification settings - Fork 6
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Choice rule payload validation #189
base: master
Are you sure you want to change the base?
Conversation
update:
|
wip: pre-compiling operations |
a0d0c26
to
0993b77
Compare
update:
This is a lot more involved but a lot more strict/stringent update:
|
0993b77
to
07a1411
Compare
rubocop: command line and web have different opinions on whether a regex is freezable. one complains no matter what code I use here. un-wip: this seems to work |
fdab00e
to
e1bce92
Compare
update:
update:
|
Can you add specs around these new classes? wondering if would catch the invalid namespace thing I found. |
e1bce92
to
4ff6108
Compare
update:
I commented out every line there, and the specs fail. So every one of those classes are tested |
Discussion: e.g.:
|
values = COMPARE_RULE.match(key) | ||
return [key, DATA_RULES[values[2]], !!values[3]] if values |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this would read better if the match was decomposed or perhaps if named captures were used instead of using values[2], values[3], etc.
Some other quetsions
- where is the prefix used (the
(String|Numeric|Boolean|Timestamp)
part)? It seems to be ignored here. - The caller does
compare_key, klass = klass_params(payload)
, but this method returns 3 items, so is that a bug? Where did the 3rd param go with the path, and why isn't it being used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the current pattern for the states/command objects to parse the payload themselves.
Here, we need to parse the string to
The old code ignored the first part, so I continued to ignore it. I can see doing type checks or conversions.
returning 2 vs 3 is probably a bug. thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- yes, we ignore the type prefix part and don't currently do data type conversion. (not sure if that is needed)
- changed this code. think all set.
Maybe, but this all feels overly complicated to me personally. I don't see why each of these can't be a simple method (or even just the original code the way it was). Just curious what problem you were trying to solve. I could see creating the classes to encapsulate a |
856fce4
to
1ceb8f4
Compare
sorry - this is for previous commit update:
|
update
|
3a48034
to
893c61d
Compare
update:
just superficial changes. Code hasn't changed |
return presence_check(context, input) if compare_key == "IsPresent" | ||
|
||
lhs = variable_value(context, input) | ||
rhs = compare_value(context, input) | ||
|
||
validate!(lhs) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before:
- fetches a value, and verifies what ever came out is not null
After:
- fetches a value and verifies it existed in the input hash
when "IsTimestamp" then is_timestamp?(lhs) | ||
when "IsNull" then is_null?(lhs, rhs) | ||
when "IsNumeric" then is_numeric?(lhs, rhs) | ||
when "IsString" then is_string?(lhs, rhs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before: "IsString" => "*"
verified it is a string.
After: "IsString" => true
works that way, but "IsString" => false
does the opposite
(across all checks)
def variable_value(context, input) | ||
fetch_path("Variable", variable, context, input) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(moved over from ChoiceRule since this is data specific)
Before:
- convert
@variable
to aPath
at runtime and fetch value.
After: @variable is a
Path`, so bad Path detected at initialization time- Path not present in input is a runtime error
- Data type of input is checked.
def parse_compare_key | ||
@compare_key = payload.keys.detect { |key| key.match?(TYPE_CHECK) || key.match?(OPERATION) } | ||
parser_error!("requires a compare key") unless compare_key | ||
|
||
@type, _operator, @path = OPERATION.match(compare_key)&.captures | ||
# TYPE_CHECK doesn't match this regex, so @path = @type = nil | ||
# @path.nil? means this the compare_value will always be a static value (true or false) | ||
# @type.nil? means we won't type check the variable or compare value | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, this got more complicated.
After:
- Can detect bad compare values at initialization time.
- Can detect bad paths at initialization time.
- Store type checking information (only for operations. bad type in the type check are informational (true false) rather than errors.
lib/floe/workflow/path.rb
Outdated
results.count < 2 ? results.first : results | ||
case results.count | ||
when 0 | ||
raise Floe::PathError, "Path [#{payload}] references an invalid value" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one change has big consequences.
Before: A path pointing to nothing would just treat the value as nil
After: A path pointing to nothing is a runtime error
lib/floe/workflow/state.rb
Outdated
rescue Floe::ExecutionError => e | ||
mark_error(context, e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was detected because tests running against a state would work, but running against a workflow would not.
Before:
- Errors needed to be caught by each
State
- Errors resulted in a state that was not complete.
- Errors did not always get their way into output.
After:
- runtime errors always set the context accordingly.
lib/floe/workflow/state.rb
Outdated
def finish(context) | ||
mark_finished(context) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code for finish
hasn't changed, it just now resides in mark_finished
.
Reasoning: If State
throws an error, it almost always threw it in finish
. When an error is thrown, super.finish
is not called. This means no logging and FinishedTime
is never set -- so the state looks like it is not finished.
Now, we catch errors thrown, and ensure super.finish
is called. Easiest way to do this is to move that code into a separate method -- mark_finish
.
893c61d
to
1dfe07e
Compare
WIP: piece by piece pulling out PRs from this |
1dfe07e
to
b60a866
Compare
b60a866
to
44a8a3e
Compare
1f374b2
to
13a4d7c
Compare
update:
update:
|
13a4d7c
to
a786b40
Compare
Checked commit kbrock@a786b40 with ruby 3.1.5, rubocop 1.56.3, haml-lint 0.51.0, and yamllint |
alt to #187
Depend upon:
[WIP] Catch and display path errors #246Overview
Choice does not quite behave the same as the AWS States Language reference implementation.
If you specify a
Path
that points to nothing or the wrong type, aws raises aStates.Runtime
. In truth, the type checking is not the most consistent, but the missing path is always raised.Changes:
Next
points to a valid state.Variable
and compare key values are the correct data type.Variable
or compare key path values not not found or the wrong data type. It used to throw ruby exceptions."IsPresent": true
to detect values present rather than not null."Is*"
comparisons now supporttrue
andfalse
values.Choice
with noDefault
provided.