diff --git a/go.mod b/go.mod index db66888..60f0225 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,12 @@ module go.unistack.org/micro-wrapper-validator/v3 go 1.17 -require go.unistack.org/micro/v3 v3.10.14 +require go.unistack.org/micro/v3 v3.10.66 + +require ( + github.com/golang/protobuf v1.5.4 // indirect + golang.org/x/sys v0.19.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.33.0 // indirect +) diff --git a/go.sum b/go.sum index 9b02878..71a046a 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,21 @@ +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/silas/dag v0.0.0-20211117232152-9d50aa809f35/go.mod h1:7RTUFBdIRC9nZ7/3RyRNH1bdqIShrDejd1YbLwgPS+I= go.unistack.org/micro/v3 v3.10.14 h1:7fgLpwGlCN67twhwtngJDEQvrMkUBDSA5vzZqxIDqNE= go.unistack.org/micro/v3 v3.10.14/go.mod h1:uMAc0U/x7dmtICCrblGf0ZLgYegu3VwQAquu+OFCw1Q= +go.unistack.org/micro/v3 v3.10.66 h1:tiG8HnyTC71IZWSC2qT/DmLhJinZJL9qvw+4Fvpm3d4= +go.unistack.org/micro/v3 v3.10.66/go.mod h1:erMgt3Bl7vQQ0e9UpQyR5NlLiZ9pKeEJ9+1tfYFaqUg= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/validator.go b/validator.go index a02aeb2..75381cd 100644 --- a/validator.go +++ b/validator.go @@ -41,15 +41,29 @@ type ( // Options struct holds wrapper options type Options struct { - ClientErrorFn ClientErrorFunc - ServerErrorFn ServerErrorFunc - PublishErrorFn PublishErrorFunc - SubscribeErrorFn SubscribeErrorFunc + ClientErrorFn ClientErrorFunc + ServerErrorFn ServerErrorFunc + PublishErrorFn PublishErrorFunc + SubscribeErrorFn SubscribeErrorFunc + ClientValidateResponse bool + ServerValidateResponse bool } // Option func signature type Option func(*Options) +func ClientValidateResponse(b bool) Option { + return func(o *Options) { + o.ClientValidateResponse = b + } +} + +func ServerValidateResponse(b bool) Option { + return func(o *Options) { + o.ClientValidateResponse = b + } +} + func ClientReqErrorFn(fn ClientErrorFunc) Option { return func(o *Options) { o.ClientErrorFn = fn @@ -87,108 +101,94 @@ func NewOptions(opts ...Option) Options { return options } +func NewHook(opts ...Option) *hook { + return &hook{opts: NewOptions(opts...)} +} + type validator interface { Validate() error } -type wrapper struct { - client.Client +type hook struct { opts Options } -func NewClientWrapper(opts ...Option) client.Wrapper { - return func(c client.Client) client.Client { - handler := &wrapper{ - Client: c, - opts: NewOptions(opts...), - } - return handler - } -} - -func NewClientCallWrapper(opts ...Option) client.CallWrapper { - options := NewOptions(opts...) - return func(fn client.CallFunc) client.CallFunc { - return func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error { - if v, ok := req.Body().(validator); ok { - if verr := v.Validate(); verr != nil { - return options.ClientErrorFn(req, nil, verr) - } +func (w *hook) ClientCall(next client.FuncCall) client.FuncCall { + return func(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { + if v, ok := req.Body().(validator); ok { + if err := v.Validate(); err != nil { + return w.opts.ClientErrorFn(req, nil, err) } - err := fn(ctx, addr, req, rsp, opts) - if v, ok := rsp.(validator); ok { - if verr := v.Validate(); verr != nil { - return options.ClientErrorFn(req, rsp, verr) - } + } + err := next(ctx, req, rsp, opts...) + if v, ok := rsp.(validator); ok && w.opts.ClientValidateResponse { + if verr := v.Validate(); verr != nil { + return w.opts.ClientErrorFn(req, rsp, verr) } - return err } + return err } } -func (w *wrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { - if v, ok := req.Body().(validator); ok { - if err := v.Validate(); err != nil { - return w.opts.ClientErrorFn(req, nil, err) +func (w *hook) ClientStream(next client.FuncStream) client.FuncStream { + return func(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) { + if v, ok := req.Body().(validator); ok { + if err := v.Validate(); err != nil { + return nil, w.opts.ClientErrorFn(req, nil, err) + } } + return next(ctx, req, opts...) } - err := w.Client.Call(ctx, req, rsp, opts...) - if v, ok := rsp.(validator); ok { - if verr := v.Validate(); verr != nil { - return w.opts.ClientErrorFn(req, rsp, verr) - } - } - return err } -func (w *wrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) { - if v, ok := req.Body().(validator); ok { - if err := v.Validate(); err != nil { - return nil, w.opts.ClientErrorFn(req, nil, err) +func (w *hook) ClientPublish(next client.FuncPublish) client.FuncPublish { + return func(ctx context.Context, msg client.Message, opts ...client.PublishOption) error { + if v, ok := msg.Payload().(validator); ok { + if err := v.Validate(); err != nil { + return w.opts.PublishErrorFn(msg, err) + } } + return next(ctx, msg, opts...) } - return w.Client.Stream(ctx, req, opts...) } -func (w *wrapper) Publish(ctx context.Context, msg client.Message, opts ...client.PublishOption) error { - if v, ok := msg.Payload().(validator); ok { - if err := v.Validate(); err != nil { - return w.opts.PublishErrorFn(msg, err) - } - } - return w.Client.Publish(ctx, msg, opts...) -} - -func NewServerHandlerWrapper(opts ...Option) server.HandlerWrapper { - options := NewOptions(opts...) - return func(fn server.HandlerFunc) server.HandlerFunc { - return func(ctx context.Context, req server.Request, rsp interface{}) error { - if v, ok := req.Body().(validator); ok { +func (w *hook) ClientBatchPublish(next client.FuncBatchPublish) client.FuncBatchPublish { + return func(ctx context.Context, msgs []client.Message, opts ...client.PublishOption) error { + for _, msg := range msgs { + if v, ok := msg.Payload().(validator); ok { if err := v.Validate(); err != nil { - return options.ClientErrorFn(req, nil, err) + return w.opts.PublishErrorFn(msg, err) } } - err := fn(ctx, req, rsp) - if v, ok := rsp.(validator); ok { - if verr := v.Validate(); verr != nil { - return options.ClientErrorFn(req, rsp, err) - } - } - return err } + return next(ctx, msgs, opts...) } } -func NewServerSubscriberWrapper(opts ...Option) server.SubscriberWrapper { - options := NewOptions(opts...) - return func(fn server.SubscriberFunc) server.SubscriberFunc { - return func(ctx context.Context, msg server.Message) error { - if v, ok := msg.Body().(validator); ok { - if err := v.Validate(); err != nil { - return options.SubscribeErrorFn(msg, err) - } +func (w *hook) ServerHandler(next server.FuncHandler) server.FuncHandler { + return func(ctx context.Context, req server.Request, rsp interface{}) error { + if v, ok := req.Body().(validator); ok { + if err := v.Validate(); err != nil { + return w.opts.ServerErrorFn(req, nil, err) } - return fn(ctx, msg) } + err := next(ctx, req, rsp) + if v, ok := rsp.(validator); ok && w.opts.ServerValidateResponse { + if verr := v.Validate(); verr != nil { + return w.opts.ServerErrorFn(req, rsp, verr) + } + } + return err + } +} + +func (w *hook) ServerSubscriber(next server.FuncSubHandler) server.FuncSubHandler { + return func(ctx context.Context, msg server.Message) error { + if v, ok := msg.Body().(validator); ok { + if err := v.Validate(); err != nil { + return w.opts.SubscribeErrorFn(msg, err) + } + } + return next(ctx, msg) } } diff --git a/validator_test.go b/validator_test.go index ac182e3..f211c01 100644 --- a/validator_test.go +++ b/validator_test.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + memory "go.unistack.org/micro/v3/broker/memory" "go.unistack.org/micro/v3/client" "go.unistack.org/micro/v3/codec" "go.unistack.org/micro/v3/server" @@ -20,10 +21,6 @@ type Message struct { func (m *Message) Validate() error { return fmt.Errorf("SSS") - if len(m.Name) == 0 || m.Name != "test" { - return fmt.Errorf("name is empty") - } - return nil } func (h *Handler) Sub(ctx context.Context, req *Message) error { @@ -34,12 +31,26 @@ func TestValidator(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() + b := memory.NewBroker() + + if err := b.Init(); err != nil { + t.Fatal(err) + } + + if err := b.Connect(ctx); err != nil { + t.Fatal(err) + } + + w := NewHook() // create server srv := server.NewServer( server.Name("helloworld"), server.Codec("application/json", codec.NewCodec()), - server.WrapHandler(NewServerHandlerWrapper()), - server.WrapSubscriber(NewServerSubscriberWrapper()), + server.Broker(b), + server.Hooks( + server.HookHandler(w.ServerHandler), + server.HookSubHandler(w.ServerSubscriber), + ), server.Context(ctx), ) @@ -61,17 +72,23 @@ func TestValidator(t *testing.T) { cli := client.NewClient( client.ContentType("application/json"), client.Codec("application/json", codec.NewCodec()), - client.Wrap(NewClientWrapper()), + client.Broker(b), + client.Hooks( + client.HookCall(w.ClientCall), + client.HookStream(w.ClientStream), + client.HookPublish(w.ClientPublish), + client.HookBatchPublish(w.ClientBatchPublish), + ), ) + if err := cli.Init(); err != nil { + t.Fatal(err) + } + if err := cli.Publish(ctx, cli.NewMessage("test", &Message{Name: "test1"}, client.WithMessageContentType("application/json"))); err == nil { t.Fatalf("validator not works as message with bad contents") } - if err := cli.Publish(ctx, cli.NewMessage("test", &Message{Name: "test"}, client.WithMessageContentType("application/json"))); err == nil { - t.Fatal(err) - } - // stop server if err := srv.Stop(); err != nil { t.Fatal(err)