forked from TrueCloudLab/frostfs-sdk-go
121 lines
4.0 KiB
Go
121 lines
4.0 KiB
Go
package relations
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
|
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
|
|
oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
|
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
|
|
)
|
|
|
|
// Tokens contains different tokens to perform requests in Relations implementations.
|
|
type Tokens struct {
|
|
Session *session.Object
|
|
Bearer *bearer.Token
|
|
}
|
|
|
|
type Relations interface {
|
|
// GetSplitInfo tries to get split info by root object id.
|
|
// If object isn't virtual it returns ErrNoSplitInfo.
|
|
GetSplitInfo(ctx context.Context, cnrID cid.ID, rootID oid.ID, tokens Tokens) (*object.SplitInfo, error)
|
|
|
|
// ListChildrenByLinker returns list of children for link object.
|
|
// Result doesn't include link object itself.
|
|
ListChildrenByLinker(ctx context.Context, cnrID cid.ID, linkerID oid.ID, tokens Tokens) ([]oid.ID, error)
|
|
|
|
// GetLeftSibling return previous object id in object chain.
|
|
// If no previous object it returns ErrNoLeftSibling.
|
|
GetLeftSibling(ctx context.Context, cnrID cid.ID, objID oid.ID, tokens Tokens) (oid.ID, error)
|
|
|
|
// FindSiblingBySplitID returns all objects that relates to the provided split id.
|
|
FindSiblingBySplitID(ctx context.Context, cnrID cid.ID, splitID *object.SplitID, tokens Tokens) ([]oid.ID, error)
|
|
|
|
// FindSiblingByParentID returns all object that relates to the provided parent id.
|
|
FindSiblingByParentID(ctx context.Context, cnrID cid.ID, parentID oid.ID, tokens Tokens) ([]oid.ID, error)
|
|
}
|
|
|
|
var (
|
|
// ErrNoLeftSibling is an error that must be returned if object doesn't have left sibling in objects chain.
|
|
ErrNoLeftSibling = errors.New("no left siblings")
|
|
|
|
// ErrNoSplitInfo is an error that must be returned if requested object isn't virtual.
|
|
ErrNoSplitInfo = errors.New("no split info")
|
|
)
|
|
|
|
// ListAllRelations return all related phy objects for provided root object ID.
|
|
// Result doesn't include root object ID itself.
|
|
func ListAllRelations(ctx context.Context, rels Relations, cnrID cid.ID, rootObjID oid.ID, tokens Tokens) ([]oid.ID, error) {
|
|
splitInfo, err := rels.GetSplitInfo(ctx, cnrID, rootObjID, tokens)
|
|
if err != nil {
|
|
if errors.Is(err, ErrNoSplitInfo) {
|
|
return []oid.ID{}, nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
// collect split chain by the descending ease of operations (ease is evaluated heuristically).
|
|
// If any approach fails, we don't try the next since we assume that it will fail too.
|
|
if idLinking, ok := splitInfo.Link(); ok {
|
|
children, err := rels.ListChildrenByLinker(ctx, cnrID, idLinking, tokens)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get linking object's header: %w", err)
|
|
}
|
|
|
|
// include linking object
|
|
return append(children, idLinking), nil
|
|
}
|
|
|
|
if idSplit := splitInfo.SplitID(); idSplit != nil {
|
|
members, err := rels.FindSiblingBySplitID(ctx, cnrID, idSplit, tokens)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to search objects by split ID: %w", err)
|
|
}
|
|
return members, nil
|
|
}
|
|
|
|
idMember, ok := splitInfo.LastPart()
|
|
if !ok {
|
|
return nil, errors.New("missing any data in received object split information")
|
|
}
|
|
|
|
chain := []oid.ID{idMember}
|
|
chainSet := map[oid.ID]struct{}{idMember: {}}
|
|
|
|
// prmHead.SetRawFlag(false)
|
|
// split members are almost definitely singular, but don't get hung up on it
|
|
|
|
for {
|
|
idMember, err = rels.GetLeftSibling(ctx, cnrID, idMember, tokens)
|
|
if err != nil {
|
|
if errors.Is(err, ErrNoLeftSibling) {
|
|
break
|
|
}
|
|
return nil, fmt.Errorf("failed to read split chain member's header: %w", err)
|
|
}
|
|
|
|
if _, ok = chainSet[idMember]; ok {
|
|
return nil, fmt.Errorf("duplicated member in the split chain %s", idMember)
|
|
}
|
|
|
|
chain = append(chain, idMember)
|
|
chainSet[idMember] = struct{}{}
|
|
}
|
|
|
|
list, err := rels.FindSiblingByParentID(ctx, cnrID, rootObjID, tokens)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to find object children: %w", err)
|
|
}
|
|
|
|
for i := range list {
|
|
if _, ok = chainSet[list[i]]; !ok {
|
|
chain = append(chain, list[i])
|
|
}
|
|
}
|
|
|
|
return chain, nil
|
|
}
|