-
Notifications
You must be signed in to change notification settings - Fork 170
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
JSON conditions for conditional policy #814
Conversation
|
||
function _M.evaluate(expression) | ||
local match_attr = ngx.re.match(expression, [[^([\w]+)$]], 'oj') | ||
function _M.evaluate(condition, context) |
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.
cyclomatic complexity of function '_M.evaluate' is too high (8 > 7)
local res = Operation.new('1', 'plain', '!=', '2', 'plain'):evaluate({}) | ||
assert.is_true(res) | ||
|
||
local res = Operation.new('1', 'plain', '!=', '1', 'plain'):evaluate({}) |
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.
variable 'res' was previously defined on line 23
local res = Operation.new('1', 'plain', '==', '1', 'plain'):evaluate({}) | ||
assert.is_true(res) | ||
|
||
local res = Operation.new('1', 'plain', '==', '2', 'plain'):evaluate({}) |
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.
variable 'res' was previously defined on line 15
local res = Operation.new('1', nil, '==', '1', nil):evaluate({}) | ||
assert.is_true(res) | ||
|
||
local res = Operation.new('1', nil, '==', '2', nil):evaluate({}) |
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.
variable 'res' was previously defined on line 31
|
||
assert.is_true(res) | ||
|
||
local res = Operation.new( |
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.
variable 'res' was previously defined on line 41
d445a71
to
6242062
Compare
Start with something simple. We will change if use cases that require more complex operations appear.
27b0d1e
to
8ec6e53
Compare
function _M:evaluate(context) | ||
if #self.operations == 0 then return true end | ||
|
||
if self.combine_op == 'and' then |
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 can be done at initialization time, right?
It would allow for better JIT if there would be no branching in this method.
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.
Right 👍
Fixed.
local left_value = self.templated_left:render(context) | ||
local right_value = self.templated_right:render(context) | ||
|
||
if self.op == '==' then |
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 we can have a dispatch table for operations like:
local operations = {
['=='] = function(left, right) return left == right end
}
And assign the comparator function during initialization for better JIT.
edit: that would allow us to get rid of the supported_ops
variable
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.
Fixed the same way as the one above 👍
local context = { var_1 = '1', var_2 = '2' } | ||
|
||
local res_true = Operation.new( | ||
'{{ var_1 }}', 'liquid', '==', '1', 'plain' |
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.
Are we going to handle types somehow? What if the variable comes from nginx and is actually a number ?
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.
In that example, even if var_1
was a number, the comparison would end up being between 2 strings, because LiquidTemplateString:render()
returns a string.
I included a test that checks this. Also, I added tostring()
in the ==
and !=
operations in Operation
to make this explicit.
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 is great for now 👍 🥇
There might be some details to clarify and improve, but that can be done later during some performance optimization phase.
The only real concern I have is regarding types. If everything is string based we might need to call tostring
on the values coming from nginx like ports.
This new module the Operation module introduced in the previous commits.
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.
🥇 Excellent! As always 👍
Closes #744
This PR defines the conditions supported in the conditional policy. It also includes an engine that evaluates them.
The kinds of conditions supported are very basic (
==
,!=
,and
,or
) and they can be expressed in JSON.The alternative was to define a custom DSL. I implemented a prototype in this branch: https://github.com/3scale/apicast/tree/lpeg-grammar-condition-engine . This approach would allow us to express more complex conditions. However, the implementation is more complicated, and it would tie us to lpeg. The solution implemented in this PR should be enough for our current needs so it looks like a better option.
For now this only support
==
and!=
. We can add more operations likestarts_with
ormatch_exactly
, later. I'd rather agree on the approach and the basics first.