This commit is contained in:
DanB
2016-05-01 17:06:30 +02:00
20 changed files with 799 additions and 21 deletions

View File

@@ -523,14 +523,14 @@ func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) error
}
a := &engine.Action{
Id: utils.GenUUID(),
Id: attrs.ActionsId,
ActionType: apiAct.Identifier,
Weight: apiAct.Weight,
ExpirationString: apiAct.ExpiryTime,
ExtraParameters: apiAct.ExtraParameters,
Filter: apiAct.Filter,
Balance: &engine.BalanceFilter{ // TODO: update this part
Uuid: utils.StringPointer(utils.GenUUID()),
Uuid: utils.StringPointer(apiAct.BalanceUuid),
ID: utils.StringPointer(apiAct.BalanceId),
Type: utils.StringPointer(apiAct.BalanceType),
Value: vf,
@@ -562,7 +562,8 @@ func (self *ApierV1) GetActions(actsId string, reply *[]*utils.TPAction) error {
return utils.NewErrServerError(err)
}
for _, engAct := range engActs {
act := &utils.TPAction{Identifier: engAct.ActionType,
act := &utils.TPAction{
Identifier: engAct.ActionType,
ExpiryTime: engAct.ExpirationString,
ExtraParameters: engAct.ExtraParameters,
Filter: engAct.Filter,
@@ -1063,3 +1064,63 @@ func (self *ApierV1) GetLoadHistory(attrs utils.Paginator, reply *[]*engine.Load
*reply = loadHist[offset:nrItems]
return nil
}
type AttrRemActions struct {
ActionIDs []string
}
func (self *ApierV1) RemActions(attr AttrRemActions, reply *string) error {
if attr.ActionIDs == nil {
err := utils.ErrNotFound
*reply = err.Error()
return err
}
stringMap := utils.NewStringMap(attr.ActionIDs...)
keys, err := self.RatingDb.GetKeysForPrefix(utils.ACTION_TRIGGER_PREFIX, true)
if err != nil {
*reply = err.Error()
return err
}
for _, key := range keys {
getAttrs, err := self.RatingDb.GetActionTriggers(key[len(utils.ACTION_TRIGGER_PREFIX):])
if err != nil {
*reply = err.Error()
return err
}
for _, atr := range getAttrs {
if _, found := stringMap[atr.ActionsID]; found {
// found action trigger referencing action; abort
err := fmt.Errorf("action %s refenced by action trigger %s", atr.ActionsID, atr.ID)
*reply = err.Error()
return err
}
}
}
allAplsMap, err := self.RatingDb.GetAllActionPlans()
if err != nil && err != utils.ErrNotFound {
*reply = err.Error()
return err
}
for _, apl := range allAplsMap {
for _, atm := range apl.ActionTimings {
if _, found := stringMap[atm.ActionsID]; found {
err := fmt.Errorf("action %s refenced by action plan %s", atm.ActionsID, apl.Id)
*reply = err.Error()
return err
}
}
}
for _, aID := range attr.ActionIDs {
if err := self.RatingDb.RemoveActions(aID); err != nil {
*reply = err.Error()
return err
}
}
if err := self.RatingDb.CacheRatingPrefixes(utils.ACTION_PREFIX); err != nil {
*reply = err.Error()
return err
}
*reply = utils.OK
return nil
}

View File

@@ -1297,7 +1297,7 @@ func TestApierResetDataAfterLoadFromFolder(t *testing.T) {
if rcvStats.Destinations != 5 ||
rcvStats.RatingPlans != 5 ||
rcvStats.RatingProfiles != 5 ||
rcvStats.Actions != 10 ||
rcvStats.Actions != 11 ||
rcvStats.DerivedChargers != 3 {
t.Errorf("Calling ApierV1.GetCacheStats received: %+v", rcvStats)
}

View File

@@ -33,7 +33,7 @@ func (self *ApierV2) GetAccounts(attr utils.AttrGetAccounts, reply *[]*engine.Ac
var accountKeys []string
var err error
if len(attr.AccountIds) == 0 {
if accountKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACCOUNT_PREFIX+utils.ConcatenatedKey(attr.Tenant), true); err != nil {
if accountKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACCOUNT_PREFIX+attr.Tenant, true); err != nil {
return err
}
} else {

View File

@@ -21,6 +21,7 @@ package v2
import (
"errors"
"fmt"
"math"
"os"
"path"
"strings"
@@ -263,3 +264,58 @@ func (self *ApierV2) LoadTariffPlanFromFolder(attrs utils.AttrLoadTpFromFolder,
*reply = *li
return nil
}
type AttrGetActions struct {
ActionIDs []string
Offset int // Set the item offset
Limit int // Limit number of items retrieved
}
// Retrieves actions attached to specific ActionsId within cache
func (self *ApierV2) GetActions(attr AttrGetActions, reply *map[string]engine.Actions) error {
var actionKeys []string
var err error
if len(attr.ActionIDs) == 0 {
if actionKeys, err = self.AccountDb.GetKeysForPrefix(utils.ACTION_PREFIX, false); err != nil {
return err
}
} else {
for _, accID := range attr.ActionIDs {
if len(accID) == 0 { // Source of error returned from redis (key not found)
continue
}
actionKeys = append(actionKeys, utils.ACCOUNT_PREFIX+accID)
}
}
if len(actionKeys) == 0 {
return nil
}
if attr.Offset > len(actionKeys) {
attr.Offset = len(actionKeys)
}
if attr.Offset < 0 {
attr.Offset = 0
}
var limitedActions []string
if attr.Limit != 0 {
max := math.Min(float64(attr.Offset+attr.Limit), float64(len(actionKeys)))
limitedActions = actionKeys[attr.Offset:int(max)]
} else {
limitedActions = actionKeys[attr.Offset:]
}
retActions := make(map[string]engine.Actions)
for _, accKey := range limitedActions {
key := accKey[len(utils.ACTION_PREFIX):]
acts, err := self.RatingDb.GetActions(key, false)
if err != nil {
return utils.NewErrServerError(err)
}
if len(acts) > 0 {
retActions[key] = acts
}
}
*reply = retActions
return nil
}

View File

@@ -22,7 +22,7 @@ import (
"flag"
"fmt"
"log"
_ "net/http/pprof"
// _ "net/http/pprof"
"os"
"runtime"
"runtime/pprof"

View File

@@ -18,7 +18,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package console
import "github.com/cgrates/cgrates/utils"
import (
"github.com/cgrates/cgrates/apier/v2"
"github.com/cgrates/cgrates/engine"
)
func init() {
c := &CmdGetActions{
@@ -33,7 +36,7 @@ func init() {
type CmdGetActions struct {
name string
rpcMethod string
rpcParams *StringWrapper
rpcParams *v2.AttrGetActions
*CommandExecuter
}
@@ -47,7 +50,7 @@ func (self *CmdGetActions) RpcMethod() string {
func (self *CmdGetActions) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &StringWrapper{}
self.rpcParams = &v2.AttrGetActions{}
}
return self.rpcParams
}
@@ -57,6 +60,6 @@ func (self *CmdGetActions) PostprocessRpcParams() error {
}
func (self *CmdGetActions) RpcResult() interface{} {
a := make([]*utils.TPAction, 0)
a := make(map[string]engine.Actions, 0)
return &a
}

62
console/actions_remove.go Normal file
View File

@@ -0,0 +1,62 @@
/*
Real-time Charging System for Telecom & ISP environments
Copyright (C) 2012-2015 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 console
import "github.com/cgrates/cgrates/apier/v1"
func init() {
c := &CmdRemoveActions{
name: "actions_remove",
rpcMethod: "ApierV1.RemActions",
}
commands[c.Name()] = c
c.CommandExecuter = &CommandExecuter{c}
}
// Commander implementation
type CmdRemoveActions struct {
name string
rpcMethod string
rpcParams *v1.AttrRemActions
*CommandExecuter
}
func (self *CmdRemoveActions) Name() string {
return self.name
}
func (self *CmdRemoveActions) RpcMethod() string {
return self.rpcMethod
}
func (self *CmdRemoveActions) RpcParams(reset bool) interface{} {
if reset || self.rpcParams == nil {
self.rpcParams = &v1.AttrRemActions{}
}
return self.rpcParams
}
func (self *CmdRemoveActions) PostprocessRpcParams() error {
return nil
}
func (self *CmdRemoveActions) RpcResult() interface{} {
var s string
return &s
}

View File

@@ -9,4 +9,5 @@ cgrates.org,1010,TEST_DATA_r,,true,
cgrates.org,1011,TEST_VOICE,,,
cgrates.org,1012,PREPAID_10,,,
cgrates.org,1013,TEST_NEG,,,
cgrates.org,1014,TEST_RPC,,,
cgrates.org,1014,TEST_RPC,,,
cgrates.org,1015,,,,
1 #Tenant Account ActionPlanId ActionTriggersId AllowNegative Disabled
9 cgrates.org 1011 TEST_VOICE
10 cgrates.org 1012 PREPAID_10
11 cgrates.org 1013 TEST_NEG
12 cgrates.org 1014 TEST_RPC
13 cgrates.org 1015

View File

@@ -10,3 +10,5 @@ TOPUP_DATA_r,*topup,,,,*data,*out,,DATA_DEST,datar,,*unlimited,,50000000000,10,f
TOPUP_VOICE,*topup,,,,*voice,*out,,GERMANY_MOBILE,,,*unlimited,,50000,10,false,false,10
TOPUP_NEG,*topup,,,,*voice,*out,,GERMANY;!GERMANY_MOBILE,*zero1m,,*unlimited,,100,10,false,false,10
RPC,*cgr_rpc,"{""Address"": ""localhost:2013"",""Transport"":""*gob"",""Method"":""ApierV2.SetAccount"",""Attempts"":1,""Async"" :false,""Params"":{""Account"":""rpc"",""Tenant"":""cgrates.org""}}",,,,,,,,,,,,,,,
DID,*debit,,,,*monetary,*out,,*any,,,*unlimited,*any,"{""Method"":""*incremental"",""Params"":{""Units"":1, ""Interval"":""month"",""Increment"":""day""}}",10.0,,,10.0
DID,*cdrlog,"{""action"":""^DID"",""prev_balance"":""BalanceValue""}",,,*monetary,*out,,*any,,,*unlimited,,,10.0,,,10.0
1 #ActionsTag[0] Action[1] ActionExtraParameters[2] Filter[3] BalanceTag[4] BalanceType[5] Directions[6] Categories[7] DestinationIds[8] RatingSubject[9] SharedGroup[10] ExpiryTime[11] TimingTags[12] Units[13] BalanceWeight[14] BalanceBlocker[15] BalanceDisabled[16] Weight[17]
10 TOPUP_VOICE *topup *voice *out GERMANY_MOBILE *unlimited 50000 10 false false 10
11 TOPUP_NEG *topup *voice *out GERMANY;!GERMANY_MOBILE *zero1m *unlimited 100 10 false false 10
12 RPC *cgr_rpc {"Address": "localhost:2013","Transport":"*gob","Method":"ApierV2.SetAccount","Attempts":1,"Async" :false,"Params":{"Account":"rpc","Tenant":"cgrates.org"}}
13 DID *debit *monetary *out *any *unlimited *any {"Method":"*incremental","Params":{"Units":1, "Interval":"month","Increment":"day"}} 10.0 10.0
14 DID *cdrlog {"action":"^DID","prev_balance":"BalanceValue"} *monetary *out *any *unlimited 10.0 10.0

View File

@@ -833,7 +833,7 @@ func TestLoadActions(t *testing.T) {
as1 := csvr.actions["MINI"]
expected := []*Action{
&Action{
Id: "MINI0",
Id: "MINI",
ActionType: TOPUP_RESET,
ExpirationString: UNLIMITED,
ExtraParameters: "",
@@ -853,7 +853,7 @@ func TestLoadActions(t *testing.T) {
},
},
&Action{
Id: "MINI1",
Id: "MINI",
ActionType: TOPUP,
ExpirationString: UNLIMITED,
ExtraParameters: "",
@@ -880,7 +880,7 @@ func TestLoadActions(t *testing.T) {
as2 := csvr.actions["SHARED"]
expected = []*Action{
&Action{
Id: "SHARED0",
Id: "SHARED",
ActionType: TOPUP,
ExpirationString: UNLIMITED,
Weight: 10,
@@ -905,7 +905,7 @@ func TestLoadActions(t *testing.T) {
as3 := csvr.actions["DEFEE"]
expected = []*Action{
&Action{
Id: "DEFEE0",
Id: "DEFEE",
ActionType: CDRLOG,
ExtraParameters: `{"Category":"^ddi","MediationRunId":"^did_run"}`,
Weight: 10,

View File

@@ -59,6 +59,7 @@ type RatingStorage interface {
SetDerivedChargers(string, *utils.DerivedChargers) error
GetActions(string, bool) (Actions, error)
SetActions(string, Actions) error
RemoveActions(string) error
GetSharedGroup(string, bool) (*SharedGroup, error)
SetSharedGroup(*SharedGroup) error
GetActionTriggers(string) (ActionTriggers, error)
@@ -204,6 +205,7 @@ func NewCodecMsgpackMarshaler() *CodecMsgpackMarshaler {
cmm := &CodecMsgpackMarshaler{new(codec.MsgpackHandle)}
mh := cmm.mh
mh.MapType = reflect.TypeOf(map[string]interface{}(nil))
mh.RawToString = true
return cmm
}

View File

@@ -469,6 +469,13 @@ func (ms *MapStorage) SetActions(key string, as Actions) (err error) {
return
}
func (ms *MapStorage) RemoveActions(key string) (err error) {
ms.mu.Lock()
defer ms.mu.Unlock()
delete(ms.dict, utils.ACTION_PREFIX+key)
return
}
func (ms *MapStorage) GetSharedGroup(key string, skipCache bool) (sg *SharedGroup, err error) {
ms.mu.RLock()
defer ms.mu.RUnlock()

View File

@@ -916,6 +916,10 @@ func (ms *MongoStorage) SetActions(key string, as Actions) error {
return err
}
func (ms *MongoStorage) RemoveActions(key string) error {
return ms.db.C(colAct).Remove(bson.M{"key": key})
}
func (ms *MongoStorage) GetSharedGroup(key string, skipCache bool) (sg *SharedGroup, err error) {
if !skipCache {
if x, err := cache2go.Get(utils.SHARED_GROUP_PREFIX + key); err == nil {

View File

@@ -590,6 +590,11 @@ func (rs *RedisStorage) SetActions(key string, as Actions) (err error) {
return
}
func (rs *RedisStorage) RemoveActions(key string) (err error) {
err = rs.db.Cmd("DEL", utils.ACTION_PREFIX+key).Err
return
}
func (rs *RedisStorage) GetSharedGroup(key string, skipCache bool) (sg *SharedGroup, err error) {
key = utils.SHARED_GROUP_PREFIX + key
if !skipCache {

View File

@@ -514,7 +514,7 @@ func (tpr *TpReader) LoadActions() (err error) {
}
}
acts[idx] = &Action{
Id: tag + strconv.Itoa(idx),
Id: tag,
ActionType: tpact.Identifier,
//BalanceType: tpact.BalanceType,
Weight: tpact.Weight,
@@ -990,7 +990,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error
}
}
acts[idx] = &Action{
Id: tag + strconv.Itoa(idx),
Id: tag,
ActionType: tpact.Identifier,
//BalanceType: tpact.BalanceType,
Weight: tpact.Weight,
@@ -1338,7 +1338,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) {
}
}
acts[idx] = &Action{
Id: tag + strconv.Itoa(idx),
Id: tag,
ActionType: tpact.Identifier,
//BalanceType: tpact.BalanceType,
Weight: tpact.Weight,

View File

@@ -8,6 +8,7 @@ import (
"time"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/apier/v2"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
@@ -239,3 +240,128 @@ func TestTpExecuteActionCgrRpc(t *testing.T) {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
}
}
func TestTpCreateExecuteActionMatch(t *testing.T) {
if !*testIntegration {
return
}
var reply string
if err := tpRPC.Call("ApierV2.SetActions", utils.AttrSetActions{
ActionsId: "PAYMENT_2056bd2fe137082970f97102b64e42fd",
Actions: []*utils.TPAction{
&utils.TPAction{
BalanceType: "*monetary",
Directions: "*out",
Identifier: "*topup",
RatingSubject: "",
Units: "10.500000",
Weight: 10,
},
},
}, &reply); err != nil {
t.Error("Got error on ApierV2.SetActions: ", err.Error())
} else if reply != utils.OK {
t.Errorf("Calling ApierV2.SetActions got reply: %s", reply)
}
if err := tpRPC.Call("ApierV2.ExecuteAction", utils.AttrExecuteAction{
Tenant: "cgrates.org",
Account: "1015",
ActionsId: "PAYMENT_2056bd2fe137082970f97102b64e42fd",
}, &reply); err != nil {
t.Error("Got error on ApierV2.ExecuteAction: ", err.Error())
} else if reply != utils.OK {
t.Errorf("Calling ExecuteAction got reply: %s", reply)
}
if err := tpRPC.Call("ApierV2.ExecuteAction", utils.AttrExecuteAction{
Tenant: "cgrates.org",
Account: "1015",
ActionsId: "PAYMENT_2056bd2fe137082970f97102b64e42fd",
}, &reply); err != nil {
t.Error("Got error on ApierV2.ExecuteAction: ", err.Error())
} else if reply != utils.OK {
t.Errorf("Calling ExecuteAction got reply: %s", reply)
}
var acnt engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1015"}
if err := tpRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
}
if len(acnt.BalanceMap) != 1 ||
len(acnt.BalanceMap[utils.MONETARY]) != 1 ||
acnt.BalanceMap[utils.MONETARY].GetTotalValue() != 21 {
t.Error("error matching previous created balance: ", utils.ToIJSON(acnt.BalanceMap))
}
}
func TestTpSetRemActions(t *testing.T) {
if !*testIntegration {
return
}
var reply string
if err := tpRPC.Call("ApierV2.SetActions", utils.AttrSetActions{
ActionsId: "TO_BE_DELETED",
Actions: []*utils.TPAction{
&utils.TPAction{
BalanceType: "*monetary",
Directions: "*out",
Identifier: "*topup",
RatingSubject: "",
Units: "10.500000",
Weight: 10,
},
},
}, &reply); err != nil {
t.Error("Got error on ApierV2.SetActions: ", err.Error())
} else if reply != utils.OK {
t.Errorf("Calling ApierV2.SetActions got reply: %s", reply)
}
actionsMap := make(map[string]engine.Actions)
if err := tpRPC.Call("ApierV2.GetActions", v2.AttrGetActions{
ActionIDs: []string{"PAYMENT_2056bd2fe137082970f97102b64e42fd"},
}, &actionsMap); err != nil {
t.Error("Got error on ApierV2.GetActions: ", err.Error())
} else if len(actionsMap) != 1 {
t.Errorf("Calling ApierV2.GetActions got reply: %s", utils.ToIJSON(actionsMap))
}
if err := tpRPC.Call("ApierV2.RemActions", v1.AttrRemActions{
ActionIDs: []string{"PAYMENT_2056bd2fe137082970f97102b64e42fd"},
}, &reply); err != nil {
t.Error("Got error on ApierV2.RemActions: ", err.Error())
} else if reply != utils.OK {
t.Errorf("Calling ApierV2.RemActions got reply: %s", reply)
}
if err := tpRPC.Call("ApierV2.GetActions", v2.AttrGetActions{
ActionIDs: []string{"PAYMENT_2056bd2fe137082970f97102b64e42fd"},
}, &actionsMap); err == nil {
t.Error("no error on ApierV2.GetActions: ", err)
}
}
func TestTpRemActionsRefenced(t *testing.T) {
if !*testIntegration {
return
}
actionsMap := make(map[string]engine.Actions)
if err := tpRPC.Call("ApierV2.GetActions", v2.AttrGetActions{
ActionIDs: []string{"TOPUP_VOICE"},
}, &actionsMap); err != nil {
t.Error("Got error on ApierV2.GetActions: ", err.Error())
} else if len(actionsMap) != 1 {
t.Errorf("Calling ApierV2.GetActions got reply: %s", utils.ToIJSON(actionsMap))
}
var reply string
if err := tpRPC.Call("ApierV2.RemActions", v1.AttrRemActions{
ActionIDs: []string{"TOPUP_VOICE"},
}, &reply); err == nil {
t.Error("No error on ApierV2.RemActions: ", err.Error())
} else if reply == utils.OK {
t.Errorf("Calling ApierV2.RemActions got reply: %s", reply)
}
if err := tpRPC.Call("ApierV2.GetActions", v2.AttrGetActions{
ActionIDs: []string{"TOPUP_VOICE"},
}, &actionsMap); err != nil {
t.Error("Got error on ApierV2.GetActions: ", err.Error())
} else if len(actionsMap) != 1 {
t.Errorf("Calling ApierV2.GetActions got reply: %s", utils.ToIJSON(actionsMap))
}
}

View File

@@ -212,6 +212,12 @@ func TestSMGDataLastUsedMultipleData(t *testing.T) {
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
aSessions := make([]*ActiveSession, 0)
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
@@ -238,6 +244,11 @@ func TestSMGDataLastUsedMultipleData(t *testing.T) {
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1068576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
@@ -264,7 +275,11 @@ func TestSMGDataLastUsedMultipleData(t *testing.T) {
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1088576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
@@ -291,6 +306,11 @@ func TestSMGDataLastUsedMultipleData(t *testing.T) {
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1108576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
@@ -317,7 +337,11 @@ func TestSMGDataLastUsedMultipleData(t *testing.T) {
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1128576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
@@ -341,6 +365,11 @@ func TestSMGDataLastUsedMultipleData(t *testing.T) {
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 0 {
t.Errorf("wrong active sessions: %+v", aSessions)
}
}
func TestSMGDataDerivedChargingNoCredit(t *testing.T) {
@@ -474,6 +503,12 @@ func TestSMGDataTTLExpiredMultiUpdates(t *testing.T) {
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
aSessions := make([]*ActiveSession, 0)
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
@@ -509,4 +544,410 @@ func TestSMGDataTTLExpiredMultiUpdates(t *testing.T) {
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 0 {
t.Errorf("wrong active sessions: %+v", aSessions)
}
}
func TestSMGDataMultipleDataNoUsage(t *testing.T) {
if !*testIntegration {
return
}
var acnt *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"}
eAcntVal := 49997767680.000000
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
smgEv := SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.SETUP_TIME: "2016-01-05 18:30:49",
utils.ANSWER_TIME: "2016-01-05 18:31:05",
utils.USAGE: "1048576",
}
var maxUsage float64
if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 1.048576e+06 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 49996712960.000000 // 1054720
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
aSessions := make([]*ActiveSession, 0)
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.USAGE: "1048576",
utils.LastUsed: "0",
}
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 1.048576e+06 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 49996712960.000000 // 0
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.USAGE: "1048576",
utils.LastUsed: "0",
}
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 1.048576e+06 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 49996712960.000000 // 0
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.USAGE: "1048576",
utils.LastUsed: "0",
}
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 1.048576e+06 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 49996712960.000000 // 0
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.USAGE: "1048576",
utils.LastUsed: "0",
}
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 1.048576e+06 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 49996712960.000000 // 0
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.LastUsed: "0",
}
var rpl string
if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK {
t.Error(err)
}
eAcntVal = 49997767680.000000 // refunded
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 0 {
t.Errorf("wrong active sessions: %+v", aSessions)
}
}
func TestSMGDataMultipleDataConstantUsage(t *testing.T) {
if !*testIntegration {
return
}
var acnt *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"}
eAcntVal := 49997767680.000000
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
smgEv := SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.SETUP_TIME: "2016-01-05 18:30:49",
utils.ANSWER_TIME: "2016-01-05 18:31:05",
utils.USAGE: "1048576",
}
var maxUsage float64
if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 1.048576e+06 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 49996712960.000000 // 1054720
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
aSessions := make([]*ActiveSession, 0)
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.USAGE: "1048576",
utils.LastUsed: "600",
}
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 1.048576e+06 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 49996712960.000000 // 0
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1049176 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.USAGE: "1048576",
utils.LastUsed: "600",
}
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 1.048576e+06 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 49996712960.000000 // 0
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1049776 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.USAGE: "1048576",
utils.LastUsed: "600",
}
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 1.048576e+06 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 49996712960.000000 // 0
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1050376 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.USAGE: "1048576",
utils.LastUsed: "600",
}
if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil {
t.Error(err)
}
if maxUsage != 1.048576e+06 {
t.Error("Bad max usage: ", maxUsage)
}
eAcntVal = 49996712960.000000 // 0
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1050976 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
smgEv = SMGenericEvent{
utils.EVENT_NAME: "TEST_EVENT",
utils.TOR: utils.DATA,
utils.ACCID: "12349",
utils.DIRECTION: utils.OUT,
utils.ACCOUNT: "1010",
utils.SUBJECT: "1010",
utils.DESTINATION: "222",
utils.CATEGORY: "data",
utils.TENANT: "cgrates.org",
utils.REQTYPE: utils.META_PREPAID,
utils.LastUsed: "0",
}
var rpl string
if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK {
t.Error(err)
}
eAcntVal = 49997757440.000000 // 10240 (from the start)
if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil {
t.Error(err)
} else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal {
t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue())
}
if err := smgRPC.Call("SMGenericV1.ActiveSessions", utils.AttrSMGGetActiveSessions{}, &aSessions); err != nil {
t.Error(err)
} else if len(aSessions) != 0 {
t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds())
}
}

View File

@@ -90,13 +90,15 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed *time.Duration) (time.
// total usage correction
self.totalUsage -= self.lastUsage
self.totalUsage += *lastUsed
//utils.Logger.Debug(fmt.Sprintf("Correction: %f", self.totalUsage.Seconds()))
//utils.Logger.Debug(fmt.Sprintf("TotalUsage Correction: %f", self.totalUsage.Seconds()))
}
}
// apply correction from previous run
if self.extraDuration < dur {
dur -= self.extraDuration
} else {
self.lastUsage = requestedDuration
self.totalUsage += self.lastUsage
ccDuration := self.extraDuration // fake ccDuration
self.extraDuration -= dur
return ccDuration, nil
@@ -111,6 +113,7 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed *time.Duration) (time.
self.cd.DurationIndex += dur
cc := &engine.CallCost{}
if err := self.rater.Call("Responder.MaxDebit", self.cd, cc); err != nil {
self.lastUsage = 0
self.lastDebit = 0
return 0, err
}

View File

@@ -275,6 +275,7 @@ type TPActions struct {
type TPAction struct {
Identifier string // Identifier mapped in the code
BalanceId string // Balance identification string (account scope)
BalanceUuid string // Balance identification string (global scope)
BalanceType string // Type of balance the action will operate on
Directions string // Balance direction
Units string // Number of units to add/deduct

View File

@@ -37,6 +37,10 @@ var ValueFormulas = map[string]valueFormula{
INCREMENTAL: incrementalFormula,
}
func (vf *ValueFormula) String() string {
return ToJSON(vf)
}
func incrementalFormula(params map[string]interface{}) float64 {
// check parameters
unitsInterface, unitsFound := params["Units"]