mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-21 15:18:44 +05:00
New filter types: *lt, *lte, *gt, *gte
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user