Skip to content

Commit 89a82b0

Browse files
refactoring/ renaming condition Tree to a generic Tree.
1 parent 1d6328f commit 89a82b0

File tree

13 files changed

+145
-171
lines changed

13 files changed

+145
-171
lines changed

optimizely/config/datafileProjectConfig/mappers/condition_trees.go

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ import (
2626
var ErrEmptyTree = errors.New("Empty Tree")
2727

2828
// Takes the conditions array from the audience in the datafile and turns it into a condition tree
29-
func buildConditionTree(conditions interface{}) (conditionTree *entities.ConditionTreeNode, retErr error) {
29+
func buildConditionTree(conditions interface{}) (conditionTree *entities.TreeNode, retErr error) {
3030

3131
value := reflect.ValueOf(conditions)
3232
visited := make(map[interface{}]bool)
3333

34-
conditionTree = &entities.ConditionTreeNode{}
35-
var populateConditions func(v reflect.Value, root *entities.ConditionTreeNode)
36-
populateConditions = func(v reflect.Value, root *entities.ConditionTreeNode) {
34+
conditionTree = &entities.TreeNode{}
35+
var populateConditions func(v reflect.Value, root *entities.TreeNode)
36+
populateConditions = func(v reflect.Value, root *entities.TreeNode) {
3737

3838
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
3939
if v.Kind() == reflect.Ptr {
@@ -50,7 +50,7 @@ func buildConditionTree(conditions interface{}) (conditionTree *entities.Conditi
5050

5151
case reflect.Slice, reflect.Array:
5252
for i := 0; i < v.Len(); i++ {
53-
n := &entities.ConditionTreeNode{}
53+
n := &entities.TreeNode{}
5454
typedV := v.Index(i).Interface()
5555
switch typedV.(type) {
5656
case string:
@@ -69,7 +69,7 @@ func buildConditionTree(conditions interface{}) (conditionTree *entities.Conditi
6969
retErr = err
7070
return
7171
}
72-
n.Condition = condition
72+
n.Item = condition
7373
}
7474

7575
root.Nodes = append(root.Nodes, n)
@@ -88,15 +88,15 @@ func buildConditionTree(conditions interface{}) (conditionTree *entities.Conditi
8888
}
8989

9090
// Takes the conditions array from the audience in the datafile and turns it into a condition tree
91-
func buildAudienceConditionTree(conditions interface{}) (conditionTree *entities.ConditionTreeNode, err error) {
91+
func buildAudienceConditionTree(conditions interface{}) (conditionTree *entities.TreeNode, err error) {
9292

9393
var operators = []string{"or", "and", "not"} // any other operators?
9494
value := reflect.ValueOf(conditions)
9595
visited := make(map[interface{}]bool)
9696

97-
conditionTree = &entities.ConditionTreeNode{}
98-
var populateConditions func(v reflect.Value, root *entities.ConditionTreeNode)
99-
populateConditions = func(v reflect.Value, root *entities.ConditionTreeNode) {
97+
conditionTree = &entities.TreeNode{}
98+
var populateConditions func(v reflect.Value, root *entities.TreeNode)
99+
populateConditions = func(v reflect.Value, root *entities.TreeNode) {
100100

101101
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface {
102102
if v.Kind() == reflect.Ptr {
@@ -113,7 +113,7 @@ func buildAudienceConditionTree(conditions interface{}) (conditionTree *entities
113113

114114
case reflect.Slice, reflect.Array:
115115
for i := 0; i < v.Len(); i++ {
116-
n := &entities.ConditionTreeNode{}
116+
n := &entities.TreeNode{}
117117
typedV := v.Index(i).Interface()
118118
switch typedV.(type) {
119119
case string:
@@ -123,9 +123,8 @@ func buildAudienceConditionTree(conditions interface{}) (conditionTree *entities
123123
root.Operator = n.Operator
124124
continue
125125
} else {
126-
n.Condition.Value = value
127-
n.Condition.Type = "audience_condition"
128-
n.Condition.Name = "optimizely_populated"
126+
n.Item = value
127+
129128
}
130129
}
131130

optimizely/config/datafileProjectConfig/mappers/condition_trees_test.go

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -33,38 +33,23 @@ func TestBuildAudienceConditionTreeSimpleAudienceCondition(t *testing.T) {
3333
assert.Fail(t, err.Error())
3434
}
3535

36-
expectedConditionTree := &entities.ConditionTreeNode{
36+
expectedConditionTree := &entities.TreeNode{
3737
Operator: "and",
38-
Nodes: []*entities.ConditionTreeNode{
38+
Nodes: []*entities.TreeNode{
3939
{
4040
Operator: "or",
41-
Nodes: []*entities.ConditionTreeNode{
41+
Nodes: []*entities.TreeNode{
4242
{
4343
Operator: "or",
44-
Nodes: []*entities.ConditionTreeNode{
44+
Nodes: []*entities.TreeNode{
4545
{
46-
Condition: entities.Condition{
47-
Name: "optimizely_populated",
48-
Match: "",
49-
Type: "audience_condition",
50-
Value: "12",
51-
},
46+
Item: "12",
5247
},
5348
{
54-
Condition: entities.Condition{
55-
Name: "optimizely_populated",
56-
Match: "",
57-
Type: "audience_condition",
58-
Value: "123",
59-
},
49+
Item: "123",
6050
},
6151
{
62-
Condition: entities.Condition{
63-
Name: "optimizely_populated",
64-
Match: "",
65-
Type: "audience_condition",
66-
Value: "1234",
67-
},
52+
Item: "1234",
6853
},
6954
},
7055
},
@@ -84,17 +69,17 @@ func TestBuildConditionTreeSimpleAudienceCondition(t *testing.T) {
8469
assert.Fail(t, err.Error())
8570
}
8671

87-
expectedConditionTree := &entities.ConditionTreeNode{
72+
expectedConditionTree := &entities.TreeNode{
8873
Operator: "and",
89-
Nodes: []*entities.ConditionTreeNode{
74+
Nodes: []*entities.TreeNode{
9075
{
9176
Operator: "or",
92-
Nodes: []*entities.ConditionTreeNode{
77+
Nodes: []*entities.TreeNode{
9378
{
9479
Operator: "or",
95-
Nodes: []*entities.ConditionTreeNode{
80+
Nodes: []*entities.TreeNode{
9681
{
97-
Condition: entities.Condition{
82+
Item: entities.Condition{
9883
Name: "s_foo",
9984
Match: "exact",
10085
Type: "custom_attribute",

optimizely/config/datafileProjectConfig/mappers/experiment_test.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,12 @@ func TestMapExperiments(t *testing.T) {
9090
EndOfRange: 10000,
9191
},
9292
},
93-
AudienceConditionTree: &entities.ConditionTreeNode{
93+
AudienceConditionTree: &entities.TreeNode{
9494
Operator: "or",
95-
Nodes: []*entities.ConditionTreeNode{
95+
Nodes: []*entities.TreeNode{
9696
{
9797
Operator: "",
98-
Condition: entities.Condition{
99-
Name: "optimizely_populated",
100-
Match: "",
101-
Type: "audience_condition",
102-
Value: "31111",
103-
},
98+
Item: "31111",
10499
},
105100
},
106101
},

optimizely/decision/evaluator/audience.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,23 +22,23 @@ import (
2222

2323
// AudienceEvaluator evaluates an audience against the given user's attributes
2424
type AudienceEvaluator interface {
25-
Evaluate(audience entities.Audience, condTreeParams *entities.ConditionTreeParameters) bool
25+
Evaluate(audience entities.Audience, condTreeParams *entities.TreeParameters) bool
2626
}
2727

2828
// TypedAudienceEvaluator evaluates typed audiences
2929
type TypedAudienceEvaluator struct {
30-
conditionTreeEvaluator ConditionTreeEvaluator
30+
conditionTreeEvaluator TreeEvaluator
3131
}
3232

3333
// NewTypedAudienceEvaluator creates a new instance of the TypedAudienceEvaluator
3434
func NewTypedAudienceEvaluator() *TypedAudienceEvaluator {
35-
conditionTreeEvaluator := NewConditionTreeEvaluator()
35+
conditionTreeEvaluator := NewTreeEvaluator()
3636
return &TypedAudienceEvaluator{
3737
conditionTreeEvaluator: *conditionTreeEvaluator,
3838
}
3939
}
4040

4141
// Evaluate evaluates the typed audience against the given user's attributes
42-
func (a TypedAudienceEvaluator) Evaluate(audience entities.Audience, condTreeParams *entities.ConditionTreeParameters) bool {
42+
func (a TypedAudienceEvaluator) Evaluate(audience entities.Audience, condTreeParams *entities.TreeParameters) bool {
4343
return a.conditionTreeEvaluator.Evaluate(audience.ConditionTree, condTreeParams)
4444
}

optimizely/decision/evaluator/condition.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,19 @@ const (
2929
gtMatchType = "gt"
3030
)
3131

32-
// ConditionEvaluator evaluates a condition against the given user's attributes
33-
type ConditionEvaluator interface {
34-
Evaluate(entities.Condition, *entities.ConditionTreeParameters) (bool, error)
32+
// ItemEvaluator evaluates a condition against the given user's attributes
33+
type ItemEvaluator interface {
34+
Evaluate(interface{}, *entities.TreeParameters) (bool, error)
3535
}
3636

3737
// CustomAttributeConditionEvaluator evaluates conditions with custom attributes
3838
type CustomAttributeConditionEvaluator struct{}
3939

4040
// Evaluate returns true if the given user's attributes match the condition
41-
func (c CustomAttributeConditionEvaluator) Evaluate(condition entities.Condition, condTreeParams *entities.ConditionTreeParameters) (bool, error) {
41+
func (c CustomAttributeConditionEvaluator) Evaluate(item interface{}, condTreeParams *entities.TreeParameters) (bool, error) {
4242
// We should only be evaluating custom attributes
43+
44+
condition := item.(entities.Condition)
4345
if condition.Type != customAttributeType {
4446
return false, fmt.Errorf(`Unable to evaluator condition of type "%s"`, condition.Type)
4547
}
@@ -76,18 +78,15 @@ func (c CustomAttributeConditionEvaluator) Evaluate(condition entities.Condition
7678
type AudienceConditionEvaluator struct{}
7779

7880
// Evaluate returns true if the given user's attributes match the condition
79-
func (c AudienceConditionEvaluator) Evaluate(condition entities.Condition, condTreeParams *entities.ConditionTreeParameters) (bool, error) {
80-
// We should only be evaluating custom attributes
81-
if condition.Type != audienceCondition {
82-
return false, fmt.Errorf(`Unable to evaluator condition of type "%s"`, condition.Type)
83-
}
81+
func (c AudienceConditionEvaluator) Evaluate(item interface{}, condTreeParams *entities.TreeParameters) (bool, error) {
82+
8483
var ok bool
8584
var audienceID string
8685

87-
if audienceID, ok = condition.Value.(string); ok {
86+
if audienceID, ok = item.(string); ok {
8887
if audience, good := condTreeParams.AudienceMap[audienceID]; good {
8988
condTree := audience.ConditionTree
90-
conditionTreeEvaluator := NewConditionTreeEvaluator()
89+
conditionTreeEvaluator := NewTreeEvaluator()
9190
retValue := conditionTreeEvaluator.Evaluate(condTree, condTreeParams)
9291
return retValue, nil
9392
}

optimizely/decision/evaluator/condition_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func TestCustomAttributeConditionEvaluator(t *testing.T) {
4141
},
4242
}
4343

44-
condTreeParams := entities.NewConditionTreeParameters(&user, map[string]entities.Audience{})
44+
condTreeParams := entities.NewTreeParameters(&user, map[string]entities.Audience{})
4545
result, _ := conditionEvaluator.Evaluate(condition, condTreeParams)
4646
assert.Equal(t, result, true)
4747

optimizely/decision/evaluator/condition_tree.go

Lines changed: 23 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
package evaluator
1818

1919
import (
20+
"fmt"
2021
"github.com/optimizely/go-sdk/optimizely/entities"
2122
)
2223

2324
const customAttributeType = "custom_attribute"
24-
const audienceCondition = "audience_condition"
2525

2626
const (
2727
// "and" operator returns true if all conditions evaluate to true
@@ -32,33 +32,26 @@ const (
3232
orOperator = "or"
3333
)
3434

35-
// ConditionTreeEvaluator evaluates a condition tree
36-
type ConditionTreeEvaluator struct {
37-
conditionEvaluatorMap map[string]ConditionEvaluator
35+
//TreeEvaluator evaluates a condition tree
36+
type TreeEvaluator struct {
3837
}
3938

40-
// NewConditionTreeEvaluator creates a condition tree evaluator with the out-of-the-box condition evaluators
41-
func NewConditionTreeEvaluator() *ConditionTreeEvaluator {
42-
// For now, only one evaluator per attribute type
43-
conditionEvaluatorMap := make(map[string]ConditionEvaluator)
44-
conditionEvaluatorMap[customAttributeType] = CustomAttributeConditionEvaluator{}
45-
conditionEvaluatorMap[audienceCondition] = AudienceConditionEvaluator{}
46-
return &ConditionTreeEvaluator{
47-
conditionEvaluatorMap: conditionEvaluatorMap,
48-
}
39+
// NewTreeEvaluator creates a condition tree evaluator with the out-of-the-box condition evaluators
40+
func NewTreeEvaluator() *TreeEvaluator {
41+
return &TreeEvaluator{}
4942
}
5043

5144
// entities.UserContext
5245
// Evaluate returns true if the userAttributes satisfy the given condition tree
53-
func (c ConditionTreeEvaluator) Evaluate(node *entities.ConditionTreeNode, condTreeParams *entities.ConditionTreeParameters) bool {
46+
func (c TreeEvaluator) Evaluate(node *entities.TreeNode, condTreeParams *entities.TreeParameters) bool {
5447
// This wrapper method converts the conditionEvalResult to a boolean
5548
result, _ := c.evaluate(node, condTreeParams)
5649
return result == true
5750
}
5851

5952
// Helper method to recursively evaluate a condition tree
6053
// Returns the result of the evaluation and whether the evaluation of the condition is valid or not (to handle null bubbling)
61-
func (c ConditionTreeEvaluator) evaluate(node *entities.ConditionTreeNode, condTreeParams *entities.ConditionTreeParameters) (evalResult bool, isValid bool) {
54+
func (c TreeEvaluator) evaluate(node *entities.TreeNode, condTreeParams *entities.TreeParameters) (evalResult bool, isValid bool) {
6255
operator := node.Operator
6356
if operator != "" {
6457
switch operator {
@@ -73,21 +66,28 @@ func (c ConditionTreeEvaluator) evaluate(node *entities.ConditionTreeNode, condT
7366
}
7467
}
7568

76-
conditionEvaluator, ok := c.conditionEvaluatorMap[node.Condition.Type]
77-
if !ok {
78-
// TODO(mng): log error
79-
// Result is invalid
69+
var result bool
70+
var err error
71+
switch v := node.Item.(type) {
72+
case entities.Condition:
73+
evaluator := CustomAttributeConditionEvaluator{}
74+
result, err = evaluator.Evaluate(node.Item, condTreeParams)
75+
case string:
76+
evaluator := AudienceConditionEvaluator{}
77+
result, err = evaluator.Evaluate(node.Item, condTreeParams)
78+
default:
79+
fmt.Printf("I don't know about type %T!\n", v)
8080
return false, false
8181
}
82-
result, err := conditionEvaluator.Evaluate(node.Condition, condTreeParams)
82+
8383
if err != nil {
8484
// Result is invalid
8585
return false, false
8686
}
8787
return result, true
8888
}
8989

90-
func (c ConditionTreeEvaluator) evaluateAnd(nodes []*entities.ConditionTreeNode, condTreeParams *entities.ConditionTreeParameters) (evalResult bool, isValid bool) {
90+
func (c TreeEvaluator) evaluateAnd(nodes []*entities.TreeNode, condTreeParams *entities.TreeParameters) (evalResult bool, isValid bool) {
9191
sawInvalid := false
9292
for _, node := range nodes {
9393
result, isValid := c.evaluate(node, condTreeParams)
@@ -106,7 +106,7 @@ func (c ConditionTreeEvaluator) evaluateAnd(nodes []*entities.ConditionTreeNode,
106106
return true, true
107107
}
108108

109-
func (c ConditionTreeEvaluator) evaluateNot(nodes []*entities.ConditionTreeNode, condTreeParams *entities.ConditionTreeParameters) (evalResult bool, isValid bool) {
109+
func (c TreeEvaluator) evaluateNot(nodes []*entities.TreeNode, condTreeParams *entities.TreeParameters) (evalResult bool, isValid bool) {
110110
if len(nodes) > 0 {
111111
result, isValid := c.evaluate(nodes[0], condTreeParams)
112112
if !isValid {
@@ -117,7 +117,7 @@ func (c ConditionTreeEvaluator) evaluateNot(nodes []*entities.ConditionTreeNode,
117117
return false, false
118118
}
119119

120-
func (c ConditionTreeEvaluator) evaluateOr(nodes []*entities.ConditionTreeNode, condTreeParams *entities.ConditionTreeParameters) (evalResult bool, isValid bool) {
120+
func (c TreeEvaluator) evaluateOr(nodes []*entities.TreeNode, condTreeParams *entities.TreeParameters) (evalResult bool, isValid bool) {
121121
sawInvalid := false
122122
for _, node := range nodes {
123123
result, isValid := c.evaluate(node, condTreeParams)

0 commit comments

Comments
 (0)