Skip to content

Commit ca35c47

Browse files
author
Mike Davis
authored
Add MatchRegistry for custom match implementations. (#286)
* Adds MatcherRegistry to store name to Matcher implementation * Refactor Matcher into an aliased type as opposed to an interface * Add condition to the function signature
1 parent 9010b01 commit ca35c47

File tree

14 files changed

+214
-185
lines changed

14 files changed

+214
-185
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ GOBUILD=$(GOCMD) build
1010
GOCLEAN=$(GOCMD) clean
1111
GOTEST=$(GOCMD) test
1212
GOGET=$(GOCMD) get
13-
GOLINT=golangci-lint
13+
GOLINT=$(GOPATH)/bin/golangci-lint
1414

1515
# Make is verbose in Linux. Make it silent.
1616
MAKEFLAGS += --silent

pkg/decision/evaluator/condition.go

Lines changed: 5 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,6 @@ import (
2424
"github.com/optimizely/go-sdk/pkg/entities"
2525
)
2626

27-
const (
28-
exactMatchType = "exact"
29-
existsMatchType = "exists"
30-
ltMatchType = "lt"
31-
gtMatchType = "gt"
32-
substringMatchType = "substring"
33-
)
34-
3527
// ItemEvaluator evaluates a condition against the given user's attributes
3628
type ItemEvaluator interface {
3729
Evaluate(interface{}, *entities.TreeParameters) (bool, error)
@@ -48,39 +40,17 @@ func (c CustomAttributeConditionEvaluator) Evaluate(condition entities.Condition
4840
return false, fmt.Errorf(`unable to evaluator condition of type "%s"`, condition.Type)
4941
}
5042

51-
var matcher matchers.Matcher
5243
matchType := condition.Match
5344
if matchType == "" {
54-
matchType = exactMatchType
45+
matchType = matchers.ExactMatchType
5546
}
56-
switch matchType {
57-
case exactMatchType:
58-
matcher = matchers.ExactMatcher{
59-
Condition: condition,
60-
}
61-
case existsMatchType:
62-
matcher = matchers.ExistsMatcher{
63-
Condition: condition,
64-
}
65-
case ltMatchType:
66-
matcher = matchers.LtMatcher{
67-
Condition: condition,
68-
}
69-
case gtMatchType:
70-
matcher = matchers.GtMatcher{
71-
Condition: condition,
72-
}
73-
case substringMatchType:
74-
matcher = matchers.SubstringMatcher{
75-
Condition: condition,
76-
}
77-
default:
47+
48+
matcher, ok := matchers.Get(matchType)
49+
if !ok {
7850
return false, fmt.Errorf(`invalid Condition matcher "%s"`, condition.Match)
7951
}
8052

81-
user := *condTreeParams.User
82-
result, err := matcher.Match(user)
83-
return result, err
53+
return matcher(condition, *condTreeParams.User)
8454
}
8555

8656
// AudienceConditionEvaluator evaluates conditions with audience condition

pkg/decision/evaluator/matchers/exact.go

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,35 +25,30 @@ import (
2525
)
2626

2727
// ExactMatcher matches against the "exact" match type
28-
type ExactMatcher struct {
29-
Condition entities.Condition
30-
}
31-
32-
// Match returns true if the user's attribute match the condition's string value
33-
func (m ExactMatcher) Match(user entities.UserContext) (bool, error) {
34-
if stringValue, ok := m.Condition.Value.(string); ok {
35-
attributeValue, err := user.GetStringAttribute(m.Condition.Name)
28+
func ExactMatcher(condition entities.Condition, user entities.UserContext) (bool, error) {
29+
if stringValue, ok := condition.Value.(string); ok {
30+
attributeValue, err := user.GetStringAttribute(condition.Name)
3631
if err != nil {
3732
return false, err
3833
}
3934
return stringValue == attributeValue, nil
4035
}
4136

42-
if boolValue, ok := m.Condition.Value.(bool); ok {
43-
attributeValue, err := user.GetBoolAttribute(m.Condition.Name)
37+
if boolValue, ok := condition.Value.(bool); ok {
38+
attributeValue, err := user.GetBoolAttribute(condition.Name)
4439
if err != nil {
4540
return false, err
4641
}
4742
return boolValue == attributeValue, nil
4843
}
4944

50-
if floatValue, ok := utils.ToFloat(m.Condition.Value); ok {
51-
attributeValue, err := user.GetFloatAttribute(m.Condition.Name)
45+
if floatValue, ok := utils.ToFloat(condition.Value); ok {
46+
attributeValue, err := user.GetFloatAttribute(condition.Name)
5247
if err != nil {
5348
return false, err
5449
}
5550
return floatValue == attributeValue, nil
5651
}
5752

58-
return false, fmt.Errorf("audience condition %s evaluated to NULL because the condition value type is not supported", m.Condition.Name)
53+
return false, fmt.Errorf("audience condition %s evaluated to NULL because the condition value type is not supported", condition.Name)
5954
}

pkg/decision/evaluator/matchers/exact_test.go

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ import (
2424
"github.com/optimizely/go-sdk/pkg/entities"
2525
)
2626

27+
var exactMatcher, _ = Get(ExactMatchType)
28+
2729
func TestExactMatcherString(t *testing.T) {
28-
matcher := ExactMatcher{
29-
Condition: entities.Condition{
30-
Match: "exact",
31-
Value: "foo",
32-
Name: "string_foo",
33-
},
30+
condition := entities.Condition{
31+
Match: "exact",
32+
Value: "foo",
33+
Name: "string_foo",
3434
}
3535

3636
// Test match
@@ -39,7 +39,7 @@ func TestExactMatcherString(t *testing.T) {
3939
"string_foo": "foo",
4040
},
4141
}
42-
result, err := matcher.Match(user)
42+
result, err := exactMatcher(condition, user)
4343
assert.NoError(t, err)
4444
assert.True(t, result)
4545

@@ -50,7 +50,7 @@ func TestExactMatcherString(t *testing.T) {
5050
},
5151
}
5252

53-
result, err = matcher.Match(user)
53+
result, err = exactMatcher(condition, user)
5454
assert.NoError(t, err)
5555
assert.False(t, result)
5656

@@ -61,17 +61,15 @@ func TestExactMatcherString(t *testing.T) {
6161
},
6262
}
6363

64-
_, err = matcher.Match(user)
64+
_, err = exactMatcher(condition, user)
6565
assert.Error(t, err)
6666
}
6767

6868
func TestExactMatcherBool(t *testing.T) {
69-
matcher := ExactMatcher{
70-
Condition: entities.Condition{
71-
Match: "exact",
72-
Value: true,
73-
Name: "bool_true",
74-
},
69+
condition := entities.Condition{
70+
Match: "exact",
71+
Value: true,
72+
Name: "bool_true",
7573
}
7674

7775
// Test match
@@ -80,7 +78,7 @@ func TestExactMatcherBool(t *testing.T) {
8078
"bool_true": true,
8179
},
8280
}
83-
result, err := matcher.Match(user)
81+
result, err := exactMatcher(condition, user)
8482
assert.NoError(t, err)
8583
assert.True(t, result)
8684

@@ -91,7 +89,7 @@ func TestExactMatcherBool(t *testing.T) {
9189
},
9290
}
9391

94-
result, err = matcher.Match(user)
92+
result, err = exactMatcher(condition, user)
9593
assert.NoError(t, err)
9694
assert.False(t, result)
9795

@@ -102,17 +100,15 @@ func TestExactMatcherBool(t *testing.T) {
102100
},
103101
}
104102

105-
_, err = matcher.Match(user)
103+
_, err = exactMatcher(condition, user)
106104
assert.Error(t, err)
107105
}
108106

109107
func TestExactMatcherInt(t *testing.T) {
110-
matcher := ExactMatcher{
111-
Condition: entities.Condition{
112-
Match: "exact",
113-
Value: 42,
114-
Name: "int_42",
115-
},
108+
condition := entities.Condition{
109+
Match: "exact",
110+
Value: 42,
111+
Name: "int_42",
116112
}
117113

118114
// Test match - same type
@@ -121,7 +117,7 @@ func TestExactMatcherInt(t *testing.T) {
121117
"int_42": 42,
122118
},
123119
}
124-
result, err := matcher.Match(user)
120+
result, err := exactMatcher(condition, user)
125121
assert.NoError(t, err)
126122
assert.True(t, result)
127123

@@ -132,7 +128,7 @@ func TestExactMatcherInt(t *testing.T) {
132128
},
133129
}
134130

135-
result, err = matcher.Match(user)
131+
result, err = exactMatcher(condition, user)
136132
assert.NoError(t, err)
137133
assert.True(t, result)
138134

@@ -143,7 +139,7 @@ func TestExactMatcherInt(t *testing.T) {
143139
},
144140
}
145141

146-
result, err = matcher.Match(user)
142+
result, err = exactMatcher(condition, user)
147143
assert.NoError(t, err)
148144
assert.False(t, result)
149145

@@ -154,17 +150,16 @@ func TestExactMatcherInt(t *testing.T) {
154150
},
155151
}
156152

