github.com/openfga/openfga@v1.5.4-rc1/pkg/storage/storagewrappers/combinedtuplereader.go (about)

     1  package storagewrappers
     2  
     3  import (
     4  	"context"
     5  
     6  	openfgav1 "github.com/openfga/api/proto/openfga/v1"
     7  
     8  	"github.com/openfga/openfga/pkg/storage"
     9  	"github.com/openfga/openfga/pkg/tuple"
    10  )
    11  
    12  // NewCombinedTupleReader returns a [storage.RelationshipTupleReader] that reads from
    13  // a persistent datastore and from the contextual tuples specified in the request.
    14  func NewCombinedTupleReader(
    15  	ds storage.RelationshipTupleReader,
    16  	contextualTuples []*openfgav1.TupleKey,
    17  ) storage.RelationshipTupleReader {
    18  	return &combinedTupleReader{
    19  		RelationshipTupleReader: ds,
    20  		contextualTuples:        contextualTuples,
    21  	}
    22  }
    23  
    24  type combinedTupleReader struct {
    25  	storage.RelationshipTupleReader
    26  	contextualTuples []*openfgav1.TupleKey
    27  }
    28  
    29  var _ storage.RelationshipTupleReader = (*combinedTupleReader)(nil)
    30  
    31  // filterTuples filters out the tuples in the provided slice by removing any tuples in the slice
    32  // that don't match the object and relation provided in the filterKey.
    33  func filterTuples(tuples []*openfgav1.TupleKey, targetObject, targetRelation string) []*openfgav1.Tuple {
    34  	var filtered []*openfgav1.Tuple
    35  	for _, tk := range tuples {
    36  		if tk.GetObject() == targetObject && tk.GetRelation() == targetRelation {
    37  			filtered = append(filtered, &openfgav1.Tuple{
    38  				Key: tk,
    39  			})
    40  		}
    41  	}
    42  
    43  	return filtered
    44  }
    45  
    46  // Read see [storage.RelationshipTupleReader.ReadUserTuple].
    47  func (c *combinedTupleReader) Read(
    48  	ctx context.Context,
    49  	storeID string,
    50  	tk *openfgav1.TupleKey,
    51  ) (storage.TupleIterator, error) {
    52  	iter1 := storage.NewStaticTupleIterator(filterTuples(c.contextualTuples, tk.GetObject(), tk.GetRelation()))
    53  
    54  	iter2, err := c.RelationshipTupleReader.Read(ctx, storeID, tk)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	return storage.NewCombinedIterator(iter1, iter2), nil
    60  }
    61  
    62  // ReadPage see [storage.RelationshipTupleReader.ReadPage].
    63  func (c *combinedTupleReader) ReadPage(
    64  	ctx context.Context,
    65  	store string,
    66  	tk *openfgav1.TupleKey,
    67  	opts storage.PaginationOptions,
    68  ) ([]*openfgav1.Tuple, []byte, error) {
    69  	// No reading from contextual tuples.
    70  	return c.RelationshipTupleReader.ReadPage(ctx, store, tk, opts)
    71  }
    72  
    73  // ReadUserTuple see [storage.RelationshipTupleReader.ReadUserTuple].
    74  func (c *combinedTupleReader) ReadUserTuple(
    75  	ctx context.Context,
    76  	store string,
    77  	tk *openfgav1.TupleKey,
    78  ) (*openfgav1.Tuple, error) {
    79  	filteredContextualTuples := filterTuples(c.contextualTuples, tk.GetObject(), tk.GetRelation())
    80  
    81  	for _, t := range filteredContextualTuples {
    82  		if t.GetKey().GetUser() == tk.GetUser() {
    83  			return t, nil
    84  		}
    85  	}
    86  
    87  	return c.RelationshipTupleReader.ReadUserTuple(ctx, store, tk)
    88  }
    89  
    90  // ReadUsersetTuples see [storage.RelationshipTupleReader].ReadUsersetTuples.
    91  func (c *combinedTupleReader) ReadUsersetTuples(
    92  	ctx context.Context,
    93  	store string,
    94  	filter storage.ReadUsersetTuplesFilter,
    95  ) (storage.TupleIterator, error) {
    96  	var usersetTuples []*openfgav1.Tuple
    97  
    98  	for _, t := range filterTuples(c.contextualTuples, filter.Object, filter.Relation) {
    99  		if tuple.GetUserTypeFromUser(t.GetKey().GetUser()) == tuple.UserSet {
   100  			usersetTuples = append(usersetTuples, t)
   101  		}
   102  	}
   103  
   104  	iter1 := storage.NewStaticTupleIterator(usersetTuples)
   105  
   106  	iter2, err := c.RelationshipTupleReader.ReadUsersetTuples(ctx, store, filter)
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  
   111  	return storage.NewCombinedIterator(iter1, iter2), nil
   112  }
   113  
   114  // ReadStartingWithUser see [storage.RelationshipTupleReader].ReadStartingWithUser.
   115  func (c *combinedTupleReader) ReadStartingWithUser(
   116  	ctx context.Context,
   117  	store string,
   118  	filter storage.ReadStartingWithUserFilter,
   119  ) (storage.TupleIterator, error) {
   120  	var filteredTuples []*openfgav1.Tuple
   121  	for _, t := range c.contextualTuples {
   122  		if tuple.GetType(t.GetObject()) != filter.ObjectType {
   123  			continue
   124  		}
   125  
   126  		if t.GetRelation() != filter.Relation {
   127  			continue
   128  		}
   129  
   130  		for _, u := range filter.UserFilter {
   131  			targetUser := u.GetObject()
   132  			if u.GetRelation() != "" {
   133  				targetUser = tuple.ToObjectRelationString(targetUser, u.GetRelation())
   134  			}
   135  
   136  			if t.GetUser() == targetUser {
   137  				filteredTuples = append(filteredTuples, &openfgav1.Tuple{
   138  					Key: t,
   139  				})
   140  			}
   141  		}
   142  	}
   143  
   144  	iter1 := storage.NewStaticTupleIterator(filteredTuples)
   145  
   146  	iter2, err := c.RelationshipTupleReader.ReadStartingWithUser(ctx, store, filter)
   147  	if err != nil {
   148  		return nil, err
   149  	}
   150  
   151  	return storage.NewCombinedIterator(iter1, iter2), nil
   152  }