Skip to content

Commit

Permalink
feat: add KeyGet3 for support parsing curly bracket (#1050)
Browse files Browse the repository at this point in the history
* feat: add KeyGet3 for support parsing curly bracket

* fix: add keyGet3 to function map
  • Loading branch information
JalinWang committed Jul 13, 2022
1 parent cfc60ff commit 7bd496f
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 1 deletion.
1 change: 1 addition & 0 deletions model/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func LoadFunctionMap() FunctionMap {
fm.AddFunction("keyMatch2", util.KeyMatch2Func)
fm.AddFunction("keyGet2", util.KeyGet2Func)
fm.AddFunction("keyMatch3", util.KeyMatch3Func)
fm.AddFunction("keyGet3", util.KeyGet3Func)
fm.AddFunction("keyMatch4", util.KeyMatch4Func)
fm.AddFunction("keyMatch5", util.KeyMatch5Func)
fm.AddFunction("regexMatch", util.RegexMatchFunc)
Expand Down
36 changes: 36 additions & 0 deletions util/builtin_operators.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,42 @@ func KeyMatch3Func(args ...interface{}) (interface{}, error) {
return bool(KeyMatch3(name1, name2)), nil
}

// KeyGet3 returns value matched pattern
// For example, "project/proj_project1_admin/" matches "project/proj_{project}_admin/"
// if the pathVar == "project", then "project1" will be returned
func KeyGet3(key1, key2 string, pathVar string) string {
key2 = strings.Replace(key2, "/*", "/.*", -1)

re := regexp.MustCompile(`\{[^/]+?\}`) // non-greedy match of `{...}` to support multiple {} in `/.../`
keys := re.FindAllString(key2, -1)
key2 = re.ReplaceAllString(key2, "$1([^/]+?)$2")
key2 = "^" + key2 + "$"
re2 := regexp.MustCompile(key2)
values := re2.FindAllStringSubmatch(key1, -1)
if len(values) == 0 {
return ""
}
for i, key := range keys {
if pathVar == key[1:len(key)-1] {
return values[0][i+1]
}
}
return ""
}

// KeyGet3Func is the wrapper for KeyGet3
func KeyGet3Func(args ...interface{}) (interface{}, error) {
if err := validateVariadicArgs(3, args...); err != nil {
return false, fmt.Errorf("%s: %s", "keyGet3", err)
}

name1 := args[0].(string)
name2 := args[1].(string)
key := args[2].(string)

return KeyGet3(name1, name2, key), nil
}

// KeyMatch4 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
// Besides what KeyMatch3 does, KeyMatch4 can also match repeated patterns:
// "/parent/123/child/123" matches "/parent/{id}/child/{id}"
Expand Down
53 changes: 52 additions & 1 deletion util/builtin_operators_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ func TestKeyGet2(t *testing.T) {

testKeyGet2(t, "/alice/all", "/:/all", "", "")
}

func testKeyMatch3(t *testing.T, key1 string, key2 string, res bool) {
t.Helper()
myRes := KeyMatch3(key1, key2)
Expand All @@ -169,7 +170,7 @@ func testKeyMatch3(t *testing.T, key1 string, key2 string, res bool) {
}

func TestKeyMatch3(t *testing.T) {
// keyMatch3() is similar with KeyMatch2(), except using "/proxy/{id}" instead of "/proxy/:id".
// KeyGet3() is similar with KeyGet2(), except using "/proxy/{id}" instead of "/proxy/:id".
testKeyMatch3(t, "/foo", "/foo", true)
testKeyMatch3(t, "/foo", "/foo*", true)
testKeyMatch3(t, "/foo", "/foo/*", false)
Expand All @@ -195,6 +196,56 @@ func TestKeyMatch3(t *testing.T) {
testKeyMatch3(t, "/myid/using/myresid", "/{id/using/{resId}", false)
}

func testKeyGet3(t *testing.T, key1 string, key2 string, pathVar string, res string) {
t.Helper()
myRes := KeyGet3(key1, key2, pathVar)
t.Logf(`%s < %s: %s = "%s"`, key1, key2, pathVar, myRes)

if myRes != res {
t.Errorf(`%s < %s: %s = "%s" supposed to be "%s"`, key1, key2, pathVar, myRes, res)
}
}

func TestKeyGet3(t *testing.T) {
// keyMatch3() is similar with KeyMatch2(), except using "/proxy/{id}" instead of "/proxy/:id".
testKeyGet2(t, "/foo", "/foo", "id", "")
testKeyGet2(t, "/foo", "/foo*", "id", "")
testKeyGet2(t, "/foo", "/foo/*", "id", "")
testKeyGet2(t, "/foo/bar", "/foo", "id", "")
testKeyGet2(t, "/foo/bar", "/foo*", "id", "")
testKeyGet2(t, "/foo/bar", "/foo/*", "id", "")
testKeyGet2(t, "/foobar", "/foo", "id", "")
testKeyGet2(t, "/foobar", "/foo*", "id", "")
testKeyGet2(t, "/foobar", "/foo/*", "id", "")

testKeyGet3(t, "/", "/{resource}", "resource", "")
testKeyGet3(t, "/resource1", "/{resource}", "resource", "resource1")
testKeyGet3(t, "/myid", "/{id}/using/{resId}", "id", "")
testKeyGet3(t, "/myid/using/myresid", "/{id}/using/{resId}", "id", "myid")
testKeyGet3(t, "/myid/using/myresid", "/{id}/using/{resId}", "resId", "myresid")

testKeyGet3(t, "/proxy/myid", "/proxy/{id}/*", "id", "")
testKeyGet3(t, "/proxy/myid/", "/proxy/{id}/*", "id", "myid")
testKeyGet3(t, "/proxy/myid/res", "/proxy/{id}/*", "id", "myid")
testKeyGet3(t, "/proxy/myid/res/res2", "/proxy/{id}/*", "id", "myid")
testKeyGet3(t, "/proxy/myid/res/res2/res3", "/proxy/{id}/*", "id", "myid")
testKeyGet3(t, "/proxy/", "/proxy/{id}/*", "id", "")

testKeyGet3(t, "/api/group1_group_name/project1_admin/info", "/api/{proj}_admin/info",
"proj", "")
testKeyGet3(t, "/{id/using/myresid", "/{id/using/{resId}", "resId", "myresid")
testKeyGet3(t, "/{id/using/myresid/status}", "/{id/using/{resId}/status}", "resId", "myresid")

testKeyGet3(t, "/proxy/myid/res/res2/res3", "/proxy/{id}/*/{res}", "res", "res3")
testKeyGet3(t, "/api/project1_admin/info", "/api/{proj}_admin/info", "proj", "project1")
testKeyGet3(t, "/api/group1_group_name/project1_admin/info", "/api/{g}_{gn}/{proj}_admin/info",
"g", "group1")
testKeyGet3(t, "/api/group1_group_name/project1_admin/info", "/api/{g}_{gn}/{proj}_admin/info",
"gn", "group_name")
testKeyGet3(t, "/api/group1_group_name/project1_admin/info", "/api/{g}_{gn}/{proj}_admin/info",
"proj", "project1")
}

func testKeyMatch4(t *testing.T, key1 string, key2 string, res bool) {
t.Helper()
myRes := KeyMatch4(key1, key2)
Expand Down

0 comments on commit 7bd496f

Please sign in to comment.