157-
_, err = matcher.Match(user)
153+
_, err = exactMatcher(condition, user)
158154
assert.Error(t, err)
159155
}
160156

161157
func TestExactMatcherFloat(t *testing.T) {
162-
matcher := ExactMatcher{
163-
Condition: entities.Condition{
164-
Match: "exact",
165-
Value: 4.2,
166-
Name: "float_4_2",
167-
},
158+
matcher, _ := Get(ExactMatchType)
159+
condition := entities.Condition{
160+
Match: "exact",
161+
Value: 4.2,
162+
Name: "float_4_2",
168163
}
169164

170165
// Test match
@@ -173,7 +168,7 @@ func TestExactMatcherFloat(t *testing.T) {
173168
"float_4_2": 4.2,
174169
},
175170
}
176-
result, err := matcher.Match(user)
171+
result, err := matcher(condition, user)
177172
assert.NoError(t, err)
178173
assert.True(t, result)
179174

@@ -184,7 +179,7 @@ func TestExactMatcherFloat(t *testing.T) {
184179
},
185180
}
186181

187-
result, err = matcher.Match(user)
182+
result, err = matcher(condition, user)
188183
assert.NoError(t, err)
189184
assert.False(t, result)
190185

@@ -195,6 +190,6 @@ func TestExactMatcherFloat(t *testing.T) {
195190
},
196191
}
197192

