forked from TrueCloudLab/frostfs-api-go
[#218] Support SplitInfo structure
Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
parent
707a0bcb35
commit
1fdeca84e1
11 changed files with 310 additions and 7 deletions
|
@ -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
49
pkg/object/splitinfo.go
Normal 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())
|
||||
}
|
42
pkg/object/splitinfo_test.go
Normal file
42
pkg/object/splitinfo_test.go
Normal 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
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
BIN
v2/object/grpc/service.pb.go
generated
BIN
v2/object/grpc/service.pb.go
generated
Binary file not shown.
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
BIN
v2/object/grpc/types.pb.go
generated
BIN
v2/object/grpc/types.pb.go
generated
Binary file not shown.
|
@ -43,6 +43,10 @@ const (
|
|||
objHeaderField = 3
|
||||
objPayloadField = 4
|
||||
|
||||
splitInfoSplitIDField = 1
|
||||
splitInfoLastPartField = 2
|
||||
splitInfoLinkField = 3
|
||||
|
||||
getReqBodyAddressField = 1
|
||||
getReqBodyRawFlagField = 2
|
||||
|
||||
|
@ -52,6 +56,7 @@ const (
|
|||
|
||||
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")
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue