Skip to content

Commit 6974ef4

Browse files
authored
refact(audience-logs): Added and refactored audience evaluation logs. (#280)
1 parent ca35c47 commit 6974ef4

27 files changed

+944
-358
lines changed

pkg/config/datafileprojectconfig/mappers/condition_trees.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2019, Optimizely, Inc. and contributors *
2+
* Copyright 2019-2020, Optimizely, Inc. and contributors *
33
* *
44
* Licensed under the Apache License, Version 2.0 (the "License"); *
55
* you may not use this file except in compliance with the License. *
@@ -127,6 +127,7 @@ func createLeafCondition(typedV map[string]interface{}, node *entities.TreeNode)
127127
if err := json.Unmarshal(jsonBody, &condition); err != nil {
128128
return err
129129
}
130+
condition.StringRepresentation = string(jsonBody)
130131
node.Item = condition
131132
return nil
132133
}

pkg/config/datafileprojectconfig/mappers/condition_trees_test.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2019, Optimizely, Inc. and contributors *
2+
* Copyright 2019-2020, Optimizely, Inc. and contributors *
33
* *
44
* Licensed under the Apache License, Version 2.0 (the "License"); *
55
* you may not use this file except in compliance with the License. *
@@ -111,9 +111,10 @@ func TestBuildConditionTreeUsingDatafileAudienceConditions(t *testing.T) {
111111
Nodes: []*entities.TreeNode{
112112
{
113113
Item: entities.Condition{
114-
Name: "s_foo",
115-
Type: "custom_attribute",
116-
Value: "foo",
114+
Name: "s_foo",
115+
Type: "custom_attribute",
116+
Value: "foo",
117+
StringRepresentation: `{"name":"s_foo","type":"custom_attribute","value":"foo"}`,
117118
},
118119
},
119120
},
@@ -145,10 +146,11 @@ func TestBuildConditionTreeSimpleAudienceCondition(t *testing.T) {
145146
Nodes: []*entities.TreeNode{
146147
{
147148
Item: entities.Condition{
148-
Name: "s_foo",
149-
Match: "exact",
150-
Type: "custom_attribute",
151-
Value: "foo",
149+
Name: "s_foo",
150+
Match: "exact",
151+
Type: "custom_attribute",
152+
Value: "foo",
153+
StringRepresentation: `{"match":"exact","name":"s_foo","type":"custom_attribute","value":"foo"}`,
152154
},
153155
},
154156
},
@@ -172,10 +174,11 @@ func TestBuildConditionTreeWithLeafNode(t *testing.T) {
172174
Nodes: []*entities.TreeNode{
173175
{
174176
Item: entities.Condition{
175-
Name: "s_foo",
176-
Match: "exact",
177-
Type: "custom_attribute",
178-
Value: "foo",
177+
Name: "s_foo",
178+
Match: "exact",
179+
Type: "custom_attribute",
180+
Value: "foo",
181+
StringRepresentation: `{"match":"exact","name":"s_foo","type":"custom_attribute","value":"foo"}`,
179182
},
180183
},
181184
},

pkg/decision/evaluator/audience.go

Lines changed: 0 additions & 45 deletions
This file was deleted.

pkg/decision/evaluator/condition.go

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2019, Optimizely, Inc. and contributors *
2+
* Copyright 2019-2020, Optimizely, Inc. and contributors *
33
* *
44
* Licensed under the Apache License, Version 2.0 (the "License"); *
55
* you may not use this file except in compliance with the License. *
@@ -22,6 +22,7 @@ import (
2222

2323
"github.com/optimizely/go-sdk/pkg/decision/evaluator/matchers"
2424
"github.com/optimizely/go-sdk/pkg/entities"
25+
"github.com/optimizely/go-sdk/pkg/logging"
2526
)
2627

2728
// ItemEvaluator evaluates a condition against the given user's attributes
@@ -30,14 +31,23 @@ type ItemEvaluator interface {
3031
}
3132

3233
// CustomAttributeConditionEvaluator evaluates conditions with custom attributes
33-
type CustomAttributeConditionEvaluator struct{}
34+
type CustomAttributeConditionEvaluator struct {
35+
logger logging.OptimizelyLogProducer
36+
}
37+
38+
// NewCustomAttributeConditionEvaluator creates a custom attribute condition
39+
func NewCustomAttributeConditionEvaluator(logger logging.OptimizelyLogProducer) *CustomAttributeConditionEvaluator {
40+
return &CustomAttributeConditionEvaluator{logger: logger}
41+
}
3442

3543
// Evaluate returns true if the given user's attributes match the condition
3644
func (c CustomAttributeConditionEvaluator) Evaluate(condition entities.Condition, condTreeParams *entities.TreeParameters) (bool, error) {
3745
// We should only be evaluating custom attributes
3846

3947
if condition.Type != customAttributeType {
40-
return false, fmt.Errorf(`unable to evaluator condition of type "%s"`, condition.Type)
48+
49+
c.logger.Warning(fmt.Sprintf(logging.UnknownConditionType.String(), condition.StringRepresentation))
50+
return false, fmt.Errorf(`unable to evaluate condition of type "%s"`, condition.Type)
4151
}
4252

4353
matchType := condition.Match
@@ -47,27 +57,36 @@ func (c CustomAttributeConditionEvaluator) Evaluate(condition entities.Condition
4757

4858
matcher, ok := matchers.Get(matchType)
4959
if !ok {
60+
c.logger.Warning(fmt.Sprintf(logging.UnknownMatchType.String(), condition.StringRepresentation))
5061
return false, fmt.Errorf(`invalid Condition matcher "%s"`, condition.Match)
5162
}
5263

53-
return matcher(condition, *condTreeParams.User)
64+
return matcher(condition, *condTreeParams.User, c.logger)
5465
}
5566

5667
// AudienceConditionEvaluator evaluates conditions with audience condition
57-
type AudienceConditionEvaluator struct{}
68+
type AudienceConditionEvaluator struct {
69+
logger logging.OptimizelyLogProducer
70+
}
71+
72+
// NewAudienceConditionEvaluator creates a audience condition evaluator
73+
func NewAudienceConditionEvaluator(logger logging.OptimizelyLogProducer) *AudienceConditionEvaluator {
74+
return &AudienceConditionEvaluator{logger: logger}
75+
}
5876

5977
// Evaluate returns true if the given user's attributes match the condition
6078
func (c AudienceConditionEvaluator) Evaluate(audienceID string, condTreeParams *entities.TreeParameters) (bool, error) {
6179

6280
if audience, ok := condTreeParams.AudienceMap[audienceID]; ok {
81+
c.logger.Debug(fmt.Sprintf(logging.AudienceEvaluationStarted.String(), audienceID))
6382
condTree := audience.ConditionTree
64-
conditionTreeEvaluator := NewMixedTreeEvaluator()
83+
conditionTreeEvaluator := NewMixedTreeEvaluator(c.logger)
6584
retValue, isValid := conditionTreeEvaluator.Evaluate(condTree, condTreeParams)
6685
if !isValid {
6786
return false, fmt.Errorf(`an error occurred while evaluating nested tree for audience ID "%s"`, audienceID)
6887
}
88+
c.logger.Debug(fmt.Sprintf(logging.AudienceEvaluatedTo.String(), audienceID, retValue))
6989
return retValue, nil
70-
7190
}
7291

7392
return false, fmt.Errorf(`unable to evaluate nested tree for audience ID "%s"`, audienceID)

pkg/decision/evaluator/condition_test.go

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2019, Optimizely, Inc. and contributors *
2+
* Copyright 2019-2020, Optimizely, Inc. and contributors *
33
* *
44
* Licensed under the Apache License, Version 2.0 (the "License"); *
55
* you may not use this file except in compliance with the License. *
@@ -17,14 +17,26 @@
1717
package evaluator
1818

1919
import (
20+
"fmt"
2021
"testing"
2122

2223
"github.com/optimizely/go-sdk/pkg/entities"
23-
"github.com/stretchr/testify/assert"
24+
"github.com/optimizely/go-sdk/pkg/logging"
25+
"github.com/stretchr/testify/suite"
2426
)
2527

26-
func TestCustomAttributeConditionEvaluator(t *testing.T) {
27-
conditionEvaluator := CustomAttributeConditionEvaluator{}
28+
type ConditionTestSuite struct {
29+
suite.Suite
30+
mockLogger *MockLogger
31+
conditionEvaluator *CustomAttributeConditionEvaluator
32+
}
33+
34+
func (s *ConditionTestSuite) SetupTest() {
35+
s.mockLogger = new(MockLogger)
36+
s.conditionEvaluator = NewCustomAttributeConditionEvaluator(s.mockLogger)
37+
}
38+
39+
func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluator() {
2840
condition := entities.Condition{
2941
Match: "exact",
3042
Value: "foo",
@@ -40,21 +52,20 @@ func TestCustomAttributeConditionEvaluator(t *testing.T) {
4052
}
4153

4254
condTreeParams := entities.NewTreeParameters(&user, map[string]entities.Audience{})
43-
result, _ := conditionEvaluator.Evaluate(condition, condTreeParams)
44-
assert.Equal(t, result, true)
55+
result, _ := s.conditionEvaluator.Evaluate(condition, condTreeParams)
56+
s.Equal(result, true)
4557

4658
// Test condition fails
4759
user = entities.UserContext{
4860
Attributes: map[string]interface{}{
4961
"string_foo": "not_foo",
5062
},
5163
}
52-
result, _ = conditionEvaluator.Evaluate(condition, condTreeParams)
53-
assert.Equal(t, result, false)
64+
result, _ = s.conditionEvaluator.Evaluate(condition, condTreeParams)
65+
s.Equal(result, false)
5466
}
5567

56-
func TestCustomAttributeConditionEvaluatorWithoutMatchType(t *testing.T) {
57-
conditionEvaluator := CustomAttributeConditionEvaluator{}
68+
func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorWithoutMatchType() {
5869
condition := entities.Condition{
5970
Value: "foo",
6071
Name: "string_foo",
@@ -69,15 +80,62 @@ func TestCustomAttributeConditionEvaluatorWithoutMatchType(t *testing.T) {
6980
}
7081

7182
condTreeParams := entities.NewTreeParameters(&user, map[string]entities.Audience{})
72-
result, _ := conditionEvaluator.Evaluate(condition, condTreeParams)
73-
assert.Equal(t, result, true)
83+
result, _ := s.conditionEvaluator.Evaluate(condition, condTreeParams)
84+
s.Equal(result, true)
7485

7586
// Test condition fails
7687
user = entities.UserContext{
7788
Attributes: map[string]interface{}{
7889
"string_foo": "not_foo",
7990
},
8091
}
81-
result, _ = conditionEvaluator.Evaluate(condition, condTreeParams)
82-
assert.Equal(t, result, false)
92+
result, _ = s.conditionEvaluator.Evaluate(condition, condTreeParams)
93+
s.Equal(result, false)
94+
}
95+
96+
func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorWithInvalidMatchType() {
97+
condition := entities.Condition{
98+
Value: "foo",
99+
Name: "string_foo",
100+
Type: "custom_attribute",
101+
Match: "invalid",
102+
}
103+
104+
// Test condition fails
105+
user := entities.UserContext{
106+
Attributes: map[string]interface{}{
107+
"string_foo": "foo",
108+
},
109+
}
110+
111+
condTreeParams := entities.NewTreeParameters(&user, map[string]entities.Audience{})
112+
s.mockLogger.On("Warning", fmt.Sprintf(logging.UnknownMatchType.String(), ""))
113+
result, _ := s.conditionEvaluator.Evaluate(condition, condTreeParams)
114+
s.Equal(result, false)
115+
s.mockLogger.AssertExpectations(s.T())
116+
}
117+
118+
func (s *ConditionTestSuite) TestCustomAttributeConditionEvaluatorWithUnknownType() {
119+
condition := entities.Condition{
120+
Value: "foo",
121+
Name: "string_foo",
122+
Type: "",
123+
}
124+
125+
// Test condition fails
126+
user := entities.UserContext{
127+
Attributes: map[string]interface{}{
128+
"string_foo": "foo",
129+
},
130+
}
131+
132+
condTreeParams := entities.NewTreeParameters(&user, map[string]entities.Audience{})
133+
s.mockLogger.On("Warning", fmt.Sprintf(logging.UnknownConditionType.String(), ""))
134+
result, _ := s.conditionEvaluator.Evaluate(condition, condTreeParams)
135+
s.Equal(result, false)
136+
s.mockLogger.AssertExpectations(s.T())
137+
}
138+
139+
func TestConditionTestSuite(t *testing.T) {
140+
suite.Run(t, new(ConditionTestSuite))
83141
}

pkg/decision/evaluator/condition_tree.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/****************************************************************************
2-
* Copyright 2019, Optimizely, Inc. and contributors *
2+
* Copyright 2019-2020, Optimizely, Inc. and contributors *
33
* *
44
* Licensed under the Apache License, Version 2.0 (the "License"); *
55
* you may not use this file except in compliance with the License. *
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222

2323
"github.com/optimizely/go-sdk/pkg/entities"
24+
"github.com/optimizely/go-sdk/pkg/logging"
2425
)
2526

2627
const customAttributeType = "custom_attribute"
@@ -41,11 +42,12 @@ type TreeEvaluator interface {
4142

4243
// MixedTreeEvaluator evaluates a tree of mixed node types (condition node or audience nodes)
4344
type MixedTreeEvaluator struct {
45+
logger logging.OptimizelyLogProducer
4446
}
4547

4648
// NewMixedTreeEvaluator creates a condition tree evaluator with the out-of-the-box condition evaluators
47-
func NewMixedTreeEvaluator() *MixedTreeEvaluator {
48-
return &MixedTreeEvaluator{}
49+
func NewMixedTreeEvaluator(logger logging.OptimizelyLogProducer) *MixedTreeEvaluator {
50+
return &MixedTreeEvaluator{logger: logger}
4951
}
5052

5153
// Evaluate returns whether the userAttributes satisfy the given condition tree and the evaluation of the condition is valid or not (to handle null bubbling)
@@ -66,10 +68,10 @@ func (c MixedTreeEvaluator) Evaluate(node *entities.TreeNode, condTreeParams *en
6668
var err error
6769
switch v := node.Item.(type) {
6870
case entities.Condition:
69-
evaluator := CustomAttributeConditionEvaluator{}
71+
evaluator := NewCustomAttributeConditionEvaluator(c.logger)
7072
result, err = evaluator.Evaluate(node.Item.(entities.Condition), condTreeParams)
7173
case string:
72-
evaluator := AudienceConditionEvaluator{}
74+
evaluator := NewAudienceConditionEvaluator(c.logger)
7375
result, err = evaluator.Evaluate(node.Item.(string), condTreeParams)
7476
default:
7577
fmt.Printf("I don't know about type %T!\n", v)

0 commit comments

Comments
 (0)