66 "errors"
77 "fmt"
88 "maps"
9+ "sort"
10+ "strings"
911 "time"
1012
1113 "github.com/open-feature/flagd/core/pkg/model"
@@ -35,7 +37,7 @@ type syncHandler struct {
3537func (s syncHandler ) SyncFlags (req * syncv1.SyncFlagsRequest , server syncv1grpc.FlagSyncService_SyncFlagsServer ) error {
3638 watcher := make (chan store.FlagQueryResult , 1 )
3739 selectorExpression := s .getSelectorExpression (server .Context (), req )
38- selector := store . NewSelector (selectorExpression )
40+ selectors := parseSelectorList (selectorExpression )
3941 ctx := server .Context ()
4042
4143 syncContextMap := make (map [string ]any )
@@ -53,7 +55,15 @@ func (s syncHandler) SyncFlags(req *syncv1.SyncFlagsRequest, server syncv1grpc.F
5355 defer cancel ()
5456 }
5557
56- s .store .Watch (ctx , & selector , watcher )
58+ switch len (selectors ) {
59+ case 0 :
60+ s .store .Watch (ctx , nil , watcher )
61+ case 1 :
62+ s .store .Watch (ctx , & selectors [0 ], watcher )
63+ default :
64+ // For multi-selector requests, watch all updates and recompute merged view in order.
65+ s .store .Watch (ctx , nil , watcher )
66+ }
5767
5868 for {
5969 select {
@@ -63,7 +73,16 @@ func (s syncHandler) SyncFlags(req *syncv1.SyncFlagsRequest, server syncv1grpc.F
6373 return fmt .Errorf ("error constructing metadata response" )
6474 }
6575
66- flags , err := s .generateResponse (payload .Flags )
76+ flagsToSend := payload .Flags
77+ if len (selectors ) != 1 {
78+ flagsToSend , err = s .fetchMergedFlags (ctx , selectors )
79+ if err != nil {
80+ s .log .Error (fmt .Sprintf ("error retrieving merged flags from store: %v" , err ))
81+ return status .Error (codes .Internal , "error retrieving flags from store" )
82+ }
83+ }
84+
85+ flags , err := s .generateResponse (flagsToSend )
6786 if err != nil {
6887 s .log .Error (fmt .Sprintf ("error retrieving flags from store: %v" , err ))
6988 return status .Error (codes .DataLoss , "error marshalling flags" )
@@ -103,8 +122,7 @@ func (s syncHandler) generateResponse(payload []model.Flag) ([]byte, error) {
103122func (s syncHandler ) getSelectorExpression (ctx context.Context , req interface {}) string {
104123 // Try to get selector from metadata (header)
105124 if md , ok := metadata .FromIncomingContext (ctx ); ok {
106- if values := md .Get (flagdService .FLAGD_SELECTOR_HEADER ); len (values ) > 0 {
107- headerSelector := values [0 ]
125+ if headerSelector := flagdService .SelectorExpressionFromGRPCMetadata (md ); headerSelector != "" {
108126 s .log .Debug (fmt .Sprintf ("using selector from request header: %s" , headerSelector ))
109127 return headerSelector
110128 }
@@ -139,8 +157,7 @@ func (s syncHandler) FetchAllFlags(ctx context.Context, req *syncv1.FetchAllFlag
139157 * syncv1.FetchAllFlagsResponse , error ,
140158) {
141159 selectorExpression := s .getSelectorExpression (ctx , req )
142- selector := store .NewSelector (selectorExpression )
143- flags , _ , err := s .store .GetAll (ctx , & selector )
160+ flags , err := s .fetchMergedFlags (ctx , parseSelectorList (selectorExpression ))
144161 if err != nil {
145162 s .log .Error (fmt .Sprintf ("error retrieving flags from store: %v" , err ))
146163 return nil , status .Error (codes .Internal , "error retrieving flags from store" )
@@ -157,8 +174,66 @@ func (s syncHandler) FetchAllFlags(ctx context.Context, req *syncv1.FetchAllFlag
157174 }, nil
158175}
159176
177+ func parseSelectorList (selectorExpression string ) []store.Selector {
178+ if strings .TrimSpace (selectorExpression ) == "" {
179+ return nil
180+ }
181+
182+ parts := strings .Split (selectorExpression , "," )
183+ selectors := make ([]store.Selector , 0 , len (parts ))
184+ for _ , part := range parts {
185+ trimmed := strings .TrimSpace (part )
186+ if trimmed == "" {
187+ continue
188+ }
189+ selector := store .NewSelector (trimmed )
190+ selectors = append (selectors , selector )
191+ }
192+ return selectors
193+ }
194+
195+ func (s syncHandler ) fetchMergedFlags (ctx context.Context , selectors []store.Selector ) ([]model.Flag , error ) {
196+ switch len (selectors ) {
197+ case 0 :
198+ flags , _ , err := s .store .GetAll (ctx , nil )
199+ return flags , err
200+ case 1 :
201+ flags , _ , err := s .store .GetAll (ctx , & selectors [0 ])
202+ return flags , err
203+ default :
204+ type flagIdentifier struct {
205+ flagSetID string
206+ key string
207+ }
208+
209+ merged := map [flagIdentifier ]model.Flag {}
210+ for _ , selector := range selectors {
211+ flags , _ , err := s .store .GetAll (ctx , & selector )
212+ if err != nil {
213+ return nil , err
214+ }
215+ for _ , flag := range flags {
216+ merged [flagIdentifier {flagSetID : flag .FlagSetId , key : flag .Key }] = flag
217+ }
218+ }
219+
220+ out := make ([]model.Flag , 0 , len (merged ))
221+ for _ , flag := range merged {
222+ out = append (out , flag )
223+ }
224+ sort .Slice (out , func (i , j int ) bool {
225+ if out [i ].FlagSetId != out [j ].FlagSetId {
226+ return out [i ].FlagSetId < out [j ].FlagSetId
227+ }
228+ return out [i ].Key < out [j ].Key
229+ })
230+ return out , nil
231+ }
232+ }
233+
160234// Deprecated - GetMetadata is deprecated and will be removed in a future release.
161235// Use the sync_context field in syncv1.SyncFlagsResponse, providing same info.
236+ //
162237//nolint:staticcheck // SA1019 temporarily suppress deprecation warning
163238func (s syncHandler ) GetMetadata (_ context.Context , _ * syncv1.GetMetadataRequest ) (
164239 * syncv1.GetMetadataResponse , error ,
0 commit comments