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 }