Skip to content

Commit 9e6ff8f

Browse files
authored
Merge pull request #15 from davejohnston/maint/fixing_panic_on_bad_evaluation
(MAINT) If an operator is nil, it can cause a panic
2 parents 65ae51d + 0dd241e commit 9e6ff8f

File tree

3 files changed

+65
-39
lines changed

3 files changed

+65
-39
lines changed

evaluation/feature.go

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@ package evaluation
33
import (
44
"encoding/json"
55
"fmt"
6-
7-
"github.com/drone/ff-golang-server-sdk/types"
8-
96
"reflect"
107
"strconv"
8+
9+
"github.com/drone/ff-golang-server-sdk/types"
1110
)
1211

1312
const (
@@ -43,26 +42,34 @@ type Clause struct {
4342

4443
// Evaluate clause using target but it can be used also with segments if Op field is segmentMach
4544
func (c *Clause) Evaluate(target *Target, segments Segments, operator types.ValueType) bool {
46-
switch c.Op {
47-
case segmentMatchOperator:
45+
46+
// Special case - segment matcher doesn't require a
47+
// valid operator.
48+
if c.Op == segmentMatchOperator {
4849
if segments == nil {
4950
return false
5051
}
5152
return segments.Evaluate(target)
52-
case inOperator:
53-
return operator.In(c.Value)
54-
case equalOperator:
55-
return operator.Equal(c.Value)
56-
case gtOperator:
57-
return operator.GreaterThan(c.Value)
58-
case startsWithOperator:
59-
return operator.StartsWith(c.Value)
60-
case endsWithOperator:
61-
return operator.EndsWith(c.Value)
62-
case containsOperator:
63-
return operator.Contains(c.Value)
64-
case equalSensitiveOperator:
65-
return operator.EqualSensitive(c.Value)
53+
}
54+
55+
// Ensure operator is valid and not nil
56+
if operator != nil {
57+
switch c.Op {
58+
case inOperator:
59+
return operator.In(c.Value)
60+
case equalOperator:
61+
return operator.Equal(c.Value)
62+
case gtOperator:
63+
return operator.GreaterThan(c.Value)
64+
case startsWithOperator:
65+
return operator.StartsWith(c.Value)
66+
case endsWithOperator:
67+
return operator.EndsWith(c.Value)
68+
case containsOperator:
69+
return operator.Contains(c.Value)
70+
case equalSensitiveOperator:
71+
return operator.EqualSensitive(c.Value)
72+
}
6673
}
6774
return false
6875
}
@@ -71,11 +78,16 @@ func (c *Clause) Evaluate(target *Target, segments Segments, operator types.Valu
7178
type Clauses []Clause
7279

7380
// Evaluate clauses using target but it can be used also with segments if Op field is segmentMach
81+
// TODO this func can return false because of an error. We need a way to indicate to the caller if this is false
82+
// because it evaluated false, or because it actually failed to work.
7483
func (c Clauses) Evaluate(target *Target, segments Segments) bool {
7584
// AND operation
7685
for _, clause := range c {
7786
// operator should be evaluated based on type of attribute
78-
op := target.GetOperator(clause.Attribute)
87+
op, err := target.GetOperator(clause.Attribute)
88+
if err != nil {
89+
fmt.Print(err)
90+
}
7991
if !clause.Evaluate(target, segments, op) {
8092
return false
8193
}

evaluation/target.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package evaluation
22

33
import (
4+
"fmt"
5+
46
"github.com/drone/ff-golang-server-sdk/types"
57

68
"reflect"
@@ -28,22 +30,22 @@ func (t Target) GetAttrValue(attr string) reflect.Value {
2830
}
2931

3032
// GetOperator returns interface based on attribute value
31-
func (t Target) GetOperator(attr string) types.ValueType {
33+
func (t Target) GetOperator(attr string) (types.ValueType, error) {
3234
value := t.GetAttrValue(attr)
3335
switch value.Kind() {
3436
case reflect.Bool:
35-
return types.Boolean(value.Bool())
37+
return types.Boolean(value.Bool()), nil
3638
case reflect.String:
37-
return types.String(value.String())
39+
return types.String(value.String()), nil
3840
case reflect.Float64, reflect.Float32:
39-
return types.Number(value.Float())
41+
return types.Number(value.Float()), nil
4042
case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Uint, reflect.Uint16,
4143
reflect.Uint32, reflect.Uint64, reflect.Uint8:
42-
return types.Integer(value.Int())
44+
return types.Integer(value.Int()), nil
4345
case reflect.Array, reflect.Chan, reflect.Complex128, reflect.Complex64, reflect.Func, reflect.Interface,
4446
reflect.Invalid, reflect.Map, reflect.Ptr, reflect.Slice, reflect.Struct, reflect.Uintptr, reflect.UnsafePointer:
45-
return nil
47+
fallthrough
4648
default:
47-
return nil
49+
return nil, fmt.Errorf("unexpected type: %s", value.Kind().String())
4850
}
4951
}

evaluation/target_test.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ func TestTarget_GetOperator(t1 *testing.T) {
2121
attr string
2222
}
2323
tests := []struct {
24-
name string
25-
fields fields
26-
args args
27-
want types.ValueType
24+
name string
25+
fields fields
26+
args args
27+
want types.ValueType
28+
wantErr bool
2829
}{
2930
{name: "boolean operator", fields: struct {
3031
Identifier string
@@ -68,8 +69,13 @@ func TestTarget_GetOperator(t1 *testing.T) {
6869
Anonymous: &val.fields.Anonymous,
6970
Attributes: &val.fields.Attributes,
7071
}
71-
if got := t.GetOperator(val.args.attr); !reflect.DeepEqual(got, val.want) {
72-
t1.Errorf("GetOperator() = %v, want %v", got, val.want)
72+
got, err := t.GetOperator(val.args.attr)
73+
if (err != nil) != val.wantErr {
74+
t1.Errorf("GetOperator() error = %v, wantErr %v", err, val.wantErr)
75+
return
76+
}
77+
if !reflect.DeepEqual(got, val.want) {
78+
t1.Errorf("GetOperator() error = %v, want %v", err, val.want)
7379
}
7480
})
7581
}
@@ -142,10 +148,11 @@ func TestTarget_GetOperator1(t1 *testing.T) {
142148

143149
name := "John"
144150
tests := []struct {
145-
name string
146-
fields fields
147-
args args
148-
want types.ValueType
151+
name string
152+
fields fields
153+
args args
154+
want types.ValueType
155+
wantErr bool
149156
}{
150157
{name: "bool operator", fields: struct {
151158
Identifier string
@@ -189,8 +196,13 @@ func TestTarget_GetOperator1(t1 *testing.T) {
189196
Anonymous: &val.fields.Anonymous,
190197
Attributes: &val.fields.Attributes,
191198
}
192-
if got := t.GetOperator(val.args.attr); !reflect.DeepEqual(got, val.want) {
193-
t1.Errorf("GetOperator() = %v, want %v", got, val.want)
199+
got, err := t.GetOperator(val.args.attr)
200+
if (err != nil) != val.wantErr {
201+
t1.Errorf("GetOperator() error = %v, wantErr %v", err, val.wantErr)
202+
return
203+
}
204+
if !reflect.DeepEqual(got, val.want) {
205+
t1.Errorf("GetOperator() error = %v, want %v", err, val.want)
194206
}
195207
})
196208
}

0 commit comments

Comments
 (0)