[#350] v2: Define response status messages

Create `status` package. Define `status.Status` message structure. Export
getters, setters and encoding methods.

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2021-11-06 13:38:52 +03:00 committed by LeL
parent 356b58a52c
commit ca746f3800
6 changed files with 472 additions and 0 deletions

103
v2/status/convert.go Normal file
View file

@ -0,0 +1,103 @@
package status
import (
"github.com/nspcc-dev/neofs-api-go/rpc/grpc"
"github.com/nspcc-dev/neofs-api-go/rpc/message"
status "github.com/nspcc-dev/neofs-api-go/v2/status/grpc"
)
func (x *Detail) ToGRPCMessage() grpc.Message {
var m *status.Status_Detail
if x != nil {
m = new(status.Status_Detail)
m.SetId(x.id)
m.SetValue(x.val)
}
return m
}
func (x *Detail) FromGRPCMessage(m grpc.Message) error {
v, ok := m.(*status.Status_Detail)
if !ok {
return message.NewUnexpectedMessageType(m, v)
}
x.id = v.GetId()
x.val = v.GetValue()
return nil
}
func CodeFromGRPC(v uint32) Code {
return Code(v)
}
func CodeToGRPC(v Code) uint32 {
return uint32(v)
}
func (x *Status) ToGRPCMessage() grpc.Message {
var m *status.Status
if x != nil {
m = new(status.Status)
m.SetCode(CodeToGRPC(x.code))
m.SetMessage(x.msg)
var ds []*status.Status_Detail
if ln := len(x.details); ln > 0 {
ds = make([]*status.Status_Detail, 0, ln)
for i := 0; i < ln; i++ {
ds = append(ds, x.details[i].ToGRPCMessage().(*status.Status_Detail))
}
}
m.SetDetails(ds)
}
return m
}
func (x *Status) FromGRPCMessage(m grpc.Message) error {
v, ok := m.(*status.Status)
if !ok {
return message.NewUnexpectedMessageType(m, v)
}
var (
ds []*Detail
dsV2 = v.GetDetails()
)
if dsV2 != nil {
ln := len(dsV2)
ds = make([]*Detail, 0, ln)
for i := 0; i < ln; i++ {
var p *Detail
if dsV2[i] != nil {
p = new(Detail)
if err := p.FromGRPCMessage(dsV2[i]); err != nil {
return err
}
}
ds = append(ds, p)
}
}
x.details = ds
x.msg = v.GetMessage()
x.code = CodeFromGRPC(v.GetCode())
return nil
}

115
v2/status/marshal.go Normal file
View file

@ -0,0 +1,115 @@
package status
import (
"github.com/nspcc-dev/neofs-api-go/rpc/message"
protoutil "github.com/nspcc-dev/neofs-api-go/util/proto"
status "github.com/nspcc-dev/neofs-api-go/v2/status/grpc"
)
const (
_ = iota
detailIDFNum
detailValueFNum
)
func (x *Detail) StableMarshal(buf []byte) ([]byte, error) {
if x == nil {
return []byte{}, nil
}
if buf == nil {
buf = make([]byte, x.StableSize())
}
var (
offset, n int
err error
)
n, err = protoutil.UInt32Marshal(detailIDFNum, buf[offset:], x.id)
if err != nil {
return nil, err
}
offset += n
_, err = protoutil.BytesMarshal(detailValueFNum, buf[offset:], x.val)
if err != nil {
return nil, err
}
return buf, nil
}
func (x *Detail) StableSize() (size int) {
size += protoutil.UInt32Size(detailIDFNum, x.id)
size += protoutil.BytesSize(detailValueFNum, x.val)
return size
}
func (x *Detail) Unmarshal(data []byte) error {
return message.Unmarshal(x, data, new(status.Status_Detail))
}
const (
_ = iota
statusCodeFNum
statusMsgFNum
statusDetailsFNum
)
func (x *Status) StableMarshal(buf []byte) ([]byte, error) {
if x == nil {
return []byte{}, nil
}
if buf == nil {
buf = make([]byte, x.StableSize())
}
var (
offset, n int
err error
)
n, err = protoutil.UInt32Marshal(statusCodeFNum, buf[offset:], CodeToGRPC(x.code))
if err != nil {
return nil, err
}
offset += n
n, err = protoutil.StringMarshal(statusMsgFNum, buf[offset:], x.msg)
if err != nil {
return nil, err
}
offset += n
for i := range x.details {
n, err = protoutil.NestedStructureMarshal(statusDetailsFNum, buf[offset:], x.details[i])
if err != nil {
return nil, err
}
offset += n
}
return buf, nil
}
func (x *Status) StableSize() (size int) {
size += protoutil.UInt32Size(statusCodeFNum, CodeToGRPC(x.code))
size += protoutil.StringSize(statusMsgFNum, x.msg)
for i := range x.details {
size += protoutil.NestedStructureSize(statusDetailsFNum, x.details[i])
}
return size
}
func (x *Status) Unmarshal(data []byte) error {
return message.Unmarshal(x, data, new(status.Status))
}

16
v2/status/message_test.go Normal file
View file

@ -0,0 +1,16 @@
package status_test
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/rpc/message"
messagetest "github.com/nspcc-dev/neofs-api-go/rpc/message/test"
statustest "github.com/nspcc-dev/neofs-api-go/v2/status/test"
)
func TestMessageConvert(t *testing.T) {
messagetest.TestRPCMessage(t,
func(empty bool) message.Message { return statustest.Detail(empty) },
func(empty bool) message.Message { return statustest.Status(empty) },
)
}

