diff --git a/client/accounting.go b/client/accounting.go index b88cbf9..545dade 100644 --- a/client/accounting.go +++ b/client/accounting.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/nspcc-dev/neofs-api-go/rpc/client" v2accounting "github.com/nspcc-dev/neofs-api-go/v2/accounting" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" + "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-sdk-go/accounting" "github.com/nspcc-dev/neofs-sdk-go/owner" diff --git a/client/client.go b/client/client.go index 0948429..9e3f365 100644 --- a/client/client.go +++ b/client/client.go @@ -3,7 +3,7 @@ package client import ( "io" - "github.com/nspcc-dev/neofs-api-go/rpc/client" + "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" ) // Client represents NeoFS client. diff --git a/client/container.go b/client/container.go index 50ea04d..425bed9 100644 --- a/client/container.go +++ b/client/container.go @@ -5,10 +5,10 @@ import ( "errors" "fmt" - "github.com/nspcc-dev/neofs-api-go/rpc/client" v2container "github.com/nspcc-dev/neofs-api-go/v2/container" "github.com/nspcc-dev/neofs-api-go/v2/refs" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" + "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-sdk-go/container" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" diff --git a/client/netmap.go b/client/netmap.go index be61893..2a430dc 100644 --- a/client/netmap.go +++ b/client/netmap.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/nspcc-dev/neofs-api-go/rpc/client" v2netmap "github.com/nspcc-dev/neofs-api-go/v2/netmap" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" + "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-sdk-go/netmap" "github.com/nspcc-dev/neofs-sdk-go/version" diff --git a/client/object.go b/client/object.go index 9b56298..a62c7ce 100644 --- a/client/object.go +++ b/client/object.go @@ -9,10 +9,10 @@ import ( "fmt" "io" - "github.com/nspcc-dev/neofs-api-go/rpc/client" v2object "github.com/nspcc-dev/neofs-api-go/v2/object" v2refs "github.com/nspcc-dev/neofs-api-go/v2/refs" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" + "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" v2session "github.com/nspcc-dev/neofs-api-go/v2/session" "github.com/nspcc-dev/neofs-api-go/v2/signature" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" diff --git a/client/opts.go b/client/opts.go index f94c94e..e464faa 100644 --- a/client/opts.go +++ b/client/opts.go @@ -5,8 +5,8 @@ import ( "crypto/tls" "time" - "github.com/nspcc-dev/neofs-api-go/rpc/client" "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" v2session "github.com/nspcc-dev/neofs-api-go/v2/session" "github.com/nspcc-dev/neofs-sdk-go/session" "github.com/nspcc-dev/neofs-sdk-go/token" diff --git a/client/raw.go b/client/raw.go index fcb5751..2c05699 100644 --- a/client/raw.go +++ b/client/raw.go @@ -3,7 +3,7 @@ package client import ( "io" - "github.com/nspcc-dev/neofs-api-go/rpc/client" + "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" ) // Raw returns underlying raw protobuf client. diff --git a/client/reputation.go b/client/reputation.go index eccc774..71b619a 100644 --- a/client/reputation.go +++ b/client/reputation.go @@ -4,9 +4,9 @@ import ( "context" "fmt" - "github.com/nspcc-dev/neofs-api-go/rpc/client" v2reputation "github.com/nspcc-dev/neofs-api-go/v2/reputation" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" + "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-sdk-go/reputation" ) diff --git a/client/session.go b/client/session.go index 197ee73..3578696 100644 --- a/client/session.go +++ b/client/session.go @@ -5,8 +5,8 @@ import ( "errors" "fmt" - "github.com/nspcc-dev/neofs-api-go/rpc/client" rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" + "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" v2session "github.com/nspcc-dev/neofs-api-go/v2/session" v2signature "github.com/nspcc-dev/neofs-api-go/v2/signature" "github.com/nspcc-dev/neofs-sdk-go/owner" diff --git a/client/status/common.go b/client/status/common.go new file mode 100644 index 0000000..9b7ff77 --- /dev/null +++ b/client/status/common.go @@ -0,0 +1,55 @@ +package apistatus + +import ( + "github.com/nspcc-dev/neofs-api-go/v2/status" +) + +// ServerInternal describes failure statuses related to internal server errors. +// Instances provide Status and StatusV2 interfaces. +// +// The status is purely informative, the client should not go into details of the error except for debugging needs. +type ServerInternal struct { + v2 status.Status +} + +func (x ServerInternal) Error() string { + return errMessageStatusV2( + globalizeCodeV2(status.Internal, status.GlobalizeCommonFail), + x.v2.Message(), + ) +} + +// implements method of the FromStatusV2 local interface. +func (x *ServerInternal) fromStatusV2(st *status.Status) { + x.v2 = *st +} + +// ToStatusV2 implements StatusV2 interface method. +// If the value was returned by FromStatusV2, returns the source message. +// Otherwise, returns message with +// * code: INTERNAL; +// * string message: empty; +// * details: empty. +func (x ServerInternal) ToStatusV2() *status.Status { + x.v2.SetCode(globalizeCodeV2(status.Internal, status.GlobalizeCommonFail)) + return &x.v2 +} + +// SetMessage sets message describing internal error. +// +// Message should be used for debug purposes only. +func (x *ServerInternal) SetMessage(msg string) { + x.v2.SetMessage(msg) +} + +// Message returns message describing internal server error. +// +// Message should be used for debug purposes only. By default, it is empty. +func (x ServerInternal) Message() string { + return x.v2.Message() +} + +// WriteInternalServerErr writes err message to ServerInternal instance. +func WriteInternalServerErr(x *ServerInternal, err error) { + x.SetMessage(err.Error()) +} diff --git a/client/status/common_test.go b/client/status/common_test.go new file mode 100644 index 0000000..f32effa --- /dev/null +++ b/client/status/common_test.go @@ -0,0 +1,26 @@ +package apistatus_test + +import ( + "testing" + + apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" + "github.com/stretchr/testify/require" +) + +func TestServerInternal_Message(t *testing.T) { + const msg = "some message" + + var st apistatus.ServerInternal + + res := st.Message() + resv2 := apistatus.ToStatusV2(st).Message() + require.Empty(t, res) + require.Empty(t, resv2) + + st.SetMessage(msg) + + res = st.Message() + resv2 = apistatus.ToStatusV2(st).Message() + require.Equal(t, msg, res) + require.Equal(t, msg, resv2) +} diff --git a/client/status/status.go b/client/status/status.go new file mode 100644 index 0000000..e5facd0 --- /dev/null +++ b/client/status/status.go @@ -0,0 +1,44 @@ +package apistatus + +// Status defines a variety of NeoFS API status returns. +// +// All statuses are split into two disjoint subsets: successful and failed, and: +// * statuses that implement the build-in error interface are considered failed statuses; +// * all other value types are considered successes (nil is a default success). +// +// In Go code type of success can be determined by a type switch, failure - by a switch with errors.As calls. +// Nil should be considered as a success, and default switch section - as an unrecognized Status. +// +// To convert statuses into errors and vice versa, use functions ErrToStatus and ErrFromStatus, respectively. +// ErrFromStatus function returns nil for successful statuses. However, to simplify the check of statuses for success, +// IsSuccessful function should be used (try to avoid nil comparison). +// It should be noted that using direct typecasting is not a compatible approach. +// +// To transport statuses using the NeoFS API V2 protocol, see StatusV2 interface and FromStatusV2 and ToStatusV2 functions. +type Status interface{} + +// ErrFromStatus converts Status instance to error if it is failed. Returns nil on successful Status. +// +// Note: direct assignment may not be compatibility-safe. +func ErrFromStatus(st Status) error { + if err, ok := st.(error); ok { + return err + } + + return nil +} + +// ErrToStatus converts the error instance to Status instance. +// +// Note: direct assignment may not be compatibility-safe. +func ErrToStatus(err error) Status { + return err +} + +// IsSuccessful checks if status is successful. +// +// Note: direct cast may not be compatibility-safe. +func IsSuccessful(st Status) bool { + _, ok := st.(error) + return !ok +} diff --git a/client/status/status_test.go b/client/status/status_test.go new file mode 100644 index 0000000..7b8d15f --- /dev/null +++ b/client/status/status_test.go @@ -0,0 +1,35 @@ +package apistatus_test + +import ( + "errors" + "testing" + + apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" + "github.com/stretchr/testify/require" +) + +func TestErrors(t *testing.T) { + t.Run("error source", func(t *testing.T) { + err := errors.New("some error") + + st := apistatus.ErrToStatus(err) + + success := apistatus.IsSuccessful(st) + require.False(t, success) + + res := apistatus.ErrFromStatus(st) + + require.Equal(t, err, res) + }) + + t.Run("non-error source", func(t *testing.T) { + var st apistatus.Status = "any non-error type" + + success := apistatus.IsSuccessful(st) + require.True(t, success) + + res := apistatus.ErrFromStatus(st) + + require.Nil(t, res) + }) +} diff --git a/client/status/success.go b/client/status/success.go new file mode 100644 index 0000000..9fc5be9 --- /dev/null +++ b/client/status/success.go @@ -0,0 +1,32 @@ +package apistatus + +import ( + "github.com/nspcc-dev/neofs-api-go/v2/status" +) + +// SuccessDefaultV2 represents Status instance of default success. Implements StatusV2. +type SuccessDefaultV2 struct { + isNil bool + + v2 *status.Status +} + +// implements method of the FromStatusV2 local interface. +func (x *SuccessDefaultV2) fromStatusV2(st *status.Status) { + x.isNil = st == nil + x.v2 = st +} + +// ToStatusV2 implements StatusV2 interface method. +// If the value was returned by FromStatusV2, returns the source message. +// Otherwise, returns message with +// * code: OK; +// * string message: empty; +// * details: empty. +func (x SuccessDefaultV2) ToStatusV2() *status.Status { + if x.isNil || x.v2 != nil { + return x.v2 + } + + return newStatusV2WithLocalCode(status.OK, status.GlobalizeSuccess) +} diff --git a/client/status/unrecognized.go b/client/status/unrecognized.go new file mode 100644 index 0000000..6feb5bc --- /dev/null +++ b/client/status/unrecognized.go @@ -0,0 +1,18 @@ +package apistatus + +import ( + "github.com/nspcc-dev/neofs-api-go/v2/status" +) + +type unrecognizedStatusV2 struct { + v2 status.Status +} + +func (x unrecognizedStatusV2) Error() string { + return errMessageStatusV2("unrecognized", x.v2.Message()) +} + +// implements method of the FromStatusV2 local interface. +func (x *unrecognizedStatusV2) fromStatusV2(st *status.Status) { + x.v2 = *st +} diff --git a/client/status/v2.go b/client/status/v2.go new file mode 100644 index 0000000..c918738 --- /dev/null +++ b/client/status/v2.go @@ -0,0 +1,99 @@ +package apistatus + +import ( + "fmt" + + "github.com/nspcc-dev/neofs-api-go/v2/status" +) + +// StatusV2 defines a variety of Status instances compatible with NeoFS API V2 protocol. +// +// Note: it is not recommended to use this type directly, it is intended for documentation of the library functionality. +type StatusV2 interface { + Status + + // ToStatusV2 returns the status as github.com/nspcc-dev/neofs-api-go/v2/status.Status message structure. + ToStatusV2() *status.Status +} + +// FromStatusV2 converts status.Status message structure to Status instance. Inverse to ToStatusV2 operation. +// +// If result is not nil, it implements StatusV2. This fact should be taken into account only when passing +// the result to the inverse function ToStatusV2, casts are not compatibility-safe. +// +// Below is the mapping of return codes to Status instance types (with a description of parsing details). +// Note: notice if the return type is a pointer. +// +// Successes: +// * status.OK: *SuccessDefaultV2 (this also includes nil argument). +// +// Common failures: +// * status.Internal: *ServerInternal. +func FromStatusV2(st *status.Status) Status { + var decoder interface { + fromStatusV2(*status.Status) + } + + switch code := st.Code(); { + case status.IsSuccess(code): + switch status.LocalizeSuccess(&code); code { + case status.OK: + decoder = new(SuccessDefaultV2) + } + case status.IsCommonFail(code): + switch status.LocalizeCommonFail(&code); code { + case status.Internal: + decoder = new(ServerInternal) + } + } + + if decoder == nil { + decoder = new(unrecognizedStatusV2) + } + + decoder.fromStatusV2(st) + + return decoder +} + +// ToStatusV2 converts Status instance to status.Status message structure. Inverse to FromStatusV2 operation. +// +// If argument is the StatusV2 instance, it is converted directly. +// Otherwise, successes are converted with status.OK code w/o details and message, failures - with status.Internal. +func ToStatusV2(st Status) *status.Status { + if v, ok := st.(StatusV2); ok { + return v.ToStatusV2() + } + + if IsSuccessful(st) { + return newStatusV2WithLocalCode(status.OK, status.GlobalizeSuccess) + } + + return newStatusV2WithLocalCode(status.Internal, status.GlobalizeCommonFail) +} + +func errMessageStatusV2(code interface{}, msg string) string { + const ( + noMsgFmt = "status: code = %v" + msgFmt = noMsgFmt + " message = %s" + ) + + if msg != "" { + return fmt.Sprintf(msgFmt, code, msg) + } + + return fmt.Sprintf(noMsgFmt, code) +} + +func newStatusV2WithLocalCode(code status.Code, globalizer func(*status.Code)) *status.Status { + var st status.Status + + st.SetCode(globalizeCodeV2(code, globalizer)) + + return &st +} + +func globalizeCodeV2(code status.Code, globalizer func(*status.Code)) status.Code { + globalizer(&code) + return code +} diff --git a/client/status/v2_test.go b/client/status/v2_test.go new file mode 100644 index 0000000..7847112 --- /dev/null +++ b/client/status/v2_test.go @@ -0,0 +1,145 @@ +package apistatus_test + +import ( + "errors" + "testing" + + apistatus "github.com/nspcc-dev/neofs-sdk-go/client/status" + "github.com/stretchr/testify/require" +) + +func TestToStatusV2(t *testing.T) { + type statusConstructor func() apistatus.Status + + for _, testItem := range [...]struct { + status interface{} // Status or statusConstructor + codeV2 uint64 + }{ + { + status: errors.New("some error"), + codeV2: 1024, + }, + { + status: 1, + codeV2: 0, + }, + { + status: "text", + codeV2: 0, + }, + { + status: true, + codeV2: 0, + }, + { + status: true, + codeV2: 0, + }, + { + status: nil, + codeV2: 0, + }, + { + status: (statusConstructor)(func() apistatus.Status { + var st apistatus.ServerInternal + + st.SetMessage("internal error message") + + return st + }), + codeV2: 1024, + }, + } { + var st apistatus.Status + + if cons, ok := testItem.status.(statusConstructor); ok { + st = cons() + } else { + st = testItem.status + } + + stv2 := apistatus.ToStatusV2(st) + + // must generate the same status.Status message + require.EqualValues(t, testItem.codeV2, stv2.Code()) + + _, ok := st.(apistatus.StatusV2) + if ok { + // restore and convert again + restored := apistatus.FromStatusV2(stv2) + + res := apistatus.ToStatusV2(restored) + + // must generate the same status.Status message + require.Equal(t, stv2, res) + } + } +} + +func TestFromStatusV2(t *testing.T) { + type statusConstructor func() apistatus.Status + + for _, testItem := range [...]struct { + status interface{} // Status or statusConstructor + codeV2 uint64 + }{ + { + status: errors.New("some error"), + codeV2: 1024, + }, + { + status: 1, + codeV2: 0, + }, + { + status: "text", + codeV2: 0, + }, + { + status: true, + codeV2: 0, + }, + { + status: true, + codeV2: 0, + }, + { + status: nil, + codeV2: 0, + }, + { + status: (statusConstructor)(func() apistatus.Status { + var st apistatus.ServerInternal + + st.SetMessage("internal error message") + + return st + }), + codeV2: 1024, + }, + } { + var st apistatus.Status + + if cons, ok := testItem.status.(statusConstructor); ok { + st = cons() + } else { + st = testItem.status + } + + stv2 := apistatus.ToStatusV2(st) + + // must generate the same status.Status message + require.EqualValues(t, testItem.codeV2, stv2.Code()) + + _, ok := st.(apistatus.StatusV2) + if ok { + // restore and convert again + restored := apistatus.FromStatusV2(stv2) + + res := apistatus.ToStatusV2(restored) + + // must generate the same status.Status message + require.Equal(t, stv2, res) + } + } +} diff --git a/go.mod b/go.mod index e1eb55e..ba43d5b 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/hashicorp/golang-lru v0.5.4 github.com/mr-tron/base58 v1.2.0 github.com/nspcc-dev/hrw v1.0.9 - github.com/nspcc-dev/neofs-api-go v1.30.0 + github.com/nspcc-dev/neofs-api-go/v2 v2.11.0-pre.0.20211118144033-580f6c5554ff github.com/nspcc-dev/neofs-crypto v0.3.0 github.com/nspcc-dev/rfc6979 v0.2.0 github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index 7fd1dfd..b98906a 100644 Binary files a/go.sum and b/go.sum differ diff --git a/pool/mock_test.go b/pool/mock_test.go index 1a44b1f..538a8d0 100644 --- a/pool/mock_test.go +++ b/pool/mock_test.go @@ -10,7 +10,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" - client "github.com/nspcc-dev/neofs-api-go/rpc/client" + client "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" accounting "github.com/nspcc-dev/neofs-sdk-go/accounting" client0 "github.com/nspcc-dev/neofs-sdk-go/client" container "github.com/nspcc-dev/neofs-sdk-go/container"