Skip to content

Commit 01fb83e

Browse files
yasirfolio3Michael Ng
authored andcommitted
feat(audience-evaluation): Added support for parsing map for leaf condition. (#175)
1 parent 0ab3594 commit 01fb83e

File tree

2 files changed

+73
-20
lines changed

2 files changed

+73
-20
lines changed

pkg/config/datafileprojectconfig/mappers/condition_trees.go

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"errors"
2222
"reflect"
2323

24-
"github.com/json-iterator/go"
24+
jsoniter "github.com/json-iterator/go"
2525
"github.com/optimizely/go-sdk/pkg/entities"
2626
)
2727

@@ -31,21 +31,16 @@ var json = jsoniter.ConfigCompatibleWithStandardLibrary
3131
// Takes the conditions array from the audience in the datafile and turns it into a condition tree
3232
func buildConditionTree(conditions interface{}) (conditionTree *entities.TreeNode, retErr error) {
3333

34-
var parsedConditions interface{}
35-
switch v := conditions.(type) {
36-
case string:
37-
if err := json.Unmarshal([]byte(v), &parsedConditions); err != nil {
38-
retErr = err
39-
return
40-
}
41-
default:
42-
parsedConditions = conditions
34+
parsedConditions, retErr := parseConditions(conditions)
35+
if retErr != nil {
36+
return
4337
}
4438

4539
value := reflect.ValueOf(parsedConditions)
4640
visited := make(map[interface{}]bool)
4741

4842
conditionTree = &entities.TreeNode{}
43+
4944
var populateConditions func(v reflect.Value, root *entities.TreeNode)
5045
populateConditions = func(v reflect.Value, root *entities.TreeNode) {
5146

@@ -73,17 +68,10 @@ func buildConditionTree(conditions interface{}) (conditionTree *entities.TreeNod
7368
continue
7469

7570
case map[string]interface{}:
76-
jsonBody, err := json.Marshal(typedV)
77-
if err != nil {
71+
if err := createLeafCondition(value, n); err != nil {
7872
retErr = err
7973
return
8074
}
81-
condition := entities.Condition{}
82-
if err := json.Unmarshal(jsonBody, &condition); err != nil {
83-
retErr = err
84-
return
85-
}
86-
n.Item = condition
8775
}
8876

8977
root.Nodes = append(root.Nodes, n)
@@ -93,22 +81,64 @@ func buildConditionTree(conditions interface{}) (conditionTree *entities.TreeNod
9381
}
9482
}
9583

96-
populateConditions(value, conditionTree)
84+
// Check for leaf conditions
85+
if value.Kind() == reflect.Map {
86+
typedV := value.Interface()
87+
if v, ok := typedV.(map[string]interface{}); ok {
88+
n := &entities.TreeNode{}
89+
if err := createLeafCondition(v, n); err != nil {
90+
retErr = err
91+
return
92+
}
93+
conditionTree.Operator = "or"
94+
conditionTree.Nodes = append(conditionTree.Nodes, n)
95+
}
96+
} else {
97+
populateConditions(value, conditionTree)
98+
}
99+
97100
if conditionTree.Nodes == nil && conditionTree.Operator == "" {
98101
retErr = errEmptyTree
99102
conditionTree = nil
100103
}
101104
return conditionTree, retErr
102105
}
103106

107+
// Parses conditions for audience in the datafile
108+
func parseConditions(conditions interface{}) (parsedConditions interface{}, retErr error) {
109+
switch v := conditions.(type) {
110+
case string:
111+
if err := json.Unmarshal([]byte(v), &parsedConditions); err != nil {
112+
return nil, err
113+
}
114+
default:
115+
parsedConditions = conditions
116+
}
117+
return parsedConditions, nil
118+
}
119+
120+
// Creates condition for the leaf node in the condition tree
121+
func createLeafCondition(typedV map[string]interface{}, node *entities.TreeNode) error {
122+
jsonBody, err := json.Marshal(typedV)
123+
if err != nil {
124+
return err
125+
}
126+
condition := entities.Condition{}
127+
if err := json.Unmarshal(jsonBody, &condition); err != nil {
128+
return err
129+
}
130+
node.Item = condition
131+
return nil
132+
}
133+
104134
// Takes the conditions array from the audience in the datafile and turns it into a condition tree
105135
func buildAudienceConditionTree(conditions interface{}) (conditionTree *entities.TreeNode, err error) {
106136

107137
var operators = []string{"or", "and", "not"} // any other operators?
108138
value := reflect.ValueOf(conditions)
109139
visited := make(map[interface{}]bool)
110140

111-
conditionTree = &entities.TreeNode{ Operator: "or" }
141+
conditionTree = &entities.TreeNode{Operator: "or"}
112142
var populateConditions func(v reflect.Value, root *entities.TreeNode)
113143
populateConditions = func(v reflect.Value, root *entities.TreeNode) {
114144

pkg/config/datafileprojectconfig/mappers/condition_trees_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,3 +159,26 @@ func TestBuildConditionTreeSimpleAudienceCondition(t *testing.T) {
159159
}
160160
assert.Equal(t, expectedConditionTree, conditionTree)
161161
}
162+
163+
func TestBuildConditionTreeWithLeafNode(t *testing.T) {
164+
conditionString := "{ \"type\": \"custom_attribute\", \"name\": \"s_foo\", \"match\": \"exact\", \"value\": \"foo\" }"
165+
var conditions interface{}
166+
json.Unmarshal([]byte(conditionString), &conditions)
167+
conditionTree, err := buildConditionTree(conditions)
168+
assert.NoError(t, err)
169+
170+
expectedConditionTree := &entities.TreeNode{
171+
Operator: "or",
172+
Nodes: []*entities.TreeNode{
173+
{
174+
Item: entities.Condition{
175+
Name: "s_foo",
176+
Match: "exact",
177+
Type: "custom_attribute",
178+
Value: "foo",
179+
},
180+
},
181+
},
182+
}
183+
assert.Equal(t, expectedConditionTree, conditionTree)
184+
}

0 commit comments

Comments
 (0)