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