Skip to content

Commit 6442cf1

Browse files
pawels-optimizelyMichael Ng
authored andcommitted
feat(evaluator): added lt, gt, exists matchers (#33)
1 parent 1c6f2f2 commit 6442cf1

File tree

6 files changed

+515
-0
lines changed

6 files changed

+515
-0
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/****************************************************************************
2+
* Copyright 2019, Optimizely, Inc. and contributors *
3+
* *
4+
* Licensed under the Apache License, Version 2.0 (the "License"); *
5+
* you may not use this file except in compliance with the License. *
6+
* You may obtain a copy of the License at *
7+
* *
8+
* http://www.apache.org/licenses/LICENSE-2.0 *
9+
* *
10+
* Unless required by applicable law or agreed to in writing, software *
11+
* distributed under the License is distributed on an "AS IS" BASIS, *
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
* See the License for the specific language governing permissions and *
14+
* limitations under the License. *
15+
***************************************************************************/
16+
17+
package matchers
18+
19+
import (
20+
"github.com/optimizely/go-sdk/optimizely/entities"
21+
)
22+
23+
// ExistsMatcher matches against the "exists" match type
24+
type ExistsMatcher struct {
25+
Condition entities.Condition
26+
}
27+
28+
// Match returns true if the user's attribute is in the condition
29+
func (m ExistsMatcher) Match(user entities.UserContext) (bool, error) {
30+
31+
_, err := user.Attributes.GetString(m.Condition.Name)
32+
if err != nil {
33+
return false, nil
34+
}
35+
36+
return true, nil
37+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/****************************************************************************
2+
* Copyright 2019, Optimizely, Inc. and contributors *
3+
* *
4+
* Licensed under the Apache License, Version 2.0 (the "License"); *
5+
* you may not use this file except in compliance with the License. *
6+
* You may obtain a copy of the License at *
7+
* *
8+
* http://www.apache.org/licenses/LICENSE-2.0 *
9+
* *
10+
* Unless required by applicable law or agreed to in writing, software *
11+
* distributed under the License is distributed on an "AS IS" BASIS, *
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
* See the License for the specific language governing permissions and *
14+
* limitations under the License. *
15+
***************************************************************************/
16+
17+
package matchers
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
24+
"github.com/optimizely/go-sdk/optimizely/entities"
25+
)
26+
27+
func TestExistsMatcher(t *testing.T) {
28+
matcher := ExistsMatcher{
29+
Condition: entities.Condition{
30+
Match: "exists",
31+
Name: "string_foo",
32+
},
33+
}
34+
35+
// Test match
36+
user := entities.UserContext{
37+
Attributes: entities.UserAttributes{
38+
Attributes: map[string]interface{}{
39+
"string_foo": "any_value",
40+
},
41+
},
42+
}
43+
result, err := matcher.Match(user)
44+
assert.NoError(t, err)
45+
assert.True(t, result)
46+
47+
// Test no match
48+
user = entities.UserContext{
49+
Attributes: entities.UserAttributes{
50+
Attributes: map[string]interface{}{
51+
"string_foo1": "not_foo",
52+
},
53+
},
54+
}
55+
56+
result, err = matcher.Match(user)
57+
assert.NoError(t, err)
58+
assert.False(t, result)
59+
60+
// Test null case
61+
user = entities.UserContext{
62+
Attributes: entities.UserAttributes{
63+
Attributes: map[string]interface{}{},
64+
},
65+
}
66+
result, err = matcher.Match(user)
67+
assert.NoError(t, err)
68+
assert.False(t, result)
69+
}
70+
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/****************************************************************************
2+
* Copyright 2019, Optimizely, Inc. and contributors *
3+
* *
4+
* Licensed under the Apache License, Version 2.0 (the "License"); *
5+
* you may not use this file except in compliance with the License. *
6+
* You may obtain a copy of the License at *
7+
* *
8+
* http://www.apache.org/licenses/LICENSE-2.0 *
9+
* *
10+
* Unless required by applicable law or agreed to in writing, software *
11+
* distributed under the License is distributed on an "AS IS" BASIS, *
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
* See the License for the specific language governing permissions and *
14+
* limitations under the License. *
15+
***************************************************************************/
16+
17+
package matchers
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/optimizely/go-sdk/optimizely/decision/evaluator/matchers/utils"
23+
"github.com/optimizely/go-sdk/optimizely/entities"
24+
)
25+
26+
// GtMatcher matches against the "gt" match type
27+
type GtMatcher struct {
28+
Condition entities.Condition
29+
}
30+
31+
// Match returns true if the user's attribute is greater than the condition's string value
32+
func (m GtMatcher) Match(user entities.UserContext) (bool, error) {
33+
34+
if floatValue, ok := utils.ToFloat(m.Condition.Value); ok {
35+
attributeValue, err := user.Attributes.GetFloat(m.Condition.Name)
36+
if err != nil {
37+
return false, err
38+
}
39+
return floatValue < attributeValue, nil
40+
}
41+
42+
return false, fmt.Errorf("audience condition %s evaluated to NULL because the condition value type is not supported", m.Condition.Name)
43+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
/****************************************************************************
2+
* Copyright 2019, Optimizely, Inc. and contributors *
3+
* *
4+
* Licensed under the Apache License, Version 2.0 (the "License"); *
5+
* you may not use this file except in compliance with the License. *
6+
* You may obtain a copy of the License at *
7+
* *
8+
* http://www.apache.org/licenses/LICENSE-2.0 *
9+
* *
10+
* Unless required by applicable law or agreed to in writing, software *
11+
* distributed under the License is distributed on an "AS IS" BASIS, *
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
* See the License for the specific language governing permissions and *
14+
* limitations under the License. *
15+
***************************************************************************/
16+
17+
package matchers
18+
19+
import (
20+
"testing"
21+
22+
"github.com/stretchr/testify/assert"
23+
24+
"github.com/optimizely/go-sdk/optimizely/entities"
25+
)
26+
27+
28+
29+
30+
func TestGtMatcherInt(t *testing.T) {
31+
matcher := GtMatcher{
32+
Condition: entities.Condition{
33+
Match: "gt",
34+
Value: 42,
35+
Name: "int_42",
36+
},
37+
}
38+
39+
// Test match - same type
40+
user := entities.UserContext{
41+
Attributes: entities.UserAttributes{
42+
Attributes: map[string]interface{}{
43+
"int_42": 43,
44+
},
45+
},
46+
}
47+
result, err := matcher.Match(user)
48+
assert.NoError(t, err)
49+
assert.True(t, result)
50+
51+
// Test match int to float
52+
user = entities.UserContext{
53+
Attributes: entities.UserAttributes{
54+
Attributes: map[string]interface{}{
55+
"int_42": 42.9999,
56+
},
57+
},
58+
}
59+
60+
result, err = matcher.Match(user)
61+
assert.NoError(t, err)
62+
assert.True(t, result)
63+
64+
// Test no match
65+
user = entities.UserContext{
66+
Attributes: entities.UserAttributes{
67+
Attributes: map[string]interface{}{
68+
"int_42": 42.00000,
69+
},
70+
},
71+
}
72+
73+
result, err = matcher.Match(user)
74+
assert.NoError(t, err)
75+
assert.False(t, result)
76+
77+
78+
// Test no match
79+
user = entities.UserContext{
80+
Attributes: entities.UserAttributes{
81+
Attributes: map[string]interface{}{
82+
"int_42": 41,
83+
},
84+
},
85+
}
86+
87+
result, err = matcher.Match(user)
88+
assert.NoError(t, err)
89+
assert.False(t, result)
90+
91+
// Test error case
92+
user = entities.UserContext{
93+
Attributes: entities.UserAttributes{
94+
Attributes: map[string]interface{}{
95+
"int_43": 42,
96+
},
97+
},
98+
}
99+
100+
_, err = matcher.Match(user)
101+
assert.Error(t, err)
102+
}
103+
104+
func TestGtMatcherFloat(t *testing.T) {
105+
matcher := GtMatcher{
106+
Condition: entities.Condition{
107+
Match: "gt",
108+
Value: 4.2,
109+
Name: "float_4_2",
110+
},
111+
}
112+
113+
// Test match float to int
114+
user := entities.UserContext{
115+
Attributes: entities.UserAttributes{
116+
Attributes: map[string]interface{}{
117+
"float_4_2": 5,
118+
},
119+
},
120+
}
121+
result, err := matcher.Match(user)
122+
assert.NoError(t, err)
123+
assert.True(t, result)
124+
125+
// Test match
126+
user = entities.UserContext{
127+
Attributes: entities.UserAttributes{
128+
Attributes: map[string]interface{}{
129+
"float_4_2": 4.29999,
130+
},
131+
},
132+
}
133+
result, err = matcher.Match(user)
134+
assert.NoError(t, err)
135+
assert.True(t, result)
136+
137+
// Test no match
138+
user = entities.UserContext{
139+
Attributes: entities.UserAttributes{
140+
Attributes: map[string]interface{}{
141+
"float_4_2": 4.2,
142+
},
143+
},
144+
}
145+
146+
result, err = matcher.Match(user)
147+
assert.NoError(t, err)
148+
assert.False(t, result)
149+
150+
// Test error case
151+
user = entities.UserContext{
152+
Attributes: entities.UserAttributes{
153+
Attributes: map[string]interface{}{
154+
"float_4_3": 4.2,
155+
},
156+
},
157+
}
158+
159+
_, err = matcher.Match(user)
160+
assert.Error(t, err)
161+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/****************************************************************************
2+
* Copyright 2019, Optimizely, Inc. and contributors *
3+
* *
4+
* Licensed under the Apache License, Version 2.0 (the "License"); *
5+
* you may not use this file except in compliance with the License. *
6+
* You may obtain a copy of the License at *
7+
* *
8+
* http://www.apache.org/licenses/LICENSE-2.0 *
9+
* *
10+
* Unless required by applicable law or agreed to in writing, software *
11+
* distributed under the License is distributed on an "AS IS" BASIS, *
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
13+
* See the License for the specific language governing permissions and *
14+
* limitations under the License. *
15+
***************************************************************************/
16+
17+
package matchers
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/optimizely/go-sdk/optimizely/decision/evaluator/matchers/utils"
23+
"github.com/optimizely/go-sdk/optimizely/entities"
24+
)
25+
26+
// LtMatcher matches against the "lt" match type
27+
type LtMatcher struct {
28+
Condition entities.Condition
29+
}
30+
31+
// Match returns true if the user's attribute is less than the condition's string value
32+
func (m LtMatcher) Match(user entities.UserContext) (bool, error) {
33+
34+
if floatValue, ok := utils.ToFloat(m.Condition.Value); ok {
35+
attributeValue, err := user.Attributes.GetFloat(m.Condition.Name)
36+
if err != nil {
37+
return false, err
38+
}
39+
return floatValue > attributeValue, nil
40+
}
41+
42+
return false, fmt.Errorf("audience condition %s evaluated to NULL because the condition value type is not supported", m.Condition.Name)
43+
}

0 commit comments

Comments
 (0)