mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-17 06:09:53 +05:00
FilterS.Pass over DataProvider interface
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
26
utils/dataprovider.go
Normal 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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user