From 6f76c211d6707c8dbccd0ddb6b113ee7e9978830 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 14 Jul 2016 16:03:16 +0200 Subject: [PATCH] RequestFilter with *rsr_fields type --- engine/reqfilter.go | 52 +++++++++++++++++++++++++++++++--------- engine/reqfilter_test.go | 33 +++++++++++++++++++++++++ utils/rsrfield.go | 44 +++++++++++++++++++++++++++++++--- utils/rsrfield_test.go | 20 ++++++++++++++++ 4 files changed, 135 insertions(+), 14 deletions(-) diff --git a/engine/reqfilter.go b/engine/reqfilter.go index feb4f5c87..711668683 100644 --- a/engine/reqfilter.go +++ b/engine/reqfilter.go @@ -19,6 +19,7 @@ along with this program. If not, see package engine import ( + "errors" "strings" "github.com/cgrates/cgrates/utils" @@ -28,18 +29,41 @@ import ( const ( MetaStringPrefix = "*string_prefix" MetaTiming = "*timing" - MetaRSRFilters = "*rsr_filters" + MetaRSRFields = "*rsr_fields" MetaCDRStats = "*cdr_stats" MetaDestinations = "*destinations" ) +func NewRequestFilter(rfType, fieldName, vals string, tpDB RatingStorage, cdrStats rpcclient.RpcClientConnection) (*RequestFilter, error) { + rf := &RequestFilter{Type: rfType, FieldName: fieldName, Values: vals, tpDB: tpDB, cdrStats: cdrStats} + switch rfType { + case MetaTiming, MetaDestinations: + if tpDB == nil { + return nil, errors.New("Missing tpDB information") + } + case MetaCDRStats: + if cdrStats == nil { + return nil, errors.New("Missing cdrStats information") + } + case MetaRSRFields: + var err error + if rf.rsrFields, err = utils.ParseRSRFields(vals, utils.INFIELD_SEP); err != nil { + return nil, err + } + } + return rf, nil +} + // RequestFilter filters requests coming into various places type RequestFilter struct { - Type string // Filter type (*string, *timing, *rsr_filters, *cdr_stats) - FieldName string // Name of the field providing us the value to check - Values string // Filter definition - rsrFltrVals []*utils.RSRFilter // Cache here the RSRFilter values - cdrStats rpcclient.RpcClientConnection // Connection towards CDRStats service (eg: for *cdr_stats type) + Type string // Filter type (*string, *timing, *rsr_filters, *cdr_stats) + FieldName string // Name of the field providing us the Values to check (used in case of some ) + Values string // Filter definition + rsrFields utils.RSRFields // Cache here the RSRFilter Values + tpDB RatingStorage + dataDB AccountingStorage + cdrStats rpcclient.RpcClientConnection // Connection towards CDRStats service (eg: for *cdr_stats type) + } func (fltr *RequestFilter) Pass(req interface{}, extraFieldsLabel string) (bool, error) { @@ -50,8 +74,8 @@ func (fltr *RequestFilter) Pass(req interface{}, extraFieldsLabel string) (bool, return fltr.passTiming(req, extraFieldsLabel) case MetaDestinations: return fltr.passDestinations(req, extraFieldsLabel) - case MetaRSRFilters: - return fltr.passRSRFilters(req, extraFieldsLabel) + case MetaRSRFields: + return fltr.passRSRFields(req, extraFieldsLabel) case MetaCDRStats: return fltr.passCDRStats(req, extraFieldsLabel) default: @@ -84,9 +108,15 @@ func (fltr *RequestFilter) passDestinations(req interface{}, extraFieldsLabel st return false, utils.ErrNotImplemented } -// ToDo -func (fltr *RequestFilter) passRSRFilters(req interface{}, extraFieldsLabel string) (bool, error) { - return false, utils.ErrNotImplemented +func (fltr *RequestFilter) passRSRFields(req interface{}, extraFieldsLabel string) (bool, error) { + for _, rsrFld := range fltr.rsrFields { + if strVal, err := utils.ReflectFieldAsString(req, rsrFld.Id, extraFieldsLabel); err != nil { + return false, err + } else if !rsrFld.FilterPasses(strVal) { + return false, nil + } + } + return true, nil } // ToDo diff --git a/engine/reqfilter_test.go b/engine/reqfilter_test.go index f1451eda9..0cba8486a 100644 --- a/engine/reqfilter_test.go +++ b/engine/reqfilter_test.go @@ -64,3 +64,36 @@ func TestPassStringPrefix(t *testing.T) { t.Error(err) } } + +func TestPassRSRFields(t *testing.T) { + cd := &CallDescriptor{Direction: "*out", Category: "call", Tenant: "cgrates.org", Subject: "dan", Destination: "+4986517174963", + TimeStart: time.Date(2013, time.October, 7, 14, 50, 0, 0, time.UTC), TimeEnd: time.Date(2013, time.October, 7, 14, 52, 12, 0, time.UTC), + DurationIndex: 132 * time.Second, ExtraFields: map[string]string{"navigation": "off"}} + rf, err := NewRequestFilter(MetaRSRFields, "", "Tenant(~^cgr.*\\.org$)", nil, nil) + if err != nil { + t.Error(err) + } + if passes, err := rf.passRSRFields(cd, "ExtraFields"); err != nil { + t.Error(err) + } else if !passes { + t.Error("Not passing") + } + rf, err = NewRequestFilter(MetaRSRFields, "", "navigation(on)", nil, nil) + if err != nil { + t.Error(err) + } + if passes, err := rf.passRSRFields(cd, "ExtraFields"); err != nil { + t.Error(err) + } else if passes { + t.Error("Passing") + } + rf, err = NewRequestFilter(MetaRSRFields, "", "navigation(off)", nil, nil) + if err != nil { + t.Error(err) + } + if passes, err := rf.passRSRFields(cd, "ExtraFields"); err != nil { + t.Error(err) + } else if !passes { + t.Error("Not passing") + } +} diff --git a/utils/rsrfield.go b/utils/rsrfield.go index d8ca48c2b..b4d1be31e 100644 --- a/utils/rsrfield.go +++ b/utils/rsrfield.go @@ -179,6 +179,42 @@ func (rsrFltr *RSRFilter) Pass(val string) bool { return val == rsrFltr.filterRule != rsrFltr.negative } +func ParseRSRFilters(fldsStr, sep string) (RSRFilters, error) { + if fldsStr == "" { + return nil, nil + } + fltrSplt := strings.Split(fldsStr, sep) + rsrFltrs := make(RSRFilters, len(fltrSplt)) + for i, rlStr := range fltrSplt { + if rsrFltr, err := NewRSRFilter(rlStr); err != nil { + return nil, err + } else if rsrFltr == nil { + return nil, fmt.Errorf("Empty RSRFilter in rule: %s", rlStr) + } else { + rsrFltrs[i] = rsrFltr + } + } + return rsrFltrs, nil +} + +type RSRFilters []*RSRFilter + +// @all: specifies whether all filters should match or at least one +func (fltrs RSRFilters) Pass(val string, allMustMatch bool) bool { + if len(fltrs) == 0 { + return true + } + var matched bool + for _, fltr := range fltrs { + if fltr.Pass(val) { + matched = true + } else if allMustMatch { + return false + } + } + return matched +} + // Parses list of RSRFields, used for example as multiple filters in derived charging func ParseRSRFields(fldsStr, sep string) (RSRFields, error) { //rsrRlsPattern := regexp.MustCompile(`^(~\w+:s/.+/.*/)|(\^.+(/.+/)?)(;(~\w+:s/.+/.*/)|(\^.+(/.+/)?))*$`) //ToDo:Fix here rule able to confirm the content @@ -198,6 +234,8 @@ func ParseRSRFieldsFromSlice(flds []string) (RSRFields, error) { for idx, ruleStr := range flds { if rsrField, err := NewRSRField(ruleStr); err != nil { return nil, err + } else if rsrField == nil { + return nil, fmt.Errorf("Empty RSRField in rule: %s", ruleStr) } else { rsrFields[idx] = rsrField } @@ -217,9 +255,9 @@ func ParseRSRFieldsMustCompile(fldsStr, sep string) RSRFields { type RSRFields []*RSRField // Return first Id of the rsrFields, used in cdre -func (self RSRFields) Id() string { - if len(self) == 0 { +func (flds RSRFields) Id() string { + if len(flds) == 0 { return "" } - return self[0].Id + return flds[0].Id } diff --git a/utils/rsrfield_test.go b/utils/rsrfield_test.go index c98c32fb1..764f61df0 100644 --- a/utils/rsrfield_test.go +++ b/utils/rsrfield_test.go @@ -352,3 +352,23 @@ func TestRSRFilterPass(t *testing.T) { t.Error("Not passing!") } } + +func TestRSRFiltersPass(t *testing.T) { + rlStr := "~^C.+S$;CGRateS;ateS$" + fltrs, err := ParseRSRFilters(rlStr, INFIELD_SEP) + if err != nil { + t.Error(err) + } + if !fltrs.Pass("CGRateS", true) { + t.Error("Not passing") + } + if fltrs.Pass("ateS", true) { + t.Error("Passing") + } + if !fltrs.Pass("ateS", false) { + t.Error("Not passing") + } + if fltrs.Pass("teS", false) { + t.Error("Passing") + } +}