Skip to content

Commit

Permalink
policy/routing: add support for liquid templating
Browse files Browse the repository at this point in the history
  • Loading branch information
davidor committed Jan 10, 2019
1 parent b5da802 commit c4eac99
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 14 deletions.
15 changes: 15 additions & 0 deletions gateway/src/apicast/policy/routing/apicast-policy.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,21 @@
},
"value": {
"type": "string"
},
"value_type": {
"description": "How to evaluate 'type'",
"type": "string",
"default": "plain",
"oneOf": [
{
"enum": ["plain"],
"title": "Evaluate 'value' as plain text."
},
{
"enum": ["liquid"],
"title": "Evaluate 'value' as liquid."
}
]
}
},
"required": [
Expand Down
21 changes: 11 additions & 10 deletions gateway/src/apicast/policy/routing/routing_operation.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,52 @@ local _M = {}

local mt = { __index = _M }

local function new(evaluate_left_side_func, op, value)
local function new(evaluate_left_side_func, op, value, value_type)
local self = setmetatable({}, mt)

self.evaluate_left_side_func = evaluate_left_side_func
self.op = op
self.value = value
self.value_type = value_type

return self
end

function _M.new_op_with_path(op, value)
function _M.new_op_with_path(op, value, value_type)
local eval_left_func = function(request) return request:get_uri() end
return new(eval_left_func, op, value)
return new(eval_left_func, op, value, value_type)
end

function _M.new_op_with_header(header_name, op, value)
function _M.new_op_with_header(header_name, op, value, value_type)
local eval_left_func = function(request)
return request:get_header(header_name)
end

return new(eval_left_func, op, value)
return new(eval_left_func, op, value, value_type)
end

function _M.new_op_with_query_arg(query_arg_name, op, value)
function _M.new_op_with_query_arg(query_arg_name, op, value, value_type)
local eval_left_func = function(request)
return request:get_uri_arg(query_arg_name)
end

return new(eval_left_func, op, value)
return new(eval_left_func, op, value, value_type)
end

function _M.new_op_with_jwt_claim(jwt_claim_name, op, value)
function _M.new_op_with_jwt_claim(jwt_claim_name, op, value, value_type)
local eval_left_func = function(request)
local jwt = request:get_validated_jwt()
return (jwt and jwt[jwt_claim_name]) or nil
end

return new(eval_left_func, op, value)
return new(eval_left_func, op, value, value_type)
end

function _M:evaluate(context)
local left_operand_val = self.evaluate_left_side_func(context.request)

local op = Operation.new(
left_operand_val, 'plain', self.op, self.value, 'plain'
left_operand_val, 'plain', self.op, self.value, self.value_type
)

return op:evaluate(context)
Expand Down
9 changes: 5 additions & 4 deletions gateway/src/apicast/policy/routing/rule.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ local function init_operation(config_operation)
local thing_to_match_val = value_of_thing_to_match(thing_to_match, config_operation)
local op = config_operation.op
local value = config_operation.value
local value_type = config_operation.value_type

if thing_to_match == 'path' then
return RoutingOperation.new_op_with_path(op, value)
return RoutingOperation.new_op_with_path(op, value, value_type)
elseif thing_to_match == 'header' then
return RoutingOperation.new_op_with_header(thing_to_match_val, op, value)
return RoutingOperation.new_op_with_header(thing_to_match_val, op, value, value_type)
elseif thing_to_match == 'query_arg' then
return RoutingOperation.new_op_with_query_arg(thing_to_match_val, op, value)
return RoutingOperation.new_op_with_query_arg(thing_to_match_val, op, value, value_type)
elseif thing_to_match == 'jwt_claim' then
return RoutingOperation.new_op_with_jwt_claim(thing_to_match_val, op, value)
return RoutingOperation.new_op_with_jwt_claim(thing_to_match_val, op, value, value_type)
else
error('Thing to be matched not supported: ' .. thing_to_match)
end
Expand Down
23 changes: 23 additions & 0 deletions spec/policy/routing/routing_operation_spec.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
local RoutingOperation = require('apicast.policy.routing.routing_operation')
local ngx_variable = require 'apicast.policy.ngx_variable'

describe('RoutingOperation', function()
describe('.evaluate', function()
Expand Down Expand Up @@ -213,5 +214,27 @@ describe('RoutingOperation', function()
assert.is_false(operation:evaluate(context))
end)
end)

it('can evaluate the right operand as liquid', function()
-- Stub the available context to avoid depending on ngx.var.*
stub(ngx_variable, 'available_context', function(context) return context end)

local path = '/a_path'

local operation = RoutingOperation.new_op_with_path(
'==', '{{ value_in_liquid_ctx }}', 'liquid'
)

local request_with_matching_path = {
get_uri = function() return path end
}

local context = {
request = request_with_matching_path,
value_in_liquid_ctx = path
}

assert.is_true(operation:evaluate(context))
end)
end)
end)
53 changes: 53 additions & 0 deletions t/apicast-policy-routing.t
Original file line number Diff line number Diff line change
Expand Up @@ -2028,3 +2028,56 @@ yay, api backend
--- error_code: 200
--- no_error_log
[error]
=== TEST 32: conditions with liquid templating
--- configuration
{
"services": [
{
"id": 42,
"proxy": {
"policy_chain": [
{
"name": "apicast.policy.routing",
"configuration": {
"rules": [
{
"url": "http://test:$TEST_NGINX_SERVER_PORT",
"condition": {
"operations": [
{
"thing_to_match": "header",
"header_name": "Service-Id",
"op": "==",
"value": "{{ service.id }}",
"value_type": "liquid"
}
]
}
}
]
}
},
{
"name": "apicast.policy.echo"
}
]
}
}
]
}
--- upstream
location / {
content_by_lua_block {
ngx.say('yay, api backend');
}
}
--- request
GET /
--- more_headers
Service-Id: 42
--- response_body
yay, api backend
--- error_code: 200
--- no_error_log
[error]

0 comments on commit c4eac99

Please sign in to comment.