@@ -20,18 +20,15 @@ import (
20
20
"github.com/optimizely/go-sdk/optimizely/entities"
21
21
)
22
22
23
- // conditionEvalResult is the result of evaluating a Condition, which can be true/false or null if the condition could not be evaluated
24
- type conditionEvalResult string
25
-
26
23
const customAttributeType = "custom_attribute"
27
24
28
25
const (
29
- // TRUE means the condition passes
30
- TRUE conditionEvalResult = "TRUE "
31
- // FALSE means the condition does not pass
32
- FALSE conditionEvalResult = "FALSE "
33
- // NULL means the condition could not be evaluated
34
- NULL conditionEvalResult = "NULL "
26
+ // "and" operator returns true if all conditions evaluate to true
27
+ andOperator = "and "
28
+ // "not" operator negates the result of the given condition
29
+ notOperator = "not "
30
+ // "or" operator returns true if any of the conditions evaluate to true
31
+ orOperator = "or "
35
32
)
36
33
37
34
// ConditionTreeEvaluator evaluates a condition tree
@@ -52,12 +49,86 @@ func NewConditionTreeEvaluator() *ConditionTreeEvaluator {
52
49
// Evaluate returns true if the userAttributes satisfy the given condition tree
53
50
func (c ConditionTreeEvaluator ) Evaluate (node * entities.ConditionTreeNode , user entities.UserContext ) bool {
54
51
// This wrapper method converts the conditionEvalResult to a boolean
55
- result := c .evaluate (node , user )
56
- return result == TRUE
52
+ result , _ := c .evaluate (node , user )
53
+ return result == true
57
54
}
58
55
59
56
// Helper method to recursively evaluate a condition tree
60
- func (c ConditionTreeEvaluator ) evaluate (node * entities.ConditionTreeNode , user entities.UserContext ) conditionEvalResult {
61
- // @TODO(mng): Implement tree evaluator with and/or/not operators
62
- return TRUE
57
+ // Returns the result of the evaluation and whether the evaluation of the condition is valid or not (to handle null bubbling)
58
+ func (c ConditionTreeEvaluator ) evaluate (node * entities.ConditionTreeNode , user entities.UserContext ) (evalResult bool , isValid bool ) {
59
+ operator := node .Operator
60
+ if operator != "" {
61
+ switch operator {
62
+ case andOperator :
63
+ return c .evaluateAnd (node .Nodes , user )
64
+ case notOperator :
65
+ return c .evaluateNot (node .Nodes , user )
66
+ case orOperator :
67
+ fallthrough
68
+ default :
69
+ return c .evaluateOr (node .Nodes , user )
70
+ }
71
+ }
72
+
73
+ conditionEvaluator , ok := c .conditionEvaluatorMap [node .Condition .Type ]
74
+ if ! ok {
75
+ // TODO(mng): log error
76
+ // Result is invalid
77
+ return false , false
78
+ }
79
+ result , err := conditionEvaluator .Evaluate (node .Condition , user )
80
+ if err != nil {
81
+ // Result is invalid
82
+ return false , false
83
+ }
84
+ return result , true
85
+ }
86
+
87
+ func (c ConditionTreeEvaluator ) evaluateAnd (nodes []* entities.ConditionTreeNode , user entities.UserContext ) (evalResult bool , isValid bool ) {
88
+ sawInvalid := false
89
+ for _ , node := range nodes {
90
+ result , isValid := c .evaluate (node , user )
91
+ if ! isValid {
92
+ return false , isValid
93
+ } else if result == false {
94
+ return result , isValid
95
+ }
96
+ }
97
+
98
+ if sawInvalid {
99
+ // bubble up the invalid result
100
+ return false , false
101
+ }
102
+
103
+ return true , true
104
+ }
105
+
106
+ func (c ConditionTreeEvaluator ) evaluateNot (nodes []* entities.ConditionTreeNode , user entities.UserContext ) (evalResult bool , isValid bool ) {
107
+ if len (nodes ) > 0 {
108
+ result , isValid := c .evaluate (nodes [0 ], user )
109
+ if ! isValid {
110
+ return false , false
111
+ }
112
+ return ! result , isValid
113
+ }
114
+ return false , false
115
+ }
116
+
117
+ func (c ConditionTreeEvaluator ) evaluateOr (nodes []* entities.ConditionTreeNode , user entities.UserContext ) (evalResult bool , isValid bool ) {
118
+ sawInvalid := false
119
+ for _ , node := range nodes {
120
+ result , isValid := c .evaluate (node , user )
121
+ if ! isValid {
122
+ sawInvalid = true
123
+ } else if result == true {
124
+ return result , isValid
125
+ }
126
+ }
127
+
128
+ if sawInvalid {
129
+ // bubble up the invalid result
130
+ return false , false
131
+ }
132
+
133
+ return false , true
63
134
}
0 commit comments