mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-22 23:58:44 +05:00
Add new filter type "*accounts"
This commit is contained in:
committed by
Dan Christian Bogos
parent
cae07d1e35
commit
6e3960fb7a
@@ -1116,6 +1116,29 @@ func (acnt *Account) Publish() {
|
||||
}
|
||||
}
|
||||
|
||||
func (acnt *Account) AsNavigableMap(_ []*config.FCTemplate) (*config.NavigableMap, error) {
|
||||
mpIface := map[string]interface{}{
|
||||
"ID": acnt.ID,
|
||||
//"UnitCounters": acnt.UnitCounters,
|
||||
"ActionTriggers": acnt.ActionTriggers,
|
||||
"AllowNegative": acnt.AllowNegative,
|
||||
"Disabled": acnt.Disabled,
|
||||
}
|
||||
|
||||
balanceMap := make(map[string]interface{}, len(acnt.BalanceMap))
|
||||
for key, balances := range acnt.BalanceMap {
|
||||
balSls := make([]*config.NavigableMap, len(balances))
|
||||
for i, balance := range balances {
|
||||
balSls[i], _ = balance.AsNavigableMap(nil)
|
||||
}
|
||||
balanceMap[key] = balSls
|
||||
}
|
||||
mpIface["BalanceMap"] = balanceMap
|
||||
|
||||
return config.NewNavigableMap(mpIface), nil
|
||||
|
||||
}
|
||||
|
||||
func NewAccountSummaryFromJSON(jsn string) (acntSummary *AccountSummary, err error) {
|
||||
if !utils.IsSliceMember([]string{"", "null"}, jsn) { // Unmarshal only when content
|
||||
json.Unmarshal([]byte(jsn), &acntSummary)
|
||||
|
||||
@@ -2279,6 +2279,54 @@ func TestAccountGetMultipleBalancesForPrefixWithSameWeight(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccountAsNavigableMap(t *testing.T) {
|
||||
acc := &Account{
|
||||
BalanceMap: map[string]Balances{
|
||||
utils.MONETARY: Balances{
|
||||
&Balance{
|
||||
ID: "SpecialBalance1",
|
||||
Value: 10,
|
||||
Weight: 10.0,
|
||||
},
|
||||
&Balance{
|
||||
ID: "SpecialBalance2",
|
||||
Value: 10,
|
||||
Weight: 10.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
nM, _ := acc.AsNavigableMap(nil)
|
||||
eVal := "SpecialBalance1"
|
||||
if strVal, err := nM.FieldAsString(
|
||||
[]string{"BalanceMap", "*monetary[0]", "ID"}); err != nil {
|
||||
t.Error(err)
|
||||
} else if strVal != eVal {
|
||||
t.Errorf("expecting: <%+v> received: <%+v>", eVal, strVal)
|
||||
}
|
||||
eVal = "10"
|
||||
if strVal, err := nM.FieldAsString(
|
||||
[]string{"BalanceMap", "*monetary[0]", "Value"}); err != nil {
|
||||
t.Error(err)
|
||||
} else if strVal != eVal {
|
||||
t.Errorf("expecting: <%+v> received: <%+v>", eVal, strVal)
|
||||
}
|
||||
eVal = "10"
|
||||
if strVal, err := nM.FieldAsString(
|
||||
[]string{"BalanceMap", "*monetary[0]", "Weight"}); err != nil {
|
||||
t.Error(err)
|
||||
} else if strVal != eVal {
|
||||
t.Errorf("expecting: <%+v> received: <%+v>", eVal, strVal)
|
||||
}
|
||||
eVal = "SpecialBalance2"
|
||||
if strVal, err := nM.FieldAsString(
|
||||
[]string{"BalanceMap", "*monetary[1]", "ID"}); err != nil {
|
||||
t.Error(err)
|
||||
} else if strVal != eVal {
|
||||
t.Errorf("expecting: <%+v> received: <%+v>", eVal, strVal)
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************** Benchmarks *******************************/
|
||||
|
||||
func BenchmarkGetSecondForPrefix(b *testing.B) {
|
||||
|
||||
@@ -185,6 +185,25 @@ func (b *Balance) Clone() *Balance {
|
||||
return n
|
||||
}
|
||||
|
||||
func (b *Balance) AsNavigableMap(_ []*config.FCTemplate) (*config.NavigableMap, error) {
|
||||
return config.NewNavigableMap(map[string]interface{}{
|
||||
"Uuid": b.Uuid,
|
||||
"ID": b.ID,
|
||||
"Value": b.Value,
|
||||
"ExpirationDate": b.ExpirationDate,
|
||||
"Weight": b.Weight,
|
||||
"DestinationIDs": b.DestinationIDs,
|
||||
"RatingSubject": b.RatingSubject,
|
||||
"Categories": b.Categories,
|
||||
"SharedGroups": b.SharedGroups,
|
||||
"Timings": b.Timings,
|
||||
"TimingIDs": b.TimingIDs,
|
||||
"Disabled": b.Disabled,
|
||||
"Factor": b.Factor,
|
||||
"Blocker": b.Blocker,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (b *Balance) getMatchingPrefixAndDestID(dest string) (prefix, destId string) {
|
||||
if len(b.DestinationIDs) != 0 && b.DestinationIDs[utils.ANY] == false {
|
||||
for _, p := range utils.SplitPrefix(dest, MIN_PREFIX_MATCH) {
|
||||
|
||||
@@ -62,7 +62,7 @@ const (
|
||||
)
|
||||
|
||||
func NewFilterS(cfg *config.CGRConfig,
|
||||
statSChan, resSChan chan rpcclient.RpcClientConnection, dm *DataManager) (fS *FilterS) {
|
||||
statSChan, resSChan, ralSChan chan rpcclient.RpcClientConnection, dm *DataManager) (fS *FilterS) {
|
||||
fS = &FilterS{
|
||||
dm: dm,
|
||||
cfg: cfg,
|
||||
@@ -73,16 +73,19 @@ func NewFilterS(cfg *config.CGRConfig,
|
||||
if len(cfg.FilterSCfg().ResourceSConns) != 0 {
|
||||
fS.connResourceS(resSChan)
|
||||
}
|
||||
if len(cfg.FilterSCfg().RALsConns) != 0 {
|
||||
fS.connRALs(ralSChan)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FilterS is a service used to take decisions in case of filters
|
||||
// uses lazy connections where necessary to avoid deadlocks on service startup
|
||||
type FilterS struct {
|
||||
cfg *config.CGRConfig
|
||||
statSConns, resSConns rpcclient.RpcClientConnection
|
||||
sSConnMux, rSConnMux sync.RWMutex // make sure only one goroutine attempts connecting
|
||||
dm *DataManager
|
||||
cfg *config.CGRConfig
|
||||
statSConns, resSConns, ralSConns rpcclient.RpcClientConnection
|
||||
sSConnMux, rSConnMux, ralSConnMux sync.RWMutex // make sure only one goroutine attempts connecting
|
||||
dm *DataManager
|
||||
}
|
||||
|
||||
// connStatS returns will connect towards StatS
|
||||
@@ -117,6 +120,22 @@ func (fS *FilterS) connResourceS(resSChan chan rpcclient.RpcClientConnection) (e
|
||||
return
|
||||
}
|
||||
|
||||
// connRALs returns will connect towards RALs
|
||||
func (fS *FilterS) connRALs(ralSChan chan rpcclient.RpcClientConnection) (err error) {
|
||||
fS.ralSConnMux.Lock()
|
||||
defer fS.ralSConnMux.Unlock()
|
||||
if fS.ralSConns != nil { // connection was populated between locks
|
||||
return
|
||||
}
|
||||
fS.ralSConns, err = NewRPCPool(rpcclient.POOL_FIRST,
|
||||
fS.cfg.TlsCfg().ClientKey, fS.cfg.TlsCfg().ClientCerificate,
|
||||
fS.cfg.TlsCfg().CaCertificate, fS.cfg.GeneralCfg().ConnectAttempts,
|
||||
fS.cfg.GeneralCfg().Reconnects, fS.cfg.GeneralCfg().ConnectTimeout,
|
||||
fS.cfg.GeneralCfg().ReplyTimeout, fS.cfg.FilterSCfg().RALsConns,
|
||||
ralSChan, true)
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
// receives the event as DataProvider so we can accept undecoded data (ie: HttpRequest)
|
||||
@@ -242,6 +261,7 @@ type FilterRule struct {
|
||||
negative *bool
|
||||
statItems []*itemFilter // Cached compiled itemFilter out of Values
|
||||
resourceItems []*itemFilter // Cached compiled itemFilter out of Values
|
||||
accountItems []*itemFilter // Cached compiled itemFilter out of Values
|
||||
}
|
||||
|
||||
// Separate method to compile RSR fields
|
||||
@@ -279,7 +299,7 @@ func (rf *FilterRule) CompileValues() (err error) {
|
||||
return fmt.Errorf("Value %s needs to contain at least 3 items", val)
|
||||
}
|
||||
// valSplt[0] filter type
|
||||
// valSplt[1] id of the Resource
|
||||
// valSplt[1] id of the AccountID.FieldToUsed
|
||||
// valSplt[2] value to compare
|
||||
rf.resourceItems[i] = &itemFilter{
|
||||
FilterType: valSplt[0],
|
||||
@@ -287,6 +307,24 @@ func (rf *FilterRule) CompileValues() (err error) {
|
||||
FilterValue: valSplt[2],
|
||||
}
|
||||
}
|
||||
case utils.MetaAccounts:
|
||||
//value for filter of type *accounts needs to be in the following form:
|
||||
//*gt:AccountID:ValueOfUsage
|
||||
rf.accountItems = make([]*itemFilter, len(rf.Values))
|
||||
for i, val := range rf.Values {
|
||||
valSplt := strings.Split(val, utils.InInFieldSep)
|
||||
if len(valSplt) != 3 {
|
||||
return fmt.Errorf("Value %s needs to contain at least 3 items", val)
|
||||
}
|
||||
// valSplt[0] filter type
|
||||
// valSplt[1] id of the Resource
|
||||
// valSplt[2] value to compare
|
||||
rf.accountItems[i] = &itemFilter{
|
||||
FilterType: valSplt[0],
|
||||
ItemID: valSplt[1],
|
||||
FilterValue: valSplt[2],
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -323,6 +361,8 @@ func (fltr *FilterRule) Pass(dP config.DataProvider,
|
||||
result, err = fltr.passResourceS(dP, rpcClnt, tenant)
|
||||
case MetaEqual, MetaNotEqual:
|
||||
result, err = fltr.passEqualTo(dP)
|
||||
case utils.MetaAccounts:
|
||||
result, err = fltr.passAccountS(dP, rpcClnt, tenant)
|
||||
default:
|
||||
err = utils.ErrPrefixNotErrNotImplemented(fltr.Type)
|
||||
}
|
||||
@@ -580,6 +620,38 @@ func (fltr *FilterRule) passResourceS(dP config.DataProvider,
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (fltr *FilterRule) passAccountS(dP config.DataProvider,
|
||||
accountS rpcclient.RpcClientConnection, tenant string) (bool, error) {
|
||||
if accountS == nil || reflect.ValueOf(accountS).IsNil() {
|
||||
return false, errors.New("Missing AccountS information")
|
||||
}
|
||||
for _, accItem := range fltr.accountItems {
|
||||
//split accItem.ItemID in two accountID and actual filter
|
||||
//AccountID.BalanceMap.*monetary[0].Value
|
||||
splittedString := strings.SplitN(accItem.ItemID, utils.NestingSep, 2)
|
||||
accID := splittedString[0]
|
||||
filterID := splittedString[1]
|
||||
var reply Account
|
||||
if err := accountS.Call(utils.ApierV2GetAccount,
|
||||
&utils.AttrGetAccount{Tenant: tenant, Account: accID}, &reply); err != nil {
|
||||
return false, err
|
||||
}
|
||||
//compose the newFilter
|
||||
fltr, err := NewFilterRule(accItem.FilterType,
|
||||
utils.DynamicDataPrefix+filterID, []string{accItem.FilterValue})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
dP, _ := reply.AsNavigableMap(nil)
|
||||
if val, err := fltr.Pass(dP, nil, tenant); err != nil || !val {
|
||||
//in case of error return false and error
|
||||
//and in case of not pass return false and nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (fltr *FilterRule) passEqualTo(dP config.DataProvider) (bool, error) {
|
||||
fldIf, err := config.GetDynamicInterface(fltr.FieldName, dP)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user