forked from TrueCloudLab/frostfs-api-go
[#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:
parent
356b58a52c
commit
ca746f3800
6 changed files with 472 additions and 0 deletions
103
v2/status/convert.go
Normal file
103
v2/status/convert.go
Normal 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
115
v2/status/marshal.go
Normal 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
16
v2/status/message_test.go
Normal 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
80
v2/status/status.go
Normal 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))
|
||||
}
|
44
v2/status/test/generate.go
Normal file
44
v2/status/test/generate.go
Normal 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
114
v2/status/types.go
Normal 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
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue