[#218] Support SplitInfo structure

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
Alex Vanin 2020-12-02 13:10:43 +03:00
parent 707a0bcb35
commit 1fdeca84e1
11 changed files with 310 additions and 7 deletions

View file

@ -445,6 +445,8 @@ func (c *Client) getObjectV2(ctx context.Context, p *GetObjectParams, opts ...Ca
} else {
payload = append(payload, v.GetChunk()...)
}
case *v2object.SplitInfo: // what else can we do here?
return nil, errors.New("object not found, split info has been provided")
default:
panic(fmt.Sprintf("unexpected Get object part type %T", v))
}

49
pkg/object/splitinfo.go Normal file
View file

@ -0,0 +1,49 @@
package object
import (
"github.com/nspcc-dev/neofs-api-go/v2/object"
)
type SplitInfo object.SplitInfo
func NewSplitInfoFromV2(v2 *object.SplitInfo) *SplitInfo {
return (*SplitInfo)(v2)
}
func NewSplitInfo() *SplitInfo {
return NewSplitInfoFromV2(new(object.SplitInfo))
}
func (s *SplitInfo) ToV2() *object.SplitInfo {
return (*object.SplitInfo)(s)
}
func (s *SplitInfo) SplitID() *SplitID {
return NewSplitIDFromV2(
(*object.SplitInfo)(s).GetSplitID(),
)
}
func (s *SplitInfo) SetSplitID(v *SplitID) {
(*object.SplitInfo)(s).SetSplitID(v.ToV2())
}
func (s *SplitInfo) LastPart() *ID {
return NewIDFromV2(
(*object.SplitInfo)(s).GetLastPart(),
)
}
func (s *SplitInfo) SetLastPart(v *ID) {
(*object.SplitInfo)(s).SetLastPart(v.ToV2())
}
func (s *SplitInfo) Link() *ID {
return NewIDFromV2(
(*object.SplitInfo)(s).GetLink(),
)
}
func (s *SplitInfo) SetLink(v *ID) {
(*object.SplitInfo)(s).SetLink(v.ToV2())
}

View file

@ -0,0 +1,42 @@
package object_test
import (
"crypto/rand"
"testing"
"github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/stretchr/testify/require"
)
func TestSplitInfo(t *testing.T) {
s := object.NewSplitInfo()
splitID := object.NewSplitID()
lastPart := generateID()
link := generateID()
s.SetSplitID(splitID)
require.Equal(t, splitID, s.SplitID())
s.SetLastPart(lastPart)
require.Equal(t, lastPart, s.LastPart())
s.SetLink(link)
require.Equal(t, link, s.Link())
t.Run("to and from v2", func(t *testing.T) {
v2 := s.ToV2()
newS := object.NewSplitInfoFromV2(v2)
require.Equal(t, s, newS)
})
}
func generateID() *object.ID {
var buf [32]byte
_, _ = rand.Read(buf[:])
id := object.NewID()
id.SetSHA256(buf)
return id
}

View file

@ -371,6 +371,46 @@ func ObjectFromGRPCMessage(m *object.Object) *Object {
return o
}
func SplitInfoToGRPCMessage(s *SplitInfo) *object.SplitInfo {
if s == nil {
return nil
}
m := new(object.SplitInfo)
m.SetSplitId(s.GetSplitID())
m.SetLastPart(
refs.ObjectIDToGRPCMessage(s.GetLastPart()),
)
m.SetLink(
refs.ObjectIDToGRPCMessage(s.GetLink()),
)
return m
}
func SplitInfoFromGRPCMessage(m *object.SplitInfo) *SplitInfo {
if m == nil {
return nil
}
r := new(SplitInfo)
r.SetSplitID(m.GetSplitId())
r.SetLastPart(
refs.ObjectIDFromGRPCMessage(m.GetLastPart()),
)
r.SetLink(
refs.ObjectIDFromGRPCMessage(m.GetLink()),
)
return r
}
func GetRequestBodyToGRPCMessage(r *GetRequestBody) *object.GetRequest_Body {
if r == nil {
return nil
@ -520,6 +560,10 @@ func GetResponseBodyToGRPCMessage(r *GetResponseBody) *object.GetResponse_Body {
m.SetChunk(
GetObjectPartChunkToGRPCMessage(t),
)
case *SplitInfo:
m.SetSplitInfo(
SplitInfoToGRPCMessage(t),
)
default:
panic(fmt.Sprintf("unknown object part %T", t))
}
@ -544,6 +588,10 @@ func GetResponseBodyFromGRPCMessage(m *object.GetResponse_Body) *GetResponseBody
r.SetObjectPart(
GetObjectPartChunkFromGRPCMessage(v),
)
case *object.GetResponse_Body_SplitInfo:
r.SetObjectPart(
SplitInfoFromGRPCMessage(v.SplitInfo),
)
default:
panic(fmt.Sprintf("unknown object part %T", v))
}

View file

@ -93,6 +93,15 @@ func (m *GetResponse_Body) SetChunk(v *GetResponse_Body_Chunk) {
}
}
// SetSplitInfo sets part of the object payload.
func (m *GetResponse_Body) SetSplitInfo(v *SplitInfo) {
if m != nil {
m.ObjectPart = &GetResponse_Body_SplitInfo{
SplitInfo: v,
}
}
}
// SetBody sets body of the response.
func (m *GetResponse) SetBody(v *GetResponse_Body) {
if m != nil {

Binary file not shown.

View file

@ -200,3 +200,24 @@ func (m *ShortHeader) SetPayloadLength(v uint64) {
m.PayloadLength = v
}
}
// SetSplitId sets id of split hierarchy.
func (m *SplitInfo) SetSplitId(v []byte) {
if m != nil {
m.SplitId = v
}
}
// SetLastPart sets id of most right child in split hierarchy.
func (m *SplitInfo) SetLastPart(v *refs.ObjectID) {
if m != nil {
m.LastPart = v
}
}
// SetLink sets id of linking object in split hierarchy.
func (m *SplitInfo) SetLink(v *refs.ObjectID) {
if m != nil {
m.Link = v
}
}

Binary file not shown.

View file

@ -43,6 +43,10 @@ const (
objHeaderField = 3
objPayloadField = 4
splitInfoSplitIDField = 1
splitInfoLastPartField = 2
splitInfoLinkField = 3
getReqBodyAddressField = 1
getReqBodyRawFlagField = 2
@ -50,8 +54,9 @@ const (
getRespInitSignatureField = 2
getRespInitHeaderField = 3
getRespBodyInitField = 1
getRespBodyChunkField = 2
getRespBodyInitField = 1
getRespBodyChunkField = 2
getRespBodySplitInfoField = 3
putReqInitObjectIDField = 1
putReqInitSignatureField = 2
@ -576,6 +581,54 @@ func (o *Object) Unmarshal(data []byte) error {
return nil
}
func (s *SplitInfo) StableMarshal(buf []byte) ([]byte, error) {
if s == nil {
return []byte{}, nil
}
if buf == nil {
buf = make([]byte, s.StableSize())
}
var (
offset, n int
err error
)
n, err = proto.BytesMarshal(splitInfoSplitIDField, buf[offset:], s.splitID)
if err != nil {
return nil, err
}
offset += n
n, err = proto.NestedStructureMarshal(splitInfoLastPartField, buf[offset:], s.lastPart)
if err != nil {
return nil, err
}
offset += n
_, err = proto.NestedStructureMarshal(splitInfoLinkField, buf[offset:], s.link)
if err != nil {
return nil, err
}
return buf, nil
}
func (s *SplitInfo) StableSize() (size int) {
if s == nil {
return 0
}
size += proto.BytesSize(splitInfoSplitIDField, s.splitID)
size += proto.NestedStructureSize(splitInfoLastPartField, s.lastPart)
size += proto.NestedStructureSize(splitInfoLinkField, s.link)
return size
}
func (r *GetRequestBody) StableMarshal(buf []byte) ([]byte, error) {
if r == nil {
return []byte{}, nil
@ -687,6 +740,11 @@ func (r *GetResponseBody) StableMarshal(buf []byte) ([]byte, error) {
return nil, err
}
}
case *SplitInfo:
_, err := proto.NestedStructureMarshal(getRespBodySplitInfoField, buf, v)
if err != nil {
return nil, err
}
default:
panic("unknown one of object get response body type")
}
@ -708,6 +766,8 @@ func (r *GetResponseBody) StableSize() (size int) {
if v != nil {
size += proto.BytesSize(getRespBodyChunkField, v.chunk)
}
case *SplitInfo:
size += proto.NestedStructureSize(getRespBodySplitInfoField, v)
default:
panic("unknown one of object get response body type")
}

View file

@ -107,8 +107,9 @@ func TestGetRequestBody_StableMarshal(t *testing.T) {
}
func TestGetResponseBody_StableMarshal(t *testing.T) {
initFrom := generateGetResponseBody(true)
chunkFrom := generateGetResponseBody(false)
initFrom := generateGetResponseBody(0)
chunkFrom := generateGetResponseBody(1)
splitInfoFrom := generateGetResponseBody(2)
transport := new(grpc.GetResponse_Body)
t.Run("init non empty", func(t *testing.T) {
@ -132,6 +133,17 @@ func TestGetResponseBody_StableMarshal(t *testing.T) {
to := object.GetResponseBodyFromGRPCMessage(transport)
require.Equal(t, chunkFrom, to)
})
t.Run("split info non empty", func(t *testing.T) {
wire, err := splitInfoFrom.StableMarshal(nil)
require.NoError(t, err)
err = goproto.Unmarshal(wire, transport)
require.NoError(t, err)
to := object.GetResponseBodyFromGRPCMessage(transport)
require.Equal(t, splitInfoFrom, to)
})
}
func TestPutRequestBody_StableMarshal(t *testing.T) {
@ -518,21 +530,29 @@ func generateGetRequestBody(cid, oid string) *object.GetRequestBody {
return req
}
func generateGetResponseBody(flag bool) *object.GetResponseBody {
func generateGetResponseBody(i int) *object.GetResponseBody {
resp := new(object.GetResponseBody)
var part object.GetObjectPart
if flag {
switch i {
case 0:
init := new(object.GetObjectPartInit)
init.SetObjectID(generateObjectID("Object ID"))
init.SetSignature(generateSignature("Key", "Signature"))
init.SetHeader(generateHeader(10))
part = init
} else {
case 1:
chunk := new(object.GetObjectPartChunk)
chunk.SetChunk([]byte("Some data chunk"))
part = chunk
default:
splitInfo := new(object.SplitInfo)
splitInfo.SetSplitID([]byte("splitID"))
splitInfo.SetLastPart(generateObjectID("Right ID"))
splitInfo.SetLink(generateObjectID("Link ID"))
part = splitInfo
}
resp.SetObjectPart(part)
return resp

View file

@ -75,6 +75,14 @@ type Object struct {
payload []byte
}
type SplitInfo struct {
splitID []byte
lastPart *refs.ObjectID
link *refs.ObjectID
}
type GetRequestBody struct {
addr *refs.Address
@ -634,6 +642,50 @@ func (o *Object) SetPayload(v []byte) {
}
}
func (s *SplitInfo) GetSplitID() []byte {
if s.splitID != nil {
return s.splitID
}
return nil
}
func (s *SplitInfo) SetSplitID(v []byte) {
if s != nil {
s.splitID = v
}
}
func (s *SplitInfo) GetLastPart() *refs.ObjectID {
if s != nil {
return s.lastPart
}
return nil
}
func (s *SplitInfo) SetLastPart(v *refs.ObjectID) {
if s != nil {
s.lastPart = v
}
}
func (s *SplitInfo) GetLink() *refs.ObjectID {
if s != nil {
return s.link
}
return nil
}
func (s *SplitInfo) SetLink(v *refs.ObjectID) {
if s != nil {
s.link = v
}
}
func (s *SplitInfo) getObjectPart() {} // implement interface to be one of response body
func (r *GetRequestBody) GetAddress() *refs.Address {
if r != nil {
return r.addr