New filter types: *lt, *lte, *gt, *gte

This commit is contained in:
DanB
2017-10-13 18:18:14 +02:00
parent b7a46619a7
commit c425722bce
3 changed files with 148 additions and 35 deletions

View File

@@ -27,24 +27,42 @@ import (
)
const (
MetaString = "*string"
MetaStringPrefix = "*string_prefix"
MetaTimings = "*timings"
MetaRSRFields = "*rsr_fields"
MetaStatS = "*stats"
MetaDestinations = "*destinations"
MetaMinCapPrefix = "*min_"
MetaMaxCapPrefix = "*max_"
MetaString = "*string"
MetaStringPrefix = "*string_prefix"
MetaTimings = "*timings"
MetaRSRFields = "*rsr_fields"
MetaStatS = "*stats"
MetaDestinations = "*destinations"
MetaMinCapPrefix = "*min_"
MetaMaxCapPrefix = "*max_"
MetaLessThan = "*lt"
MetaLessOrEqual = "*lte"
MetaGreaterThan = "*gt"
MetaGreaterOrEqual = "*gte"
)
type Filter struct {
Tenant string
ID string
RequestFilters []*RequestFilter
ActivationInterval *utils.ActivationInterval
}
func (flt *Filter) TenantID() string {
return utils.ConcatenatedKey(flt.Tenant, flt.ID)
}
func NewRequestFilter(rfType, fieldName string, vals []string) (*RequestFilter, error) {
if !utils.IsSliceMember([]string{MetaStringPrefix, MetaTimings, MetaRSRFields, MetaStatS, MetaDestinations}, rfType) {
if !utils.IsSliceMember([]string{MetaStringPrefix, MetaTimings, MetaRSRFields, MetaStatS, MetaDestinations,
MetaLessThan, MetaLessOrEqual, MetaGreaterThan, MetaGreaterOrEqual}, rfType) {
return nil, fmt.Errorf("Unsupported filter Type: %s", rfType)
}
if fieldName == "" && utils.IsSliceMember([]string{MetaStringPrefix, MetaTimings, MetaDestinations}, rfType) {
if fieldName == "" && utils.IsSliceMember([]string{MetaStringPrefix, MetaTimings, MetaDestinations,
MetaLessThan, MetaLessOrEqual, MetaGreaterThan, MetaGreaterOrEqual}, rfType) {
return nil, fmt.Errorf("FieldName is mandatory for Type: %s", rfType)
}
if len(vals) == 0 && utils.IsSliceMember([]string{MetaStringPrefix, MetaTimings, MetaRSRFields, MetaDestinations, MetaDestinations}, rfType) {
if len(vals) == 0 && utils.IsSliceMember([]string{MetaStringPrefix, MetaTimings, MetaRSRFields,
MetaDestinations, MetaDestinations, MetaLessThan, MetaLessOrEqual, MetaGreaterThan, MetaGreaterOrEqual}, rfType) {
return nil, fmt.Errorf("Values is mandatory for Type: %s", rfType)
}
rf := &RequestFilter{Type: rfType, FieldName: fieldName, Values: vals}
@@ -63,7 +81,7 @@ type RFStatSThreshold struct {
// RequestFilter filters requests coming into various places
// Pass rule: default negative, one mathing rule should pass the filter
type RequestFilter struct {
Type string // Filter type (*string, *timing, *rsr_filters, *cdr_stats)
Type string // Filter type (*string, *timing, *rsr_filters, *stats, *lt, *lte, *gt, *gte)
FieldName string // Name of the field providing us the Values to check (used in case of some )
Values []string // Filter definition
ActivationInterval *utils.ActivationInterval
@@ -71,17 +89,6 @@ type RequestFilter struct {
statSThresholds []*RFStatSThreshold // Cached compiled RFStatsThreshold out of Values
}
type Filter struct {
Tenant string
ID string
RequestFilters []*RequestFilter
ActivationInterval *utils.ActivationInterval
}
func (flt *Filter) TenantID() string {
return utils.ConcatenatedKey(flt.Tenant, flt.ID)
}
// Separate method to compile RSR fields
func (rf *RequestFilter) CompileValues() (err error) {
if rf.Type == MetaRSRFields {
@@ -127,6 +134,8 @@ func (fltr *RequestFilter) Pass(req interface{}, extraFieldsLabel string, rpcCln
return fltr.passRSRFields(req, extraFieldsLabel)
case MetaStatS:
return fltr.passStatS(req, extraFieldsLabel, rpcClnt)
case MetaLessThan, MetaLessOrEqual, MetaGreaterThan, MetaGreaterOrEqual:
return fltr.passGreaterThan(req, extraFieldsLabel)
default:
return false, utils.ErrNotImplemented
}
@@ -228,3 +237,29 @@ func (fltr *RequestFilter) passStatS(req interface{}, extraFieldsLabel string, s
}
return false, nil
}
func (fltr *RequestFilter) passGreaterThan(req interface{}, extraFieldsLabel string) (bool, error) {
fldIf, err := utils.ReflectFieldInterface(req, fltr.FieldName, extraFieldsLabel)
if err != nil {
if err == utils.ErrNotFound {
return false, nil
}
return false, err
}
for _, val := range fltr.Values {
ifVal := utils.StringToInterface(val)
orEqual := false
if fltr.Type == MetaGreaterOrEqual ||
fltr.Type == MetaLessThan {
orEqual = true
}
if gte, err := utils.GreaterThan(fldIf, ifVal, orEqual); err != nil {
return false, err
} else if utils.IsSliceMember([]string{MetaGreaterThan, MetaGreaterOrEqual}, fltr.Type) && gte {
return true, nil
} else if !gte && utils.IsSliceMember([]string{MetaLessThan, MetaLessOrEqual}, fltr.Type) && !gte {
return true, nil
}
}
return false, nil
}

View File

@@ -139,3 +139,64 @@ func TestReqFilterPassDestinations(t *testing.T) {
t.Error("Passing")
}
}
func TestReqFilterPassGreaterThan(t *testing.T) {
rf, err := NewRequestFilter(MetaLessThan, "ASR", []string{"40"})
if err != nil {
t.Error(err)
}
ev := map[string]interface{}{
"ASR": 20,
}
if passes, err := rf.passGreaterThan(ev, ""); err != nil {
t.Error(err)
} else if !passes {
t.Error("not passing")
}
ev = map[string]interface{}{
"ASR": 40,
}
if passes, err := rf.passGreaterThan(ev, ""); err != nil {
t.Error(err)
} else if passes {
t.Error("equal should not be passing")
}
rf, err = NewRequestFilter(MetaLessOrEqual, "ASR", []string{"40"})
if err != nil {
t.Error(err)
}
if passes, err := rf.passGreaterThan(ev, ""); err != nil {
t.Error(err)
} else if !passes {
t.Error("not passing")
}
rf, err = NewRequestFilter(MetaGreaterOrEqual, "ASR", []string{"40"})
if err != nil {
t.Error(err)
}
if passes, err := rf.passGreaterThan(ev, ""); err != nil {
t.Error(err)
} else if !passes {
t.Error("not passing")
}
ev = map[string]interface{}{
"ASR": 20,
}
if passes, err := rf.passGreaterThan(ev, ""); err != nil {
t.Error(err)
} else if passes {
t.Error("should not pass")
}
rf, err = NewRequestFilter(MetaGreaterOrEqual, "ACD", []string{"1m50s"})
if err != nil {
t.Error(err)
}
ev = map[string]interface{}{
"ACD": time.Duration(2 * time.Minute),
}
if passes, err := rf.passGreaterThan(ev, ""); err != nil {
t.Error(err)
} else if !passes {
t.Error("not pass")
}
}

View File

@@ -161,28 +161,45 @@ func AsMapStringIface(item interface{}) (map[string]interface{}, error) {
// GreaterThan attempts to compare two items
// returns the result or error if not comparable
func GreaterThan(item, oItem interface{}, orEqual bool) (gte bool, err error) {
valItm := reflect.ValueOf(item)
valOtItm := reflect.ValueOf(oItem)
// convert to wider type so we can be compatible with StringToInterface function
switch valItm.Kind() {
case reflect.Float32:
item = valItm.Float()
valItm = reflect.ValueOf(item)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
item = valItm.Int()
valItm = reflect.ValueOf(item)
}
switch valOtItm.Kind() {
case reflect.Float32:
oItem = valOtItm.Float()
valOtItm = reflect.ValueOf(oItem)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32:
oItem = valOtItm.Int()
valOtItm = reflect.ValueOf(oItem)
}
typItem := reflect.TypeOf(item)
typOItem := reflect.TypeOf(oItem)
fmt.Println(typItem.Comparable(),
typOItem.Comparable(),
typItem,
typOItem,
typItem == typOItem)
if !typItem.Comparable() ||
!typOItem.Comparable() ||
typItem != typOItem {
return false, errors.New("incomparable")
}
if orEqual && reflect.DeepEqual(item, oItem) {
return true, nil
}
valItm := reflect.ValueOf(item)
valOItm := reflect.ValueOf(oItem)
switch typItem.Kind() {
case reflect.Float32, reflect.Float64:
gte = valItm.Float() > valOItm.Float()
if orEqual {
gte = valItm.Float() >= valOtItm.Float()
} else {
gte = valItm.Float() > valOtItm.Float()
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
gte = valItm.Int() > valOItm.Int()
if orEqual {
gte = valItm.Int() >= valOtItm.Int()
} else {
gte = valItm.Int() > valOtItm.Int()
}
default: // unsupported comparison
err = fmt.Errorf("unsupported type: %v", typItem)
}