From 1192df9843d9c606a118f7564b36d99203108977 Mon Sep 17 00:00:00 2001 From: TeoV Date: Thu, 8 Nov 2018 10:35:04 -0500 Subject: [PATCH] Add support for RSRFilter for > , >= , < , <= --- agents/agentreq_test.go | 33 ++++++++++++++++++++ engine/filters.go | 2 +- engine/filters_test.go | 40 ++++++++++++++++++++++++ utils/consts.go | 4 +++ utils/rsrfield.go | 41 ++++++++++++++++++++++++ utils/rsrfield_test.go | 69 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 188 insertions(+), 1 deletion(-) diff --git a/agents/agentreq_test.go b/agents/agentreq_test.go index 43f0e6eff..410f529bd 100644 --- a/agents/agentreq_test.go +++ b/agents/agentreq_test.go @@ -122,3 +122,36 @@ func TestAgReqAsNavigableMap(t *testing.T) { t.Errorf("expecting: %+v, received: %+v", eMp, mpOut) } } + +func TestAgReqMaxCost(t *testing.T) { + data, _ := engine.NewMapStorage() + dm := engine.NewDataManager(data) + cfg, _ := config.NewDefaultCGRConfig() + filterS := engine.NewFilterS(cfg, nil, dm) + agReq := newAgentRequest(nil, nil, nil, nil, "cgrates.org", "", filterS) + // populate request, emulating the way will be done in HTTPAgent + agReq.CGRRequest.Set([]string{utils.CapMaxUsage}, "120s", false) + + cgrRply := map[string]interface{}{ + utils.CapMaxUsage: time.Duration(120 * time.Second), + } + agReq.CGRReply = config.NewNavigableMap(cgrRply) + + tplFlds := []*config.FCTemplate{ + &config.FCTemplate{Tag: "MaxUsage", + FieldId: "MaxUsage", Type: utils.META_COMPOSED, + Filters: []string{"*rsr::~*cgrep.MaxUsage(>0s)"}, + Value: config.NewRSRParsersMustCompile( + "~*cgrep.MaxUsage{*duration_seconds}", true)}, + } + eMp := config.NewNavigableMap(nil) + + eMp.Set([]string{"MaxUsage"}, []*config.NMItem{ + &config.NMItem{Data: "120", Path: []string{"MaxUsage"}, + Config: tplFlds[0]}}, true) + if mpOut, err := agReq.AsNavigableMap(tplFlds); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eMp, mpOut) { + t.Errorf("expecting: %+v, received: %+v", eMp, mpOut) + } +} diff --git a/engine/filters.go b/engine/filters.go index f6dd088e9..bb4bb0aea 100644 --- a/engine/filters.go +++ b/engine/filters.go @@ -353,7 +353,7 @@ func (fltr *FilterRule) passGreaterThan(dP config.DataProvider) (bool, error) { 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 { + } else if utils.IsSliceMember([]string{MetaLessThan, MetaLessOrEqual}, fltr.Type) && !gte { return true, nil } } diff --git a/engine/filters_test.go b/engine/filters_test.go index 991f6f315..2fc8021f3 100644 --- a/engine/filters_test.go +++ b/engine/filters_test.go @@ -467,3 +467,43 @@ func TestPassFiltersForEventWithEmptyFilter(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", true, pass) } } + +func TestPassFilterMaxCost(t *testing.T) { + data, _ := NewMapStorage() + dmFilterPass := NewDataManager(data) + cfg, _ := config.NewDefaultCGRConfig() + filterS := FilterS{ + cfg: cfg, + dm: dmFilterPass, + } + //check with max usage -1 should fail + passEvent1 := map[string]interface{}{ + "MaxUsage": time.Duration(-1), + } + if pass, err := filterS.Pass("cgrates.org", + []string{"*rsr::~MaxUsage{*duration_nanoseconds}(>0)"}, config.NewNavigableMap(passEvent1)); err != nil { + t.Errorf(err.Error()) + } else if pass { + t.Errorf("Expecting: false , received: %+v", pass) + } + //check with max usage 0 should fail + passEvent2 := map[string]interface{}{ + "MaxUsage": time.Duration(0), + } + if pass, err := filterS.Pass("cgrates.org", + []string{"*rsr::~MaxUsage{*duration_nanoseconds}(>0)"}, config.NewNavigableMap(passEvent2)); err != nil { + t.Errorf(err.Error()) + } else if pass { + t.Errorf("Expecting: false, received: %+v", pass) + } + //check with max usage 123 should pass + passEvent3 := map[string]interface{}{ + "MaxUsage": time.Duration(123), + } + if pass, err := filterS.Pass("cgrates.org", + []string{"*rsr::~MaxUsage{*duration_nanoseconds}(>0)"}, config.NewNavigableMap(passEvent3)); err != nil { + t.Errorf(err.Error()) + } else if !pass { + t.Errorf("Expecting: true, received: %+v", pass) + } +} diff --git a/utils/consts.go b/utils/consts.go index d88e5e507..e5402619a 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -315,6 +315,10 @@ const ( MetaString = "*string" NegativePrefix = "!" MatchStartPrefix = "^" + MatchGreaterThanOrEqual = ">=" + MatchLessThanOrEqual = "<=" + MatchGreaterThan = ">" + MatchLessThan = "<" MatchEndPrefix = "$" MetaGrouped = "*grouped" MetaRaw = "*raw" diff --git a/utils/rsrfield.go b/utils/rsrfield.go index aaf2a557b..a093f36b2 100644 --- a/utils/rsrfield.go +++ b/utils/rsrfield.go @@ -254,6 +254,47 @@ func (rsrFltr *RSRFilter) Pass(val string) bool { if rsrFltr.filterRule[lastIdx:] == MatchEndPrefix { return strings.HasSuffix(val, rsrFltr.filterRule[:lastIdx]) != rsrFltr.negative } + if len(rsrFltr.filterRule) > 2 && rsrFltr.filterRule[:2] == MatchGreaterThanOrEqual { + gt, err := GreaterThan(StringToInterface(val), + StringToInterface(rsrFltr.filterRule[2:]), true) + if err != nil { + Logger.Warning(fmt.Sprintf(" rule: <%s>, err: <%s>", rsrFltr.filterRule, err.Error())) + return false + } + return gt && !rsrFltr.negative + } + + if len(rsrFltr.filterRule) > 2 && rsrFltr.filterRule[:2] == MatchLessThanOrEqual { + gt, err := GreaterThan(StringToInterface(rsrFltr.filterRule[2:]), // compare the rule with the val + StringToInterface(val), + true) + if err != nil { + Logger.Warning(fmt.Sprintf(" rule: <%s>, err: <%s>", rsrFltr.filterRule, err.Error())) + return false + } + return gt && !rsrFltr.negative + } + + if rsrFltr.filterRule[:1] == MatchGreaterThan { + gt, err := GreaterThan(StringToInterface(val), + StringToInterface(rsrFltr.filterRule[1:]), false) + if err != nil { + Logger.Warning(fmt.Sprintf(" rule: <%s>, err: <%s>", rsrFltr.filterRule, err.Error())) + return false + } + return gt && !rsrFltr.negative + } + + if rsrFltr.filterRule[:1] == MatchLessThan { + gt, err := GreaterThan(StringToInterface(rsrFltr.filterRule[1:]), // compare the rule with the val + StringToInterface(val), + false) + if err != nil { + Logger.Warning(fmt.Sprintf(" rule: <%s>, err: <%s>", rsrFltr.filterRule, err.Error())) + return false + } + return gt && !rsrFltr.negative + } return (strings.Index(val, rsrFltr.filterRule) != -1) != rsrFltr.negative // default is string index } diff --git a/utils/rsrfield_test.go b/utils/rsrfield_test.go index 3e4af5b28..68c0b41b3 100644 --- a/utils/rsrfield_test.go +++ b/utils/rsrfield_test.go @@ -481,6 +481,75 @@ func TestRSRFilterPass(t *testing.T) { if !fltr.Pass("") { t.Error("Passing!") } + + // compare greaterThan + fltr, err = NewRSRFilter(">0s") + if err != nil { + t.Error(err) + } + if fltr.Pass("0s") { + t.Error("passing!") + } + if fltr.Pass("13") { + t.Error("passing!") + } + if !fltr.Pass("12s") { + t.Error("not passing!") + } + + // compare greaterThanOrEqual + fltr, err = NewRSRFilter(">=0s") + if err != nil { + t.Error(err) + } + if fltr.Pass("-1s") { + t.Error("passing!") + } + if !fltr.Pass("0s") { + t.Error("not passing!") + } + if fltr.Pass("13") { + t.Error("passing!") + } + if !fltr.Pass("12s") { + t.Error("not passing!") + } + + // compare lessThan + fltr, err = NewRSRFilter("<0s") + if err != nil { + t.Error(err) + } + if fltr.Pass("1ns") { + t.Error("passing!") + } + if fltr.Pass("13") { + t.Error("passing!") + } + if fltr.Pass("12s") { + t.Error("passing!") + } + if !fltr.Pass("-12s") { + t.Error("not passing!") + } + + // compare lessThanOrEqual + fltr, err = NewRSRFilter("<=0s") + if err != nil { + t.Error(err) + } + if !fltr.Pass("-1s") { + t.Error("not passing!") + } + if !fltr.Pass("0s") { + t.Error("not passing!") + } + if fltr.Pass("13") { + t.Error("passing!") + } + if fltr.Pass("12s") { + t.Error("passing!") + } } func TestRSRFiltersPass(t *testing.T) {