From 399e18b59a27172b018f173b4d29bbf6b714f0d9 Mon Sep 17 00:00:00 2001 From: Michele Righi Date: Fri, 22 Nov 2024 17:51:53 +0100 Subject: [PATCH] feat: add support for keymatch5 function (#159) * feat: add KeyMatch5 matcher Fixes #153 * test: add test for KeyMatch5 --- src/model/FunctionMap.lua | 3 +- src/util/BuiltInFunctions.lua | 26 +++++++++++++++- tests/util/built_in_functions_spec.lua | 42 ++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/model/FunctionMap.lua b/src/model/FunctionMap.lua index 7931013..570c2a6 100644 --- a/src/model/FunctionMap.lua +++ b/src/model/FunctionMap.lua @@ -23,7 +23,8 @@ local FunctionMap = { ["keyMatch4"] = BuiltInFunctions.keyMatch4Func, ["regexMatch"] = BuiltInFunctions.regexMatchFunc, ["IPMatch"] = BuiltInFunctions.IPMatchFunc, - ["globMatch"] = BuiltInFunctions.globMatch + ["globMatch"] = BuiltInFunctions.globMatch, + ["keyMatch5"] = BuiltInFunctions.keyMatch4Func, } -- FunctionMap provides a set of built in functions diff --git a/src/util/BuiltInFunctions.lua b/src/util/BuiltInFunctions.lua index 06f223d..ee653c4 100644 --- a/src/util/BuiltInFunctions.lua +++ b/src/util/BuiltInFunctions.lua @@ -19,7 +19,7 @@ local BuiltInFunctions = {} function BuiltInFunctions.validateVariadicArgs(expectedLen, args) if #args ~= expectedLen then - return error("Expected"..expectedLen.." arguments, but got "..#args) + return error("Expected "..expectedLen.." arguments, but got "..#args) end for i=1,expectedLen do if type(args[i])~="string" then @@ -247,6 +247,30 @@ function BuiltInFunctions.globMatch(key1, key2) end end +-- Wrapper for keyMatch5 +function BuiltInFunctions.keyMatch5Func(args) + BuiltInFunctions.validateVariadicArgs(2, args) + return BuiltInFunctions.keyMatch5(args[1], args[2]) +end + +-- KeyMatch5 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a * +-- For example, +-- - "/foo/bar?status=1&type=2" matches "/foo/bar" +-- - "/parent/child1" and "/parent/child1" matches "/parent/*" +-- - "/parent/child1?status=1" matches "/parent/*" +function BuiltInFunctions.keyMatch5(key1, key2) + local i = string.find(key1, "?", 1, true) + + if i then + key1 = string.sub(key1, 1, i - 1) + end + + key2 = string.gsub(key2, "/%*", "/.*") + key2 = string.gsub(key2, "%{[^/]+%}", "[^/]+") + + return string.match(key1, "^" .. key2 .. "$") ~= nil +end + -- GenerateGFunction is the factory method of the g(_, _) function. function BuiltInFunctions.generateGFunction(rm) local function f(args) diff --git a/tests/util/built_in_functions_spec.lua b/tests/util/built_in_functions_spec.lua index 2a8781e..c496c91 100644 --- a/tests/util/built_in_functions_spec.lua +++ b/tests/util/built_in_functions_spec.lua @@ -200,4 +200,46 @@ describe("BuiltInFunctions tests", function () assert.is.False(BuiltInFunctions.IPMatch("192.168.2.124", "192.168.2.123")) assert.is.False(BuiltInFunctions.IPMatch("192.166.2.123", "192.168.2.123")) end) + + it("keyMatch5 tests", function () + assert.has_error(function () BuiltInFunctions.keyMatch5Func({"/foo"}) end, "Expected 2 arguments, but got 1") + assert.has_error(function () BuiltInFunctions.keyMatch5Func({"/foo/create/123", "/foo/*", "/foo/update/123"}) end, "Expected 2 arguments, but got 3") + assert.has_error(function () BuiltInFunctions.keyMatch5Func({"/parent/123", true}) end, "Argument must be a string") + + assert.is.True(BuiltInFunctions.keyMatch5("/parent/child?status=1&type=2", "/parent/child")) + assert.is.False(BuiltInFunctions.keyMatch5("/parent?status=1&type=2", "/parent/child")) + + assert.is.True(BuiltInFunctions.keyMatch5("/parent/child/?status=1&type=2", "/parent/child/")) + assert.is.False(BuiltInFunctions.keyMatch5("/parent/child/?status=1&type=2", "/parent/child")) + assert.is.False(BuiltInFunctions.keyMatch5("/parent/child?status=1&type=2", "/parent/child/")) + + assert.is.True(BuiltInFunctions.keyMatch5("/foo", "/foo")) + assert.is.True(BuiltInFunctions.keyMatch5("/foo", "/foo*")) + assert.is.False(BuiltInFunctions.keyMatch5("/foo", "/foo/*")) + assert.is.False(BuiltInFunctions.keyMatch5("/foo/bar", "/foo")) + assert.is.False(BuiltInFunctions.keyMatch5("/foo/bar", "/foo*")) + assert.is.True(BuiltInFunctions.keyMatch5("/foo/bar", "/foo/*")) + assert.is.False(BuiltInFunctions.keyMatch5("/foobar", "/foo")) + assert.is.False(BuiltInFunctions.keyMatch5("/foobar", "/foo*")) + assert.is.False(BuiltInFunctions.keyMatch5("/foobar", "/foo/*")) + + assert.is.False(BuiltInFunctions.keyMatch5("/", "/{resource}")) + assert.is.True(BuiltInFunctions.keyMatch5("/resource1", "/{resource}")) + assert.is.False(BuiltInFunctions.keyMatch5("/myid", "/{id}/using/{resId}")) + assert.is.True(BuiltInFunctions.keyMatch5("/myid/using/myresid", "/{id}/using/{resId}")) + + assert.is.False(BuiltInFunctions.keyMatch5("/proxy/myid", "/proxy/{id}/*")) + assert.is.True(BuiltInFunctions.keyMatch5("/proxy/myid/", "/proxy/{id}/*")) + assert.is.True(BuiltInFunctions.keyMatch5("/proxy/myid/res", "/proxy/{id}/*")) + assert.is.True(BuiltInFunctions.keyMatch5("/proxy/myid/res/res2", "/proxy/{id}/*")) + assert.is.True(BuiltInFunctions.keyMatch5("/proxy/myid/res/res2/res3", "/proxy/{id}/*")) + assert.is.False(BuiltInFunctions.keyMatch5("/proxy/", "/proxy/{id}/*")) + + assert.is.False(BuiltInFunctions.keyMatch5("/proxy/myid?status=1&type=2", "/proxy/{id}/*")) + assert.is.True(BuiltInFunctions.keyMatch5("/proxy/myid/", "/proxy/{id}/*")) + assert.is.True(BuiltInFunctions.keyMatch5("/proxy/myid/res?status=1&type=2", "/proxy/{id}/*")) + assert.is.True(BuiltInFunctions.keyMatch5("/proxy/myid/res/res2?status=1&type=2", "/proxy/{id}/*")) + assert.is.True(BuiltInFunctions.keyMatch5("/proxy/myid/res/res2/res3?status=1&type=2", "/proxy/{id}/*")) + assert.is.False(BuiltInFunctions.keyMatch5("/proxy/", "/proxy/{id}/*")) + end) end) \ No newline at end of file