@@ -22,62 +22,67 @@ func getPriorityOrDefault(priority *float64) float64 {
2222}
2323
2424func getMatchingSegmentsAndOverrides (ec * engine_eval.EngineEvaluationContext ) ([]engine_eval.SegmentResult , map [string ]featureContextWithSegmentName ) {
25- segments := []engine_eval.SegmentResult {}
26- segmentFeatureContexts := make (map [string ]featureContextWithSegmentName )
25+ segmentResults := []engine_eval.SegmentResult {}
26+ featureOverrides := make (map [string ]featureContextWithSegmentName )
2727
28- // Get sorted segment keys for deterministic ordering
29- segmentKeys := make ([]string , 0 , len (ec .Segments ))
30- for key := range ec .Segments {
31- segmentKeys = append (segmentKeys , key )
32- }
33- sort .Strings (segmentKeys )
34-
35- // Process segments in sorted order
36- for _ , key := range segmentKeys {
37- segmentContext := ec .Segments [key ]
28+ // Process segments in deterministic order (sorted by key)
29+ for _ , segmentContext := range getSortedSegments (ec .Segments ) {
3830 if ! engine_eval .IsContextInSegment (ec , & segmentContext ) {
3931 continue
4032 }
4133
42- // Add segment to results
43- segments = append (segments , engine_eval.SegmentResult {
44- Key : segmentContext .Key ,
34+ // Record matched segment
35+ segmentResults = append (segmentResults , engine_eval.SegmentResult {
4536 Name : segmentContext .Name ,
4637 Metadata : segmentContext .Metadata ,
4738 })
4839
49- // Process segment overrides
50- if segmentContext .Overrides != nil {
51- for i := range segmentContext .Overrides {
52- override := & segmentContext .Overrides [i ]
53- featureKey := override .FeatureKey
54-
55- // Check if we should update the segment feature context
56- shouldUpdate := false
57- if existing , exists := segmentFeatureContexts [featureKey ]; ! exists {
58- shouldUpdate = true
59- } else {
60- existingPriority := getPriorityOrDefault (existing .featureContext .Priority )
61- overridePriority := getPriorityOrDefault (override .Priority )
62- if overridePriority < existingPriority {
63- shouldUpdate = true
64- }
65- }
40+ // Apply segment's feature overrides (respecting priority)
41+ applySegmentOverrides (& segmentContext , featureOverrides )
42+ }
6643
67- if shouldUpdate {
68- segmentFeatureContexts [featureKey ] = featureContextWithSegmentName {
69- featureContext : override ,
70- segmentName : segmentContext .Name ,
71- }
72- }
44+ return segmentResults , featureOverrides
45+ }
46+
47+ // getSortedSegments returns segments sorted by their keys for deterministic ordering.
48+ func getSortedSegments (segments map [string ]engine_eval.SegmentContext ) []engine_eval.SegmentContext {
49+ keys := make ([]string , 0 , len (segments ))
50+ for key := range segments {
51+ keys = append (keys , key )
52+ }
53+ sort .Strings (keys )
54+
55+ sorted := make ([]engine_eval.SegmentContext , 0 , len (keys ))
56+ for _ , key := range keys {
57+ sorted = append (sorted , segments [key ])
58+ }
59+ return sorted
60+ }
61+
62+ // applySegmentOverrides updates the feature overrides map with this segment's overrides,
63+ // only replacing existing overrides if the new one has equal or higher priority.
64+ func applySegmentOverrides (segment * engine_eval.SegmentContext , featureOverrides map [string ]featureContextWithSegmentName ) {
65+ for i := range segment .Overrides {
66+ override := & segment .Overrides [i ]
67+ newPriority := getPriorityOrDefault (override .Priority )
68+
69+ // Check if we should use this override
70+ if existing , exists := featureOverrides [override .Name ]; exists {
71+ existingPriority := getPriorityOrDefault (existing .featureContext .Priority )
72+ if newPriority > existingPriority {
73+ continue // Existing override has higher priority
7374 }
7475 }
75- }
7676
77- return segments , segmentFeatureContexts
77+ // Use this override (either it's new or has equal/higher priority)
78+ featureOverrides [override .Name ] = featureContextWithSegmentName {
79+ featureContext : override ,
80+ segmentName : segment .Name ,
81+ }
82+ }
7883}
7984
80- func getFlagResults (ec * engine_eval.EngineEvaluationContext , segmentFeatureContexts map [string ]featureContextWithSegmentName ) map [string ]* engine_eval.FlagResult {
85+ func getFlagResults (ec * engine_eval.EngineEvaluationContext , featureOverrides map [string ]featureContextWithSegmentName ) map [string ]* engine_eval.FlagResult {
8186 flags := make (map [string ]* engine_eval.FlagResult )
8287
8388 // Get identity key if identity exists
@@ -87,24 +92,20 @@ func getFlagResults(ec *engine_eval.EngineEvaluationContext, segmentFeatureConte
8792 }
8893
8994 if ec .Features != nil {
90- for _ , featureContext := range ec .Features {
91- // Check if we have a segment override for this feature
92- if segmentFeatureCtx , exists := segmentFeatureContexts [featureContext .FeatureKey ]; exists {
93- // Use segment override
94- fc := segmentFeatureCtx .featureContext
95- reason := fmt .Sprintf ("TARGETING_MATCH; segment=%s" , segmentFeatureCtx .segmentName )
96- flags [featureContext .Name ] = & engine_eval.FlagResult {
97- Enabled : fc .Enabled ,
98- FeatureKey : fc .FeatureKey ,
99- Name : fc .Name ,
100- Reason : & reason ,
101- Value : fc .Value ,
102- Metadata : fc .Metadata ,
95+ for featureName , featureContext := range ec .Features {
96+ // Check if there's an override for this feature
97+ if override , ok := featureOverrides [featureName ]; ok {
98+ flags [featureName ] = & engine_eval.FlagResult {
99+ Enabled : override .featureContext .Enabled ,
100+ Name : featureName ,
101+ Reason : fmt .Sprintf ("TARGETING_MATCH; segment=%s" , override .segmentName ),
102+ Value : override .featureContext .Value ,
103+ Metadata : override .featureContext .Metadata ,
103104 }
104105 } else {
105106 // Use default feature context
106- flagResult := getFlagResultFromFeatureContext (& featureContext , identityKey )
107- flags [featureContext . Name ] = & flagResult
107+ flagResult := getFlagResultFromFeatureContext (featureName , & featureContext , identityKey )
108+ flags [featureName ] = & flagResult
108109 }
109110 }
110111 }
@@ -114,20 +115,20 @@ func getFlagResults(ec *engine_eval.EngineEvaluationContext, segmentFeatureConte
114115
115116// GetEvaluationResult computes flags and matched segments.
116117func GetEvaluationResult (ec * engine_eval.EngineEvaluationContext ) engine_eval.EvaluationResult {
117- // Process segments
118- segments , segmentFeatureContexts := getMatchingSegmentsAndOverrides (ec )
118+ // Process segments and get overrides
119+ segmentResults , featureOverrides := getMatchingSegmentsAndOverrides (ec )
119120
120121 // Get flag results
121- flags := getFlagResults (ec , segmentFeatureContexts )
122+ flags := getFlagResults (ec , featureOverrides )
122123
123124 return engine_eval.EvaluationResult {
124125 Flags : flags ,
125- Segments : segments ,
126+ Segments : segmentResults ,
126127 }
127128}
128129
129130// getFlagResultFromFeatureContext creates a FlagResult from a FeatureContext.
130- func getFlagResultFromFeatureContext (featureContext * engine_eval.FeatureContext , identityKey * string ) engine_eval.FlagResult {
131+ func getFlagResultFromFeatureContext (featureName string , featureContext * engine_eval.FeatureContext , identityKey * string ) engine_eval.FlagResult {
131132 reason := "DEFAULT"
132133 value := featureContext .Value
133134
@@ -153,12 +154,11 @@ func getFlagResultFromFeatureContext(featureContext *engine_eval.FeatureContext,
153154 }
154155
155156 flagResult := engine_eval.FlagResult {
156- Enabled : featureContext .Enabled ,
157- FeatureKey : featureContext .FeatureKey ,
158- Name : featureContext .Name ,
159- Value : value ,
160- Reason : & reason ,
161- Metadata : featureContext .Metadata ,
157+ Enabled : featureContext .Enabled ,
158+ Name : featureName ,
159+ Value : value ,
160+ Reason : reason ,
161+ Metadata : featureContext .Metadata ,
162162 }
163163
164164 return flagResult
0 commit comments