[#245] storagegroup: Add Object <-> SG converters

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
Pavel Karpy 2022-05-17 22:15:21 +03:00 committed by LeL
parent 517d7a1e4a
commit 6709b00c89
2 changed files with 183 additions and 0 deletions

View file

@ -1,9 +1,14 @@
package storagegroup
import (
"fmt"
"strconv"
objectV2 "github.com/nspcc-dev/neofs-api-go/v2/object"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/storagegroup"
"github.com/nspcc-dev/neofs-sdk-go/checksum"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
)
@ -205,3 +210,87 @@ func formatCheck(v2 *storagegroup.StorageGroup) error {
return nil
}
// ReadFromObject assemble StorageGroup from a regular
// Object structure. Object must contain unambiguous information
// about its expiration epoch, otherwise behaviour is undefined.
//
// Returns any error appeared during storage group parsing; returns
// error if object is not of TypeStorageGroup type.
func ReadFromObject(sg *StorageGroup, o objectSDK.Object) error {
if typ := o.Type(); typ != objectSDK.TypeStorageGroup {
return fmt.Errorf("object is not of StorageGroup type: %s", typ)
}
err := sg.Unmarshal(o.Payload())
if err != nil {
return fmt.Errorf("could not unmarshal object: %w", err)
}
var expObj uint64
for _, attr := range o.Attributes() {
if attr.Key() == objectV2.SysAttributeExpEpoch {
expObj, err = strconv.ParseUint(attr.Value(), 10, 64)
if err != nil {
return fmt.Errorf("could not get expiration from object: %w", err)
}
break
}
}
// Supporting deprecated functionality.
// See https://github.com/nspcc-dev/neofs-api/pull/205.
if expSG := sg.ExpirationEpoch(); expObj != expSG {
return fmt.Errorf(
"expiration does not match: from object: %d, from payload: %d",
expObj, expSG)
}
return nil
}
// WriteToObject writes StorageGroup to a regular
// Object structure. Object must not contain ambiguous
// information about its expiration epoch or must not
// have it at all.
//
// Written information:
// * expiration epoch;
// * object type (TypeStorageGroup);
// * raw payload.
func WriteToObject(sg StorageGroup, o *objectSDK.Object) {
sgRaw, err := sg.Marshal()
if err != nil {
// Marshal() does not return errors
// in the next API release
panic(fmt.Errorf("could not marshal storage group: %w", err))
}
o.SetPayload(sgRaw)
o.SetType(objectSDK.TypeStorageGroup)
attrs := o.Attributes()
var expAttrFound bool
for i := range attrs {
if attrs[i].Key() == objectV2.SysAttributeExpEpoch {
expAttrFound = true
attrs[i].SetValue(strconv.FormatUint(sg.ExpirationEpoch(), 10))
break
}
}
if !expAttrFound {
var attr objectSDK.Attribute
attr.SetKey(objectV2.SysAttributeExpEpoch)
attr.SetValue(strconv.FormatUint(sg.ExpirationEpoch(), 10))
attrs = append(attrs, attr)
}
o.SetAttributes(attrs...)
}

View file

@ -2,13 +2,16 @@ package storagegroup_test
import (
"crypto/sha256"
"strconv"
"testing"
objectV2 "github.com/nspcc-dev/neofs-api-go/v2/object"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
storagegroupV2 "github.com/nspcc-dev/neofs-api-go/v2/storagegroup"
storagegroupV2test "github.com/nspcc-dev/neofs-api-go/v2/storagegroup/test"
"github.com/nspcc-dev/neofs-sdk-go/checksum"
checksumtest "github.com/nspcc-dev/neofs-sdk-go/checksum/test"
objectSDK "github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
"github.com/nspcc-dev/neofs-sdk-go/storagegroup"
@ -193,3 +196,94 @@ func TestStorageGroup_SetMembers_DoubleSetting(t *testing.T) {
// and apply update correctly
sg.SetMembers(mm[:1])
}
func TestStorageGroupFromObject(t *testing.T) {
sg := storagegrouptest.StorageGroup()
var o objectSDK.Object
var expAttr objectSDK.Attribute
expAttr.SetKey(objectV2.SysAttributeExpEpoch)
expAttr.SetValue(strconv.FormatUint(sg.ExpirationEpoch(), 10))
sgRaw, err := sg.Marshal()
require.NoError(t, err)
o.SetPayload(sgRaw)
o.SetType(objectSDK.TypeStorageGroup)
t.Run("correct object", func(t *testing.T) {
o.SetAttributes(objectSDK.Attribute{}, expAttr, objectSDK.Attribute{})
var sg2 storagegroup.StorageGroup
require.NoError(t, storagegroup.ReadFromObject(&sg2, o))
require.Equal(t, sg, sg2)
})
t.Run("incorrect exp attr", func(t *testing.T) {
var sg2 storagegroup.StorageGroup
expAttr.SetValue(strconv.FormatUint(sg.ExpirationEpoch()+1, 10))
o.SetAttributes(expAttr)
require.Error(t, storagegroup.ReadFromObject(&sg2, o))
})
t.Run("incorrect object type", func(t *testing.T) {
var sg2 storagegroup.StorageGroup
o.SetType(objectSDK.TypeTombstone)
require.Error(t, storagegroup.ReadFromObject(&sg2, o))
})
}
func TestStorageGroupToObject(t *testing.T) {
sg := storagegrouptest.StorageGroup()
sgRaw, err := sg.Marshal()
require.NoError(t, err)
t.Run("empty object", func(t *testing.T) {
var o objectSDK.Object
storagegroup.WriteToObject(sg, &o)
exp, found := expFromObj(t, o)
require.True(t, found)
require.Equal(t, sgRaw, o.Payload())
require.Equal(t, sg.ExpirationEpoch(), exp)
require.Equal(t, objectSDK.TypeStorageGroup, o.Type())
})
t.Run("obj already has exp attr", func(t *testing.T) {
var o objectSDK.Object
var attr objectSDK.Attribute
attr.SetKey(objectV2.SysAttributeExpEpoch)
attr.SetValue(strconv.FormatUint(sg.ExpirationEpoch()+1, 10))
o.SetAttributes(objectSDK.Attribute{}, attr, objectSDK.Attribute{})
storagegroup.WriteToObject(sg, &o)
exp, found := expFromObj(t, o)
require.True(t, found)
require.Equal(t, sgRaw, o.Payload())
require.Equal(t, sg.ExpirationEpoch(), exp)
require.Equal(t, objectSDK.TypeStorageGroup, o.Type())
})
}
func expFromObj(t *testing.T, o objectSDK.Object) (uint64, bool) {
for _, attr := range o.Attributes() {
if attr.Key() == objectV2.SysAttributeExpEpoch {
exp, err := strconv.ParseUint(attr.Value(), 10, 64)
require.NoError(t, err)
return exp, true
}
}
return 0, false
}