Rate Limit policy: allow templating using liquid#719
Conversation
| [200, 200] | ||
|
|
||
| === TEST 17: Liquid templating (jwt.aud). | ||
| Return 429 code. |
There was a problem hiding this comment.
I think this should be more descriptive.
|
@y-tabata Thanks for your contribution! 👍 Can you add a changelog entry, please? Something like Also, could you add some unit tests for this? I see that you added some integration tests which is good 👍 , but we also need the unit ones. You can use the ones we added for the headers policy as an example: #716 |
| use Test::APIcast 'no_plan'; | ||
| use Cwd qw(abs_path); | ||
|
|
||
| $ENV{TEST_NGINX_LUA_PATH} = "$Test::APIcast::spec/?.lua;$ENV{TEST_NGINX_LUA_PATH}"; |
There was a problem hiding this comment.
In order to resolve 'fixtures.rsa'.
I referenced apicast-oidc.t.
There was a problem hiding this comment.
I see. I think we can find a better solution. I think these tests should make use of the fixtures directory in t instead of the one in spec. However, as you said, we're doing the same in apicast-oidc.t. We should fix this, but I think we can do it after this PR.
62e781d to
7cbc4aa
Compare
| end | ||
| limiter.key_name = key_name | ||
| else | ||
| limiter.key_name = limiter.template_string:render(context) |
There was a problem hiding this comment.
Your solution works, but I think there's a simpler way to solve the same problem.
What we want to do is to make ngx.var.uri and ngx.var.host available in the liquid templates.
The regex matching is not necessary. We can achieve our goal by including new keys in the context we pass to template_string:render(context). If that context includes a table with these keys uri = ngx.var.uri and host = ngx.var.host we'll be able to use {{ uri }} and {{ host }} in the config instead of {{ ngx.var.uri }} and {{ ngx.var.host }}.
Another option is to include ngx.var.host in the context that we send to template_string:render(). However, I think that when configuring this from the 3scale UI, users will find it more natural to write {{host}} than to write {{ngx.var.host}}. In theory, they don't even need to know that apicast is based on nginx. I had a discussion with @mikz about this in another PR: #716 (comment)
There was a problem hiding this comment.
If we don't use the regex, we have to have the list like uri = ngx.var.uri or host = ngx.var.host and so on.
The best place is liquid_filters of template_string.lua, isn't it?
Do you have any idea to avoid the "API disabled in the current context" error?
There was a problem hiding this comment.
I don't think liquid_filters in template_string.lua is the best place for this. Those are filters, functions like md5 that allow us to have things like {{ 'a_value' | md5 }} in the policy config.
But uri and host are values, not filters. I think uri and host need to be included in the context passed to render. For that, I think a possible solution is to have a table somewhere with the values we want to have available in the context. As a starting point, we could have something like:
local function context_values()
return {
uri = ngx.var.uri,
host = ngx.var.host,
-- possibly many others
}
end
_M.available_context(policies_context)
-- merge policies_context with context_values and return the result
endMaybe we could include this in the Policy module or even in a new separate module.
If we evaluate the ngx.var* vars in access(), we won't see the API disabled in the current context error.
f63f876 to
bdc32d8
Compare
| end | ||
|
|
||
| function _M.available_context(policies_context) | ||
| for name, value in pairs(context_values()) do |
There was a problem hiding this comment.
pairs method can't be JITed, it is always better to use ipairs and change the data structure.
In this case it is not even needed. What are you doing is mutating the passed policies_context. That is really not expected. It is way better to return a new object.
You don't need to even copy the context table, just use metatable __index property to delegate unknown fields:
return setmetatable(context_values(), { __index = policies_context })|
|
||
| local function interpolate_key_name(context, limiters) | ||
| for _, limiter in ipairs(limiters) do | ||
| limiter.key_name = limiter.template_string:render(context) |
There was a problem hiding this comment.
This mutates the limiter object which is shared across all requests.
You can just pass context to build_limiters_and_keys and use it there without storing it in the shared limiter object.
c0b91f8 to
c1ddc21
Compare
|
Do you have any idea to avoid |
| end | ||
|
|
||
| function _M.available_context(policies_context) | ||
| return setmetatable(policies_context, { __index = context_values() }) |
There was a problem hiding this comment.
This still mutates the context. It should be the other way around. Setting the metatable of context_values.
|
@y-tabata the tests fail because of #719 (comment) |
c1ddc21 to
a31bc40
Compare
|
@mikz thanks! |
Closes #713.