Skip to content

Commit

Permalink
✨ Binding: add filter builder. (#463)
Browse files Browse the repository at this point in the history
Add support for building filter queries.

Example:
```
filter = binding.Filter{}
filter.And("name").Eq("Elmer")
filter.And("age").Gt(10)
filter.And("height").Lt(44)
filter.And("weight").LtEq(150)
filter.And("hair").NotEq("blond")
filter.And("pet").Like("Rov*")
filter.And("friend").Eq(Any{"Sam","Ed"})
p := filter.Param()
client.Get(path, object, p)
```

Adds binding.RuleSet.Find() needed by analyzer addon to get rulesets for
labels.
Example:
```
filter.And("labels").Eq(labels)
```

---------

Signed-off-by: Jeff Ortel <jortel@redhat.com>
  • Loading branch information
jortel authored Jul 28, 2023
1 parent eca0072 commit 1dfad03
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 1 deletion.
5 changes: 4 additions & 1 deletion addon/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ type Client = binding.Client
type Params = binding.Params
type Param = binding.Param
type Path = binding.Path
type Field = binding.Field

//
// Error
Expand All @@ -60,6 +59,10 @@ type Setting = binding.Setting
type Tag = binding.Tag
type TagCategory = binding.TagCategory

//
// Filter
type Filter = binding.Filter

//
// The Adapter provides hub/addon integration.
type Adapter struct {
Expand Down
15 changes: 15 additions & 0 deletions binding/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/gin-gonic/gin/binding"
liberr "github.com/jortel/go-utils/error"
"github.com/konveyor/tackle2-hub/api"
qf "github.com/konveyor/tackle2-hub/binding/filter"
"io"
"mime/multipart"
"net"
Expand All @@ -36,6 +37,20 @@ type Param struct {
Value string
}

//
// Filter
type Filter struct {
qf.Filter
}

//
// Param returns a filter parameter.
func (r *Filter) Param() (p Param) {
p.Key = api.Filter
p.Value = r.String()
return
}

//
// Params mapping.
type Params map[string]interface{}
Expand Down
173 changes: 173 additions & 0 deletions binding/filter/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package filter

/*
filter = Filter{}
filter.And("name").Eq("Elmer")
filter.And("age").Gt(10)
filter.And("height").Lt(44)
filter.And("weight").LtEq(150)
filter.And("hair").NotEq("blond")
filter.And("pet").Like("Rov*")
filter.And("friend").Eq(Any{"Sam","Ed"})
*/

import (
qf "github.com/konveyor/tackle2-hub/api/filter"
"reflect"
"strconv"
"strings"
)

const (
EQ = string(qf.EQ)
NOT = string(qf.NOT)
GT = string(qf.GT)
LT = string(qf.LT)
LIKE = string(qf.LIKE)
AND = string(qf.AND)
OR = string(qf.OR)
)

//
// Any match any.
type Any []interface{}

//
// All match all.
type All []interface{}

//
// Filter builder.
type Filter struct {
predicates []*Predicate
}

//
// And adds a predicate.
// Example: filter.And("name").Equals("Elmer")
func (f *Filter) And(field string) (p *Predicate) {
p = &Predicate{
field: field,
operator: EQ,
}
f.predicates = append(f.predicates, p)
return p
}

//
// String returns string representation.
func (f *Filter) String() (s string) {
var preds []string
for _, p := range f.predicates {
preds = append(preds, p.String())
}
s = strings.Join(preds, string(qf.COMMA))
return
}

//
// Predicate is a filter query predicate.
type Predicate struct {
field string
operator string
value string
}

//
// String returns a string representation of the predicate.
func (p *Predicate) String() (s string) {
s = p.field + p.operator + p.value
return
}

//
// Eq returns a (=) predicate.
func (p *Predicate) Eq(object interface{}) *Predicate {
p.operator = EQ
p.value = p.valueOf(object)
return p
}

//
// NotEq returns a (!=) predicate.
func (p *Predicate) NotEq(object interface{}) *Predicate {
p.operator = NOT + EQ
p.value = p.valueOf(object)
return p
}

//
// Like returns a (~) predicate.
func (p *Predicate) Like(object interface{}) *Predicate {
p.operator = LIKE
p.value = p.valueOf(object)
return p
}

//
// Gt returns a (>) predicate.
func (p *Predicate) Gt(object interface{}) *Predicate {
p.operator = GT
p.value = p.valueOf(object)
return p
}

//
// GtEq returns a (>=) predicate.
func (p *Predicate) GtEq(object interface{}) *Predicate {
p.operator = GT + EQ
p.value = p.valueOf(object)
return p
}

//
// Lt returns a (<) predicate.
func (p *Predicate) Lt(object interface{}) *Predicate {
p.operator = LT
p.value = p.valueOf(object)
return p
}

//
// LtEq returns a (<) predicate.
func (p *Predicate) LtEq(object interface{}) *Predicate {
p.operator = LT + EQ
p.value = p.valueOf(object)
return p
}

func (p *Predicate) valueOf(object interface{}) (result string) {
kind := reflect.TypeOf(object).Kind()
value := reflect.ValueOf(object)
switch kind {
case reflect.String:
result = "'" + value.String() + "'"
case reflect.Int,
reflect.Int8,
reflect.Int16,
reflect.Int32,
reflect.Int64:
n := value.Int()
result = strconv.Itoa(int(n))
case reflect.Bool:
result = strconv.FormatBool(value.Bool())
case reflect.Slice:
var items []string
for i := 0; i < value.Len(); i++ {
item := p.valueOf(value.Index(i).Interface())
items = append(items, item)
}
var operator string
switch object.(type) {
case Any:
operator = OR
case All:
operator = AND
default:
operator = OR
}
result = strings.Join(items, operator)
result = "(" + result + ")"
}
return
}
29 changes: 29 additions & 0 deletions binding/filter/filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package filter

import (
"github.com/onsi/gomega"
"testing"
)

func TestFilter(t *testing.T) {
g := gomega.NewGomegaWithT(t)
filter := Filter{}
list := Any{"One", "Two", "Three"}
filter.And("name").Eq(list)
p := filter.String()
g.Expect("name=('One'|'Two'|'Three')").To(gomega.Equal(p))

filter = Filter{}
filter.And("name").Eq(All{"One", "Two", "Three"})
p = filter.String()
g.Expect("name=('One','Two','Three')").To(gomega.Equal(p))

filter = Filter{}
filter.And("name").Eq("Elmer")
filter.And("age").Gt(10)
filter.And("height").Lt(44)
filter.And("weight").LtEq(150)
filter.And("hair").NotEq("blond")
p = filter.String()
g.Expect("name='Elmer',age>10,height<44,weight<=150,hair!='blond'").To(gomega.Equal(p))
}
8 changes: 8 additions & 0 deletions binding/ruleset.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ func (h *RuleSet) List() (list []api.RuleSet, err error) {
return
}

//
// Find RuleSets with filter.
func (h *RuleSet) Find(filter Filter) (list []api.RuleSet, err error) {
list = []api.RuleSet{}
err = h.client.Get(api.RuleSetsRoot, &list, filter.Param())
return
}

//
// Update a RuleSet.
func (h *RuleSet) Update(r *api.RuleSet) (err error) {
Expand Down

0 comments on commit 1dfad03

Please sign in to comment.