From f4de8b17dec4df23ddce1743f0d79c6ec14d6e6a Mon Sep 17 00:00:00 2001 From: delu Date: Fri, 23 Dec 2022 15:04:28 +0800 Subject: [PATCH] feat: add function render_array (#358) * feat: add function render_array Signed-off-by: delu * feat: add function render_array Signed-off-by: delu * fix: golangci init Signed-off-by: delu Signed-off-by: delu --- .../transform/action/render/array.go | 114 +++++++++++++ .../transform/action/render/array_test.go | 155 ++++++++++++++++++ internal/primitive/transform/arg/arg.go | 2 +- internal/primitive/transform/runtime/init.go | 7 +- 4 files changed, 274 insertions(+), 4 deletions(-) create mode 100644 internal/primitive/transform/action/render/array.go create mode 100644 internal/primitive/transform/action/render/array_test.go diff --git a/internal/primitive/transform/action/render/array.go b/internal/primitive/transform/action/render/array.go new file mode 100644 index 000000000..5d34c6290 --- /dev/null +++ b/internal/primitive/transform/action/render/array.go @@ -0,0 +1,114 @@ +// Copyright 2022 Linkall Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package render + +import ( + "fmt" + "strings" + + "github.com/linkall-labs/vanus/internal/primitive/transform/action" + "github.com/linkall-labs/vanus/internal/primitive/transform/arg" + "github.com/linkall-labs/vanus/internal/primitive/transform/common" + "github.com/linkall-labs/vanus/internal/primitive/transform/context" +) + +// ["render_array","targetPath","render root","render template"]. +type renderArrayAction struct { + action.CommonAction + paths []string + template string + leftDelim string + rightDelim string +} + +func NewRenderArrayAction() action.Action { + a := &renderArrayAction{ + leftDelim: "<@", + rightDelim: ">", + } + a.CommonAction = action.CommonAction{ + ActionName: "RENDER_ARRAY", + FixedArgs: []arg.TypeList{arg.EventList, []arg.Type{arg.EventData}, []arg.Type{arg.Constant}}, + } + return a +} + +func (a *renderArrayAction) Init(args []arg.Arg) error { + a.TargetArg = args[0] + jsonPrefix := args[1].Original() + text := args[2].Original() + var pos int + var sb strings.Builder + leftDelimLen := len(a.leftDelim) + for { + x := strings.Index(text[pos:], a.leftDelim) + if x < 0 { + sb.WriteString(text[pos:]) + break + } + ldp := pos + x + leftDelimLen + y := strings.Index(text[ldp:], a.rightDelim) + if y < 0 { + sb.WriteString(text[pos:]) + break + } + if x > 0 { + sb.WriteString(text[pos : pos+x]) + } + a.paths = append(a.paths, text[ldp:ldp+y]) + + sb.WriteString("%v") + pos = ldp + y + len(a.rightDelim) + if pos == len(text) { + break + } + } + a.template = sb.String() + for _, path := range a.paths { + _arg, err := arg.NewArg(jsonPrefix + "[:]" + path) + if err != nil { + return err + } + a.Args = append(a.Args, _arg) + a.ArgTypes = append(a.ArgTypes, common.Array) + } + return nil +} + +func (a *renderArrayAction) Execute(ceCtx *context.EventContext) error { + args, err := a.RunArgs(ceCtx) + if err != nil { + return err + } + if len(args) == 0 { + return a.TargetArg.SetValue(ceCtx, []string{a.template}) + } + _len := len(args[0].([]interface{})) + for i := 1; i < len(args); i++ { + if len(args[i].([]interface{})) != _len { + return fmt.Errorf("template %s value length is not same", a.leftDelim+a.paths[i]+a.rightDelim) + } + } + + target := make([]string, _len) + for i := 0; i < _len; i++ { + value := make([]interface{}, len(a.paths)) + for j := 0; j < len(args); j++ { + value[j] = args[j].([]interface{})[i] + } + target[i] = fmt.Sprintf(a.template, value...) + } + return a.TargetArg.SetValue(ceCtx, target) +} diff --git a/internal/primitive/transform/action/render/array_test.go b/internal/primitive/transform/action/render/array_test.go new file mode 100644 index 000000000..cce109cac --- /dev/null +++ b/internal/primitive/transform/action/render/array_test.go @@ -0,0 +1,155 @@ +// Copyright 2022 Linkall Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package render_test + +import ( + stdJson "encoding/json" + "testing" + + cetest "github.com/cloudevents/sdk-go/v2/test" + "github.com/linkall-labs/vanus/internal/primitive/transform/action/render" + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + "github.com/linkall-labs/vanus/internal/primitive/transform/runtime" + . "github.com/smartystreets/goconvey/convey" +) + +func TestRenderArrayAction(t *testing.T) { + funcName := render.NewRenderArrayAction().Name() + Convey("test render array invalid", t, func() { + jsonStr := `{ + "array": [ + { + "name": "name1", + "number": 1 + }, + { + "name": "name2", + "number": "2" + }, + { + "name": "name3" + } + ] + }` + Convey("render param not same", func() { + a, err := runtime.NewAction([]interface{}{funcName, "$.data.render", "$.data.array", "begin <@.name> <@.number> end"}) + So(err, ShouldBeNil) + e := cetest.MinEvent() + var data map[string]interface{} + err = stdJson.Unmarshal([]byte(jsonStr), &data) + So(err, ShouldBeNil) + err = a.Execute(&context.EventContext{ + Event: &e, + Data: data, + }) + So(err, ShouldNotBeNil) + }) + }) + Convey("test render array valid", t, func() { + jsonStr := `{ + "array": [ + { + "name": "name1", + "number": 1 + }, + { + "name": "name2", + "number": "2" + }, + { + "name": "name3", + "number": "3" + } + ] + }` + Convey("render constants", func() { + a, err := runtime.NewAction([]interface{}{funcName, "$.data.render", "$.data.array", "abc"}) + So(err, ShouldBeNil) + + e := cetest.MinEvent() + var data map[string]interface{} + err = stdJson.Unmarshal([]byte(jsonStr), &data) + So(err, ShouldBeNil) + err = a.Execute(&context.EventContext{ + Event: &e, + Data: data, + }) + So(err, ShouldBeNil) + render, exist := data["render"] + So(exist, ShouldBeTrue) + So(len(render.([]string)), ShouldEqual, 1) + So(render.([]string)[0], ShouldEqual, "abc") + }) + Convey("render variable template", func() { + Convey("render all variable", func() { + a, err := runtime.NewAction([]interface{}{funcName, "$.data.render", "$.data.array", "<@.name><@.number>"}) + So(err, ShouldBeNil) + e := cetest.MinEvent() + var data map[string]interface{} + err = stdJson.Unmarshal([]byte(jsonStr), &data) + So(err, ShouldBeNil) + err = a.Execute(&context.EventContext{ + Event: &e, + Data: data, + }) + So(err, ShouldBeNil) + render, exist := data["render"] + So(exist, ShouldBeTrue) + So(len(render.([]string)), ShouldEqual, 3) + So(render.([]string)[0], ShouldEqual, "name11") + So(render.([]string)[1], ShouldEqual, "name22") + So(render.([]string)[2], ShouldEqual, "name33") + }) + Convey("render begin and end", func() { + a, err := runtime.NewAction([]interface{}{funcName, "$.data.render", "$.data.array", "<@.name> and <@.number>"}) + So(err, ShouldBeNil) + e := cetest.MinEvent() + var data map[string]interface{} + err = stdJson.Unmarshal([]byte(jsonStr), &data) + So(err, ShouldBeNil) + err = a.Execute(&context.EventContext{ + Event: &e, + Data: data, + }) + So(err, ShouldBeNil) + render, exist := data["render"] + So(exist, ShouldBeTrue) + So(len(render.([]string)), ShouldEqual, 3) + So(render.([]string)[0], ShouldEqual, "name1 and 1") + So(render.([]string)[1], ShouldEqual, "name2 and 2") + So(render.([]string)[2], ShouldEqual, "name3 and 3") + }) + Convey("render other", func() { + a, err := runtime.NewAction([]interface{}{funcName, "$.data.render", "$.data.array", "Name: <@.name> Num: <@.number> <@abc"}) + So(err, ShouldBeNil) + e := cetest.MinEvent() + var data map[string]interface{} + err = stdJson.Unmarshal([]byte(jsonStr), &data) + So(err, ShouldBeNil) + err = a.Execute(&context.EventContext{ + Event: &e, + Data: data, + }) + So(err, ShouldBeNil) + render, exist := data["render"] + So(exist, ShouldBeTrue) + So(len(render.([]string)), ShouldEqual, 3) + So(render.([]string)[0], ShouldEqual, "Name: name1 Num: 1 <@abc") + So(render.([]string)[1], ShouldEqual, "Name: name2 Num: 2 <@abc") + So(render.([]string)[2], ShouldEqual, "Name: name3 Num: 3 <@abc") + }) + }) + }) +} diff --git a/internal/primitive/transform/arg/arg.go b/internal/primitive/transform/arg/arg.go index 96872c3b9..a928ed070 100644 --- a/internal/primitive/transform/arg/arg.go +++ b/internal/primitive/transform/arg/arg.go @@ -83,7 +83,7 @@ func NewArg(arg interface{}) (Arg, error) { if argLen >= 2 && argName[:2] == EventArgPrefix { return newEventAttribute(argName) } - if argLen >= 3 && argName[0] == '<' && argName[argLen-1] == '>' { + if argLen >= 3 && argName[0] == '<' && argName[argLen-1] == '>' && argName[1] != '@' { return newDefine(argName), nil } } diff --git a/internal/primitive/transform/runtime/init.go b/internal/primitive/transform/runtime/init.go index 0e6739bb7..60438cb5d 100644 --- a/internal/primitive/transform/runtime/init.go +++ b/internal/primitive/transform/runtime/init.go @@ -15,13 +15,12 @@ package runtime import ( + "github.com/linkall-labs/vanus/internal/primitive/transform/action/condition" "github.com/linkall-labs/vanus/internal/primitive/transform/action/datetime" "github.com/linkall-labs/vanus/internal/primitive/transform/action/math" - + "github.com/linkall-labs/vanus/internal/primitive/transform/action/render" "github.com/linkall-labs/vanus/internal/primitive/transform/action/strings" "github.com/linkall-labs/vanus/internal/primitive/transform/action/structs" - - "github.com/linkall-labs/vanus/internal/primitive/transform/action/condition" ) func init() { @@ -49,6 +48,8 @@ func init() { strings.NewReplaceWithRegexAction, // condition condition.NewConditionIfAction, + // render + render.NewRenderArrayAction, } { if err := AddAction(fn); err != nil { panic(err)