git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/object/relations/relations.go (about)

     1  package relations
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/bearer"
     9  	cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
    10  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object"
    11  	oid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/object/id"
    12  	"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/session"
    13  )
    14  
    15  // Tokens contains different tokens to perform requests in Relations implementations.
    16  type Tokens struct {
    17  	Session *session.Object
    18  	Bearer  *bearer.Token
    19  }
    20  
    21  type Relations interface {
    22  	// GetSplitInfo tries to get split info by root object id.
    23  	// If object isn't virtual it returns ErrNoSplitInfo.
    24  	GetSplitInfo(ctx context.Context, cnrID cid.ID, rootID oid.ID, tokens Tokens) (*object.SplitInfo, error)
    25  
    26  	// ListChildrenByLinker returns list of children for link object.
    27  	// Result doesn't include link object itself.
    28  	ListChildrenByLinker(ctx context.Context, cnrID cid.ID, linkerID oid.ID, tokens Tokens) ([]oid.ID, error)
    29  
    30  	// GetLeftSibling return previous object id in object chain.
    31  	// If no previous object it returns ErrNoLeftSibling.
    32  	GetLeftSibling(ctx context.Context, cnrID cid.ID, objID oid.ID, tokens Tokens) (oid.ID, error)
    33  
    34  	// FindSiblingBySplitID returns all objects that relates to the provided split id.
    35  	FindSiblingBySplitID(ctx context.Context, cnrID cid.ID, splitID *object.SplitID, tokens Tokens) ([]oid.ID, error)
    36  
    37  	// FindSiblingByParentID returns all object that relates to the provided parent id.
    38  	FindSiblingByParentID(ctx context.Context, cnrID cid.ID, parentID oid.ID, tokens Tokens) ([]oid.ID, error)
    39  }
    40  
    41  var (
    42  	// ErrNoLeftSibling is an error that must be returned if object doesn't have left sibling in objects chain.
    43  	ErrNoLeftSibling = errors.New("no left siblings")
    44  
    45  	// ErrNoSplitInfo is an error that must be returned if requested object isn't virtual.
    46  	ErrNoSplitInfo = errors.New("no split info")
    47  )
    48  
    49  // ListAllRelations return all related phy objects for provided root object ID.
    50  // Result doesn't include root object ID itself.
    51  func ListAllRelations(ctx context.Context, rels Relations, cnrID cid.ID, rootObjID oid.ID, tokens Tokens) ([]oid.ID, error) {
    52  	splitInfo, err := rels.GetSplitInfo(ctx, cnrID, rootObjID, tokens)
    53  	if err != nil {
    54  		if errors.Is(err, ErrNoSplitInfo) {
    55  			return []oid.ID{}, nil
    56  		}
    57  		return nil, err
    58  	}
    59  
    60  	// collect split chain by the descending ease of operations (ease is evaluated heuristically).
    61  	// If any approach fails, we don't try the next since we assume that it will fail too.
    62  	if idLinking, ok := splitInfo.Link(); ok {
    63  		children, err := rels.ListChildrenByLinker(ctx, cnrID, idLinking, tokens)
    64  		if err != nil {
    65  			return nil, fmt.Errorf("failed to get linking object's header: %w", err)
    66  		}
    67  
    68  		// include linking object
    69  		return append(children, idLinking), nil
    70  	}
    71  
    72  	if idSplit := splitInfo.SplitID(); idSplit != nil {
    73  		members, err := rels.FindSiblingBySplitID(ctx, cnrID, idSplit, tokens)
    74  		if err != nil {
    75  			return nil, fmt.Errorf("failed to search objects by split ID: %w", err)
    76  		}
    77  		return members, nil
    78  	}
    79  
    80  	idMember, ok := splitInfo.LastPart()
    81  	if !ok {
    82  		return nil, errors.New("missing any data in received object split information")
    83  	}
    84  
    85  	chain := []oid.ID{idMember}
    86  	chainSet := map[oid.ID]struct{}{idMember: {}}
    87  
    88  	// prmHead.SetRawFlag(false)
    89  	// split members are almost definitely singular, but don't get hung up on it
    90  
    91  	for {
    92  		idMember, err = rels.GetLeftSibling(ctx, cnrID, idMember, tokens)
    93  		if err != nil {
    94  			if errors.Is(err, ErrNoLeftSibling) {
    95  				break
    96  			}
    97  			return nil, fmt.Errorf("failed to read split chain member's header: %w", err)
    98  		}
    99  
   100  		if _, ok = chainSet[idMember]; ok {
   101  			return nil, fmt.Errorf("duplicated member in the split chain %s", idMember)
   102  		}
   103  
   104  		chain = append(chain, idMember)
   105  		chainSet[idMember] = struct{}{}
   106  	}
   107  
   108  	list, err := rels.FindSiblingByParentID(ctx, cnrID, rootObjID, tokens)
   109  	if err != nil {
   110  		return nil, fmt.Errorf("failed to find object children: %w", err)
   111  	}
   112  
   113  	for i := range list {
   114  		if _, ok = chainSet[list[i]]; !ok {
   115  			chain = append(chain, list[i])
   116  		}
   117  	}
   118  
   119  	return chain, nil
   120  }