Skip to content

Add support for float type #7

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
Expand Down Expand Up @@ -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
Expand Down
36 changes: 36 additions & 0 deletions filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
17 changes: 16 additions & 1 deletion main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"},
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand All @@ -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"))
Expand Down
36 changes: 36 additions & 0 deletions validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
33 changes: 33 additions & 0 deletions validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down