diff --git a/.golangci.yaml b/.golangci.yaml index e5c063ed1..1be9adf94 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -412,6 +412,9 @@ issues: linters: - gosec - gomnd + - path: "action.go" + linters: + - gochecknoinits - path: "^vsctl" linters: - gomnd diff --git a/go.mod b/go.mod index 166b1a2d0..c30f91976 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,9 @@ require ( github.com/huandu/skiplist v1.2.0 github.com/iceber/iouring-go v0.0.0-20220609112130-b1dc8dd9fbfd github.com/jedib0t/go-pretty/v6 v6.3.1 + github.com/json-iterator/go v1.1.12 github.com/labstack/echo/v4 v4.7.2 + github.com/labstack/gommon v0.3.1 github.com/linkall-labs/embed-etcd v0.1.1 github.com/linkall-labs/vanus/client v0.4.0-alpha github.com/linkall-labs/vanus/observability v0.4.0-alpha @@ -31,6 +33,7 @@ require ( github.com/linkall-labs/vanus/raft v0.4.0-alpha github.com/mwitkow/grpc-proxy v0.0.0 github.com/ncw/directio v1.0.5 + github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 github.com/prashantv/gostub v1.1.0 github.com/prometheus/client_golang v1.13.0 github.com/smartystreets/goconvey v1.7.2 @@ -86,10 +89,8 @@ require ( github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.2.2 // indirect - github.com/json-iterator/go v1.1.12 // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/klauspost/compress v1.13.6 // indirect - github.com/labstack/gommon v0.3.1 // indirect github.com/mattn/go-colorable v0.1.11 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect diff --git a/go.sum b/go.sum index d2892e943..adf9d8240 100644 --- a/go.sum +++ b/go.sum @@ -370,6 +370,8 @@ github.com/ncw/directio v1.0.5/go.mod h1:rX/pKEYkOXBGOggmcyJeJGloCkleSvphPx2eV3t github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 h1:Yl0tPBa8QPjGmesFh1D0rDy+q1Twx6FyU7VWHi8wZbI= +github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= diff --git a/internal/controller/snowflake/snowflake.go b/internal/controller/snowflake/snowflake.go index c431fa5bb..6a171a04b 100644 --- a/internal/controller/snowflake/snowflake.go +++ b/internal/controller/snowflake/snowflake.go @@ -108,9 +108,9 @@ func (sf *snowflake) RegisterNode(ctx context.Context, in *wrapperspb.UInt32Valu id := uint16(in.Value) // TODO(wenfeng) find a good solution in future - //_, exist := sf.nodes[id] + // _, exist := sf.nodes[id] // - //if exist { + // if exist { // return nil, errors.New("node has been register") //} diff --git a/internal/controller/trigger/controller.go b/internal/controller/trigger/controller.go index cb21ec538..a06f6c3dc 100644 --- a/internal/controller/trigger/controller.go +++ b/internal/controller/trigger/controller.go @@ -134,6 +134,9 @@ func (ctrl *controller) CreateSubscription(ctx context.Context, } err := validation.ValidateSubscriptionRequest(ctx, request.Subscription) if err != nil { + log.Info(ctx, "create subscription validate fail", map[string]interface{}{ + log.KeyError: err, + }) return nil, err } sub := convert.FromPbSubscriptionRequest(request.Subscription) diff --git a/internal/controller/trigger/validation/subscripton.go b/internal/controller/trigger/validation/subscripton.go index 9f981c636..08a4a7b22 100644 --- a/internal/controller/trigger/validation/subscripton.go +++ b/internal/controller/trigger/validation/subscripton.go @@ -17,6 +17,11 @@ package validation import ( "context" "fmt" + "net/url" + + "github.com/linkall-labs/vanus/internal/primitive/transform/action" + + "github.com/linkall-labs/vanus/internal/primitive/transform/arg" "github.com/linkall-labs/vanus/internal/controller/errors" "github.com/linkall-labs/vanus/internal/primitive" @@ -49,6 +54,9 @@ func ValidateSubscriptionRequest(ctx context.Context, request *ctrlpb.Subscripti if err := validateSubscriptionConfig(ctx, request.Config); err != nil { return err } + if err := validateTransformer(ctx, request.Transformer); err != nil { + return err + } return nil } @@ -86,6 +94,10 @@ func ValidateSinkAndProtocol(ctx context.Context, WithMessage("protocol is gcloud functions, sink credential can not be nil and credential type is gcloud") } case metapb.Protocol_HTTP: + if _, err := url.Parse(sink); err != nil { + return errors.ErrInvalidRequest. + WithMessage("protocol is http, sink is url,url parse error").Wrap(err) + } } return nil } @@ -147,6 +159,35 @@ func validateSubscriptionConfig(ctx context.Context, cfg *metapb.SubscriptionCon } return nil } + +func validateTransformer(ctx context.Context, transformer *metapb.Transformer) error { + if transformer == nil { + return nil + } + if len(transformer.Define) > 0 { + for key, value := range transformer.Define { + _, err := arg.NewArg(value) + if err != nil { + return errors.ErrInvalidRequest.WithMessage( + fmt.Sprintf("transformer define %s:%s is invalid:[%s]", key, value, err.Error())) + } + } + } + if len(transformer.Pipeline) > 0 { + for n, a := range transformer.Pipeline { + commands := make([]interface{}, len(a.Command)) + for i, command := range a.Command { + commands[i] = command.AsInterface() + } + if _, err := action.NewAction(commands); err != nil { + return errors.ErrInvalidRequest.WithMessage( + fmt.Sprintf("transformer pipeline %dst command %s is invalid:[%s]", n+1, commands[0], err.Error())) + } + } + } + return nil +} + func ValidateFilterList(ctx context.Context, filters []*metapb.Filter) error { if len(filters) == 0 { return nil diff --git a/internal/controller/trigger/validation/suscription_test.go b/internal/controller/trigger/validation/suscription_test.go index af0c4a2bd..7372f68eb 100644 --- a/internal/controller/trigger/validation/suscription_test.go +++ b/internal/controller/trigger/validation/suscription_test.go @@ -18,6 +18,8 @@ import ( "context" "testing" + "google.golang.org/protobuf/types/known/structpb" + ctrlpb "github.com/linkall-labs/vanus/proto/pkg/controller" metapb "github.com/linkall-labs/vanus/proto/pkg/meta" @@ -72,6 +74,46 @@ func TestValidateSubscriptionConfig(t *testing.T) { }) } +func TestValidateTransformer(t *testing.T) { + ctx := context.Background() + Convey("test validate transformer ", t, func() { + Convey("test define valid", func() { + trans := &metapb.Transformer{ + Define: map[string]string{ + "var1": "var", + "var2": "$.id", + "var3": "$.data.id", + }, + } + So(validateTransformer(ctx, trans), ShouldBeNil) + }) + Convey("test define invalid", func() { + trans := &metapb.Transformer{ + Define: map[string]string{ + "var2": "$.a-bc", + }, + } + So(validateTransformer(ctx, trans), ShouldNotBeNil) + }) + Convey("test pipeline valid", func() { + trans := &metapb.Transformer{ + Pipeline: []*metapb.Action{ + {Command: []*structpb.Value{structpb.NewStringValue("delete"), structpb.NewStringValue("$.id")}}, + }, + } + So(validateTransformer(ctx, trans), ShouldBeNil) + }) + Convey("test pipeline invalid", func() { + trans := &metapb.Transformer{ + Pipeline: []*metapb.Action{ + {Command: []*structpb.Value{structpb.NewStringValue("noExistActionName")}}, + }, + } + So(validateTransformer(ctx, trans), ShouldNotBeNil) + }) + }) +} + func TestValidateSinkAndProtocol(t *testing.T) { ctx := context.Background() Convey("sink is empty", t, func() { diff --git a/internal/convert/convert.go b/internal/convert/convert.go index 932972686..c0f01b72c 100644 --- a/internal/convert/convert.go +++ b/internal/convert/convert.go @@ -22,6 +22,7 @@ import ( ctrl "github.com/linkall-labs/vanus/proto/pkg/controller" pb "github.com/linkall-labs/vanus/proto/pkg/meta" pbtrigger "github.com/linkall-labs/vanus/proto/pkg/trigger" + "google.golang.org/protobuf/types/known/structpb" ) func FromPbSubscriptionRequest(sub *ctrl.SubscriptionRequest) *metadata.Subscription { @@ -259,7 +260,7 @@ func ToPbAddSubscription(sub *primitive.Subscription) *pbtrigger.AddSubscription EventBus: sub.EventBus, Offsets: ToPbOffsetInfos(sub.Offsets), Filters: toPbFilters(sub.Filters), - Transformer: toPbTransformer(sub.Transformer), + Transformer: ToPbTransformer(sub.Transformer), Config: toPbSubscriptionConfig(sub.Config), Protocol: toPbProtocol(sub.Protocol), ProtocolSettings: toPbProtocolSettings(sub.ProtocolSetting), @@ -279,7 +280,7 @@ func ToPbSubscription(sub *metadata.Subscription, offsets info.ListOffsetInfo) * ProtocolSettings: toPbProtocolSettings(sub.ProtocolSetting), EventBus: sub.EventBus, Filters: toPbFilters(sub.Filters), - Transformer: toPbTransformer(sub.Transformer), + Transformer: ToPbTransformer(sub.Transformer), Offsets: ToPbOffsetInfos(offsets), } return to @@ -409,15 +410,54 @@ func fromPbTransformer(transformer *pb.Transformer) *primitive.Transformer { return &primitive.Transformer{ Define: transformer.Define, Template: transformer.Template, + Pipeline: fromPbActions(transformer.Pipeline), } } -func toPbTransformer(transformer *primitive.Transformer) *pb.Transformer { +func fromPbActions(actions []*pb.Action) []*primitive.Action { + to := make([]*primitive.Action, 0, len(actions)) + for _, action := range actions { + to = append(to, fromPbCommand(action)) + } + return to +} + +func fromPbCommand(action *pb.Action) *primitive.Action { + to := &primitive.Action{} + commands := make([]interface{}, len(action.Command)) + for i, command := range action.Command { + commands[i] = command.AsInterface() + } + to.Command = commands + return to +} + +func toPbActions(actions []*primitive.Action) []*pb.Action { + to := make([]*pb.Action, len(actions)) + for i, action := range actions { + to[i] = toPbCommand(action) + } + return to +} + +func toPbCommand(action *primitive.Action) *pb.Action { + to := &pb.Action{} + commands := make([]*structpb.Value, len(action.Command)) + for i, command := range action.Command { + c, _ := structpb.NewValue(command) + commands[i] = c + } + to.Command = commands + return to +} + +func ToPbTransformer(transformer *primitive.Transformer) *pb.Transformer { if transformer == nil { return nil } return &pb.Transformer{ Define: transformer.Define, Template: transformer.Template, + Pipeline: toPbActions(transformer.Pipeline), } } diff --git a/internal/primitive/subscription.go b/internal/primitive/subscription.go index 2cf3ab7f8..f94fb7acc 100644 --- a/internal/primitive/subscription.go +++ b/internal/primitive/subscription.go @@ -104,6 +104,7 @@ func (l SubscriptionFilterList) String() string { type Transformer struct { Define map[string]string `json:"define,omitempty"` + Pipeline []*Action `json:"pipeline,omitempty"` Template string `json:"template,omitempty"` } @@ -119,12 +120,16 @@ func (t *Transformer) Exist() bool { if t == nil { return false } - if t.Template == "" { + if t.Template == "" && len(t.Pipeline) == 0 { return false } return true } +type Action struct { + Command []interface{} `json:"command"` +} + /* annotation no use code . type SinkSpec struct { Type string diff --git a/internal/primitive/transform/action/action.go b/internal/primitive/transform/action/action.go new file mode 100644 index 000000000..cbf0fb85d --- /dev/null +++ b/internal/primitive/transform/action/action.go @@ -0,0 +1,229 @@ +// 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 action + +import ( + "fmt" + "strings" + + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + + "github.com/pkg/errors" + + "github.com/linkall-labs/vanus/internal/primitive/transform/arg" + "github.com/linkall-labs/vanus/internal/primitive/transform/function" +) + +type newAction func() Action + +type Action interface { + // Name func name + Name() string + // Arity arg number + Arity() int + // ArgType arg type + ArgType(index int) arg.TypeList + // IsVariadic is exist variadic + IsVariadic() bool + Init(args []arg.Arg) error + Execute(ceCtx *context.EventContext) error +} + +type commonAction struct { + name string + fixedArgs []arg.TypeList + variadicArg arg.TypeList + fn function.Function + + args []arg.Arg + argTypes []function.Type + targetArg arg.Arg +} + +func (a *commonAction) Name() string { + if a.name != "" { + return a.name + } + if a.fn != nil { + return a.fn.Name() + } + return "" +} + +func (a *commonAction) Arity() int { + return len(a.fixedArgs) +} + +func (a *commonAction) ArgType(index int) arg.TypeList { + if index < len(a.fixedArgs) { + return a.fixedArgs[index] + } + return a.variadicArg +} + +func (a *commonAction) IsVariadic() bool { + return len(a.variadicArg) > 0 +} + +func (a *commonAction) Init(args []arg.Arg) error { + a.targetArg = args[0] + a.args = args[1:] + return a.setArgTypes() +} + +func (a *commonAction) setArgTypes() error { + if a.fn == nil { + return fmt.Errorf("fn is nil") + } + if len(a.args) < a.fn.Arity() { + return ErrArgNumber + } + if len(a.args) > a.fn.Arity() && !a.fn.IsVariadic() { + return ErrArgNumber + } + argTypes := make([]function.Type, len(a.args)) + for i := 0; i < len(a.args); i++ { + argTypes[i] = *a.fn.ArgType(i) + } + a.argTypes = argTypes + return nil +} + +func (a *commonAction) Execute(ceCtx *context.EventContext) error { + if a.fn == nil { + return fmt.Errorf("fn is nil") + } + args, err := a.runArgs(ceCtx) + if err != nil { + return err + } + fnValue, err := a.fn.Execute(args) + if err != nil { + return err + } + return a.targetArg.SetValue(ceCtx, fnValue) +} + +func (a *commonAction) runArgs(ceCtx *context.EventContext) ([]interface{}, error) { + args := make([]interface{}, len(a.args)) + if len(a.args) != len(a.argTypes) { + return nil, fmt.Errorf("arg lenth %d not same arg type %d", len(a.args), len(a.argTypes)) + } + for i, _arg := range a.args { + value, err := _arg.Evaluate(ceCtx) + if err != nil { + return nil, errors.Wrapf(err, "arg %s evaluate error", _arg.Original()) + } + v, err := function.Cast(value, a.argTypes[i]) + if err != nil { + return nil, err + } + args[i] = v + } + return args, nil +} + +type sourceTargetSameAction struct { + commonAction +} + +func (a *sourceTargetSameAction) Init(args []arg.Arg) error { + a.targetArg = args[0] + a.args = args + return a.setArgTypes() +} + +var actionMap = map[string]newAction{} + +func AddAction(actionFn newAction) error { + a := actionFn() + if _, exist := actionMap[a.Name()]; exist { + return ErrExist + } + actionMap[a.Name()] = actionFn + return nil +} + +func init() { + for _, fn := range []newAction{ + // struct + newCreateActionAction, + newDeleteAction, + newReplaceAction, + newMoveActionAction, + newRenameActionAction, + // math + newMathAddActionAction, + newMathSubActionAction, + newMathMulActionAction, + newMathDivActionAction, + // format + newFormatDateAction, + newFormatUnixTimeAction, + // string + newJoinAction, + newUpperAction, + newLowerAction, + newAddPrefixAction, + newAddSuffixAction, + newReplaceWithRegexAction, + } { + if err := AddAction(fn); err != nil { + panic(err) + } + } +} + +func NewAction(command []interface{}) (Action, error) { + funcName, ok := command[0].(string) + if !ok { + return nil, fmt.Errorf("command name must be stirng") + } + actionFn, exist := actionMap[strings.ToUpper(funcName)] + if !exist { + return nil, fmt.Errorf("command %s not exist", funcName) + } + a := actionFn() + argNum := len(command) - 1 + if argNum < a.Arity() { + return nil, fmt.Errorf("command %s arg number is not enough, it need %d but only have %d", + funcName, a.Arity(), argNum) + } + if argNum > a.Arity() && !a.IsVariadic() { + return nil, fmt.Errorf("command %s arg number is too many, it need %d but have %d", funcName, a.Arity(), argNum) + } + args := make([]arg.Arg, argNum) + for i := 1; i < len(command); i++ { + _arg, err := arg.NewArg(command[i]) + if err != nil { + return nil, errors.Wrapf(err, "command %s arg %d is invalid", funcName, i) + } + argType := a.ArgType(i - 1) + if !argType.Contains(_arg) { + return nil, fmt.Errorf("command %s arg %d not support type %s", funcName, i, _arg.Type()) + } + args[i-1] = _arg + } + err := a.Init(args) + if err != nil { + return nil, errors.Wrapf(err, "command %s init error", funcName) + } + return a, nil +} + +var ( + ErrExist = fmt.Errorf("action have exist") + ErrArgNumber = fmt.Errorf("action arg number invalid") +) diff --git a/internal/primitive/transform/action/action_bench_test.go b/internal/primitive/transform/action/action_bench_test.go new file mode 100644 index 000000000..cabfee0b5 --- /dev/null +++ b/internal/primitive/transform/action/action_bench_test.go @@ -0,0 +1,82 @@ +// 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 action_test + +import ( + "testing" + + "github.com/linkall-labs/vanus/internal/primitive/transform/action" + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + + ce "github.com/cloudevents/sdk-go/v2" + cetest "github.com/cloudevents/sdk-go/v2/test" +) + +func actionBenchmark(command []interface{}) func(b *testing.B) { + a, err := action.NewAction(command) + if err != nil { + panic(err) + } + event := cetest.FullEvent() + event.SetExtension("testKey", "testValue") + event.SetData(ce.ApplicationJSON, map[string]interface{}{"str": "string", "number": 123.456}) + ceCtx := newEventContext(event) + err = a.Execute(ceCtx) + if err != nil { + panic(err) + } + return func(b *testing.B) { + for i := 0; i < b.N; i++ { + ceCtx = newEventContext(event) + _ = a.Execute(ceCtx) + } + } +} + +func newEventContext(event ce.Event) *context.EventContext { + return &context.EventContext{ + Event: &event, + Define: map[string]interface{}{ + "str": "defineStr", + "number": 123.321, + }, + Data: map[string]interface{}{ + "str": "dataStr", + "number": 456.654, + "second": float64(1669120748), + "time": "2022/11/22 20:40:30", + }, + } +} + +func BenchmarkAction(b *testing.B) { + b.Run("delete", actionBenchmark([]interface{}{"delete", "$.data.str"})) + b.Run("create", actionBenchmark([]interface{}{"create", "$.data.create", "create"})) + b.Run("replace", actionBenchmark([]interface{}{"replace", "$.data.str", "replaceValue"})) + b.Run("move", actionBenchmark([]interface{}{"move", "$.data.str", "$.data.strObj.str"})) + b.Run("rename", actionBenchmark([]interface{}{"rename", "$.data.str", "$.data.strNew"})) + b.Run("math_add", actionBenchmark([]interface{}{"math_add", "$.data.math_add", "$.data.number", ""})) + b.Run("math_sub", actionBenchmark([]interface{}{"math_sub", "$.data.math_sub", "$.data.number", ""})) + b.Run("math_mul", actionBenchmark([]interface{}{"math_mul", "$.data.math_mul", "$.data.number", ""})) + b.Run("math_div", actionBenchmark([]interface{}{"math_div", "$.data.math_div", "$.data.number", ""})) + b.Run("format_unix_time", actionBenchmark([]interface{}{"format_unix_time", "$.data.second", "yyyy-mm-dd HH:MM:SS"})) + b.Run("format_date", actionBenchmark([]interface{}{"format_date", "$.data.time", "yyyy/mm/dd HH:MM:SS", "yyyy-mm-dd HH:MM:SS"})) + b.Run("join", actionBenchmark([]interface{}{"join", "$.data.join", ",", "$.data.str", ""})) + b.Run("upper_case", actionBenchmark([]interface{}{"upper_case", "$.data.str"})) + b.Run("lower_case", actionBenchmark([]interface{}{"lower_case", "$.data.str"})) + b.Run("add_prefix", actionBenchmark([]interface{}{"add_prefix", "$.data.str", "prefix"})) + b.Run("add_suffix", actionBenchmark([]interface{}{"add_suffix", "$.data.str", "suffix"})) + b.Run("replace_with_regex", actionBenchmark([]interface{}{"replace_with_regex", "$.data.str", "a", "Aa"})) +} diff --git a/internal/primitive/transform/action/action_test.go b/internal/primitive/transform/action/action_test.go new file mode 100644 index 000000000..74474c94f --- /dev/null +++ b/internal/primitive/transform/action/action_test.go @@ -0,0 +1,54 @@ +// 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 action + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestNewAction(t *testing.T) { + Convey("test new action", t, func() { + Convey("func name is not string", func() { + _, err := NewAction([]interface{}{123}) + So(err, ShouldNotBeNil) + }) + Convey("func name no exist", func() { + _, err := NewAction([]interface{}{"UnknownCommand"}) + So(err, ShouldNotBeNil) + }) + Convey("func arity not enough", func() { + _, err := NewAction([]interface{}{"delete"}) + So(err, ShouldNotBeNil) + }) + Convey("func arity number greater than", func() { + _, err := NewAction([]interface{}{"delete", "arg1", "arg2"}) + So(err, ShouldNotBeNil) + }) + Convey("func new arg error", func() { + _, err := NewAction([]interface{}{"delete", "$.a-b"}) + So(err, ShouldNotBeNil) + }) + Convey("func new arg type is invalid", func() { + _, err := NewAction([]interface{}{"delete", "arg"}) + So(err, ShouldNotBeNil) + }) + Convey("func new valid", func() { + _, err := NewAction([]interface{}{"delete", "$.id"}) + So(err, ShouldBeNil) + }) + }) +} diff --git a/internal/primitive/transform/action/math_action.go b/internal/primitive/transform/action/math_action.go new file mode 100644 index 000000000..ce1b78892 --- /dev/null +++ b/internal/primitive/transform/action/math_action.go @@ -0,0 +1,54 @@ +// 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 action + +import ( + "github.com/linkall-labs/vanus/internal/primitive/transform/arg" + "github.com/linkall-labs/vanus/internal/primitive/transform/function" +) + +// ["math_add", "toKey","key1","key2"]. +func newMathAddActionAction() Action { + return &commonAction{ + fixedArgs: []arg.TypeList{arg.EventList, arg.All, arg.All}, + variadicArg: arg.All, + fn: function.MathAddFunction, + } +} + +// ["math_sub", "toKey","key1","key2"]. +func newMathSubActionAction() Action { + return &commonAction{ + fixedArgs: []arg.TypeList{arg.EventList, arg.All, arg.All}, + fn: function.MathSubFunction, + } +} + +// ["math_mul", "toKey","key1","key2"]. +func newMathMulActionAction() Action { + return &commonAction{ + fixedArgs: []arg.TypeList{arg.EventList, arg.All, arg.All}, + variadicArg: arg.All, + fn: function.MathMulFunction, + } +} + +// ["math_div", "toKey","key1","key2"]. +func newMathDivActionAction() Action { + return &commonAction{ + fixedArgs: []arg.TypeList{arg.EventList, arg.All, arg.All}, + fn: function.MathDivFunction, + } +} diff --git a/internal/primitive/transform/action/math_action_test.go b/internal/primitive/transform/action/math_action_test.go new file mode 100644 index 000000000..4a90f5031 --- /dev/null +++ b/internal/primitive/transform/action/math_action_test.go @@ -0,0 +1,77 @@ +// 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 action + +import ( + "testing" + + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestMathAction(t *testing.T) { + Convey("test math add", t, func() { + a, err := NewAction([]interface{}{newMathAddActionAction().Name(), "$.test", "123", "456", "321"}) + So(err, ShouldBeNil) + e := newEvent() + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, int32(900)) + }) + Convey("test math sub", t, func() { + a, err := NewAction([]interface{}{newMathSubActionAction().Name(), "$.test", "456", "123"}) + So(err, ShouldBeNil) + e := newEvent() + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, int32(333)) + }) + Convey("test math mul", t, func() { + a, err := NewAction([]interface{}{newMathMulActionAction().Name(), "$.test", "111", "2", "3"}) + So(err, ShouldBeNil) + e := newEvent() + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, int32(666)) + }) + Convey("test math div", t, func() { + Convey("div zero", func() { + a, err := NewAction([]interface{}{newMathDivActionAction().Name(), "$.test", "333", "0"}) + So(err, ShouldBeNil) + e := newEvent() + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldNotBeNil) + }) + Convey("div", func() { + a, err := NewAction([]interface{}{newMathDivActionAction().Name(), "$.test", "333", "3"}) + So(err, ShouldBeNil) + e := newEvent() + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, int32(111)) + }) + }) +} diff --git a/internal/primitive/transform/action/regex_action.go b/internal/primitive/transform/action/regex_action.go new file mode 100644 index 000000000..757f4e46e --- /dev/null +++ b/internal/primitive/transform/action/regex_action.go @@ -0,0 +1,92 @@ +// 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 action + +import ( + "regexp" + "sync" + + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + + "github.com/linkall-labs/vanus/internal/primitive/transform/function" + + "github.com/pkg/errors" + + "github.com/linkall-labs/vanus/internal/primitive/transform/arg" +) + +type replaceWithRegexAction struct { + commonAction + pattern *regexp.Regexp + expr string + lock sync.RWMutex +} + +// ["replace_with_regex", "key", "pattern", "value"]. +func newReplaceWithRegexAction() Action { + return &replaceWithRegexAction{ + commonAction: commonAction{ + name: "REPLACE_WITH_REGEX", + fixedArgs: []arg.TypeList{arg.EventList, arg.All, arg.All}, + }, + } +} + +func (a *replaceWithRegexAction) Init(args []arg.Arg) error { + a.targetArg = args[0] + a.args = args + a.argTypes = []function.Type{function.String, function.String, function.String} + return nil +} + +func (a *replaceWithRegexAction) Execute(ceCtx *context.EventContext) error { + args, err := a.runArgs(ceCtx) + if err != nil { + return err + } + originalValue, _ := args[0].(string) + value, _ := args[2].(string) + expr, _ := args[1].(string) + if expr != a.expr { + err = a.setPattern(expr) + if err != nil { + return err + } + } + newValue := a.getPattern().ReplaceAllString(originalValue, value) + return a.targetArg.SetValue(ceCtx, newValue) +} + +func (a *replaceWithRegexAction) setPattern(expr string) error { + a.lock.Lock() + defer a.lock.Unlock() + if expr == a.expr { + return nil + } + p, err := regexp.Compile(expr) + if err != nil { + a.lock.Unlock() + return errors.Wrapf(err, "replace_with_regex arg pattern regex invalid") + } + a.pattern = p + a.expr = expr + return nil +} + +func (a *replaceWithRegexAction) getPattern() *regexp.Regexp { + a.lock.RLock() + defer a.lock.RUnlock() + return a.pattern +} diff --git a/internal/primitive/transform/action/regex_action_test.go b/internal/primitive/transform/action/regex_action_test.go new file mode 100644 index 000000000..36326d81b --- /dev/null +++ b/internal/primitive/transform/action/regex_action_test.go @@ -0,0 +1,37 @@ +// 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 action + +import ( + "testing" + + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestRegexAction(t *testing.T) { + Convey("test replace with regex", t, func() { + a, err := NewAction([]interface{}{newReplaceWithRegexAction().Name(), "$.test", "a", "value"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "a-a") + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, "value-value") + }) +} diff --git a/internal/primitive/transform/action/string_action.go b/internal/primitive/transform/action/string_action.go new file mode 100644 index 000000000..5e645b92a --- /dev/null +++ b/internal/primitive/transform/action/string_action.go @@ -0,0 +1,69 @@ +// 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 action + +import ( + "github.com/linkall-labs/vanus/internal/primitive/transform/arg" + "github.com/linkall-labs/vanus/internal/primitive/transform/function" +) + +// ["join", "toKey", "separator","key1","key2"]. +func newJoinAction() Action { + return &commonAction{ + fixedArgs: []arg.TypeList{arg.EventList, arg.All, arg.All, arg.All}, + variadicArg: arg.All, + fn: function.JoinFunction, + } +} + +// ["upper_case", "key"]. +func newUpperAction() Action { + return &sourceTargetSameAction{ + commonAction{ + fixedArgs: []arg.TypeList{arg.EventList}, + fn: function.UpperFunction, + }, + } +} + +// ["lower_case", "key"]. +func newLowerAction() Action { + return &sourceTargetSameAction{ + commonAction{ + fixedArgs: []arg.TypeList{arg.EventList}, + fn: function.LowerFunction, + }, + } +} + +// ["add_prefix", "key", "value"]. +func newAddPrefixAction() Action { + return &sourceTargetSameAction{ + commonAction{ + fixedArgs: []arg.TypeList{arg.EventList, arg.All}, + fn: function.AddPrefixFunction, + }, + } +} + +// ["add_suffix", "key", "value"]. +func newAddSuffixAction() Action { + return &sourceTargetSameAction{ + commonAction{ + fixedArgs: []arg.TypeList{arg.EventList, arg.All}, + fn: function.AddSuffixFunction, + }, + } +} diff --git a/internal/primitive/transform/action/string_action_test.go b/internal/primitive/transform/action/string_action_test.go new file mode 100644 index 000000000..21e346f03 --- /dev/null +++ b/internal/primitive/transform/action/string_action_test.go @@ -0,0 +1,84 @@ +// 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 action + +import ( + "testing" + + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestStringAction(t *testing.T) { + Convey("test join action", t, func() { + a, err := NewAction([]interface{}{newJoinAction().Name(), "$.test", ",", "abc", "123"}) + So(err, ShouldBeNil) + e := newEvent() + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, "abc,123") + }) + Convey("test upper", t, func() { + a, err := NewAction([]interface{}{newUpperAction().Name(), "$.test"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "testValue") + ceCtx := &context.EventContext{ + Event: e, + } + err = a.Execute(ceCtx) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, "TESTVALUE") + }) + Convey("test lower", t, func() { + a, err := NewAction([]interface{}{newLowerAction().Name(), "$.test"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "testValue") + ceCtx := &context.EventContext{ + Event: e, + } + err = a.Execute(ceCtx) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, "testvalue") + }) + Convey("test add prefix", t, func() { + a, err := NewAction([]interface{}{newAddPrefixAction().Name(), "$.test", "prefix"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "testValue") + ceCtx := &context.EventContext{ + Event: e, + } + err = a.Execute(ceCtx) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, "prefixtestValue") + }) + Convey("test add suffix", t, func() { + a, err := NewAction([]interface{}{newAddSuffixAction().Name(), "$.test", "suffix"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "testValue") + ceCtx := &context.EventContext{ + Event: e, + } + err = a.Execute(ceCtx) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, "testValuesuffix") + }) +} diff --git a/internal/primitive/transform/action/struct_action.go b/internal/primitive/transform/action/struct_action.go new file mode 100644 index 000000000..eb9343e4f --- /dev/null +++ b/internal/primitive/transform/action/struct_action.go @@ -0,0 +1,187 @@ +// 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 action + +import ( + "fmt" + + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + + "github.com/linkall-labs/vanus/internal/primitive/transform/arg" + "github.com/linkall-labs/vanus/internal/primitive/transform/function" +) + +// ["delete", "key"]. +type deleteAction struct { + commonAction +} + +func newDeleteAction() Action { + return &deleteAction{ + commonAction{ + name: "DELETE", + fixedArgs: []arg.TypeList{arg.EventList}, + }, + } +} + +func (a *deleteAction) Init(args []arg.Arg) error { + a.targetArg = args[0] + return nil +} + +func (a *deleteAction) Execute(ceCtx *context.EventContext) error { + return a.targetArg.DeleteValue(ceCtx) +} + +type createAction struct { + commonAction +} + +// ["create", "toKey", value]. +func newCreateActionAction() Action { + return &createAction{ + commonAction{ + name: "CREATE", + fixedArgs: []arg.TypeList{arg.EventList, arg.All}, + }, + } +} + +func (a *createAction) Init(args []arg.Arg) error { + a.targetArg = args[0] + a.args = args[1:] + a.argTypes = []function.Type{function.Any} + return nil +} + +func (a *createAction) Execute(ceCtx *context.EventContext) error { + v, _ := a.targetArg.Evaluate(ceCtx) + if v != nil { + return fmt.Errorf("key %s exist", a.targetArg.Original()) + } + args, err := a.runArgs(ceCtx) + if err != nil { + return err + } + return a.targetArg.SetValue(ceCtx, args[0]) +} + +type replaceAction struct { + commonAction +} + +// ["replace", "toKey", value]. +func newReplaceAction() Action { + return &replaceAction{ + commonAction{ + name: "REPLACE", + fixedArgs: []arg.TypeList{arg.EventList, arg.All}, + }, + } +} + +func (a *replaceAction) Init(args []arg.Arg) error { + a.targetArg = args[0] + a.args = args[1:] + a.argTypes = []function.Type{function.Any} + return nil +} + +func (a *replaceAction) Execute(ceCtx *context.EventContext) error { + v, _ := a.targetArg.Evaluate(ceCtx) + if v == nil { + return fmt.Errorf("key %s not exist", a.targetArg.Original()) + } + args, err := a.runArgs(ceCtx) + if err != nil { + return err + } + return a.targetArg.SetValue(ceCtx, args[0]) +} + +type moveAction struct { + commonAction +} + +// ["move", "fromKey", "toKey"]. +func newMoveActionAction() Action { + return &moveAction{ + commonAction{ + name: "MOVE", + fixedArgs: []arg.TypeList{arg.EventList, arg.EventList}, + }, + } +} + +func (a *moveAction) Init(args []arg.Arg) error { + a.targetArg = args[1] + a.args = args[:1] + a.argTypes = []function.Type{function.Any} + return nil +} + +func (a *moveAction) Execute(ceCtx *context.EventContext) error { + v, _ := a.targetArg.Evaluate(ceCtx) + if v != nil { + return fmt.Errorf("key %s exist", a.targetArg.Original()) + } + args, err := a.runArgs(ceCtx) + if err != nil { + return err + } + err = a.targetArg.SetValue(ceCtx, args[0]) + if err != nil { + return err + } + return a.args[0].DeleteValue(ceCtx) +} + +type renameAction struct { + commonAction +} + +// ["rename", "key", "newKey"]. +func newRenameActionAction() Action { + return &renameAction{ + commonAction{ + name: "RENAME", + fixedArgs: []arg.TypeList{arg.EventList, arg.EventList}, + }, + } +} + +func (a *renameAction) Init(args []arg.Arg) error { + a.targetArg = args[1] + a.args = args[:1] + a.argTypes = []function.Type{function.Any} + return nil +} + +func (a *renameAction) Execute(ceCtx *context.EventContext) error { + v, _ := a.targetArg.Evaluate(ceCtx) + if v != nil { + return fmt.Errorf("key %s exist", a.targetArg.Original()) + } + args, err := a.runArgs(ceCtx) + if err != nil { + return err + } + err = a.targetArg.SetValue(ceCtx, args[0]) + if err != nil { + return err + } + return a.args[0].DeleteValue(ceCtx) +} diff --git a/internal/primitive/transform/action/struct_action_test.go b/internal/primitive/transform/action/struct_action_test.go new file mode 100644 index 000000000..ad0a04bd2 --- /dev/null +++ b/internal/primitive/transform/action/struct_action_test.go @@ -0,0 +1,157 @@ +// 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 action + +import ( + "testing" + + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestStructAction(t *testing.T) { + Convey("test delete", t, func() { + Convey("delete cant not delete key", func() { + a, err := NewAction([]interface{}{newDeleteAction().Name(), "$.id"}) + So(err, ShouldBeNil) + e := newEvent() + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldNotBeNil) + }) + Convey("delete", func() { + a, err := NewAction([]interface{}{newDeleteAction().Name(), "$.test"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "abc") + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldBeNil) + So(len(e.Extensions()), ShouldEqual, 0) + }) + }) + Convey("test create", t, func() { + Convey("create exist key", func() { + a, err := NewAction([]interface{}{newCreateActionAction().Name(), "$.test", "newValue"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "abc") + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldNotBeNil) + }) + Convey("creat invalid value", func() { + a, err := NewAction([]interface{}{newCreateActionAction().Name(), "$.test", map[string]interface{}{"a": "b"}}) + So(err, ShouldBeNil) + e := newEvent() + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldNotBeNil) + }) + Convey("creat", func() { + a, err := NewAction([]interface{}{newCreateActionAction().Name(), "$.test", "testValue"}) + So(err, ShouldBeNil) + e := newEvent() + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, "testValue") + }) + }) + Convey("test replace", t, func() { + Convey("replace no exist key", func() { + a, err := NewAction([]interface{}{newReplaceAction().Name(), "$.test", "newValue"}) + So(err, ShouldBeNil) + e := newEvent() + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldNotBeNil) + }) + Convey("replace", func() { + a, err := NewAction([]interface{}{newReplaceAction().Name(), "$.test", "testValue"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "abc") + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, "testValue") + }) + }) + Convey("test move", t, func() { + Convey("move target key exist", func() { + a, err := NewAction([]interface{}{newMoveActionAction().Name(), "$.test", "$.data.abc.test"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "abc") + err = a.Execute(&context.EventContext{ + Event: e, + Data: map[string]interface{}{ + "abc": map[string]interface{}{ + "test": "value", + }, + }, + }) + So(err, ShouldNotBeNil) + }) + Convey("move", func() { + a, err := NewAction([]interface{}{newMoveActionAction().Name(), "$.test", "$.data.abc.test"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "abc") + ceCtx := &context.EventContext{ + Event: e, + Data: map[string]interface{}{}, + } + err = a.Execute(ceCtx) + So(err, ShouldBeNil) + So(len(e.Extensions()), ShouldEqual, 0) + So(ceCtx.Data.(map[string]interface{})["abc"].(map[string]interface{})["test"], ShouldEqual, "abc") + }) + }) + Convey("test rename", t, func() { + Convey("rename target key exist", func() { + a, err := NewAction([]interface{}{newRenameActionAction().Name(), "$.test", "$.test2"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "abc") + e.SetExtension("test2", "abc2") + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldNotBeNil) + }) + Convey("rename", func() { + a, err := NewAction([]interface{}{newRenameActionAction().Name(), "$.test", "$.test2"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "abc") + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldBeNil) + So(len(e.Extensions()), ShouldEqual, 1) + So(e.Extensions()["test2"], ShouldEqual, "abc") + }) + }) +} diff --git a/internal/primitive/transform/action/time_action.go b/internal/primitive/transform/action/time_action.go new file mode 100644 index 000000000..ede978093 --- /dev/null +++ b/internal/primitive/transform/action/time_action.go @@ -0,0 +1,40 @@ +// 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 action + +import ( + "github.com/linkall-labs/vanus/internal/primitive/transform/arg" + "github.com/linkall-labs/vanus/internal/primitive/transform/function" +) + +// ["format_date", "key", "oldFormat","newFormat"]. +func newFormatDateAction() Action { + return &sourceTargetSameAction{ + commonAction{ + fixedArgs: []arg.TypeList{arg.EventList, arg.All, arg.All}, + fn: function.FormatDateFunction, + }, + } +} + +// ["format_unix_time", "key", "format"]. +func newFormatUnixTimeAction() Action { + return &sourceTargetSameAction{ + commonAction{ + fixedArgs: []arg.TypeList{arg.EventList, arg.All}, + fn: function.FormatUnixTimeFunction, + }, + } +} diff --git a/internal/primitive/transform/action/time_action_test.go b/internal/primitive/transform/action/time_action_test.go new file mode 100644 index 000000000..1de1ab01f --- /dev/null +++ b/internal/primitive/transform/action/time_action_test.go @@ -0,0 +1,57 @@ +// 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 action + +import ( + "testing" + + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + + ce "github.com/cloudevents/sdk-go/v2" + . "github.com/smartystreets/goconvey/convey" +) + +func newEvent() *ce.Event { + e := ce.NewEvent() + e.SetID("testID") + e.SetType("testType") + e.SetSource("testSource") + return &e +} + +func TestFormatAction(t *testing.T) { + Convey("test format date", t, func() { + a, err := NewAction([]interface{}{newFormatDateAction().Name(), "$.test", "yyyy-mm-ddTHH:MM:SSZ", "yyyy-mm-dd HH:MM:SS"}) + So(err, ShouldBeNil) + e := newEvent() + e.SetExtension("test", "2022-11-15T15:41:25Z") + err = a.Execute(&context.EventContext{ + Event: e, + }) + So(err, ShouldBeNil) + So(e.Extensions()["test"], ShouldEqual, "2022-11-15 15:41:25") + }) + Convey("test format unix time", t, func() { + a, err := NewAction([]interface{}{newFormatUnixTimeAction().Name(), "$.data.time", "yyyy-mm-ddTHH:MM:SSZ"}) + So(err, ShouldBeNil) + ceCtx := &context.EventContext{ + Event: newEvent(), + Data: map[string]interface{}{"time": float64(1668498285)}, + } + err = a.Execute(ceCtx) + So(err, ShouldBeNil) + So(ceCtx.Data.(map[string]interface{})["time"], ShouldEqual, "2022-11-15T07:44:45Z") + }) +} diff --git a/internal/primitive/transform/arg/arg.go b/internal/primitive/transform/arg/arg.go new file mode 100644 index 000000000..b723c50a3 --- /dev/null +++ b/internal/primitive/transform/arg/arg.go @@ -0,0 +1,96 @@ +// 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 arg + +import ( + "strings" + + "github.com/linkall-labs/vanus/internal/primitive/transform/context" +) + +type Type uint8 + +const ( + Constant Type = iota + EventAttribute + EventData + Define + Any +) + +func (t Type) String() string { + switch t { + case Constant: + return "Constant" + case EventAttribute: + return "EventAttribute" + case EventData: + return "EventData" + case Define: + return "Define" + } + return "unknown" +} + +type TypeList []Type + +func (list TypeList) Contains(arg Arg) bool { + if len(list) == 0 { + return false + } + for _, t := range list { + if arg.Type() == t { + return true + } + } + return false +} + +var ( + EventList = []Type{EventAttribute, EventData} + All = []Type{EventAttribute, EventData, Constant, Define} +) + +type Arg interface { + Type() Type + Name() string + Original() string + // Evaluate arg value + Evaluate(ceCtx *context.EventContext) (interface{}, error) + SetValue(ceCtx *context.EventContext, v interface{}) error + DeleteValue(ceCtx *context.EventContext) error +} + +func NewArg(arg interface{}) (Arg, error) { + if argName, ok := arg.(string); ok { + argName = strings.TrimSpace(argName) + argLen := len(argName) + if argLen >= 6 && argName[:6] == EventDataArgPrefix { + return newEventData(argName), nil + } + if argLen >= 2 && argName[:2] == EventArgPrefix { + return newEventAttribute(argName) + } + if argLen >= 3 && argName[0] == '<' && argName[argLen-1] == '>' { + return newDefine(argName), nil + } + } + return newConstant(arg), nil +} + +const ( + EventArgPrefix = "$." + EventDataArgPrefix = EventArgPrefix + "data" +) diff --git a/internal/primitive/transform/arg/arg_test.go b/internal/primitive/transform/arg/arg_test.go new file mode 100644 index 000000000..07dcd1fe7 --- /dev/null +++ b/internal/primitive/transform/arg/arg_test.go @@ -0,0 +1,157 @@ +// 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 arg + +import ( + "testing" + + vContext "github.com/linkall-labs/vanus/internal/primitive/transform/context" + + ce "github.com/cloudevents/sdk-go/v2" + . "github.com/smartystreets/goconvey/convey" +) + +func TestNewArg(t *testing.T) { + Convey("test new arg", t, func() { + Convey("test new event data", func() { + arg, err := NewArg("$.data.key") + So(err, ShouldBeNil) + So(arg.Type(), ShouldEqual, EventData) + So(arg.Name(), ShouldEqual, "key") + }) + Convey("test new event attribute", func() { + arg, err := NewArg("$.source") + So(err, ShouldBeNil) + So(arg.Type(), ShouldEqual, EventAttribute) + So(arg.Name(), ShouldEqual, "source") + _, err = NewArg("$.source_") + So(err, ShouldNotBeNil) + }) + Convey("test new define", func() { + arg, err := NewArg("") + So(err, ShouldBeNil) + So(arg.Type(), ShouldEqual, Define) + So(arg.Name(), ShouldEqual, "var") + }) + Convey("test constants string", func() { + arg, err := NewArg("data.key") + So(err, ShouldBeNil) + So(arg.Type(), ShouldEqual, Constant) + So(arg.Name(), ShouldEqual, "data.key") + }) + Convey("test constants number", func() { + arg, err := NewArg(3) + So(err, ShouldBeNil) + So(arg.Type(), ShouldEqual, Constant) + So(arg.Name(), ShouldEqual, "3") + }) + Convey("test constants bool", func() { + arg, err := NewArg(true) + So(err, ShouldBeNil) + So(arg.Type(), ShouldEqual, Constant) + So(arg.Name(), ShouldEqual, "true") + }) + }) +} + +func TestArgEvaluate(t *testing.T) { + Convey("test new arg evaluate", t, func() { + event := ce.NewEvent() + event.SetID("idValue") + event.SetSource("sourceValue") + ceCtx := &vContext.EventContext{ + Event: &event, + Define: map[string]interface{}{ + "var1": "str", + "var2": 123.456, + "var3": true, + }, + Data: map[string]interface{}{ + "key1": "strData", + "key2": 456.123, + }, + } + Convey("test event data", func() { + arg, err := NewArg("$.data.key1") + So(err, ShouldBeNil) + v, err := arg.Evaluate(ceCtx) + So(err, ShouldBeNil) + So(v, ShouldEqual, "strData") + arg, err = NewArg("$.data.key2") + So(err, ShouldBeNil) + v, err = arg.Evaluate(ceCtx) + So(err, ShouldBeNil) + So(v, ShouldEqual, 456.123) + }) + Convey("test event attribute", func() { + arg, err := NewArg("$.source") + So(err, ShouldBeNil) + v, err := arg.Evaluate(ceCtx) + So(err, ShouldBeNil) + So(v, ShouldEqual, "sourceValue") + arg, err = NewArg("$.abc") + So(err, ShouldBeNil) + v, err = arg.Evaluate(ceCtx) + So(err, ShouldNotBeNil) + So(v, ShouldBeNil) + }) + Convey("test define", func() { + arg, err := NewArg("") + So(err, ShouldBeNil) + v, err := arg.Evaluate(ceCtx) + So(err, ShouldBeNil) + So(v, ShouldEqual, "str") + arg, err = NewArg("") + So(err, ShouldBeNil) + v, err = arg.Evaluate(ceCtx) + So(err, ShouldBeNil) + So(v, ShouldEqual, 123.456) + }) + Convey("test define empty", func() { + arg, err := NewArg("") + So(err, ShouldBeNil) + v, err := arg.Evaluate(ceCtx) + So(err, ShouldNotBeNil) + So(v, ShouldBeNil) + ceCtx.Define = map[string]interface{}{} + arg, err = NewArg("") + So(err, ShouldBeNil) + v, err = arg.Evaluate(ceCtx) + So(err, ShouldNotBeNil) + So(v, ShouldBeNil) + }) + Convey("test constants string", func() { + arg, err := NewArg("data.key") + So(err, ShouldBeNil) + v, err := arg.Evaluate(ceCtx) + So(err, ShouldBeNil) + So(v, ShouldEqual, "data.key") + }) + Convey("test constants number", func() { + arg, err := NewArg(3) + So(err, ShouldBeNil) + v, err := arg.Evaluate(ceCtx) + So(err, ShouldBeNil) + So(v, ShouldEqual, 3) + }) + Convey("test constants bool", func() { + arg, err := NewArg(true) + So(err, ShouldBeNil) + v, err := arg.Evaluate(ceCtx) + So(err, ShouldBeNil) + So(v, ShouldEqual, true) + }) + }) +} diff --git a/internal/primitive/transform/arg/constant.go b/internal/primitive/transform/arg/constant.go new file mode 100644 index 000000000..954142657 --- /dev/null +++ b/internal/primitive/transform/arg/constant.go @@ -0,0 +1,56 @@ +// 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 arg + +import ( + "fmt" + + "github.com/linkall-labs/vanus/internal/primitive/transform/context" +) + +type constant struct { + value interface{} +} + +func newConstant(value interface{}) Arg { + return constant{ + value: value, + } +} + +func (arg constant) Type() Type { + return Constant +} +func (arg constant) Name() string { + return arg.Original() +} +func (arg constant) Original() string { + if v, ok := arg.value.(string); ok { + return v + } + return fmt.Sprintf("%v", arg.value) +} + +func (arg constant) Evaluate(*context.EventContext) (interface{}, error) { + return arg.value, nil +} + +func (arg constant) SetValue(*context.EventContext, interface{}) error { + return ErrOperationNotSupport +} + +func (arg constant) DeleteValue(ceCtx *context.EventContext) error { + return ErrOperationNotSupport +} diff --git a/internal/primitive/transform/arg/define.go b/internal/primitive/transform/arg/define.go new file mode 100644 index 000000000..5ee105e75 --- /dev/null +++ b/internal/primitive/transform/arg/define.go @@ -0,0 +1,63 @@ +// 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 arg + +import ( + "github.com/linkall-labs/vanus/internal/primitive/transform/context" +) + +type define struct { + name string + original string +} + +// newDefine name format is . +func newDefine(name string) Arg { + return define{ + name: name[1 : len(name)-1], + original: name, + } +} + +func (arg define) Type() Type { + return Define +} + +func (arg define) Name() string { + return arg.name +} + +func (arg define) Original() string { + return arg.original +} + +func (arg define) Evaluate(ceCtx *context.EventContext) (interface{}, error) { + if len(ceCtx.Define) == 0 { + return nil, ErrArgValueNil + } + v, exist := ceCtx.Define[arg.name] + if !exist { + return nil, ErrArgValueNil + } + return v, nil +} + +func (arg define) SetValue(*context.EventContext, interface{}) error { + return ErrOperationNotSupport +} + +func (arg define) DeleteValue(*context.EventContext) error { + return ErrOperationNotSupport +} diff --git a/internal/primitive/transform/arg/error.go b/internal/primitive/transform/arg/error.go new file mode 100644 index 000000000..0ebd8e6ea --- /dev/null +++ b/internal/primitive/transform/arg/error.go @@ -0,0 +1,22 @@ +// 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 arg + +import "fmt" + +var ( + ErrOperationNotSupport = fmt.Errorf("operation not support") + ErrArgValueNil = fmt.Errorf("arg value is nil") +) diff --git a/internal/primitive/transform/arg/event.go b/internal/primitive/transform/arg/event.go new file mode 100644 index 000000000..060e9f27b --- /dev/null +++ b/internal/primitive/transform/arg/event.go @@ -0,0 +1,140 @@ +// 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 arg + +import ( + "strings" + + "github.com/pkg/errors" + + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + + "github.com/linkall-labs/vanus/internal/trigger/util" + pkgUtil "github.com/linkall-labs/vanus/pkg/util" +) + +type eventAttribute struct { + attr string + original string +} + +// newEventAttribute name format is $.source . +func newEventAttribute(name string) (Arg, error) { + attr := strings.ToLower(name[2:]) + err := pkgUtil.ValidateEventAttrName(attr) + if err != nil { + return nil, err + } + return eventAttribute{ + attr: attr, + original: name, + }, nil +} + +func (arg eventAttribute) Type() Type { + return EventAttribute +} +func (arg eventAttribute) Name() string { + return arg.attr +} + +func (arg eventAttribute) Original() string { + return arg.original +} + +func (arg eventAttribute) Evaluate(ceCtx *context.EventContext) (interface{}, error) { + v, exist := util.LookupAttribute(*ceCtx.Event, arg.attr) + if !exist { + return nil, ErrArgValueNil + } + return v, nil +} + +func (arg eventAttribute) SetValue(ceCtx *context.EventContext, value interface{}) error { + return util.SetAttribute(ceCtx.Event, arg.attr, value) +} + +func (arg eventAttribute) DeleteValue(ceCtx *context.EventContext) error { + return util.DeleteAttribute(ceCtx.Event, arg.attr) +} + +type eventData struct { + path string + original string + data bool +} + +// newEventData name format is $.data.key . +func newEventData(name string) Arg { + var data bool + var path string + if name == EventDataArgPrefix { + data = true + path = name + } else { + path = name[7:] + } + return eventData{ + data: data, + path: path, + original: name, + } +} + +func (arg eventData) Type() Type { + return EventData +} + +func (arg eventData) Name() string { + return arg.path +} + +func (arg eventData) Original() string { + return arg.original +} + +func (arg eventData) Evaluate(ceCtx *context.EventContext) (interface{}, error) { + if arg.data { + return ceCtx.Data, nil + } + v, err := util.LookupData(ceCtx.Data, EventArgPrefix+arg.path) + if err != nil { + if errors.Is(err, util.ErrKeyNotFound) { + return nil, ErrArgValueNil + } + return nil, err + } + if v == nil { + return nil, ErrArgValueNil + } + return v, nil +} + +func (arg eventData) SetValue(ceCtx *context.EventContext, value interface{}) error { + if arg.data { + ceCtx.Data = value + return nil + } + util.SetData(ceCtx.Data, arg.path, value) + return nil +} + +func (arg eventData) DeleteValue(ceCtx *context.EventContext) error { + if arg.data { + ceCtx.Data = map[string]interface{}{} + return nil + } + return util.DeleteData(ceCtx.Data, arg.path) +} diff --git a/internal/primitive/transform/context/event_context.go b/internal/primitive/transform/context/event_context.go new file mode 100644 index 000000000..e3d3da185 --- /dev/null +++ b/internal/primitive/transform/context/event_context.go @@ -0,0 +1,23 @@ +// 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 context + +import ce "github.com/cloudevents/sdk-go/v2" + +type EventContext struct { + Event *ce.Event + Define map[string]interface{} + Data interface{} +} diff --git a/internal/primitive/transform/function/cast.go b/internal/primitive/transform/function/cast.go new file mode 100644 index 000000000..a50808646 --- /dev/null +++ b/internal/primitive/transform/function/cast.go @@ -0,0 +1,68 @@ +// 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 function + +import ( + "fmt" + "strconv" + "strings" +) + +func Cast(val interface{}, target Type) (interface{}, error) { + if target.IsSameType(val) { + return val, nil + } + switch target { + case String: + switch value := val.(type) { + case int32: // ce attribute + return strconv.Itoa(int(value)), nil + case float64: // ce data json marshal + return strconv.FormatFloat(value, 'f', -1, 64), nil + case bool: + return strconv.FormatBool(value), nil + } + // Casting to string is always defined + return fmt.Sprintf("%v", val), nil + case Number: + switch value := val.(type) { + case string: + v, err := strconv.ParseFloat(value, 64) + if err != nil { + err = fmt.Errorf("cannot cast from String to Float: %w", err) + } + return v, err + case int32: + return float64(value), nil + case int64: + return float64(value), nil + } + return 0, fmt.Errorf("undefined cast from %v to %v", TypeFromVal(val), target) + case Bool: + if value, ok := val.(string); ok { + lowerCase := strings.ToLower(value) + if lowerCase == "true" { + return true, nil + } else if lowerCase == "false" { + return false, nil + } + return false, fmt.Errorf("cannot cast String to Bool, actual value: %v", val) + } + return false, fmt.Errorf("undefined cast from %v to %v", TypeFromVal(val), target) + } + + // AnyType doesn't need casting + return val, nil +} diff --git a/internal/primitive/transform/function/format_functions.go b/internal/primitive/transform/function/format_functions.go new file mode 100644 index 000000000..5e424f2d6 --- /dev/null +++ b/internal/primitive/transform/function/format_functions.go @@ -0,0 +1,55 @@ +// 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 function + +import ( + "strings" + "time" +) + +// convertTimeFormat2Go convert "yyyy-mm-dd HH:MM:SS" to "2006-01-02 15:04:05". +func convertTimeFormat2Go(format string) string { + v := strings.ReplaceAll(format, "yyyy", "2006") + v = strings.ReplaceAll(v, "mm", "01") + v = strings.ReplaceAll(v, "dd", "02") + v = strings.ReplaceAll(v, "HH", "15") + v = strings.ReplaceAll(v, "MM", "04") + v = strings.ReplaceAll(v, "SS", "05") + return v +} + +var FormatDateFunction = function{ + name: "FORMAT_DATE", + fixedArgs: []Type{String, String, String}, + fn: func(args []interface{}) (interface{}, error) { + fromFormat := convertTimeFormat2Go(args[1].(string)) + t, err := time.Parse(fromFormat, args[0].(string)) + if err != nil { + return nil, err + } + toFormat := convertTimeFormat2Go(args[2].(string)) + return t.Format(toFormat), nil + }, +} + +var FormatUnixTimeFunction = function{ + name: "FORMAT_UNIX_TIME", + fixedArgs: []Type{Number, String}, + fn: func(args []interface{}) (interface{}, error) { + t := time.Unix(int64(args[0].(float64)), 0) + toFormat := convertTimeFormat2Go(args[1].(string)) + return t.UTC().Format(toFormat), nil + }, +} diff --git a/internal/primitive/transform/function/function.go b/internal/primitive/transform/function/function.go new file mode 100644 index 000000000..993bb98ce --- /dev/null +++ b/internal/primitive/transform/function/function.go @@ -0,0 +1,58 @@ +// 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 function + +type Function interface { + // Name func name + Name() string + // Arity arg number + Arity() int + // ArgType arg type + ArgType(index int) *Type + // IsVariadic is exist variadic + IsVariadic() bool + // Execute cal func result + Execute(args []interface{}) (interface{}, error) +} + +type function struct { + name string + fixedArgs []Type + variadicArgs *Type + fn func(args []interface{}) (interface{}, error) +} + +func (f function) Name() string { + return f.name +} + +func (f function) Arity() int { + return len(f.fixedArgs) +} + +func (f function) ArgType(index int) *Type { + if index < len(f.fixedArgs) { + return &f.fixedArgs[index] + } + return f.variadicArgs +} + +func (f function) IsVariadic() bool { + return f.variadicArgs != nil +} + +func (f function) Execute(args []interface{}) (interface{}, error) { + return f.fn(args) +} diff --git a/internal/primitive/transform/function/math_functions.go b/internal/primitive/transform/function/math_functions.go new file mode 100644 index 000000000..7bdb89448 --- /dev/null +++ b/internal/primitive/transform/function/math_functions.go @@ -0,0 +1,64 @@ +// 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 function + +import "fmt" + +var MathAddFunction = function{ + name: "MATH_ADD", + fixedArgs: []Type{Number, Number}, + variadicArgs: TypePtr(Number), + fn: func(args []interface{}) (interface{}, error) { + var sum float64 + for i := 0; i < len(args); i++ { + v, _ := args[i].(float64) + sum += v + } + return sum, nil + }, +} + +var MathSubFunction = function{ + name: "MATH_SUB", + fixedArgs: []Type{Number, Number}, + fn: func(args []interface{}) (interface{}, error) { + return args[0].(float64) - args[1].(float64), nil + }, +} + +var MathMulFunction = function{ + name: "MATH_MUL", + fixedArgs: []Type{Number, Number}, + variadicArgs: TypePtr(Number), + fn: func(args []interface{}) (interface{}, error) { + sum := float64(1) + for i := 0; i < len(args); i++ { + v, _ := args[i].(float64) + sum *= v + } + return sum, nil + }, +} + +var MathDivFunction = function{ + name: "MATH_DIV", + fixedArgs: []Type{Number, Number}, + fn: func(args []interface{}) (interface{}, error) { + if args[1].(float64) == 0 { + return nil, fmt.Errorf("division by zero") + } + return args[0].(float64) / args[1].(float64), nil + }, +} diff --git a/internal/primitive/transform/function/strings_functions.go b/internal/primitive/transform/function/strings_functions.go new file mode 100644 index 000000000..5ece2a4ee --- /dev/null +++ b/internal/primitive/transform/function/strings_functions.go @@ -0,0 +1,79 @@ +// 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 function + +import "strings" + +var JoinFunction = function{ + name: "JOIN", + fixedArgs: []Type{String, String, String}, + variadicArgs: TypePtr(String), + fn: func(args []interface{}) (interface{}, error) { + separator, _ := args[0].(string) + var sb strings.Builder + for i := 1; i < len(args)-1; i++ { + sb.WriteString(args[i].(string)) + sb.WriteString(separator) + } + sb.WriteString(args[len(args)-1].(string)) + return sb.String(), nil + }, +} + +var UpperFunction = function{ + name: "UPPER_CASE", + fixedArgs: []Type{String}, + fn: func(args []interface{}) (interface{}, error) { + return strings.ToUpper(args[0].(string)), nil + }, +} + +var LowerFunction = function{ + name: "LOWER_CASE", + fixedArgs: []Type{String}, + fn: func(args []interface{}) (interface{}, error) { + return strings.ToLower(args[0].(string)), nil + }, +} + +var AddPrefixFunction = function{ + name: "ADD_PREFIX", + fixedArgs: []Type{String, String}, + fn: func(args []interface{}) (interface{}, error) { + return args[1].(string) + args[0].(string), nil + }, +} + +var AddSuffixFunction = function{ + name: "ADD_SUFFIX", + fixedArgs: []Type{String, String}, + fn: func(args []interface{}) (interface{}, error) { + return args[0].(string) + args[1].(string), nil + }, +} + +var SplitWithSepFunction = function{ + name: "SPLIT_WITH_SEP", + fixedArgs: []Type{String, String}, + variadicArgs: TypePtr(Number), + fn: func(args []interface{}) (interface{}, error) { + s, _ := args[0].(string) + sep, _ := args[1].(string) + if len(args) == 2 { + return strings.Split(s, sep), nil + } + return strings.SplitN(s, sep, int(args[2].(float64))), nil + }, +} diff --git a/internal/primitive/transform/function/types.go b/internal/primitive/transform/function/types.go new file mode 100644 index 000000000..aee729da3 --- /dev/null +++ b/internal/primitive/transform/function/types.go @@ -0,0 +1,68 @@ +// 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 function + +type Type uint8 + +const ( + String Type = iota + Number + Bool + Object + Array + Any +) + +func (t Type) String() string { + switch t { + case String: + return "string" + case Number: + return "number" + case Bool: + return "bool" + case Object: + return "map" + case Array: + return "array" + case Any: + return "any" + } + return "unknown" +} + +func TypePtr(t Type) *Type { + return &t +} + +func (t Type) IsSameType(val interface{}) bool { + return TypeFromVal(val) == t +} + +func TypeFromVal(val interface{}) Type { + switch val.(type) { + case string: + return String + case float64: + return Number + case bool: + return Bool + case map[string]interface{}: + return Object + case []interface{}: + return Array + } + return Any +} diff --git a/internal/trigger/filter/prefix_filter.go b/internal/trigger/filter/prefix_filter.go index 04de4527b..5fd664c1d 100644 --- a/internal/trigger/filter/prefix_filter.go +++ b/internal/trigger/filter/prefix_filter.go @@ -21,6 +21,7 @@ import ( "github.com/linkall-labs/vanus/internal/trigger/util" "github.com/linkall-labs/vanus/observability/log" + pkgUtil "github.com/linkall-labs/vanus/pkg/util" ce "github.com/cloudevents/sdk-go/v2" ) @@ -45,7 +46,7 @@ func NewPrefixFilter(prefix map[string]string) Filter { func (filter *prefixFilter) Filter(event ce.Event) Result { for attr, prefix := range filter.prefix { value, ok := util.LookupAttribute(event, attr) - if !ok || !strings.HasPrefix(value, prefix) { + if !ok || !strings.HasPrefix(pkgUtil.StringValue(value), prefix) { return FailFilter } } diff --git a/internal/trigger/filter/suffix_filter.go b/internal/trigger/filter/suffix_filter.go index b9963d9f1..a771ef2f0 100644 --- a/internal/trigger/filter/suffix_filter.go +++ b/internal/trigger/filter/suffix_filter.go @@ -21,6 +21,7 @@ import ( "github.com/linkall-labs/vanus/internal/trigger/util" "github.com/linkall-labs/vanus/observability/log" + pkgUtil "github.com/linkall-labs/vanus/pkg/util" ce "github.com/cloudevents/sdk-go/v2" ) @@ -45,7 +46,7 @@ func NewSuffixFilter(suffix map[string]string) Filter { func (filter *suffixFilter) Filter(event ce.Event) Result { for attr, suffix := range filter.suffix { value, ok := util.LookupAttribute(event, attr) - if !ok || !strings.HasSuffix(value, suffix) { + if !ok || !strings.HasSuffix(pkgUtil.StringValue(value), suffix) { return FailFilter } } diff --git a/internal/trigger/transform/define/define.go b/internal/trigger/transform/define/define.go new file mode 100644 index 000000000..af44e05c4 --- /dev/null +++ b/internal/trigger/transform/define/define.go @@ -0,0 +1,66 @@ +// 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 define + +import ( + stdCtx "context" + + "github.com/linkall-labs/vanus/internal/primitive/transform/arg" + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + "github.com/linkall-labs/vanus/observability/log" +) + +type Define struct { + args map[string]arg.Arg +} + +func NewDefine() *Define { + return &Define{ + args: map[string]arg.Arg{}, + } +} + +func (d *Define) Parse(define map[string]string) { + if len(define) == 0 { + return + } + for key, value := range define { + _arg, err := arg.NewArg(value) + if err != nil { + log.Warning(stdCtx.TODO(), "arg is invalid", map[string]interface{}{ + log.KeyError: err, + "argName": value, + }) + continue + } + d.args[key] = _arg + } +} + +func (d *Define) EvaluateValue(ceCtx *context.EventContext) (map[string]interface{}, error) { + maps := make(map[string]interface{}, len(d.args)) + for k, v := range d.args { + value, err := v.Evaluate(ceCtx) + if err != nil { + log.Warning(stdCtx.TODO(), "define var evaluate error", map[string]interface{}{ + log.KeyError: err, + "name": v.Original(), + "type": v.Type(), + }) + } + maps[k] = value + } + return maps, nil +} diff --git a/internal/trigger/transform/define/parse.go b/internal/trigger/transform/define/parse.go deleted file mode 100644 index 201617456..000000000 --- a/internal/trigger/transform/define/parse.go +++ /dev/null @@ -1,83 +0,0 @@ -// 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 define - -import ( - "strings" -) - -type Type int - -const ( - Constant Type = iota - ContextVariable - DataVariable -) - -type Node struct { - Type Type - Key string - Value string -} - -type Parser struct { - nodes map[string]Node - hasDataVariable bool -} - -func NewParse() *Parser { - return &Parser{ - nodes: map[string]Node{}, - } -} - -func (p *Parser) HasDataVariable() bool { - return p.hasDataVariable -} - -func (p *Parser) GetNodes() map[string]Node { - return p.nodes -} - -func (p *Parser) GetNode(keyName string) (Node, bool) { - n, exist := p.nodes[keyName] - return n, exist -} - -func (p *Parser) Parse(input map[string]string) { - if len(input) == 0 { - return - } - nodeMap := make(map[string]Node, len(input)) - for k, v := range input { - if len(v) <= 2 || v[:2] != "$." { - nodeMap[k] = Node{Type: Constant, Key: k, Value: v} - continue - } - - keys := strings.Split(v[2:], ".") - if keys[0] == "data" { - if len(keys) == 1 { - nodeMap[k] = Node{Type: DataVariable, Key: k} - } else { - p.hasDataVariable = true - nodeMap[k] = Node{Type: DataVariable, Key: k, Value: v[7:]} - } - } else { - nodeMap[k] = Node{Type: ContextVariable, Key: k, Value: v[2:]} - } - } - p.nodes = nodeMap -} diff --git a/internal/trigger/transform/define/parse_test.go b/internal/trigger/transform/define/parse_test.go deleted file mode 100644 index 2082dd637..000000000 --- a/internal/trigger/transform/define/parse_test.go +++ /dev/null @@ -1,84 +0,0 @@ -// 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 define_test - -import ( - "testing" - - "github.com/linkall-labs/vanus/internal/trigger/transform/define" - - // third-party. - . "github.com/smartystreets/goconvey/convey" -) - -func TestParse(t *testing.T) { - Convey("value is empty", t, func() { - p := define.NewParse() - p.Parse(map[string]string{}) - n := p.GetNodes() - So(len(n), ShouldEqual, 0) - }) - Convey("value is black", t, func() { - p := define.NewParse() - p.Parse(map[string]string{"k": " "}) - n, exist := p.GetNode("k") - So(exist, ShouldBeTrue) - So(n.Type, ShouldEqual, define.Constant) - }) - Convey("value is constant", t, func() { - p := define.NewParse() - p.Parse(map[string]string{"k": "ctx"}) - n, exist := p.GetNode("k") - So(exist, ShouldBeTrue) - So(n.Type, ShouldEqual, define.Constant) - So(n.Value, ShouldEqual, "ctx") - }) - - Convey("value is context", t, func() { - p := define.NewParse() - p.Parse(map[string]string{"k": "$.ctx"}) - n, exist := p.GetNode("k") - So(exist, ShouldBeTrue) - So(n.Type, ShouldEqual, define.ContextVariable) - So(n.Value, ShouldEqual, "ctx") - }) - - Convey("value is data", t, func() { - p := define.NewParse() - p.Parse(map[string]string{"k": "$.data"}) - n, exist := p.GetNode("k") - So(exist, ShouldBeTrue) - So(n.Type, ShouldEqual, define.DataVariable) - So(n.Value, ShouldEqual, "") - }) - - Convey("value is data one", t, func() { - p := define.NewParse() - p.Parse(map[string]string{"k": "$.data.one"}) - n, exist := p.GetNode("k") - So(exist, ShouldBeTrue) - So(n.Type, ShouldEqual, define.DataVariable) - So(n.Value, ShouldEqual, "one") - }) - - Convey("value is data two", t, func() { - p := define.NewParse() - p.Parse(map[string]string{"k": "$.data.one.two"}) - n, exist := p.GetNode("k") - So(exist, ShouldBeTrue) - So(n.Type, ShouldEqual, define.DataVariable) - So(n.Value, ShouldEqual, "one.two") - }) -} diff --git a/internal/trigger/transform/pipeline/pipeline.go b/internal/trigger/transform/pipeline/pipeline.go new file mode 100644 index 000000000..9a26ac1d5 --- /dev/null +++ b/internal/trigger/transform/pipeline/pipeline.go @@ -0,0 +1,63 @@ +// 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 pipeline + +import ( + stdCtx "context" + + "github.com/linkall-labs/vanus/internal/primitive" + "github.com/linkall-labs/vanus/internal/primitive/transform/action" + "github.com/linkall-labs/vanus/internal/primitive/transform/context" + "github.com/linkall-labs/vanus/observability/log" +) + +type Pipeline struct { + actions []action.Action +} + +func NewPipeline() *Pipeline { + return &Pipeline{ + actions: make([]action.Action, 0), + } +} + +func (p *Pipeline) Parse(actions []*primitive.Action) { + p.actions = make([]action.Action, 0, len(actions)) + for i := range actions { + _action, err := action.NewAction(actions[i].Command) + if err != nil { + // it has check in controller so err must be nil otherwise controller check has bug + log.Warning(stdCtx.TODO(), "new action error", map[string]interface{}{ + log.KeyError: err, + "command": actions[i].Command, + }) + continue + } + p.actions = append(p.actions, _action) + } +} + +func (p *Pipeline) Run(ceCtx *context.EventContext) error { + for _, a := range p.actions { + err := a.Execute(ceCtx) + if err != nil { + log.Warning(stdCtx.TODO(), "action execute error", map[string]interface{}{ + log.KeyError: err, + "command": a.Name(), + }) + } + } + return nil +} diff --git a/internal/trigger/transform/template/exec.go b/internal/trigger/transform/template/exec.go index 7b60935b5..30f40ae8f 100644 --- a/internal/trigger/transform/template/exec.go +++ b/internal/trigger/transform/template/exec.go @@ -15,94 +15,48 @@ package template import ( - "strings" -) - -type DataType int + "bytes" -const ( - Null = iota - Text - Other + jsoniter "github.com/json-iterator/go" ) -type Data struct { - DataType - Raw []byte -} - -func (d Data) String() string { - switch d.DataType { - case Null: - return "null" - default: - return string(d.Raw) - } -} - -func NewNullData() Data { - return Data{DataType: Null} -} - -func NewTextData(d []byte) Data { - return Data{Text, d} -} - -func NewOtherData(d []byte) Data { - return Data{Other, d} -} - -func (p *Parser) executeJSON(data map[string]Data) string { - var sb strings.Builder - for _, node := range p.GetNodes() { +func (t *Template) Execute(data map[string]interface{}) []byte { + var sb bytes.Buffer + stream := jsoniter.ConfigFastest.BorrowStream(&sb) + defer jsoniter.ConfigFastest.ReturnStream(stream) + for _, node := range t.parser.getNodes() { switch node.Type() { case Constant: - sb.WriteString(node.Value()) + stream.WriteRaw(node.Value()) case Variable: v, exist := data[node.Value()] - if !exist || v.DataType == Null { - sb.WriteString("null") - } - if v.DataType == Text { - sb.WriteString("\"") - } - sb.Write(v.Raw) - if v.DataType == Text { - sb.WriteString("\"") + if !exist { + stream.WriteString("<" + node.Value() + ">") + continue } + stream.WriteVal(v) case StringVariable: v, exist := data[node.Value()] - if !exist || v.DataType == Null { + if !exist { + stream.WriteRaw("<" + node.Value() + ">") continue } - sb.Write(v.Raw) - } - } - return sb.String() -} - -func (p *Parser) executeText(data map[string]Data) string { - var sb strings.Builder - for _, node := range p.GetNodes() { - switch node.Type() { - case Constant: - sb.WriteString(node.Value()) - case Variable, StringVariable: - v, exist := data[node.Value()] - if !exist || v.DataType == Null { + if v == nil { continue } - sb.Write(v.Raw) + // type string no need quota + switch val := v.(type) { + case string: + stream.WriteRaw(val) + case []interface{}: + stream.WriteRaw("[]") + case map[string]interface{}: + stream.WriteRaw("{}") + default: + stream.WriteVal(v) + } } } - return sb.String() -} - -func (p *Parser) Execute(data map[string]Data) string { - switch p.OutputType { - case JSON: - return p.executeJSON(data) - default: - return p.executeText(data) - } + stream.Flush() + return sb.Bytes() } diff --git a/internal/trigger/transform/template/exec_test.go b/internal/trigger/transform/template/exec_test.go index 2ea0246b6..4098b0eb0 100644 --- a/internal/trigger/transform/template/exec_test.go +++ b/internal/trigger/transform/template/exec_test.go @@ -20,116 +20,95 @@ import ( . "github.com/smartystreets/goconvey/convey" ) -func TestExecuteJsonString(t *testing.T) { - p := NewParser() - p.Parse(`{"key":"${str}"}`) - Convey("no data", t, func() { - m := make(map[string]Data) - v := p.Execute(m) - So(v, ShouldEqual, `{"key":""}`) - }) - Convey("null", t, func() { - m := make(map[string]Data) - m["str"] = NewNullData() - v := p.Execute(m) - So(v, ShouldEqual, `{"key":""}`) - }) - Convey("string", t, func() { - m := make(map[string]Data) - m["str"] = NewTextData([]byte("str")) - v := p.Execute(m) - So(v, ShouldEqual, `{"key":"str"}`) - }) - Convey("other num", t, func() { - m := make(map[string]Data) - m["str"] = NewOtherData([]byte("123")) - v := p.Execute(m) - So(v, ShouldEqual, `{"key":"123"}`) - }) -} - -func TestExecuteJsonValue(t *testing.T) { - p := NewParser() - p.Parse(`{"key":${str}}`) - Convey("no data", t, func() { - m := make(map[string]Data) - v := p.Execute(m) - So(v, ShouldEqual, `{"key":null}`) - }) - Convey("null", t, func() { - m := make(map[string]Data) - m["str"] = NewNullData() - v := p.Execute(m) - So(v, ShouldEqual, `{"key":null}`) - }) - Convey("string", t, func() { - m := make(map[string]Data) - m["str"] = NewTextData([]byte("str")) - v := p.Execute(m) - So(v, ShouldEqual, `{"key":"str"}`) - }) - Convey("other num", t, func() { - m := make(map[string]Data) - m["str"] = NewOtherData([]byte("123")) - v := p.Execute(m) - So(v, ShouldEqual, `{"key":123}`) - }) - Convey("other bool", t, func() { - m := make(map[string]Data) - m["str"] = NewOtherData([]byte("true")) - v := p.Execute(m) - So(v, ShouldEqual, `{"key":true}`) - }) - - Convey("other obj", t, func() { - m := make(map[string]Data) - m["str"] = NewOtherData([]byte(`{"k":"v"}`)) - v := p.Execute(m) - So(v, ShouldEqual, `{"key":{"k":"v"}}`) - }) -} - -func TestExecuteText(t *testing.T) { - p := NewParser() - Convey("no parse", t, func() { - v := p.Execute(nil) - So(v, ShouldEqual, "") - }) - p.Parse(`abc ${str}`) - Convey("no data", t, func() { - m := make(map[string]Data) - v := p.Execute(m) - So(v, ShouldEqual, `abc `) - }) - Convey("null", t, func() { - m := make(map[string]Data) - m["str"] = NewNullData() - v := p.Execute(m) - So(v, ShouldEqual, `abc `) - }) - Convey("string", t, func() { - m := make(map[string]Data) - m["str"] = NewTextData([]byte("str")) - v := p.Execute(m) - So(v, ShouldEqual, `abc str`) - }) - Convey("other num", t, func() { - m := make(map[string]Data) - m["str"] = NewOtherData([]byte("123")) - v := p.Execute(m) - So(v, ShouldEqual, `abc 123`) - }) - Convey("other bool", t, func() { - m := make(map[string]Data) - m["str"] = NewOtherData([]byte("true")) - v := p.Execute(m) - So(v, ShouldEqual, `abc true`) - }) -} - -func TestDataString(t *testing.T) { - Convey("test string", t, func() { - So(NewNullData().String(), ShouldEqual, "null") - So(NewOtherData([]byte("str")).String(), ShouldEqual, "str") +func TestExecute(t *testing.T) { + Convey("test string var", t, func() { + p := NewTemplate() + p.Parse(`{"key":""}`) + Convey("no data", func() { + m := make(map[string]interface{}) + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":""}`) + }) + Convey("null", func() { + m := make(map[string]interface{}) + m["str"] = nil + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":""}`) + }) + Convey("string", func() { + m := make(map[string]interface{}) + m["str"] = "str" + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":"str"}`) + }) + Convey("number", func() { + m := make(map[string]interface{}) + m["str"] = 123.456 + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":"123.456"}`) + }) + Convey("bool", func() { + m := make(map[string]interface{}) + m["str"] = true + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":"true"}`) + }) + Convey("object", func() { + m := make(map[string]interface{}) + m["str"] = map[string]interface{}{"str": true} + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":"{}"}`) + }) + Convey("array", func() { + m := make(map[string]interface{}) + m["str"] = []interface{}{"str", true} + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":"[]"}`) + }) + }) + Convey("test var", t, func() { + p := NewTemplate() + p.Parse(`{"key":}`) + Convey("no data", func() { + m := make(map[string]interface{}) + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":""}`) + }) + Convey("null", func() { + m := make(map[string]interface{}) + m["str"] = nil + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":null}`) + }) + Convey("string", func() { + m := make(map[string]interface{}) + m["str"] = "str" + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":"str"}`) + }) + Convey("number", func() { + m := make(map[string]interface{}) + m["str"] = 123.456 + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":123.456}`) + }) + Convey("bool", func() { + m := make(map[string]interface{}) + m["str"] = true + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":true}`) + }) + Convey("obj", func() { + m := make(map[string]interface{}) + m["str"] = map[string]interface{}{"k": "v"} + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":{"k":"v"}}`) + }) + Convey("array", func() { + m := make(map[string]interface{}) + m["str"] = []interface{}{"str", 123.456} + v := p.Execute(m) + So(string(v), ShouldEqual, `{"key":["str",123.456]}`) + }) }) } diff --git a/internal/trigger/transform/template/node.go b/internal/trigger/transform/template/node.go index f6b433e02..191fa6f66 100644 --- a/internal/trigger/transform/template/node.go +++ b/internal/trigger/transform/template/node.go @@ -22,8 +22,8 @@ func (t NodeType) Type() NodeType { const ( Constant NodeType = iota // Plain text. - Variable // A ${var} variable. - StringVariable // A ${var} variable. + Variable // A variable, example "key": + StringVariable // A variable, example "key":"" or "key":"other " ) type Node interface { @@ -36,7 +36,7 @@ type ConstantNode struct { Text string } -func (p *Parser) newConstant(text string) *ConstantNode { +func (p *parser) newConstant(text string) *ConstantNode { return &ConstantNode{Text: text, NodeType: Constant} } @@ -49,23 +49,10 @@ type VariableNode struct { Name string } -func (p *Parser) newVariable(name string) *VariableNode { - return &VariableNode{Name: name, NodeType: Variable} +func (p *parser) newVariable(name string, nodeType NodeType) *VariableNode { + return &VariableNode{Name: name, NodeType: nodeType} } func (t *VariableNode) Value() string { return t.Name } - -type StringVariableNode struct { - NodeType - Name string -} - -func (p *Parser) newStringVariable(name string) *VariableNode { - return &VariableNode{Name: name, NodeType: StringVariable} -} - -func (t *StringVariableNode) Value() string { - return t.Name -} diff --git a/internal/trigger/transform/template/parse.go b/internal/trigger/transform/template/parse.go index cb78e8f12..3b7739a07 100644 --- a/internal/trigger/transform/template/parse.go +++ b/internal/trigger/transform/template/parse.go @@ -20,61 +20,62 @@ import ( "github.com/linkall-labs/vanus/pkg/util" ) -type OutputType int +type Template struct { + parser *parser + exist bool +} -const ( - TEXT = iota - JSON -) +func NewTemplate() *Template { + return &Template{ + parser: newParser(), + } +} + +func (t *Template) Exist() bool { + return t.exist +} -type Parser struct { +func (t *Template) Parse(text string) { + if text == "" { + t.exist = false + return + } + t.exist = true + t.parser.parse(text) +} + +type parser struct { leftDelim string rightDelim string nodes []Node - OutputType OutputType } -func NewParser() *Parser { - p := &Parser{} +func newParser() *parser { + p := &parser{} p.init() return p } -func (p *Parser) init() { - p.leftDelim = "${" - p.rightDelim = "}" +func (p *parser) init() { + p.leftDelim = "<" + p.rightDelim = ">" } -func (p *Parser) GetNodes() []Node { +func (p *parser) getNodes() []Node { return p.nodes } -func (p *Parser) addNode(node Node) { +func (p *parser) addNode(node Node) { p.nodes = append(p.nodes, node) } -func (p *Parser) parseType(text string) { - for pos := 0; pos < len(text); pos++ { - c := text[pos] - if util.IsSpace(c) { - continue - } - if c == '{' { - p.OutputType = JSON - } else { - p.OutputType = TEXT - } - break - } -} - // isJSONKeyColon check colon is key end colon,maybe: -// "key": ${v} -// "key": ":${v}" -// "key": "other:${v}" -// "key": "\":${v}" . +// "key": +// "key": ":" +// "key": "other:" +// "key": "\":" . func isJSONKeyColon(text string, pos int) bool { - var hasQuota bool + var hasQuote bool for i := pos; i >= 0; i-- { c := text[i] if util.IsSpace(c) { @@ -82,19 +83,20 @@ func isJSONKeyColon(text string, pos int) bool { } switch c { case '"': - if hasQuota { + if hasQuote { return false } - hasQuota = true + hasQuote = true case '\\', ':': return false default: - return hasQuota + return hasQuote } } return false } -func isStringVar(text string, pos int) bool { + +func (p *parser) parseVarNodeType(text string, pos int) NodeType { for i := pos; i >= 0; i-- { c := text[i] if util.IsSpace(c) { @@ -102,18 +104,20 @@ func isStringVar(text string, pos int) bool { } switch c { case '"': - return true + return StringVariable case ':': // 是否是json key后面的冒号 b := isJSONKeyColon(text, i-1) - return !b + if b { + return Variable + } + return StringVariable } } - return false + return Variable } -func (p *Parser) Parse(text string) { - p.parseType(text) +func (p *parser) parse(text string) { var pos int leftDelimLen := len(p.leftDelim) rightDelimLen := len(p.rightDelim) @@ -126,20 +130,14 @@ func (p *Parser) Parse(text string) { ldp := pos + x + leftDelimLen y := strings.Index(text[ldp:], p.rightDelim) if y < 0 { - continue - } - var stringVar bool - if p.OutputType == JSON { - stringVar = isStringVar(text, pos+x-1) + p.addNode(p.newConstant(text[pos:])) + break } + varNodeType := p.parseVarNodeType(text, pos+x-1) if x > 0 { p.addNode(p.newConstant(text[pos : pos+x])) } - if stringVar { - p.addNode(p.newStringVariable(text[ldp : ldp+y])) - } else { - p.addNode(p.newVariable(text[ldp : ldp+y])) - } + p.addNode(p.newVariable(text[ldp:ldp+y], varNodeType)) pos = ldp + y + rightDelimLen if pos == len(text) { break diff --git a/internal/trigger/transform/template/parse_test.go b/internal/trigger/transform/template/parse_test.go index 887fd6c6c..6b32be225 100644 --- a/internal/trigger/transform/template/parse_test.go +++ b/internal/trigger/transform/template/parse_test.go @@ -22,73 +22,74 @@ import ( func TestParse(t *testing.T) { Convey("parse constants", t, func() { - p := NewParser() - p.Parse("constants") - So(len(p.GetNodes()), ShouldEqual, 1) - n := p.GetNodes()[0] + p := newParser() + p.parse("constants") + So(len(p.getNodes()), ShouldEqual, 1) + n := p.getNodes()[0] So(n.Type(), ShouldEqual, Constant) }) Convey("parse variable", t, func() { - p := NewParser() - p.Parse("${str}") - So(len(p.GetNodes()), ShouldEqual, 1) - n := p.GetNodes()[0] + p := newParser() + p.parse("") + So(len(p.getNodes()), ShouldEqual, 1) + n := p.getNodes()[0] So(n.Type(), ShouldEqual, Variable) So(n.Value(), ShouldEqual, "str") }) - Convey("parse text", t, func() { - p := NewParser() - p.Parse("begin ${str} end") - So(len(p.GetNodes()), ShouldEqual, 3) - n := p.GetNodes()[1] + p := newParser() + p.parse("begin end") + So(len(p.getNodes()), ShouldEqual, 3) + n := p.getNodes()[1] So(n.Type(), ShouldEqual, Variable) So(n.Value(), ShouldEqual, "str") }) - Convey("parse json", t, func() { - p := NewParser() - p.Parse(`{"key":"${str}","key2":${str2}}`) - So(len(p.GetNodes()), ShouldEqual, 5) - n := p.GetNodes()[1] + p := newParser() + p.parse(`{"key":"","key2":}`) + So(len(p.getNodes()), ShouldEqual, 5) + n := p.getNodes()[1] So(n.Type(), ShouldEqual, StringVariable) So(n.Value(), ShouldEqual, "str") - n = p.GetNodes()[2] + n = p.getNodes()[2] So(n.Type(), ShouldEqual, Constant) So(n.Value(), ShouldEqual, `","key2":`) - n = p.GetNodes()[3] + n = p.getNodes()[3] So(n.Type(), ShouldEqual, Variable) So(n.Value(), ShouldEqual, "str2") }) Convey("parse json with special symbol 1", t, func() { - p := NewParser() - p.Parse(` {"key": ":${str}"}`) - n := p.GetNodes()[1] + p := newParser() + p.parse(` {"key": ":"}`) + n := p.getNodes()[1] So(n.Type(), ShouldEqual, StringVariable) So(n.Value(), ShouldEqual, "str") }) Convey("parse json with special symbol 2", t, func() { - p := NewParser() - p.Parse(` {"key": "abc:${str}"}`) - n := p.GetNodes()[1] + p := newParser() + p.parse(` {"key": "abc:">`) + n := p.getNodes()[1] So(n.Type(), ShouldEqual, StringVariable) So(n.Value(), ShouldEqual, "str") }) Convey("parse json with special symbol 3", t, func() { - p := NewParser() - p.Parse(` {"key": "\":${str}"}`) - n := p.GetNodes()[1] + p := newParser() + p.parse(` {"key": "\":"}`) + n := p.getNodes()[1] So(n.Type(), ShouldEqual, StringVariable) So(n.Value(), ShouldEqual, "str") }) Convey("parse json with special symbol 4", t, func() { - p := NewParser() - p.Parse(` {"key": "\":${str} sdf: ${str2}"}`) - n := p.GetNodes()[1] + p := newParser() + p.parse(` {"key": "\": sdf: ,"data2": "","data3": ,"data4": ""}}` it := NewTransformer(input) it.Execute(&e) - So(string(e.Data()), ShouldEqual, `{"body": {"data": "value","data2": "value","data3": null,"data4": ""}}`) + So(string(e.Data()), ShouldEqual, `{"body": {"data": "value","data2": "value","data3": "","data4": ""}}`) }) Convey("test execute json with a part of value", func() { _ = e.SetData(ce.ApplicationJSON, map[string]interface{}{ "key": "value", "key1": "value1", }) - input.Template = ` {"body": {"data": "source is ${dataKey}","data2": "source is ${noExist}"}}` + input.Template = ` {"body": {"data": "source is ","data2": "source is "}}` it := NewTransformer(input) it.Execute(&e) - So(string(e.Data()), ShouldEqual, ` {"body": {"data": "source is value","data2": "source is "}}`) + So(string(e.Data()), ShouldEqual, ` {"body": {"data": "source is value","data2": "source is "}}`) }) Convey("test execute json with a part of value has colon", func() { _ = e.SetData(ce.ApplicationJSON, map[string]interface{}{ "key": "value", "key1": "value1", }) - input.Template = `{"body": {"data": ":${dataKey}","data2": "\":${dataKey}\"","data3": "::${dataKey} other:${ctxId}"}}` + input.Template = `{"body": {"data": ":","data2": "\":\"","data3": ":: other:"}}` it := NewTransformer(input) it.Execute(&e) So(string(e.Data()), ShouldEqual, `{"body": {"data": ":value","data2": "\":value\"","data3": "::value other:testId"}}`) @@ -152,10 +73,10 @@ func TestExecute(t *testing.T) { "key": "value", "key1": "value1", }) - input.Template = `{"body": {"data": "source is \"${dataKey}\"","data2": "source is \"${noExist}\""}}` + input.Template = `{"body": {"data": "source is \"\"","data2": "source is \"\""}}` it := NewTransformer(input) it.Execute(&e) - So(string(e.Data()), ShouldEqual, `{"body": {"data": "source is \"value\"","data2": "source is \"\""}}`) + So(string(e.Data()), ShouldEqual, `{"body": {"data": "source is \"value\"","data2": "source is \"\""}}`) }) }) } diff --git a/internal/trigger/util/event.go b/internal/trigger/util/event.go index 1acf1755e..9c74d75be 100644 --- a/internal/trigger/util/event.go +++ b/internal/trigger/util/event.go @@ -16,14 +16,15 @@ package util import ( "fmt" + "strings" + "time" ce "github.com/cloudevents/sdk-go/v2" + "github.com/oliveagle/jsonpath" ) -func LookupAttribute(event ce.Event, attr string) (string, bool) { - // Set standard context attributes. The attributes available may not be - // exactly the same as the attributes defined in the current version of the - // ce spec. +// LookupAttribute lookup event attribute value by attribute name. +func LookupAttribute(event ce.Event, attr string) (interface{}, bool) { switch attr { case "specversion": return event.SpecVersion(), true @@ -42,11 +43,164 @@ func LookupAttribute(event ce.Event, attr string) (string, bool) { case "datacontenttype": return event.DataContentType(), true default: - val, ok := event.Extensions()[attr] - var str string - if ok { - str = fmt.Sprintf("%v", val) + extensions := event.Context.AsV1().Extensions + if len(extensions) == 0 { + return nil, false } - return str, ok + val, ok := extensions[attr] + return val, ok + } +} + +// LookupData lookup event data value by JSON path. +func LookupData(data interface{}, path string) (interface{}, error) { + v, err := jsonpath.JsonPathLookup(data, path) + if err != nil { + if strings.Contains(err.Error(), "not found in object") { + return nil, ErrKeyNotFound + } + return nil, err + } + return v, nil +} + +var ( + ErrKeyNotFound = fmt.Errorf("data key not found") + errValueIsNil = fmt.Errorf("value is nil") + errAttributeValue = fmt.Errorf("attribute value is invalid") + errDeleteAttrNotSupport = fmt.Errorf("delete attribute not support") + + specAttributes = map[string]struct{}{ + "id": {}, + "source": {}, + "type": {}, + "specversion": {}, + } +) + +func SetAttribute(e *ce.Event, attr string, value interface{}) error { + if value == nil { + return errValueIsNil + } + event := e.Context + switch attr { + case "type": + v, ok := value.(string) + if !ok { + return errAttributeValue + } + return event.SetType(v) + case "source": + v, ok := value.(string) + if !ok { + return errAttributeValue + } + return event.SetSource(v) + case "subject": + v, ok := value.(string) + if !ok { + return errAttributeValue + } + return event.SetSubject(v) + case "id": + v, ok := value.(string) + if !ok { + return errAttributeValue + } + return event.SetID(v) + case "time": + var v time.Time + switch val := value.(type) { + case string: + ts, err := time.Parse(time.RFC3339Nano, val) + if err != nil { + return errAttributeValue + } + v = ts + case ce.Timestamp: + v = val.Time + case time.Time: + v = val + case *time.Time: + v = *val + default: + return errAttributeValue + } + return event.SetTime(v) + case "dataschema": + v, ok := value.(string) + if !ok { + return errAttributeValue + } + return event.SetDataSchema(v) + case "datacontenttype", "specversion": + return fmt.Errorf("attribute %s not support modify", attr) + default: + return event.SetExtension(attr, value) + } +} + +func DeleteAttribute(e *ce.Event, attr string) error { + if _, exist := specAttributes[attr]; exist { + return errDeleteAttrNotSupport + } + event, _ := e.Context.(*ce.EventContextV1) + if len(event.Extensions) == 0 { + return nil + } + delete(event.Extensions, attr) + return nil +} + +// SetData set value to data path, now data must is map, not support array. +func SetData(data interface{}, path string, value interface{}) { + paths := strings.Split(path, ".") + switch data.(type) { + case map[string]interface{}: + setData(data, paths, value) + case []interface{}: + // todo ,now not support + } +} + +func setData(data interface{}, paths []string, value interface{}) { + switch m := data.(type) { + case map[string]interface{}: + if len(paths) == 1 { + m[paths[0]] = value + return + } + v, ok := m[paths[0]] + if !ok { + v = make(map[string]interface{}) + m[paths[0]] = v + } + setData(v, paths[1:], value) + case []interface{}: + // todo ,now not support + } +} + +func DeleteData(data interface{}, path string) error { + paths := strings.Split(path, ".") + switch data.(type) { + case map[string]interface{}: + deleteData(data, paths) + case []interface{}: + // todo ,now not support + } + return nil +} + +func deleteData(data interface{}, paths []string) { + switch m := data.(type) { + case map[string]interface{}: + if len(paths) == 1 { + delete(m, paths[0]) + return + } + deleteData(m[paths[0]], paths[1:]) + case []interface{}: + // todo ,now not support } } diff --git a/internal/trigger/util/event_test.go b/internal/trigger/util/event_test.go new file mode 100644 index 000000000..7165f2acc --- /dev/null +++ b/internal/trigger/util/event_test.go @@ -0,0 +1,305 @@ +// 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 util + +import ( + "testing" + "time" + + ce "github.com/cloudevents/sdk-go/v2" + . "github.com/smartystreets/goconvey/convey" +) + +func TestLookupData(t *testing.T) { + Convey("test lookup data", t, func() { + data := map[string]interface{}{ + "map": map[string]interface{}{ + "number": 123.4, + "str": "str", + }, + "str": "stringV", + "array": []interface{}{1.1, "str"}, + } + Convey("get string", func() { + v, err := LookupData(data, "$.str") + So(err, ShouldBeNil) + So(v, ShouldEqual, "stringV") + }) + Convey("get map", func() { + v, err := LookupData(data, "$.map") + So(err, ShouldBeNil) + So(len(v.(map[string]interface{})), ShouldEqual, 2) + }) + Convey("get array", func() { + v, err := LookupData(data, "$.array") + So(err, ShouldBeNil) + So(len(v.([]interface{})), ShouldEqual, 2) + }) + }) +} + +func TestSetAttribute(t *testing.T) { + Convey("test set attribute", t, func() { + event := ce.NewEvent() + Convey("set event spec attribute", func() { + err := SetAttribute(&event, "id", "idV") + So(err, ShouldBeNil) + So(event.ID(), ShouldEqual, "idV") + err = SetAttribute(&event, "source", "sourceV") + So(err, ShouldBeNil) + So(event.Source(), ShouldEqual, "sourceV") + err = SetAttribute(&event, "type", "typeV") + So(err, ShouldBeNil) + So(event.Type(), ShouldEqual, "typeV") + err = SetAttribute(&event, "source", "sourceV") + So(err, ShouldBeNil) + So(event.Source(), ShouldEqual, "sourceV") + err = SetAttribute(&event, "subject", "subjectV") + So(err, ShouldBeNil) + So(event.Subject(), ShouldEqual, "subjectV") + err = SetAttribute(&event, "subject", "subjectV") + So(err, ShouldBeNil) + So(event.Subject(), ShouldEqual, "subjectV") + err = SetAttribute(&event, "dataschema", "http://schema.com/1") + So(err, ShouldBeNil) + So(event.DataSchema(), ShouldEqual, "http://schema.com/1") + Convey("test time", func() { + now := time.Now() + err = SetAttribute(&event, "time", now) + So(err, ShouldBeNil) + So(event.Time(), ShouldEqual, now) + err = SetAttribute(&event, "time", &now) + So(err, ShouldBeNil) + So(event.Time(), ShouldEqual, now) + err = SetAttribute(&event, "time", ce.Timestamp{Time: now}) + So(err, ShouldBeNil) + So(event.Time(), ShouldEqual, now) + err = SetAttribute(&event, "time", now.Format(time.RFC3339Nano)) + So(err, ShouldBeNil) + So(event.Time(), ShouldEqual, now) + }) + err = SetAttribute(&event, "datacontenttype", "json") + So(err, ShouldNotBeNil) + err = SetAttribute(&event, "specversion", "1.0") + So(err, ShouldNotBeNil) + }) + Convey("set event extension", func() { + err := SetAttribute(&event, "vanus", "vanusV") + So(err, ShouldBeNil) + So(event.Extensions()["vanus"], ShouldEqual, "vanusV") + err = SetAttribute(&event, "Vanus", "vanusV") + So(err, ShouldBeNil) + So(event.Extensions()["vanus"], ShouldEqual, "vanusV") + err = SetAttribute(&event, "vanus.vanus", "vanusV") + So(err, ShouldNotBeNil) + }) + Convey("set value nil", func() { + err := SetAttribute(&event, "id", nil) + So(err, ShouldNotBeNil) + err = SetAttribute(&event, "vanus", nil) + So(err, ShouldNotBeNil) + }) + }) +} + +func TestDeleteAttribute(t *testing.T) { + Convey("test delete attribute", t, func() { + event := ce.NewEvent() + event.SetID("idV") + event.SetSource("sourceV") + event.SetType("typeV") + event.SetExtension("vanus", "vanusV") + Convey("delete spec attribute", func() { + err := DeleteAttribute(&event, "id") + So(err, ShouldNotBeNil) + }) + Convey("delete extension attribute", func() { + Convey("delete exist", func() { + err := DeleteAttribute(&event, "vanus") + So(err, ShouldBeNil) + _, exist := event.Extensions()["vanus"] + So(exist, ShouldBeFalse) + }) + Convey("delete not exist", func() { + err := DeleteAttribute(&event, "van") + So(err, ShouldBeNil) + _, exist := event.Extensions()["van"] + So(exist, ShouldBeFalse) + }) + }) + }) +} + +func TestSetData(t *testing.T) { + Convey("test set event data", t, func() { + data := map[string]interface{}{ + "map": map[string]interface{}{ + "number": 123.4, + "str": "str", + }, + "str": "stringV", + "array": []interface{}{1.1, "str"}, + } + Convey("test add root key", func() { + Convey("add common", func() { + SetData(data, "addKey1", 1.1) + SetData(data, "addKey2", "str") + SetData(data, "addKey3", true) + v, exist := data["addKey1"] + So(exist, ShouldBeTrue) + So(v, ShouldEqual, 1.1) + v, exist = data["addKey2"] + So(exist, ShouldBeTrue) + So(v, ShouldEqual, "str") + v, exist = data["addKey3"] + So(exist, ShouldBeTrue) + So(v, ShouldEqual, true) + }) + Convey("add map", func() { + SetData(data, "addKey", map[string]interface{}{"k": "v"}) + v, exist := data["addKey"].(map[string]interface{})["k"] + So(exist, ShouldBeTrue) + So(v, ShouldEqual, "v") + }) + Convey("add array", func() { + SetData(data, "addKey", []interface{}{123, "str"}) + v, exist := data["addKey"].([]interface{}) + So(exist, ShouldBeTrue) + So(v[0], ShouldEqual, 123) + So(v[1], ShouldEqual, "str") + }) + }) + Convey("test replace root key", func() { + SetData(data, "str", 1.1) + v, exist := data["str"] + So(exist, ShouldBeTrue) + So(v, ShouldEqual, 1.1) + }) + Convey("test add second key", func() { + Convey("add common", func() { + SetData(data, "map.addKey1", 123) + SetData(data, "map.addKey2", "str") + SetData(data, "map.addKey3", true) + v, exist := data["map"].(map[string]interface{})["addKey1"] + So(exist, ShouldBeTrue) + So(v, ShouldEqual, 123) + v, exist = data["map"].(map[string]interface{})["addKey2"] + So(exist, ShouldBeTrue) + So(v, ShouldEqual, "str") + v, exist = data["map"].(map[string]interface{})["addKey3"] + So(exist, ShouldBeTrue) + So(v, ShouldEqual, true) + }) + Convey("add map", func() { + SetData(data, "map.addKey", map[string]interface{}{"k": "v"}) + v, exist := data["map"].(map[string]interface{})["addKey"].(map[string]interface{})["k"] + So(exist, ShouldBeTrue) + So(v, ShouldEqual, "v") + }) + Convey("add array", func() { + SetData(data, "map.addKey", []interface{}{123, "string"}) + v, exist := data["map"].(map[string]interface{})["addKey"].([]interface{}) + So(exist, ShouldBeTrue) + So(v[0], ShouldEqual, 123) + So(v[1], ShouldEqual, "string") + }) + }) + Convey("test replace second key", func() { + SetData(data, "map.str", map[string]interface{}{"k": "v"}) + _, exist := data["map"].(map[string]interface{})["str"] + So(exist, ShouldBeTrue) + }) + Convey("test add third key", func() { + Convey("add map", func() { + SetData(data, "map.addKey1.addKey2", map[string]interface{}{"k": "v"}) + v, exist := data["map"].(map[string]interface{})["addKey1"].(map[string]interface{})["addKey2"] + So(exist, ShouldBeTrue) + So(v.(map[string]interface{})["k"], ShouldEqual, "v") + }) + }) + }) +} + +func TestDeleteData(t *testing.T) { + Convey("test delete event data", t, func() { + data := map[string]interface{}{ + "map": map[string]interface{}{ + "number": 123.4, + "str": "str", + "array": []interface{}{1.1, "str"}, + "map": map[string]interface{}{ + "number": 123.4, + "str": "str", + "array": []interface{}{1.1, "str"}, + }, + }, + "map2": map[string]interface{}{ + "number": 123.4, + "str": "str", + "array": []interface{}{1.1, "str"}, + "map": map[string]interface{}{ + "number": 123.4, + }, + }, + "number": 123.4, + "str": "stringV", + "array": []interface{}{1.1, "str"}, + } + Convey("delete root key", func() { + Convey("delete map", func() { + DeleteData(data, "map") + _, exist := data["map"] + So(exist, ShouldBeFalse) + }) + Convey("delete array", func() { + DeleteData(data, "array") + _, exist := data["array"] + So(exist, ShouldBeFalse) + }) + Convey("delete common", func() { + DeleteData(data, "str") + _, exist := data["str"] + So(exist, ShouldBeFalse) + DeleteData(data, "str1") + _, exist = data["str1"] + So(exist, ShouldBeFalse) + }) + }) + Convey("delete second key", func() { + Convey("delete map", func() { + DeleteData(data, "map.map") + _, exist := data["map"].(map[string]interface{})["map"] + So(exist, ShouldBeFalse) + }) + Convey("delete array", func() { + DeleteData(data, "map.array") + _, exist := data["map"].(map[string]interface{})["array"] + So(exist, ShouldBeFalse) + }) + Convey("delete common", func() { + DeleteData(data, "map.str") + _, exist := data["map"].(map[string]interface{})["str"] + So(exist, ShouldBeFalse) + }) + }) + Convey("delete third key", func() { + Convey("delete map number", func() { + DeleteData(data, "map2.map.number") + _, exist := data["map"].(map[string]interface{})["map"] + So(exist, ShouldBeTrue) + }) + }) + }) +} diff --git a/pkg/util/event.go b/pkg/util/event.go new file mode 100644 index 000000000..612b30423 --- /dev/null +++ b/pkg/util/event.go @@ -0,0 +1,31 @@ +// 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 util + +import "fmt" + +const maxEventAttrNameLength = 20 + +func ValidateEventAttrName(attr string) error { + if len(attr) > maxEventAttrNameLength { + return fmt.Errorf("CloudEvents attribute names length SHOULD NOT exceed 20 characters") + } + for _, c := range attr { + if !((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) { + return fmt.Errorf("CloudEvents attribute names MUST consist of lower-case letters ('a' to 'z') or digits ('0' to '9') from the ASCII character set") + } + } + return nil +} diff --git a/pkg/util/util.go b/pkg/util/util.go index 6dbda8be2..9547b73c7 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -26,3 +26,11 @@ func GetIDByAddr(addr string) string { func IsSpace(c byte) bool { return c <= ' ' && (c == ' ' || c == '\t' || c == '\r' || c == '\n') } + +func StringValue(value interface{}) string { + v, ok := value.(string) + if ok { + return v + } + return fmt.Sprintf("%v", value) +} diff --git a/proto/pkg/meta/meta.pb.go b/proto/pkg/meta/meta.pb.go index 704aa758a..426f4a75d 100644 --- a/proto/pkg/meta/meta.pb.go +++ b/proto/pkg/meta/meta.pb.go @@ -23,6 +23,7 @@ package meta import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + structpb "google.golang.org/protobuf/types/known/structpb" reflect "reflect" sync "sync" ) @@ -185,24 +186,24 @@ type SinkCredential_CredentialType int32 const ( SinkCredential_None SinkCredential_CredentialType = 0 - SinkCredential_AWS SinkCredential_CredentialType = 1 - SinkCredential_GCLOUD SinkCredential_CredentialType = 2 - SinkCredential_PLAIN SinkCredential_CredentialType = 3 + SinkCredential_PLAIN SinkCredential_CredentialType = 1 + SinkCredential_AWS SinkCredential_CredentialType = 2 + SinkCredential_GCLOUD SinkCredential_CredentialType = 3 ) // Enum value maps for SinkCredential_CredentialType. var ( SinkCredential_CredentialType_name = map[int32]string{ 0: "None", - 1: "AWS", - 2: "GCLOUD", - 3: "PLAIN", + 1: "PLAIN", + 2: "AWS", + 3: "GCLOUD", } SinkCredential_CredentialType_value = map[string]int32{ "None": 0, - "AWS": 1, - "GCLOUD": 2, - "PLAIN": 3, + "PLAIN": 1, + "AWS": 2, + "GCLOUD": 3, } ) @@ -948,9 +949,9 @@ type SinkCredential struct { CredentialType SinkCredential_CredentialType `protobuf:"varint,1,opt,name=credential_type,json=credentialType,proto3,enum=linkall.vanus.meta.SinkCredential_CredentialType" json:"credential_type,omitempty"` // Types that are assignable to Credential: + // *SinkCredential_Plain // *SinkCredential_Aws // *SinkCredential_Gcloud - // *SinkCredential_Plain Credential isSinkCredential_Credential `protobuf_oneof:"credential"` } @@ -1000,6 +1001,13 @@ func (m *SinkCredential) GetCredential() isSinkCredential_Credential { return nil } +func (x *SinkCredential) GetPlain() *PlainCredential { + if x, ok := x.GetCredential().(*SinkCredential_Plain); ok { + return x.Plain + } + return nil +} + func (x *SinkCredential) GetAws() *AKSKCredential { if x, ok := x.GetCredential().(*SinkCredential_Aws); ok { return x.Aws @@ -1014,35 +1022,28 @@ func (x *SinkCredential) GetGcloud() *GCloudCredential { return nil } -func (x *SinkCredential) GetPlain() *PlainCredential { - if x, ok := x.GetCredential().(*SinkCredential_Plain); ok { - return x.Plain - } - return nil -} - type isSinkCredential_Credential interface { isSinkCredential_Credential() } +type SinkCredential_Plain struct { + Plain *PlainCredential `protobuf:"bytes,2,opt,name=plain,proto3,oneof"` +} + type SinkCredential_Aws struct { - Aws *AKSKCredential `protobuf:"bytes,2,opt,name=aws,proto3,oneof"` + Aws *AKSKCredential `protobuf:"bytes,3,opt,name=aws,proto3,oneof"` } type SinkCredential_Gcloud struct { - Gcloud *GCloudCredential `protobuf:"bytes,3,opt,name=gcloud,proto3,oneof"` + Gcloud *GCloudCredential `protobuf:"bytes,4,opt,name=gcloud,proto3,oneof"` } -type SinkCredential_Plain struct { - Plain *PlainCredential `protobuf:"bytes,4,opt,name=plain,proto3,oneof"` -} +func (*SinkCredential_Plain) isSinkCredential_Credential() {} func (*SinkCredential_Aws) isSinkCredential_Credential() {} func (*SinkCredential_Gcloud) isSinkCredential_Credential() {} -func (*SinkCredential_Plain) isSinkCredential_Credential() {} - type PlainCredential struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1555,6 +1556,7 @@ type Transformer struct { Define map[string]string `protobuf:"bytes,1,rep,name=define,proto3" json:"define,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Template string `protobuf:"bytes,2,opt,name=template,proto3" json:"template,omitempty"` + Pipeline []*Action `protobuf:"bytes,3,rep,name=pipeline,proto3" json:"pipeline,omitempty"` } func (x *Transformer) Reset() { @@ -1603,281 +1605,344 @@ func (x *Transformer) GetTemplate() string { return "" } +func (x *Transformer) GetPipeline() []*Action { + if x != nil { + return x.Pipeline + } + return nil +} + +type Action struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Command []*structpb.Value `protobuf:"bytes,1,rep,name=command,proto3" json:"command,omitempty"` +} + +func (x *Action) Reset() { + *x = Action{} + if protoimpl.UnsafeEnabled { + mi := &file_meta_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Action) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Action) ProtoMessage() {} + +func (x *Action) ProtoReflect() protoreflect.Message { + mi := &file_meta_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Action.ProtoReflect.Descriptor instead. +func (*Action) Descriptor() ([]byte, []int) { + return file_meta_proto_rawDescGZIP(), []int{17} +} + +func (x *Action) GetCommand() []*structpb.Value { + if x != nil { + return x.Command + } + return nil +} + var File_meta_proto protoreflect.FileDescriptor var file_meta_proto_rawDesc = []byte{ 0x0a, 0x0a, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x22, 0x29, 0x0a, 0x11, 0x56, 0x61, 0x6e, 0x75, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x7f, 0x0a, 0x08, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, - 0x6f, 0x67, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x09, 0x6c, 0x6f, 0x67, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x04, 0x6c, 0x6f, - 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, - 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0xb1, 0x01, 0x0a, - 0x08, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x12, 0x24, 0x0a, 0x0e, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x5f, 0x62, 0x75, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, - 0x20, 0x0a, 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x49, - 0x64, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x67, - 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x05, 0x52, 0x15, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x67, 0x6d, 0x65, - 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x22, 0x4f, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, - 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, - 0x44, 0x22, 0xf1, 0x04, 0x0a, 0x07, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, - 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, - 0x13, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x70, 0x72, 0x65, 0x76, - 0x69, 0x6f, 0x75, 0x73, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x26, 0x0a, - 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x53, 0x65, 0x67, 0x6d, - 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6c, - 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x13, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x49, 0x6e, 0x4c, 0x6f, 0x67, 0x12, 0x29, 0x0a, 0x11, 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x0e, 0x65, 0x6e, 0x64, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x4c, 0x6f, - 0x67, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, - 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, - 0x79, 0x12, 0x2e, 0x0a, 0x13, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, - 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, - 0x64, 0x12, 0x45, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, - 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x72, - 0x65, 0x73, 0x73, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x52, 0x0a, 0x63, 0x6f, - 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x45, - 0x0a, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x29, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, - 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x72, 0x65, 0x70, - 0x6c, 0x69, 0x63, 0x61, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, - 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x1a, 0x56, 0x0a, - 0x0d, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, - 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf6, 0x02, 0x0a, 0x11, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, - 0x74, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, - 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, - 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x05, 0x52, 0x0b, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x12, 0x33, 0x0a, 0x15, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x14, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x66, 0x75, 0x6c, 0x6c, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x12, 0x16, - 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, - 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x31, 0x0a, 0x15, 0x66, 0x69, - 0x72, 0x73, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6f, 0x72, 0x6e, 0x5f, 0x74, - 0x69, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x6f, 0x72, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2f, 0x0a, - 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6f, 0x72, 0x6e, - 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6c, 0x61, 0x73, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x6f, 0x72, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xc9, - 0x04, 0x0a, 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, - 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x12, 0x3e, 0x0a, - 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, - 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, - 0x74, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, - 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, + 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x29, + 0x0a, 0x11, 0x56, 0x61, 0x6e, 0x75, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, + 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x7f, 0x0a, 0x08, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x42, 0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6c, 0x6f, 0x67, + 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x6c, + 0x6f, 0x67, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x30, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, + 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x22, 0xb1, 0x01, 0x0a, 0x08, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x12, 0x24, 0x0a, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x5f, 0x62, 0x75, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x12, + 0x36, 0x0a, 0x17, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x67, 0x6d, 0x65, + 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x15, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, + 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x4f, + 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x49, 0x44, 0x22, + 0xf1, 0x04, 0x0a, 0x07, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x13, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x6e, + 0x65, 0x78, 0x74, 0x5f, 0x73, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6e, 0x65, 0x78, 0x74, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, + 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x67, + 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x13, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x10, 0x73, 0x74, 0x61, 0x72, 0x74, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x49, + 0x6e, 0x4c, 0x6f, 0x67, 0x12, 0x29, 0x0a, 0x11, 0x65, 0x6e, 0x64, 0x5f, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x5f, 0x69, 0x6e, 0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0e, 0x65, 0x6e, 0x64, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x4c, 0x6f, 0x67, 0x12, + 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, + 0x69, 0x7a, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, + 0x08, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, + 0x2e, 0x0a, 0x13, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, + 0x73, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x18, 0x09, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x6e, 0x75, + 0x6d, 0x62, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x64, 0x12, + 0x45, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, + 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, + 0x73, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x70, + 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x45, 0x0a, 0x08, + 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, - 0x65, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x6e, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x73, 0x69, 0x6e, 0x6b, 0x12, 0x4b, 0x0a, 0x0f, 0x73, 0x69, 0x6e, 0x6b, 0x5f, - 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, - 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x53, 0x69, 0x6e, 0x6b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x52, 0x0e, 0x73, 0x69, 0x6e, 0x6b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, - 0x74, 0x69, 0x61, 0x6c, 0x12, 0x38, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, - 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x74, - 0x6f, 0x63, 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x50, - 0x0a, 0x11, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, - 0x6e, 0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x10, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, - 0x12, 0x1b, 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x75, 0x73, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x73, 0x12, 0x41, 0x0a, - 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, - 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, - 0x6d, 0x65, 0x72, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x72, - 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, - 0x12, 0x38, 0x0a, 0x07, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x73, 0x18, 0x65, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, - 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x66, - 0x6f, 0x52, 0x07, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x73, 0x22, 0xeb, 0x02, 0x0a, 0x0e, 0x53, - 0x69, 0x6e, 0x6b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x5a, 0x0a, - 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, - 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x53, 0x69, 0x6e, 0x6b, - 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x36, 0x0a, 0x03, 0x61, 0x77, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, - 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x41, 0x4b, 0x53, 0x4b, - 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x03, 0x61, 0x77, - 0x73, 0x12, 0x3e, 0x0a, 0x06, 0x67, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, - 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x47, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x43, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x06, 0x67, 0x63, 0x6c, 0x6f, 0x75, - 0x64, 0x12, 0x3b, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x23, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, - 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x22, 0x3a, - 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x57, - 0x53, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x47, 0x43, 0x4c, 0x4f, 0x55, 0x44, 0x10, 0x02, 0x12, - 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, 0x4e, 0x10, 0x03, 0x42, 0x0c, 0x0a, 0x0a, 0x63, 0x72, - 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x22, 0x49, 0x0a, 0x0f, 0x50, 0x6c, 0x61, 0x69, - 0x6e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x69, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x22, 0x60, 0x0a, 0x0e, 0x41, 0x4b, 0x53, 0x4b, 0x43, 0x72, 0x65, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, - 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x4b, 0x65, 0x79, 0x22, 0x3d, 0x0a, 0x10, 0x47, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x43, - 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x72, 0x65, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, - 0x4a, 0x73, 0x6f, 0x6e, 0x22, 0x99, 0x01, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, - 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, - 0x22, 0x8e, 0x03, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, - 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x61, 0x74, 0x65, 0x5f, - 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x72, 0x61, 0x74, - 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x52, 0x0a, 0x0b, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, - 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x6c, 0x69, + 0x65, 0x74, 0x61, 0x2e, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x72, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6c, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x1a, 0x56, 0x0a, 0x0d, 0x52, + 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, + 0x74, 0x61, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0xf6, 0x02, 0x0a, 0x11, 0x53, 0x65, 0x67, 0x6d, 0x65, 0x6e, 0x74, 0x48, + 0x65, 0x61, 0x6c, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x63, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x63, + 0x61, 0x70, 0x61, 0x63, 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x05, 0x52, 0x0b, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x33, + 0x0a, 0x15, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x14, 0x73, + 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x56, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x69, 0x73, 0x5f, 0x66, 0x75, 0x6c, 0x6c, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x12, 0x16, 0x0a, 0x06, + 0x6c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6c, 0x65, + 0x61, 0x64, 0x65, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x04, 0x74, 0x65, 0x72, 0x6d, 0x12, 0x31, 0x0a, 0x15, 0x66, 0x69, 0x72, 0x73, + 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6f, 0x72, 0x6e, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x03, 0x52, 0x12, 0x66, 0x69, 0x72, 0x73, 0x74, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x42, 0x6f, 0x72, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x2f, 0x0a, 0x14, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x6f, 0x72, 0x6e, 0x5f, 0x74, + 0x69, 0x6d, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x42, 0x6f, 0x72, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xc9, 0x04, 0x0a, + 0x0c, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x73, 0x12, 0x3e, 0x0a, 0x06, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, - 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2e, 0x0a, 0x10, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x04, 0x48, 0x00, 0x52, 0x0f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, - 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x54, 0x69, - 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, - 0x72, 0x79, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x05, 0x52, 0x10, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x65, 0x6d, - 0x70, 0x74, 0x73, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x61, 0x64, 0x5f, 0x6c, 0x65, 0x74, 0x74, - 0x65, 0x72, 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x12, 0x64, 0x65, 0x61, 0x64, 0x4c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x62, 0x75, 0x73, 0x22, 0x35, 0x0a, 0x0a, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x54, 0x45, 0x53, 0x54, 0x10, 0x00, 0x12, - 0x0c, 0x0a, 0x08, 0x45, 0x41, 0x52, 0x4c, 0x49, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x0d, 0x0a, - 0x09, 0x54, 0x49, 0x4d, 0x45, 0x53, 0x54, 0x41, 0x4d, 0x50, 0x10, 0x02, 0x42, 0x13, 0x0a, 0x11, - 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x22, 0xa3, 0x04, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x05, - 0x65, 0x78, 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6c, 0x69, - 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x61, 0x63, 0x74, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x05, 0x65, 0x78, 0x61, 0x63, 0x74, 0x12, 0x3e, 0x0a, 0x06, 0x70, 0x72, 0x65, - 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, - 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x46, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x75, 0x66, - 0x66, 0x69, 0x78, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x66, 0x69, 0x67, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x34, 0x0a, 0x07, 0x66, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, + 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, + 0x61, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x73, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x6e, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x73, 0x69, 0x6e, 0x6b, 0x12, 0x4b, 0x0a, 0x0f, 0x73, 0x69, 0x6e, 0x6b, 0x5f, 0x63, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, + 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, + 0x65, 0x74, 0x61, 0x2e, 0x53, 0x69, 0x6e, 0x6b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x52, 0x0e, 0x73, 0x69, 0x6e, 0x6b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x12, 0x38, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, + 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x6f, 0x6c, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x50, 0x0a, 0x11, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, + 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, + 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x10, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x1b, + 0x0a, 0x09, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x62, 0x75, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x75, 0x73, 0x12, 0x41, 0x0a, 0x0b, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, + 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x65, + 0x72, 0x52, 0x0b, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x72, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x64, 0x20, 0x01, 0x28, 0x04, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, + 0x0a, 0x07, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x73, 0x18, 0x65, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, + 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, + 0x07, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x73, 0x22, 0xeb, 0x02, 0x0a, 0x0e, 0x53, 0x69, 0x6e, + 0x6b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x5a, 0x0a, 0x0f, 0x63, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, + 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x53, 0x69, 0x6e, 0x6b, 0x43, 0x72, + 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x2e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0e, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x3b, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x69, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, + 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x50, 0x6c, 0x61, 0x69, + 0x6e, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x05, 0x70, + 0x6c, 0x61, 0x69, 0x6e, 0x12, 0x36, 0x0a, 0x03, 0x61, 0x77, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x22, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, + 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x41, 0x4b, 0x53, 0x4b, 0x43, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x48, 0x00, 0x52, 0x03, 0x61, 0x77, 0x73, 0x12, 0x3e, 0x0a, 0x06, + 0x67, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6c, + 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, + 0x61, 0x2e, 0x47, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x61, 0x6c, 0x48, 0x00, 0x52, 0x06, 0x67, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x22, 0x3a, 0x0a, 0x0e, + 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, + 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x50, 0x4c, 0x41, 0x49, + 0x4e, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x57, 0x53, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06, + 0x47, 0x43, 0x4c, 0x4f, 0x55, 0x44, 0x10, 0x03, 0x42, 0x0c, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, + 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x22, 0x49, 0x0a, 0x0f, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x43, + 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x22, 0x60, 0x0a, 0x0e, 0x41, 0x4b, 0x53, 0x4b, 0x43, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x61, 0x6c, 0x12, 0x22, 0x0a, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x65, + 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x4b, 0x65, 0x79, 0x22, 0x3d, 0x0a, 0x10, 0x47, 0x43, 0x6c, 0x6f, 0x75, 0x64, 0x43, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x5f, 0x6a, 0x73, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x73, 0x4a, 0x73, + 0x6f, 0x6e, 0x22, 0x99, 0x01, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x53, + 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x4a, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, + 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x68, 0x65, 0x61, 0x64, 0x65, + 0x72, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x8e, + 0x03, 0x0a, 0x12, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x6c, 0x69, + 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x72, 0x61, 0x74, 0x65, 0x4c, + 0x69, 0x6d, 0x69, 0x74, 0x12, 0x52, 0x0a, 0x0b, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x74, + 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x31, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, + 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x53, + 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x2e, 0x0a, 0x10, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x48, 0x00, 0x52, 0x0f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x10, 0x64, 0x65, 0x6c, 0x69, + 0x76, 0x65, 0x72, 0x79, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x05, 0x52, 0x0f, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x54, 0x69, 0x6d, 0x65, + 0x6f, 0x75, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x65, 0x74, 0x72, 0x79, + 0x5f, 0x61, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, + 0x10, 0x6d, 0x61, 0x78, 0x52, 0x65, 0x74, 0x72, 0x79, 0x41, 0x74, 0x74, 0x65, 0x6d, 0x70, 0x74, + 0x73, 0x12, 0x30, 0x0a, 0x14, 0x64, 0x65, 0x61, 0x64, 0x5f, 0x6c, 0x65, 0x74, 0x74, 0x65, 0x72, + 0x5f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x62, 0x75, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x12, 0x64, 0x65, 0x61, 0x64, 0x4c, 0x65, 0x74, 0x74, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x62, 0x75, 0x73, 0x22, 0x35, 0x0a, 0x0a, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x54, 0x45, 0x53, 0x54, 0x10, 0x00, 0x12, 0x0c, 0x0a, + 0x08, 0x45, 0x41, 0x52, 0x4c, 0x49, 0x45, 0x53, 0x54, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x54, + 0x49, 0x4d, 0x45, 0x53, 0x54, 0x41, 0x4d, 0x50, 0x10, 0x02, 0x42, 0x13, 0x0a, 0x11, 0x5f, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, + 0xa3, 0x04, 0x0a, 0x06, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x3b, 0x0a, 0x05, 0x65, 0x78, + 0x61, 0x63, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x46, - 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x12, 0x2c, 0x0a, 0x03, 0x6e, 0x6f, 0x74, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, - 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, 0x74, - 0x65, 0x72, 0x52, 0x03, 0x6e, 0x6f, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x05, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, + 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x45, 0x78, 0x61, 0x63, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x05, 0x65, 0x78, 0x61, 0x63, 0x74, 0x12, 0x3e, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, + 0x78, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, + 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, + 0x78, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, + 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x2e, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x06, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x12, 0x2c, 0x0a, 0x03, 0x6e, 0x6f, 0x74, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, - 0x52, 0x03, 0x61, 0x6c, 0x6c, 0x12, 0x2c, 0x0a, 0x03, 0x61, 0x6e, 0x79, 0x18, 0x06, 0x20, 0x03, + 0x52, 0x03, 0x6e, 0x6f, 0x74, 0x12, 0x2c, 0x0a, 0x03, 0x61, 0x6c, 0x6c, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x03, - 0x61, 0x6e, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x73, 0x71, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x63, 0x65, 0x6c, 0x1a, 0x38, 0x0a, 0x0a, 0x45, 0x78, 0x61, 0x63, 0x74, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, - 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, - 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x75, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, 0x63, - 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x27, 0x0a, 0x0f, 0x73, - 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x07, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, - 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x73, 0x22, 0x46, - 0x0a, 0x0a, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, - 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, - 0x66, 0x73, 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, - 0x67, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x22, 0xa9, 0x01, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x06, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, - 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x66, 0x6f, 0x72, 0x6d, 0x65, 0x72, 0x2e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x06, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, - 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x1a, 0x39, 0x0a, 0x0b, 0x44, 0x65, 0x66, 0x69, 0x6e, - 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x2a, 0x33, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, 0x69, 0x65, - 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x45, 0x4d, 0x4f, 0x52, 0x59, 0x10, 0x00, 0x12, 0x07, 0x0a, - 0x03, 0x53, 0x53, 0x44, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x48, 0x44, 0x44, 0x10, 0x02, 0x12, - 0x06, 0x0a, 0x02, 0x53, 0x33, 0x10, 0x03, 0x2a, 0x26, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x72, - 0x65, 0x73, 0x73, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x08, 0x0a, 0x04, - 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x5a, 0x34, 0x10, 0x01, 0x2a, - 0x3a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x08, 0x0a, 0x04, 0x48, - 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x57, 0x53, 0x5f, 0x4c, 0x41, 0x4d, - 0x42, 0x44, 0x41, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x47, 0x43, 0x4c, 0x4f, 0x55, 0x44, 0x5f, - 0x46, 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x10, 0x02, 0x42, 0x2e, 0x5a, 0x2c, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, - 0x6c, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x61, 0x6c, 0x6c, 0x12, 0x2c, 0x0a, 0x03, 0x61, 0x6e, 0x79, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, + 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x52, 0x03, 0x61, 0x6e, + 0x79, 0x12, 0x10, 0x0a, 0x03, 0x73, 0x71, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, + 0x73, 0x71, 0x6c, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x65, 0x6c, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x63, 0x65, 0x6c, 0x1a, 0x38, 0x0a, 0x0a, 0x45, 0x78, 0x61, 0x63, 0x74, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x39, 0x0a, 0x0b, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x53, 0x75, + 0x66, 0x66, 0x69, 0x78, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x75, 0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x27, 0x0a, 0x0f, 0x73, 0x75, 0x62, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0e, 0x73, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x64, 0x12, 0x38, 0x0a, 0x07, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, 0x61, + 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x49, + 0x6e, 0x66, 0x6f, 0x52, 0x07, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x73, 0x22, 0x46, 0x0a, 0x0a, + 0x4f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, + 0x66, 0x73, 0x65, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, + 0x65, 0x74, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4c, + 0x6f, 0x67, 0x49, 0x64, 0x22, 0xe1, 0x01, 0x0a, 0x0b, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x6f, + 0x72, 0x6d, 0x65, 0x72, 0x12, 0x43, 0x0a, 0x06, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, 0x6c, 0x2e, 0x76, + 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, + 0x6f, 0x72, 0x6d, 0x65, 0x72, 0x2e, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x06, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x65, 0x6d, + 0x70, 0x6c, 0x61, 0x74, 0x65, 0x12, 0x36, 0x0a, 0x08, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, + 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x6c, + 0x6c, 0x2e, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x70, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x1a, 0x39, 0x0a, + 0x0b, 0x44, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3a, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x07, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x2a, 0x33, 0x0a, 0x0b, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x54, + 0x69, 0x65, 0x72, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x45, 0x4d, 0x4f, 0x52, 0x59, 0x10, 0x00, 0x12, + 0x07, 0x0a, 0x03, 0x53, 0x53, 0x44, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x48, 0x44, 0x44, 0x10, + 0x02, 0x12, 0x06, 0x0a, 0x02, 0x53, 0x33, 0x10, 0x03, 0x2a, 0x26, 0x0a, 0x11, 0x43, 0x6f, 0x6d, + 0x70, 0x72, 0x65, 0x73, 0x73, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x08, + 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x5a, 0x34, 0x10, + 0x01, 0x2a, 0x3a, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x08, 0x0a, + 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x41, 0x57, 0x53, 0x5f, 0x4c, + 0x41, 0x4d, 0x42, 0x44, 0x41, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x47, 0x43, 0x4c, 0x4f, 0x55, + 0x44, 0x5f, 0x46, 0x55, 0x4e, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x10, 0x02, 0x42, 0x2e, 0x5a, + 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x6e, 0x6b, + 0x61, 0x6c, 0x6c, 0x2d, 0x6c, 0x61, 0x62, 0x73, 0x2f, 0x76, 0x61, 0x6e, 0x75, 0x73, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1893,7 +1958,7 @@ func file_meta_proto_rawDescGZIP() []byte { } var file_meta_proto_enumTypes = make([]protoimpl.EnumInfo, 5) -var file_meta_proto_msgTypes = make([]protoimpl.MessageInfo, 23) +var file_meta_proto_msgTypes = make([]protoimpl.MessageInfo, 24) var file_meta_proto_goTypes = []interface{}{ (StorageTier)(0), // 0: linkall.vanus.meta.StorageTier (CompressAlgorithm)(0), // 1: linkall.vanus.meta.CompressAlgorithm @@ -1917,17 +1982,19 @@ var file_meta_proto_goTypes = []interface{}{ (*SubscriptionInfo)(nil), // 19: linkall.vanus.meta.SubscriptionInfo (*OffsetInfo)(nil), // 20: linkall.vanus.meta.OffsetInfo (*Transformer)(nil), // 21: linkall.vanus.meta.Transformer - nil, // 22: linkall.vanus.meta.Segment.ReplicasEntry - nil, // 23: linkall.vanus.meta.ProtocolSetting.HeadersEntry - nil, // 24: linkall.vanus.meta.Filter.ExactEntry - nil, // 25: linkall.vanus.meta.Filter.PrefixEntry - nil, // 26: linkall.vanus.meta.Filter.SuffixEntry - nil, // 27: linkall.vanus.meta.Transformer.DefineEntry + (*Action)(nil), // 22: linkall.vanus.meta.Action + nil, // 23: linkall.vanus.meta.Segment.ReplicasEntry + nil, // 24: linkall.vanus.meta.ProtocolSetting.HeadersEntry + nil, // 25: linkall.vanus.meta.Filter.ExactEntry + nil, // 26: linkall.vanus.meta.Filter.PrefixEntry + nil, // 27: linkall.vanus.meta.Filter.SuffixEntry + nil, // 28: linkall.vanus.meta.Transformer.DefineEntry + (*structpb.Value)(nil), // 29: google.protobuf.Value } var file_meta_proto_depIdxs = []int32{ 7, // 0: linkall.vanus.meta.EventBus.logs:type_name -> linkall.vanus.meta.EventLog 1, // 1: linkall.vanus.meta.Segment.compressed:type_name -> linkall.vanus.meta.CompressAlgorithm - 22, // 2: linkall.vanus.meta.Segment.replicas:type_name -> linkall.vanus.meta.Segment.ReplicasEntry + 23, // 2: linkall.vanus.meta.Segment.replicas:type_name -> linkall.vanus.meta.Segment.ReplicasEntry 17, // 3: linkall.vanus.meta.Subscription.config:type_name -> linkall.vanus.meta.SubscriptionConfig 18, // 4: linkall.vanus.meta.Subscription.filters:type_name -> linkall.vanus.meta.Filter 12, // 5: linkall.vanus.meta.Subscription.sink_credential:type_name -> linkall.vanus.meta.SinkCredential @@ -1936,25 +2003,27 @@ var file_meta_proto_depIdxs = []int32{ 21, // 8: linkall.vanus.meta.Subscription.transformer:type_name -> linkall.vanus.meta.Transformer 20, // 9: linkall.vanus.meta.Subscription.offsets:type_name -> linkall.vanus.meta.OffsetInfo 3, // 10: linkall.vanus.meta.SinkCredential.credential_type:type_name -> linkall.vanus.meta.SinkCredential.CredentialType - 14, // 11: linkall.vanus.meta.SinkCredential.aws:type_name -> linkall.vanus.meta.AKSKCredential - 15, // 12: linkall.vanus.meta.SinkCredential.gcloud:type_name -> linkall.vanus.meta.GCloudCredential - 13, // 13: linkall.vanus.meta.SinkCredential.plain:type_name -> linkall.vanus.meta.PlainCredential - 23, // 14: linkall.vanus.meta.ProtocolSetting.headers:type_name -> linkall.vanus.meta.ProtocolSetting.HeadersEntry + 13, // 11: linkall.vanus.meta.SinkCredential.plain:type_name -> linkall.vanus.meta.PlainCredential + 14, // 12: linkall.vanus.meta.SinkCredential.aws:type_name -> linkall.vanus.meta.AKSKCredential + 15, // 13: linkall.vanus.meta.SinkCredential.gcloud:type_name -> linkall.vanus.meta.GCloudCredential + 24, // 14: linkall.vanus.meta.ProtocolSetting.headers:type_name -> linkall.vanus.meta.ProtocolSetting.HeadersEntry 4, // 15: linkall.vanus.meta.SubscriptionConfig.offset_type:type_name -> linkall.vanus.meta.SubscriptionConfig.OffsetType - 24, // 16: linkall.vanus.meta.Filter.exact:type_name -> linkall.vanus.meta.Filter.ExactEntry - 25, // 17: linkall.vanus.meta.Filter.prefix:type_name -> linkall.vanus.meta.Filter.PrefixEntry - 26, // 18: linkall.vanus.meta.Filter.suffix:type_name -> linkall.vanus.meta.Filter.SuffixEntry + 25, // 16: linkall.vanus.meta.Filter.exact:type_name -> linkall.vanus.meta.Filter.ExactEntry + 26, // 17: linkall.vanus.meta.Filter.prefix:type_name -> linkall.vanus.meta.Filter.PrefixEntry + 27, // 18: linkall.vanus.meta.Filter.suffix:type_name -> linkall.vanus.meta.Filter.SuffixEntry 18, // 19: linkall.vanus.meta.Filter.not:type_name -> linkall.vanus.meta.Filter 18, // 20: linkall.vanus.meta.Filter.all:type_name -> linkall.vanus.meta.Filter 18, // 21: linkall.vanus.meta.Filter.any:type_name -> linkall.vanus.meta.Filter 20, // 22: linkall.vanus.meta.SubscriptionInfo.offsets:type_name -> linkall.vanus.meta.OffsetInfo - 27, // 23: linkall.vanus.meta.Transformer.define:type_name -> linkall.vanus.meta.Transformer.DefineEntry - 8, // 24: linkall.vanus.meta.Segment.ReplicasEntry.value:type_name -> linkall.vanus.meta.Block - 25, // [25:25] is the sub-list for method output_type - 25, // [25:25] is the sub-list for method input_type - 25, // [25:25] is the sub-list for extension type_name - 25, // [25:25] is the sub-list for extension extendee - 0, // [0:25] is the sub-list for field type_name + 28, // 23: linkall.vanus.meta.Transformer.define:type_name -> linkall.vanus.meta.Transformer.DefineEntry + 22, // 24: linkall.vanus.meta.Transformer.pipeline:type_name -> linkall.vanus.meta.Action + 29, // 25: linkall.vanus.meta.Action.command:type_name -> google.protobuf.Value + 8, // 26: linkall.vanus.meta.Segment.ReplicasEntry.value:type_name -> linkall.vanus.meta.Block + 27, // [27:27] is the sub-list for method output_type + 27, // [27:27] is the sub-list for method input_type + 27, // [27:27] is the sub-list for extension type_name + 27, // [27:27] is the sub-list for extension extendee + 0, // [0:27] is the sub-list for field type_name } func init() { file_meta_proto_init() } @@ -2167,11 +2236,23 @@ func file_meta_proto_init() { return nil } } + file_meta_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Action); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_meta_proto_msgTypes[7].OneofWrappers = []interface{}{ + (*SinkCredential_Plain)(nil), (*SinkCredential_Aws)(nil), (*SinkCredential_Gcloud)(nil), - (*SinkCredential_Plain)(nil), } file_meta_proto_msgTypes[12].OneofWrappers = []interface{}{} type x struct{} @@ -2180,7 +2261,7 @@ func file_meta_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_meta_proto_rawDesc, NumEnums: 5, - NumMessages: 23, + NumMessages: 24, NumExtensions: 0, NumServices: 0, }, diff --git a/proto/proto/meta.proto b/proto/proto/meta.proto index e37694e28..e5ce28e5a 100644 --- a/proto/proto/meta.proto +++ b/proto/proto/meta.proto @@ -16,6 +16,8 @@ syntax = "proto3"; package linkall.vanus.meta; +import "google/protobuf/struct.proto"; + option go_package = "github.com/linkall-labs/vanus/proto/pkg/meta"; message VanusResourceName { @@ -181,4 +183,10 @@ message OffsetInfo { message Transformer { map define = 1; string template = 2; + repeated Action pipeline = 3; +} + +message Action { + repeated google.protobuf.Value command = 1; } + diff --git a/vsctl/command/subscription.go b/vsctl/command/subscription.go index 768f5d373..e9be7ba36 100644 --- a/vsctl/command/subscription.go +++ b/vsctl/command/subscription.go @@ -22,6 +22,9 @@ import ( "os" "time" + "github.com/linkall-labs/vanus/internal/convert" + "github.com/linkall-labs/vanus/internal/primitive" + "github.com/aws/aws-sdk-go-v2/aws/arn" "github.com/fatih/color" "github.com/golang/protobuf/ptypes/empty" @@ -137,10 +140,12 @@ func createSubscriptionCommand() *cobra.Command { var trans *meta.Transformer if transformer != "" { - err := json.Unmarshal([]byte(transformer), &trans) + var _transformer *primitive.Transformer + err := json.Unmarshal([]byte(transformer), &_transformer) if err != nil { cmdFailedf(cmd, "the transformer invalid: %s", err) } + trans = convert.ToPbTransformer(_transformer) } // subscription config