Skip to content
This repository was archived by the owner on Apr 12, 2020. It is now read-only.

Commit eb23bd3

Browse files
committed
added has and does_not_have operators
has and does_not_have operators test whether a property value that's a list of values contains or does not contain a specific value
1 parent cb421da commit eb23bd3

File tree

3 files changed

+53
-0
lines changed

3 files changed

+53
-0
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ These are the types of values that should be used with together. The "Rule Valu
5555
| string | `not_in` — _not one of a group_ | []string |
5656
| bool | `eq` — _equal to_ | bool |
5757
| bool | `not_eq` — _not equal to_ | bool |
58+
| []string | `has` — _includes item_ | string |
59+
| []string | `does_not_have` — _doesn't include item_ | string
5860

5961
#### Nested Example
6062

isit.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ func ruleTest(rule Rule, values map[string]interface{}) (bool, error) {
113113

114114
switch t := actual.(type) {
115115
default:
116+
if isSlice(actual) {
117+
if ss, err := toStringSlice(actual); err == nil {
118+
return ruleTestStringSlice(ss, rule)
119+
}
120+
}
116121
return false, fmt.Errorf("unexpected type %T in rule value", t)
117122
case bool:
118123
v, _ := values[rule.Property].(bool)
@@ -124,7 +129,21 @@ func ruleTest(rule Rule, values map[string]interface{}) (bool, error) {
124129
v, _ := floatFromInterface(values[rule.Property])
125130
return ruleTestNumeric(actual, v, rule)
126131
}
132+
}
127133

134+
func ruleTestStringSlice(ss []string, rule Rule) (bool, error) {
135+
expected, ok := rule.Value.(string)
136+
if !ok {
137+
return false, fmt.Errorf("Invalid rule value type (%T) for property type []string", expected)
138+
}
139+
switch strings.ToUpper(rule.Operator) {
140+
default:
141+
return false, fmt.Errorf("unsupported operator: %s for type []string", rule.Operator)
142+
case "HAS":
143+
return stringSliceContains(ss, expected), nil
144+
case "DOES_NOT_HAVE":
145+
return !stringSliceContains(ss, expected), nil
146+
}
128147
}
129148

130149
func ruleTestNumeric(actual interface{}, v float64, rule Rule) (bool, error) {
@@ -285,3 +304,12 @@ func toStringSlice(o interface{}) ([]string, error) {
285304

286305
return ret, nil
287306
}
307+
308+
func stringSliceContains(ss []string, s string) bool {
309+
for _, v := range ss {
310+
if s == v {
311+
return true
312+
}
313+
}
314+
return false
315+
}

isit_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,29 @@ func Test_ruleTest_numeric(t *testing.T) {
154154
}
155155
}
156156

157+
func Test_ruleTest_stringSlice(t *testing.T) {
158+
rule := Rule{
159+
Property: "v",
160+
Operator: "has",
161+
Value: "oranges",
162+
}
163+
if res, err := ruleTest(rule, map[string]interface{}{"v": []string{"a", "b", "oranges"}}); !res || err != nil {
164+
t.Errorf(`testing ["a", "b", "oranges"] has "oranges" returned %v %v`, res, err)
165+
}
166+
if res, err := ruleTest(rule, map[string]interface{}{"v": []string{"a", "b", "c"}}); res || err != nil {
167+
t.Errorf(`testing ["a", "b", "c"] has "oranges" returned %v %v`, res, err)
168+
}
169+
170+
rule.Operator = "does_not_have"
171+
if res, err := ruleTest(rule, map[string]interface{}{"v": []string{"a", "b", "oranges"}}); res || err != nil {
172+
t.Errorf(`testing ["a", "b", "oranges"] does_not_have "oranges" returned %v %v`, res, err)
173+
}
174+
if res, err := ruleTest(rule, map[string]interface{}{"v": []string{"a", "b", "c"}}); !res || err != nil {
175+
t.Errorf(`testing ["a", "b", "c"] does_not_have "oranges" returned %v %v`, res, err)
176+
}
177+
178+
}
179+
157180
func Test_ruleTest_string(t *testing.T) {
158181
rule := Rule{
159182
Property: "v",

0 commit comments

Comments
 (0)