Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add parameter name to attribute checker #2488

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 74 additions & 12 deletions pkg/attribute/attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,60 +52,122 @@ type Description struct {
Type reflect.Type
Default interface{}
Doc string
Checker func(i interface{}) error
Checker func(i interface{}, name string) error
}

// Int declares an attribute of int-typed in Dictionary d.
func (d Dictionary) Int(name string, value int, doc string, checker func(int) error) Dictionary {
func (d Dictionary) Int(name string, value int, doc string, checker func(int, string) error) Dictionary {
if err := checker(value, name); err != nil {
log.Panic(err)
}

d[name] = &Description{
Type: Int,
Default: value,
Doc: doc,
Checker: func(x interface{}) error { return checker(x.(int)) },
Checker: func(x interface{}, attrName string) error {
if v, ok := x.(int); ok {
return checker(v, attrName)
}
return fmt.Errorf("attribute %s should be of type int, but got type %T(%v)", attrName, x, x)
},
}
return d
}

// Float declares an attribute of float32-typed in Dictionary d.
func (d Dictionary) Float(name string, value float32, doc string, checker func(float32) error) Dictionary {
func (d Dictionary) Float(name string, value float32, doc string, checker func(float32, string) error) Dictionary {
if err := checker(value, name); err != nil {
log.Panic(err)
}

d[name] = &Description{
Type: Float,
Default: value,
Doc: doc,
Checker: func(x interface{}) error { return checker(x.(float32)) },
Checker: func(x interface{}, attrName string) error {
if v, ok := x.(float32); ok {
return checker(v, attrName)
}
return fmt.Errorf("attribute %s should be of type float, but got type %T(%v)", attrName, x, x)
},
}
return d
}

// Bool declares an attribute of bool-typed in Dictionary d.
func (d Dictionary) Bool(name string, value bool, doc string, checker func(bool) error) Dictionary {
func (d Dictionary) Bool(name string, value bool, doc string, checker func(bool, string) error) Dictionary {
if err := checker(value, name); err != nil {
log.Panic(err)
}

d[name] = &Description{
Type: Bool,
Default: value,
Doc: doc,
Checker: func(x interface{}) error { return checker(x.(bool)) },
Checker: func(x interface{}, attrName string) error {
if v, ok := x.(bool); ok {
return checker(v, attrName)
}
return fmt.Errorf("attribute %s should be of type bool, but got type %T(%v)", attrName, x, x)
},
}
return d
}

// String declares an attribute of string-typed in Dictionary d.
func (d Dictionary) String(name string, value string, doc string, checker func(string) error) Dictionary {
func (d Dictionary) String(name string, value string, doc string, checker func(string, string) error) Dictionary {
if err := checker(value, name); err != nil {
log.Panic(err)
}

d[name] = &Description{
Type: String,
Default: value,
Doc: doc,
Checker: func(x interface{}) error { return checker(x.(string)) },
Checker: func(x interface{}, attrName string) error {
if v, ok := x.(string); ok {
return checker(v, attrName)
}
return fmt.Errorf("attribute %s should be of type string, but got type %T(%v)", attrName, x, x)
},
}
return d
}

// IntList declares an attribute of []int-typed in Dictionary d.
func (d Dictionary) IntList(name string, value []int, doc string, checker func([]int) error) Dictionary {
func (d Dictionary) IntList(name string, value []int, doc string, checker func([]int, string) error) Dictionary {
if err := checker(value, name); err != nil {
log.Panic(err)
}

d[name] = &Description{
Type: IntList,
Default: value,
Doc: doc,
Checker: func(x interface{}) error { return checker(x.([]int)) },
Checker: func(x interface{}, attrName string) error {
if v, ok := x.([]int); ok {
return checker(v, attrName)
}
return fmt.Errorf("attribute %s should be of type []int, but got type %T(%v)", attrName, x, x)
},
}
return d
}

// Unknown declares an attribute of unknown type
func (d Dictionary) Unknown(name string, value interface{}, doc string, checker func(interface{}, string) error) Dictionary {
if value != nil {
if err := checker(value, name); err != nil {
log.Panic(err)
}
}

d[name] = &Description{
Type: Unknown,
Default: value,
Doc: doc,
Checker: checker,
}
return d
}
Expand Down Expand Up @@ -159,7 +221,7 @@ func (d Dictionary) Validate(attrs map[string]interface{}) error {
}

if desc.Checker != nil {
if err := desc.Checker(v); err != nil {
if err := desc.Checker(v, k); err != nil {
return err
}
}
Expand Down
89 changes: 87 additions & 2 deletions pkg/attribute/attribute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package attribute
import (
"fmt"
"reflect"
"strings"
"testing"

"github.com/stretchr/testify/assert"
Expand All @@ -24,10 +25,10 @@ import (
func TestDictionaryValidate(t *testing.T) {
a := assert.New(t)

checker := func(i interface{}) error {
checker := func(i interface{}, name string) error {
ii, ok := i.(int)
if !ok {
return fmt.Errorf("%T %v should of type integer", i, i)
return fmt.Errorf("attribute %s %T %v should of type integer", name, i, i)
}
if ii < 0 {
return fmt.Errorf("some error")
Expand All @@ -43,6 +44,90 @@ func TestDictionaryValidate(t *testing.T) {
a.NoError(tb.Validate(map[string]interface{}{"b": 1}))
}

func TestDictionaryChecker(t *testing.T) {
a := assert.New(t)
var tb Dictionary
var err error

boolChecker := func(b bool, name string) error {
if !b {
return fmt.Errorf("attribute %s must be true", name)
}
return nil
}
tb = Dictionary{}.Bool("a", true, "attr a", boolChecker)
err = tb.Validate(map[string]interface{}{"a": false})
a.Error(err)
a.True(strings.HasPrefix(err.Error(), "attribute a"))
a.NoError(tb.Validate(map[string]interface{}{"a": true}))

intChecker := func(i int, name string) error {
if i == 3 {
return fmt.Errorf("attribute %s cannot be 3", name)
}
return nil
}
tb = Dictionary{}.Int("b", 2, "attr b", intChecker)
err = tb.Validate(map[string]interface{}{"b": 3})
a.Error(err)
a.True(strings.HasPrefix(err.Error(), "attribute b"))
a.NoError(tb.Validate(map[string]interface{}{"b": 1}))

floatChecker := func(f float32, name string) error {
if f >= 0 && f <= 7 {
return fmt.Errorf("attribute %s must not be between [0, 7]", name)
}
return nil
}
tb = Dictionary{}.Float("c", float32(-10.0), "attr c", floatChecker)
err = tb.Validate(map[string]interface{}{"c": 5})
a.Error(err)
a.True(strings.HasPrefix(err.Error(), "attribute c"))
a.NoError(tb.Validate(map[string]interface{}{"c": float32(-1.0)}))

stringChecker := func(s string, name string) error {
if !strings.HasPrefix(s, "valid") {
return fmt.Errorf("attribute %s must have prefix valid", name)
}
return nil
}
tb = Dictionary{}.String("d", "valid.any_str", "attr d", stringChecker)
err = tb.Validate(map[string]interface{}{"d": "invalid.str"})
a.Error(err)
a.True(strings.HasPrefix(err.Error(), "attribute d"))
a.NoError(tb.Validate(map[string]interface{}{"d": "valid.str"}))

intListChecker := func(intList []int, name string) error {
if len(intList) != 2 {
return fmt.Errorf("attribute %s must have length of 2", name)
}
return nil
}
tb = Dictionary{}.IntList("e", []int{1, 3}, "attr e", intListChecker)
err = tb.Validate(map[string]interface{}{"e": []int{1, 2, 3}})
a.Error(err)
a.True(strings.HasPrefix(err.Error(), "attribute e"))
a.NoError(tb.Validate(map[string]interface{}{"e": []int{4, 5}}))

unknownChecker := func(i interface{}, name string) error {
switch i.(type) {
case int:
return nil
case string:
return nil
default:
return fmt.Errorf("attribute %s must be of type int or string", name)
}
}
tb = Dictionary{}.Unknown("f", "abc", "attr f", unknownChecker)
err = tb.Validate(map[string]interface{}{"f": float32(4.5)})
a.Error(err)
a.True(strings.HasPrefix(err.Error(), "attribute f"))
a.NoError(tb.Validate(map[string]interface{}{"f": 3}))
a.NoError(tb.Validate(map[string]interface{}{"f": "any_str"}))

}

func TestParamsDocs(t *testing.T) {
a := assert.New(t)

Expand Down
Loading