diff --git a/agents/httpagent.go b/agents/httpagent.go
index fc984f7c3..305a6722a 100644
--- a/agents/httpagent.go
+++ b/agents/httpagent.go
@@ -21,14 +21,17 @@ package agents
import (
"fmt"
"net/http"
+ "strconv"
"github.com/cgrates/cgrates/config"
+ "github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
"github.com/cgrates/rpcclient"
)
// NewHttpAgent will construct a HTTPAgent
func NewHTTPAgent(sessionS rpcclient.RpcClientConnection,
+ filterS *engine.FilterS,
timezone, reqPayload, rplyPayload string,
reqProcessors []*config.HttpAgntProcCfg) *HTTPAgent {
return &HTTPAgent{sessionS: sessionS, timezone: timezone,
@@ -39,6 +42,7 @@ func NewHTTPAgent(sessionS rpcclient.RpcClientConnection,
// HTTPAgent is a handler for HTTP requests
type HTTPAgent struct {
sessionS rpcclient.RpcClientConnection
+ filterS *engine.FilterS
timezone,
reqPayload,
rplyPayload string
@@ -47,7 +51,7 @@ type HTTPAgent struct {
// ServeHTTP implements http.Handler interface
func (ha *HTTPAgent) ServeHTTP(w http.ResponseWriter, req *http.Request) {
- dcdr, err := newHAReqDecoder(ha.reqPayload, req) // dcdr will provide information from request
+ dcdr, err := newHADataProvider(ha.reqPayload, req) // dcdr will provide information from request
if err != nil {
utils.Logger.Warning(
fmt.Sprintf("<%s> error creating decoder: %s",
@@ -63,7 +67,8 @@ func (ha *HTTPAgent) ServeHTTP(w http.ResponseWriter, req *http.Request) {
procVars, rpl); lclProcessed {
processed = lclProcessed
}
- if err != nil || (lclProcessed && !reqProcessor.ContinueOnSuccess) {
+ if err != nil ||
+ (lclProcessed && !reqProcessor.ContinueOnSuccess) {
break
}
}
@@ -94,9 +99,34 @@ func (ha *HTTPAgent) ServeHTTP(w http.ResponseWriter, req *http.Request) {
}
// processRequest represents one processor processing the request
-func (ha *HTTPAgent) processRequest(reqProc *config.HttpAgntProcCfg,
- dcdr httpAgentReqDecoder, procVars processorVars,
+func (ha *HTTPAgent) processRequest(reqProcessor *config.HttpAgntProcCfg,
+ dP utils.DataProvider, procVars processorVars,
reply *httpReplyFields) (processed bool, err error) {
+ tnt, err := dP.FieldAsString([]string{utils.Tenant})
+ if err != nil {
+ return false, err
+ }
+ if pass, err := ha.filterS.Pass(tnt, reqProcessor.Filters, dP); err != nil {
+ return false, err
+ } else if !pass {
+ return false, nil
+ }
+ for k, v := range reqProcessor.Flags { // update procVars with flags from processor
+ procVars[k] = strconv.FormatBool(v)
+ }
+ if reqProcessor.DryRun {
+ utils.Logger.Info(fmt.Sprintf("<%s> DRY_RUN, RADIUS request: %s", utils.RadiusAgent, dP))
+ utils.Logger.Info(fmt.Sprintf("<%s> DRY_RUN, process variabiles: %+v", utils.RadiusAgent, procVars))
+ }
+ /*
+ cgrEv, err := radReqAsCGREvent(req, procVars, reqProcessor.Flags, reqProcessor.RequestFields)
+ if err != nil {
+ return false, err
+ }
+ if reqProcessor.DryRun {
+ utils.Logger.Info(fmt.Sprintf("<%s> DRY_RUN, CGREvent: %s", utils.RadiusAgent, utils.ToJSON(cgrEv)))
+ }
+ */
return
}
diff --git a/agents/libhttpagent.go b/agents/libhttpagent.go
index 1d9b72a91..9183e3d54 100644
--- a/agents/libhttpagent.go
+++ b/agents/libhttpagent.go
@@ -21,12 +21,14 @@ package agents
import (
"fmt"
"net/http"
+
+ "github.com/cgrates/cgrates/utils"
)
// httpReplyField is one field written in HTTP reply
type httpReplyField struct {
- fldPath,
- fldVal string
+ fldPath string
+ fldVal string
}
func newHTTPReplyFields() *httpReplyFields {
@@ -42,19 +44,14 @@ type httpReplyFields struct {
}
// newHAReqDecoder produces decoders
-func newHAReqDecoder(dcdType string,
- req *http.Request) (rD httpAgentReqDecoder, err error) {
- switch dcdType {
+func newHADataProvider(dpType string,
+ req *http.Request) (dP utils.DataProvider, err error) {
+ switch dpType {
default:
- return nil, fmt.Errorf("unsupported decoder type <%s>", dcdType)
+ return nil, fmt.Errorf("unsupported decoder type <%s>", dpType)
}
}
-// httpAgentReqDecoder will decode request values
-type httpAgentReqDecoder interface {
- getFieldVal(fldPath string) (interface{}, error)
-}
-
// newHAReplyEncoder constructs a httpAgentReqDecoder based on encoder type
func newHAReplyEncoder(encType string,
w http.ResponseWriter) (rE httpAgentReplyEncoder, err error) {
diff --git a/agents/librad.go b/agents/librad.go
index 087b1088c..9f9ef8b8e 100644
--- a/agents/librad.go
+++ b/agents/librad.go
@@ -61,7 +61,7 @@ func (pv processorVars) valAsInterface(fldPath string) (val interface{}, err err
err = errors.New("not found")
return
}
- return utils.NavigableMap(pv).GetField(fldPath, utils.HIERARCHY_SEP)
+ return utils.NavigableMap(pv).FieldAsInterface(strings.Split(fldPath, utils.HIERARCHY_SEP))
}
// valAsString returns the string value for fldName
@@ -74,7 +74,7 @@ func (pv processorVars) valAsString(fldPath string) (val string, err error) {
if !pv.hasVar(fldName) {
return "", utils.ErrNotFoundNoCaps
}
- return utils.NavigableMap(pv).GetFieldAsString(fldPath, utils.HIERARCHY_SEP)
+ return utils.NavigableMap(pv).FieldAsString(strings.Split(fldPath, utils.HIERARCHY_SEP))
}
// asV1AuthorizeArgs returns the arguments needed by SessionSv1.AuthorizeEvent
diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go
index 5b710849f..bc81ff444 100644
--- a/cmd/cgr-engine/cgr-engine.go
+++ b/cmd/cgr-engine/cgr-engine.go
@@ -351,7 +351,10 @@ func startKamAgent(internalSMGChan chan rpcclient.RpcClientConnection, exitChan
exitChan <- true
}
-func startHTTPAgent(internalSMGChan chan rpcclient.RpcClientConnection, exitChan chan bool, server *utils.Server) {
+func startHTTPAgent(internalSMGChan chan rpcclient.RpcClientConnection,
+ exitChan chan bool, server *utils.Server, filterSChan chan *engine.FilterS) {
+ filterS := <-filterSChan
+ filterSChan <- filterS
utils.Logger.Info("Starting HTTP agent")
var err error
for _, agntCfg := range cfg.HttpAgentCfg() {
@@ -369,7 +372,7 @@ func startHTTPAgent(internalSMGChan chan rpcclient.RpcClientConnection, exitChan
}
}
server.RegisterHttpHandler(agntCfg.Url,
- agents.NewHTTPAgent(sSConn, agntCfg.Timezone, agntCfg.RequestPayload,
+ agents.NewHTTPAgent(sSConn, filterS, agntCfg.Timezone, agntCfg.RequestPayload,
agntCfg.ReplyPayload, agntCfg.RequestProcessors))
}
exitChan <- true
@@ -546,13 +549,17 @@ func startAttributeService(internalAttributeSChan chan rpcclient.RpcClientConnec
aS, err := engine.NewAttributeService(dm, filterS,
cfg.AttributeSCfg().StringIndexedFields, cfg.AttributeSCfg().PrefixIndexedFields)
if err != nil {
- utils.Logger.Crit(fmt.Sprintf("<%s> Could not init, error: %s", utils.AttributeS, err.Error()))
+ utils.Logger.Crit(
+ fmt.Sprintf("<%s> Could not init, error: %s",
+ utils.AttributeS, err.Error()))
exitChan <- true
return
}
go func() {
if err := aS.ListenAndServe(exitChan); err != nil {
- utils.Logger.Crit(fmt.Sprintf("<%s> Error: %s listening for packets", utils.AttributeS, err.Error()))
+ utils.Logger.Crit(
+ fmt.Sprintf("<%s> Error: %s listening for packets",
+ utils.AttributeS, err.Error()))
}
aS.Shutdown()
exitChan <- true
@@ -1170,7 +1177,7 @@ func main() {
}
if len(cfg.HttpAgentCfg()) != 0 {
- go startHTTPAgent(internalSMGChan, exitChan, server)
+ go startHTTPAgent(internalSMGChan, exitChan, server, filterSChan)
}
// Start PubSubS service
diff --git a/engine/attributes.go b/engine/attributes.go
index 1ac7895c9..c0ff1b22f 100644
--- a/engine/attributes.go
+++ b/engine/attributes.go
@@ -90,8 +90,8 @@ func (alS *AttributeService) matchingAttributeProfilesForEvent(ev *utils.CGREven
!aPrfl.ActivationInterval.IsActiveAtTime(*ev.Time) { // not active
continue
}
- if pass, err := alS.filterS.PassFiltersForEvent(ev.Tenant,
- ev.Event, aPrfl.FilterIDs); err != nil {
+ if pass, err := alS.filterS.Pass(ev.Tenant, aPrfl.FilterIDs,
+ utils.NavigableMap(ev.Event)); err != nil {
return nil, err
} else if !pass {
continue
diff --git a/engine/calldesc.go b/engine/calldesc.go
index 64d995d10..4fd256bb6 100644
--- a/engine/calldesc.go
+++ b/engine/calldesc.go
@@ -1433,3 +1433,24 @@ func (cd *CallDescriptor) AccountSummary() *AccountSummary {
}
return cd.account.AsAccountSummary()
}
+
+// FieldAsInterface is part of utils.DataProvider
+func (cd *CallDescriptor) FieldAsInterface(fldPath []string) (fldVal interface{}, err error) {
+ if len(fldPath) == 0 {
+ return nil, utils.ErrNotFound
+ }
+ return utils.ReflectFieldInterface(cd, fldPath[0], utils.EXTRA_FIELDS)
+}
+
+// FieldAsString is part of utils.DataProvider
+func (cd *CallDescriptor) FieldAsString(fldPath []string) (fldVal string, err error) {
+ if len(fldPath) == 0 {
+ return "", utils.ErrNotFound
+ }
+ return utils.ReflectFieldAsString(cd, fldPath[0], utils.EXTRA_FIELDS)
+}
+
+// String is part of utils.DataProvider
+func (cd *CallDescriptor) String() string {
+ return utils.ToJSON(cd)
+}
diff --git a/engine/filters.go b/engine/filters.go
index 4a50f56b3..cd65051aa 100644
--- a/engine/filters.go
+++ b/engine/filters.go
@@ -65,15 +65,19 @@ func (fS *FilterS) connStatS() (err error) {
if fS.statSConns != nil { // connection was populated between locks
return
}
- fS.statSConns, err = NewRPCPool(rpcclient.POOL_FIRST, fS.cfg.TLSClientKey, fS.cfg.TLSClientCerificate,
- fS.cfg.ConnectAttempts, fS.cfg.Reconnects, fS.cfg.ConnectTimeout, fS.cfg.ReplyTimeout,
- fS.cfg.FilterSCfg().StatSConns, fS.statSChan, fS.cfg.InternalTtl)
+ fS.statSConns, err = NewRPCPool(rpcclient.POOL_FIRST,
+ fS.cfg.TLSClientKey, fS.cfg.TLSClientCerificate,
+ fS.cfg.ConnectAttempts, fS.cfg.Reconnects,
+ fS.cfg.ConnectTimeout, fS.cfg.ReplyTimeout,
+ fS.cfg.FilterSCfg().StatSConns,
+ fS.statSChan, fS.cfg.InternalTtl)
return
}
-// PassFiltersForEvent will check all filters wihin filterIDs and require them passing for event
+// Pass will check all filters wihin filterIDs and require them passing for dataProvider
// there should be at least one filter passing, ie: if filters are not active event will fail to pass
-func (fS *FilterS) PassFiltersForEvent(tenant string, ev map[string]interface{}, filterIDs []string) (pass bool, err error) {
+// receives the event as utils.DataProvider so we can accept undecoded data (ie: HttpRequest)
+func (fS *FilterS) Pass(tenant string, filterIDs []string, ev utils.DataProvider) (pass bool, err error) {
if len(filterIDs) == 0 {
return true, nil
}
@@ -205,29 +209,29 @@ func (rf *FilterRule) CompileValues() (err error) {
}
// Pass is the method which should be used from outside.
-func (fltr *FilterRule) Pass(req interface{}, rpcClnt rpcclient.RpcClientConnection) (bool, error) {
+func (fltr *FilterRule) Pass(dP utils.DataProvider, rpcClnt rpcclient.RpcClientConnection) (bool, error) {
switch fltr.Type {
case MetaString:
- return fltr.passString(req)
+ return fltr.passString(dP)
case MetaPrefix:
- return fltr.passStringPrefix(req)
+ return fltr.passStringPrefix(dP)
case MetaTimings:
- return fltr.passTimings(req)
+ return fltr.passTimings(dP)
case MetaDestinations:
- return fltr.passDestinations(req)
+ return fltr.passDestinations(dP)
case MetaRSR:
- return fltr.passRSR(req)
+ return fltr.passRSR(dP)
case MetaStatS:
- return fltr.passStatS(req, rpcClnt)
+ return fltr.passStatS(dP, rpcClnt)
case MetaLessThan, MetaLessOrEqual, MetaGreaterThan, MetaGreaterOrEqual:
- return fltr.passGreaterThan(req)
+ return fltr.passGreaterThan(dP)
default:
return false, utils.ErrNotImplemented
}
}
-func (fltr *FilterRule) passString(req interface{}) (bool, error) {
- strVal, err := utils.ReflectFieldAsString(req, fltr.FieldName, utils.EXTRA_FIELDS)
+func (fltr *FilterRule) passString(dP utils.DataProvider) (bool, error) {
+ strVal, err := dP.FieldAsString(strings.Split(fltr.FieldName, utils.HIERARCHY_SEP))
if err != nil {
if err == utils.ErrNotFound {
return false, nil
@@ -242,8 +246,8 @@ func (fltr *FilterRule) passString(req interface{}) (bool, error) {
return false, nil
}
-func (fltr *FilterRule) passStringPrefix(req interface{}) (bool, error) {
- strVal, err := utils.ReflectFieldAsString(req, fltr.FieldName, utils.EXTRA_FIELDS)
+func (fltr *FilterRule) passStringPrefix(dP utils.DataProvider) (bool, error) {
+ strVal, err := dP.FieldAsString(strings.Split(fltr.FieldName, utils.HIERARCHY_SEP))
if err != nil {
if err == utils.ErrNotFound {
return false, nil
@@ -259,12 +263,12 @@ func (fltr *FilterRule) passStringPrefix(req interface{}) (bool, error) {
}
// ToDo when Timings will be available in DataDb
-func (fltr *FilterRule) passTimings(req interface{}) (bool, error) {
+func (fltr *FilterRule) passTimings(dP utils.DataProvider) (bool, error) {
return false, utils.ErrNotImplemented
}
-func (fltr *FilterRule) passDestinations(req interface{}) (bool, error) {
- dst, err := utils.ReflectFieldAsString(req, fltr.FieldName, utils.EXTRA_FIELDS)
+func (fltr *FilterRule) passDestinations(dP utils.DataProvider) (bool, error) {
+ dst, err := dP.FieldAsString(strings.Split(fltr.FieldName, utils.HIERARCHY_SEP))
if err != nil {
if err == utils.ErrNotFound {
return false, nil
@@ -285,9 +289,9 @@ func (fltr *FilterRule) passDestinations(req interface{}) (bool, error) {
return false, nil
}
-func (fltr *FilterRule) passRSR(req interface{}) (bool, error) {
+func (fltr *FilterRule) passRSR(dP utils.DataProvider) (bool, error) {
for _, rsrFld := range fltr.rsrFields {
- fldIface, err := utils.ReflectFieldInterface(req, rsrFld.Id, utils.EXTRA_FIELDS)
+ fldIface, err := dP.FieldAsInterface(strings.Split(rsrFld.Id, utils.HIERARCHY_SEP))
if err != nil {
if err == utils.ErrNotFound {
return false, nil
@@ -301,7 +305,7 @@ func (fltr *FilterRule) passRSR(req interface{}) (bool, error) {
return false, nil
}
-func (fltr *FilterRule) passStatS(req interface{},
+func (fltr *FilterRule) passStatS(dP utils.DataProvider,
stats rpcclient.RpcClientConnection) (bool, error) {
if stats == nil || reflect.ValueOf(stats).IsNil() {
return false, errors.New("Missing StatS information")
@@ -326,8 +330,8 @@ func (fltr *FilterRule) passStatS(req interface{},
return false, nil
}
-func (fltr *FilterRule) passGreaterThan(req interface{}) (bool, error) {
- fldIf, err := utils.ReflectFieldInterface(req, fltr.FieldName, utils.EXTRA_FIELDS)
+func (fltr *FilterRule) passGreaterThan(dP utils.DataProvider) (bool, error) {
+ fldIf, err := dP.FieldAsInterface(strings.Split(fltr.FieldName, utils.HIERARCHY_SEP))
if err != nil {
if err == utils.ErrNotFound {
return false, nil
diff --git a/engine/filters_test.go b/engine/filters_test.go
index b5af3324d..7c5424595 100644
--- a/engine/filters_test.go
+++ b/engine/filters_test.go
@@ -84,9 +84,12 @@ func TestFilterPassStringPrefix(t *testing.T) {
}
func TestFilterPassRSRFields(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"}}
+ 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 := NewFilterRule(MetaRSR, "", []string{"Tenant(~^cgr.*\\.org$)"})
if err != nil {
t.Error(err)
@@ -151,7 +154,7 @@ func TestFilterPassGreaterThan(t *testing.T) {
if err != nil {
t.Error(err)
}
- ev := map[string]interface{}{
+ ev := utils.NavigableMap{
"ASR": 20,
}
if passes, err := rf.passGreaterThan(ev); err != nil {
@@ -288,18 +291,18 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
passEvent := map[string]interface{}{
"Account": "1007",
}
- if _, err := filterS.PassFiltersForEvent("cgrates.org",
- nil, []string{"*string:Account:1007:error"}); err == nil {
+ if _, err := filterS.Pass("cgrates.org",
+ []string{"*string:Account:1007:error"}, nil); err == nil {
t.Errorf(err.Error())
}
- if pass, err := filterS.PassFiltersForEvent("cgrates.org",
- failEvent, []string{"*string:Account:1007"}); err != nil {
+ if pass, err := filterS.Pass("cgrates.org",
+ []string{"*string:Account:1007"}, utils.NavigableMap(failEvent)); err != nil {
t.Errorf(err.Error())
} else if pass {
t.Errorf("Expecting: %+v, received: %+v", false, pass)
}
- if pass, err := filterS.PassFiltersForEvent("cgrates.org",
- passEvent, []string{"*string:Account:1007"}); err != nil {
+ if pass, err := filterS.Pass("cgrates.org",
+ []string{"*string:Account:1007"}, utils.NavigableMap(passEvent)); err != nil {
t.Errorf(err.Error())
} else if !pass {
t.Errorf("Expecting: %+v, received: %+v", true, pass)
@@ -310,14 +313,14 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
passEvent = map[string]interface{}{
"Account": "1007",
}
- if pass, err := filterS.PassFiltersForEvent("cgrates.org",
- failEvent, []string{"*prefix:Account:10"}); err != nil {
+ if pass, err := filterS.Pass("cgrates.org",
+ []string{"*prefix:Account:10"}, utils.NavigableMap(failEvent)); err != nil {
t.Errorf(err.Error())
} else if pass {
t.Errorf("Expecting: %+v, received: %+v", false, pass)
}
- if pass, err := filterS.PassFiltersForEvent("cgrates.org",
- passEvent, []string{"*prefix:Account:10"}); err != nil {
+ if pass, err := filterS.Pass("cgrates.org",
+ []string{"*prefix:Account:10"}, utils.NavigableMap(passEvent)); err != nil {
t.Errorf(err.Error())
} else if !pass {
t.Errorf("Expecting: %+v, received: %+v", true, pass)
@@ -328,14 +331,14 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
passEvent = map[string]interface{}{
"Tenant": "cgrates.org",
}
- if pass, err := filterS.PassFiltersForEvent("cgrates.org",
- failEvent, []string{"*rsr::Tenant(~^cgr.*\\.org$)"}); err != nil {
+ if pass, err := filterS.Pass("cgrates.org",
+ []string{"*rsr::Tenant(~^cgr.*\\.org$)"}, utils.NavigableMap(failEvent)); err != nil {
t.Errorf(err.Error())
} else if pass {
t.Errorf("Expecting: %+v, received: %+v", false, pass)
}
- if pass, err := filterS.PassFiltersForEvent("cgrates.org",
- passEvent, []string{"*rsr::Tenant(~^cgr.*\\.org$)"}); err != nil {
+ if pass, err := filterS.Pass("cgrates.org",
+ []string{"*rsr::Tenant(~^cgr.*\\.org$)"}, utils.NavigableMap(passEvent)); err != nil {
t.Errorf(err.Error())
} else if !pass {
t.Errorf("Expecting: %+v, received: %+v", true, pass)
@@ -348,14 +351,14 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
passEvent = map[string]interface{}{
utils.Destination: "+4986517174963",
}
- if pass, err := filterS.PassFiltersForEvent("cgrates.org",
- failEvent, []string{"*destinations:Destination:EU"}); err != nil {
+ if pass, err := filterS.Pass("cgrates.org",
+ []string{"*destinations:Destination:EU"}, utils.NavigableMap(failEvent)); err != nil {
t.Errorf(err.Error())
} else if pass {
t.Errorf("Expecting: %+v, received: %+v", false, pass)
}
- if pass, err := filterS.PassFiltersForEvent("cgrates.org",
- passEvent, []string{"*destinations:Destination:EU_LANDLINE"}); err != nil {
+ if pass, err := filterS.Pass("cgrates.org",
+ []string{"*destinations:Destination:EU_LANDLINE"}, utils.NavigableMap(passEvent)); err != nil {
t.Errorf(err.Error())
} else if !pass {
t.Errorf("Expecting: %+v, received: %+v", true, pass)
@@ -366,14 +369,14 @@ func TestInlineFilterPassFiltersForEvent(t *testing.T) {
passEvent = map[string]interface{}{
utils.Weight: 20,
}
- if pass, err := filterS.PassFiltersForEvent("cgrates.org",
- failEvent, []string{"*gte:Weight:20"}); err != nil {
+ if pass, err := filterS.Pass("cgrates.org",
+ []string{"*gte:Weight:20"}, utils.NavigableMap(failEvent)); err != nil {
t.Errorf(err.Error())
} else if pass {
t.Errorf("Expecting: %+v, received: %+v", false, pass)
}
- if pass, err := filterS.PassFiltersForEvent("cgrates.org",
- passEvent, []string{"*gte:Weight:10"}); err != nil {
+ if pass, err := filterS.Pass("cgrates.org",
+ []string{"*gte:Weight:10"}, utils.NavigableMap(passEvent)); err != nil {
t.Errorf(err.Error())
} else if !pass {
t.Errorf("Expecting: %+v, received: %+v", true, pass)
@@ -400,14 +403,14 @@ func TestPassFiltersForEventWithEmptyFilter(t *testing.T) {
utils.Destination: "+4986517174963",
utils.Weight: 20,
}
- if pass, err := filterS.PassFiltersForEvent("cgrates.org",
- passEvent1, []string{}); err != nil {
+ if pass, err := filterS.Pass("cgrates.org",
+ []string{}, utils.NavigableMap(passEvent1)); err != nil {
t.Errorf(err.Error())
} else if !pass {
t.Errorf("Expecting: %+v, received: %+v", false, pass)
}
- if pass, err := filterS.PassFiltersForEvent("itsyscom.com",
- passEvent2, []string{}); err != nil {
+ if pass, err := filterS.Pass("itsyscom.com",
+ []string{}, utils.NavigableMap(passEvent2)); err != nil {
t.Errorf(err.Error())
} else if !pass {
t.Errorf("Expecting: %+v, received: %+v", true, pass)
diff --git a/engine/resources.go b/engine/resources.go
index 7ec18061b..fdf876c04 100644
--- a/engine/resources.go
+++ b/engine/resources.go
@@ -463,7 +463,8 @@ func (rS *ResourceService) matchingResourcesForEvent(ev *utils.CGREvent, usageTT
!rPrf.ActivationInterval.IsActiveAtTime(*ev.Time) { // not active
continue
}
- if pass, err := rS.filterS.PassFiltersForEvent(ev.Tenant, ev.Event, rPrf.FilterIDs); err != nil {
+ if pass, err := rS.filterS.Pass(ev.Tenant, rPrf.FilterIDs,
+ utils.NavigableMap(ev.Event)); err != nil {
return nil, err
} else if !pass {
continue
diff --git a/engine/stats.go b/engine/stats.go
index 494818fcf..0229b85e1 100644
--- a/engine/stats.go
+++ b/engine/stats.go
@@ -168,7 +168,8 @@ func (sS *StatService) matchingStatQueuesForEvent(ev *utils.CGREvent) (sqs StatQ
!sqPrfl.ActivationInterval.IsActiveAtTime(*ev.Time) { // not active
continue
}
- if pass, err := sS.filterS.PassFiltersForEvent(ev.Tenant, ev.Event, sqPrfl.FilterIDs); err != nil {
+ if pass, err := sS.filterS.Pass(ev.Tenant, sqPrfl.FilterIDs,
+ utils.NavigableMap(ev.Event)); err != nil {
return nil, err
} else if !pass {
continue
diff --git a/engine/suppliers.go b/engine/suppliers.go
index 7cf2569f9..e46eaed09 100644
--- a/engine/suppliers.go
+++ b/engine/suppliers.go
@@ -136,8 +136,8 @@ func (spS *SupplierService) matchingSupplierProfilesForEvent(ev *utils.CGREvent)
!splPrfl.ActivationInterval.IsActiveAtTime(*ev.Time) { // not active
continue
}
- if pass, err := spS.filterS.PassFiltersForEvent(ev.Tenant,
- ev.Event, splPrfl.FilterIDs); err != nil {
+ if pass, err := spS.filterS.Pass(ev.Tenant, splPrfl.FilterIDs,
+ utils.NavigableMap(ev.Event)); err != nil {
return nil, err
} else if !pass {
continue
@@ -276,8 +276,8 @@ func (spS *SupplierService) sortedSuppliersForEvent(args *ArgsGetSuppliers) (sor
var spls []*Supplier
for _, s := range splPrfl.Suppliers {
if len(s.FilterIDs) != 0 { // filters should be applied, check them here
- if pass, err := spS.filterS.PassFiltersForEvent(args.Tenant,
- args.Event, s.FilterIDs); err != nil {
+ if pass, err := spS.filterS.Pass(args.Tenant, s.FilterIDs,
+ utils.NavigableMap(args.Event)); err != nil {
return nil, err
} else if !pass {
continue
diff --git a/engine/thresholds.go b/engine/thresholds.go
index 3051dbeb9..fe335a788 100644
--- a/engine/thresholds.go
+++ b/engine/thresholds.go
@@ -245,7 +245,8 @@ func (tS *ThresholdService) matchingThresholdsForEvent(args *ArgsProcessEvent) (
!tPrfl.ActivationInterval.IsActiveAtTime(*args.Time) { // not active
continue
}
- if pass, err := tS.filterS.PassFiltersForEvent(args.Tenant, args.Event, tPrfl.FilterIDs); err != nil {
+ if pass, err := tS.filterS.Pass(args.Tenant, tPrfl.FilterIDs,
+ utils.NavigableMap(args.Event)); err != nil {
return nil, err
} else if !pass {
continue
diff --git a/utils/dataprovider.go b/utils/dataprovider.go
new file mode 100644
index 000000000..040120fdd
--- /dev/null
+++ b/utils/dataprovider.go
@@ -0,0 +1,26 @@
+/*
+Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
+Copyright (C) ITsysCOM GmbH
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see
+*/
+
+package utils
+
+// DataProvider is a data source from multiple formats
+type DataProvider interface {
+ FieldAsInterface(fldPath []string) (interface{}, error)
+ FieldAsString(fldPath []string) (string, error)
+ String() string // printable versin of data
+}
diff --git a/utils/navigablemap.go b/utils/navigablemap.go
index 1884f0533..64b4f8021 100644
--- a/utils/navigablemap.go
+++ b/utils/navigablemap.go
@@ -21,7 +21,6 @@ package utils
import (
"errors"
"fmt"
- "strings"
)
// CGRReplier is the interface supported by replies convertible to CGRReply
@@ -32,22 +31,21 @@ type NavigableMapper interface {
// NavigableMap is a map who's values can be navigated via path
type NavigableMap map[string]interface{}
-// GetField returns the field value as interface{} for the path specified
-func (nM NavigableMap) GetField(fldPath string, sep string) (fldVal interface{}, err error) {
- path := strings.Split(fldPath, sep)
- lenPath := len(path)
+// FieldAsInterface returns the field value as interface{} for the path specified
+// implements DataProvider
+func (nM NavigableMap) FieldAsInterface(fldPath []string) (fldVal interface{}, err error) {
+ lenPath := len(fldPath)
if lenPath == 0 {
return nil, errors.New("empty field path")
}
lastMp := nM // last map when layered
var canCast bool
- for i, spath := range path {
+ for i, spath := range fldPath {
if i == lenPath-1 { // lastElement
var has bool
fldVal, has = lastMp[spath]
if !has {
- err = fmt.Errorf("no field with path: <%s>", fldPath)
- return
+ return nil, ErrNotFound
}
return
} else {
@@ -58,7 +56,8 @@ func (nM NavigableMap) GetField(fldPath string, sep string) (fldVal interface{},
}
lastMp, canCast = elmnt.(map[string]interface{})
if !canCast {
- err = fmt.Errorf("cannot cast field: %s to map[string]interface{}", ToJSON(elmnt))
+ err = fmt.Errorf("cannot cast field: %s to map[string]interface{}",
+ ToJSON(elmnt))
return
}
}
@@ -67,10 +66,11 @@ func (nM NavigableMap) GetField(fldPath string, sep string) (fldVal interface{},
return
}
-// GetFieldAsString returns the field value as string for the path specified
-func (nM NavigableMap) GetFieldAsString(fldPath string, sep string) (fldVal string, err error) {
+// FieldAsString returns the field value as string for the path specified
+// implements DataProvider
+func (nM NavigableMap) FieldAsString(fldPath []string) (fldVal string, err error) {
var valIface interface{}
- valIface, err = nM.GetField(fldPath, sep)
+ valIface, err = nM.FieldAsInterface(fldPath)
if err != nil {
return
}
@@ -81,6 +81,10 @@ func (nM NavigableMap) GetFieldAsString(fldPath string, sep string) (fldVal stri
return
}
+func (nM NavigableMap) String() string {
+ return ToJSON(nM)
+}
+
// NewCGRReply is specific to replies coming from CGRateS
func NewCGRReply(rply NavigableMapper, errRply error) (nM map[string]interface{}, err error) {
if errRply != nil {
diff --git a/utils/navigablemap_test.go b/utils/navigablemap_test.go
index 31172cd88..de1e53e08 100644
--- a/utils/navigablemap_test.go
+++ b/utils/navigablemap_test.go
@@ -20,6 +20,7 @@ package utils
import (
"errors"
"reflect"
+ "strings"
"testing"
)
@@ -35,19 +36,22 @@ func TestNavMapGetFieldAsString(t *testing.T) {
"AnotherFirstLevel": "ValAnotherFirstLevel",
}
eVal := "Val1"
- if strVal, err := nM.GetFieldAsString("FirstLevel>SecondLevel>ThirdLevel>Fld1", ">"); err != nil {
+ if strVal, err := nM.FieldAsString(
+ strings.Split("FirstLevel>SecondLevel>ThirdLevel>Fld1", ">")); err != nil {
t.Error(err)
} else if strVal != eVal {
t.Errorf("expecting: <%s> received: <%s>", eVal, strVal)
}
eVal = "ValAnotherFirstLevel"
- if strVal, err := nM.GetFieldAsString("AnotherFirstLevel", ">"); err != nil {
+ if strVal, err := nM.FieldAsString(
+ strings.Split("AnotherFirstLevel", ">")); err != nil {
t.Error(err)
} else if strVal != eVal {
t.Errorf("expecting: <%s> received: <%s>", eVal, strVal)
}
fPath := "NonExisting>AnotherFirstLevel"
- if _, err := nM.GetFieldAsString(fPath, ">"); err.Error() != errors.New("no map at path: ").Error() {
+ if _, err := nM.FieldAsString(strings.Split(fPath, ">")); err.Error() !=
+ errors.New("no map at path: ").Error() {
t.Error(err)
}
}