Skip to content

Commit 13a0824

Browse files
YuanCMichael Ng
authored andcommitted
feat(decision): Add Substring Matcher (#55)
1 parent 94b8496 commit 13a0824

File tree

3 files changed

+119
-4
lines changed

3 files changed

+119
-4
lines changed

optimizely/decision/evaluator/condition.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@ import (
2424
)
2525

2626
const (
27-
exactMatchType = "exact"
28-
existsMatchType = "exists"
29-
ltMatchType = "lt"
30-
gtMatchType = "gt"
27+
exactMatchType = "exact"
28+
existsMatchType = "exists"
29+
ltMatchType = "lt"
30+
gtMatchType = "gt"
31+
substringMatchType = "substring"
3132
)
3233

3334
// ItemEvaluator evaluates a condition against the given user's attributes
@@ -68,6 +69,10 @@ func (c CustomAttributeConditionEvaluator) Evaluate(condition entities.Condition
6869
matcher = matchers.GtMatcher{
6970
Condition: condition,
7071
}
72+
case substringMatchType:
73+
matcher = matchers.SubstringMatcher{
74+
Condition: condition,
75+
}
7176
default:
7277
return false, fmt.Errorf(`Invalid Condition matcher "%s"`, condition.Match)
7378
}
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+
"strings"
22+
23+
"github.com/optimizely/go-sdk/optimizely/entities"
24+
)
25+
26+
// SubstringMatcher matches against the "substring" match type
27+
type SubstringMatcher struct {
28+
Condition entities.Condition
29+
}
30+
31+
// Match returns true if the user's attribute is a substring of the condition's string value
32+
func (m SubstringMatcher) Match(user entities.UserContext) (bool, error) {
33+
34+
if stringValue, ok := m.Condition.Value.(string); ok {
35+
attributeValue, err := user.GetStringAttribute(m.Condition.Name)
36+
if err != nil {
37+
return false, err
38+
}
39+
return strings.Contains(stringValue, 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: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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 TestSubstringMatcher(t *testing.T) {
28+
matcher := SubstringMatcher{
29+
Condition: entities.Condition{
30+
Match: "substring",
31+
Value: "foobar",
32+
Name: "string_foo",
33+
},
34+
}
35+
36+
// Test match
37+
user := entities.UserContext{
38+
Attributes: map[string]interface{}{
39+
"string_foo": "foo",
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: map[string]interface{}{
50+
"string_foo": "not_foobar",
51+
},
52+
}
53+
54+
result, err = matcher.Match(user)
55+
assert.NoError(t, err)
56+
assert.False(t, result)
57+
58+
// Test error case
59+
user = entities.UserContext{
60+
Attributes: map[string]interface{}{
61+
"not_string_foo": "foo",
62+
},
63+
}
64+
65+
_, err = matcher.Match(user)
66+
assert.Error(t, err)
67+
}

0 commit comments

Comments
 (0)