frostfs-sdk-go/object/object.go
Airat Arifullin 6281a25556
All checks were successful
/ DCO (pull_request) Successful in 1m17s
/ Lint (pull_request) Successful in 2m7s
/ Tests (1.19) (pull_request) Successful in 5m56s
/ Tests (1.20) (pull_request) Successful in 6m37s
[#100] types: Make sdk types as protobuf wrappers
Signed-off-by: Airat Arifullin a.arifullin@yadro.com
2023-07-12 19:08:37 +03:00

685 lines
16 KiB
Go

package object
import (
"errors"
"fmt"
object "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object"
objectgrpc "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/object/grpc"
refsgrpc "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs/grpc"
sessiongrpc "git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/session/grpc"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/checksum"
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/util/slices"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
"github.com/golang/protobuf/proto"
"google.golang.org/protobuf/encoding/protojson"
)
// Object represents in-memory structure of the FrostFS objectgrpc.
// Type is compatible with FrostFS API V2 protocol.
//
// Instance can be created depending on scenario:
// - InitCreation (an object to be placed in container);
// - New (blank instance, usually needed for decoding);
// - NewFromV2 (when working under FrostFS API V2 protocol).
type Object struct {
object *objectgrpc.Object
}
// RequiredFields contains the minimum set of object data that must be set
// by the FrostFS user at the stage of creation.
type RequiredFields struct {
// Identifier of the FrostFS container associated with the objectgrpc.
Container cid.ID
// Object owner's user ID in the FrostFS system.
Owner user.ID
}
// InitCreation initializes the object instance with minimum set of required fields.
// Object is expected (but not required) to be blank. Object must not be nil.
func InitCreation(dst *Object, rf RequiredFields) {
dst.SetContainerID(rf.Container)
dst.SetOwnerID(&rf.Owner)
}
// NewFromV2 wraps v2 Object message to Object.
func NewFromV2(obj *objectgrpc.Object) *Object {
objSDK := &Object{
object: new(objectgrpc.Object),
}
proto.Merge(objSDK.object, obj)
return objSDK
}
// New creates and initializes blank Object.
//
// Works similar as NewFromV2(new(Object)).
func New() *Object {
return NewFromV2(new(objectgrpc.Object))
}
// ToV2 converts Object to v2 Object message.
func (o *Object) ToV2() *objectgrpc.Object {
return o.object
}
// MarshalHeaderJSON marshals object's header
// into JSON format.
func (o *Object) MarshalHeaderJSON() ([]byte, error) {
return protojson.MarshalOptions{
EmitUnpopulated: true,
}.Marshal(
o.object.GetHeader(),
)
}
func (o *Object) setHeaderField(setter func(*objectgrpc.Header)) {
h := o.object.GetHeader()
if h == nil {
h = new(objectgrpc.Header)
o.object.SetHeader(h)
}
setter(h)
}
func (o *Object) setSplitFields(setter func(*objectgrpc.Header_Split)) {
o.setHeaderField(func(h *objectgrpc.Header) {
split := h.GetSplit()
if split == nil {
split = new(objectgrpc.Header_Split)
h.SetSplit(split)
}
setter(split)
})
}
// ID returns object identifier.
func (o *Object) ID() (v oid.ID, isSet bool) {
v2 := o.object
if id := v2.GetObjectId(); id != nil {
_ = v.ReadFromV2(v2.GetObjectId())
isSet = true
}
return
}
// SetID sets object identifier.
func (o *Object) SetID(v oid.ID) {
var v2 refsgrpc.ObjectID
v.WriteToV2(&v2)
o.object.SetObjectId(&v2)
}
// Signature returns signature of the object identifier.
func (o *Object) Signature() *frostfscrypto.Signature {
sigv2 := o.object.GetSignature()
if sigv2 == nil {
return nil
}
sig := frostfscrypto.NewSignature()
_ = sig.ReadFromV2(sigv2) // FIXME(@cthulhu-rider): #226 handle error
return &sig
}
// SetSignature sets signature of the object identifier.
func (o *Object) SetSignature(v *frostfscrypto.Signature) {
var sigv2 *refsgrpc.Signature
if v != nil {
sigv2 = new(refsgrpc.Signature)
v.WriteToV2(sigv2)
}
o.object.SetSignature(sigv2)
}
// Payload returns payload bytes.
func (o *Object) Payload() []byte {
return o.object.GetPayload()
}
// SetPayload sets payload bytes.
func (o *Object) SetPayload(v []byte) {
o.object.SetPayload(v)
}
// Version returns version of the objectgrpc.
func (o *Object) Version() *version.Version {
ver := version.NewVersion()
if verV2 := o.object.GetHeader().GetVersion(); verV2 != nil {
ver.ReadFromV2(verV2)
}
return &ver
}
// SetVersion sets version of the objectgrpc.
func (o *Object) SetVersion(v version.Version) {
var verV2 refsgrpc.Version
v.WriteToV2(&verV2)
o.setHeaderField(func(h *objectgrpc.Header) {
h.SetVersion(&verV2)
})
}
// PayloadSize returns payload length of the objectgrpc.
func (o *Object) PayloadSize() uint64 {
return o.object.
GetHeader().
GetPayloadLength()
}
// SetPayloadSize sets payload length of the objectgrpc.
func (o *Object) SetPayloadSize(v uint64) {
o.setHeaderField(func(h *objectgrpc.Header) {
h.SetPayloadLength(v)
})
}
// ContainerID returns identifier of the related container.
func (o *Object) ContainerID() (v cid.ID, isSet bool) {
v2 := o.object
cidV2 := v2.GetHeader().GetContainerId()
if cidV2 != nil {
_ = v.ReadFromV2(cidV2)
isSet = true
}
return
}
// SetContainerID sets identifier of the related container.
func (o *Object) SetContainerID(v cid.ID) {
var cidV2 refsgrpc.ContainerID
v.WriteToV2(&cidV2)
o.setHeaderField(func(h *objectgrpc.Header) {
h.SetContainerId(&cidV2)
})
}
func (o *Object) Equal(o1 *Object) bool {
return proto.Equal(o.object, o1.object)
}
// OwnerID returns identifier of the object owner.
func (o *Object) OwnerID() *user.ID {
var id user.ID
m := o.object.GetHeader().GetOwnerId()
if m != nil {
_ = id.ReadFromV2(m)
}
return &id
}
// SetOwnerID sets identifier of the object owner.
func (o *Object) SetOwnerID(v *user.ID) {
o.setHeaderField(func(h *objectgrpc.Header) {
var m refsgrpc.OwnerID
v.WriteToV2(&m)
h.SetOwnerId(&m)
})
}
// CreationEpoch returns epoch number in which object was created.
func (o *Object) CreationEpoch() uint64 {
return o.object.
GetHeader().
GetCreationEpoch()
}
// SetCreationEpoch sets epoch number in which object was created.
func (o *Object) SetCreationEpoch(v uint64) {
o.setHeaderField(func(h *objectgrpc.Header) {
h.SetCreationEpoch(v)
})
}
// PayloadChecksum returns checksum of the object payload and
// bool that indicates checksum presence in the objectgrpc.
//
// Zero Object does not have payload checksum.
//
// See also SetPayloadChecksum.
func (o *Object) PayloadChecksum() (checksum.Checksum, bool) {
v := checksum.NewChecksum()
v2 := o.object
if hash := v2.GetHeader().GetPayloadHash(); hash != nil {
_ = v.ReadFromV2(hash) // FIXME(@cthulhu-rider): #226 handle error
return v, true
}
return v, false
}
// SetPayloadChecksum sets checksum of the object payload.
//
// See also PayloadChecksum.
func (o *Object) SetPayloadChecksum(v checksum.Checksum) {
var v2 refsgrpc.Checksum
v.WriteToV2(&v2)
o.setHeaderField(func(h *objectgrpc.Header) {
h.SetPayloadHash(&v2)
})
}
// PayloadHomomorphicHash returns homomorphic hash of the object
// payload and bool that indicates checksum presence in the objectgrpc.
//
// Zero Object does not have payload homomorphic checksum.
//
// See also SetPayloadHomomorphicHash.
func (o *Object) PayloadHomomorphicHash() (checksum.Checksum, bool) {
v := checksum.NewChecksum()
v2 := o.object
if hash := v2.GetHeader().GetHomomorphicHash(); hash != nil {
_ = v.ReadFromV2(hash) // FIXME(@cthulhu-rider): #226 handle error
return v, true
}
return v, false
}
// SetPayloadHomomorphicHash sets homomorphic hash of the object payload.
//
// See also PayloadHomomorphicHash.
func (o *Object) SetPayloadHomomorphicHash(v checksum.Checksum) {
var v2 refsgrpc.Checksum
v.WriteToV2(&v2)
o.setHeaderField(func(h *objectgrpc.Header) {
h.SetHomomorphicHash(&v2)
})
}
// Attributes returns object attributes.
func (o *Object) Attributes() []Attribute {
attrs := o.object.
GetHeader().
GetAttributes()
res := make([]Attribute, len(attrs))
for i := range attrs {
res[i] = *NewAttributeFromV2(attrs[i])
}
return res
}
// SetAttributes sets object attributes.
func (o *Object) SetAttributes(v ...Attribute) {
attrs := slices.MakePreallocPointerSlice[objectgrpc.Header_Attribute](len(v))
for i := range v {
attrs[i] = v[i].ToV2()
}
o.setHeaderField(func(h *objectgrpc.Header) {
h.SetAttributes(attrs)
})
}
// PreviousID returns identifier of the previous sibling objectgrpc.
func (o *Object) PreviousID() (v oid.ID, isSet bool) {
v2 := o.object
v2Prev := v2.GetHeader().GetSplit().GetPrevious()
if v2Prev != nil {
_ = v.ReadFromV2(v2Prev)
isSet = true
}
return
}
// SetPreviousID sets identifier of the previous sibling objectgrpc.
func (o *Object) SetPreviousID(v oid.ID) {
var v2 refsgrpc.ObjectID
v.WriteToV2(&v2)
o.setSplitFields(func(split *objectgrpc.Header_Split) {
split.SetPrevious(&v2)
})
}
// Children return list of the identifiers of the child objects.
func (o *Object) Children() []oid.ID {
v2 := o.object
ids := v2.GetHeader().GetSplit().GetChildren()
var (
id oid.ID
res = make([]oid.ID, len(ids))
)
for i := range ids {
_ = id.ReadFromV2(ids[i])
res[i] = id
}
return res
}
// SetChildren sets list of the identifiers of the child objects.
func (o *Object) SetChildren(v ...oid.ID) {
ids := make([]*refsgrpc.ObjectID, len(v))
for i := range v {
var v2 refsgrpc.ObjectID
v[i].WriteToV2(&v2)
ids[i] = &v2
}
o.setSplitFields(func(split *objectgrpc.Header_Split) {
split.SetChildren(ids)
})
}
// NotificationInfo groups information about object notification
// that can be written to objectgrpc.
//
// Topic is an optional field.
type NotificationInfo struct {
ni object.NotificationInfo
}
// Epoch returns object notification tick
// epoch.
func (n NotificationInfo) Epoch() uint64 {
return n.ni.Epoch()
}
// SetEpoch sets object notification tick
// epoch.
func (n *NotificationInfo) SetEpoch(epoch uint64) {
n.ni.SetEpoch(epoch)
}
// Topic return optional object notification
// topic.
func (n NotificationInfo) Topic() string {
return n.ni.Topic()
}
// SetTopic sets optional object notification
// topic.
func (n *NotificationInfo) SetTopic(topic string) {
n.ni.SetTopic(topic)
}
// NotificationInfo returns notification info
// read from the object structure.
// Returns any error that appeared during notification
// information parsing.
func (o *Object) NotificationInfo() (*NotificationInfo, error) {
ni, err := object.GetNotificationInfo(o.object)
if err != nil {
return nil, err
}
return &NotificationInfo{
ni: *ni,
}, nil
}
// SetNotification writes NotificationInfo to the object structure.
func (o *Object) SetNotification(ni NotificationInfo) {
object.WriteNotificationInfo(o.object, ni.ni)
}
// SplitID return split identity of split objectgrpc. If object is not split
// returns nil.
func (o *Object) SplitID() *SplitID {
return NewSplitIDFromV2(
o.object.
GetHeader().
GetSplit().
GetSplitId(),
)
}
// SetSplitID sets split identifier for the split objectgrpc.
func (o *Object) SetSplitID(id *SplitID) {
o.setSplitFields(func(split *objectgrpc.Header_Split) {
split.SetSplitId(id.ToV2())
})
}
// ParentID returns identifier of the parent objectgrpc.
func (o *Object) ParentID() (v oid.ID, isSet bool) {
v2 := o.object
v2Par := v2.GetHeader().GetSplit().GetParent()
if v2Par != nil {
_ = v.ReadFromV2(v2Par)
isSet = true
}
return
}
// SetParentID sets identifier of the parent objectgrpc.
func (o *Object) SetParentID(v oid.ID) {
var v2 refsgrpc.ObjectID
v.WriteToV2(&v2)
o.setSplitFields(func(split *objectgrpc.Header_Split) {
split.SetParent(&v2)
})
}
// Parent returns parent object w/o payload.
func (o *Object) Parent() *Object {
h := o.object.
GetHeader().
GetSplit()
parSig := h.GetParentSignature()
parHdr := h.GetParentHeader()
if parSig == nil && parHdr == nil {
return nil
}
oV2 := new(objectgrpc.Object)
oV2.SetObjectId(h.GetParent())
oV2.SetSignature(parSig)
oV2.SetHeader(parHdr)
return NewFromV2(oV2)
}
// SetParent sets parent object w/o payload.
func (o *Object) SetParent(v *Object) {
o.setSplitFields(func(split *objectgrpc.Header_Split) {
split.SetParent(v.object.GetObjectId())
split.SetParentSignature(v.object.GetSignature())
split.SetParentHeader(v.object.GetHeader())
})
}
func (o *Object) initRelations() {
o.setHeaderField(func(h *objectgrpc.Header) {
h.SetSplit(new(objectgrpc.Header_Split))
})
}
func (o *Object) resetRelations() {
o.setHeaderField(func(h *objectgrpc.Header) {
h.SetSplit(nil)
})
}
// SessionToken returns token of the session
// within which object was created.
func (o *Object) SessionToken() *session.Object {
tokv2 := o.object.GetHeader().GetSessionToken()
if tokv2 == nil {
return nil
}
res := session.NewObject()
_ = res.ReadFromV2(tokv2)
return res
}
// SetSessionToken sets token of the session
// within which object was created.
func (o *Object) SetSessionToken(v *session.Object) {
o.setHeaderField(func(h *objectgrpc.Header) {
var tokv2 *sessiongrpc.SessionToken
if v != nil {
tokv2 = new(sessiongrpc.SessionToken)
v.WriteToV2(tokv2)
}
h.SetSessionToken(tokv2)
})
}
// Type returns type of the object.
func (o *Object) Type() Type {
return TypeFromV2(
o.object.
GetHeader().
GetObjectType(),
)
}
// SetType sets type of the objectgrpc.
func (o *Object) SetType(v Type) {
o.setHeaderField(func(h *objectgrpc.Header) {
h.SetObjectType(v.ToV2())
})
}
// CutPayload returns Object with empty payload.
func (o *Object) CutPayload() *Object {
ov2 := New()
proto.Merge(ov2.object, o.object)
ov2.object.SetPayload(nil)
return ov2
}
func (o *Object) HasParent() bool {
return o.object.
GetHeader().
GetSplit() != nil
}
// ResetRelations removes all fields of links with other objects.
func (o *Object) ResetRelations() {
o.resetRelations()
}
// InitRelations initializes relation field.
func (o *Object) InitRelations() {
o.initRelations()
}
// Marshal marshals object into a protobuf binary form.
func (o *Object) Marshal() ([]byte, error) {
return o.object.StableMarshal(nil), nil
}
// Unmarshal unmarshals protobuf binary representation of object.
func (o *Object) Unmarshal(data []byte) error {
if o.object == nil {
o.object = new(objectgrpc.Object)
}
err := proto.Unmarshal(data, o.object)
if err != nil {
return err
}
return formatCheck(o.object)
}
// MarshalJSON encodes object to protobuf JSON format.
func (o *Object) MarshalJSON() ([]byte, error) {
return protojson.MarshalOptions{
EmitUnpopulated: true,
}.Marshal(
o.object,
)
}
// UnmarshalJSON decodes object from protobuf JSON format.
func (o *Object) UnmarshalJSON(data []byte) error {
err := protojson.Unmarshal(data, o.object)
if err != nil {
return err
}
return formatCheck(o.object)
}
var errOIDNotSet = errors.New("object ID is not set")
var errCIDNotSet = errors.New("container ID is not set")
func formatCheck(v2 *objectgrpc.Object) error {
var (
oID oid.ID
cID cid.ID
)
oidV2 := v2.GetObjectId()
if oidV2 == nil {
return errOIDNotSet
}
err := oID.ReadFromV2(oidV2)
if err != nil {
return fmt.Errorf("could not convert V2 object ID: %w", err)
}
cidV2 := v2.GetHeader().GetContainerId()
if cidV2 == nil {
return errCIDNotSet
}
err = cID.ReadFromV2(cidV2)
if err != nil {
return fmt.Errorf("could not convert V2 container ID: %w", err)
}
if prev := v2.GetHeader().GetSplit().GetPrevious(); prev != nil {
err = oID.ReadFromV2(prev)
if err != nil {
return fmt.Errorf("could not convert previous object ID: %w", err)
}
}
if parent := v2.GetHeader().GetSplit().GetParent(); parent != nil {
err = oID.ReadFromV2(parent)
if err != nil {
return fmt.Errorf("could not convert parent object ID: %w", err)
}
}
return nil
}