[#245] storagegroup: Add Object <-> SG converters
Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
parent
517d7a1e4a
commit
6709b00c89
2 changed files with 183 additions and 0 deletions
|
@ -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...)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue