From a669e890dc132b007da10603fd7661f64d15b4d3 Mon Sep 17 00:00:00 2001 From: Felix Kollmann Date: Tue, 23 Nov 2021 14:43:32 +0100 Subject: [PATCH] Add support for float type --- README.md | 8 ++++++++ filter.go | 36 ++++++++++++++++++++++++++++++++++++ main_test.go | 17 ++++++++++++++++- validation.go | 36 ++++++++++++++++++++++++++++++++++++ validation_test.go | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d48e6bd..f99e41c 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,12 @@ See cmd/main.go and tests for more examples. } return errors.New("i: must be greater then 1 and lower then 10") }, + "f:float": func(value interface{}) error { + if value.(float32) > 1.0 && value.(float32) < 10.0 { + return nil + } + return errors.New("f: must be greater then 1.0 and lower then 10.0") + }, "email": nil, "name": nil, }) @@ -68,11 +74,13 @@ See cmd/main.go and tests for more examples. ## Validation modificators: * `:required` - parameter is required. Must present in the query string. Raise error if not. * `:int` - parameter must be convertable to int type. Raise error if not. +* `:float` - parameter must be convertable to float32 type. Raise error if not. * `:bool` - parameter must be convertable to bool type. Raise error if not. ## Supported types - `string` - the default type for all provided filters if not specified another. Could be compared by `eq, ne, gt, lt, gte, lte, like, ilike, nlike, nilike, in, nin, is, not` methods (`nlike, nilike` means `NOT LIKE, NOT ILIKE` respectively, `in, nin` means `IN, NOT IN` respectively, `is, not` for comparison to NULL `IS NULL, IS NOT NULL`). - `int` - integer type. Must be specified with tag ":int". Could be compared by `eq, ne, gt, lt, gte, lte, in, nin` methods. +- `float` - integer type. Must be specified with tag ":float". Could be compared by `eq, ne, gt, lt, gte, lte, in, nin` methods. However, avoid the `eq, ne, in, nin` methods. - `bool` - boolean type. Must be specified with tag ":bool". Could be compared by `eq` method. ## Date usage diff --git a/filter.go b/filter.go index 5647fea..c9f78d5 100644 --- a/filter.go +++ b/filter.go @@ -53,6 +53,8 @@ func detectType(name string, validations Validations) string { switch split[1] { case "int", "i": return "int" + case "float", "f": + return "float" case "bool", "b": return "bool" default: @@ -181,6 +183,11 @@ func (f *Filter) parseValue(valueType string, value string, delimiter string) er if err != nil { return err } + case "float": + err := f.setFloat(list) + if err != nil { + return err + } case "bool": err := f.setBool(list) if err != nil { @@ -286,6 +293,35 @@ func (f *Filter) setInt(list []string) error { return nil } +func (f *Filter) setFloat(list []string) error { + if len(list) == 1 { + switch f.Method { + case EQ, NE, GT, LT, GTE, LTE, IN, NIN: + i, err := strconv.ParseFloat(list[0], 32) + if err != nil { + return ErrBadFormat + } + f.Value = float32(i) + default: + return ErrMethodNotAllowed + } + } else { + if f.Method != IN && f.Method != NIN { + return ErrMethodNotAllowed + } + floatSlice := make([]float32, len(list)) + for i, s := range list { + v, err := strconv.ParseFloat(s, 32) + if err != nil { + return ErrBadFormat + } + floatSlice[i] = float32(v) + } + f.Value = floatSlice + } + return nil +} + func (f *Filter) setBool(list []string) error { if len(list) == 1 { if f.Method != EQ { diff --git a/main_test.go b/main_test.go index e57a555..5719870 100644 --- a/main_test.go +++ b/main_test.go @@ -242,6 +242,9 @@ func TestWhere(t *testing.T) { {url: "?id[eq]=1&id[eq]=4", expected: " WHERE id = ? AND id = ?"}, {url: "?id[gte]=1&id[lte]=4", expected: " WHERE id >= ? AND id <= ?", expected2: " WHERE id <= ? AND id >= ?"}, {url: "?id[gte]=1|id[lte]=4", expected: " WHERE (id >= ? OR id <= ?)", expected2: " WHERE (id <= ? OR id >= ?)"}, + // float + {url: "?f[gte]=1.5&f[lte]=4.7", expected: " WHERE f >= ? AND f <= ?", expected2: " WHERE f <= ? AND f >= ?"}, + {url: "?f[gte]=1.5|f[lte]=4.7", expected: " WHERE (f >= ? OR f <= ?)", expected2: " WHERE (f <= ? OR f >= ?)"}, // null: {url: "?u[not]=NULL", expected: " WHERE u IS NOT NULL"}, {url: "?u[is]=NULL", expected: " WHERE u IS NULL"}, @@ -263,6 +266,12 @@ func TestWhere(t *testing.T) { } return nil }, + "f:float": func(value interface{}) error { + if value.(float32) > 8.5 { + return errors.New("can't be greater then 8.5") + } + return nil + }, "s": In( "super", "best", @@ -302,6 +311,12 @@ func TestWhere2(t *testing.T) { } return nil }, + "f:float": func(value interface{}) error { + if value.(float32) > 8.5 { + return errors.New("can't be greater then 8.5") + } + return nil + }, "s": In( "super", "best", @@ -311,7 +326,7 @@ func TestWhere2(t *testing.T) { return nil }, }) - assert.NoError(t, q.SetUrlString("?id[eq]=10&s[like]=super|u[like]=*best*&id[gt]=1")) + assert.NoError(t, q.SetUrlString("?id[eq]=10&f[gt]=4&s[like]=super|u[like]=*best*&id[gt]=1")) assert.NoError(t, q.Parse()) //t.Log(q.SQL("tab"), q.Args()) assert.NoError(t, q.SetUrlString("?id[eq]=10&s[like]=super|u[like]=&id[gt]=1")) diff --git a/validation.go b/validation.go index ca8ba1e..5826942 100644 --- a/validation.go +++ b/validation.go @@ -84,6 +84,42 @@ func MinMax(min, max int) ValidationFunc { } } +// MinFloat validation if value greater or equal then min +func MinFloat(min float32) ValidationFunc { + return func(value interface{}) error { + if limit, ok := value.(float32); ok { + if limit >= min { + return nil + } + } + return errors.Wrapf(ErrNotInScope, "%v", value) + } +} + +// MaxFloat validation if value lower or equal then max +func MaxFloat(max float32) ValidationFunc { + return func(value interface{}) error { + if limit, ok := value.(float32); ok { + if limit <= max { + return nil + } + } + return errors.Wrapf(ErrNotInScope, "%v", value) + } +} + +// MinMaxFloat validation if value between or equal min and max +func MinMaxFloat(min, max float32) ValidationFunc { + return func(value interface{}) error { + if limit, ok := value.(float32); ok { + if min <= limit && limit <= max { + return nil + } + } + return errors.Wrapf(ErrNotInScope, "%v", value) + } +} + // NotEmpty validation if string value length more then 0 func NotEmpty() ValidationFunc { return func(value interface{}) error { diff --git a/validation_test.go b/validation_test.go index 8341525..aefcca5 100644 --- a/validation_test.go +++ b/validation_test.go @@ -55,6 +55,39 @@ func TestMinMax(t *testing.T) { } +func TestMinMaxFloat(t *testing.T) { + err := MaxFloat(100)(101) + assert.Equal(t, errors.Cause(err), ErrNotInScope) + assert.EqualError(t, err, "101: not in scope") + + err = MaxFloat(100)(100) + assert.NoError(t, err) + + err = MinFloat(100)(100) + assert.NoError(t, err) + + err = MinMaxFloat(10, 100)(9) + assert.Equal(t, errors.Cause(err), ErrNotInScope) + assert.EqualError(t, err, "9: not in scope") + + err = MinMaxFloat(10, 100)(101) + assert.Equal(t, errors.Cause(err), ErrNotInScope) + assert.EqualError(t, err, "101: not in scope") + + err = MinMaxFloat(10, 100)(50) + assert.NoError(t, err) + + err = Multi(MinFloat(10), MaxFloat(100))(50) + assert.NoError(t, err) + + err = Multi(MinFloat(10), MaxFloat(100))(101) + assert.Equal(t, errors.Cause(err), ErrNotInScope) + + err = MinMaxFloat(10, 100)("one") + assert.Equal(t, errors.Cause(err), ErrNotInScope) + assert.EqualError(t, err, "one: not in scope") +} + func TestNotEmpty(t *testing.T) { // good case err := NotEmpty()("test")