diff --git a/perxis-proto b/perxis-proto index b0b20c34b7b106bf6704a78fdaf8b808459bda14..81c967842f55811b459e455572703631712d7d86 160000 --- a/perxis-proto +++ b/perxis-proto @@ -1 +1 @@ -Subproject commit b0b20c34b7b106bf6704a78fdaf8b808459bda14 +Subproject commit 81c967842f55811b459e455572703631712d7d86 diff --git a/pkg/expr/expr.go b/pkg/expr/expr.go index 9c788e71e108e85b483ec71a8700b7fc3bb046be..93d6ee612968ac0a7e1c963a05cda4955532f110 100644 --- a/pkg/expr/expr.go +++ b/pkg/expr/expr.go @@ -1,12 +1,21 @@ package expr import ( + "regexp" + "strings" + + "git.perx.ru/perxis/perxis-go/pkg/data" compiler2 "github.com/antonmedv/expr/compiler" "github.com/antonmedv/expr/parser" "github.com/antonmedv/expr/vm" "golang.org/x/net/context" ) +var ( + additionalFunctions = []string{"contains", "startsWith", "endsWith", "and", "or", "in", "not"} + isExpression = regexp.MustCompile(`[()}{<>=|&%*+\-\/\]\[\\]`).MatchString +) + const EnvContextKey = "$context" func Eval(ctx context.Context, input string, env map[string]interface{}) (interface{}, error) { @@ -56,3 +65,17 @@ func EvalKV(ctx context.Context, input string, kv ...interface{}) (interface{}, return Eval(ctx, input, m) } + +func IsExpression(input string) bool { + if isExpression(input) { + return true + } + + for _, s := range strings.Fields(input) { + if data.Contains(s, additionalFunctions) { + return true + } + } + + return false +} diff --git a/pkg/expr/expr_test.go b/pkg/expr/expr_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5eafc368bed934e0bd0805f4818442a75256931c --- /dev/null +++ b/pkg/expr/expr_test.go @@ -0,0 +1,51 @@ +package expr + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestIsExpression(t *testing.T) { + now := time.Now() + + tests := []struct { + name string + eval string + want bool + }{ + {"equal", "i == 3", true}, + {"in array", "i in [1,2,3]", true}, + {"contains", "value contains 'some'", true}, + {"contains with . + () $ {} ^", "value contains 'something with . + () $ {} ^'", true}, + {"startsWith", "value startsWith 'some'", true}, + {"startsWith . + () $ {} ^", "value startsWith '. + () $ {} ^'", true}, + {"endsWith", "value endsWith 'some'", true}, + {"endsWith . + () $ {} ^", "value endsWith '. + () $ {} ^'", true}, + {"icontains", "icontains(value, 'some')", true}, + {"icontains with . + () $ {} ^", "icontains (value, 'something with . + () $ {} ^')", true}, + {"istartsWith", "istartsWith(value, 'Some')", true}, + {"istartsWith . + () $ {} ^ . + () $ {} ^", "istartsWith(value, '. + () $ {} ^')", true}, + {"iendsWith", "iendsWith(value, 'some')", true}, + {"iendsWith . + () $ {} ^", "iendsWith(value,'. + () $ {} ^')", true}, + {"or", "i == 2 || i > 10", true}, + {"search", "search('some') || i > 10", true}, + {"vars:or", "i == a + 2 || i > a + 10", true}, + {"near", "near(a, [55.5, 37.5], 1000)", true}, + {"within", "within(a, 'box', [[54.54, 36.36], [55.55, 37.37]])", true}, + {"time", "d > Time.Date('2021-08-31')", true}, + {"time", fmt.Sprintf("d > Time.Time('%s')", now.Format(time.RFC3339)), true}, + {"in", "In(s, [1,2,3])", true}, + {"in", "In(s, 1)", true}, + {"text search or id", "id", false}, + {"numbers", "3", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := IsExpression(tt.eval) + assert.Equal(t, tt.want, got) + }) + } +}