Bladeren bron

fix(func): object_construct only ignore nil (#2240)

Signed-off-by: Jiyong Huang <huangjy@emqx.io>
ngjaying 1 jaar geleden
bovenliggende
commit
2733fd742e

+ 1 - 30
internal/binder/function/funcs_misc.go

@@ -546,36 +546,7 @@ func registerMiscFunc() {
 		exec:  nil, // directly return in the valuer
 		val:   ValidateNoArg,
 	}
-	builtins["object_construct"] = builtinFunc{
-		fType: ast.FuncTypeScalar,
-		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
-			result := make(map[string]interface{})
-			for i := 0; i < len(args); i += 2 {
-				if args[i+1] != nil {
-					s, err := cast.ToString(args[i], cast.CONVERT_SAMEKIND)
-					if err != nil {
-						return fmt.Errorf("key %v is not a string", args[i]), false
-					}
-					result[s] = args[i+1]
-				}
-			}
-			return result, true
-		},
-		val: func(_ api.FunctionContext, args []ast.Expr) error {
-			if len(args)%2 != 0 {
-				return fmt.Errorf("the args must be key value pairs")
-			}
-			for i, arg := range args {
-				if i%2 == 0 {
-					if ast.IsNumericArg(arg) || ast.IsTimeArg(arg) || ast.IsBooleanArg(arg) {
-						return ProduceErrInfo(i, "string")
-					}
-				}
-			}
-			return nil
-		},
-		check: returnNilIfHasAnyNil,
-	}
+
 	builtins["delay"] = builtinFunc{
 		fType: ast.FuncTypeScalar,
 		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {

+ 0 - 48
internal/binder/function/funcs_misc_test.go

@@ -37,54 +37,6 @@ func init() {
 	testx.InitEnv()
 }
 
-func TestToMap(t *testing.T) {
-	f, ok := builtins["object_construct"]
-	if !ok {
-		t.Fatal("builtin not found")
-	}
-	contextLogger := conf.Log.WithField("rule", "testExec")
-	ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
-	tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
-	fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
-	tests := []struct {
-		args   []interface{}
-		result interface{}
-	}{
-		{ // 0
-			args: []interface{}{
-				"foo",
-				"bar",
-			},
-			result: map[string]interface{}{
-				"foo": "bar",
-			},
-		}, { // 1
-			args: []interface{}{
-				true,
-				"bar",
-			},
-			result: fmt.Errorf("key true is not a string"),
-		}, { // 2
-			args: []interface{}{
-				"key1",
-				"bar",
-				"key2",
-				"foo",
-			},
-			result: map[string]interface{}{
-				"key1": "bar",
-				"key2": "foo",
-			},
-		},
-	}
-	for i, tt := range tests {
-		result, _ := f.exec(fctx, tt.args)
-		if !reflect.DeepEqual(result, tt.result) {
-			t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
-		}
-	}
-}
-
 func TestCoalesceExec(t *testing.T) {
 	f, ok := builtins["coalesce"]
 	if !ok {

+ 29 - 0
internal/binder/function/funcs_obj.go

@@ -159,6 +159,35 @@ func registerObjectFunc() {
 			return ValidateAtLeast(2, len(args))
 		},
 	}
+	builtins["object_construct"] = builtinFunc{
+		fType: ast.FuncTypeScalar,
+		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {
+			result := make(map[string]interface{})
+			for i := 0; i < len(args); i += 2 {
+				if args[i+1] != nil {
+					s, err := cast.ToString(args[i], cast.CONVERT_SAMEKIND)
+					if err != nil {
+						return fmt.Errorf("key %v is not a string", args[i]), false
+					}
+					result[s] = args[i+1]
+				}
+			}
+			return result, true
+		},
+		val: func(_ api.FunctionContext, args []ast.Expr) error {
+			if len(args)%2 != 0 {
+				return fmt.Errorf("the args must be key value pairs")
+			}
+			for i, arg := range args {
+				if i%2 == 0 {
+					if ast.IsNumericArg(arg) || ast.IsTimeArg(arg) || ast.IsBooleanArg(arg) {
+						return ProduceErrInfo(i, "string")
+					}
+				}
+			}
+			return nil
+		},
+	}
 	builtins["erase"] = builtinFunc{
 		fType: ast.FuncTypeScalar,
 		exec: func(ctx api.FunctionContext, args []interface{}) (interface{}, bool) {

+ 104 - 0
internal/binder/function/funcs_obj_test.go

@@ -16,6 +16,7 @@ package function
 
 import (
 	"fmt"
+	"reflect"
 	"sort"
 	"testing"
 
@@ -26,8 +27,111 @@ import (
 	kctx "github.com/lf-edge/ekuiper/internal/topo/context"
 	"github.com/lf-edge/ekuiper/internal/topo/state"
 	"github.com/lf-edge/ekuiper/pkg/api"
+	"github.com/lf-edge/ekuiper/pkg/ast"
 )
 
+func TestToMap(t *testing.T) {
+	f, ok := builtins["object_construct"]
+	if !ok {
+		t.Fatal("builtin not found")
+	}
+	contextLogger := conf.Log.WithField("rule", "testExec")
+	ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
+	tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
+	fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
+	tests := []struct {
+		args   []interface{}
+		result interface{}
+	}{
+		{ // 0
+			args: []interface{}{
+				"foo",
+				"bar",
+			},
+			result: map[string]interface{}{
+				"foo": "bar",
+			},
+		}, { // 1
+			args: []interface{}{
+				true,
+				"bar",
+			},
+			result: fmt.Errorf("key true is not a string"),
+		}, { // 2
+			args: []interface{}{
+				"key1",
+				"bar",
+				"key2",
+				"foo",
+			},
+			result: map[string]interface{}{
+				"key1": "bar",
+				"key2": "foo",
+			},
+		}, { // 3
+			args: []interface{}{
+				"key1",
+				nil,
+				"key2",
+				"foo",
+				"key3",
+				nil,
+			},
+			result: map[string]interface{}{
+				"key2": "foo",
+			},
+		},
+	}
+	for i, tt := range tests {
+		result, _ := f.exec(fctx, tt.args)
+		if !reflect.DeepEqual(result, tt.result) {
+			t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
+		}
+	}
+}
+
+func TestToMapVal(t *testing.T) {
+	f, ok := builtins["object_construct"]
+	if !ok {
+		t.Fatal("builtin not found")
+	}
+	tests := []struct {
+		args []ast.Expr
+		err  error
+	}{
+		{
+			args: []ast.Expr{
+				&ast.StringLiteral{Val: "foo"},
+			},
+			err: fmt.Errorf("the args must be key value pairs"),
+		}, {
+			args: []ast.Expr{
+				&ast.StringLiteral{Val: "foo"},
+				&ast.StringLiteral{Val: "bar"},
+			},
+		}, {
+			args: []ast.Expr{
+				&ast.StringLiteral{Val: "foo"},
+				&ast.StringLiteral{Val: "bar"},
+				&ast.StringLiteral{Val: "baz"},
+			},
+			err: fmt.Errorf("the args must be key value pairs"),
+		}, {
+			args: []ast.Expr{
+				&ast.BooleanLiteral{Val: true},
+				&ast.StringLiteral{Val: "baz"},
+			},
+			err: fmt.Errorf("Expect string type for parameter 1"),
+		},
+	}
+	for i, tt := range tests {
+		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
+			err := f.val(nil, tt.args)
+			assert.Equal(t, tt.err, err)
+		})
+	}
+}
+
 func TestObjectFunctions(t *testing.T) {
 	contextLogger := conf.Log.WithField("rule", "testExec")
 	ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)