80
v2/status/status.go Normal file
View file

@ -0,0 +1,80 @@
package status
const sectionBitSize = 10
// InSections checks if the Code is in [i,j] section list.
func (x Code) InSections(i, j uint32) bool {
return uint32(x) >= i<<sectionBitSize && uint32(x) < (j+1)<<sectionBitSize
}
// LocalizeSection localizes the Code to the sec-th section.
//
// Does not make sense if the Code is outside the section.
func (x *Code) LocalizeSection(sec uint32) {
*x = *x - Code(sec<<sectionBitSize)
}
// GlobalizeSection globalizes the Code of the sec-th section.
//
// Does not make sense if the Code is outside the section.
func (x *Code) GlobalizeSection(sec uint32) {
*x = *x + Code(sec<<sectionBitSize)
}
// IsInSection returns true if the Code belongs to sec-th section.
func IsInSection(code Code, sec uint32) bool {
return code.InSections(sec, sec)
}
const successSections = 1
// IsSuccess checks if the Code is a success code.
func IsSuccess(c Code) bool {
return c.InSections(0, successSections-1)
}
// LocalizeSuccess localizes the Code to the success section.
func LocalizeSuccess(c *Code) {
c.LocalizeSection(0)
}
// GlobalizeSuccess globalizes the Code to the success section.
func GlobalizeSuccess(c *Code) {
c.GlobalizeSection(0)
}
func sectionAfterSuccess(sec uint32) uint32 {
return successSections + sec
}
// Success codes.
const (
// OK is a local status Code value for default success.
OK Code = iota
)
// Common failure codes.
const (
// Internal is a local Code value for INTERNAL failure status.
Internal Code = iota
)
const (
_ = iota - 1
sectionCommon
)
// IsCommonFail checks if the Code is a common failure code.
func IsCommonFail(c Code) bool {
return IsInSection(c, sectionAfterSuccess(sectionCommon))
}
// LocalizeCommonFail localizes the Code to the common fail section.
func LocalizeCommonFail(c *Code) {
c.LocalizeSection(sectionAfterSuccess(sectionCommon))
}
// GlobalizeCommonFail globalizes the Code to the common fail section.
func GlobalizeCommonFail(c *Code) {
c.GlobalizeSection(sectionAfterSuccess(sectionCommon))
}

View file

@ -0,0 +1,44 @@
package statustest
import (
"github.com/nspcc-dev/neofs-api-go/v2/status"
)
// Detail returns status.Detail filled with static random values.
func Detail(empty bool) *status.Detail {
m := new(status.Detail)
if !empty {
m.SetID(345)
m.SetValue([]byte("value"))
}
return m
}
// Details returns several status.Detail messages filled with static random values.
func Details(empty bool) []*status.Detail {
var res []*status.Detail
if !empty {
res = append(res,
Detail(false),
Detail(false),
)
}
return res
}
// Status returns status.Status messages filled with static random values.
func Status(empty bool) *status.Status {
m := new(status.Status)
if !empty {
m.SetCode(765)
m.SetMessage("some string")
m.SetDetails(Details(false))
}
return m
}

114
v2/status/types.go Normal file
View file

@ -0,0 +1,114 @@
package status
// Detail represents structure of NeoFS API V2-compatible status detail message.
type Detail struct {
id uint32
val []byte
}
// ID returns identifier of the Detail.
func (x *Detail) ID() uint32 {
if x != nil {
return x.id
}
return 0
}
// SetID sets identifier of the Detail.
func (x *Detail) SetID(id uint32) {
if x != nil {
x.id = id
}
}
// Value returns value of the Detail.
func (x *Detail) Value() []byte {
if x != nil {
return x.val
}
return nil
}
// SetValue sets value of the Detail.
func (x *Detail) SetValue(val []byte) {
if x != nil {
x.val = val
}
}
// Code represents NeoFS API V2-compatible status code.
type Code uint32
// Status represents structure of NeoFS API V2-compatible status return message.
type Status struct {
code Code
msg string
details []*Detail
}
// Code returns code of the Status.
func (x *Status) Code() Code {
if x != nil {
return x.code
}
return 0
}
// SetCode sets code of the Status.
func (x *Status) SetCode(code Code) {
if x != nil {
x.code = code
}
}
// Message sets message of the Status.
func (x *Status) Message() string {
if x != nil {
return x.msg
}
return ""
}
// SetMessage sets message of the Status.
func (x *Status) SetMessage(msg string) {
if x != nil {
x.msg = msg
}
}
// NumberOfParameters returns number of network parameters.
func (x *Status) NumberOfDetails() int {
if x != nil {
return len(x.details)
}
return 0
}
// IterateDetails iterates over details of the Status.
// Breaks iteration on f's true return.
//
// Handler must not be nil.
func (x *Status) IterateDetails(f func(*Detail) bool) {
if x != nil {
for i := range x.details {
if f(x.details[i]) {
break
}
}
}
}
// SetDetails sets Detail list of the Status.
func (x *Status) SetDetails(v []*Detail) {
if x != nil {
x.details = v
}
}