FilterS.Pass over DataProvider interface

This commit is contained in:
DanB
2018-06-11 18:12:15 +02:00
parent f02b3dd2e8
commit b3ed751e28
15 changed files with 200 additions and 101 deletions

View File

@@ -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
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

26
utils/dataprovider.go Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>
*/
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
}

View File

@@ -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 {

View File

@@ -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: <NonExisting>").Error() {
if _, err := nM.FieldAsString(strings.Split(fPath, ">")); err.Error() !=
errors.New("no map at path: <NonExisting>").Error() {
t.Error(err)
}
}