198-
_, err = matcher.Match(user)
193+
_, err = matcher(condition, user)
199194
assert.Error(t, err)
200195
}

pkg/decision/evaluator/matchers/exists.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,6 @@ import (
2222
)
2323

2424
// ExistsMatcher matches against the "exists" match type
25-
type ExistsMatcher struct {
26-
Condition entities.Condition
27-
}
28-
29-
// Match returns true if the user's attribute is in the condition
30-
func (m ExistsMatcher) Match(user entities.UserContext) (bool, error) {
31-
32-
return user.CheckAttributeExists(m.Condition.Name), nil
25+
func ExistsMatcher(condition entities.Condition, user entities.UserContext) (bool, error) {
26+
return user.CheckAttributeExists(condition.Name), nil
3327
}

pkg/decision/evaluator/matchers/exists_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ import (
2424
"github.com/optimizely/go-sdk/pkg/entities"
2525
)
2626

27+
var existsMatcher, _ = Get(ExistsMatchType)
28+
2729
func TestExistsMatcher(t *testing.T) {
28-
matcher := ExistsMatcher{
29-
Condition: entities.Condition{
30-
Match: "exists",
31-
Name: "string_foo",
32-
},
30+
condition := entities.Condition{
31+
Match: "exists",
32+
Name: "string_foo",
3333
}
3434

3535
// Test match
@@ -38,7 +38,7 @@ func TestExistsMatcher(t *testing.T) {
3838
"string_foo": "any_value",
3939
},
4040
}
41-
result, err := matcher.Match(user)
41+
result, err := existsMatcher(condition, user)
4242
assert.NoError(t, err)
4343
assert.True(t, result)
4444

@@ -49,15 +49,15 @@ func TestExistsMatcher(t *testing.T) {
4949
},
5050
}
5151

52-
result, err = matcher.Match(user)
52+
result, err = existsMatcher(condition, user)
5353
assert.NoError(t, err)
5454
assert.False(t, result)
5555

5656
// Test null case
5757
user = entities.UserContext{
5858
Attributes: map[string]interface{}{},
5959
}
60-
result, err = matcher.Match(user)
60+
result, err = ExistsMatcher(condition, user)
6161
assert.NoError(t, err)
6262
assert.False(t, result)
6363
}

pkg/decision/evaluator/matchers/gt.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,15 @@ import (
2525
)
2626

2727
// GtMatcher matches against the "gt" match type
28-
type GtMatcher struct {
29-
Condition entities.Condition
30-
}
31-
32-
// Match returns true if the user's attribute is greater than the condition's string value
33-
func (m GtMatcher) Match(user entities.UserContext) (bool, error) {
28+
func GtMatcher(condition entities.Condition, user entities.UserContext) (bool, error) {
3429

35-
if floatValue, ok := utils.ToFloat(m.Condition.Value); ok {
36-
attributeValue, err := user.GetFloatAttribute(m.Condition.Name)
30+
if floatValue, ok := utils.ToFloat(condition.Value); ok {
31+
attributeValue, err := user.GetFloatAttribute(condition.Name)
3732
if err != nil {
3833
return false, err
3934
}
4035
return floatValue < attributeValue, nil
4136
}
4237

43-
return false, fmt.Errorf("audience condition %s evaluated to NULL because the condition value type is not supported", m.Condition.Name)
38+
return false, fmt.Errorf("audience condition %s evaluated to NULL because the condition value type is not supported", condition.Name)
4439
}

0 commit comments

Comments
 (0)