diff --git a/v2/container/client.go b/v2/container/client.go new file mode 100644 index 0000000..9c8e1a3 --- /dev/null +++ b/v2/container/client.go @@ -0,0 +1,321 @@ +package container + +import ( + "context" + + "github.com/nspcc-dev/neofs-api-go/v2/client" + container "github.com/nspcc-dev/neofs-api-go/v2/container/grpc" + "github.com/pkg/errors" + "google.golang.org/grpc" +) + +// Client represents universal container +// transport client. +type Client struct { + cPut *putClient + + cGet *getClient + + cDel *delClient + + cList *listClient + + cSetEACL *setEACLClient + + cGetEACL *getEACLClient +} + +// Option represents Client option. +type Option func(*cfg) + +type cfg struct { + proto client.Protocol + + globalOpts []client.Option + + gRPC cfgGRPC +} + +type cfgGRPC struct { + serviceClient container.ContainerServiceClient + + grpcCallOpts []grpc.CallOption + + callOpts []container.Option + + client *container.Client +} + +type putClient struct { + requestConverter func(*PutRequest) interface{} + + caller func(context.Context, interface{}) (interface{}, error) + + responseConverter func(interface{}) *PutResponse +} + +type getClient struct { + requestConverter func(*GetRequest) interface{} + + caller func(context.Context, interface{}) (interface{}, error) + + responseConverter func(interface{}) *GetResponse +} + +type delClient struct { + requestConverter func(*DeleteRequest) interface{} + + caller func(context.Context, interface{}) (interface{}, error) + + responseConverter func(interface{}) *DeleteResponse +} + +type listClient struct { + requestConverter func(*ListRequest) interface{} + + caller func(context.Context, interface{}) (interface{}, error) + + responseConverter func(interface{}) *ListResponse +} + +type setEACLClient struct { + requestConverter func(*SetExtendedACLRequest) interface{} + + caller func(context.Context, interface{}) (interface{}, error) + + responseConverter func(interface{}) *SetExtendedACLResponse +} + +type getEACLClient struct { + requestConverter func(*GetExtendedACLRequest) interface{} + + caller func(context.Context, interface{}) (interface{}, error) + + responseConverter func(interface{}) *GetExtendedACLResponse +} + +// Put sends PutRequest over the network and returns PutResponse. +// +// It returns any error encountered during the call. +func (c *Client) Put(ctx context.Context, req *PutRequest) (*PutResponse, error) { + resp, err := c.cPut.caller(ctx, c.cPut.requestConverter(req)) + if err != nil { + return nil, errors.Wrap(err, "could not send container put request") + } + + return c.cPut.responseConverter(resp), nil +} + +// Get sends GetRequest over the network and returns GetResponse. +// +// It returns any error encountered during the call. +func (c *Client) Get(ctx context.Context, req *GetRequest) (*GetResponse, error) { + resp, err := c.cGet.caller(ctx, c.cGet.requestConverter(req)) + if err != nil { + return nil, errors.Wrap(err, "could not send container get request") + } + + return c.cGet.responseConverter(resp), nil +} + +// Delete sends GetRequest over the network and returns GetResponse. +// +// It returns any error encountered during the call. +func (c *Client) Delete(ctx context.Context, req *DeleteRequest) (*DeleteResponse, error) { + resp, err := c.cDel.caller(ctx, c.cDel.requestConverter(req)) + if err != nil { + return nil, errors.Wrap(err, "could not send container delete request") + } + + return c.cDel.responseConverter(resp), nil +} + +// List sends ListRequest over the network and returns ListResponse. +// +// It returns any error encountered during the call. +func (c *Client) List(ctx context.Context, req *ListRequest) (*ListResponse, error) { + resp, err := c.cList.caller(ctx, c.cList.requestConverter(req)) + if err != nil { + return nil, errors.Wrap(err, "could not send container list request") + } + + return c.cList.responseConverter(resp), nil +} + +// SetExtendedACL sends SetExtendedACLRequest over the network and returns SetExtendedACLResponse. +// +// It returns any error encountered during the call. +func (c *Client) SetExtendedACL(ctx context.Context, req *SetExtendedACLRequest) (*SetExtendedACLResponse, error) { + resp, err := c.cSetEACL.caller(ctx, c.cSetEACL.requestConverter(req)) + if err != nil { + return nil, errors.Wrap(err, "could not send container set EACL request") + } + + return c.cSetEACL.responseConverter(resp), nil +} + +// GetExtendedACL sends GetExtendedACLRequest over the network and returns GetExtendedACLResponse. +// +// It returns any error encountered during the call. +func (c *Client) GetExtendedACL(ctx context.Context, req *GetExtendedACLRequest) (*GetExtendedACLResponse, error) { + resp, err := c.cGetEACL.caller(ctx, c.cGetEACL.requestConverter(req)) + if err != nil { + return nil, errors.Wrap(err, "could not send container get EACL request") + } + + return c.cGetEACL.responseConverter(resp), nil +} + +func defaultCfg() *cfg { + return &cfg{ + proto: client.ProtoGRPC, + } +} + +func New(opts ...Option) (*Client, error) { + cfg := defaultCfg() + + for i := range opts { + opts[i](cfg) + } + + var err error + + switch cfg.proto { + case client.ProtoGRPC: + var c *container.Client + if c, err = newGRPCClient(cfg); err != nil { + break + } + + return &Client{ + cPut: &putClient{ + requestConverter: func(req *PutRequest) interface{} { + return PutRequestToGRPCMessage(req) + }, + caller: func(ctx context.Context, req interface{}) (interface{}, error) { + return c.Put(ctx, req.(*container.PutRequest)) + }, + responseConverter: func(resp interface{}) *PutResponse { + return PutResponseFromGRPCMessage(resp.(*container.PutResponse)) + }, + }, + cGet: &getClient{ + requestConverter: func(req *GetRequest) interface{} { + return GetRequestToGRPCMessage(req) + }, + caller: func(ctx context.Context, req interface{}) (interface{}, error) { + return c.Get(ctx, req.(*container.GetRequest)) + }, + responseConverter: func(resp interface{}) *GetResponse { + return GetResponseFromGRPCMessage(resp.(*container.GetResponse)) + }, + }, + cDel: &delClient{ + requestConverter: func(req *DeleteRequest) interface{} { + return DeleteRequestToGRPCMessage(req) + }, + caller: func(ctx context.Context, req interface{}) (interface{}, error) { + return c.Delete(ctx, req.(*container.DeleteRequest)) + }, + responseConverter: func(resp interface{}) *DeleteResponse { + return DeleteResponseFromGRPCMessage(resp.(*container.DeleteResponse)) + }, + }, + cList: &listClient{ + requestConverter: func(req *ListRequest) interface{} { + return ListRequestToGRPCMessage(req) + }, + caller: func(ctx context.Context, req interface{}) (interface{}, error) { + return c.List(ctx, req.(*container.ListRequest)) + }, + responseConverter: func(resp interface{}) *ListResponse { + return ListResponseFromGRPCMessage(resp.(*container.ListResponse)) + }, + }, + cSetEACL: &setEACLClient{ + requestConverter: func(req *SetExtendedACLRequest) interface{} { + return SetExtendedACLRequestToGRPCMessage(req) + }, + caller: func(ctx context.Context, req interface{}) (interface{}, error) { + return c.SetExtendedACL(ctx, req.(*container.SetExtendedACLRequest)) + }, + responseConverter: func(resp interface{}) *SetExtendedACLResponse { + return SetExtendedACLResponseFromGRPCMessage(resp.(*container.SetExtendedACLResponse)) + }, + }, + cGetEACL: &getEACLClient{ + requestConverter: func(req *GetExtendedACLRequest) interface{} { + return GetExtendedACLRequestToGRPCMessage(req) + }, + caller: func(ctx context.Context, req interface{}) (interface{}, error) { + return c.GetExtendedACL(ctx, req.(*container.GetExtendedACLRequest)) + }, + responseConverter: func(resp interface{}) *GetExtendedACLResponse { + return GetExtendedACLResponseFromGRPCMessage(resp.(*container.GetExtendedACLResponse)) + }, + }, + }, nil + default: + err = client.ErrProtoUnsupported + } + + return nil, errors.Wrapf(err, "could not create %s Session client", cfg.proto) +} + +func newGRPCClient(cfg *cfg) (*container.Client, error) { + var err error + + if cfg.gRPC.client == nil { + if cfg.gRPC.serviceClient == nil { + conn, err := client.NewGRPCClientConn(cfg.globalOpts...) + if err != nil { + return nil, errors.Wrap(err, "could not open gRPC client connection") + } + + cfg.gRPC.serviceClient = container.NewContainerServiceClient(conn) + } + + cfg.gRPC.client, err = container.NewClient( + cfg.gRPC.serviceClient, + append( + cfg.gRPC.callOpts, + container.WithCallOptions(cfg.gRPC.grpcCallOpts), + )..., + ) + } + + return cfg.gRPC.client, err +} + +func WithGlobalOpts(v ...client.Option) Option { + return func(c *cfg) { + if len(v) > 0 { + c.globalOpts = v + } + } +} + +func WithGRPCServiceClient(v container.ContainerServiceClient) Option { + return func(c *cfg) { + c.gRPC.serviceClient = v + } +} + +func WithGRPCCallOpts(v []grpc.CallOption) Option { + return func(c *cfg) { + c.gRPC.grpcCallOpts = v + } +} + +func WithGRPCClientOpts(v []container.Option) Option { + return func(c *cfg) { + c.gRPC.callOpts = v + } +} + +func WithGRPCClient(v *container.Client) Option { + return func(c *cfg) { + c.gRPC.client = v + } +}