From 2b778be2a33fe74cfb5cae52874f821fc7ed0e53 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 21 Jan 2016 18:10:44 +0100 Subject: [PATCH 001/199] Diameter Correct usage in case of unauthorized event --- agents/dmtagent.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 651f3b5ae..342350643 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -111,6 +111,9 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro err = self.smg.Call("SMGenericV1.SessionEnd", smgEv, &rpl) } else if ccr.CCRequestType == 4 { err = self.smg.Call("SMGenericV1.ChargeEvent", smgEv, &maxUsage) + if maxUsage == 0 { + smgEv[utils.USAGE] = 0 // For CDR not to debit + } } if self.cgrCfg.DiameterAgentCfg().CreateCDR { if errCdr := self.smg.Call("SMGenericV1.ProcessCdr", smgEv, &rpl); errCdr != nil { From 8167188a9fd2ebadfb5650f3a965010a0d939332 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 22 Jan 2016 13:03:29 +0100 Subject: [PATCH 002/199] nasreq.xml using appid4 instead of 1, test for loading Framed-IP-Address --- agents/dmtagent_it_test.go | 21 +++++++++++++++++ .../samples/dmtagent/diameter_processors.json | 23 +++++++++++++++++++ data/diameter/dict/huawei/nasreq.xml | 2 +- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 3ec68ef73..394387874 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -445,6 +445,27 @@ func TestDmtAgentCdrs(t *testing.T) { } } +func TestDmtAgentDryRun1(t *testing.T) { + if !*testIntegration { + return + } + ccr := diam.NewRequest(diam.CreditControl, 4, nil) + ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("cgrates;1451911932;00082")) + ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) + ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) + ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) + ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("dryrun1")) // Match specific DryRun profile + ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(4)) + ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(0)) + ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) + if _, err := ccr.NewAVP("Framed-IP-Address", avp.Mbit, 0, datatype.UTF8String("10.228.16.4")); err != nil { + t.Error(err) + } + if err := dmtClient.SendMessage(ccr); err != nil { + t.Error(err) + } +} + func TestDmtAgentStopEngine(t *testing.T) { if !*testIntegration { return diff --git a/data/conf/samples/dmtagent/diameter_processors.json b/data/conf/samples/dmtagent/diameter_processors.json index 90d8b611a..ae2f5e53c 100644 --- a/data/conf/samples/dmtagent/diameter_processors.json +++ b/data/conf/samples/dmtagent/diameter_processors.json @@ -2,6 +2,29 @@ "diameter_agent": { "request_processors": [ + { + "id": "dryrun1", // formal identifier of this processor + "dry_run": true, // do not send the events to SMG, just log them + "request_filter": "Service-Context-Id(dryrun1)", // filter requests processed by this processor + "continue_on_success": false, // continue to the next template if executed + "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*sms", "mandatory": true}, + {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, + {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*prepaid", "mandatory": true}, + {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, + {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true}, + {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, + {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, + {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, + {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>SMS-Information>Recipient-Address>Address-Data", "mandatory": true}, + {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "Usage", "field_id": "Usage", "type": "*composed", "value": "Requested-Service-Unit>CC-Time", "mandatory": true}, + ], + "cca_fields":[ // fields returned in CCA + {"tag": "GrantedUnits", "field_id": "Granted-Service-Unit>CC-Time", "type": "*handler", "handler_id": "*cca_usage", "mandatory": true}, + ], + }, { "id": "*default", // formal identifier of this processor "dry_run": false, // do not send the events to SMG, just log them diff --git a/data/diameter/dict/huawei/nasreq.xml b/data/diameter/dict/huawei/nasreq.xml index 347bf7199..97784e691 100644 --- a/data/diameter/dict/huawei/nasreq.xml +++ b/data/diameter/dict/huawei/nasreq.xml @@ -1,6 +1,6 @@ - + From df883cbe96081b893a093f522fdeac9c20282117 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 22 Jan 2016 13:32:08 +0100 Subject: [PATCH 003/199] Test returned code from DryRun template --- agents/dmtagent_it_test.go | 19 ++++++++++++++++--- .../samples/dmtagent/diameter_processors.json | 6 +----- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 394387874..15b3b0bd0 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -401,9 +401,10 @@ func TestDmtAgentSendCCRSMS(t *testing.T) { if err := dmtClient.SendMessage(ccr); err != nil { t.Error(err) } + + time.Sleep(time.Duration(100) * time.Millisecond) + dmtClient.ReceivedMessage() // Discard the received message so we can test next one /* - time.Sleep(time.Duration(100) * time.Millisecond) - msg := dmtClient.ReceivedMessage() if msg == nil { t.Fatal("No message returned") } @@ -430,7 +431,7 @@ func TestDmtAgentCdrs(t *testing.T) { return } var cdrs []*engine.ExternalCDR - req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}} + req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, ToRs: []string{utils.VOICE}} if err := apierRpc.Call("ApierV2.GetCdrs", req, &cdrs); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(cdrs) != 1 { @@ -464,6 +465,18 @@ func TestDmtAgentDryRun1(t *testing.T) { if err := dmtClient.SendMessage(ccr); err != nil { t.Error(err) } + time.Sleep(time.Duration(100) * time.Millisecond) + msg := dmtClient.ReceivedMessage() + if msg == nil { + t.Fatal("No message returned") + } + if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Result-Code") + } else if strResult := avpValAsString(avps[0]); strResult != "300" { // Result-Code set in the template + t.Errorf("Expecting 200, received: %s", strResult) + } } func TestDmtAgentStopEngine(t *testing.T) { diff --git a/data/conf/samples/dmtagent/diameter_processors.json b/data/conf/samples/dmtagent/diameter_processors.json index ae2f5e53c..4adf3990e 100644 --- a/data/conf/samples/dmtagent/diameter_processors.json +++ b/data/conf/samples/dmtagent/diameter_processors.json @@ -14,15 +14,11 @@ {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true}, {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, - {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, - {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, - {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>SMS-Information>Recipient-Address>Address-Data", "mandatory": true}, {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, - {"tag": "Usage", "field_id": "Usage", "type": "*composed", "value": "Requested-Service-Unit>CC-Time", "mandatory": true}, ], "cca_fields":[ // fields returned in CCA - {"tag": "GrantedUnits", "field_id": "Granted-Service-Unit>CC-Time", "type": "*handler", "handler_id": "*cca_usage", "mandatory": true}, + {"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "300"}, ], }, { From cc5ec5177cffcb33bc4b6017dbbef908472506ac Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 22 Jan 2016 13:58:25 +0100 Subject: [PATCH 004/199] Diameter config, add publish_event and pubsubs options --- config/config_defaults.go | 2 ++ config/config_json_test.go | 2 ++ config/daconfig.go | 8 ++++++++ config/libconfig_json.go | 4 +++- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/config/config_defaults.go b/config/config_defaults.go index 0cedd84f0..930333429 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -267,6 +267,7 @@ const CGRATES_CFG_JSON = ` "listen": "127.0.0.1:3868", // address where to listen for diameter requests "dictionaries_dir": "/usr/share/cgrates/diameter/dict/", // path towards directory holding additional dictionaries to load "sm_generic": "internal", // connection towards SMG component for session management + "pubsubs": "", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234> "create_cdr": true, // create CDR out of CCR terminate and send it to SMG component "debit_interval": "5m", // interval for CCR updates "timezone": "", // timezone for timestamps where not specified, empty for general defaults <""|UTC|Local|$IANA_TZ_DB> @@ -279,6 +280,7 @@ const CGRATES_CFG_JSON = ` { "id": "*default", // formal identifier of this processor "dry_run": false, // do not send the events to SMG, just log them + "publish_event": false, // if enabled, it will publish internal event to pubsub "request_filter": "Subscription-Id>Subscription-Id-Type(0)", // filter requests processed by this processor "continue_on_success": false, // continue to the next template if executed "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value diff --git a/config/config_json_test.go b/config/config_json_test.go index 2548ec5c4..59637b796 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -426,6 +426,7 @@ func TestDiameterAgentJsonCfg(t *testing.T) { Listen: utils.StringPointer("127.0.0.1:3868"), Dictionaries_dir: utils.StringPointer("/usr/share/cgrates/diameter/dict/"), Sm_generic: utils.StringPointer("internal"), + Pubsubs: utils.StringPointer(""), Create_cdr: utils.BoolPointer(true), Debit_interval: utils.StringPointer("5m"), Timezone: utils.StringPointer(""), @@ -438,6 +439,7 @@ func TestDiameterAgentJsonCfg(t *testing.T) { &DARequestProcessorJsnCfg{ Id: utils.StringPointer("*default"), Dry_run: utils.BoolPointer(false), + Publish_event: utils.BoolPointer(false), Request_filter: utils.StringPointer("Subscription-Id>Subscription-Id-Type(0)"), Continue_on_success: utils.BoolPointer(false), CCR_fields: &[]*CdrFieldJsonCfg{ diff --git a/config/daconfig.go b/config/daconfig.go index 13210e376..b56fd73a3 100644 --- a/config/daconfig.go +++ b/config/daconfig.go @@ -29,6 +29,7 @@ type DiameterAgentCfg struct { Listen string // address where to listen for diameter requests DictionariesDir string SMGeneric string // connection towards SMG component + PubSubS string // connection towards pubsubs CreateCDR bool DebitInterval time.Duration Timezone string // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB> @@ -56,6 +57,9 @@ func (self *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg) erro if jsnCfg.Sm_generic != nil { self.SMGeneric = *jsnCfg.Sm_generic } + if jsnCfg.Pubsubs != nil { + self.PubSubS = *jsnCfg.Pubsubs + } if jsnCfg.Create_cdr != nil { self.CreateCDR = *jsnCfg.Create_cdr } @@ -102,6 +106,7 @@ func (self *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg) erro type DARequestProcessor struct { Id string DryRun bool + PublishEvent bool RequestFilter utils.RSRFields ContinueOnSuccess bool CCRFields []*CfgCdrField @@ -118,6 +123,9 @@ func (self *DARequestProcessor) loadFromJsonCfg(jsnCfg *DARequestProcessorJsnCfg if jsnCfg.Dry_run != nil { self.DryRun = *jsnCfg.Dry_run } + if jsnCfg.Publish_event != nil { + self.PublishEvent = *jsnCfg.Publish_event + } var err error if jsnCfg.Request_filter != nil { if self.RequestFilter, err = utils.ParseRSRFields(*jsnCfg.Request_filter, utils.INFIELD_SEP); err != nil { diff --git a/config/libconfig_json.go b/config/libconfig_json.go index fba619846..3facc6945 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -244,7 +244,8 @@ type DiameterAgentJsonCfg struct { Enabled *bool // enables the diameter agent: Listen *string // address where to listen for diameter requests Dictionaries_dir *string // path towards additional dictionaries - Sm_generic *string // Connection towards generic SM + Sm_generic *string // connection towards generic SM + Pubsubs *string // connection towards pubsubs Create_cdr *bool Debit_interval *string Timezone *string // timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB> @@ -260,6 +261,7 @@ type DiameterAgentJsonCfg struct { type DARequestProcessorJsnCfg struct { Id *string Dry_run *bool + Publish_event *bool Request_filter *string Continue_on_success *bool CCR_fields *[]*CdrFieldJsonCfg From a0c4613adc0c60d78fd18f4041a0489783199a0f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 22 Jan 2016 15:29:06 +0200 Subject: [PATCH 005/199] keep action plan account ids or csv reload --- apier/v1/accounts.go | 10 +++++----- apier/v1/apier.go | 2 +- apier/v2/accounts.go | 4 ++-- cmd/cgr-loader/migrator_rc8.go | 4 ++-- engine/action_plan.go | 14 +++++++------- engine/actions_test.go | 28 ++++++++++++++-------------- engine/loader_csv_test.go | 2 +- engine/storage_map.go | 6 ++++++ engine/storage_mongo_datadb.go | 6 ++++++ engine/storage_redis.go | 7 +++++++ engine/tp_reader.go | 27 +++++++++++++++++++++++---- 11 files changed, 74 insertions(+), 36 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index c94a788ca..bc527df95 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -212,9 +212,9 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error } if _, exists := ap.AccountIDs[accID]; !exists { if ap.AccountIDs == nil { - ap.AccountIDs = make(map[string]struct{}) + ap.AccountIDs = make(utils.StringMap) } - ap.AccountIDs[accID] = struct{}{} + ap.AccountIDs[accID] = true schedulerReloadNeeded = true // create tasks for _, at := range ap.ActionTimings { @@ -409,7 +409,7 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { } } at := &engine.ActionTiming{} - at.SetAccountIDs(map[string]struct{}{accID: struct{}{}}) + at.SetAccountIDs(utils.StringMap{accID: true}) aType := engine.DEBIT // reverse the sign as it is a debit @@ -456,7 +456,7 @@ func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) e return utils.ErrNotFound } at := &engine.ActionTiming{} - at.SetAccountIDs(map[string]struct{}{accID: struct{}{}}) + at.SetAccountIDs(utils.StringMap{accID: true}) at.SetActions(engine.Actions{ &engine.Action{ @@ -496,7 +496,7 @@ func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error { } at := &engine.ActionTiming{} - at.SetAccountIDs(map[string]struct{}{accID: struct{}{}}) + at.SetAccountIDs(utils.StringMap{accID: true}) at.SetActions(engine.Actions{ &engine.Action{ ActionType: engine.REMOVE_BALANCE, diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 45fb0ca96..1f5c2ef35 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -107,7 +107,7 @@ func (self *ApierV1) ExecuteAction(attr *utils.AttrExecuteAction, reply *string) at := &engine.ActionTiming{ ActionsID: attr.ActionsId, } - at.SetAccountIDs(map[string]struct{}{accID: struct{}{}}) + at.SetAccountIDs(utils.StringMap{accID: true}) if err := at.Execute(); err != nil { *reply = err.Error() return err diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index d1c49b5fd..16d08f004 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -113,9 +113,9 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { } if _, exists := ap.AccountIDs[accID]; !exists { if ap.AccountIDs == nil { - ap.AccountIDs = make(map[string]struct{}) + ap.AccountIDs = make(utils.StringMap) } - ap.AccountIDs[accID] = struct{}{} + ap.AccountIDs[accID] = true schedulerReloadNeeded = true // create tasks for _, at := range ap.ActionTimings { diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index 08eca3650..25aebd1e0 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -491,14 +491,14 @@ func (mig MigratorRC8) migrateActionPlans() error { if !exists { newApl = &engine.ActionPlan{ Id: apl.Id, - AccountIDs: make(map[string]struct{}), + AccountIDs: make(utils.StringMap), } newAplMap[key] = newApl } if !apl.IsASAP() { for _, accID := range apl.AccountIds { if _, exists := newApl.AccountIDs[accID]; !exists { - newApl.AccountIDs[accID] = struct{}{} + newApl.AccountIDs[accID] = true } } } diff --git a/engine/action_plan.go b/engine/action_plan.go index 163ca4595..23aa88f04 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -37,9 +37,9 @@ type ActionTiming struct { ActionsID string Weight float64 actions Actions - accountIDs map[string]struct{} // copy of action plans accounts - actionPlanID string // the id of the belonging action plan (info only) - stCache time.Time // cached time of the next start + accountIDs utils.StringMap // copy of action plans accounts + actionPlanID string // the id of the belonging action plan (info only) + stCache time.Time // cached time of the next start } type Task struct { @@ -50,7 +50,7 @@ type Task struct { type ActionPlan struct { Id string // informative purpose only - AccountIDs map[string]struct{} + AccountIDs utils.StringMap ActionTimings []*ActionTiming } @@ -65,7 +65,7 @@ func (t *Task) Execute() error { return (&ActionTiming{ Uuid: t.Uuid, ActionsID: t.ActionsID, - accountIDs: map[string]struct{}{t.AccountID: struct{}{}}, + accountIDs: utils.StringMap{t.AccountID: true}, }).Execute() } @@ -250,11 +250,11 @@ func (at *ActionTiming) SetActions(as Actions) { at.actions = as } -func (at *ActionTiming) SetAccountIDs(accIDs map[string]struct{}) { +func (at *ActionTiming) SetAccountIDs(accIDs utils.StringMap) { at.accountIDs = accIDs } -func (at *ActionTiming) GetAccountIDs() map[string]struct{} { +func (at *ActionTiming) GetAccountIDs() utils.StringMap { return at.accountIDs } diff --git a/engine/actions_test.go b/engine/actions_test.go index 604b85447..d2cd20538 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -429,7 +429,7 @@ func TestActionPlanFunctionNotAvailable(t *testing.T) { Balance: &Balance{Value: 1.1}, } at := &ActionTiming{ - accountIDs: map[string]struct{}{"cgrates.org:dy": struct{}{}}, + accountIDs: utils.StringMap{"cgrates.org:dy": true}, Timing: &RateInterval{}, actions: []*Action{a}, } @@ -1058,7 +1058,7 @@ func TestActionPlanLogging(t *testing.T) { }, } at := &ActionTiming{ - accountIDs: map[string]struct{}{"one": struct{}{}, "two": struct{}{}, "three": struct{}{}}, + accountIDs: utils.StringMap{"one": true, "two": true, "three": true}, Timing: i, Weight: 10.0, ActionsID: "TEST_ACTIONS", @@ -1104,7 +1104,7 @@ func TestRemoveAction(t *testing.T) { } at := &ActionTiming{ - accountIDs: map[string]struct{}{"cgrates.org:remo": struct{}{}}, + accountIDs: utils.StringMap{"cgrates.org:remo": true}, actions: Actions{a}, } at.Execute() @@ -1123,7 +1123,7 @@ func TestTopupAction(t *testing.T) { } at := &ActionTiming{ - accountIDs: map[string]struct{}{"vdf:minu": struct{}{}}, + accountIDs: utils.StringMap{"vdf:minu": true}, actions: Actions{a}, } @@ -1145,7 +1145,7 @@ func TestTopupActionLoaded(t *testing.T) { } at := &ActionTiming{ - accountIDs: map[string]struct{}{"vdf:minitsboy": struct{}{}}, + accountIDs: utils.StringMap{"vdf:minitsboy": true}, actions: Actions{a}, } @@ -1297,7 +1297,7 @@ func TestActionTransactionFuncType(t *testing.T) { t.Error("Error setting account: ", err) } at := &ActionTiming{ - accountIDs: map[string]struct{}{"cgrates.org:trans": struct{}{}}, + accountIDs: utils.StringMap{"cgrates.org:trans": true}, Timing: &RateInterval{}, actions: []*Action{ &Action{ @@ -1335,7 +1335,7 @@ func TestActionTransactionBalanceType(t *testing.T) { t.Error("Error setting account: ", err) } at := &ActionTiming{ - accountIDs: map[string]struct{}{"cgrates.org:trans": struct{}{}}, + accountIDs: utils.StringMap{"cgrates.org:trans": true}, Timing: &RateInterval{}, actions: []*Action{ &Action{ @@ -1373,7 +1373,7 @@ func TestActionWithExpireWithoutExpire(t *testing.T) { t.Error("Error setting account: ", err) } at := &ActionTiming{ - accountIDs: map[string]struct{}{"cgrates.org:exp": struct{}{}}, + accountIDs: utils.StringMap{"cgrates.org:exp": true}, Timing: &RateInterval{}, actions: []*Action{ &Action{ @@ -1428,7 +1428,7 @@ func TestActionRemoveBalance(t *testing.T) { t.Error("Error setting account: ", err) } at := &ActionTiming{ - accountIDs: map[string]struct{}{"cgrates.org:rembal": struct{}{}}, + accountIDs: utils.StringMap{"cgrates.org:rembal": true}, Timing: &RateInterval{}, actions: []*Action{ &Action{ @@ -1486,7 +1486,7 @@ func TestActionTransferMonetaryDefault(t *testing.T) { } at := &ActionTiming{ - accountIDs: map[string]struct{}{"cgrates.org:trans": struct{}{}}, + accountIDs: utils.StringMap{"cgrates.org:trans": true}, actions: Actions{a}, } at.Execute() @@ -1547,7 +1547,7 @@ func TestActionTransferMonetaryDefaultFilter(t *testing.T) { } at := &ActionTiming{ - accountIDs: map[string]struct{}{"cgrates.org:trans": struct{}{}}, + accountIDs: utils.StringMap{"cgrates.org:trans": true}, actions: Actions{a}, } at.Execute() @@ -1613,7 +1613,7 @@ func TestActionConditionalTopup(t *testing.T) { } at := &ActionTiming{ - accountIDs: map[string]struct{}{"cgrates.org:cond": struct{}{}}, + accountIDs: utils.StringMap{"cgrates.org:cond": true}, actions: Actions{a}, } at.Execute() @@ -1677,7 +1677,7 @@ func TestActionConditionalTopupNoMatch(t *testing.T) { } at := &ActionTiming{ - accountIDs: map[string]struct{}{"cgrates.org:cond": struct{}{}}, + accountIDs: utils.StringMap{"cgrates.org:cond": true}, actions: Actions{a}, } at.Execute() @@ -1741,7 +1741,7 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) { } at := &ActionTiming{ - accountIDs: map[string]struct{}{"cgrates.org:cond": struct{}{}}, + accountIDs: utils.StringMap{"cgrates.org:cond": true}, actions: Actions{a}, } at.Execute() diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index c13bac58d..827303a6e 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -999,7 +999,7 @@ func TestLoadActionTimings(t *testing.T) { atm := csvr.actionPlans["MORE_MINUTES"] expected := &ActionPlan{ Id: "MORE_MINUTES", - AccountIDs: map[string]struct{}{"vdf:minitsboy": struct{}{}}, + AccountIDs: utils.StringMap{"vdf:minitsboy": true}, ActionTimings: []*ActionTiming{ &ActionTiming{ Uuid: atm.ActionTimings[0].Uuid, diff --git a/engine/storage_map.go b/engine/storage_map.go index a362e1e01..82dca2f51 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -671,6 +671,12 @@ func (ms *MapStorage) SetActionPlan(key string, ats *ActionPlan) (err error) { cache2go.RemKey(utils.ACTION_PLAN_PREFIX + key) return } + // get existing action plan to merge the account ids + if existingAts, _ := ms.GetActionPlan(utils.ACTION_PLAN_PREFIX, true); existingAts != nil { + for accID := range existingAts.AccountIDs { + ats.AccountIDs[accID] = true + } + } result, err := ms.ms.Marshal(&ats) ms.dict[utils.ACTION_PLAN_PREFIX+key] = result return diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index b77313b59..5c78ee184 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -1166,6 +1166,12 @@ func (ms *MongoStorage) SetActionPlan(key string, ats *ActionPlan) error { } return nil } + // get existing action plan to merge the account ids + if existingAts, _ := ms.GetActionPlan(utils.ACTION_PLAN_PREFIX, true); existingAts != nil { + for accID := range existingAts.AccountIDs { + ats.AccountIDs[accID] = true + } + } result, err := ms.ms.Marshal(ats) if err != nil { return err diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 0eb83c93c..2337d62c5 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -927,6 +927,13 @@ func (rs *RedisStorage) SetActionPlan(key string, ats *ActionPlan) (err error) { cache2go.RemKey(utils.ACTION_PLAN_PREFIX + key) return err } + // get existing action plan to merge the account ids + if existingAts, _ := rs.GetActionPlan(utils.ACTION_PLAN_PREFIX, true); existingAts != nil { + for accID := range existingAts.AccountIDs { + ats.AccountIDs[accID] = true + } + } + result, err := rs.ms.Marshal(ats) if err != nil { return err diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 517606d95..9b019074e 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -9,6 +9,7 @@ import ( "time" "github.com/cgrates/cgrates/utils" + "github.com/cgrates/structmatcher" ) type TpReader struct { @@ -506,6 +507,12 @@ func (tpr *TpReader) LoadActions() (err error) { for tag, tpacts := range storActs { acts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { + // check filter field + if len(tpact.Filter) > 0 { + if _, err := structmatcher.NewStructMatcher(tpact.Filter); err != nil { + return fmt.Errorf("error parsing action %s filter field: %v", tag, err) + } + } acts[idx] = &Action{ Id: tag + strconv.Itoa(idx), ActionType: tpact.Identifier, @@ -671,7 +678,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error // action timings if accountAction.ActionPlanId != "" { // get old userBalanceIds - exitingAccountIds := make(map[string]struct{}) + exitingAccountIds := make(utils.StringMap) existingActionPlan, err := tpr.ratingStorage.GetActionPlan(accountAction.ActionPlanId, true) if err == nil && existingActionPlan != nil { exitingAccountIds = existingActionPlan.AccountIDs @@ -733,7 +740,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error }) // collect action ids from timings actionsIds = append(actionsIds, at.ActionsId) - exitingAccountIds[id] = struct{}{} + exitingAccountIds[id] = true actionPlan.AccountIDs = exitingAccountIds } @@ -831,6 +838,12 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error for tag, tpacts := range as { enacts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { + // check filter field + if len(tpact.Filter) > 0 { + if _, err := structmatcher.NewStructMatcher(tpact.Filter); err != nil { + return fmt.Errorf("error parsing action %s filter field: %v", tag, err) + } + } enacts[idx] = &Action{ Id: tag + strconv.Itoa(idx), ActionType: tpact.Identifier, @@ -915,9 +928,9 @@ func (tpr *TpReader) LoadAccountActions() (err error) { // must not continue here } if actionPlan.AccountIDs == nil { - actionPlan.AccountIDs = make(map[string]struct{}) + actionPlan.AccountIDs = make(utils.StringMap) } - actionPlan.AccountIDs[aa.KeyId()] = struct{}{} + actionPlan.AccountIDs[aa.KeyId()] = true } return nil } @@ -1061,6 +1074,12 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { for tag, tpacts := range as { enacts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { + // check filter field + if len(tpact.Filter) > 0 { + if _, err := structmatcher.NewStructMatcher(tpact.Filter); err != nil { + return fmt.Errorf("error parsing action %s filter field: %v", tag, err) + } + } enacts[idx] = &Action{ Id: tag + strconv.Itoa(idx), ActionType: tpact.Identifier, From 2e2edcf23337e46ede0c9cea9535b473432c4b76 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 22 Jan 2016 15:47:42 +0200 Subject: [PATCH 006/199] fixes for keeping account ids --- engine/storage_map.go | 5 ++++- engine/storage_mongo_datadb.go | 5 ++++- engine/storage_redis.go | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/engine/storage_map.go b/engine/storage_map.go index 82dca2f51..84f5547cf 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -672,7 +672,10 @@ func (ms *MapStorage) SetActionPlan(key string, ats *ActionPlan) (err error) { return } // get existing action plan to merge the account ids - if existingAts, _ := ms.GetActionPlan(utils.ACTION_PLAN_PREFIX, true); existingAts != nil { + if existingAts, _ := ms.GetActionPlan(key, true); existingAts != nil { + if ats.AccountIDs == nil && len(existingAts.AccountIDs) > 0 { + ats.AccountIDs = make(utils.StringMap) + } for accID := range existingAts.AccountIDs { ats.AccountIDs[accID] = true } diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index 5c78ee184..32dd40197 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -1167,7 +1167,10 @@ func (ms *MongoStorage) SetActionPlan(key string, ats *ActionPlan) error { return nil } // get existing action plan to merge the account ids - if existingAts, _ := ms.GetActionPlan(utils.ACTION_PLAN_PREFIX, true); existingAts != nil { + if existingAts, _ := ms.GetActionPlan(key, true); existingAts != nil { + if ats.AccountIDs == nil && len(existingAts.AccountIDs) > 0 { + ats.AccountIDs = make(utils.StringMap) + } for accID := range existingAts.AccountIDs { ats.AccountIDs[accID] = true } diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 2337d62c5..9ea09ccfc 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -928,7 +928,10 @@ func (rs *RedisStorage) SetActionPlan(key string, ats *ActionPlan) (err error) { return err } // get existing action plan to merge the account ids - if existingAts, _ := rs.GetActionPlan(utils.ACTION_PLAN_PREFIX, true); existingAts != nil { + if existingAts, _ := rs.GetActionPlan(key, true); existingAts != nil { + if ats.AccountIDs == nil && len(existingAts.AccountIDs) > 0 { + ats.AccountIDs = make(utils.StringMap) + } for accID := range existingAts.AccountIDs { ats.AccountIDs[accID] = true } From 5b828ec98f3bb68c304faec917c3fb9959536dff Mon Sep 17 00:00:00 2001 From: DanB Date: Sun, 24 Jan 2016 16:04:10 +0100 Subject: [PATCH 007/199] Diameter event publishing, fixes #263 --- agents/dmtagent.go | 21 +++++++++++++--- cmd/cgr-engine/cgr-engine.go | 46 ++++++++++++++++++++--------------- cmd/cgr-engine/rater.go | 7 +++--- engine/calldesc.go | 7 +++--- engine/cdrs.go | 5 ++-- engine/pubsub.go | 47 ++++++++++++++++++++++++++++++++++++ sessionmanager/smg_event.go | 14 +++++++++++ utils/coreutils.go | 29 ++++++++++++++++++++++ utils/utils_test.go | 21 ++++++++++++++++ 9 files changed, 166 insertions(+), 31 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 342350643..f111412f8 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -30,8 +30,8 @@ import ( "github.com/fiorix/go-diameter/diam/sm" ) -func NewDiameterAgent(cgrCfg *config.CGRConfig, smg *rpcclient.RpcClient) (*DiameterAgent, error) { - da := &DiameterAgent{cgrCfg: cgrCfg, smg: smg} +func NewDiameterAgent(cgrCfg *config.CGRConfig, smg *rpcclient.RpcClient, pubsubs *rpcclient.RpcClient) (*DiameterAgent, error) { + da := &DiameterAgent{cgrCfg: cgrCfg, smg: smg, pubsubs: pubsubs} dictsDir := cgrCfg.DiameterAgentCfg().DictionariesDir if len(dictsDir) != 0 { if err := loadDictionaries(dictsDir, "DiameterAgent"); err != nil { @@ -42,8 +42,9 @@ func NewDiameterAgent(cgrCfg *config.CGRConfig, smg *rpcclient.RpcClient) (*Diam } type DiameterAgent struct { - cgrCfg *config.CGRConfig - smg *rpcclient.RpcClient // Connection towards CGR-SMG component + cgrCfg *config.CGRConfig + smg *rpcclient.RpcClient // Connection towards CGR-SMG component + pubsubs *rpcclient.RpcClient // Connection towards CGR-PubSub component } // Creates the message handlers @@ -91,6 +92,18 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro utils.Logger.Err(fmt.Sprintf(" Processing message: %+v AsSMGenericEvent, error: %s", ccr.diamMessage, err)) return cca } + if reqProcessor.PublishEvent && self.pubsubs != nil { + evt, err := smgEv.AsMapStringString() + if err != nil { + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v failed converting SMGEvent to pubsub one, error: %s", ccr.diamMessage, err)) + return nil + } + var reply string + if err := self.pubsubs.Call("PubSubV1.Publish", evt, reply); err != nil { + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v failed publishing event, error: %s", ccr.diamMessage, err)) + return nil + } + } var maxUsage float64 if reqProcessor.DryRun { // DryRun does not send over network utils.Logger.Info(fmt.Sprintf(" SMGenericEvent: %+v", smgEv)) diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 352bd9ba0..a4c54bd3b 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -196,9 +196,9 @@ func startSmGeneric(internalSMGChan chan rpcclient.RpcClientConnection, internal server.BijsonRegisterOnDisconnect(smg_econns.OnClientDisconnect) } -func startDiameterAgent(internalSMGChan chan rpcclient.RpcClientConnection, exitChan chan bool) { +func startDiameterAgent(internalSMGChan, internalPubSubSChan chan rpcclient.RpcClientConnection, exitChan chan bool) { utils.Logger.Info("Starting CGRateS DiameterAgent service.") - var smgConn *rpcclient.RpcClient + var smgConn, pubsubConn *rpcclient.RpcClient var err error if cfg.DiameterAgentCfg().SMGeneric == utils.INTERNAL { smgRpc := <-internalSMGChan @@ -212,7 +212,19 @@ func startDiameterAgent(internalSMGChan chan rpcclient.RpcClientConnection, exit exitChan <- true return } - da, err := agents.NewDiameterAgent(cfg, smgConn) + if cfg.DiameterAgentCfg().PubSubS == utils.INTERNAL { + pubSubRpc := <-internalPubSubSChan + internalPubSubSChan <- pubSubRpc + pubsubConn, err = rpcclient.NewRpcClient("", "", 0, 0, rpcclient.INTERNAL_RPC, pubSubRpc) + } else if len(cfg.DiameterAgentCfg().PubSubS) != 0 { + pubsubConn, err = rpcclient.NewRpcClient("tcp", cfg.DiameterAgentCfg().PubSubS, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil) + } + if err != nil { + utils.Logger.Crit(fmt.Sprintf(" Could not connect to PubSubS: %s", err.Error())) + exitChan <- true + return + } + da, err := agents.NewDiameterAgent(cfg, smgConn, pubsubConn) if err != nil { utils.Logger.Err(fmt.Sprintf(" error: %s!", err)) exitChan <- true @@ -372,7 +384,7 @@ func startSmOpenSIPS(internalRaterChan chan *engine.Responder, cdrDb engine.CdrS } func startCDRS(internalCdrSChan chan *engine.CdrServer, logDb engine.LogStorage, cdrDb engine.CdrStorage, - internalRaterChan chan *engine.Responder, internalPubSubSChan chan engine.PublisherSubscriber, + internalRaterChan chan *engine.Responder, internalPubSubSChan chan rpcclient.RpcClientConnection, internalUserSChan chan engine.UserService, internalAliaseSChan chan engine.AliasService, internalCdrStatSChan chan engine.StatsInterface, server *utils.Server, exitChan chan bool) { utils.Logger.Info("Starting CGRateS CDRS service.") @@ -394,23 +406,19 @@ func startCDRS(internalCdrSChan chan *engine.CdrServer, logDb engine.LogStorage, raterConn = &engine.RPCClientConnector{Client: client} } // Pubsub connection init - var pubSubConn engine.PublisherSubscriber + var pubSubConn rpcclient.RpcClientConnection if cfg.CDRSPubSub == utils.INTERNAL { pubSubs := <-internalPubSubSChan pubSubConn = pubSubs internalPubSubSChan <- pubSubs } else if len(cfg.CDRSPubSub) != 0 { - if cfg.CDRSRater == cfg.CDRSPubSub { - pubSubConn = &engine.ProxyPubSub{Client: client} - } else { - client, err = rpcclient.NewRpcClient("tcp", cfg.CDRSPubSub, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil) - if err != nil { - utils.Logger.Crit(fmt.Sprintf(" Could not connect to pubsub server: %s", err.Error())) - exitChan <- true - return - } - pubSubConn = &engine.ProxyPubSub{Client: client} + client, err = rpcclient.NewRpcClient("tcp", cfg.CDRSPubSub, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil) + if err != nil { + utils.Logger.Crit(fmt.Sprintf(" Could not connect to pubsub server: %s", err.Error())) + exitChan <- true + return } + pubSubConn = client } // Users connection init var usersConn engine.UserService @@ -515,7 +523,7 @@ func startHistoryServer(internalHistorySChan chan history.Scribe, server *utils. internalHistorySChan <- scribeServer } -func startPubSubServer(internalPubSubSChan chan engine.PublisherSubscriber, accountDb engine.AccountingStorage, server *utils.Server) { +func startPubSubServer(internalPubSubSChan chan rpcclient.RpcClientConnection, accountDb engine.AccountingStorage, server *utils.Server) { pubSubServer := engine.NewPubSub(accountDb, cfg.HttpSkipTlsVerify) server.RpcRegisterName("PubSubV1", pubSubServer) internalPubSubSChan <- pubSubServer @@ -548,7 +556,7 @@ func startRpc(server *utils.Server, internalRaterChan chan *engine.Responder, internalCdrSChan chan *engine.CdrServer, internalCdrStatSChan chan engine.StatsInterface, internalHistorySChan chan history.Scribe, - internalPubSubSChan chan engine.PublisherSubscriber, + internalPubSubSChan chan rpcclient.RpcClientConnection, internalUserSChan chan engine.UserService, internalAliaseSChan chan engine.AliasService) { select { // Any of the rpc methods will unlock listening to rpc requests @@ -674,7 +682,7 @@ func main() { internalCdrSChan := make(chan *engine.CdrServer, 1) internalCdrStatSChan := make(chan engine.StatsInterface, 1) internalHistorySChan := make(chan history.Scribe, 1) - internalPubSubSChan := make(chan engine.PublisherSubscriber, 1) + internalPubSubSChan := make(chan rpcclient.RpcClientConnection, 1) internalUserSChan := make(chan engine.UserService, 1) internalAliaseSChan := make(chan engine.AliasService, 1) internalSMGChan := make(chan rpcclient.RpcClientConnection, 1) @@ -735,7 +743,7 @@ func main() { } if cfg.DiameterAgentCfg().Enabled { - go startDiameterAgent(internalSMGChan, exitChan) + go startDiameterAgent(internalSMGChan, internalPubSubSChan, exitChan) } // Start HistoryS service diff --git a/cmd/cgr-engine/rater.go b/cmd/cgr-engine/rater.go index 0ee787728..41535636d 100644 --- a/cmd/cgr-engine/rater.go +++ b/cmd/cgr-engine/rater.go @@ -29,6 +29,7 @@ import ( "github.com/cgrates/cgrates/history" "github.com/cgrates/cgrates/scheduler" "github.com/cgrates/cgrates/utils" + "github.com/cgrates/rpcclient" ) func startBalancer(internalBalancerChan chan *balancer2go.Balancer, stopHandled *bool, exitChan chan bool) { @@ -41,7 +42,7 @@ func startBalancer(internalBalancerChan chan *balancer2go.Balancer, stopHandled // Starts rater and reports on chan func startRater(internalRaterChan chan *engine.Responder, cacheDoneChan chan struct{}, internalBalancerChan chan *balancer2go.Balancer, internalSchedulerChan chan *scheduler.Scheduler, internalCdrStatSChan chan engine.StatsInterface, internalHistorySChan chan history.Scribe, - internalPubSubSChan chan engine.PublisherSubscriber, internalUserSChan chan engine.UserService, internalAliaseSChan chan engine.AliasService, + internalPubSubSChan chan rpcclient.RpcClientConnection, internalUserSChan chan engine.UserService, internalAliaseSChan chan engine.AliasService, server *utils.Server, ratingDb engine.RatingStorage, accountDb engine.AccountingStorage, loadDb engine.LoadStorage, cdrDb engine.CdrStorage, logDb engine.LogStorage, stopHandled *bool, exitChan chan bool) { @@ -163,7 +164,7 @@ func startRater(internalRaterChan chan *engine.Responder, cacheDoneChan chan str waitTasks = append(waitTasks, pubsubTaskChan) go func() { defer close(pubsubTaskChan) - var pubSubServer engine.PublisherSubscriber + var pubSubServer rpcclient.RpcClientConnection if cfg.RaterPubSubServer == utils.INTERNAL { select { case pubSubServer = <-internalPubSubSChan: @@ -173,7 +174,7 @@ func startRater(internalRaterChan chan *engine.Responder, cacheDoneChan chan str exitChan <- true return } - } else if pubSubServer, err = engine.NewProxyPubSub(cfg.RaterPubSubServer, cfg.ConnectAttempts, -1); err != nil { + } else if pubSubServer, err = rpcclient.NewRpcClient("tcp", cfg.RaterPubSubServer, cfg.ConnectAttempts, cfg.Reconnects, utils.GOB, nil); err != nil { utils.Logger.Crit(fmt.Sprintf(" Could not connect to pubsubs: %s", err.Error())) exitChan <- true return diff --git a/engine/calldesc.go b/engine/calldesc.go index c38b3be18..2a4ccc69d 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -30,6 +30,7 @@ import ( "github.com/cgrates/cgrates/cache2go" "github.com/cgrates/cgrates/history" "github.com/cgrates/cgrates/utils" + "github.com/cgrates/rpcclient" ) const ( @@ -76,7 +77,7 @@ var ( debitPeriod = 10 * time.Second globalRoundingDecimals = 5 historyScribe history.Scribe - pubSubServer PublisherSubscriber + pubSubServer rpcclient.RpcClientConnection userService UserService aliasService AliasService ) @@ -114,7 +115,7 @@ func SetHistoryScribe(scribe history.Scribe) { historyScribe = scribe } -func SetPubSub(ps PublisherSubscriber) { +func SetPubSub(ps rpcclient.RpcClientConnection) { pubSubServer = ps } @@ -129,7 +130,7 @@ func SetAliasService(as AliasService) { func Publish(event CgrEvent) { if pubSubServer != nil { var s string - pubSubServer.Publish(event, &s) + pubSubServer.Call("PubSubV1.Publish", event, &s) } } diff --git a/engine/cdrs.go b/engine/cdrs.go index 0d11122a3..b522b829c 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -27,6 +27,7 @@ import ( "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" + "github.com/cgrates/rpcclient" "github.com/jinzhu/gorm" mgov2 "gopkg.in/mgo.v2" ) @@ -66,7 +67,7 @@ func fsCdrHandler(w http.ResponseWriter, r *http.Request) { } } -func NewCdrServer(cgrCfg *config.CGRConfig, cdrDb CdrStorage, rater Connector, pubsub PublisherSubscriber, users UserService, aliases AliasService, stats StatsInterface) (*CdrServer, error) { +func NewCdrServer(cgrCfg *config.CGRConfig, cdrDb CdrStorage, rater Connector, pubsub rpcclient.RpcClientConnection, users UserService, aliases AliasService, stats StatsInterface) (*CdrServer, error) { return &CdrServer{cgrCfg: cgrCfg, cdrDb: cdrDb, rater: rater, pubsub: pubsub, users: users, aliases: aliases, stats: stats, guard: &GuardianLock{locksMap: make(map[string]chan bool)}}, nil } @@ -74,7 +75,7 @@ type CdrServer struct { cgrCfg *config.CGRConfig cdrDb CdrStorage rater Connector - pubsub PublisherSubscriber + pubsub rpcclient.RpcClientConnection users UserService aliases AliasService stats StatsInterface diff --git a/engine/pubsub.go b/engine/pubsub.go index 61e609020..2ae544df0 100644 --- a/engine/pubsub.go +++ b/engine/pubsub.go @@ -165,6 +165,53 @@ func (ps *PubSub) ShowSubscribers(in string, out *map[string]*SubscriberData) er return nil } +// rpcclient.RpcClientConnection interface +func (ps *PubSub) Call(serviceMethod string, args interface{}, reply interface{}) error { + switch serviceMethod { + case "PubSubV1.Subscribe": + argsConverted, canConvert := args.(SubscribeInfo) + if !canConvert { + return rpcclient.ErrWrongArgsType + } + replyConverted, canConvert := reply.(*string) + if !canConvert { + return rpcclient.ErrWrongReplyType + } + return ps.Subscribe(argsConverted, replyConverted) + case "PubSubV1.Unsubscribe": + argsConverted, canConvert := args.(SubscribeInfo) + if !canConvert { + return rpcclient.ErrWrongArgsType + } + replyConverted, canConvert := reply.(*string) + if !canConvert { + return rpcclient.ErrWrongReplyType + } + return ps.Unsubscribe(argsConverted, replyConverted) + case "PubSubV1.Publish": + argsConverted, canConvert := args.(CgrEvent) + if !canConvert { + return rpcclient.ErrWrongArgsType + } + replyConverted, canConvert := reply.(*string) + if !canConvert { + return rpcclient.ErrWrongReplyType + } + return ps.Publish(argsConverted, replyConverted) + case "PubSubV1.ShowSubscribers": + argsConverted, canConvert := args.(string) + if !canConvert { + return rpcclient.ErrWrongArgsType + } + replyConverted, canConvert := reply.(*map[string]*SubscriberData) + if !canConvert { + return rpcclient.ErrWrongReplyType + } + return ps.ShowSubscribers(argsConverted, replyConverted) + } + return rpcclient.ErrUnsupporteServiceMethod +} + type ProxyPubSub struct { Client *rpcclient.RpcClient } diff --git a/sessionmanager/smg_event.go b/sessionmanager/smg_event.go index c775c45a1..5245e9906 100644 --- a/sessionmanager/smg_event.go +++ b/sessionmanager/smg_event.go @@ -20,6 +20,7 @@ package sessionmanager import ( "encoding/json" + "fmt" "strconv" "time" @@ -348,3 +349,16 @@ func (self SMGenericEvent) AsLcrRequest() *engine.LcrRequest { Duration: usageStr, } } + +// AsMapStringString Converts into map[string]string, used for example as pubsub event +func (self SMGenericEvent) AsMapStringString() (map[string]string, error) { + mp := make(map[string]string) + for k, v := range self { + if strV, casts := utils.CastIfToString(v); !casts { + return nil, fmt.Errorf("Value %+v does not cast to string", v) + } else { + mp[k] = strV + } + } + return mp, nil +} diff --git a/utils/coreutils.go b/utils/coreutils.go index 9646d6e1e..549fa97ed 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -474,3 +474,32 @@ func FmtFieldWidth(source string, width int, strip, padding string, mandatory bo } return source, nil } + +// Returns the string representation of iface or error if not convertible +func CastIfToString(iface interface{}) (strVal string, casts bool) { + switch iface.(type) { + case string: + strVal = iface.(string) + casts = true + case int: + strVal = strconv.Itoa(iface.(int)) + casts = true + case int64: + strVal = strconv.FormatInt(iface.(int64), 10) + casts = true + case float64: + strVal = strconv.FormatFloat(iface.(float64), 'f', -1, 64) + casts = true + case bool: + strVal = strconv.FormatBool(iface.(bool)) + casts = true + case []uint8: + var byteVal []byte + if byteVal, casts = iface.([]byte); casts { + strVal = string(byteVal) + } + default: // Maybe we are lucky and the value converts to string + strVal, casts = iface.(string) + } + return strVal, casts +} diff --git a/utils/utils_test.go b/utils/utils_test.go index 1f5951c07..6c66c8a0d 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -579,3 +579,24 @@ func TestPaddingNotAllowed(t *testing.T) { t.Error("Expected error") } } + +func TestCastIfToString(t *testing.T) { + v := interface{}("somestr") + if sOut, casts := CastIfToString(v); !casts { + t.Error("Does not cast") + } else if sOut != "somestr" { + t.Errorf("Received: %+v", sOut) + } + v = interface{}(1) + if sOut, casts := CastIfToString(v); !casts { + t.Error("Does not cast") + } else if sOut != "1" { + t.Errorf("Received: %+v", sOut) + } + v = interface{}(1.2) + if sOut, casts := CastIfToString(v); !casts { + t.Error("Does not cast") + } else if sOut != "1.2" { + t.Errorf("Received: %+v", sOut) + } +} From ccda93af48868821c4d9b253d96c9a41926ec5e3 Mon Sep 17 00:00:00 2001 From: DanB Date: Sun, 24 Jan 2016 18:11:01 +0100 Subject: [PATCH 008/199] Diameter - Fix pubsub issue --- agents/dmtagent.go | 3 ++- agents/dmtagent_it_test.go | 2 +- data/conf/samples/dmtagent/cgrates.json | 1 + .../samples/dmtagent/diameter_processors.json | 20 +++++++++++++++++++ 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index f111412f8..dec21d553 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -23,6 +23,7 @@ import ( "strconv" "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" "github.com/cgrates/rpcclient" "github.com/fiorix/go-diameter/diam" @@ -99,7 +100,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro return nil } var reply string - if err := self.pubsubs.Call("PubSubV1.Publish", evt, reply); err != nil { + if err := self.pubsubs.Call("PubSubV1.Publish", engine.CgrEvent(evt), &reply); err != nil { utils.Logger.Err(fmt.Sprintf(" Processing message: %+v failed publishing event, error: %s", ccr.diamMessage, err)) return nil } diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 15b3b0bd0..25f7de3a0 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -455,7 +455,7 @@ func TestDmtAgentDryRun1(t *testing.T) { ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("dryrun1")) // Match specific DryRun profile + ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("pubsub1")) // Match specific DryRun profile ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(4)) ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(0)) ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) diff --git a/data/conf/samples/dmtagent/cgrates.json b/data/conf/samples/dmtagent/cgrates.json index ff1387e85..93fe0df45 100644 --- a/data/conf/samples/dmtagent/cgrates.json +++ b/data/conf/samples/dmtagent/cgrates.json @@ -53,6 +53,7 @@ "diameter_agent": { "enabled": true, + "pubsubs": "internal", }, } diff --git a/data/conf/samples/dmtagent/diameter_processors.json b/data/conf/samples/dmtagent/diameter_processors.json index 4adf3990e..465dba558 100644 --- a/data/conf/samples/dmtagent/diameter_processors.json +++ b/data/conf/samples/dmtagent/diameter_processors.json @@ -21,6 +21,26 @@ {"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "300"}, ], }, + { + "id": "pubsub1", // formal identifier of this processor + "dry_run": true, // do not send the events to SMG, just log them + "publish_event": true, + "request_filter": "Service-Context-Id(pubsub1)", // filter requests processed by this processor + "continue_on_success": false, // continue to the next template if executed + "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*sms", "mandatory": true}, + {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, + {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*prepaid", "mandatory": true}, + {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, + {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true}, + {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, + {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + ], + "cca_fields":[ // fields returned in CCA + {"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "300"}, + ], + }, { "id": "*default", // formal identifier of this processor "dry_run": false, // do not send the events to SMG, just log them From fa1b866931684ff72188f6a93e9479c072c503c3 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 22 Jan 2016 20:50:01 +0200 Subject: [PATCH 009/199] first version of set balance api --- apier/v1/accounts.go | 73 +++++++++-- console/{balance_debit.go => balance_add.go} | 41 +++---- console/balance_remove.go | 65 ++++++++++ console/balance_set.go | 16 +-- engine/account.go | 120 +++++++++++++++---- engine/action.go | 17 ++- engine/actions_test.go | 69 +++++++++++ 7 files changed, 337 insertions(+), 64 deletions(-) rename console/{balance_debit.go => balance_add.go} (51%) create mode 100644 console/balance_remove.go diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index bc527df95..f4b1a1fff 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -388,10 +388,14 @@ type AttrAddBalance struct { Weight float64 SharedGroups string Overwrite bool // When true it will reset if the balance is already there + Blocker bool Disabled bool } func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { + if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() @@ -411,12 +415,9 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { at := &engine.ActionTiming{} at.SetAccountIDs(utils.StringMap{accID: true}) - aType := engine.DEBIT - // reverse the sign as it is a debit - attr.Value = -attr.Value - + aType := engine.TOPUP if attr.Overwrite { - aType = engine.DEBIT_RESET + aType = engine.TOPUP_RESET } at.SetActions(engine.Actions{ &engine.Action{ @@ -433,6 +434,7 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { Categories: utils.ParseStringMap(attr.Categories), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), + Blocker: attr.Blocker, Disabled: attr.Disabled, }, }, @@ -446,7 +448,10 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { } func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) error { - expTime, err := utils.ParseDate(attr.ExpiryTime) + if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() return err @@ -468,10 +473,12 @@ func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) e Value: attr.Value, ExpirationDate: expTime, RatingSubject: attr.RatingSubject, + Categories: utils.ParseStringMap(attr.Categories), Directions: utils.ParseStringMap(attr.Directions), DestinationIds: utils.ParseStringMap(attr.DestinationIds), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), + Blocker: attr.Blocker, Disabled: attr.Disabled, }, }, @@ -485,7 +492,10 @@ func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) e } func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error { - expTime, err := utils.ParseDate(attr.ExpiryTime) + if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() return err @@ -509,8 +519,57 @@ func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error { RatingSubject: attr.RatingSubject, Directions: utils.ParseStringMap(attr.Directions), DestinationIds: utils.ParseStringMap(attr.DestinationIds), + Categories: utils.ParseStringMap(attr.Categories), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), + Blocker: attr.Blocker, + Disabled: attr.Disabled, + }, + }, + }) + if err := at.Execute(); err != nil { + *reply = err.Error() + return err + } + *reply = OK + return nil +} + +func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { + if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if attr.BalanceId == "" && attr.BalanceUuid == "" { + return utils.NewErrMandatoryIeMissing("BalanceId", "or", "BalanceUuid") + } + expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) + if err != nil { + *reply = err.Error() + return err + } + accID := utils.ConcatenatedKey(attr.Tenant, attr.Account) + if _, err := self.AccountDb.GetAccount(accID); err != nil { + return utils.ErrNotFound + } + at := &engine.ActionTiming{} + at.SetAccountIDs(utils.StringMap{accID: true}) + + at.SetActions(engine.Actions{ + &engine.Action{ + ActionType: engine.SET_BALANCE, + BalanceType: attr.BalanceType, + Balance: &engine.Balance{ + Uuid: attr.BalanceUuid, + Id: attr.BalanceId, + Value: attr.Value, + ExpirationDate: expTime, + RatingSubject: attr.RatingSubject, + Directions: utils.ParseStringMap(attr.Directions), + DestinationIds: utils.ParseStringMap(attr.DestinationIds), + Categories: utils.ParseStringMap(attr.Categories), + Weight: attr.Weight, + SharedGroups: utils.ParseStringMap(attr.SharedGroups), + Blocker: true, Disabled: attr.Disabled, }, }, diff --git a/console/balance_debit.go b/console/balance_add.go similarity index 51% rename from console/balance_debit.go rename to console/balance_add.go index ab6f45ec8..b60a59380 100644 --- a/console/balance_debit.go +++ b/console/balance_add.go @@ -18,51 +18,48 @@ along with this program. If not, see package console -import "github.com/cgrates/cgrates/engine" +import ( + "github.com/cgrates/cgrates/apier/v1" + "github.com/cgrates/cgrates/utils" +) func init() { - c := &CmdDebitBalance{ - name: "balance_debit", - rpcMethod: "Responder.Debit", - rpcParams: &engine.CallDescriptor{Direction: "*out"}, - clientArgs: []string{"Direction", "Category", "TOR", "Tenant", "Subject", "Account", "Destination", "TimeStart", "TimeEnd", "CallDuration", "FallbackSubject"}, + c := &CmdAddBalance{ + name: "balance_add", + rpcMethod: "ApierV1.AddBalance", } commands[c.Name()] = c c.CommandExecuter = &CommandExecuter{c} } // Commander implementation -type CmdDebitBalance struct { - name string - rpcMethod string - rpcParams *engine.CallDescriptor - clientArgs []string +type CmdAddBalance struct { + name string + rpcMethod string + rpcParams *v1.AttrAddBalance *CommandExecuter } -func (self *CmdDebitBalance) Name() string { +func (self *CmdAddBalance) Name() string { return self.name } -func (self *CmdDebitBalance) RpcMethod() string { +func (self *CmdAddBalance) RpcMethod() string { return self.rpcMethod } -func (self *CmdDebitBalance) RpcParams(reset bool) interface{} { +func (self *CmdAddBalance) RpcParams(reset bool) interface{} { if reset || self.rpcParams == nil { - self.rpcParams = &engine.CallDescriptor{Direction: "*out"} + self.rpcParams = &v1.AttrAddBalance{BalanceType: utils.MONETARY, Overwrite: false} } return self.rpcParams } -func (self *CmdDebitBalance) PostprocessRpcParams() error { +func (self *CmdAddBalance) PostprocessRpcParams() error { return nil } -func (self *CmdDebitBalance) RpcResult() interface{} { - return &engine.CallCost{} -} - -func (self *CmdDebitBalance) ClientArgs() []string { - return self.clientArgs +func (self *CmdAddBalance) RpcResult() interface{} { + var s string + return &s } diff --git a/console/balance_remove.go b/console/balance_remove.go new file mode 100644 index 000000000..b01324f6f --- /dev/null +++ b/console/balance_remove.go @@ -0,0 +1,65 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package console + +import ( + "github.com/cgrates/cgrates/apier/v1" + "github.com/cgrates/cgrates/utils" +) + +func init() { + c := &CmdRemoveBalance{ + name: "balance_remove", + rpcMethod: "ApierV1.RemoveBalances", + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdRemoveBalance struct { + name string + rpcMethod string + rpcParams *v1.AttrAddBalance + *CommandExecuter +} + +func (self *CmdRemoveBalance) Name() string { + return self.name +} + +func (self *CmdRemoveBalance) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdRemoveBalance) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &v1.AttrAddBalance{BalanceType: utils.MONETARY, Overwrite: false} + } + return self.rpcParams +} + +func (self *CmdRemoveBalance) PostprocessRpcParams() error { + return nil +} + +func (self *CmdRemoveBalance) RpcResult() interface{} { + var s string + return &s +} diff --git a/console/balance_set.go b/console/balance_set.go index 89f6a70c5..eee047382 100644 --- a/console/balance_set.go +++ b/console/balance_set.go @@ -24,42 +24,42 @@ import ( ) func init() { - c := &CmdAddBalance{ + c := &CmdSetBalance{ name: "balance_set", - rpcMethod: "ApierV1.AddBalance", + rpcMethod: "ApierV1.SetBalance", } commands[c.Name()] = c c.CommandExecuter = &CommandExecuter{c} } // Commander implementation -type CmdAddBalance struct { +type CmdSetBalance struct { name string rpcMethod string rpcParams *v1.AttrAddBalance *CommandExecuter } -func (self *CmdAddBalance) Name() string { +func (self *CmdSetBalance) Name() string { return self.name } -func (self *CmdAddBalance) RpcMethod() string { +func (self *CmdSetBalance) RpcMethod() string { return self.rpcMethod } -func (self *CmdAddBalance) RpcParams(reset bool) interface{} { +func (self *CmdSetBalance) RpcParams(reset bool) interface{} { if reset || self.rpcParams == nil { self.rpcParams = &v1.AttrAddBalance{BalanceType: utils.MONETARY, Overwrite: false} } return self.rpcParams } -func (self *CmdAddBalance) PostprocessRpcParams() error { +func (self *CmdSetBalance) PostprocessRpcParams() error { return nil } -func (self *CmdAddBalance) RpcResult() interface{} { +func (self *CmdSetBalance) RpcResult() interface{} { var s string return &s } diff --git a/engine/account.go b/engine/account.go index 7ae6b8083..9044639b1 100644 --- a/engine/account.go +++ b/engine/account.go @@ -88,6 +88,83 @@ func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duratio return } +// sets all the fields of the balance +func (ub *Account) setBalanceAction(a *Action) error { + if a == nil { + return errors.New("nil action") + } + bClone := a.Balance.Clone() + if bClone == nil { + return errors.New("nil balance") + } + // load ValueFactor if defined in extra parametrs + if a.ExtraParameters != "" { + vf := ValueFactor{} + err := json.Unmarshal([]byte(a.ExtraParameters), &vf) + if err == nil { + bClone.Factor = vf + } else { + utils.Logger.Warning(fmt.Sprintf("Could load value factor from actions: extra parametrs: %s", a.ExtraParameters)) + } + } + if ub.BalanceMap == nil { + ub.BalanceMap = make(map[string]BalanceChain, 1) + } + found := false + balanceType := a.BalanceType + var previousSharedGroups utils.StringMap // kept for comparison + for _, b := range ub.BalanceMap[balanceType] { + if b.IsExpired() { + continue // just to be safe (cleaned expired balances above) + } + b.account = ub + if b.MatchFilter(a.Balance, false) { // for Id or Uuid only + bClone.Id = b.Id + bClone.Uuid = b.Uuid + previousSharedGroups = b.SharedGroups + *b = *bClone + found = true + break // only set one balance + } + } + // if it is not found then we add it to the list + if !found { + // check if the Id is *default (user trying to create the default balance) + // use only it's value value + if bClone.Id == utils.META_DEFAULT { + bClone = &Balance{ + Id: utils.META_DEFAULT, + Value: bClone.GetValue(), + } + } + bClone.dirty = true // Mark the balance as dirty since we have modified and it should be checked by action triggers + bClone.Uuid = utils.GenUUID() // alway overwrite the uuid for consistency + ub.BalanceMap[balanceType] = append(ub.BalanceMap[balanceType], bClone) + } + if !found || !previousSharedGroups.Equal(bClone.SharedGroups) { + for sgId := range a.Balance.SharedGroups { + // add shared group member + sg, err := ratingStorage.GetSharedGroup(sgId, false) + if err != nil || sg == nil { + //than is problem + utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) + } else { + if _, found := sg.MemberIds[ub.Id]; !found { + // add member and save + if sg.MemberIds == nil { + sg.MemberIds = make(utils.StringMap) + } + sg.MemberIds[ub.Id] = true + ratingStorage.SetSharedGroup(sg) + } + } + } + } + ub.InitCounters() + ub.executeActionTriggers(nil) + return nil +} + // Debits some amount of user's specified balance adding the balance if it does not exists. // Returns the remaining credit in user's balance. func (ub *Account) debitBalanceAction(a *Action, reset bool) error { @@ -102,8 +179,8 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { ub.BalanceMap = make(map[string]BalanceChain, 1) } found := false - id := a.BalanceType - for _, b := range ub.BalanceMap[id] { + balanceType := a.BalanceType + for _, b := range ub.BalanceMap[balanceType] { if b.IsExpired() { continue // just to be safe (cleaned expired balances above) } @@ -113,6 +190,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { b.SetValue(0) } b.SubstractValue(bClone.GetValue()) + b.dirty = true found = true } } @@ -131,9 +209,8 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { } } bClone.dirty = true // Mark the balance as dirty since we have modified and it should be checked by action triggers - if bClone.Uuid == "" { - bClone.Uuid = utils.GenUUID() - } + + bClone.Uuid = utils.GenUUID() // alway overwrite the uuid for consistency // load ValueFactor if defined in extra parametrs if a.ExtraParameters != "" { vf := ValueFactor{} @@ -144,27 +221,28 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { utils.Logger.Warning(fmt.Sprintf("Could load value factor from actions: extra parametrs: %s", a.ExtraParameters)) } } - ub.BalanceMap[id] = append(ub.BalanceMap[id], bClone) - } - for sgId := range a.Balance.SharedGroups { - // add shared group member - sg, err := ratingStorage.GetSharedGroup(sgId, false) - if err != nil || sg == nil { - //than is problem - utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) - } else { - if _, found := sg.MemberIds[ub.Id]; !found { - // add member and save - if sg.MemberIds == nil { - sg.MemberIds = make(utils.StringMap) + ub.BalanceMap[balanceType] = append(ub.BalanceMap[balanceType], bClone) + + for sgId := range a.Balance.SharedGroups { + // add shared group member + sg, err := ratingStorage.GetSharedGroup(sgId, false) + if err != nil || sg == nil { + //than is problem + utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) + } else { + if _, found := sg.MemberIds[ub.Id]; !found { + // add member and save + if sg.MemberIds == nil { + sg.MemberIds = make(utils.StringMap) + } + sg.MemberIds[ub.Id] = true + ratingStorage.SetSharedGroup(sg) } - sg.MemberIds[ub.Id] = true - ratingStorage.SetSharedGroup(sg) } } } ub.executeActionTriggers(nil) - return nil //ub.BalanceMap[id].GetTotalValue() + return nil } func (ub *Account) enableDisableBalanceAction(a *Action) error { diff --git a/engine/action.go b/engine/action.go index 147b176a7..b579482fc 100644 --- a/engine/action.go +++ b/engine/action.go @@ -57,6 +57,7 @@ const ( DENY_NEGATIVE = "*deny_negative" RESET_ACCOUNT = "*reset_account" REMOVE_ACCOUNT = "*remove_account" + SET_BALANCE = "*set_balance" REMOVE_BALANCE = "*remove_balance" TOPUP_RESET = "*topup_reset" TOPUP = "*topup" @@ -135,8 +136,10 @@ func getActionFunc(typ string) (actionTypeFunc, bool) { return removeAccountAction, true case REMOVE_BALANCE: return removeBalanceAction, true + case SET_BALANCE: + return setBalanceAction, true case TRANSFER_MONETARY_DEFAULT: - return transferMonetaryDefault, true + return transferMonetaryDefaultAction, true } return nil, false } @@ -588,11 +591,14 @@ func removeBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac if !found { return utils.ErrNotFound } - // update account in storage - return accountingStorage.SetAccount(ub) + return nil } -func transferMonetaryDefault(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { +func setBalanceAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { + return acc.setBalanceAction(a) +} + +func transferMonetaryDefaultAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { if acc == nil { utils.Logger.Err("*transfer_monetary_default called without account") return utils.ErrAccountNotFound @@ -612,8 +618,7 @@ func transferMonetaryDefault(acc *Account, sq *StatsQueueTriggered, a *Action, a } } } - // update account in storage - return accountingStorage.SetAccount(acc) + return nil } // Structure to store actions according to weight diff --git a/engine/actions_test.go b/engine/actions_test.go index d2cd20538..662da3d65 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -1759,6 +1759,75 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) { } } +func TestActionSetBalance(t *testing.T) { + err := accountingStorage.SetAccount( + &Account{ + Id: "cgrates.org:setb", + BalanceMap: map[string]BalanceChain{ + utils.MONETARY: BalanceChain{ + &Balance{ + Id: "m1", + Uuid: utils.GenUUID(), + Value: 1, + Weight: 10, + }, + &Balance{ + Id: "m2", + Uuid: utils.GenUUID(), + Value: 6, + Weight: 20, + }, + }, + utils.VOICE: BalanceChain{ + &Balance{ + Id: "v1", + Uuid: utils.GenUUID(), + Value: 10, + Weight: 10, + }, + &Balance{ + Id: "v2", + Uuid: utils.GenUUID(), + Value: 100, + Weight: 20, + }, + }, + }, + }) + if err != nil { + t.Errorf("error setting account: %v", err) + } + + a := &Action{ + ActionType: SET_BALANCE, + BalanceType: utils.MONETARY, + Balance: &Balance{ + Id: "m2", + Value: 11, + Weight: 10, + }, + } + + at := &ActionTiming{ + accountIDs: utils.StringMap{"cgrates.org:setb": true}, + actions: Actions{a}, + } + at.Execute() + + afterUb, err := accountingStorage.GetAccount("cgrates.org:setb") + if err != nil { + t.Error("account not found: ", err, afterUb) + } + if len(afterUb.BalanceMap[utils.MONETARY]) != 2 || + afterUb.BalanceMap[utils.MONETARY][1].Value != 11 || + afterUb.BalanceMap[utils.MONETARY][1].Weight != 10 { + for _, b := range afterUb.BalanceMap[utils.MONETARY] { + t.Logf("B: %+v", b) + } + t.Errorf("Balance: %+v", afterUb.BalanceMap[utils.MONETARY][1]) + } +} + /**************** Benchmarks ********************************/ func BenchmarkUUID(b *testing.B) { From 430837ba5b463dded1295f98f4fabec281db9136 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 25 Jan 2016 10:38:29 +0200 Subject: [PATCH 010/199] fix general tests --- general_tests/acntacts_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/general_tests/acntacts_test.go b/general_tests/acntacts_test.go index 572f60180..524e0fb1e 100644 --- a/general_tests/acntacts_test.go +++ b/general_tests/acntacts_test.go @@ -23,6 +23,7 @@ import ( "testing" "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" ) var ratingDbAcntActs engine.RatingStorage @@ -77,7 +78,7 @@ func TestAcntActsDisableAcnt(t *testing.T) { at := &engine.ActionTiming{ ActionsID: "DISABLE_ACNT", } - at.SetAccountIDs(map[string]struct{}{acnt1Tag: struct{}{}}) + at.SetAccountIDs(utils.StringMap{acnt1Tag: true}) if err := at.Execute(); err != nil { t.Error(err) } @@ -94,7 +95,7 @@ func TestAcntActsEnableAcnt(t *testing.T) { at := &engine.ActionTiming{ ActionsID: "ENABLE_ACNT", } - at.SetAccountIDs(map[string]struct{}{acnt1Tag: struct{}{}}) + at.SetAccountIDs(utils.StringMap{acnt1Tag: true}) if err := at.Execute(); err != nil { t.Error(err) } From 9ce16de6f8e71d7335e5845f4ffebbf5097a1fa0 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 25 Jan 2016 15:39:33 +0200 Subject: [PATCH 011/199] SetAccount api --- apier/v1/accounts.go | 160 +++++++++++++++++++++++++++++++++++++++++ console/balance_set.go | 4 +- engine/account.go | 82 ++++++++++++--------- engine/account_test.go | 2 +- engine/action.go | 2 +- engine/balances.go | 4 ++ utils/map.go | 3 + 7 files changed, 220 insertions(+), 37 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index f4b1a1fff..c80e6ff00 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -385,6 +385,7 @@ type AttrAddBalance struct { RatingSubject string Categories string DestinationIds string + TimingIds string Weight float64 SharedGroups string Overwrite bool // When true it will reset if the balance is already there @@ -434,6 +435,7 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { Categories: utils.ParseStringMap(attr.Categories), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), + TimingIDs: utils.ParseStringMap(attr.TimingIds), Blocker: attr.Blocker, Disabled: attr.Disabled, }, @@ -478,6 +480,7 @@ func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) e DestinationIds: utils.ParseStringMap(attr.DestinationIds), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), + TimingIDs: utils.ParseStringMap(attr.TimingIds), Blocker: attr.Blocker, Disabled: attr.Disabled, }, @@ -522,6 +525,7 @@ func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error { Categories: utils.ParseStringMap(attr.Categories), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), + TimingIDs: utils.ParseStringMap(attr.TimingIds), Blocker: attr.Blocker, Disabled: attr.Disabled, }, @@ -535,6 +539,65 @@ func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error { return nil } +type AttrSetBalance struct { + Tenant string + Account string + BalanceType string + BalanceUuid *string + BalanceId *string + Directions *string + Value *float64 + ExpiryTime *string + RatingSubject *string + Categories *string + DestinationIds *string + SharedGroups *string + TimingIds *string + Weight *float64 + Blocker *bool + Disabled *bool + expTime time.Time +} + +func (attr *AttrSetBalance) SetBalance(b *engine.Balance) { + if attr.Directions != nil { + b.Directions = utils.ParseStringMap(*attr.Directions) + } + if attr.Value != nil { + b.Value = *attr.Value + } + if attr.ExpiryTime != nil { + b.ExpirationDate = attr.expTime + } + if attr.RatingSubject != nil { + b.RatingSubject = *attr.RatingSubject + } + if attr.Categories != nil { + b.Categories = utils.ParseStringMap(*attr.Categories) + } + if attr.DestinationIds != nil { + b.DestinationIds = utils.ParseStringMap(*attr.DestinationIds) + } + if attr.SharedGroups != nil { + b.SharedGroups = utils.ParseStringMap(*attr.SharedGroups) + } + if attr.TimingIds != nil { + b.TimingIDs = utils.ParseStringMap(*attr.TimingIds) + } + if attr.Weight != nil { + b.Weight = *attr.Weight + } + if attr.Blocker != nil { + b.Blocker = *attr.Blocker + } + if attr.Disabled != nil { + b.Disabled = *attr.Disabled + } + b.SetDirty() // Mark the balance as dirty since we have modified and it should be checked by action triggers +} + +/* // SetAccount api using action and action timing to set balance, +//to be uncommented when using pointers in action.balance func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) @@ -551,6 +614,7 @@ func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { if _, err := self.AccountDb.GetAccount(accID); err != nil { return utils.ErrNotFound } + at := &engine.ActionTiming{} at.SetAccountIDs(utils.StringMap{accID: true}) @@ -569,6 +633,7 @@ func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { Categories: utils.ParseStringMap(attr.Categories), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), + TimingIDs: utils.ParseStringMap(attr.TimingIds), Blocker: true, Disabled: attr.Disabled, }, @@ -581,3 +646,98 @@ func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { *reply = OK return nil } +*/ + +func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { + if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if (attr.BalanceId == nil || *attr.BalanceId == "") && + (attr.BalanceUuid == nil || *attr.BalanceUuid == "") { + return utils.NewErrMandatoryIeMissing("BalanceId", "or", "BalanceUuid") + } + var err error + if attr.ExpiryTime != nil { + attr.expTime, err = utils.ParseTimeDetectLayout(*attr.ExpiryTime, self.Config.DefaultTimezone) + if err != nil { + *reply = err.Error() + return err + } + } + accID := utils.ConcatenatedKey(attr.Tenant, attr.Account) + _, err = engine.Guardian.Guard(func() (interface{}, error) { + account, err := self.AccountDb.GetAccount(accID) + if err != nil { + return 0, utils.ErrNotFound + } + + if account.BalanceMap == nil { + account.BalanceMap = make(map[string]engine.BalanceChain, 1) + } + var previousSharedGroups utils.StringMap // kept for comparison + var balance *engine.Balance + var found bool + for _, b := range account.BalanceMap[attr.BalanceType] { + if b.IsExpired() { + continue + } + if (attr.BalanceUuid != nil && b.Uuid == *attr.BalanceUuid) || + (attr.BalanceId != nil && b.Id == *attr.BalanceId) { + previousSharedGroups = b.SharedGroups + balance = b + found = true + break // only set one balance + } + } + + // if it is not found then we add it to the list + if balance == nil { + balance := &engine.Balance{} + balance.Uuid = utils.GenUUID() // alway overwrite the uuid for consistency + account.BalanceMap[attr.BalanceType] = append(account.BalanceMap[attr.BalanceType], balance) + } + + if attr.BalanceId != nil && *attr.BalanceId == utils.META_DEFAULT { + balance.Id = utils.META_DEFAULT + if attr.Value != nil { + balance.Value = *attr.Value + } + } else { + attr.SetBalance(balance) + } + + if !found || !previousSharedGroups.Equal(balance.SharedGroups) { + _, err = engine.Guardian.Guard(func() (interface{}, error) { + for sgId := range balance.SharedGroups { + // add shared group member + sg, err := self.RatingDb.GetSharedGroup(sgId, false) + if err != nil || sg == nil { + //than is problem + utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) + } else { + if _, found := sg.MemberIds[account.Id]; !found { + // add member and save + if sg.MemberIds == nil { + sg.MemberIds = make(utils.StringMap) + } + sg.MemberIds[account.Id] = true + self.RatingDb.SetSharedGroup(sg) + } + } + } + return 0, nil + }, 0, balance.SharedGroups.Slice()...) + } + + account.InitCounters() + account.ExecuteActionTriggers(nil) + self.AccountDb.SetAccount(account) + return 0, nil + }, 0, accID) + if err != nil { + *reply = err.Error() + return err + } + *reply = utils.OK + return nil +} diff --git a/console/balance_set.go b/console/balance_set.go index eee047382..bc52e8489 100644 --- a/console/balance_set.go +++ b/console/balance_set.go @@ -36,7 +36,7 @@ func init() { type CmdSetBalance struct { name string rpcMethod string - rpcParams *v1.AttrAddBalance + rpcParams *v1.AttrSetBalance *CommandExecuter } @@ -50,7 +50,7 @@ func (self *CmdSetBalance) RpcMethod() string { func (self *CmdSetBalance) RpcParams(reset bool) interface{} { if reset || self.rpcParams == nil { - self.rpcParams = &v1.AttrAddBalance{BalanceType: utils.MONETARY, Overwrite: false} + self.rpcParams = &v1.AttrSetBalance{BalanceType: utils.MONETARY} } return self.rpcParams } diff --git a/engine/account.go b/engine/account.go index 9044639b1..339ca6c4c 100644 --- a/engine/account.go +++ b/engine/account.go @@ -122,7 +122,11 @@ func (ub *Account) setBalanceAction(a *Action) error { bClone.Id = b.Id bClone.Uuid = b.Uuid previousSharedGroups = b.SharedGroups - *b = *bClone + if bClone.Id != utils.META_DEFAULT { + *b = *bClone + } else { + b.Value = bClone.GetValue() + } found = true break // only set one balance } @@ -141,27 +145,34 @@ func (ub *Account) setBalanceAction(a *Action) error { bClone.Uuid = utils.GenUUID() // alway overwrite the uuid for consistency ub.BalanceMap[balanceType] = append(ub.BalanceMap[balanceType], bClone) } + if !found || !previousSharedGroups.Equal(bClone.SharedGroups) { - for sgId := range a.Balance.SharedGroups { - // add shared group member - sg, err := ratingStorage.GetSharedGroup(sgId, false) - if err != nil || sg == nil { - //than is problem - utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) - } else { - if _, found := sg.MemberIds[ub.Id]; !found { - // add member and save - if sg.MemberIds == nil { - sg.MemberIds = make(utils.StringMap) + _, err := Guardian.Guard(func() (interface{}, error) { + for sgId := range bClone.SharedGroups { + // add shared group member + sg, err := ratingStorage.GetSharedGroup(sgId, false) + if err != nil || sg == nil { + //than is problem + utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) + } else { + if _, found := sg.MemberIds[ub.Id]; !found { + // add member and save + if sg.MemberIds == nil { + sg.MemberIds = make(utils.StringMap) + } + sg.MemberIds[ub.Id] = true + ratingStorage.SetSharedGroup(sg) } - sg.MemberIds[ub.Id] = true - ratingStorage.SetSharedGroup(sg) } } + return 0, nil + }, 0, bClone.SharedGroups.Slice()...) + if err != nil { + return err } } ub.InitCounters() - ub.executeActionTriggers(nil) + ub.ExecuteActionTriggers(nil) return nil } @@ -222,26 +233,31 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { } } ub.BalanceMap[balanceType] = append(ub.BalanceMap[balanceType], bClone) - - for sgId := range a.Balance.SharedGroups { - // add shared group member - sg, err := ratingStorage.GetSharedGroup(sgId, false) - if err != nil || sg == nil { - //than is problem - utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) - } else { - if _, found := sg.MemberIds[ub.Id]; !found { - // add member and save - if sg.MemberIds == nil { - sg.MemberIds = make(utils.StringMap) + _, err := Guardian.Guard(func() (interface{}, error) { + for sgId := range bClone.SharedGroups { + // add shared group member + sg, err := ratingStorage.GetSharedGroup(sgId, false) + if err != nil || sg == nil { + //than is problem + utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) + } else { + if _, found := sg.MemberIds[ub.Id]; !found { + // add member and save + if sg.MemberIds == nil { + sg.MemberIds = make(utils.StringMap) + } + sg.MemberIds[ub.Id] = true + ratingStorage.SetSharedGroup(sg) } - sg.MemberIds[ub.Id] = true - ratingStorage.SetSharedGroup(sg) } } + return 0, nil + }, 0, bClone.SharedGroups.Slice()...) + if err != nil { + return err } } - ub.executeActionTriggers(nil) + ub.ExecuteActionTriggers(nil) return nil } @@ -552,7 +568,7 @@ func (ub *Account) refundIncrement(increment *Increment, cd *CallDescriptor, cou } // Scans the action trigers and execute the actions for which trigger is met -func (ub *Account) executeActionTriggers(a *Action) { +func (ub *Account) ExecuteActionTriggers(a *Action) { ub.ActionTriggers.Sort() for _, at := range ub.ActionTriggers { // sanity check @@ -618,7 +634,7 @@ func (acc *Account) ResetActionTriggers(a *Action) { } at.Executed = false } - acc.executeActionTriggers(a) + acc.ExecuteActionTriggers(a) } // Sets/Unsets recurrent flag for action triggers @@ -634,7 +650,7 @@ func (acc *Account) SetRecurrent(a *Action, recurrent bool) { // Increments the counter for the type func (acc *Account) countUnits(amount float64, kind string, cc *CallCost, b *Balance) { acc.UnitCounters.addUnits(amount, kind, cc, b) - acc.executeActionTriggers(nil) + acc.ExecuteActionTriggers(nil) } // Create counters for all triggered actions diff --git a/engine/account_test.go b/engine/account_test.go index 334836999..577dfc0b8 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -976,7 +976,7 @@ func TestAccountExpActionTrigger(t *testing.T) { &ActionTrigger{ID: "check expired balances", BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } - ub.executeActionTriggers(nil) + ub.ExecuteActionTriggers(nil) if ub.BalanceMap[utils.MONETARY][0].IsExpired() || ub.BalanceMap[utils.MONETARY][0].GetValue() != 10 || // expired was cleaned ub.BalanceMap[utils.VOICE][0].GetValue() != 20 || diff --git a/engine/action.go b/engine/action.go index b579482fc..d5af34654 100644 --- a/engine/action.go +++ b/engine/action.go @@ -57,7 +57,7 @@ const ( DENY_NEGATIVE = "*deny_negative" RESET_ACCOUNT = "*reset_account" REMOVE_ACCOUNT = "*remove_account" - SET_BALANCE = "*set_balance" + SET_BALANCE = "*set_balance" // not ready for production until switching to pointers REMOVE_BALANCE = "*remove_balance" TOPUP_RESET = "*topup_reset" TOPUP = "*topup" diff --git a/engine/balances.go b/engine/balances.go index bc0ad33f8..e1aa16777 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -359,6 +359,10 @@ func (b *Balance) SetValue(amount float64) { b.dirty = true } +func (b *Balance) SetDirty() { + b.dirty = true +} + func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances BalanceChain, count bool, dryRun, debitConnectFee bool) (cc *CallCost, err error) { if !b.IsActiveAt(cd.TimeStart) || b.GetValue() <= 0 { return diff --git a/utils/map.go b/utils/map.go index 6fa89fe0f..6756488b8 100644 --- a/utils/map.go +++ b/utils/map.go @@ -86,6 +86,9 @@ func ParseStringMap(s string) StringMap { } func (sm StringMap) Equal(om StringMap) bool { + if sm == nil && om != nil { + return false + } if len(sm) != len(om) { return false } From 4da254b421fe9944020c2625cb35f4945970cbd3 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 25 Jan 2016 17:00:53 +0200 Subject: [PATCH 012/199] test action filter csv load --- apier/v2/accounts.go | 16 +++++++++------- engine/actions_test.go | 10 ++++++++++ engine/loader_csv_test.go | 3 ++- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index 16d08f004..f16d53f6c 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -79,13 +79,15 @@ func (self *ApierV2) GetAccount(attr *utils.AttrGetAccount, reply *engine.Accoun } type AttrSetAccount struct { - Tenant string - Account string - ActionPlanId string - ActionTriggersId string - AllowNegative *bool - Disabled *bool - ReloadScheduler bool + Tenant string + Account string + ActionPlanIds string + ActionPlansOverwrite bool + ActionTriggersId string + ActionTriggerOverwrite bool + AllowNegative *bool + Disabled *bool + ReloadScheduler bool } func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { diff --git a/engine/actions_test.go b/engine/actions_test.go index 662da3d65..68a693450 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -1828,6 +1828,16 @@ func TestActionSetBalance(t *testing.T) { } } +func TestActionCSVFilter(t *testing.T) { + act, err := ratingStorage.GetActions("FILTER", false) + if err != nil { + t.Error("error getting actions: ", err) + } + if len(act) != 1 || act[0].Filter != `{"Type":"*voice","Value":{"*gte":100}}` { + t.Error("Error loading actions: ", act[0].Filter) + } +} + /**************** Benchmarks ********************************/ func BenchmarkUUID(b *testing.B) { diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 827303a6e..d2e4d605f 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -174,6 +174,7 @@ DEFEE,*cdrlog,"{""Category"":""^ddi"",""MediationRunId"":""^did_run""}",,,,,,,,, NEG,*allow_negative,,,,*monetary,*out,,,,,*unlimited,,0,10,false,false,10 BLOCK,*topup,,,bblocker,*monetary,*out,,NAT,,,*unlimited,,10,20,true,false,20 BLOCK,*topup,,,bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 +FILTER,*topup,,"{""Type"":""*voice"",""Value"":{""*gte"":100}}",bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 ` actionPlans = ` MORE_MINUTES,MINI,ONE_TIME_RUN,10 @@ -814,7 +815,7 @@ func TestLoadRatingProfiles(t *testing.T) { } func TestLoadActions(t *testing.T) { - if len(csvr.actions) != 10 { + if len(csvr.actions) != 11 { t.Error("Failed to load actions: ", len(csvr.actions)) } as1 := csvr.actions["MINI"] From a2e9e156c64a964356c786a4fdfa7fc72b5fd52c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 25 Jan 2016 17:20:13 +0200 Subject: [PATCH 013/199] fix build --- apier/v2/accounts.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index f16d53f6c..48e07e27f 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -83,7 +83,7 @@ type AttrSetAccount struct { Account string ActionPlanIds string ActionPlansOverwrite bool - ActionTriggersId string + ActionTriggersIds string ActionTriggerOverwrite bool AllowNegative *bool Disabled *bool @@ -105,11 +105,11 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { Id: accID, } } - if len(attr.ActionPlanId) != 0 { + if len(attr.ActionPlanIds) != 0 { _, err := engine.Guardian.Guard(func() (interface{}, error) { var ap *engine.ActionPlan var err error - ap, err = self.RatingDb.GetActionPlan(attr.ActionPlanId, false) + ap, err = self.RatingDb.GetActionPlan(attr.ActionPlanIds, false) if err != nil { return 0, err } @@ -132,11 +132,11 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { } } } - if err := self.RatingDb.SetActionPlan(attr.ActionPlanId, ap); err != nil { + if err := self.RatingDb.SetActionPlan(attr.ActionPlanIds, ap); err != nil { return 0, err } // update cache - self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + attr.ActionPlanId}}) + self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + attr.ActionPlanIds}}) } return 0, nil }, 0, utils.ACTION_PLAN_PREFIX) @@ -145,8 +145,8 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { } } - if len(attr.ActionTriggersId) != 0 { - atrs, err := self.RatingDb.GetActionTriggers(attr.ActionTriggersId) + if len(attr.ActionTriggersIds) != 0 { + atrs, err := self.RatingDb.GetActionTriggers(attr.ActionTriggersIds) if err != nil { return 0, err } From 8464f28d1059b6cd759b843c61310e3b4e223478 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 25 Jan 2016 19:58:09 +0200 Subject: [PATCH 014/199] new v2 SetAccount API --- apier/v1/accounts.go | 58 ++++++++++++------------ apier/v2/accounts.go | 95 ++++++++++++++++++++++++++------------- console/account_set.go | 8 ++-- engine/action_trigger.go | 5 +++ engine/actions_test.go | 2 +- engine/loader_csv_test.go | 2 +- utils/map.go | 21 +++++---- 7 files changed, 116 insertions(+), 75 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index c80e6ff00..2290a13db 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -543,16 +543,16 @@ type AttrSetBalance struct { Tenant string Account string BalanceType string - BalanceUuid *string - BalanceId *string - Directions *string + BalanceUUID *string + BalanceID *string + Directions *[]string Value *float64 ExpiryTime *string RatingSubject *string - Categories *string - DestinationIds *string - SharedGroups *string - TimingIds *string + Categories *[]string + DestinationIDs *[]string + SharedGroups *[]string + TimingIDs *[]string Weight *float64 Blocker *bool Disabled *bool @@ -561,7 +561,7 @@ type AttrSetBalance struct { func (attr *AttrSetBalance) SetBalance(b *engine.Balance) { if attr.Directions != nil { - b.Directions = utils.ParseStringMap(*attr.Directions) + b.Directions = utils.StringMapFromSlice(*attr.Directions) } if attr.Value != nil { b.Value = *attr.Value @@ -573,16 +573,16 @@ func (attr *AttrSetBalance) SetBalance(b *engine.Balance) { b.RatingSubject = *attr.RatingSubject } if attr.Categories != nil { - b.Categories = utils.ParseStringMap(*attr.Categories) + b.Categories = utils.StringMapFromSlice(*attr.Categories) } - if attr.DestinationIds != nil { - b.DestinationIds = utils.ParseStringMap(*attr.DestinationIds) + if attr.DestinationIDs != nil { + b.DestinationIds = utils.StringMapFromSlice(*attr.DestinationIDs) } if attr.SharedGroups != nil { - b.SharedGroups = utils.ParseStringMap(*attr.SharedGroups) + b.SharedGroups = utils.StringMapFromSlice(*attr.SharedGroups) } - if attr.TimingIds != nil { - b.TimingIDs = utils.ParseStringMap(*attr.TimingIds) + if attr.TimingIDs != nil { + b.TimingIDs = utils.StringMapFromSlice(*attr.TimingIDs) } if attr.Weight != nil { b.Weight = *attr.Weight @@ -602,8 +602,8 @@ func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - if attr.BalanceId == "" && attr.BalanceUuid == "" { - return utils.NewErrMandatoryIeMissing("BalanceId", "or", "BalanceUuid") + if attr.BalanceID == "" && attr.BalanceUUID == "" { + return utils.NewErrMandatoryIeMissing("BalanceID", "or", "BalanceUUID") } expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) if err != nil { @@ -623,17 +623,17 @@ func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { ActionType: engine.SET_BALANCE, BalanceType: attr.BalanceType, Balance: &engine.Balance{ - Uuid: attr.BalanceUuid, - Id: attr.BalanceId, + Uuuid: attr.BalanceUUID, + ID: attr.BalanceID, Value: attr.Value, ExpirationDate: expTime, RatingSubject: attr.RatingSubject, Directions: utils.ParseStringMap(attr.Directions), - DestinationIds: utils.ParseStringMap(attr.DestinationIds), + DestinationIDs: utils.ParseStringMap(attr.DestinationIDs), Categories: utils.ParseStringMap(attr.Categories), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), - TimingIDs: utils.ParseStringMap(attr.TimingIds), + TimingIDs: utils.ParseStringMap(attr.TimingIDs), Blocker: true, Disabled: attr.Disabled, }, @@ -652,9 +652,9 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - if (attr.BalanceId == nil || *attr.BalanceId == "") && - (attr.BalanceUuid == nil || *attr.BalanceUuid == "") { - return utils.NewErrMandatoryIeMissing("BalanceId", "or", "BalanceUuid") + if (attr.BalanceID == nil || *attr.BalanceID == "") && + (attr.BalanceUUID == nil || *attr.BalanceUUID == "") { + return utils.NewErrMandatoryIeMissing("BalanceID", "or", "BalanceUUID") } var err error if attr.ExpiryTime != nil { @@ -681,8 +681,8 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { if b.IsExpired() { continue } - if (attr.BalanceUuid != nil && b.Uuid == *attr.BalanceUuid) || - (attr.BalanceId != nil && b.Id == *attr.BalanceId) { + if (attr.BalanceUUID != nil && b.Uuid == *attr.BalanceUUID) || + (attr.BalanceID != nil && b.Id == *attr.BalanceID) { previousSharedGroups = b.SharedGroups balance = b found = true @@ -697,7 +697,7 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { account.BalanceMap[attr.BalanceType] = append(account.BalanceMap[attr.BalanceType], balance) } - if attr.BalanceId != nil && *attr.BalanceId == utils.META_DEFAULT { + if attr.BalanceID != nil && *attr.BalanceID == utils.META_DEFAULT { balance.Id = utils.META_DEFAULT if attr.Value != nil { balance.Value = *attr.Value @@ -708,12 +708,12 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { if !found || !previousSharedGroups.Equal(balance.SharedGroups) { _, err = engine.Guardian.Guard(func() (interface{}, error) { - for sgId := range balance.SharedGroups { + for sgID := range balance.SharedGroups { // add shared group member - sg, err := self.RatingDb.GetSharedGroup(sgId, false) + sg, err := self.RatingDb.GetSharedGroup(sgID, false) if err != nil || sg == nil { //than is problem - utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) + utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgID)) } else { if _, found := sg.MemberIds[account.Id]; !found { // add member and save diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index 48e07e27f..d1cd45ed4 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -81,9 +81,9 @@ func (self *ApierV2) GetAccount(attr *utils.AttrGetAccount, reply *engine.Accoun type AttrSetAccount struct { Tenant string Account string - ActionPlanIds string + ActionPlanIds *[]string ActionPlansOverwrite bool - ActionTriggersIds string + ActionTriggersIds *[]string ActionTriggerOverwrite bool AllowNegative *bool Disabled *bool @@ -94,7 +94,7 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - var schedulerReloadNeeded = false + dirtyActionPlans := make(map[string]*engine.ActionPlan) accID := utils.AccountKey(attr.Tenant, attr.Account) var ub *engine.Account _, err := engine.Guardian.Guard(func() (interface{}, error) { @@ -105,38 +105,55 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { Id: accID, } } - if len(attr.ActionPlanIds) != 0 { + if attr.ActionPlanIds != nil { _, err := engine.Guardian.Guard(func() (interface{}, error) { - var ap *engine.ActionPlan - var err error - ap, err = self.RatingDb.GetActionPlan(attr.ActionPlanIds, false) + actionPlansMap, err := self.RatingDb.GetAllActionPlans() if err != nil { return 0, err } - if _, exists := ap.AccountIDs[accID]; !exists { - if ap.AccountIDs == nil { - ap.AccountIDs = make(utils.StringMap) + if attr.ActionPlansOverwrite { + // clean previous action plans + for actionPlanID, ap := range actionPlansMap { + if _, exists := ap.AccountIDs[accID]; exists { + delete(ap.AccountIDs, accID) + dirtyActionPlans[actionPlanID] = ap + } } - ap.AccountIDs[accID] = true - schedulerReloadNeeded = true - // create tasks - for _, at := range ap.ActionTimings { - if at.IsASAP() { - t := &engine.Task{ - Uuid: utils.GenUUID(), - AccountID: accID, - ActionsID: at.ActionsID, - } - if err = self.RatingDb.PushTask(t); err != nil { - return 0, err + } + + for _, actionPlanID := range *attr.ActionPlanIds { + ap, ok := actionPlansMap[actionPlanID] + if !ok { + return 0, utils.ErrNotFound + } + + if _, exists := ap.AccountIDs[accID]; !exists { + if ap.AccountIDs == nil { + ap.AccountIDs = make(utils.StringMap) + } + ap.AccountIDs[accID] = true + dirtyActionPlans[actionPlanID] = ap + // create tasks + for _, at := range ap.ActionTimings { + if at.IsASAP() { + t := &engine.Task{ + Uuid: utils.GenUUID(), + AccountID: accID, + ActionsID: at.ActionsID, + } + if err = self.RatingDb.PushTask(t); err != nil { + return 0, err + } } } } - if err := self.RatingDb.SetActionPlan(attr.ActionPlanIds, ap); err != nil { + } + for actionPlanID, ap := range dirtyActionPlans { + if err := self.RatingDb.SetActionPlan(actionPlanID, ap); err != nil { return 0, err } // update cache - self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + attr.ActionPlanIds}}) + self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + actionPlanID}}) } return 0, nil }, 0, utils.ACTION_PLAN_PREFIX) @@ -145,14 +162,30 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { } } - if len(attr.ActionTriggersIds) != 0 { - atrs, err := self.RatingDb.GetActionTriggers(attr.ActionTriggersIds) - if err != nil { - return 0, err + if attr.ActionTriggersIds != nil { + if attr.ActionTriggerOverwrite { + ub.ActionTriggers = make(engine.ActionTriggers, 0) + } + for _, actionTriggerID := range *attr.ActionTriggersIds { + atrs, err := self.RatingDb.GetActionTriggers(actionTriggerID) + if err != nil { + return 0, err + } + for _, at := range atrs { + var found bool + for _, existingAt := range ub.ActionTriggers { + if existingAt.Equals(at) { + found = true + break + } + } + if !found { + ub.ActionTriggers = append(ub.ActionTriggers, at) + } + } } - ub.ActionTriggers = atrs - ub.InitCounters() } + ub.InitCounters() if attr.AllowNegative != nil { ub.AllowNegative = *attr.AllowNegative } @@ -168,7 +201,7 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { if err != nil { return utils.NewErrServerError(err) } - if attr.ReloadScheduler && schedulerReloadNeeded { + if attr.ReloadScheduler && len(dirtyActionPlans) > 0 { // reload scheduler if self.Sched != nil { self.Sched.Reload(true) diff --git a/console/account_set.go b/console/account_set.go index d81b0c38e..5906d32db 100644 --- a/console/account_set.go +++ b/console/account_set.go @@ -18,12 +18,12 @@ along with this program. If not, see package console -import "github.com/cgrates/cgrates/utils" +import "github.com/cgrates/cgrates/apier/v2" func init() { c := &CmdAddAccount{ name: "account_set", - rpcMethod: "ApierV1.SetAccount", + rpcMethod: "ApierV2.SetAccount", } commands[c.Name()] = c c.CommandExecuter = &CommandExecuter{c} @@ -33,7 +33,7 @@ func init() { type CmdAddAccount struct { name string rpcMethod string - rpcParams *utils.AttrSetAccount + rpcParams *v2.AttrSetAccount *CommandExecuter } @@ -47,7 +47,7 @@ func (self *CmdAddAccount) RpcMethod() string { func (self *CmdAddAccount) RpcParams(reset bool) interface{} { if reset || self.rpcParams == nil { - self.rpcParams = &utils.AttrSetAccount{} + self.rpcParams = &v2.AttrSetAccount{} } return self.rpcParams } diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 9fa9ace1d..658e93590 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -183,6 +183,11 @@ func (at *ActionTrigger) CreateBalance() *Balance { } } +func (at *ActionTrigger) Equals(oat *ActionTrigger) bool { + // ids only + return at.ID == oat.ID && at.UniqueID == oat.UniqueID +} + // Structure to store actions according to weight type ActionTriggers []*ActionTrigger diff --git a/engine/actions_test.go b/engine/actions_test.go index 68a693450..1c6da1a7d 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -1833,7 +1833,7 @@ func TestActionCSVFilter(t *testing.T) { if err != nil { t.Error("error getting actions: ", err) } - if len(act) != 1 || act[0].Filter != `{"Type":"*voice","Value":{"*gte":100}}` { + if len(act) != 1 || act[0].Filter != `{"*and":[{"Value":{"*lt":0}},{"Id":{"*eq":"*default"}}]}` { t.Error("Error loading actions: ", act[0].Filter) } } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index d2e4d605f..885cca250 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -174,7 +174,7 @@ DEFEE,*cdrlog,"{""Category"":""^ddi"",""MediationRunId"":""^did_run""}",,,,,,,,, NEG,*allow_negative,,,,*monetary,*out,,,,,*unlimited,,0,10,false,false,10 BLOCK,*topup,,,bblocker,*monetary,*out,,NAT,,,*unlimited,,10,20,true,false,20 BLOCK,*topup,,,bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 -FILTER,*topup,,"{""Type"":""*voice"",""Value"":{""*gte"":100}}",bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 +FILTER,*topup,,"{""*and"":[{""Value"":{""*lt"":0}},{""Id"":{""*eq"":""*default""}}]}",bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 ` actionPlans = ` MORE_MINUTES,MINI,ONE_TIME_RUN,10 diff --git a/utils/map.go b/utils/map.go index 6756488b8..d303efcaf 100644 --- a/utils/map.go +++ b/utils/map.go @@ -74,15 +74,7 @@ func NewStringMap(s ...string) StringMap { } func ParseStringMap(s string) StringMap { - slice := strings.Split(s, INFIELD_SEP) - result := make(StringMap) - for _, v := range slice { - v = strings.TrimSpace(v) - if v != "" { - result[v] = true - } - } - return result + return StringMapFromSlice(strings.Split(s, INFIELD_SEP)) } func (sm StringMap) Equal(om StringMap) bool { @@ -130,6 +122,17 @@ func (sm StringMap) Slice() []string { return result } +func StringMapFromSlice(s []string) StringMap { + result := make(StringMap) + for _, v := range s { + v = strings.TrimSpace(v) + if v != "" { + result[v] = true + } + } + return result +} + func (sm StringMap) Copy(o StringMap) { for k, v := range o { sm[k] = v From 708a3f8db5ef4c769371c59cec64a1b9c6ae722d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 25 Jan 2016 22:02:33 +0200 Subject: [PATCH 015/199] replay past scheduled actions --- apier/v1/scheduler.go | 86 ++++++++++++++++++++ console/scheduler_execute.go | 63 ++++++++++++++ data/tariffplans/tutorial/AccountActions.csv | 1 + data/tariffplans/tutorial/ActionPlans.csv | 1 + data/tariffplans/tutorial/Actions.csv | 1 + data/tariffplans/tutorial/Timings.csv | 1 + engine/action_plan.go | 21 ++++- scheduler/scheduler.go | 3 +- 8 files changed, 174 insertions(+), 3 deletions(-) create mode 100644 console/scheduler_execute.go diff --git a/apier/v1/scheduler.go b/apier/v1/scheduler.go index a91701204..99ecf443b 100644 --- a/apier/v1/scheduler.go +++ b/apier/v1/scheduler.go @@ -20,9 +20,12 @@ package v1 import ( "errors" + "fmt" + "sort" "strings" "time" + "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" ) @@ -168,3 +171,86 @@ func (self *ApierV1) GetScheduledActions(attrs AttrsGetScheduledActions, reply * *reply = schedActions return nil } + +type AttrsExecuteScheduledActions struct { + ActionPlanID string + TimeStart, TimeEnd time.Time // replay the action timings between the two dates +} + +func (self *ApierV1) ExecuteScheduledActions(attr AttrsExecuteScheduledActions, reply *string) error { + if attr.ActionPlanID != "" { // execute by ActionPlanID + apl, err := self.RatingDb.GetActionPlan(attr.ActionPlanID, false) + if err != nil { + *reply = err.Error() + return err + } + if apl != nil { + // order by weight + engine.ActionTimingWeightOnlyPriorityList(apl.ActionTimings).Sort() + for _, at := range apl.ActionTimings { + if at.IsASAP() { + continue + } + + at.SetAccountIDs(apl.AccountIDs) // copy the accounts + at.SetActionPlanID(apl.Id) + go at.Execute() + utils.Logger.Info(fmt.Sprintf(" Executing action %s ", at.ActionsID)) + } + } + } + if !attr.TimeStart.IsZero() && !attr.TimeEnd.IsZero() { // execute between two dates + actionPlans, err := self.RatingDb.GetAllActionPlans() + if err != nil && err != utils.ErrNotFound { + err := fmt.Errorf("cannot get action plans: %v", err) + *reply = err.Error() + return err + } + + // recreate the queue + queue := engine.ActionTimingPriorityList{} + for _, actionPlan := range actionPlans { + for _, at := range actionPlan.ActionTimings { + if at.Timing == nil { + continue + } + if at.IsASAP() { + continue + } + if at.GetNextStartTime(attr.TimeStart).Before(attr.TimeStart) { + // the task is obsolete, do not add it to the queue + continue + } + at.SetAccountIDs(actionPlan.AccountIDs) // copy the accounts + at.SetActionPlanID(actionPlan.Id) + at.ResetStartTimeCache() + queue = append(queue, at) + } + } + sort.Sort(queue) + // start playback execution loop + current := attr.TimeStart + for len(queue) > 0 && current.Before(attr.TimeEnd) { + a0 := queue[0] + current = a0.GetNextStartTime(current) + if current.Before(attr.TimeEnd) || current.Equal(attr.TimeEnd) { + utils.Logger.Info(fmt.Sprintf(" Executing action %s for time %v", a0.ActionsID, current)) + go a0.Execute() + // if after execute the next start time is in the past then + // do not add it to the queue + a0.ResetStartTimeCache() + current = current.Add(time.Second) + start := a0.GetNextStartTime(current) + if start.Before(current) || start.After(attr.TimeEnd) { + queue = queue[1:] + } else { + queue = append(queue, a0) + queue = queue[1:] + sort.Sort(queue) + } + } + } + } + *reply = utils.OK + return nil +} diff --git a/console/scheduler_execute.go b/console/scheduler_execute.go new file mode 100644 index 000000000..172d3e833 --- /dev/null +++ b/console/scheduler_execute.go @@ -0,0 +1,63 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package console + +import "github.com/cgrates/cgrates/apier/v1" + +func init() { + c := &CmdExecuteScheduledActions{ + name: "scheduler_execute", + rpcMethod: "ApierV1.ExecuteScheduledActions", + rpcParams: &v1.AttrsExecuteScheduledActions{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdExecuteScheduledActions struct { + name string + rpcMethod string + rpcParams *v1.AttrsExecuteScheduledActions + *CommandExecuter +} + +func (self *CmdExecuteScheduledActions) Name() string { + return self.name +} + +func (self *CmdExecuteScheduledActions) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdExecuteScheduledActions) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &v1.AttrsExecuteScheduledActions{} + } + return self.rpcParams +} + +func (self *CmdExecuteScheduledActions) PostprocessRpcParams() error { + return nil +} + +func (self *CmdExecuteScheduledActions) RpcResult() interface{} { + var s string + return &s +} diff --git a/data/tariffplans/tutorial/AccountActions.csv b/data/tariffplans/tutorial/AccountActions.csv index 97bcd5f64..bfdad1dd0 100644 --- a/data/tariffplans/tutorial/AccountActions.csv +++ b/data/tariffplans/tutorial/AccountActions.csv @@ -4,3 +4,4 @@ cgrates.org,1002,PACKAGE_10,STANDARD_TRIGGERS,, cgrates.org,1003,PACKAGE_10,STANDARD_TRIGGERS,, cgrates.org,1004,PACKAGE_10,STANDARD_TRIGGERS,, cgrates.org,1007,USE_SHARED_A,STANDARD_TRIGGERS,, +cgrates.org,1009,TEST_EXE,,, diff --git a/data/tariffplans/tutorial/ActionPlans.csv b/data/tariffplans/tutorial/ActionPlans.csv index 3d59fe3e9..ec5a53cce 100644 --- a/data/tariffplans/tutorial/ActionPlans.csv +++ b/data/tariffplans/tutorial/ActionPlans.csv @@ -6,3 +6,4 @@ USE_SHARED_A,SHARED_A_0,*asap,10 PACKAGE_1001,TOPUP_RST_5,*asap,10 PACKAGE_1001,TOPUP_RST_SHARED_5,*asap,10 PACKAGE_1001,TOPUP_120_DST1003,*asap,10 +TEST_EXE,TOPUP_EXE,DAILY,10 \ No newline at end of file diff --git a/data/tariffplans/tutorial/Actions.csv b/data/tariffplans/tutorial/Actions.csv index 93a3faa1f..690285fc3 100644 --- a/data/tariffplans/tutorial/Actions.csv +++ b/data/tariffplans/tutorial/Actions.csv @@ -4,6 +4,7 @@ TOPUP_RST_5,*topup_reset,,,,*monetary,*out,,*any,,,*unlimited,,5,20,false,false, TOPUP_RST_5,*topup_reset,,,,*voice,*out,,DST_1002,SPECIAL_1002,,*unlimited,,90,20,false,false,10 TOPUP_120_DST1003,*topup_reset,,,,*voice,*out,,DST_1003,,,*unlimited,,120,20,false,false,10 TOPUP_RST_SHARED_5,*topup,,,,*monetary,*out,,*any,,SHARED_A,*unlimited,,5,10,false,false,10 +TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 SHARED_A_0,*topup_reset,,,,*monetary,*out,,*any,,SHARED_A,*unlimited,,0,10,false,false,10 LOG_WARNING,*log,,,,,,,,,,,,,,false,false,10 DISABLE_AND_LOG,*log,,,,,,,,,,,,,,false,false,10 diff --git a/data/tariffplans/tutorial/Timings.csv b/data/tariffplans/tutorial/Timings.csv index 59fbab0a9..d957c710f 100644 --- a/data/tariffplans/tutorial/Timings.csv +++ b/data/tariffplans/tutorial/Timings.csv @@ -5,3 +5,4 @@ PEAK,*any,*any,*any,1;2;3;4;5,08:00:00 OFFPEAK_MORNING,*any,*any,*any,1;2;3;4;5,00:00:00 OFFPEAK_EVENING,*any,*any,*any,1;2;3;4;5,19:00:00 OFFPEAK_WEEKEND,*any,*any,*any,6;7,00:00:00 +DAILY,*any,*any,*any,*any,00:00:00 \ No newline at end of file diff --git a/engine/action_plan.go b/engine/action_plan.go index 23aa88f04..a633c8386 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -368,7 +368,7 @@ func (at *ActionTiming) IsASAP() bool { return at.Timing.Timing.StartTime == utils.ASAP } -// Structure to store actions according to weight +// Structure to store actions according to execution time and weight type ActionTimingPriorityList []*ActionTiming func (atpl ActionTimingPriorityList) Len() int { @@ -390,3 +390,22 @@ func (atpl ActionTimingPriorityList) Less(i, j int) bool { func (atpl ActionTimingPriorityList) Sort() { sort.Sort(atpl) } + +// Structure to store actions according to weight +type ActionTimingWeightOnlyPriorityList []*ActionTiming + +func (atpl ActionTimingWeightOnlyPriorityList) Len() int { + return len(atpl) +} + +func (atpl ActionTimingWeightOnlyPriorityList) Swap(i, j int) { + atpl[i], atpl[j] = atpl[j], atpl[i] +} + +func (atpl ActionTimingWeightOnlyPriorityList) Less(i, j int) bool { + return atpl[i].Weight > atpl[j].Weight +} + +func (atpl ActionTimingWeightOnlyPriorityList) Sort() { + sort.Sort(atpl) +} diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index e8c6896a9..2cfb53b8d 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -79,7 +79,7 @@ func (s *Scheduler) Loop() { select { case <-s.timer.C: // timer has expired - utils.Logger.Info(fmt.Sprintf(" Time for action on %v", a0.ActionsID)) + utils.Logger.Info(fmt.Sprintf(" Time for action on %s", a0.ActionsID)) case <-s.restartLoop: // nothing to do, just continue the loop } @@ -127,7 +127,6 @@ func (s *Scheduler) loadActionPlans() { if at.IsASAP() { continue } - now := time.Now() if at.GetNextStartTime(now).Before(now) { // the task is obsolete, do not add it to the queue From 8f5743e7f5d8f0f141d6ef7006720e7964ce27aa Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 25 Jan 2016 22:35:01 +0200 Subject: [PATCH 016/199] added action trigger management APIs --- apier/v1/scheduler.go | 12 +++- apier/v1/triggers.go | 140 ++++++++++++++++++++++++++++++++++++++++++ apier/v2/accounts.go | 2 +- 3 files changed, 151 insertions(+), 3 deletions(-) create mode 100644 apier/v1/triggers.go diff --git a/apier/v1/scheduler.go b/apier/v1/scheduler.go index 99ecf443b..f8ebda45c 100644 --- a/apier/v1/scheduler.go +++ b/apier/v1/scheduler.go @@ -194,7 +194,11 @@ func (self *ApierV1) ExecuteScheduledActions(attr AttrsExecuteScheduledActions, at.SetAccountIDs(apl.AccountIDs) // copy the accounts at.SetActionPlanID(apl.Id) - go at.Execute() + err := at.Execute() + if err != nil { + *reply = err.Error() + return err + } utils.Logger.Info(fmt.Sprintf(" Executing action %s ", at.ActionsID)) } } @@ -235,7 +239,11 @@ func (self *ApierV1) ExecuteScheduledActions(attr AttrsExecuteScheduledActions, current = a0.GetNextStartTime(current) if current.Before(attr.TimeEnd) || current.Equal(attr.TimeEnd) { utils.Logger.Info(fmt.Sprintf(" Executing action %s for time %v", a0.ActionsID, current)) - go a0.Execute() + err := a0.Execute() + if err != nil { + *reply = err.Error() + return err + } // if after execute the next start time is in the past then // do not add it to the queue a0.ResetStartTimeCache() diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go new file mode 100644 index 000000000..571f4f44c --- /dev/null +++ b/apier/v1/triggers.go @@ -0,0 +1,140 @@ +package v1 + +import ( + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +type AttrSetActionTriggers struct { + Tenant string + Account string + ActionTriggersIds *[]string + ActionTriggerOverwrite bool +} + +func (self *ApierV1) SetActionTriggers(attr AttrSetActionTriggers, reply *string) error { + if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + accID := utils.AccountKey(attr.Tenant, attr.Account) + var account *engine.Account + _, err := engine.Guardian.Guard(func() (interface{}, error) { + if acc, err := self.AccountDb.GetAccount(accID); err != nil { + account = acc + } else { + return 0, err + } + if attr.ActionTriggersIds != nil { + if attr.ActionTriggerOverwrite { + account.ActionTriggers = make(engine.ActionTriggers, 0) + } + for _, actionTriggerID := range *attr.ActionTriggersIds { + atrs, err := self.RatingDb.GetActionTriggers(actionTriggerID) + if err != nil { + + return 0, err + } + for _, at := range atrs { + var found bool + for _, existingAt := range account.ActionTriggers { + if existingAt.Equals(at) { + found = true + break + } + } + if !found { + account.ActionTriggers = append(account.ActionTriggers, at) + } + } + } + } + account.InitCounters() + return 0, nil + }, 0, accID) + if err != nil { + *reply = err.Error() + return err + } + *reply = utils.OK + return nil +} + +type AttrRemoveActionTriggers struct { + Tenant string + Account string + GroupID string + UniqueID string +} + +func (self *ApierV1) RemoveActionTriggers(attr AttrRemoveActionTriggers, reply *string) error { + if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + accID := utils.AccountKey(attr.Tenant, attr.Account) + var account *engine.Account + _, err := engine.Guardian.Guard(func() (interface{}, error) { + if acc, err := self.AccountDb.GetAccount(accID); err != nil { + account = acc + } else { + return 0, err + } + var newActionTriggers engine.ActionTriggers + for _, at := range account.ActionTriggers { + if (attr.UniqueID == "" || at.UniqueID == attr.UniqueID) && + (attr.GroupID == "" || at.ID == attr.GroupID) { + // remove action trigger + continue + } + newActionTriggers = append(newActionTriggers, at) + } + account.ActionTriggers = newActionTriggers + + account.InitCounters() + return 0, nil + }, 0, accID) + if err != nil { + *reply = err.Error() + return err + } + *reply = utils.OK + return nil +} + +type AttrResetActionTriggers struct { + Tenant string + Account string + GroupID string + UniqueID string +} + +func (self *ApierV1) ResetActionTriggers(attr AttrResetActionTriggers, reply *string) error { + + if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + accID := utils.AccountKey(attr.Tenant, attr.Account) + var account *engine.Account + _, err := engine.Guardian.Guard(func() (interface{}, error) { + if acc, err := self.AccountDb.GetAccount(accID); err != nil { + account = acc + } else { + return 0, err + } + for _, at := range account.ActionTriggers { + if (attr.UniqueID == "" || at.UniqueID == attr.UniqueID) && + (attr.GroupID == "" || at.ID == attr.GroupID) { + // reset action trigger + at.Executed = false + } + + } + account.ExecuteActionTriggers(nil) + return 0, nil + }, 0, accID) + if err != nil { + *reply = err.Error() + return err + } + *reply = utils.OK + return nil +} diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index d1cd45ed4..e18c90eeb 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -94,8 +94,8 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - dirtyActionPlans := make(map[string]*engine.ActionPlan) accID := utils.AccountKey(attr.Tenant, attr.Account) + dirtyActionPlans := make(map[string]*engine.ActionPlan) var ub *engine.Account _, err := engine.Guardian.Guard(func() (interface{}, error) { if bal, _ := self.AccountDb.GetAccount(accID); bal != nil { From 09653b7826672e7bf840333ae71d3b06a69bf979 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 26 Jan 2016 11:10:24 +0200 Subject: [PATCH 017/199] fix for nil account crash --- engine/action.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/engine/action.go b/engine/action.go index d5af34654..ed032e90b 100644 --- a/engine/action.go +++ b/engine/action.go @@ -158,8 +158,12 @@ func logAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (er // Used by cdrLogAction to dynamically parse values out of account and action func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) string { - dta, err := utils.NewTAFromAccountKey(acnt.Id) // Account information should be valid - if err != nil { + var err error + var dta *utils.TenantAccount + if acnt != nil { + dta, err = utils.NewTAFromAccountKey(acnt.Id) // Account information should be valid + } + if err != nil || acnt == nil { dta = new(utils.TenantAccount) // Init with empty values } var parsedValue string // Template values From b7d0637e156a6ffa6f1b33a22421a1aecfad346f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 26 Jan 2016 16:37:58 +0200 Subject: [PATCH 018/199] fix console help crash --- console/command_executer.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/console/command_executer.go b/console/command_executer.go index bf836da45..27d0ded67 100644 --- a/console/command_executer.go +++ b/console/command_executer.go @@ -67,7 +67,10 @@ func (ce *CommandExecuter) clientArgs(iface interface{}) (args []string) { for i := 0; i < typ.NumField(); i++ { valField := val.Field(i) typeField := typ.Field(i) - //log.Printf("%v (%v)", typeField.Name, valField.Kind()) + //log.Printf("%v (%v : %v)", typeField.Name, valField.Kind(), typeField.PkgPath) + if len(typeField.PkgPath) > 0 { //unexported field + continue + } switch valField.Kind() { case reflect.Ptr, reflect.Struct: if valField.Kind() == reflect.Ptr { @@ -79,6 +82,7 @@ func (ce *CommandExecuter) clientArgs(iface interface{}) (args []string) { } } args = append(args, ce.clientArgs(valField.Interface())...) + default: args = append(args, typeField.Name) } From 3da4e2518d2554bdfa83a4b2faabf3a85b2f4f5c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 26 Jan 2016 20:26:15 +0200 Subject: [PATCH 019/199] console commands for account trigger APIs --- apier/v1/apier.go | 70 ++----------------- apier/v1/triggers.go | 106 +++++++++++++++++++++++------ apier/v2/accounts.go | 12 ++-- console/trigger_remove.go | 63 +++++++++++++++++ console/trigger_reset.go | 63 +++++++++++++++++ console/trigger_set.go | 63 +++++++++++++++++ engine/action_plan.go | 1 + engine/actions_test.go | 138 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 425 insertions(+), 91 deletions(-) create mode 100644 console/trigger_remove.go create mode 100644 console/trigger_reset.go create mode 100644 console/trigger_set.go diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 1f5c2ef35..dcdfeda45 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -556,6 +556,11 @@ func (self *ApierV1) GetActions(actsId string, reply *[]*utils.TPAction) error { act.RatingSubject = engAct.Balance.RatingSubject act.SharedGroups = engAct.Balance.SharedGroups.String() act.BalanceWeight = engAct.Balance.Weight + act.TimingTags = engAct.Balance.TimingIDs.String() + act.BalanceId = engAct.Balance.Id + act.Categories = engAct.Balance.Categories.String() + act.BalanceBlocker = engAct.Balance.Blocker + act.BalanceDisabled = engAct.Balance.Disabled } acts = append(acts, act) } @@ -658,71 +663,6 @@ func (self *ApierV1) GetActionPlan(attr AttrGetActionPlan, reply *[]*engine.Acti return nil } -type AttrAddActionTrigger struct { - ActionTriggersId string - ActionTriggersUniqueId string - Tenant string - Account string - ThresholdType string - ThresholdValue float64 - BalanceId string - BalanceType string - BalanceDirection string - BalanceDestinationIds string - BalanceRatingSubject string //ToDo - BalanceWeight float64 - BalanceExpiryTime string - BalanceSharedGroup string //ToDo - Weight float64 - ActionsId string -} - -func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string) error { - if attr.BalanceDirection == "" { - attr.BalanceDirection = utils.OUT - } - balExpiryTime, err := utils.ParseTimeDetectLayout(attr.BalanceExpiryTime, self.Config.DefaultTimezone) - if err != nil { - return utils.NewErrServerError(err) - } - at := &engine.ActionTrigger{ - ID: attr.ActionTriggersId, - UniqueID: attr.ActionTriggersUniqueId, - ThresholdType: attr.ThresholdType, - ThresholdValue: attr.ThresholdValue, - BalanceId: attr.BalanceId, - BalanceType: attr.BalanceType, - BalanceDirections: utils.ParseStringMap(attr.BalanceDirection), - BalanceDestinationIds: utils.ParseStringMap(attr.BalanceDestinationIds), - BalanceWeight: attr.BalanceWeight, - BalanceExpirationDate: balExpiryTime, - Weight: attr.Weight, - ActionsId: attr.ActionsId, - Executed: false, - } - - tag := utils.AccountKey(attr.Tenant, attr.Account) - _, err = engine.Guardian.Guard(func() (interface{}, error) { - userBalance, err := self.AccountDb.GetAccount(tag) - if err != nil { - return 0, err - } - - userBalance.ActionTriggers = append(userBalance.ActionTriggers, at) - - if err = self.AccountDb.SetAccount(userBalance); err != nil { - return 0, err - } - return 0, nil - }, 0, tag) - if err != nil { - *reply = err.Error() - return err - } - *reply = OK - return nil -} - type AttrResetTriggeredAction struct { Id string Tenant string diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index 571f4f44c..1ea6a0fae 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -5,30 +5,95 @@ import ( "github.com/cgrates/cgrates/utils" ) -type AttrSetActionTriggers struct { +type AttrAddActionTrigger struct { + ActionTriggersId string + ActionTriggersUniqueId string Tenant string Account string - ActionTriggersIds *[]string + ThresholdType string + ThresholdValue float64 + BalanceId string + BalanceType string + BalanceDirection string + BalanceDestinationIds string + BalanceRatingSubject string //ToDo + BalanceWeight float64 + BalanceExpiryTime string + BalanceSharedGroup string //ToDo + Weight float64 + ActionsId string +} + +func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string) error { + if attr.BalanceDirection == "" { + attr.BalanceDirection = utils.OUT + } + balExpiryTime, err := utils.ParseTimeDetectLayout(attr.BalanceExpiryTime, self.Config.DefaultTimezone) + if err != nil { + return utils.NewErrServerError(err) + } + at := &engine.ActionTrigger{ + ID: attr.ActionTriggersId, + UniqueID: attr.ActionTriggersUniqueId, + ThresholdType: attr.ThresholdType, + ThresholdValue: attr.ThresholdValue, + BalanceId: attr.BalanceId, + BalanceType: attr.BalanceType, + BalanceDirections: utils.ParseStringMap(attr.BalanceDirection), + BalanceDestinationIds: utils.ParseStringMap(attr.BalanceDestinationIds), + BalanceWeight: attr.BalanceWeight, + BalanceExpirationDate: balExpiryTime, + Weight: attr.Weight, + ActionsId: attr.ActionsId, + Executed: false, + } + + tag := utils.AccountKey(attr.Tenant, attr.Account) + _, err = engine.Guardian.Guard(func() (interface{}, error) { + userBalance, err := self.AccountDb.GetAccount(tag) + if err != nil { + return 0, err + } + + userBalance.ActionTriggers = append(userBalance.ActionTriggers, at) + + if err = self.AccountDb.SetAccount(userBalance); err != nil { + return 0, err + } + return 0, nil + }, 0, tag) + if err != nil { + *reply = err.Error() + return err + } + *reply = OK + return nil +} + +type AttrSetAccountActionTriggers struct { + Tenant string + Account string + ActionTriggersIDs *[]string ActionTriggerOverwrite bool } -func (self *ApierV1) SetActionTriggers(attr AttrSetActionTriggers, reply *string) error { +func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } accID := utils.AccountKey(attr.Tenant, attr.Account) var account *engine.Account _, err := engine.Guardian.Guard(func() (interface{}, error) { - if acc, err := self.AccountDb.GetAccount(accID); err != nil { + if acc, err := self.AccountDb.GetAccount(accID); err == nil { account = acc } else { return 0, err } - if attr.ActionTriggersIds != nil { + if attr.ActionTriggersIDs != nil { if attr.ActionTriggerOverwrite { account.ActionTriggers = make(engine.ActionTriggers, 0) } - for _, actionTriggerID := range *attr.ActionTriggersIds { + for _, actionTriggerID := range *attr.ActionTriggersIDs { atrs, err := self.RatingDb.GetActionTriggers(actionTriggerID) if err != nil { @@ -49,6 +114,9 @@ func (self *ApierV1) SetActionTriggers(attr AttrSetActionTriggers, reply *string } } account.InitCounters() + if err := self.AccountDb.SetAccount(account); err != nil { + return 0, err + } return 0, nil }, 0, accID) if err != nil { @@ -59,21 +127,21 @@ func (self *ApierV1) SetActionTriggers(attr AttrSetActionTriggers, reply *string return nil } -type AttrRemoveActionTriggers struct { +type AttrRemoveAccountActionTriggers struct { Tenant string Account string GroupID string UniqueID string } -func (self *ApierV1) RemoveActionTriggers(attr AttrRemoveActionTriggers, reply *string) error { +func (self *ApierV1) RemoveAccountActionTriggers(attr AttrRemoveAccountActionTriggers, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } accID := utils.AccountKey(attr.Tenant, attr.Account) - var account *engine.Account _, err := engine.Guardian.Guard(func() (interface{}, error) { - if acc, err := self.AccountDb.GetAccount(accID); err != nil { + var account *engine.Account + if acc, err := self.AccountDb.GetAccount(accID); err == nil { account = acc } else { return 0, err @@ -88,8 +156,10 @@ func (self *ApierV1) RemoveActionTriggers(attr AttrRemoveActionTriggers, reply * newActionTriggers = append(newActionTriggers, at) } account.ActionTriggers = newActionTriggers - account.InitCounters() + if err := self.AccountDb.SetAccount(account); err != nil { + return 0, err + } return 0, nil }, 0, accID) if err != nil { @@ -100,14 +170,7 @@ func (self *ApierV1) RemoveActionTriggers(attr AttrRemoveActionTriggers, reply * return nil } -type AttrResetActionTriggers struct { - Tenant string - Account string - GroupID string - UniqueID string -} - -func (self *ApierV1) ResetActionTriggers(attr AttrResetActionTriggers, reply *string) error { +func (self *ApierV1) ResetAccountActionTriggers(attr AttrRemoveAccountActionTriggers, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) @@ -115,7 +178,7 @@ func (self *ApierV1) ResetActionTriggers(attr AttrResetActionTriggers, reply *st accID := utils.AccountKey(attr.Tenant, attr.Account) var account *engine.Account _, err := engine.Guardian.Guard(func() (interface{}, error) { - if acc, err := self.AccountDb.GetAccount(accID); err != nil { + if acc, err := self.AccountDb.GetAccount(accID); err == nil { account = acc } else { return 0, err @@ -129,6 +192,9 @@ func (self *ApierV1) ResetActionTriggers(attr AttrResetActionTriggers, reply *st } account.ExecuteActionTriggers(nil) + if err := self.AccountDb.SetAccount(account); err != nil { + return 0, err + } return 0, nil }, 0, accID) if err != nil { diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index e18c90eeb..b852b212d 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -81,9 +81,9 @@ func (self *ApierV2) GetAccount(attr *utils.AttrGetAccount, reply *engine.Accoun type AttrSetAccount struct { Tenant string Account string - ActionPlanIds *[]string + ActionPlanIDs *[]string ActionPlansOverwrite bool - ActionTriggersIds *[]string + ActionTriggerIDs *[]string ActionTriggerOverwrite bool AllowNegative *bool Disabled *bool @@ -105,7 +105,7 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { Id: accID, } } - if attr.ActionPlanIds != nil { + if attr.ActionPlanIDs != nil { _, err := engine.Guardian.Guard(func() (interface{}, error) { actionPlansMap, err := self.RatingDb.GetAllActionPlans() if err != nil { @@ -121,7 +121,7 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { } } - for _, actionPlanID := range *attr.ActionPlanIds { + for _, actionPlanID := range *attr.ActionPlanIDs { ap, ok := actionPlansMap[actionPlanID] if !ok { return 0, utils.ErrNotFound @@ -162,11 +162,11 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { } } - if attr.ActionTriggersIds != nil { + if attr.ActionTriggerIDs != nil { if attr.ActionTriggerOverwrite { ub.ActionTriggers = make(engine.ActionTriggers, 0) } - for _, actionTriggerID := range *attr.ActionTriggersIds { + for _, actionTriggerID := range *attr.ActionTriggerIDs { atrs, err := self.RatingDb.GetActionTriggers(actionTriggerID) if err != nil { return 0, err diff --git a/console/trigger_remove.go b/console/trigger_remove.go new file mode 100644 index 000000000..a460d8528 --- /dev/null +++ b/console/trigger_remove.go @@ -0,0 +1,63 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package console + +import "github.com/cgrates/cgrates/apier/v1" + +func init() { + c := &CmdRemoveTriggers{ + name: "triggers_remove", + rpcMethod: "ApierV1.RemoveAccountActionTriggers", + rpcParams: &v1.AttrRemoveAccountActionTriggers{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdRemoveTriggers struct { + name string + rpcMethod string + rpcParams *v1.AttrRemoveAccountActionTriggers + *CommandExecuter +} + +func (self *CmdRemoveTriggers) Name() string { + return self.name +} + +func (self *CmdRemoveTriggers) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdRemoveTriggers) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &v1.AttrRemoveAccountActionTriggers{} + } + return self.rpcParams +} + +func (self *CmdRemoveTriggers) PostprocessRpcParams() error { + return nil +} + +func (self *CmdRemoveTriggers) RpcResult() interface{} { + var s string + return &s +} diff --git a/console/trigger_reset.go b/console/trigger_reset.go new file mode 100644 index 000000000..0d5be9f24 --- /dev/null +++ b/console/trigger_reset.go @@ -0,0 +1,63 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package console + +import "github.com/cgrates/cgrates/apier/v1" + +func init() { + c := &CmdResetTriggers{ + name: "triggers_reset", + rpcMethod: "ApierV1.ResetAccountActionTriggers", + rpcParams: &v1.AttrRemoveAccountActionTriggers{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdResetTriggers struct { + name string + rpcMethod string + rpcParams *v1.AttrRemoveAccountActionTriggers + *CommandExecuter +} + +func (self *CmdResetTriggers) Name() string { + return self.name +} + +func (self *CmdResetTriggers) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdResetTriggers) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &v1.AttrRemoveAccountActionTriggers{} + } + return self.rpcParams +} + +func (self *CmdResetTriggers) PostprocessRpcParams() error { + return nil +} + +func (self *CmdResetTriggers) RpcResult() interface{} { + var s string + return &s +} diff --git a/console/trigger_set.go b/console/trigger_set.go new file mode 100644 index 000000000..2e9095d95 --- /dev/null +++ b/console/trigger_set.go @@ -0,0 +1,63 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package console + +import "github.com/cgrates/cgrates/apier/v1" + +func init() { + c := &CmdSetTriggers{ + name: "triggers_set", + rpcMethod: "ApierV1.SetAccountActionTriggers", + rpcParams: &v1.AttrSetAccountActionTriggers{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdSetTriggers struct { + name string + rpcMethod string + rpcParams *v1.AttrSetAccountActionTriggers + *CommandExecuter +} + +func (self *CmdSetTriggers) Name() string { + return self.name +} + +func (self *CmdSetTriggers) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdSetTriggers) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &v1.AttrSetAccountActionTriggers{} + } + return self.rpcParams +} + +func (self *CmdSetTriggers) PostprocessRpcParams() error { + return nil +} + +func (self *CmdSetTriggers) RpcResult() interface{} { + var s string + return &s +} diff --git a/engine/action_plan.go b/engine/action_plan.go index a633c8386..f3d2b4563 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -294,6 +294,7 @@ func (at *ActionTiming) Execute() (err error) { // check action filter if len(a.Filter) > 0 { matched, err := ub.matchActionFilter(a.Filter) + //log.Print("Checkng: ", a.Filter, matched) if err != nil { return 0, err } diff --git a/engine/actions_test.go b/engine/actions_test.go index 1c6da1a7d..1eb5367d6 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -1759,6 +1759,144 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) { } } +func TestActionConditionalDisabledIfNegative(t *testing.T) { + err := accountingStorage.SetAccount( + &Account{ + Id: "cgrates.org:af", + BalanceMap: map[string]BalanceChain{ + "*data": BalanceChain{ + &Balance{ + Uuid: "fc927edb-1bd6-425e-a2a3-9fd8bafaa524", + Id: "for_v3hsillmilld500m_data_500_m", + Value: 5.242, + Weight: 10, + RatingSubject: "for_v3hsillmilld500m_data_forfait", + Categories: utils.StringMap{ + "data_france": true, + }, + }, + }, + "*monetary": BalanceChain{ + &Balance{ + Uuid: "9fa1847a-f36a-41a7-8ec0-dfaab370141e", + Id: "*default", + Value: -1.95001, + }, + }, + "*sms": BalanceChain{ + &Balance{ + Uuid: "d348d15d-2988-4ee4-b847-6a552f94e2ec", + Id: "for_v3hsillmilld500m_mms_ill", + Value: 20000, + Weight: 10, + DestinationIds: utils.StringMap{ + "FRANCE_NATIONAL": true, + }, + Categories: utils.StringMap{ + "mms_france": true, + "tmms_france": true, + "vmms_france": true, + }, + }, + &Balance{ + Uuid: "f4643517-31f6-4199-980f-04cf535471ed", + Id: "for_v3hsillmilld500m_sms_ill", + Value: 20000, + Weight: 10, + DestinationIds: utils.StringMap{ + "FRANCE_NATIONAL": true, + }, + Categories: utils.StringMap{ + "ms_france": true, + }, + }, + }, + "*voice": BalanceChain{ + &Balance{ + Uuid: "079ab190-77f4-44f3-9c6f-3a0dd1a59dfd", + Id: "for_v3hsillmilld500m_voice_3_h", + Value: 10800, + Weight: 10, + DestinationIds: utils.StringMap{ + "FRANCE_NATIONAL": true, + }, + Categories: utils.StringMap{ + "call_france": true, + }, + }, + }, + }, + }) + if err != nil { + t.Errorf("error setting account: %v", err) + } + + a1 := &Action{ + ActionType: "*enable_disable_balance", + BalanceType: "*sms", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &Balance{ + Weight: 10, + Disabled: true, + }, + Weight: 9, + } + a2 := &Action{ + ActionType: "*enable_disable_balance", + BalanceType: "*sms", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &Balance{ + DestinationIds: utils.NewStringMap("FRANCE_NATIONAL"), + Weight: 10, + Disabled: true, + }, + Weight: 8, + } + a3 := &Action{ + ActionType: "*enable_disable_balance", + BalanceType: "*data", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &Balance{ + RatingSubject: "for_v3hsillmilld500m_data_forfait", + Weight: 10, + Disabled: true, + }, + Weight: 7, + } + a4 := &Action{ + ActionType: "*enable_disable_balance", + BalanceType: "*voice", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &Balance{ + DestinationIds: utils.NewStringMap("FRANCE_NATIONAL"), + Weight: 10, + Disabled: true, + }, + Weight: 6, + } + + at := &ActionTiming{ + accountIDs: utils.StringMap{"cgrates.org:af": true}, + actions: Actions{a1, a2, a3, a4}, + } + at.Execute() + + afterUb, err := accountingStorage.GetAccount("cgrates.org:af") + if err != nil { + t.Error("account not found: ", err, afterUb) + } + + for btype, chain := range afterUb.BalanceMap { + if btype != utils.MONETARY { + for _, b := range chain { + if b.Disabled != true { + t.Errorf("Failed to disabled balance (%s): %+v", btype, b) + } + } + } + } +} + func TestActionSetBalance(t *testing.T) { err := accountingStorage.SetAccount( &Account{ From 23515b5973528df500df803b3c8eff0e8dde75f0 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 26 Jan 2016 22:35:58 +0200 Subject: [PATCH 020/199] use v2 structures for SetAccount tests --- engine/action_trigger.go | 2 +- general_tests/tutorial_local_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 658e93590..3e2dc7ea1 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -34,7 +34,7 @@ type ActionTrigger struct { ThresholdType string //*min_event_counter, *max_event_counter, *min_balance_counter, *max_balance_counter, *min_balance, *max_balance, *exp_balance // stats: *min_asr, *max_asr, *min_acd, *max_acd, *min_tcd, *max_tcd, *min_acc, *max_acc, *min_tcc, *max_tcc, *min_ddc, *max_ddc ThresholdValue float64 - Recurrent bool // reset eexcuted flag each run + Recurrent bool // reset excuted flag each run MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers BalanceId string BalanceType string // *monetary/*voice etc diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index f0d8e95a9..017c0f13f 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -26,6 +26,7 @@ import ( "testing" "time" + "github.com/cgrates/cgrates/apier/v2" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" @@ -1131,7 +1132,7 @@ func TestTutLocalSetAccount(t *testing.T) { return } var reply string - attrs := &utils.AttrSetAccount{Tenant: "cgrates.org", Account: "tutacnt1", ActionPlanId: "PACKAGE_10", ActionTriggersId: "STANDARD_TRIGGERS", ReloadScheduler: true} + attrs := &v2.AttrSetAccount{Tenant: "cgrates.org", Account: "tutacnt1", ActionPlanIDs: &[]string{"PACKAGE_10"}, ActionTriggerIDs: &[]string{"STANDARD_TRIGGERS"}, ReloadScheduler: true} if err := tutLocalRpc.Call("ApierV2.SetAccount", attrs, &reply); err != nil { t.Error("Got error on ApierV2.SetAccount: ", err.Error()) } else if reply != "OK" { @@ -1169,7 +1170,7 @@ func TestTutLocalSetAccount(t *testing.T) { t.Error("Disabled should not be set") } } - attrs = &utils.AttrSetAccount{Tenant: "cgrates.org", Account: "tutacnt1", AllowNegative: utils.BoolPointer(true), Disabled: utils.BoolPointer(true), ReloadScheduler: true} + attrs = &v2.AttrSetAccount{Tenant: "cgrates.org", Account: "tutacnt1", AllowNegative: utils.BoolPointer(true), Disabled: utils.BoolPointer(true), ReloadScheduler: true} if err := tutLocalRpc.Call("ApierV2.SetAccount", attrs, &reply); err != nil { t.Error("Got error on ApierV2.SetAccount: ", err.Error()) } else if reply != "OK" { @@ -1198,7 +1199,6 @@ func TestTutLocalSetAccount(t *testing.T) { t.Error("Disabled should be set") } } - } /* From ecc693df7c8a8ee7bcfdc5b9e53a4ec3782fa7e5 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 26 Jan 2016 22:45:32 +0200 Subject: [PATCH 021/199] test double triggers and plans --- general_tests/tutorial_local_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index 017c0f13f..209b00379 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -1170,7 +1170,8 @@ func TestTutLocalSetAccount(t *testing.T) { t.Error("Disabled should not be set") } } - attrs = &v2.AttrSetAccount{Tenant: "cgrates.org", Account: "tutacnt1", AllowNegative: utils.BoolPointer(true), Disabled: utils.BoolPointer(true), ReloadScheduler: true} + attrs = &v2.AttrSetAccount{Tenant: "cgrates.org", Account: "tutacnt1", ActionPlanIDs: &[]string{"PACKAGE_10"}, ActionTriggerIDs: &[]string{"STANDARD_TRIGGERS"}, AllowNegative: utils.BoolPointer(true), Disabled: utils.BoolPointer(true), ReloadScheduler: true} + if err := tutLocalRpc.Call("ApierV2.SetAccount", attrs, &reply); err != nil { t.Error("Got error on ApierV2.SetAccount: ", err.Error()) } else if reply != "OK" { From 9a7fff8822846b311bec29f3555e0f706c100202 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 26 Jan 2016 22:48:15 +0200 Subject: [PATCH 022/199] test extra triggers --- general_tests/tutorial_local_test.go | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index 209b00379..7fc4a4b7d 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -1200,6 +1200,37 @@ func TestTutLocalSetAccount(t *testing.T) { t.Error("Disabled should be set") } } + + attrs = &v2.AttrSetAccount{Tenant: "cgrates.org", Account: "tutacnt1", ActionPlanIDs: &[]string{"PACKAGE_1001"}, ActionTriggerIDs: &[]string{"CDRST1_WARN"}, AllowNegative: utils.BoolPointer(true), Disabled: utils.BoolPointer(true), ReloadScheduler: true} + + if err := tutLocalRpc.Call("ApierV2.SetAccount", attrs, &reply); err != nil { + t.Error("Got error on ApierV2.SetAccount: ", err.Error()) + } else if reply != "OK" { + t.Errorf("Calling ApierV2.SetAccount received: %s", reply) + } + if err := tutLocalRpc.Call("ApierV2.GetAccounts", utils.AttrGetAccounts{Tenant: attrs.Tenant, AccountIds: []string{attrs.Account}}, &acnts); err != nil { + t.Error(err) + } else if len(acnts) != 1 { + t.Errorf("Accounts received: %+v", acnts) + } else { + acnt := acnts[0] + dta, _ := utils.NewTAFromAccountKey(acnt.Id) + if dta.Tenant != attrs.Tenant || dta.Account != attrs.Account { + t.Error("Unexpected account id received: ", acnt.Id) + } + if balances := acnt.BalanceMap["*monetary"]; len(balances) != 1 { + t.Errorf("Unexpected balances found: %+v", balances) + } + if len(acnt.ActionTriggers) != 4 { + t.Errorf("Unexpected action triggers for account: %+v", acnt.ActionTriggers) + } + if !acnt.AllowNegative { + t.Error("AllowNegative should be set") + } + if !acnt.Disabled { + t.Error("Disabled should be set") + } + } } /* From e67a73f9c720a9fefe83c74e3266f34f4e033feb Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 26 Jan 2016 22:49:56 +0200 Subject: [PATCH 023/199] fix test --- general_tests/tutorial_local_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index 7fc4a4b7d..fe515fde2 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -1221,7 +1221,7 @@ func TestTutLocalSetAccount(t *testing.T) { if balances := acnt.BalanceMap["*monetary"]; len(balances) != 1 { t.Errorf("Unexpected balances found: %+v", balances) } - if len(acnt.ActionTriggers) != 4 { + if len(acnt.ActionTriggers) != 7 { t.Errorf("Unexpected action triggers for account: %+v", acnt.ActionTriggers) } if !acnt.AllowNegative { From 4bfe53245c854b9aefc60bb6c6df629ee7d68e70 Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 27 Jan 2016 15:30:22 +0100 Subject: [PATCH 024/199] CGRID in case of SMG calculated just out of OriginID due to EventTimestamp issues in diameter --- sessionmanager/smg_event.go | 5 +++-- sessionmanager/smg_event_test.go | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sessionmanager/smg_event.go b/sessionmanager/smg_event.go index 5245e9906..aefe28dfb 100644 --- a/sessionmanager/smg_event.go +++ b/sessionmanager/smg_event.go @@ -50,8 +50,9 @@ func (self SMGenericEvent) GetTOR(fieldName string) string { } func (self SMGenericEvent) GetCgrId(timezone string) string { - setupTime, _ := self.GetSetupTime(utils.META_DEFAULT, timezone) - return utils.Sha1(self.GetUUID(), setupTime.UTC().String()) + //setupTime, _ := self.GetSetupTime(utils.META_DEFAULT, timezone) + //return utils.Sha1(self.GetUUID(), setupTime.UTC().String()) + return utils.Sha1(self.GetUUID()) } func (self SMGenericEvent) GetUUID() string { diff --git a/sessionmanager/smg_event_test.go b/sessionmanager/smg_event_test.go index f6c395b7b..091453f65 100644 --- a/sessionmanager/smg_event_test.go +++ b/sessionmanager/smg_event_test.go @@ -54,7 +54,7 @@ func TestSMGenericEventParseFields(t *testing.T) { if smGev.GetName() != "TEST_EVENT" { t.Error("Unexpected: ", smGev.GetName()) } - if smGev.GetCgrId("UTC") != "0711eaa78e53937f1593dabc08c83ea04a915f2e" { + if smGev.GetCgrId("UTC") != "8cb2237d0679ca88db6464eac60da96345513964" { t.Error("Unexpected: ", smGev.GetCgrId("UTC")) } if smGev.GetUUID() != "12345" { @@ -147,7 +147,7 @@ func TestSMGenericEventAsStoredCdr(t *testing.T) { smGev[utils.CDRHOST] = "10.0.3.15" smGev["Extra1"] = "Value1" smGev["Extra2"] = 5 - eStoredCdr := &engine.CDR{CGRID: "0711eaa78e53937f1593dabc08c83ea04a915f2e", + eStoredCdr := &engine.CDR{CGRID: "8cb2237d0679ca88db6464eac60da96345513964", ToR: utils.SMS, OriginID: "12345", OriginHost: "10.0.3.15", Source: "SMG_TEST_EVENT", RequestType: utils.META_PREPAID, Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "account1", Subject: "subject1", Destination: "+4986517174963", SetupTime: time.Date(2015, 11, 9, 14, 21, 24, 0, time.UTC), AnswerTime: time.Date(2015, 11, 9, 14, 22, 2, 0, time.UTC), From 3322226b6554372ebe811f45a5aec7f48349b87c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 27 Jan 2016 17:42:01 +0200 Subject: [PATCH 025/199] fix enable_disable toggle issue --- engine/account.go | 1 + engine/action.go | 30 +++++++++++++++--------------- engine/action_plan.go | 3 ++- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/engine/account.go b/engine/account.go index 339ca6c4c..03224b12d 100644 --- a/engine/account.go +++ b/engine/account.go @@ -280,6 +280,7 @@ func (ub *Account) enableDisableBalanceAction(a *Action) error { found = true } } + a.Balance.Disabled = disabled // restore balance aaction as it is cached if !found { return utils.ErrNotFound } diff --git a/engine/action.go b/engine/action.go index ed032e90b..eac9cb299 100644 --- a/engine/action.go +++ b/engine/action.go @@ -275,7 +275,7 @@ func cdrLogAction(acc *Account, sq *StatsQueueTriggered, a *Action, acs Actions) func resetTriggersAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } ub.ResetActionTriggers(a) return @@ -283,7 +283,7 @@ func resetTriggersAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac func setRecurrentAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } ub.SetRecurrent(a, true) return @@ -291,7 +291,7 @@ func setRecurrentAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Act func unsetRecurrentAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } ub.SetRecurrent(a, false) return @@ -299,7 +299,7 @@ func unsetRecurrentAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs A func allowNegativeAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } ub.AllowNegative = true return @@ -307,7 +307,7 @@ func allowNegativeAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac func denyNegativeAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } ub.AllowNegative = false return @@ -315,14 +315,14 @@ func denyNegativeAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Act func resetAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } return genericReset(ub) } func topupResetAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } if ub.BalanceMap == nil { // Init the map since otherwise will get error if nil ub.BalanceMap = make(map[string]BalanceChain, 0) @@ -334,7 +334,7 @@ func topupResetAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actio func topupAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } c := a.Clone() genericMakeNegative(c) @@ -343,7 +343,7 @@ func topupAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) ( func debitResetAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } if ub.BalanceMap == nil { // Init the map since otherwise will get error if nil ub.BalanceMap = make(map[string]BalanceChain, 0) @@ -353,7 +353,7 @@ func debitResetAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actio func debitAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } err = genericDebit(ub, a, false) return @@ -361,7 +361,7 @@ func debitAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) ( func resetCountersAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } if ub.UnitCounters != nil { ub.UnitCounters.resetCounters(a) @@ -377,7 +377,7 @@ func genericMakeNegative(a *Action) { func genericDebit(ub *Account, a *Action, reset bool) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } if ub.BalanceMap == nil { ub.BalanceMap = make(map[string]BalanceChain) @@ -387,7 +387,7 @@ func genericDebit(ub *Account, a *Action, reset bool) (err error) { func enableUserAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } ub.Disabled = false return @@ -395,7 +395,7 @@ func enableUserAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actio func disableUserAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } ub.Disabled = true return @@ -403,7 +403,7 @@ func disableUserAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Acti func enableDisableBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { - return errors.New("nil user balance") + return errors.New("nil account") } ub.enableDisableBalanceAction(a) return diff --git a/engine/action_plan.go b/engine/action_plan.go index f3d2b4563..3b8069c64 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -291,6 +291,7 @@ func (at *ActionTiming) Execute() (err error) { transactionFailed := false removeAccountActionFound := false for _, a := range aac { + //log.Print("A: ", utils.ToJSON(a)) // check action filter if len(a.Filter) > 0 { matched, err := ub.matchActionFilter(a.Filter) @@ -349,7 +350,7 @@ func (at *ActionTiming) Execute() (err error) { break } if err := actionFunction(nil, nil, a, aac); err != nil { - utils.Logger.Err(fmt.Sprintf("Error executing action %s: %v!", a.ActionType, err)) + utils.Logger.Err(fmt.Sprintf("Error executing accountless action %s: %v!", a.ActionType, err)) break } } From 4540df80500feaaeb077f0db79e4fc35ef6f00f8 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 28 Jan 2016 11:18:31 +0200 Subject: [PATCH 026/199] better load rating info errors --- engine/calldesc.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/engine/calldesc.go b/engine/calldesc.go index 2a4ccc69d..40fdb7e79 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -193,16 +193,18 @@ func (cd *CallDescriptor) LoadRatingPlans() (err error) { if err == utils.ErrNotFound && rec == 1 { //if err != nil || !cd.continousRatingInfos() { // use the default subject only if the initial one was not found + //utils.Logger.Debug(fmt.Sprintf("Try the default subject for %s and account: %s, subject: %s", cd.Destination, cd.GetAccountKey(), cd.GetKey(cd.Subject))) err, _ = cd.getRatingPlansForPrefix(cd.GetKey(FALLBACK_SUBJECT), 1) } //load the rating plans if err != nil { utils.Logger.Err(fmt.Sprintf("Rating plan not found for destination %s and account: %s, subject: %s", cd.Destination, cd.GetAccountKey(), cd.GetKey(cd.Subject))) - err = utils.ErrRatingPlanNotFound + return utils.ErrRatingPlanNotFound + } if !cd.continousRatingInfos() { utils.Logger.Err(fmt.Sprintf("Destination %s not authorized for account: %s, subject: %s", cd.Destination, cd.GetAccountKey(), cd.GetKey(cd.Subject))) - err = utils.ErrUnauthorizedDestination + return utils.ErrUnauthorizedDestination } return } From 11dd95cb5f2328d588b52d34a8fac093311dffd7 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 28 Jan 2016 13:10:30 +0200 Subject: [PATCH 027/199] added debit_max console command --- console/debit_max.go | 67 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 console/debit_max.go diff --git a/console/debit_max.go b/console/debit_max.go new file mode 100644 index 000000000..3861f9388 --- /dev/null +++ b/console/debit_max.go @@ -0,0 +1,67 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package console + +import "github.com/cgrates/cgrates/engine" + +func init() { + c := &CmdMaxDebit{ + name: "debit", + rpcMethod: "Responder.MaxDebit", + clientArgs: []string{"Direction", "Category", "TOR", "Tenant", "Subject", "Account", "Destination", "TimeStart", "TimeEnd", "CallDuration", "FallbackSubject"}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdMaxDebit struct { + name string + rpcMethod string + rpcParams *engine.CallDescriptor + clientArgs []string + *CommandExecuter +} + +func (self *CmdMaxDebit) Name() string { + return self.name +} + +func (self *CmdMaxDebit) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdMaxDebit) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &engine.CallDescriptor{Direction: "*out"} + } + return self.rpcParams +} + +func (self *CmdMaxDebit) PostprocessRpcParams() error { + return nil +} + +func (self *CmdMaxDebit) RpcResult() interface{} { + return &engine.CallCost{} +} + +func (self *CmdMaxDebit) ClientArgs() []string { + return self.clientArgs +} From 03f2ba4ee9e61198e16ba176bfb604f594279c4c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 28 Jan 2016 13:24:20 +0200 Subject: [PATCH 028/199] fix debit_max console command name --- console/debit_max.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/console/debit_max.go b/console/debit_max.go index 3861f9388..291b8281d 100644 --- a/console/debit_max.go +++ b/console/debit_max.go @@ -22,7 +22,7 @@ import "github.com/cgrates/cgrates/engine" func init() { c := &CmdMaxDebit{ - name: "debit", + name: "debit_max", rpcMethod: "Responder.MaxDebit", clientArgs: []string{"Direction", "Category", "TOR", "Tenant", "Subject", "Account", "Destination", "TimeStart", "TimeEnd", "CallDuration", "FallbackSubject"}, } From de5e332e72d820827ef1205d40cdd59b4eaa58ad Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 28 Jan 2016 14:22:59 +0200 Subject: [PATCH 029/199] fix for UNAUTHORIZED_DESTINATION on sms/mms --- engine/responder.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/engine/responder.go b/engine/responder.go index 55ded65f7..a36d201af 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -358,6 +358,8 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *CDR, reply *float64) error { usage = config.CgrConfig().MaxCallDuration } cd := &CallDescriptor{ + CgrId: ev.GetCgrId(rs.Timezone), + TOR: ev.ToR, Direction: ev.GetDirection(dc.DirectionField), Tenant: ev.GetTenant(dc.TenantField), Category: ev.GetCategory(dc.CategoryField), @@ -435,6 +437,13 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { }) return errors.New("Error parsing answer event start time") } + endTime, err := ev.GetEndTime("", rs.Timezone) + if err != nil { + rs.getCache().Cache(utils.GET_SESS_RUNS_CACHE_PREFIX+ev.CGRID, &cache2go.CacheItem{ + Err: err, + }) + return errors.New("Error parsing answer event end time") + } cd := &CallDescriptor{ CgrId: ev.GetCgrId(rs.Timezone), TOR: ev.ToR, @@ -445,6 +454,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { Account: ev.GetAccount(dc.AccountField), Destination: ev.GetDestination(dc.DestinationField), TimeStart: startTime, + endTime: endTime, ExtraFields: ev.GetExtraFields()} sesRuns = append(sesRuns, &SessionRun{DerivedCharger: dc, CallDescriptor: cd}) } From c071651fc87f6d390274eb6df8c85bf37039a2dd Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 28 Jan 2016 14:38:13 +0200 Subject: [PATCH 030/199] compilation and tests fixes --- engine/responder.go | 2 +- engine/responder_test.go | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/engine/responder.go b/engine/responder.go index a36d201af..d9f9510f1 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -454,7 +454,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { Account: ev.GetAccount(dc.AccountField), Destination: ev.GetDestination(dc.DestinationField), TimeStart: startTime, - endTime: endTime, + TimeEnd: endTime, ExtraFields: ev.GetExtraFields()} sesRuns = append(sesRuns, &SessionRun{DerivedCharger: dc, CallDescriptor: cd}) } diff --git a/engine/responder_test.go b/engine/responder_test.go index 969993ad7..b896a15c8 100644 --- a/engine/responder_test.go +++ b/engine/responder_test.go @@ -152,19 +152,19 @@ func TestResponderGetSessionRuns(t *testing.T) { eSRuns := []*SessionRun{ &SessionRun{DerivedCharger: extra1DC, CallDescriptor: &CallDescriptor{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), Direction: "*out", Category: "0", - Tenant: "vdf", Subject: "rif", Account: "minitsboy", Destination: "0256", TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - TOR: utils.VOICE, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}}, + Tenant: "vdf", Subject: "rif", Account: "minitsboy", Destination: "0256", TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), TimeEnd: time.Date(2013, 11, 7, 8, 42, 36, 0, time.UTC), TOR: utils.VOICE, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}}, &SessionRun{DerivedCharger: extra2DC, CallDescriptor: &CallDescriptor{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), Direction: "*out", Category: "call", - Tenant: "vdf", Subject: "ivo", Account: "ivo", Destination: "1002", TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - TOR: utils.VOICE, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}}, + Tenant: "vdf", Subject: "ivo", Account: "ivo", Destination: "1002", TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), TimeEnd: time.Date(2013, 11, 7, 8, 42, 36, 0, time.UTC), TOR: utils.VOICE, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}}, &SessionRun{DerivedCharger: dfDC, CallDescriptor: &CallDescriptor{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), Direction: "*out", Category: "call", - Tenant: "vdf", Subject: "dan2", Account: "dan2", Destination: "1002", TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - TOR: utils.VOICE, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}}} + Tenant: "vdf", Subject: "dan2", Account: "dan2", Destination: "1002", TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), TimeEnd: time.Date(2013, 11, 7, 8, 42, 36, 0, time.UTC), TOR: utils.VOICE, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}}} if err := rsponder.GetSessionRuns(cdr, &sesRuns); err != nil { t.Error(err) } else if !reflect.DeepEqual(eSRuns, sesRuns) { + for _, sr := range sesRuns { + t.Logf("sr cd: %+v", sr.CallDescriptor) + } t.Errorf("Expecting: %+v, received: %+v", eSRuns, sesRuns) } } From ddf57df2ebc31c6565005311f768ab6d29a26f09 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 28 Jan 2016 15:34:40 +0200 Subject: [PATCH 031/199] added DebitBalance api --- apier/v1/accounts.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 2290a13db..fa2991869 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -394,6 +394,13 @@ type AttrAddBalance struct { } func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { + return self.modifyBalance(engine.TOPUP, attr, reply) +} +func (self *ApierV1) DebitBalance(attr *AttrAddBalance, reply *string) error { + return self.modifyBalance(engine.DEBIT, attr, reply) +} + +func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } @@ -416,9 +423,8 @@ func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { at := &engine.ActionTiming{} at.SetAccountIDs(utils.StringMap{accID: true}) - aType := engine.TOPUP if attr.Overwrite { - aType = engine.TOPUP_RESET + aType += "_reset" // => *topup_reset/*debit_reset } at.SetActions(engine.Actions{ &engine.Action{ From cdc6b7de2e3aaf912dcf8ddfb0b9fad0fb7d0128 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 28 Jan 2016 16:13:37 +0200 Subject: [PATCH 032/199] added call descriptor runid --- engine/calldesc.go | 3 ++- engine/responder.go | 16 ++++++++------ engine/responder_test.go | 6 ++--- general_tests/tutorial_local_test.go | 33 ++++++++++++++++++---------- sessionmanager/session.go | 2 +- 5 files changed, 37 insertions(+), 23 deletions(-) diff --git a/engine/calldesc.go b/engine/calldesc.go index 40fdb7e79..051d98940 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -153,7 +153,8 @@ type CallDescriptor struct { MaxRate float64 MaxRateUnit time.Duration MaxCostSoFar float64 - CgrId string + CgrID string + RunID string account *Account testCallcost *CallCost // testing purpose only! } diff --git a/engine/responder.go b/engine/responder.go index d9f9510f1..a40a4bfee 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -186,7 +186,7 @@ func (rs *Responder) FakeDebit(arg *CallDescriptor, reply *CallCost) (err error) } func (rs *Responder) MaxDebit(arg *CallDescriptor, reply *CallCost) (err error) { - if item, err := rs.getCache().Get(utils.MAX_DEBIT_CACHE_PREFIX + arg.CgrId); err == nil && item != nil { + if item, err := rs.getCache().Get(utils.MAX_DEBIT_CACHE_PREFIX + arg.CgrID + arg.RunID); err == nil && item != nil { *reply = *(item.Value.(*CallCost)) return item.Err } @@ -216,7 +216,7 @@ func (rs *Responder) MaxDebit(arg *CallDescriptor, reply *CallCost) (err error) } else { r, e := arg.MaxDebit() if e != nil { - rs.getCache().Cache(utils.MAX_DEBIT_CACHE_PREFIX+arg.CgrId, &cache2go.CacheItem{ + rs.getCache().Cache(utils.MAX_DEBIT_CACHE_PREFIX+arg.CgrID+arg.RunID, &cache2go.CacheItem{ Err: e, }) return e @@ -224,7 +224,7 @@ func (rs *Responder) MaxDebit(arg *CallDescriptor, reply *CallCost) (err error) *reply = *r } } - rs.getCache().Cache(utils.MAX_DEBIT_CACHE_PREFIX+arg.CgrId, &cache2go.CacheItem{ + rs.getCache().Cache(utils.MAX_DEBIT_CACHE_PREFIX+arg.CgrID+arg.RunID, &cache2go.CacheItem{ Value: reply, Err: err, }) @@ -232,7 +232,7 @@ func (rs *Responder) MaxDebit(arg *CallDescriptor, reply *CallCost) (err error) } func (rs *Responder) RefundIncrements(arg *CallDescriptor, reply *float64) (err error) { - if item, err := rs.getCache().Get(utils.REFUND_INCR_CACHE_PREFIX + arg.CgrId); err == nil && item != nil { + if item, err := rs.getCache().Get(utils.REFUND_INCR_CACHE_PREFIX + arg.CgrID + arg.RunID); err == nil && item != nil { *reply = *(item.Value.(*float64)) return item.Err } @@ -261,7 +261,7 @@ func (rs *Responder) RefundIncrements(arg *CallDescriptor, reply *float64) (err } else { *reply, err = arg.RefundIncrements() } - rs.getCache().Cache(utils.REFUND_INCR_CACHE_PREFIX+arg.CgrId, &cache2go.CacheItem{ + rs.getCache().Cache(utils.REFUND_INCR_CACHE_PREFIX+arg.CgrID+arg.RunID, &cache2go.CacheItem{ Value: reply, Err: err, }) @@ -358,7 +358,8 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *CDR, reply *float64) error { usage = config.CgrConfig().MaxCallDuration } cd := &CallDescriptor{ - CgrId: ev.GetCgrId(rs.Timezone), + CgrID: ev.GetCgrId(rs.Timezone), + RunID: ev.RunID, TOR: ev.ToR, Direction: ev.GetDirection(dc.DirectionField), Tenant: ev.GetTenant(dc.TenantField), @@ -445,7 +446,8 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { return errors.New("Error parsing answer event end time") } cd := &CallDescriptor{ - CgrId: ev.GetCgrId(rs.Timezone), + CgrID: ev.GetCgrId(rs.Timezone), + RunID: ev.RunID, TOR: ev.ToR, Direction: ev.GetDirection(dc.DirectionField), Tenant: ev.GetTenant(dc.TenantField), diff --git a/engine/responder_test.go b/engine/responder_test.go index b896a15c8..fc54bd46f 100644 --- a/engine/responder_test.go +++ b/engine/responder_test.go @@ -151,13 +151,13 @@ func TestResponderGetSessionRuns(t *testing.T) { sesRuns := make([]*SessionRun, 0) eSRuns := []*SessionRun{ &SessionRun{DerivedCharger: extra1DC, - CallDescriptor: &CallDescriptor{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), Direction: "*out", Category: "0", + CallDescriptor: &CallDescriptor{CgrID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), RunID: "*default", Direction: "*out", Category: "0", Tenant: "vdf", Subject: "rif", Account: "minitsboy", Destination: "0256", TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), TimeEnd: time.Date(2013, 11, 7, 8, 42, 36, 0, time.UTC), TOR: utils.VOICE, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}}, &SessionRun{DerivedCharger: extra2DC, - CallDescriptor: &CallDescriptor{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), Direction: "*out", Category: "call", + CallDescriptor: &CallDescriptor{CgrID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), RunID: "*default", Direction: "*out", Category: "call", Tenant: "vdf", Subject: "ivo", Account: "ivo", Destination: "1002", TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), TimeEnd: time.Date(2013, 11, 7, 8, 42, 36, 0, time.UTC), TOR: utils.VOICE, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}}, &SessionRun{DerivedCharger: dfDC, - CallDescriptor: &CallDescriptor{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), Direction: "*out", Category: "call", + CallDescriptor: &CallDescriptor{CgrID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), RunID: "*default", Direction: "*out", Category: "call", Tenant: "vdf", Subject: "dan2", Account: "dan2", Destination: "1002", TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), TimeEnd: time.Date(2013, 11, 7, 8, 42, 36, 0, time.UTC), TOR: utils.VOICE, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}}}} if err := rsponder.GetSessionRuns(cdr, &sesRuns); err != nil { t.Error(err) diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index fe515fde2..30409089c 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -754,7 +754,8 @@ func TestTutLocalLcrStatic(t *testing.T) { }, } var lcr engine.LCRCost - cd.CgrId = "1" + cd.CgrID = "1" + cd.RunID = "1" if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) { @@ -779,7 +780,8 @@ func TestTutLocalLcrStatic(t *testing.T) { &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 1.2, Duration: 60 * time.Second}, }, } - cd.CgrId = "2" + cd.CgrID = "2" + cd.RunID = "2" if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) { @@ -858,7 +860,8 @@ func TestTutLocalLcrQos(t *testing.T) { } var lcr engine.LCRCost // Since there is no real quality difference, the suppliers will come in random order here - cd.CgrId = "3" + cd.CgrID = "3" + cd.RunID = "3" if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) { @@ -897,7 +900,8 @@ func TestTutLocalLcrQos(t *testing.T) { QOS: map[string]float64{engine.TCD: 90, engine.ACC: 0.325, engine.TCC: 0.325, engine.ASR: 100, engine.ACD: 90}}, }, } - cd.CgrId = "4" + cd.CgrID = "4" + cd.RunID = "4" if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) { @@ -925,7 +929,8 @@ func TestTutLocalLcrQos(t *testing.T) { QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}}, }, } - cd.CgrId = "5" + cd.CgrID = "5" + cd.RunID = "5" if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) { @@ -961,7 +966,8 @@ func TestTutLocalLcrQosThreshold(t *testing.T) { }, } var lcr engine.LCRCost - cd.CgrId = "6" + cd.CgrID = "6" + cd.RunID = "6" if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eLcr.Entry, lcr.Entry) { @@ -987,7 +993,8 @@ func TestTutLocalLcrQosThreshold(t *testing.T) { QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}}, }, } - cd.CgrId = "7" + cd.CgrID = "7" + cd.RunID = "7" if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eLcr.Entry, lcr.Entry) { @@ -1024,7 +1031,8 @@ func TestTutLocalLcrQosThreshold(t *testing.T) { }, } */ - cd.CgrId = "8" + cd.CgrID = "8" + cd.RunID = "8" if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eLcr.Entry, lcr.Entry) { @@ -1049,7 +1057,8 @@ func TestTutLocalLcrQosThreshold(t *testing.T) { QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}}, }, } - cd.CgrId = "9" + cd.CgrID = "9" + cd.RunID = "9" if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eLcr.Entry, lcr.Entry) { @@ -1084,7 +1093,8 @@ func TestTutLocalLeastCost(t *testing.T) { }, } var lcr engine.LCRCost - cd.CgrId = "10" + cd.CgrID = "10" + cd.RunID = "10" if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) { @@ -1116,7 +1126,8 @@ func TestTutLocalLeastCost(t *testing.T) { &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second}, }, } - cd.CgrId = "11" + cd.CgrID = "11" + cd.RunID = "11" if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) { diff --git a/sessionmanager/session.go b/sessionmanager/session.go index 31f765250..061ea84ca 100644 --- a/sessionmanager/session.go +++ b/sessionmanager/session.go @@ -70,7 +70,7 @@ func NewSession(ev engine.Event, connId string, sm SessionManager) *Session { // the debit loop method (to be stoped by sending somenthing on stopDebit channel) func (s *Session) debitLoop(runIdx int) { nextCd := s.sessionRuns[runIdx].CallDescriptor - nextCd.CgrId = s.eventStart.GetCgrId("") + nextCd.CgrID = s.eventStart.GetCgrId("") index := 0.0 debitPeriod := s.sessionManager.DebitInterval() for { From 06d33d0dec2c3f9b948f8217759f68f41ecf1b23 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 28 Jan 2016 18:59:44 +0200 Subject: [PATCH 033/199] fix for wrong cdr cost --- engine/callcost.go | 8 +++++++- engine/calldesc.go | 4 ++-- engine/cdrs.go | 3 +++ engine/timespans.go | 9 ++++----- engine/timespans_test.go | 10 +++++----- sessionmanager/session.go | 2 ++ sessionmanager/smg_session.go | 2 ++ 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/engine/callcost.go b/engine/callcost.go index e786318a7..98984333c 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -162,13 +162,19 @@ func (cc *CallCost) AsJSON() string { return utils.ToJSON(cc) } +// public function to update final (merged) callcost +func (cc *CallCost) UpdateCost() { + cc.deductConnectFee = true + cc.updateCost() +} + func (cc *CallCost) updateCost() { cost := 0.0 if cc.deductConnectFee { // add back the connectFee cost += cc.GetConnectFee() } for _, ts := range cc.Timespans { - ts.Cost = ts.calculateCost() + ts.Cost = ts.CalculateCost() cost += ts.Cost cost = utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) // just get rid of the extra decimals } diff --git a/engine/calldesc.go b/engine/calldesc.go index 051d98940..2ead17ea5 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -458,7 +458,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) { // handle max cost maxCost, strategy := ts.RateInterval.GetMaxCost() - ts.Cost = ts.calculateCost() + ts.Cost = ts.CalculateCost() cost += ts.Cost cd.MaxCostSoFar += cost //log.Print("Before: ", cost) @@ -517,7 +517,7 @@ func (cd *CallDescriptor) getCost() (*CallCost, error) { if cd.LoopIndex == 0 && i == 0 && ts.RateInterval != nil { cost += ts.RateInterval.Rating.ConnectFee } - cost += ts.calculateCost() + cost += ts.CalculateCost() } //startIndex := len(fmt.Sprintf("%s:%s:%s:", cd.Direction, cd.Tenant, cd.Category)) diff --git a/engine/cdrs.go b/engine/cdrs.go index b522b829c..f4a5b0714 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -108,6 +108,7 @@ func (self *CdrServer) ProcessExternalCdr(eCDR *ExternalCDR) error { // RPC method, used to log callcosts to db func (self *CdrServer) LogCallCost(ccl *CallCostLog) error { + ccl.CallCost.UpdateCost() // make sure the total cost reflect the increments ccl.CallCost.UpdateRatedUsage() // make sure rated usage is updated if ccl.CheckDuplicate { _, err := self.guard.Guard(func() (interface{}, error) { @@ -177,6 +178,7 @@ func (self *CdrServer) processCdr(cdr *CDR) (err error) { } if self.cgrCfg.CDRSStoreCdrs { // Store RawCDRs, this we do sync so we can reply with the status if cdr.CostDetails != nil { + cdr.CostDetails.UpdateCost() cdr.CostDetails.UpdateRatedUsage() } if err := self.cdrDb.SetCDR(cdr, false); err != nil { // Only original CDR stored in primary table, no derived @@ -236,6 +238,7 @@ func (self *CdrServer) rateStoreStatsReplicate(cdr *CDR, sendToStats bool) error if self.cgrCfg.CDRSStoreCdrs { // Store CDRs // Store RatedCDR if cdr.CostDetails != nil { + cdr.CostDetails.UpdateCost() cdr.CostDetails.UpdateRatedUsage() } if err := self.cdrDb.SetCDR(cdr, true); err != nil { diff --git a/engine/timespans.go b/engine/timespans.go index b89739552..b7f530d59 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -280,7 +280,7 @@ func (incs Increments) GetTotalCost() float64 { for _, increment := range incs { cost += increment.GetCost() } - return cost + return utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) } func (incs Increments) Length() (length int) { @@ -321,15 +321,14 @@ func (ts *TimeSpan) SetRateInterval(interval *RateInterval) { // Returns the cost of the timespan according to the relevant cost interval. // It also sets the Cost field of this timespan (used for refund on session // manager debit loop where the cost cannot be recalculated) -func (ts *TimeSpan) calculateCost() float64 { +func (ts *TimeSpan) CalculateCost() float64 { if ts.Increments.Length() == 0 { if ts.RateInterval == nil { return 0 } return ts.RateInterval.GetCost(ts.GetDuration(), ts.GetGroupStart()) } else { - cost := ts.Increments.GetTotalCost() - return utils.Round(cost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) + return ts.Increments.GetTotalCost() } } @@ -352,7 +351,7 @@ func (ts *TimeSpan) createIncrementsSlice() { // because ts cost is rounded //incrementCost := rate / rateUnit.Seconds() * rateIncrement.Seconds() nbIncrements := int(ts.GetDuration() / rateIncrement) - incrementCost := ts.calculateCost() / float64(nbIncrements) + incrementCost := ts.CalculateCost() / float64(nbIncrements) incrementCost = utils.Round(incrementCost, ts.RateInterval.Rating.RoundingDecimals, ts.RateInterval.Rating.RoundingMethod) for s := 0; s < nbIncrements; s++ { inc := &Increment{ diff --git a/engine/timespans_test.go b/engine/timespans_test.go index 47b64315d..225cba090 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -214,7 +214,7 @@ func TestTSTimespanGetCost(t *testing.T) { t1 := time.Date(2012, time.February, 5, 17, 45, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 5, 17, 55, 0, 0, time.UTC) ts1 := TimeSpan{TimeStart: t1, TimeEnd: t2} - if ts1.calculateCost() != 0 { + if ts1.CalculateCost() != 0 { t.Error("No interval and still kicking") } ts1.SetRateInterval( @@ -223,12 +223,12 @@ func TestTSTimespanGetCost(t *testing.T) { Rating: &RIRate{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 1 * time.Second}}}, }, ) - if ts1.calculateCost() != 600 { + if ts1.CalculateCost() != 600 { t.Error("Expected 10 got ", ts1.Cost) } ts1.RateInterval = nil ts1.SetRateInterval(&RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{0, 1.0, 1 * time.Second, 60 * time.Second}}}}) - if ts1.calculateCost() != 10 { + if ts1.CalculateCost() != 10 { t.Error("Expected 6000 got ", ts1.Cost) } } @@ -239,8 +239,8 @@ func TestTSTimespanGetCostIntervals(t *testing.T) { for i := 0; i < 11; i++ { ts.Increments[i] = &Increment{Cost: 0.02} } - if ts.calculateCost() != 0.22 { - t.Error("Error caclulating timespan cost: ", ts.calculateCost()) + if ts.CalculateCost() != 0.22 { + t.Error("Error caclulating timespan cost: ", ts.CalculateCost()) } } diff --git a/sessionmanager/session.go b/sessionmanager/session.go index 061ea84ca..91fb952d2 100644 --- a/sessionmanager/session.go +++ b/sessionmanager/session.go @@ -180,6 +180,7 @@ func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error { lastCC.Timespans = lastCC.Timespans[:i] } else { ts.SplitByIncrement(lastRefundedIncrementIndex) + ts.Cost = ts.CalculateCost() } break // do not go to other timespans } else { @@ -214,6 +215,7 @@ func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error { } //utils.Logger.Debug(fmt.Sprintf("REFUND INCR: %s", utils.ToJSON(refundIncrements))) lastCC.Cost -= refundIncrements.GetTotalCost() + lastCC.UpdateRatedUsage() lastCC.Timespans.Compress() return nil } diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 18a86a9b5..d097b91e5 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -132,6 +132,7 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { lastCC.Timespans = lastCC.Timespans[:i] } else { ts.SplitByIncrement(lastRefundedIncrementIndex) + ts.Cost = ts.CalculateCost() } break // do not go to other timespans } else { @@ -166,6 +167,7 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { } //utils.Logger.Debug(fmt.Sprintf("REFUND INCR: %s", utils.ToJSON(refundIncrements))) lastCC.Cost -= refundIncrements.GetTotalCost() + lastCC.UpdateRatedUsage() lastCC.Timespans.Compress() return nil } From 583b53c1437f3a9f80c47e9605d4fc9aad620646 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 29 Jan 2016 10:52:59 +0200 Subject: [PATCH 034/199] moved test data from tut and renamed prepaid1cent --- apier/v1/apier_local_test.go | 4 +- apier/v1/triggers.go | 65 ------------------- .../AccountActions.csv | 1 + .../ActionPlans.csv | 1 + .../ActionTriggers.csv | 0 .../{prepaid1centpsec => testtp}/Actions.csv | 1 + .../{prepaid1centpsec => testtp}/Aliases.csv | 0 .../{prepaid1centpsec => testtp}/CdrStats.csv | 0 .../DerivedChargers.csv | 0 .../DestinationRates.csv | 0 .../Destinations.csv | 0 .../{prepaid1centpsec => testtp}/LcrRules.csv | 0 .../{prepaid1centpsec => testtp}/README.md | 0 .../{prepaid1centpsec => testtp}/Rates.csv | 0 .../RatingPlans.csv | 0 .../RatingProfiles.csv | 0 .../SharedGroups.csv | 0 .../{prepaid1centpsec => testtp}/Timings.csv | 2 +- .../{prepaid1centpsec => testtp}/Users.csv | 0 data/tariffplans/tutorial/AccountActions.csv | 1 - data/tariffplans/tutorial/ActionPlans.csv | 1 - data/tariffplans/tutorial/Actions.csv | 1 - data/tariffplans/tutorial/Timings.csv | 3 +- engine/loader_local_test.go | 2 +- general_tests/multiplecdrc_local_test.go | 2 +- 25 files changed, 9 insertions(+), 75 deletions(-) rename data/tariffplans/{prepaid1centpsec => testtp}/AccountActions.csv (91%) rename data/tariffplans/{prepaid1centpsec => testtp}/ActionPlans.csv (76%) rename data/tariffplans/{prepaid1centpsec => testtp}/ActionTriggers.csv (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/Actions.csv (88%) rename data/tariffplans/{prepaid1centpsec => testtp}/Aliases.csv (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/CdrStats.csv (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/DerivedChargers.csv (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/DestinationRates.csv (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/Destinations.csv (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/LcrRules.csv (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/README.md (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/Rates.csv (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/RatingPlans.csv (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/RatingProfiles.csv (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/SharedGroups.csv (100%) rename data/tariffplans/{prepaid1centpsec => testtp}/Timings.csv (71%) rename data/tariffplans/{prepaid1centpsec => testtp}/Users.csv (100%) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 36b4fc86e..10ced4435 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -52,7 +52,7 @@ README: * Flush tables in storDb to start clean. * Start engine with default configuration and give it some time to listen (here caching can slow down, hence the command argument parameter). * Connect rpc client depending on encoding defined in configuration. - * Execute remote Apis and test their replies(follow prepaid1cent scenario so we can test load in dataDb also). + * Execute remote Apis and test their replies(follow testtp scenario so we can test load in dataDb also). */ var cfgPath string @@ -1226,7 +1226,7 @@ func TestApierLoadTariffPlanFromFolder(t *testing.T) { t.Error(err) } // Simple test that command is executed without errors - attrs = &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "prepaid1centpsec")} + attrs = &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "testtp")} if err := rater.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil { t.Error("Got error on ApierV1.LoadTariffPlanFromFolder: ", err.Error()) } else if reply != "OK" { diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index 1ea6a0fae..59791e49b 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -5,71 +5,6 @@ import ( "github.com/cgrates/cgrates/utils" ) -type AttrAddActionTrigger struct { - ActionTriggersId string - ActionTriggersUniqueId string - Tenant string - Account string - ThresholdType string - ThresholdValue float64 - BalanceId string - BalanceType string - BalanceDirection string - BalanceDestinationIds string - BalanceRatingSubject string //ToDo - BalanceWeight float64 - BalanceExpiryTime string - BalanceSharedGroup string //ToDo - Weight float64 - ActionsId string -} - -func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string) error { - if attr.BalanceDirection == "" { - attr.BalanceDirection = utils.OUT - } - balExpiryTime, err := utils.ParseTimeDetectLayout(attr.BalanceExpiryTime, self.Config.DefaultTimezone) - if err != nil { - return utils.NewErrServerError(err) - } - at := &engine.ActionTrigger{ - ID: attr.ActionTriggersId, - UniqueID: attr.ActionTriggersUniqueId, - ThresholdType: attr.ThresholdType, - ThresholdValue: attr.ThresholdValue, - BalanceId: attr.BalanceId, - BalanceType: attr.BalanceType, - BalanceDirections: utils.ParseStringMap(attr.BalanceDirection), - BalanceDestinationIds: utils.ParseStringMap(attr.BalanceDestinationIds), - BalanceWeight: attr.BalanceWeight, - BalanceExpirationDate: balExpiryTime, - Weight: attr.Weight, - ActionsId: attr.ActionsId, - Executed: false, - } - - tag := utils.AccountKey(attr.Tenant, attr.Account) - _, err = engine.Guardian.Guard(func() (interface{}, error) { - userBalance, err := self.AccountDb.GetAccount(tag) - if err != nil { - return 0, err - } - - userBalance.ActionTriggers = append(userBalance.ActionTriggers, at) - - if err = self.AccountDb.SetAccount(userBalance); err != nil { - return 0, err - } - return 0, nil - }, 0, tag) - if err != nil { - *reply = err.Error() - return err - } - *reply = OK - return nil -} - type AttrSetAccountActionTriggers struct { Tenant string Account string diff --git a/data/tariffplans/prepaid1centpsec/AccountActions.csv b/data/tariffplans/testtp/AccountActions.csv similarity index 91% rename from data/tariffplans/prepaid1centpsec/AccountActions.csv rename to data/tariffplans/testtp/AccountActions.csv index 1e3d995b5..ca940949b 100644 --- a/data/tariffplans/prepaid1centpsec/AccountActions.csv +++ b/data/tariffplans/testtp/AccountActions.csv @@ -4,3 +4,4 @@ cgrates.org,1002,PREPAID_10,STANDARD_TRIGGERS,, cgrates.org,1003,PREPAID_10,STANDARD_TRIGGERS,, cgrates.org,1004,PREPAID_10,STANDARD_TRIGGERS,, cgrates.org,1005,PREPAID_10,STANDARD_TRIGGERS,, +cgrates.org,1009,TEST_EXE,,, \ No newline at end of file diff --git a/data/tariffplans/prepaid1centpsec/ActionPlans.csv b/data/tariffplans/testtp/ActionPlans.csv similarity index 76% rename from data/tariffplans/prepaid1centpsec/ActionPlans.csv rename to data/tariffplans/testtp/ActionPlans.csv index 756ee2ede..670cf8c93 100644 --- a/data/tariffplans/prepaid1centpsec/ActionPlans.csv +++ b/data/tariffplans/testtp/ActionPlans.csv @@ -1,3 +1,4 @@ #Tag,ActionsTag,TimingTag,Weight PREPAID_10,PREPAID_10,ASAP,10 PREPAID_10,BONUS_1,ASAP,10 +TEST_EXE,TOPUP_EXE,ALWAYS,10 \ No newline at end of file diff --git a/data/tariffplans/prepaid1centpsec/ActionTriggers.csv b/data/tariffplans/testtp/ActionTriggers.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/ActionTriggers.csv rename to data/tariffplans/testtp/ActionTriggers.csv diff --git a/data/tariffplans/prepaid1centpsec/Actions.csv b/data/tariffplans/testtp/Actions.csv similarity index 88% rename from data/tariffplans/prepaid1centpsec/Actions.csv rename to data/tariffplans/testtp/Actions.csv index 2729b8c77..045fc0666 100644 --- a/data/tariffplans/prepaid1centpsec/Actions.csv +++ b/data/tariffplans/testtp/Actions.csv @@ -4,3 +4,4 @@ BONUS_1,*topup,,,,*monetary,*out,,*any,,,*unlimited,,1,10,false,false,10 LOG_BALANCE,*log,,,,,,,,,,,,,,false,false,10 CDRST_WARN_HTTP,*call_url,http://localhost:8080,,,,,,,,,,,,,false,false,10 CDRST_LOG,*log,,,,,,,,,,,,,,false,false,10 +TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 \ No newline at end of file diff --git a/data/tariffplans/prepaid1centpsec/Aliases.csv b/data/tariffplans/testtp/Aliases.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/Aliases.csv rename to data/tariffplans/testtp/Aliases.csv diff --git a/data/tariffplans/prepaid1centpsec/CdrStats.csv b/data/tariffplans/testtp/CdrStats.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/CdrStats.csv rename to data/tariffplans/testtp/CdrStats.csv diff --git a/data/tariffplans/prepaid1centpsec/DerivedChargers.csv b/data/tariffplans/testtp/DerivedChargers.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/DerivedChargers.csv rename to data/tariffplans/testtp/DerivedChargers.csv diff --git a/data/tariffplans/prepaid1centpsec/DestinationRates.csv b/data/tariffplans/testtp/DestinationRates.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/DestinationRates.csv rename to data/tariffplans/testtp/DestinationRates.csv diff --git a/data/tariffplans/prepaid1centpsec/Destinations.csv b/data/tariffplans/testtp/Destinations.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/Destinations.csv rename to data/tariffplans/testtp/Destinations.csv diff --git a/data/tariffplans/prepaid1centpsec/LcrRules.csv b/data/tariffplans/testtp/LcrRules.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/LcrRules.csv rename to data/tariffplans/testtp/LcrRules.csv diff --git a/data/tariffplans/prepaid1centpsec/README.md b/data/tariffplans/testtp/README.md similarity index 100% rename from data/tariffplans/prepaid1centpsec/README.md rename to data/tariffplans/testtp/README.md diff --git a/data/tariffplans/prepaid1centpsec/Rates.csv b/data/tariffplans/testtp/Rates.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/Rates.csv rename to data/tariffplans/testtp/Rates.csv diff --git a/data/tariffplans/prepaid1centpsec/RatingPlans.csv b/data/tariffplans/testtp/RatingPlans.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/RatingPlans.csv rename to data/tariffplans/testtp/RatingPlans.csv diff --git a/data/tariffplans/prepaid1centpsec/RatingProfiles.csv b/data/tariffplans/testtp/RatingProfiles.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/RatingProfiles.csv rename to data/tariffplans/testtp/RatingProfiles.csv diff --git a/data/tariffplans/prepaid1centpsec/SharedGroups.csv b/data/tariffplans/testtp/SharedGroups.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/SharedGroups.csv rename to data/tariffplans/testtp/SharedGroups.csv diff --git a/data/tariffplans/prepaid1centpsec/Timings.csv b/data/tariffplans/testtp/Timings.csv similarity index 71% rename from data/tariffplans/prepaid1centpsec/Timings.csv rename to data/tariffplans/testtp/Timings.csv index f6c4a6ffb..8e6f78589 100644 --- a/data/tariffplans/prepaid1centpsec/Timings.csv +++ b/data/tariffplans/testtp/Timings.csv @@ -1,3 +1,3 @@ #Tag,Years,Months,MonthDays,WeekDays,Time ALWAYS,*any,*any,*any,*any,00:00:00 -ASAP,*any,*any,*any,*any,*asap +ASAP,*any,*any,*any,*any,*asap \ No newline at end of file diff --git a/data/tariffplans/prepaid1centpsec/Users.csv b/data/tariffplans/testtp/Users.csv similarity index 100% rename from data/tariffplans/prepaid1centpsec/Users.csv rename to data/tariffplans/testtp/Users.csv diff --git a/data/tariffplans/tutorial/AccountActions.csv b/data/tariffplans/tutorial/AccountActions.csv index bfdad1dd0..97bcd5f64 100644 --- a/data/tariffplans/tutorial/AccountActions.csv +++ b/data/tariffplans/tutorial/AccountActions.csv @@ -4,4 +4,3 @@ cgrates.org,1002,PACKAGE_10,STANDARD_TRIGGERS,, cgrates.org,1003,PACKAGE_10,STANDARD_TRIGGERS,, cgrates.org,1004,PACKAGE_10,STANDARD_TRIGGERS,, cgrates.org,1007,USE_SHARED_A,STANDARD_TRIGGERS,, -cgrates.org,1009,TEST_EXE,,, diff --git a/data/tariffplans/tutorial/ActionPlans.csv b/data/tariffplans/tutorial/ActionPlans.csv index ec5a53cce..3d59fe3e9 100644 --- a/data/tariffplans/tutorial/ActionPlans.csv +++ b/data/tariffplans/tutorial/ActionPlans.csv @@ -6,4 +6,3 @@ USE_SHARED_A,SHARED_A_0,*asap,10 PACKAGE_1001,TOPUP_RST_5,*asap,10 PACKAGE_1001,TOPUP_RST_SHARED_5,*asap,10 PACKAGE_1001,TOPUP_120_DST1003,*asap,10 -TEST_EXE,TOPUP_EXE,DAILY,10 \ No newline at end of file diff --git a/data/tariffplans/tutorial/Actions.csv b/data/tariffplans/tutorial/Actions.csv index 690285fc3..93a3faa1f 100644 --- a/data/tariffplans/tutorial/Actions.csv +++ b/data/tariffplans/tutorial/Actions.csv @@ -4,7 +4,6 @@ TOPUP_RST_5,*topup_reset,,,,*monetary,*out,,*any,,,*unlimited,,5,20,false,false, TOPUP_RST_5,*topup_reset,,,,*voice,*out,,DST_1002,SPECIAL_1002,,*unlimited,,90,20,false,false,10 TOPUP_120_DST1003,*topup_reset,,,,*voice,*out,,DST_1003,,,*unlimited,,120,20,false,false,10 TOPUP_RST_SHARED_5,*topup,,,,*monetary,*out,,*any,,SHARED_A,*unlimited,,5,10,false,false,10 -TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 SHARED_A_0,*topup_reset,,,,*monetary,*out,,*any,,SHARED_A,*unlimited,,0,10,false,false,10 LOG_WARNING,*log,,,,,,,,,,,,,,false,false,10 DISABLE_AND_LOG,*log,,,,,,,,,,,,,,false,false,10 diff --git a/data/tariffplans/tutorial/Timings.csv b/data/tariffplans/tutorial/Timings.csv index d957c710f..d5018f529 100644 --- a/data/tariffplans/tutorial/Timings.csv +++ b/data/tariffplans/tutorial/Timings.csv @@ -4,5 +4,4 @@ ASAP,*any,*any,*any,*any,*asap PEAK,*any,*any,*any,1;2;3;4;5,08:00:00 OFFPEAK_MORNING,*any,*any,*any,1;2;3;4;5,00:00:00 OFFPEAK_EVENING,*any,*any,*any,1;2;3;4;5,19:00:00 -OFFPEAK_WEEKEND,*any,*any,*any,6;7,00:00:00 -DAILY,*any,*any,*any,*any,00:00:00 \ No newline at end of file +OFFPEAK_WEEKEND,*any,*any,*any,6;7,00:00:00 \ No newline at end of file diff --git a/engine/loader_local_test.go b/engine/loader_local_test.go index 0320f6840..e03f8e485 100644 --- a/engine/loader_local_test.go +++ b/engine/loader_local_test.go @@ -47,7 +47,7 @@ var accountDbCsv, accountDbStor, accountDbApier AccountingStorage // Each rating var storDb LoadStorage var lCfg *config.CGRConfig -var tpCsvScenario = flag.String("tp_scenario", "prepaid1centpsec", "Use this scenario folder to import tp csv data from") +var tpCsvScenario = flag.String("tp_scenario", "testtp", "Use this scenario folder to import tp csv data from") // Create connection to ratingDb // Will use 3 different datadbs in order to be able to see differences in data loaded diff --git a/general_tests/multiplecdrc_local_test.go b/general_tests/multiplecdrc_local_test.go index 0d393554e..651404bda 100644 --- a/general_tests/multiplecdrc_local_test.go +++ b/general_tests/multiplecdrc_local_test.go @@ -135,7 +135,7 @@ func TestMCDRCApierLoadTariffPlanFromFolder(t *testing.T) { } reply := "" // Simple test that command is executed without errors - attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "prepaid1centpsec")} + attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "testtp")} if err := rater.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil { t.Error("Got error on ApierV1.LoadTariffPlanFromFolder: ", err.Error()) } else if reply != "OK" { From cb8023eb22c26df57cbd036343f663307e4359ee Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 29 Jan 2016 12:21:50 +0200 Subject: [PATCH 035/199] fix compilation and tests --- apier/v1/accounts.go | 56 ------------------------------ apier/v1/apier_local_test.go | 15 ++++---- apier/v1/triggers.go | 63 ++++++++++++++++++++++++++++++++-- console/triggeredaction_add.go | 62 --------------------------------- 4 files changed, 67 insertions(+), 129 deletions(-) delete mode 100644 console/triggeredaction_add.go diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index fa2991869..15f660133 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -21,7 +21,6 @@ package v1 import ( "fmt" "math" - "regexp" "strings" "time" @@ -131,61 +130,6 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e return nil } -// Returns a list of ActionTriggers on an account -func (self *ApierV1) GetAccountActionTriggers(attrs AttrAcntAction, reply *engine.ActionTriggers) error { - if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account"}); len(missing) != 0 { - return utils.NewErrMandatoryIeMissing(missing...) - } - if balance, err := self.AccountDb.GetAccount(utils.AccountKey(attrs.Tenant, attrs.Account)); err != nil { - return utils.NewErrServerError(err) - } else { - *reply = balance.ActionTriggers - } - return nil -} - -type AttrRemAcntActionTriggers struct { - Tenant string // Tenant he account belongs to - Account string // Account name - ActionTriggersId string // Id filtering only specific id to remove (can be regexp pattern) - ActionTriggersUniqueId string -} - -// Returns a list of ActionTriggers on an account -func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, reply *string) error { - if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account"}); len(missing) != 0 { - return utils.NewErrMandatoryIeMissing(missing...) - } - accID := utils.AccountKey(attrs.Tenant, attrs.Account) - _, err := engine.Guardian.Guard(func() (interface{}, error) { - ub, err := self.AccountDb.GetAccount(accID) - if err != nil { - return 0, err - } - nactrs := make(engine.ActionTriggers, 0) - for _, actr := range ub.ActionTriggers { - match, _ := regexp.MatchString(attrs.ActionTriggersId, actr.ID) - if len(attrs.ActionTriggersId) != 0 && match { - continue - } - if len(attrs.ActionTriggersUniqueId) != 0 && attrs.ActionTriggersUniqueId == actr.UniqueID { - continue - } - nactrs = append(nactrs, actr) - } - ub.ActionTriggers = nactrs - if err := self.AccountDb.SetAccount(ub); err != nil { - return 0, err - } - return 0, nil - }, 0, accID) - if err != nil { - return utils.NewErrServerError(err) - } - *reply = OK - return nil -} - // Ads a new account into dataDb. If already defined, returns success. func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 10ced4435..fb28458b8 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1009,20 +1009,19 @@ func TestApierAddTriggeredAction(t *testing.T) { } reply := "" // Add balance to a previously known account - attrs := &AttrAddActionTrigger{ActionTriggersId: "STTR", ActionTriggersUniqueId: "1", Tenant: "cgrates.org", Account: "dan2", BalanceDirection: "*out", BalanceType: "*monetary", - ThresholdType: "*min_balance", ThresholdValue: 2, BalanceDestinationIds: "*any", Weight: 10, ActionsId: "WARN_VIA_HTTP"} - if err := rater.Call("ApierV1.AddTriggeredAction", attrs, &reply); err != nil { - t.Error("Got error on ApierV1.AddTriggeredAction: ", err.Error()) + attrs := &AttrSetAccountActionTriggers{ActionTriggerIDs: &[]string{"STANDARD_TRIGGERS"}, Tenant: "cgrates.org", Account: "dan2"} + if err := rater.Call("ApierV1.SetAccountActionTriggers", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.SetAccountActionTriggers: ", err.Error()) } else if reply != "OK" { - t.Errorf("Calling ApierV1.AddTriggeredAction received: %s", reply) + t.Errorf("Calling ApierV1.SetAccountActionTriggers received: %s", reply) } reply2 := "" - attrs2 := new(AttrAddActionTrigger) + attrs2 := new(AttrSetAccountActionTriggers) *attrs2 = *attrs attrs2.Account = "dan10" // Does not exist so it should error when adding triggers on it // Add trigger to an account which does n exist - if err := rater.Call("ApierV1.AddTriggeredAction", attrs2, &reply2); err == nil || reply2 == "OK" { - t.Error("Expecting error on ApierV1.AddTriggeredAction.", err, reply2) + if err := rater.Call("ApierV1.SetAccountActionTriggers", attrs2, &reply2); err == nil || reply2 == "OK" { + t.Error("Expecting error on ApierV1.SetAccountActionTriggers.", err, reply2) } } diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index 59791e49b..ae39f7d13 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -1,14 +1,71 @@ package v1 import ( + "regexp" + "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" ) +// Returns a list of ActionTriggers on an account +func (self *ApierV1) GetAccountActionTriggers(attrs AttrAcntAction, reply *engine.ActionTriggers) error { + if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if balance, err := self.AccountDb.GetAccount(utils.AccountKey(attrs.Tenant, attrs.Account)); err != nil { + return utils.NewErrServerError(err) + } else { + *reply = balance.ActionTriggers + } + return nil +} + +type AttrRemAcntActionTriggers struct { + Tenant string // Tenant he account belongs to + Account string // Account name + ActionTriggersId string // Id filtering only specific id to remove (can be regexp pattern) + ActionTriggersUniqueId string +} + +// Returns a list of ActionTriggers on an account +func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, reply *string) error { + if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + accID := utils.AccountKey(attrs.Tenant, attrs.Account) + _, err := engine.Guardian.Guard(func() (interface{}, error) { + ub, err := self.AccountDb.GetAccount(accID) + if err != nil { + return 0, err + } + nactrs := make(engine.ActionTriggers, 0) + for _, actr := range ub.ActionTriggers { + match, _ := regexp.MatchString(attrs.ActionTriggersId, actr.ID) + if len(attrs.ActionTriggersId) != 0 && match { + continue + } + if len(attrs.ActionTriggersUniqueId) != 0 && attrs.ActionTriggersUniqueId == actr.UniqueID { + continue + } + nactrs = append(nactrs, actr) + } + ub.ActionTriggers = nactrs + if err := self.AccountDb.SetAccount(ub); err != nil { + return 0, err + } + return 0, nil + }, 0, accID) + if err != nil { + return utils.NewErrServerError(err) + } + *reply = OK + return nil +} + type AttrSetAccountActionTriggers struct { Tenant string Account string - ActionTriggersIDs *[]string + ActionTriggerIDs *[]string ActionTriggerOverwrite bool } @@ -24,11 +81,11 @@ func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, } else { return 0, err } - if attr.ActionTriggersIDs != nil { + if attr.ActionTriggerIDs != nil { if attr.ActionTriggerOverwrite { account.ActionTriggers = make(engine.ActionTriggers, 0) } - for _, actionTriggerID := range *attr.ActionTriggersIDs { + for _, actionTriggerID := range *attr.ActionTriggerIDs { atrs, err := self.RatingDb.GetActionTriggers(actionTriggerID) if err != nil { diff --git a/console/triggeredaction_add.go b/console/triggeredaction_add.go deleted file mode 100644 index 298782b6c..000000000 --- a/console/triggeredaction_add.go +++ /dev/null @@ -1,62 +0,0 @@ -/* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2012-2015 ITsysCOM - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package console - -import "github.com/cgrates/cgrates/apier/v1" - -func init() { - c := &CmdAddTriggeredAction{ - name: "triggeredaction_add", - rpcMethod: "ApierV1.AddTriggeredAction", - } - commands[c.Name()] = c - c.CommandExecuter = &CommandExecuter{c} -} - -// Commander implementation -type CmdAddTriggeredAction struct { - name string - rpcMethod string - rpcParams *v1.AttrAddActionTrigger - *CommandExecuter -} - -func (self *CmdAddTriggeredAction) Name() string { - return self.name -} - -func (self *CmdAddTriggeredAction) RpcMethod() string { - return self.rpcMethod -} - -func (self *CmdAddTriggeredAction) RpcParams(reset bool) interface{} { - if reset || self.rpcParams == nil { - self.rpcParams = &v1.AttrAddActionTrigger{BalanceDirection: "*out"} - } - return self.rpcParams -} - -func (self *CmdAddTriggeredAction) PostprocessRpcParams() error { - return nil -} - -func (self *CmdAddTriggeredAction) RpcResult() interface{} { - var s string - return &s -} From aa43ed7ca93f8129d08821224820b3dd42e9d0ed Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 29 Jan 2016 19:07:46 +0200 Subject: [PATCH 036/199] tests and fixes for action triggers APIs --- apier/v1/apier_local_test.go | 23 +++++++++-------- apier/v1/triggers.go | 50 ++++-------------------------------- 2 files changed, 18 insertions(+), 55 deletions(-) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index fb28458b8..264dedbfd 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1034,7 +1034,7 @@ func TestApierGetAccountActionTriggers(t *testing.T) { req := AttrAcntAction{Tenant: "cgrates.org", Account: "dan2"} if err := rater.Call("ApierV1.GetAccountActionTriggers", req, &reply); err != nil { t.Error("Got error on ApierV1.GetAccountActionTimings: ", err.Error()) - } else if len(reply) != 1 || reply[0].ActionsId != "WARN_VIA_HTTP" { + } else if len(reply) != 1 || reply[0].ActionsId != "LOG_BALANCE" { t.Errorf("Unexpected action triggers received %v", reply) } } @@ -1049,13 +1049,16 @@ func TestApierRemAccountActionTriggers(t *testing.T) { req := AttrAcntAction{Tenant: "cgrates.org", Account: "dan2"} if err := rater.Call("ApierV1.GetAccountActionTriggers", req, &reply); err != nil { t.Error("Got error on ApierV1.GetAccountActionTimings: ", err.Error()) - } else if len(reply) != 1 || reply[0].ActionsId != "WARN_VIA_HTTP" { + } else if len(reply) != 1 || reply[0].ActionsId != "LOG_BALANCE" { + for _, atr := range reply { + t.Logf("ATR: %+v", atr) + } t.Errorf("Unexpected action triggers received %v", reply) } var rmReply string - rmReq := AttrRemAcntActionTriggers{Tenant: "cgrates.org", Account: "dan2", ActionTriggersUniqueId: reply[0].UniqueID} - if err := rater.Call("ApierV1.RemAccountActionTriggers", rmReq, &rmReply); err != nil { - t.Error("Got error on ApierV1.RemActionTiming: ", err.Error()) + rmReq := AttrRemoveAccountActionTriggers{Tenant: "cgrates.org", Account: "dan2", UniqueID: reply[0].UniqueID} + if err := rater.Call("ApierV1.RemoveAccountActionTriggers", rmReq, &rmReply); err != nil { + t.Error("Got error on ApierV1.RemoveActionTiming: ", err.Error()) } else if rmReply != OK { t.Error("Unexpected answer received", rmReply) } @@ -1101,13 +1104,13 @@ func TestApierGetAccountActionPlan(t *testing.T) { t.Error("Unexpected action plan received") } else { if reply[0].ActionPlanId != "ATMS_1" { - t.Errorf("Unexpected ActionPlanId received") + t.Errorf("Unexpected ActionoveAccountPlanId received") } } } -// Test here RemActionTiming -func TestApierRemActionTiming(t *testing.T) { +// Test here RemoveActionTiming +func TestApierRemUniqueIDActionTiming(t *testing.T) { if !*testLocal { return } @@ -1254,9 +1257,9 @@ func TestApierResetDataAfterLoadFromFolder(t *testing.T) { if rcvStats.Destinations != 4 || rcvStats.RatingPlans != 3 || rcvStats.RatingProfiles != 3 || - rcvStats.Actions != 5 || + rcvStats.Actions != 6 || rcvStats.DerivedChargers != 2 { - t.Errorf("Calling ApierV1.GetCacheStats received: %v", rcvStats) + t.Errorf("Calling ApierV1.GetCacheStats received: %+v", rcvStats) } } } diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index ae39f7d13..908a315c4 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -1,8 +1,6 @@ package v1 import ( - "regexp" - "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" ) @@ -12,53 +10,15 @@ func (self *ApierV1) GetAccountActionTriggers(attrs AttrAcntAction, reply *engin if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - if balance, err := self.AccountDb.GetAccount(utils.AccountKey(attrs.Tenant, attrs.Account)); err != nil { + if account, err := self.AccountDb.GetAccount(utils.AccountKey(attrs.Tenant, attrs.Account)); err != nil { return utils.NewErrServerError(err) } else { - *reply = balance.ActionTriggers - } - return nil -} - -type AttrRemAcntActionTriggers struct { - Tenant string // Tenant he account belongs to - Account string // Account name - ActionTriggersId string // Id filtering only specific id to remove (can be regexp pattern) - ActionTriggersUniqueId string -} - -// Returns a list of ActionTriggers on an account -func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, reply *string) error { - if missing := utils.MissingStructFields(&attrs, []string{"Tenant", "Account"}); len(missing) != 0 { - return utils.NewErrMandatoryIeMissing(missing...) - } - accID := utils.AccountKey(attrs.Tenant, attrs.Account) - _, err := engine.Guardian.Guard(func() (interface{}, error) { - ub, err := self.AccountDb.GetAccount(accID) - if err != nil { - return 0, err + ats := account.ActionTriggers + if ats == nil { + ats = engine.ActionTriggers{} } - nactrs := make(engine.ActionTriggers, 0) - for _, actr := range ub.ActionTriggers { - match, _ := regexp.MatchString(attrs.ActionTriggersId, actr.ID) - if len(attrs.ActionTriggersId) != 0 && match { - continue - } - if len(attrs.ActionTriggersUniqueId) != 0 && attrs.ActionTriggersUniqueId == actr.UniqueID { - continue - } - nactrs = append(nactrs, actr) - } - ub.ActionTriggers = nactrs - if err := self.AccountDb.SetAccount(ub); err != nil { - return 0, err - } - return 0, nil - }, 0, accID) - if err != nil { - return utils.NewErrServerError(err) + *reply = ats } - *reply = OK return nil } From d09ae04f2ac0055bf3b6fcd1954d63d7443beffa Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Sat, 30 Jan 2016 18:45:54 +0200 Subject: [PATCH 037/199] fix for removing account from action plan --- apier/v1/accounts.go | 81 ++++++++++++++++++++++++---------- apier/v1/apier.go | 2 +- apier/v1/apier_local_test.go | 5 +++ apier/v2/accounts.go | 8 +++- engine/action.go | 28 +++++++----- engine/storage_interface.go | 2 +- engine/storage_map.go | 18 ++++---- engine/storage_mongo_datadb.go | 18 ++++---- engine/storage_redis.go | 18 ++++---- engine/tp_reader.go | 4 +- 10 files changed, 120 insertions(+), 64 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 15f660133..e00faee96 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -95,7 +95,7 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e if attrs.ActionPlanId != "" { // delete the entire action plan ap.ActionTimings = nil // will delete the action plan - return 0, self.RatingDb.SetActionPlan(ap.Id, ap) + return 0, self.RatingDb.SetActionPlan(ap.Id, ap, true) } if attrs.ActionTimingId != "" { // delete only a action timing from action plan @@ -106,13 +106,13 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e break } } - return 0, self.RatingDb.SetActionPlan(ap.Id, ap) + return 0, self.RatingDb.SetActionPlan(ap.Id, ap, true) } if attrs.Tenant != "" && attrs.Account != "" { accID := utils.AccountKey(attrs.Tenant, attrs.Account) delete(ap.AccountIDs, accID) - return 0, self.RatingDb.SetActionPlan(ap.Id, ap) + return 0, self.RatingDb.SetActionPlan(ap.Id, ap, true) } // update cache @@ -148,8 +148,30 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error } if len(attr.ActionPlanId) != 0 { _, err := engine.Guardian.Guard(func() (interface{}, error) { + // clean previous action plans + actionPlansMap, err := self.RatingDb.GetAllActionPlans() + if err != nil { + return 0, err + } + var dirtyAps []string + for actionPlanID, ap := range actionPlansMap { + if actionPlanID == attr.ActionPlanId { + // don't remove it if it's the current one + continue + } + if _, exists := ap.AccountIDs[accID]; exists { + delete(ap.AccountIDs, accID) + dirtyAps = append(dirtyAps, utils.ACTION_PLAN_PREFIX+actionPlanID) + } + } + + if len(dirtyAps) > 0 { + // update cache + self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: dirtyAps}) + schedulerReloadNeeded = true + } + var ap *engine.ActionPlan - var err error ap, err = self.RatingDb.GetActionPlan(attr.ActionPlanId, false) if err != nil { return 0, err @@ -173,7 +195,7 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error } } } - if err := self.RatingDb.SetActionPlan(attr.ActionPlanId, ap); err != nil { + if err := self.RatingDb.SetActionPlan(attr.ActionPlanId, ap, false); err != nil { return 0, err } // update cache @@ -223,39 +245,50 @@ func (self *ApierV1) RemoveAccount(attr utils.AttrRemoveAccount, reply *string) if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } + dirtyActionPlans := make(map[string]*engine.ActionPlan) accID := utils.AccountKey(attr.Tenant, attr.Account) - var schedulerReloadNeeded bool _, err := engine.Guardian.Guard(func() (interface{}, error) { // remove it from all action plans - allAPs, err := self.RatingDb.GetAllActionPlans() - if err != nil && err != utils.ErrNotFound { - return 0, err - } - for key, ap := range allAPs { - if _, exists := ap.AccountIDs[accID]; !exists { - schedulerReloadNeeded = true - _, err := engine.Guardian.Guard(func() (interface{}, error) { - // save action plan - self.RatingDb.SetActionPlan(key, ap) - // cache - self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + key}}) - return 0, nil - }, 0, utils.ACTION_PLAN_PREFIX) - if err != nil { - return 0, err + _, err := engine.Guardian.Guard(func() (interface{}, error) { + actionPlansMap, err := self.RatingDb.GetAllActionPlans() + if err != nil { + return 0, err + } + + for actionPlanID, ap := range actionPlansMap { + if _, exists := ap.AccountIDs[accID]; exists { + delete(ap.AccountIDs, accID) + dirtyActionPlans[actionPlanID] = ap } } + + var actionPlansCacheIds []string + for actionPlanID, ap := range dirtyActionPlans { + if err := self.RatingDb.SetActionPlan(actionPlanID, ap, true); err != nil { + return 0, err + } + actionPlansCacheIds = append(actionPlansCacheIds, utils.ACTION_PLAN_PREFIX+actionPlanID) + } + if len(actionPlansCacheIds) > 0 { + // update cache + self.RatingDb.CacheRatingPrefixValues(map[string][]string{ + utils.ACTION_PLAN_PREFIX: actionPlansCacheIds}) + } + return 0, nil + }, 0, utils.ACTION_PLAN_PREFIX) + if err != nil { + return 0, err } + if err := self.AccountDb.RemoveAccount(accID); err != nil { return 0, err } return 0, nil }, 0, accID) - // FIXME: remove from all actionplans? if err != nil { return utils.NewErrServerError(err) } - if schedulerReloadNeeded { + if attr.ReloadScheduler && len(dirtyActionPlans) > 0 { // reload scheduler if self.Sched != nil { self.Sched.Reload(true) diff --git a/apier/v1/apier.go b/apier/v1/apier.go index dcdfeda45..2b4f4b576 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -624,7 +624,7 @@ func (self *ApierV1) SetActionPlan(attrs AttrSetActionPlan, reply *string) error ActionsID: apiAtm.ActionsId, }) } - if err := self.RatingDb.SetActionPlan(ap.Id, ap); err != nil { + if err := self.RatingDb.SetActionPlan(ap.Id, ap, true); err != nil { return utils.NewErrServerError(err) } self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + attrs.Id}}) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 264dedbfd..e78886dee 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1057,6 +1057,11 @@ func TestApierRemAccountActionTriggers(t *testing.T) { } var rmReply string rmReq := AttrRemoveAccountActionTriggers{Tenant: "cgrates.org", Account: "dan2", UniqueID: reply[0].UniqueID} + if err := rater.Call("ApierV1.ResetAccountActionTriggers", rmReq, &rmReply); err != nil { + t.Error("Got error on ApierV1.ResetActionTiming: ", err.Error()) + } else if rmReply != OK { + t.Error("Unexpected answer received", rmReply) + } if err := rater.Call("ApierV1.RemoveAccountActionTriggers", rmReq, &rmReply); err != nil { t.Error("Got error on ApierV1.RemoveActionTiming: ", err.Error()) } else if rmReply != OK { diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index b852b212d..e0e0f11b9 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -148,12 +148,16 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { } } } + var actionPlansCacheIds []string for actionPlanID, ap := range dirtyActionPlans { - if err := self.RatingDb.SetActionPlan(actionPlanID, ap); err != nil { + if err := self.RatingDb.SetActionPlan(actionPlanID, ap, true); err != nil { return 0, err } + actionPlansCacheIds = append(actionPlansCacheIds, utils.ACTION_PLAN_PREFIX+actionPlanID) + } + if len(actionPlansCacheIds) > 0 { // update cache - self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + actionPlanID}}) + self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: actionPlansCacheIds}) } return 0, nil }, 0, utils.ACTION_PLAN_PREFIX) diff --git a/engine/action.go b/engine/action.go index eac9cb299..0134cbc3c 100644 --- a/engine/action.go +++ b/engine/action.go @@ -559,20 +559,28 @@ func removeAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac utils.Logger.Err(fmt.Sprintf("Could not get action plans: %s: %v", accID, err)) return err } - for key, ap := range allAPs { - if _, exists := ap.AccountIDs[accID]; !exists { - _, err := Guardian.Guard(func() (interface{}, error) { + var dirtyAps []string + _, err = Guardian.Guard(func() (interface{}, error) { + for key, ap := range allAPs { + if _, exists := ap.AccountIDs[accID]; !exists { // save action plan - ratingStorage.SetActionPlan(key, ap) - // cache - ratingStorage.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + key}}) - return 0, nil - }, 0, utils.ACTION_PLAN_PREFIX) - if err != nil { - return err + delete(ap.AccountIDs, key) + ratingStorage.SetActionPlan(key, ap, true) + dirtyAps = append(dirtyAps, utils.ACTION_PLAN_PREFIX+key) } } + if len(dirtyAps) > 0 { + // cache + ratingStorage.CacheRatingPrefixValues(map[string][]string{ + utils.ACTION_PLAN_PREFIX: dirtyAps}) + } + return 0, nil + + }, 0, utils.ACTION_PLAN_PREFIX) + if err != nil { + return err } + // TODO: no scheduler reload? return nil } diff --git a/engine/storage_interface.go b/engine/storage_interface.go index fd5781dc5..8d082ae14 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -64,7 +64,7 @@ type RatingStorage interface { GetActionTriggers(string) (ActionTriggers, error) SetActionTriggers(string, ActionTriggers) error GetActionPlan(string, bool) (*ActionPlan, error) - SetActionPlan(string, *ActionPlan) error + SetActionPlan(string, *ActionPlan, bool) error GetAllActionPlans() (map[string]*ActionPlan, error) PushTask(*Task) error PopTask() (*Task, error) diff --git a/engine/storage_map.go b/engine/storage_map.go index 84f5547cf..d1e788745 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -664,20 +664,22 @@ func (ms *MapStorage) GetActionPlan(key string, skipCache bool) (ats *ActionPlan return } -func (ms *MapStorage) SetActionPlan(key string, ats *ActionPlan) (err error) { +func (ms *MapStorage) SetActionPlan(key string, ats *ActionPlan, overwrite bool) (err error) { if len(ats.ActionTimings) == 0 { // delete the key delete(ms.dict, utils.ACTION_PLAN_PREFIX+key) cache2go.RemKey(utils.ACTION_PLAN_PREFIX + key) return } - // get existing action plan to merge the account ids - if existingAts, _ := ms.GetActionPlan(key, true); existingAts != nil { - if ats.AccountIDs == nil && len(existingAts.AccountIDs) > 0 { - ats.AccountIDs = make(utils.StringMap) - } - for accID := range existingAts.AccountIDs { - ats.AccountIDs[accID] = true + if !overwrite { + // get existing action plan to merge the account ids + if existingAts, _ := ms.GetActionPlan(key, true); existingAts != nil { + if ats.AccountIDs == nil && len(existingAts.AccountIDs) > 0 { + ats.AccountIDs = make(utils.StringMap) + } + for accID := range existingAts.AccountIDs { + ats.AccountIDs[accID] = true + } } } result, err := ms.ms.Marshal(&ats) diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index 32dd40197..85cd4f7ef 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -1156,7 +1156,7 @@ func (ms *MongoStorage) GetActionPlan(key string, skipCache bool) (ats *ActionPl return } -func (ms *MongoStorage) SetActionPlan(key string, ats *ActionPlan) error { +func (ms *MongoStorage) SetActionPlan(key string, ats *ActionPlan, overwrite bool) error { // clean dots from account ids map if len(ats.ActionTimings) == 0 { cache2go.RemKey(utils.ACTION_PLAN_PREFIX + key) @@ -1166,13 +1166,15 @@ func (ms *MongoStorage) SetActionPlan(key string, ats *ActionPlan) error { } return nil } - // get existing action plan to merge the account ids - if existingAts, _ := ms.GetActionPlan(key, true); existingAts != nil { - if ats.AccountIDs == nil && len(existingAts.AccountIDs) > 0 { - ats.AccountIDs = make(utils.StringMap) - } - for accID := range existingAts.AccountIDs { - ats.AccountIDs[accID] = true + if !overwrite { + // get existing action plan to merge the account ids + if existingAts, _ := ms.GetActionPlan(key, true); existingAts != nil { + if ats.AccountIDs == nil && len(existingAts.AccountIDs) > 0 { + ats.AccountIDs = make(utils.StringMap) + } + for accID := range existingAts.AccountIDs { + ats.AccountIDs[accID] = true + } } } result, err := ms.ms.Marshal(ats) diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 9ea09ccfc..9c9fe6a51 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -920,20 +920,22 @@ func (rs *RedisStorage) GetActionPlan(key string, skipCache bool) (ats *ActionPl return } -func (rs *RedisStorage) SetActionPlan(key string, ats *ActionPlan) (err error) { +func (rs *RedisStorage) SetActionPlan(key string, ats *ActionPlan, overwrite bool) (err error) { if len(ats.ActionTimings) == 0 { // delete the key err = rs.db.Cmd("DEL", utils.ACTION_PLAN_PREFIX+key).Err cache2go.RemKey(utils.ACTION_PLAN_PREFIX + key) return err } - // get existing action plan to merge the account ids - if existingAts, _ := rs.GetActionPlan(key, true); existingAts != nil { - if ats.AccountIDs == nil && len(existingAts.AccountIDs) > 0 { - ats.AccountIDs = make(utils.StringMap) - } - for accID := range existingAts.AccountIDs { - ats.AccountIDs[accID] = true + if !overwrite { + // get existing action plan to merge the account ids + if existingAts, _ := rs.GetActionPlan(key, true); existingAts != nil { + if ats.AccountIDs == nil && len(existingAts.AccountIDs) > 0 { + ats.AccountIDs = make(utils.StringMap) + } + for accID := range existingAts.AccountIDs { + ats.AccountIDs[accID] = true + } } } diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 9b019074e..444a9bc57 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -760,7 +760,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error } } // write action plan - err = tpr.ratingStorage.SetActionPlan(accountAction.ActionPlanId, actionPlan) + err = tpr.ratingStorage.SetActionPlan(accountAction.ActionPlanId, actionPlan, false) if err != nil { return errors.New(err.Error() + " (SetActionPlan): " + accountAction.ActionPlanId) } @@ -1399,7 +1399,7 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose bool) (err error) { } } } - err = tpr.ratingStorage.SetActionPlan(k, ap) + err = tpr.ratingStorage.SetActionPlan(k, ap, false) if err != nil { return err } From 3ef4a21cb8fbb5a40892f069293873b8eeac5f17 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Sun, 31 Jan 2016 19:38:10 +0200 Subject: [PATCH 038/199] fix for lcr crash --- engine/responder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/responder.go b/engine/responder.go index a40a4bfee..ee6cc8843 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -541,7 +541,7 @@ func (rs *Responder) GetLCR(attrs *AttrGetLcr, reply *LCRCost) error { if err != nil { return err } - if lcrCost.Entry.Strategy == LCR_STRATEGY_LOAD { + if lcrCost.Entry != nil && lcrCost.Entry.Strategy == LCR_STRATEGY_LOAD { for _, suppl := range lcrCost.SupplierCosts { suppl.Cost = -1 // In case of load distribution we don't calculate costs } From e0c790eb1b8f7d12d80ffca90830d510a7d7ae1d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 1 Feb 2016 15:48:12 +0200 Subject: [PATCH 039/199] set balance api fix --- apier/v1/accounts.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index e00faee96..929b19a83 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -543,6 +543,9 @@ type AttrSetBalance struct { } func (attr *AttrSetBalance) SetBalance(b *engine.Balance) { + if b == nil { + return + } if attr.Directions != nil { b.Directions = utils.StringMapFromSlice(*attr.Directions) } @@ -675,7 +678,7 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { // if it is not found then we add it to the list if balance == nil { - balance := &engine.Balance{} + balance = &engine.Balance{} balance.Uuid = utils.GenUUID() // alway overwrite the uuid for consistency account.BalanceMap[attr.BalanceType] = append(account.BalanceMap[attr.BalanceType], balance) } From 369f6e99f01eaa1cfb79afe8cbf84abbb8c1fe67 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 1 Feb 2016 20:51:32 +0200 Subject: [PATCH 040/199] better ApierV1 set account --- apier/v1/accounts.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 929b19a83..bed4ce81c 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -195,7 +195,7 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error } } } - if err := self.RatingDb.SetActionPlan(attr.ActionPlanId, ap, false); err != nil { + if err := self.RatingDb.SetActionPlan(attr.ActionPlanId, ap, true); err != nil { return 0, err } // update cache From b8e2d3141a67e9b06a724483d5b6beb64d71d6ec Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 1 Feb 2016 21:17:29 +0200 Subject: [PATCH 041/199] fixed dmtagent test --- agents/dmtagent_it_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 25f7de3a0..58279745b 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -440,7 +440,7 @@ func TestDmtAgentCdrs(t *testing.T) { if cdrs[0].Usage != "610" { t.Errorf("Unexpected CDR Usage received, cdr: %+v ", cdrs[0]) } - if cdrs[0].Cost != 0.5349 { + if cdrs[0].Cost != 0.795 { t.Errorf("Unexpected CDR Cost received, cdr: %+v ", cdrs[0]) } } From 060a901a6a3f49946bea43b2d3e58d0da286ac9f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 2 Feb 2016 22:14:30 +0200 Subject: [PATCH 042/199] keep counter value on re-init --- engine/account.go | 37 ++++++++++------ engine/action.go | 17 ++++--- engine/units_counter.go | 17 +++++++ engine/units_counter_test.go | 86 ++++++++++++++++++++++++++++++++++++ 4 files changed, 134 insertions(+), 23 deletions(-) diff --git a/engine/account.go b/engine/account.go index 03224b12d..99875285b 100644 --- a/engine/account.go +++ b/engine/account.go @@ -569,9 +569,9 @@ func (ub *Account) refundIncrement(increment *Increment, cd *CallDescriptor, cou } // Scans the action trigers and execute the actions for which trigger is met -func (ub *Account) ExecuteActionTriggers(a *Action) { - ub.ActionTriggers.Sort() - for _, at := range ub.ActionTriggers { +func (acc *Account) ExecuteActionTriggers(a *Action) { + acc.ActionTriggers.Sort() + for _, at := range acc.ActionTriggers { // sanity check if !strings.Contains(at.ThresholdType, "counter") && !strings.Contains(at.ThresholdType, "balance") { continue @@ -585,45 +585,45 @@ func (ub *Account) ExecuteActionTriggers(a *Action) { continue } if strings.Contains(at.ThresholdType, "counter") { - for _, uc := range ub.UnitCounters { + for _, uc := range acc.UnitCounters { if uc.BalanceType == at.BalanceType && strings.Contains(at.ThresholdType, uc.CounterType[1:]) { - for _, mb := range uc.Balances { + for _, b := range uc.Balances { if strings.HasPrefix(at.ThresholdType, "*max") { - if mb.MatchActionTrigger(at) && mb.GetValue() >= at.ThresholdValue { - at.Execute(ub, nil) + if b.MatchActionTrigger(at) && b.GetValue() >= at.ThresholdValue { + at.Execute(acc, nil) } } else { //MIN - if mb.MatchActionTrigger(at) && mb.GetValue() <= at.ThresholdValue { - at.Execute(ub, nil) + if b.MatchActionTrigger(at) && b.GetValue() <= at.ThresholdValue { + at.Execute(acc, nil) } } } } } } else { // BALANCE - for _, b := range ub.BalanceMap[at.BalanceType] { + for _, b := range acc.BalanceMap[at.BalanceType] { if !b.dirty && at.ThresholdType != utils.TRIGGER_BALANCE_EXPIRED { // do not check clean balances continue } switch at.ThresholdType { case utils.TRIGGER_MAX_BALANCE: if b.MatchActionTrigger(at) && b.GetValue() >= at.ThresholdValue { - at.Execute(ub, nil) + at.Execute(acc, nil) } case utils.TRIGGER_MIN_BALANCE: if b.MatchActionTrigger(at) && b.GetValue() <= at.ThresholdValue { - at.Execute(ub, nil) + at.Execute(acc, nil) } case utils.TRIGGER_BALANCE_EXPIRED: if b.MatchActionTrigger(at) && b.IsExpired() { - at.Execute(ub, nil) + at.Execute(acc, nil) } } } } } - ub.CleanExpiredBalances() + acc.CleanExpiredBalances() } // Mark all action trigers as ready for execution @@ -656,6 +656,7 @@ func (acc *Account) countUnits(amount float64, kind string, cc *CallCost, b *Bal // Create counters for all triggered actions func (acc *Account) InitCounters() { + oldUcs := acc.UnitCounters acc.UnitCounters = nil ucTempMap := make(map[string]*UnitCounter) for _, at := range acc.ActionTriggers { @@ -682,6 +683,14 @@ func (acc *Account) InitCounters() { uc.Balances = append(uc.Balances, b) } } + // copy old counter values + for _, uc := range acc.UnitCounters { + for _, oldUc := range oldUcs { + if uc.CopyCounterValues(oldUc) { + break + } + } + } } func (acc *Account) CleanExpiredBalances() { diff --git a/engine/action.go b/engine/action.go index 0134cbc3c..00e23bcb5 100644 --- a/engine/action.go +++ b/engine/action.go @@ -553,14 +553,14 @@ func removeAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac utils.Logger.Err(fmt.Sprintf("Could not remove account Id: %s: %v", accID, err)) return err } - // clean the account id from all action plans - allAPs, err := ratingStorage.GetAllActionPlans() - if err != nil && err != utils.ErrNotFound { - utils.Logger.Err(fmt.Sprintf("Could not get action plans: %s: %v", accID, err)) - return err - } - var dirtyAps []string - _, err = Guardian.Guard(func() (interface{}, error) { + _, err := Guardian.Guard(func() (interface{}, error) { + // clean the account id from all action plans + allAPs, err := ratingStorage.GetAllActionPlans() + if err != nil && err != utils.ErrNotFound { + utils.Logger.Err(fmt.Sprintf("Could not get action plans: %s: %v", accID, err)) + return 0, err + } + var dirtyAps []string for key, ap := range allAPs { if _, exists := ap.AccountIDs[accID]; !exists { // save action plan @@ -580,7 +580,6 @@ func removeAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac if err != nil { return err } - // TODO: no scheduler reload? return nil } diff --git a/engine/units_counter.go b/engine/units_counter.go index a25602504..77aa21f31 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -27,6 +27,23 @@ type UnitCounter struct { Balances BalanceChain // first balance is the general one (no destination) } +// Returns true if the counters were of the same type +// Copies the value from old balances +func (uc *UnitCounter) CopyCounterValues(oldUc *UnitCounter) bool { + if uc.BalanceType+uc.CounterType != oldUc.BalanceType+oldUc.CounterType { // type check + return false + } + for _, b := range uc.Balances { + for _, oldB := range oldUc.Balances { + if b.Equal(oldB) { + b.Value = oldB.Value + break + } + } + } + return true +} + type UnitCounters []*UnitCounter func (ucs UnitCounters) addUnits(amount float64, kind string, cc *CallCost, b *Balance) { diff --git a/engine/units_counter_test.go b/engine/units_counter_test.go index f136c23b7..aeee72cdf 100644 --- a/engine/units_counter_test.go +++ b/engine/units_counter_test.go @@ -246,6 +246,92 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { } } +func TestUnitCountersKeepValuesAfterInit(t *testing.T) { + a := &Account{ + ActionTriggers: ActionTriggers{ + &ActionTrigger{ + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + BalanceType: utils.MONETARY, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceWeight: 10, + }, + &ActionTrigger{ + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + BalanceType: utils.MONETARY, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceWeight: 20, + }, + &ActionTrigger{ + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + BalanceType: utils.VOICE, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceDestinationIds: utils.NewStringMap("NAT"), + BalanceWeight: 10, + }, + &ActionTrigger{ + UniqueID: "TestTR22", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + BalanceType: utils.VOICE, + BalanceDestinationIds: utils.NewStringMap("RET"), + BalanceWeight: 10, + }, + &ActionTrigger{ + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + BalanceType: utils.VOICE, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceWeight: 10, + }, + &ActionTrigger{ + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + BalanceType: utils.SMS, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceWeight: 10, + }, + &ActionTrigger{ + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + BalanceType: utils.SMS, + BalanceDirections: utils.NewStringMap(utils.OUT), + BalanceWeight: 10, + }, + }, + } + a.InitCounters() + a.UnitCounters.addUnits(10, utils.VOICE, &CallCost{Destination: "0723045326"}, nil) + + if len(a.UnitCounters) != 4 || + len(a.UnitCounters[1].Balances) != 2 || + a.UnitCounters[1].Balances[0].Value != 10 || + a.UnitCounters[1].Balances[1].Value != 10 { + for _, uc := range a.UnitCounters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Balances { + t.Logf("B: %+v", b) + } + } + t.Errorf("Error adding unit counters: %v", len(a.UnitCounters)) + } + a.InitCounters() + + if len(a.UnitCounters) != 4 || + len(a.UnitCounters[1].Balances) != 2 || + a.UnitCounters[1].Balances[0].Value != 10 || + a.UnitCounters[1].Balances[1].Value != 10 { + for _, uc := range a.UnitCounters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Balances { + t.Logf("B: %+v", b) + } + } + t.Errorf("Error keeping counter values after init: %v", len(a.UnitCounters)) + } +} + func TestUnitCountersResetCounterById(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ From 54538fc4e5f1a579647267a677b5b4d561b72f90 Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 3 Feb 2016 10:28:34 +0100 Subject: [PATCH 043/199] Better selection of database connections when we start services --- cmd/cgr-engine/cgr-engine.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index a4c54bd3b..1c1da74d7 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -632,7 +632,7 @@ func main() { var logDb engine.LogStorage var loadDb engine.LoadStorage var cdrDb engine.CdrStorage - if cfg.RaterEnabled || cfg.SchedulerEnabled { // Only connect to dataDb if necessary + if cfg.RaterEnabled || cfg.SchedulerEnabled || cfg.CDRStatsEnabled { // Only connect to dataDb if necessary ratingDb, err = engine.ConfigureRatingStorage(cfg.TpDbType, cfg.TpDbHost, cfg.TpDbPort, cfg.TpDbName, cfg.TpDbUser, cfg.TpDbPass, cfg.DBDataEncoding) if err != nil { // Cannot configure getter database, show stopper @@ -641,6 +641,8 @@ func main() { } defer ratingDb.Close() engine.SetRatingStorage(ratingDb) + } + if cfg.RaterEnabled || cfg.CDRStatsEnabled || cfg.PubSubServerEnabled || cfg.AliasesServerEnabled || cfg.UserServerEnabled { accountDb, err = engine.ConfigureAccountingStorage(cfg.DataDbType, cfg.DataDbHost, cfg.DataDbPort, cfg.DataDbName, cfg.DataDbUser, cfg.DataDbPass, cfg.DBDataEncoding) if err != nil { // Cannot configure getter database, show stopper From c0dd58c43874dea174b298248781195f6c65aeb4 Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 3 Feb 2016 13:39:58 +0100 Subject: [PATCH 044/199] Diameter - Return 5030 when no account found --- agents/dmtagent.go | 40 ++++++++++++------- agents/dmtagent_it_test.go | 80 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 104 insertions(+), 16 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index dec21d553..a3624a99a 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -21,6 +21,7 @@ package agents import ( "fmt" "strconv" + "strings" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" @@ -135,16 +136,27 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro } } } - if err != nil { - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), - false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) - return nil - } - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v, API error: %s", ccr.diamMessage, err)) - return cca - } var unauthorizedResultCode bool + if err != nil { + utils.Logger.Debug(fmt.Sprintf("Received error from rater: %+v", err)) + if strings.HasSuffix(err.Error(), utils.ErrAccountNotFound.Error()) { // 5030 in case of AccountNotFound + if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "5030", + false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) + return nil + } + unauthorizedResultCode = true + } else { + if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), + false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) + return nil + } + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v, API error: %s", ccr.diamMessage, err)) + return cca + } + + } if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && maxUsage == 0 { // Not enough balance, RFC demands 4012 if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "4012", false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { @@ -152,10 +164,12 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro return nil } unauthorizedResultCode = true - } else if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(diam.Success), - false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) - return nil + } else if !unauthorizedResultCode { + if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(diam.Success), + false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) + return nil + } } if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && !unauthorizedResultCode { // For terminate or previously marked unauthorized, we don't add granted-service-unit AVP if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Granted-Service-Unit", "CC-Time"}, strconv.FormatFloat(maxUsage, 'f', 0, 64), diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 58279745b..6d1dc1fe0 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -179,8 +179,7 @@ func TestDmtAgentTPFromFolder(t *testing.T) { time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups } -// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:47:26Z"' -func TestDmtAgentSendCCRInit(t *testing.T) { +func TestConnectDiameterClient(t *testing.T) { if !*testIntegration { return } @@ -189,6 +188,13 @@ func TestDmtAgentSendCCRInit(t *testing.T) { if err != nil { t.Fatal(err) } +} + +// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:47:26Z"' +func TestDmtAgentSendCCRInit(t *testing.T) { + if !*testIntegration { + return + } cdr := &engine.CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", Supplier: "SUPPL1", @@ -426,6 +432,74 @@ func TestDmtAgentSendCCRSMS(t *testing.T) { */ } +func TestDmtAgentSendCCRSMSWrongAccount(t *testing.T) { + if !*testIntegration { + return + } + ccr := diam.NewRequest(diam.CreditControl, 4, nil) + ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("cgrates;1451911932;00083")) + ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) + ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) + ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) + ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("message@huawei.com")) + ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(4)) + ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(0)) + ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) + ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)), + diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("non_existent")), // Subscription-Id-Data + }}) + ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)), + diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("104502200011")), // Subscription-Id-Data + }}) + ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0)) + ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(0)) + ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.CCTime, avp.Mbit, 0, datatype.Unsigned32(1))}}) + ccr.NewAVP(873, avp.Mbit, 10415, &diam.GroupedAVP{ // + AVP: []*diam.AVP{ + diam.NewAVP(20300, avp.Mbit, 2011, &diam.GroupedAVP{ // IN-Information + AVP: []*diam.AVP{ + diam.NewAVP(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number + diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP + }, + }), + diam.NewAVP(2000, avp.Mbit, 10415, &diam.GroupedAVP{ // SMS-Information + AVP: []*diam.AVP{ + diam.NewAVP(886, avp.Mbit, 10415, &diam.GroupedAVP{ // Originator-Address + AVP: []*diam.AVP{ + diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type + diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("49602200011")), // Address-Data + }}), + diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // Recipient-Address + AVP: []*diam.AVP{ + diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type + diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("49780029555")), // Address-Data + }}), + }, + }), + }}) + if err := dmtClient.SendMessage(ccr); err != nil { + t.Error(err) + } + time.Sleep(time.Duration(100) * time.Millisecond) + msg := dmtClient.ReceivedMessage() // Discard the received message so we can test next one + if msg == nil { + t.Fatal("No message returned") + } + if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Result-Code") + } else if strResult := avpValAsString(avps[0]); strResult != "5030" { // Result-Code set in the template + t.Errorf("Expecting 5030, received: %s", strResult) + } +} + func TestDmtAgentCdrs(t *testing.T) { if !*testIntegration { return @@ -475,7 +549,7 @@ func TestDmtAgentDryRun1(t *testing.T) { } else if len(avps) == 0 { t.Error("Result-Code") } else if strResult := avpValAsString(avps[0]); strResult != "300" { // Result-Code set in the template - t.Errorf("Expecting 200, received: %s", strResult) + t.Errorf("Expecting 300, received: %s", strResult) } } From e8c5287a7dd3b2e82e56611a5537101e18c6252c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 4 Feb 2016 14:11:03 +0200 Subject: [PATCH 045/199] started rounding --- engine/calldesc.go | 2 +- engine/timespans.go | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/engine/calldesc.go b/engine/calldesc.go index 2ead17ea5..4706e63be 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -75,7 +75,7 @@ var ( storageLogger LogStorage cdrStorage CdrStorage debitPeriod = 10 * time.Second - globalRoundingDecimals = 5 + globalRoundingDecimals = 6 historyScribe history.Scribe pubSubServer rpcclient.RpcClientConnection userService UserService diff --git a/engine/timespans.go b/engine/timespans.go index b7f530d59..6c65b67f2 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -347,12 +347,15 @@ func (ts *TimeSpan) createIncrementsSlice() { ts.Increments = make([]*Increment, 0) // create rated units series _, rateIncrement, _ := ts.RateInterval.GetRateParameters(ts.GetGroupStart()) - // we will use the cost calculated cost and devide by nb of increments + // we will use the calculated cost and devide by nb of increments // because ts cost is rounded //incrementCost := rate / rateUnit.Seconds() * rateIncrement.Seconds() nbIncrements := int(ts.GetDuration() / rateIncrement) incrementCost := ts.CalculateCost() / float64(nbIncrements) - incrementCost = utils.Round(incrementCost, ts.RateInterval.Rating.RoundingDecimals, ts.RateInterval.Rating.RoundingMethod) + // no more rounding at increment leve as it deviates from intended cost + // move rounding at highest point possible + //incrementCost = utils.Round(incrementCost, ts.RateInterval.Rating.RoundingDecimals, + // ts.RateInterval.Rating.RoundingMethod) for s := 0; s < nbIncrements; s++ { inc := &Increment{ Duration: rateIncrement, From b2a39ed8671e0c5f91ea2c9f77c8f99b3ef4b56d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 4 Feb 2016 18:46:06 +0200 Subject: [PATCH 046/199] action triggers expiration and activation times --- apier/v1/apier_local_test.go | 14 +- apier/v1/triggers.go | 141 +++++++++++++++++- console/trigger_set.go | 8 +- .../mysql/create_tariffplan_tables.sql | 2 + .../postgres/create_tariffplan_tables.sql | 2 + data/tariffplans/cdrstats/ActionTriggers.csv | 12 +- data/tariffplans/testtp/ActionTriggers.csv | 18 +-- data/tariffplans/tutorial/ActionTriggers.csv | 24 +-- engine/account.go | 15 +- engine/account_test.go | 2 +- engine/action_trigger.go | 10 ++ engine/loader_csv_test.go | 20 +-- engine/model_converters.go | 2 + engine/model_helpers.go | 2 + engine/model_helpers_test.go | 4 +- engine/models.go | 32 ++-- engine/tp_reader.go | 31 +++- utils/apitpdata.go | 2 + 18 files changed, 266 insertions(+), 75 deletions(-) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index e78886dee..4f7211162 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1009,19 +1009,19 @@ func TestApierAddTriggeredAction(t *testing.T) { } reply := "" // Add balance to a previously known account - attrs := &AttrSetAccountActionTriggers{ActionTriggerIDs: &[]string{"STANDARD_TRIGGERS"}, Tenant: "cgrates.org", Account: "dan2"} - if err := rater.Call("ApierV1.SetAccountActionTriggers", attrs, &reply); err != nil { - t.Error("Got error on ApierV1.SetAccountActionTriggers: ", err.Error()) + attrs := &AttrAddAccountActionTriggers{ActionTriggerIDs: &[]string{"STANDARD_TRIGGERS"}, Tenant: "cgrates.org", Account: "dan2"} + if err := rater.Call("ApierV1.AddAccountActionTriggers", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.AddAccountActionTriggers: ", err.Error()) } else if reply != "OK" { - t.Errorf("Calling ApierV1.SetAccountActionTriggers received: %s", reply) + t.Errorf("Calling ApierV1.AddAccountActionTriggers received: %s", reply) } reply2 := "" - attrs2 := new(AttrSetAccountActionTriggers) + attrs2 := new(AttrAddAccountActionTriggers) *attrs2 = *attrs attrs2.Account = "dan10" // Does not exist so it should error when adding triggers on it // Add trigger to an account which does n exist - if err := rater.Call("ApierV1.SetAccountActionTriggers", attrs2, &reply2); err == nil || reply2 == "OK" { - t.Error("Expecting error on ApierV1.SetAccountActionTriggers.", err, reply2) + if err := rater.Call("ApierV1.AddAccountActionTriggers", attrs2, &reply2); err == nil || reply2 == "OK" { + t.Error("Expecting error on ApierV1.AddAccountActionTriggers.", err, reply2) } } diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index 908a315c4..37b2b89b4 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -22,14 +22,14 @@ func (self *ApierV1) GetAccountActionTriggers(attrs AttrAcntAction, reply *engin return nil } -type AttrSetAccountActionTriggers struct { +type AttrAddAccountActionTriggers struct { Tenant string Account string ActionTriggerIDs *[]string ActionTriggerOverwrite bool } -func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, reply *string) error { +func (self *ApierV1) AddAccountActionTriggers(attr AttrAddAccountActionTriggers, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } @@ -156,3 +156,140 @@ func (self *ApierV1) ResetAccountActionTriggers(attr AttrRemoveAccountActionTrig *reply = utils.OK return nil } + +type AttrSetAccountActionTriggers struct { + Tenant string + Account string + GroupID string + UniqueID string + ThresholdType *string + ThresholdValue *float64 + Recurrent *bool + MinSleep *string + ExpirationDate *string + ActivationDate *string + BalanceId *string + BalanceType *string + BalanceDirections *[]string + BalanceDestinationIds *[]string + BalanceWeight *float64 + BalanceExpirationDate *string + BalanceTimingTags *[]string + BalanceRatingSubject *string + BalanceCategories *[]string + BalanceSharedGroups *[]string + BalanceBlocker *bool + BalanceDisabled *bool + MinQueuedItems *int + ActionsId *string +} + +func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, reply *string) error { + + if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + accID := utils.AccountKey(attr.Tenant, attr.Account) + var account *engine.Account + _, err := engine.Guardian.Guard(func() (interface{}, error) { + if acc, err := self.AccountDb.GetAccount(accID); err == nil { + account = acc + } else { + return 0, err + } + for _, at := range account.ActionTriggers { + if (attr.UniqueID == "" || at.UniqueID == attr.UniqueID) && + (attr.GroupID == "" || at.ID == attr.GroupID) { + // we have a winner + if attr.ThresholdType != nil { + at.ThresholdType = *attr.ThresholdType + } + if attr.ThresholdValue != nil { + at.ThresholdValue = *attr.ThresholdValue + } + if attr.Recurrent != nil { + at.Recurrent = *attr.Recurrent + } + if attr.MinSleep != nil { + minSleep, err := utils.ParseDurationWithSecs(*attr.MinSleep) + if err != nil { + return 0, err + } + at.MinSleep = minSleep + } + if attr.ExpirationDate != nil { + expTime, err := utils.ParseDate(*attr.ExpirationDate) + if err != nil { + return 0, err + } + at.ExpirationDate = expTime + } + if attr.ActivationDate != nil { + actTime, err := utils.ParseDate(*attr.ActivationDate) + if err != nil { + return 0, err + } + at.ActivationDate = actTime + } + if attr.BalanceId != nil { + at.BalanceId = *attr.BalanceId + } + if attr.BalanceType != nil { + at.BalanceType = *attr.BalanceType + } + if attr.BalanceDirections != nil { + at.BalanceDirections = utils.NewStringMap(*attr.BalanceDirections...) + } + if attr.BalanceDestinationIds != nil { + at.BalanceDestinationIds = utils.NewStringMap(*attr.BalanceDestinationIds...) + } + if attr.BalanceWeight != nil { + at.BalanceWeight = *attr.BalanceWeight + } + if attr.BalanceExpirationDate != nil { + balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate) + if err != nil { + return 0, err + } + at.BalanceExpirationDate = balanceExpTime + } + if attr.BalanceTimingTags != nil { + at.BalanceTimingTags = utils.NewStringMap(*attr.BalanceTimingTags...) + } + if attr.BalanceRatingSubject != nil { + at.BalanceRatingSubject = *attr.BalanceRatingSubject + } + if attr.BalanceCategories != nil { + at.BalanceCategories = utils.NewStringMap(*attr.BalanceCategories...) + } + if attr.BalanceSharedGroups != nil { + at.BalanceSharedGroups = utils.NewStringMap(*attr.BalanceSharedGroups...) + } + if attr.BalanceBlocker != nil { + at.BalanceBlocker = *attr.BalanceBlocker + } + if attr.BalanceDisabled != nil { + at.BalanceDisabled = *attr.BalanceDisabled + } + if attr.MinQueuedItems != nil { + at.MinQueuedItems = *attr.MinQueuedItems + } + if attr.ActionsId != nil { + at.ActionsId = *attr.ActionsId + } + } + + } + account.ExecuteActionTriggers(nil) + if err := self.AccountDb.SetAccount(account); err != nil { + return 0, err + } + return 0, nil + }, 0, accID) + if err != nil { + *reply = err.Error() + return err + } + *reply = utils.OK + return nil +} diff --git a/console/trigger_set.go b/console/trigger_set.go index 2e9095d95..fa7944eb7 100644 --- a/console/trigger_set.go +++ b/console/trigger_set.go @@ -23,8 +23,8 @@ import "github.com/cgrates/cgrates/apier/v1" func init() { c := &CmdSetTriggers{ name: "triggers_set", - rpcMethod: "ApierV1.SetAccountActionTriggers", - rpcParams: &v1.AttrSetAccountActionTriggers{}, + rpcMethod: "ApierV1.AddAccountActionTriggers", + rpcParams: &v1.AttrAddAccountActionTriggers{}, } commands[c.Name()] = c c.CommandExecuter = &CommandExecuter{c} @@ -34,7 +34,7 @@ func init() { type CmdSetTriggers struct { name string rpcMethod string - rpcParams *v1.AttrSetAccountActionTriggers + rpcParams *v1.AttrAddAccountActionTriggers *CommandExecuter } @@ -48,7 +48,7 @@ func (self *CmdSetTriggers) RpcMethod() string { func (self *CmdSetTriggers) RpcParams(reset bool) interface{} { if reset || self.rpcParams == nil { - self.rpcParams = &v1.AttrSetAccountActionTriggers{} + self.rpcParams = &v1.AttrAddAccountActionTriggers{} } return self.rpcParams } diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 15783c927..0a640135a 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -203,6 +203,8 @@ CREATE TABLE `tp_action_triggers` ( `threshold_value` DECIMAL(20,4) NOT NULL, `recurrent` BOOLEAN NOT NULL, `min_sleep` varchar(16) NOT NULL, + `expiry_time` varchar(24) NOT NULL, + `activation_time` varchar(24) NOT NULL, `balance_tag` varchar(64) NOT NULL, `balance_type` varchar(24) NOT NULL, `balance_directions` varchar(8) NOT NULL, diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index 857fb956f..e4d3aaee8 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -198,6 +198,8 @@ CREATE TABLE tp_action_triggers ( threshold_value NUMERIC(20,4) NOT NULL, recurrent BOOLEAN NOT NULL, min_sleep VARCHAR(16) NOT NULL, + expiry_time VARCHAR(24) NOT NULL, + activation_time VARCHAR(24) NOT NULL, balance_tag VARCHAR(64) NOT NULL, balance_type VARCHAR(24) NOT NULL, balance_directions VARCHAR(8) NOT NULL, diff --git a/data/tariffplans/cdrstats/ActionTriggers.csv b/data/tariffplans/cdrstats/ActionTriggers.csv index 6081b9d67..153da6ceb 100644 --- a/data/tariffplans/cdrstats/ActionTriggers.csv +++ b/data/tariffplans/cdrstats/ActionTriggers.csv @@ -1,6 +1,6 @@ -#Tag[0],UniqueId[1],ThresholdType[2],ThresholdValue[3],Recurrent[4],MinSleep[5],BalanceTag[6],BalanceType[7],BalanceDirections[8],BalanceCategories[9],BalanceDestinationTags[10],BalanceRatingSubject[11],BalanceSharedGroup[12],BalanceExpiryTime[13],BalanceTimingTags[14],BalanceWeight[15],BalanceBlocker[16],BalanceDisabled[17],StatsMinQueuedItems[18],ActionsTag[19],Weight[20] -CDRST3_WARN_ASR,,*min_asr,45,true,1h,,,,,,,,,,,,,3,CDRST_LOG,10 -CDRST3_WARN_ACD,,*min_acd,10,true,1h,,,,,,,,,,,,,5,CDRST_LOG,10 -CDRST3_WARN_ACC,,*max_acc,10,true,10m,,,,,,,,,,,,,5,CDRST_LOG,10 -CDRST4_WARN_ASR,,*min_asr,30,true,0,,,,,,,,,,,,,5,CDRST_LOG,10 -CDRST4_WARN_ACD,,*min_acd,3,true,0,,,,,,,,,,,,,2,CDRST_LOG,10 +#Tag[0],UniqueId[1],ThresholdType[2],ThresholdValue[3],Recurrent[4],MinSleep[5],ExpiryTime[6],ActivationTime[7],BalanceTag[8],BalanceType[9],BalanceDirections[10],BalanceCategories[11],BalanceDestinationIds[12],BalanceRatingSubject[13],BalanceSharedGroup[14],BalanceExpiryTime[15],BalanceTimingIds[16],BalanceWeight[17],BalanceBlocker[18],BalanceDisabled[19],StatsMinQueuedItems[20],ActionsId[21],Weight[22] +CDRST3_WARN_ASR,,*min_asr,45,true,1h,,,,,,,,,,,,,,,3,CDRST_LOG,10 +CDRST3_WARN_ACD,,*min_acd,10,true,1h,,,,,,,,,,,,,,,5,CDRST_LOG,10 +CDRST3_WARN_ACC,,*max_acc,10,true,10m,,,,,,,,,,,,,,,5,CDRST_LOG,10 +CDRST4_WARN_ASR,,*min_asr,30,true,0,,,,,,,,,,,,,,,5,CDRST_LOG,10 +CDRST4_WARN_ACD,,*min_acd,3,true,0,,,,,,,,,,,,,,,2,CDRST_LOG,10 diff --git a/data/tariffplans/testtp/ActionTriggers.csv b/data/tariffplans/testtp/ActionTriggers.csv index c50ff9a7c..795ef6ab8 100644 --- a/data/tariffplans/testtp/ActionTriggers.csv +++ b/data/tariffplans/testtp/ActionTriggers.csv @@ -1,9 +1,9 @@ -#Tag[0],UniqueId[1],ThresholdType[2],ThresholdValue[3],Recurrent[4],MinSleep[5],BalanceTag[6],BalanceType[7],BalanceDirections[8],BalanceCategories[9],BalanceDestinationTags[10],BalanceRatingSubject[11],BalanceSharedGroup[12],BalanceExpiryTime[13],BalanceTimingTags[14],BalanceWeight[15],BalanceBlocker[16],BalanceDisabled[17],StatsMinQueuedItems[18],ActionsTag[19],Weight[20] -STANDARD_TRIGGERS,,*min_balance,2,false,0,,*monetary,*out,,,,,,,,,,,LOG_BALANCE,10 -STANDARD_TRIGGERS,,*max_balance,20,false,0,,*monetary,*out,,,,,,,,,,,LOG_BALANCE,10 -STANDARD_TRIGGERS,,*max_event_counter,15,false,0,,*monetary,*out,,FS_USERS,,,,,,,,,LOG_BALANCE,10 -CDRST1_WARN_ASR,,*min_asr,45,true,1h,,,,,,,,,,,,,3,CDRST_WARN_HTTP,10 -CDRST1_WARN_ACD,,*min_acd,10,true,1h,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 -CDRST1_WARN_ACC,,*max_acc,10,true,10m,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 -CDRST2_WARN_ASR,,*min_asr,30,true,0,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 -CDRST2_WARN_ACD,,*min_acd,3,true,0,,,,,,,,,,,,,2,CDRST_LOG,10 +#Tag[0],UniqueId[1],ThresholdType[2],ThresholdValue[3],Recurrent[4],MinSleep[5],ExpiryTime[6],ActivationTime[7],BalanceTag[8],BalanceType[9],BalanceDirections[10],BalanceCategories[11],BalanceDestinationIds[12],BalanceRatingSubject[13],BalanceSharedGroup[14],BalanceExpiryTime[15],BalanceTimingIds[16],BalanceWeight[17],BalanceBlocker[18],BalanceDisabled[19],StatsMinQueuedItems[20],ActionsId[21],Weight[22] +STANDARD_TRIGGERS,,*min_balance,2,false,0,,,,*monetary,*out,,,,,,,,,,,LOG_BALANCE,10 +STANDARD_TRIGGERS,,*max_balance,20,false,0,,,,*monetary,*out,,,,,,,,,,,LOG_BALANCE,10 +STANDARD_TRIGGERS,,*max_event_counter,15,false,0,,,,*monetary,*out,,FS_USERS,,,,,,,,,LOG_BALANCE,10 +CDRST1_WARN_ASR,,*min_asr,45,true,1h,,,,,,,,,,,,,,,3,CDRST_WARN_HTTP,10 +CDRST1_WARN_ACD,,*min_acd,10,true,1h,,,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 +CDRST1_WARN_ACC,,*max_acc,10,true,10m,,,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 +CDRST2_WARN_ASR,,*min_asr,30,true,0,,,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 +CDRST2_WARN_ACD,,*min_acd,3,true,0,,,,,,,,,,,,,,,2,CDRST_LOG,10 diff --git a/data/tariffplans/tutorial/ActionTriggers.csv b/data/tariffplans/tutorial/ActionTriggers.csv index e68fbdf34..2036c0ad2 100644 --- a/data/tariffplans/tutorial/ActionTriggers.csv +++ b/data/tariffplans/tutorial/ActionTriggers.csv @@ -1,12 +1,12 @@ -#Tag[0],UniqueId[1],ThresholdType[2],ThresholdValue[3],Recurrent[4],MinSleep[5],BalanceTag[6],BalanceType[7],BalanceDirections[8],BalanceCategories[9],BalanceDestinationIds[10],BalanceRatingSubject[11],BalanceSharedGroup[12],BalanceExpiryTime[13],BalanceTimingIds[14],BalanceWeight[15],BalanceBlocker[16],BalanceDisabled[17],StatsMinQueuedItems[18],ActionsId[19],Weight[20] -STANDARD_TRIGGERS,,*min_balance,2,false,0,,*monetary,*out,,,,,,,,,,,LOG_WARNING,10 -STANDARD_TRIGGERS,,*max_event_counter,5,false,0,,*monetary,*out,,FS_USERS,,,,,,,,,LOG_WARNING,10 -STANDARD_TRIGGERS,,*max_balance,20,false,0,,*monetary,*out,,,,,,,,,,,LOG_WARNING,10 -STANDARD_TRIGGERS,,*max_balance,100,false,0,,*monetary,*out,,,,,,,,,,,DISABLE_AND_LOG,10 -CDRST1_WARN,,*min_asr,45,true,1m,,,,,,,,,,,,,3,LOG_WARNING,10 -CDRST1_WARN,,*min_acd,10,true,1m,,,,,,,,,,,,,5,LOG_WARNING,10 -CDRST1_WARN,,*max_acc,10,true,1m,,,,,,,,,,,,,5,LOG_WARNING,10 -CDRST1001_WARN,,*min_asr,65,true,1m,,,,,,,,,,,,,3,LOG_WARNING,10 -CDRST1001_WARN,,*min_acd,10,true,1m,,,,,,,,,,,,,5,LOG_WARNING,10 -CDRST1001_WARN,,*max_acc,5,true,1m,,,,,,,,,,,,,5,LOG_WARNING,10 -CDRST3_WARN,,*min_acd,60,false,1m,,,,,,,,,,,,,5,LOG_WARNING,10 +#Tag[0],UniqueId[1],ThresholdType[2],ThresholdValue[3],Recurrent[4],MinSleep[5],ExpiryTime[6],ActivationTime[7],BalanceTag[8],BalanceType[9],BalanceDirections[10],BalanceCategories[11],BalanceDestinationIds[12],BalanceRatingSubject[13],BalanceSharedGroup[14],BalanceExpiryTime[15],BalanceTimingIds[16],BalanceWeight[17],BalanceBlocker[18],BalanceDisabled[19],StatsMinQueuedItems[20],ActionsId[21],Weight[22] +STANDARD_TRIGGERS,,*min_balance,2,false,0,,,,*monetary,*out,,,,,,,,,,,LOG_WARNING,10 +STANDARD_TRIGGERS,,*max_event_counter,5,false,0,,,,*monetary,*out,,FS_USERS,,,,,,,,,LOG_WARNING,10 +STANDARD_TRIGGERS,,*max_balance,20,false,0,,,,*monetary,*out,,,,,,,,,,,LOG_WARNING,10 +STANDARD_TRIGGERS,,*max_balance,100,false,0,,,,*monetary,*out,,,,,,,,,,,DISABLE_AND_LOG,10 +CDRST1_WARN,,*min_asr,45,true,1m,,,,,,,,,,,,,,,3,LOG_WARNING,10 +CDRST1_WARN,,*min_acd,10,true,1m,,,,,,,,,,,,,,,5,LOG_WARNING,10 +CDRST1_WARN,,*max_acc,10,true,1m,,,,,,,,,,,,,,,5,LOG_WARNING,10 +CDRST1001_WARN,,*min_asr,65,true,1m,,,,,,,,,,,,,,,3,LOG_WARNING,10 +CDRST1001_WARN,,*min_acd,10,true,1m,,,,,,,,,,,,,,,5,LOG_WARNING,10 +CDRST1001_WARN,,*max_acc,5,true,1m,,,,,,,,,,,,,,,5,LOG_WARNING,10 +CDRST3_WARN,,*min_acd,60,false,1m,,,,,,,,,,,,,,,5,LOG_WARNING,10 diff --git a/engine/account.go b/engine/account.go index 99875285b..287c3ab3a 100644 --- a/engine/account.go +++ b/engine/account.go @@ -572,6 +572,11 @@ func (ub *Account) refundIncrement(increment *Increment, cd *CallDescriptor, cou func (acc *Account) ExecuteActionTriggers(a *Action) { acc.ActionTriggers.Sort() for _, at := range acc.ActionTriggers { + // check is effective + if at.IsExpired(time.Now()) || !at.IsActive(time.Now()) { + continue + } + // sanity check if !strings.Contains(at.ThresholdType, "counter") && !strings.Contains(at.ThresholdType, "balance") { continue @@ -623,7 +628,7 @@ func (acc *Account) ExecuteActionTriggers(a *Action) { } } } - acc.CleanExpiredBalances() + acc.CleanExpiredStuff() } // Mark all action trigers as ready for execution @@ -693,7 +698,7 @@ func (acc *Account) InitCounters() { } } -func (acc *Account) CleanExpiredBalances() { +func (acc *Account) CleanExpiredStuff() { for key, bm := range acc.BalanceMap { for i := 0; i < len(bm); i++ { if bm[i].IsExpired() { @@ -703,6 +708,12 @@ func (acc *Account) CleanExpiredBalances() { } acc.BalanceMap[key] = bm } + + for i := 0; i < len(acc.ActionTriggers); i++ { + if acc.ActionTriggers[i].IsExpired(time.Now()) { + acc.ActionTriggers = append(acc.ActionTriggers[:i], acc.ActionTriggers[i+1:]...) + } + } } func (acc *Account) allBalancesExpired() bool { diff --git a/engine/account_test.go b/engine/account_test.go index 577dfc0b8..885a23c7d 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -997,7 +997,7 @@ func TestCleanExpired(t *testing.T) { &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}, }}, } - ub.CleanExpiredBalances() + ub.CleanExpiredStuff() if len(ub.BalanceMap[utils.MONETARY]) != 2 { t.Error("Error cleaning expired balances!") } diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 3e2dc7ea1..71c8aed6d 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -36,6 +36,8 @@ type ActionTrigger struct { ThresholdValue float64 Recurrent bool // reset excuted flag each run MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers + ExpirationDate time.Time + ActivationDate time.Time BalanceId string BalanceType string // *monetary/*voice etc BalanceDirections utils.StringMap // filter for balance @@ -188,6 +190,14 @@ func (at *ActionTrigger) Equals(oat *ActionTrigger) bool { return at.ID == oat.ID && at.UniqueID == oat.UniqueID } +func (at *ActionTrigger) IsActive(t time.Time) bool { + return at.ActivationDate.IsZero() || t.After(at.ActivationDate) +} + +func (at *ActionTrigger) IsExpired(t time.Time) bool { + return !at.ExpirationDate.IsZero() && t.After(at.ExpirationDate) +} + // Structure to store actions according to weight type ActionTriggers []*ActionTrigger diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 885cca250..ffb85bbae 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -189,16 +189,16 @@ BLOCK_AT,BLOCK,*asap,10 ` actionTriggers = ` -STANDARD_TRIGGER,st0,*min_event_counter,10,false,0,,*voice,*out,,GERMANY_O2,,,,,,,,,SOME_1,10 -STANDARD_TRIGGER,st1,*max_balance,200,false,0,,*voice,*out,,GERMANY,,,,,,,,,SOME_2,10 -STANDARD_TRIGGERS,,*min_balance,2,false,0,,*monetary,*out,,,,,,,,,,,LOG_WARNING,10 -STANDARD_TRIGGERS,,*max_balance,20,false,0,,*monetary,*out,,,,,,,,,,,LOG_WARNING,10 -STANDARD_TRIGGERS,,*max_event_counter,5,false,0,,*monetary,*out,,FS_USERS,,,,,,,,,LOG_WARNING,10 -CDRST1_WARN_ASR,,*min_asr,45,true,1h,,,,,,,,,,,,,3,CDRST_WARN_HTTP,10 -CDRST1_WARN_ACD,,*min_acd,10,true,1h,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 -CDRST1_WARN_ACC,,*max_acc,10,true,10m,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 -CDRST2_WARN_ASR,,*min_asr,30,true,0,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 -CDRST2_WARN_ACD,,*min_acd,3,true,0,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 +STANDARD_TRIGGER,st0,*min_event_counter,10,false,0,,,,*voice,*out,,GERMANY_O2,,,,,,,,,SOME_1,10 +STANDARD_TRIGGER,st1,*max_balance,200,false,0,,,,*voice,*out,,GERMANY,,,,,,,,,SOME_2,10 +STANDARD_TRIGGERS,,*min_balance,2,false,0,,,,*monetary,*out,,,,,,,,,,,LOG_WARNING,10 +STANDARD_TRIGGERS,,*max_balance,20,false,0,,,,*monetary,*out,,,,,,,,,,,LOG_WARNING,10 +STANDARD_TRIGGERS,,*max_event_counter,5,false,0,,,,*monetary,*out,,FS_USERS,,,,,,,,,LOG_WARNING,10 +CDRST1_WARN_ASR,,*min_asr,45,true,1h,,,,,,,,,,,,,,,3,CDRST_WARN_HTTP,10 +CDRST1_WARN_ACD,,*min_acd,10,true,1h,,,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 +CDRST1_WARN_ACC,,*max_acc,10,true,10m,,,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 +CDRST2_WARN_ASR,,*min_asr,30,true,0,,,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 +CDRST2_WARN_ACD,,*min_acd,3,true,0,,,,,,,,,,,,,,,5,CDRST_WARN_HTTP,10 ` accountActions = ` vdf,minitsboy,MORE_MINUTES,STANDARD_TRIGGER,, diff --git a/engine/model_converters.go b/engine/model_converters.go index d3035772e..f15e99abd 100644 --- a/engine/model_converters.go +++ b/engine/model_converters.go @@ -224,6 +224,8 @@ func APItoModelActionTrigger(ats *utils.TPActionTriggers) (result []TpActionTrig ThresholdValue: at.ThresholdValue, Recurrent: at.Recurrent, MinSleep: at.MinSleep, + ExpiryTime: at.ExpirationDate, + ActivationTime: at.ActivationDate, BalanceTag: at.BalanceId, BalanceType: at.BalanceType, BalanceDirections: at.BalanceDirections, diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 0d2d0a648..3d29e2d47 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -430,6 +430,8 @@ func (tps TpActionTriggers) GetActionTriggers() (map[string][]*utils.TPActionTri ThresholdValue: tpAt.ThresholdValue, Recurrent: tpAt.Recurrent, MinSleep: tpAt.MinSleep, + ExpirationDate: tpAt.ExpiryTime, + ActivationDate: tpAt.ActivationTime, BalanceId: tpAt.BalanceTag, BalanceType: tpAt.BalanceType, BalanceDirections: tpAt.BalanceDirections, diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 7d0413c9a..90f1986c3 100644 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -597,8 +597,8 @@ func TestTPActionPlanAsExportSlice(t *testing.T) { }, } expectedSlc := [][]string{ - []string{"STANDARD_TRIGGERS", "1", "*min_balance", "2", "false", "0", "b1", "*monetary", "*out", "call", "", "special1", "SHARED_1", "*never", "T1", "0", "false", "false", "0", "LOG_WARNING", "10"}, - []string{"STANDARD_TRIGGERS", "2", "*max_event_counter", "5", "false", "0", "b2", "*monetary", "*out", "call", "FS_USERS", "special1", "SHARED_1", "*never", "T1", "0", "false", "false", "0", "LOG_WARNING", "10"}, + []string{"STANDARD_TRIGGERS", "1", "*min_balance", "2", "false", "0", "", "", "b1", "*monetary", "*out", "call", "", "special1", "SHARED_1", "*never", "T1", "0", "false", "false", "0", "LOG_WARNING", "10"}, + []string{"STANDARD_TRIGGERS", "2", "*max_event_counter", "5", "false", "0", "", "", "b2", "*monetary", "*out", "call", "FS_USERS", "special1", "SHARED_1", "*never", "T1", "0", "false", "false", "0", "LOG_WARNING", "10"}, } ms := APItoModelActionTrigger(at) var slc [][]string diff --git a/engine/models.go b/engine/models.go index 2fb2ffc56..d16916891 100644 --- a/engine/models.go +++ b/engine/models.go @@ -193,21 +193,23 @@ type TpActionTrigger struct { ThresholdValue float64 `index:"3" re:"\d+\.?\d*"` Recurrent bool `index:"4" re:"true|false"` MinSleep string `index:"5" re:"\d+[smh]?"` - BalanceTag string `index:"6" re:"\w+\s*"` - BalanceType string `index:"7" re:"\*\w+"` - BalanceDirections string `index:"8" re:"\*out"` - BalanceCategories string `index:"9" re:""` - BalanceDestinationTags string `index:"10" re:"\w+|\*any"` - BalanceRatingSubject string `index:"11" re:"\w+|\*any"` - BalanceSharedGroups string `index:"12" re:"\w+|\*any"` - BalanceExpiryTime string `index:"13" re:"\*\w+\s*|\+\d+[smh]\s*|\d+\s*"` - BalanceTimingTags string `index:"14" re:"[0-9A-Za-z_;]*|\*any"` - BalanceWeight float64 `index:"15" re:"\d+\.?\d*"` - BalanceBlocker bool `index:"16" re:""` - BalanceDisabled bool `index:"17" re:""` - MinQueuedItems int `index:"18" re:"\d+"` - ActionsTag string `index:"19" re:"\w+"` - Weight float64 `index:"20" re:"\d+\.?\d*"` + ExpiryTime string `index:"6" re:""` + ActivationTime string `index:"7" re:""` + BalanceTag string `index:"8" re:"\w+\s*"` + BalanceType string `index:"9" re:"\*\w+"` + BalanceDirections string `index:"10" re:"\*out"` + BalanceCategories string `index:"11" re:""` + BalanceDestinationTags string `index:"12" re:"\w+|\*any"` + BalanceRatingSubject string `index:"13" re:"\w+|\*any"` + BalanceSharedGroups string `index:"14" re:"\w+|\*any"` + BalanceExpiryTime string `index:"15" re:"\*\w+\s*|\+\d+[smh]\s*|\d+\s*"` + BalanceTimingTags string `index:"16" re:"[0-9A-Za-z_;]*|\*any"` + BalanceWeight float64 `index:"17" re:"\d+\.?\d*"` + BalanceBlocker bool `index:"18" re:""` + BalanceDisabled bool `index:"19" re:""` + MinQueuedItems int `index:"20" re:"\d+"` + ActionsTag string `index:"21" re:"\w+"` + Weight float64 `index:"22" re:"\d+\.?\d*"` CreatedAt time.Time } diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 444a9bc57..af4cae816 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -625,7 +625,18 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { for key, atrsLst := range storAts { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, atr := range atrsLst { - balanceExpirationDate, _ := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) + balanceExpirationDate, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) + if err != nil { + return err + } + expirationDate, err := utils.ParseTimeDetectLayout(atr.ExpirationDate, tpr.timezone) + if err != nil { + return err + } + activationDate, err := utils.ParseTimeDetectLayout(atr.ActivationDate, tpr.timezone) + if err != nil { + return err + } minSleep, err := utils.ParseDurationWithSecs(atr.MinSleep) if err != nil { return err @@ -640,6 +651,8 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { ThresholdValue: atr.ThresholdValue, Recurrent: atr.Recurrent, MinSleep: minSleep, + ExpirationDate: expirationDate, + ActivationDate: activationDate, BalanceId: atr.BalanceId, BalanceType: atr.BalanceType, BalanceDirections: utils.ParseStringMap(atr.BalanceDirections), @@ -783,7 +796,9 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error atrs := make([]*ActionTrigger, len(atrsLst)) for idx, apiAtr := range atrsLst { minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep) - expTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) + balanceExpTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) + expTime, _ := utils.ParseDate(apiAtr.ExpirationDate) + actTime, _ := utils.ParseDate(apiAtr.ActivationDate) if apiAtr.UniqueID == "" { apiAtr.UniqueID = utils.GenUUID() } @@ -794,12 +809,14 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error ThresholdValue: apiAtr.ThresholdValue, Recurrent: apiAtr.Recurrent, MinSleep: minSleep, + ExpirationDate: expTime, + ActivationDate: actTime, BalanceId: apiAtr.BalanceId, BalanceType: apiAtr.BalanceType, BalanceDirections: utils.ParseStringMap(apiAtr.BalanceDirections), BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds), BalanceWeight: apiAtr.BalanceWeight, - BalanceExpirationDate: expTime, + BalanceExpirationDate: balanceExpTime, BalanceTimingTags: utils.ParseStringMap(apiAtr.BalanceTimingTags), BalanceRatingSubject: apiAtr.BalanceRatingSubject, BalanceCategories: utils.ParseStringMap(apiAtr.BalanceCategories), @@ -1012,7 +1029,9 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, apiAtr := range atrsLst { minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep) - expTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) + balanceExpTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) + expTime, _ := utils.ParseDate(apiAtr.ExpirationDate) + actTime, _ := utils.ParseDate(apiAtr.ActivationDate) if apiAtr.UniqueID == "" { apiAtr.UniqueID = utils.GenUUID() } @@ -1023,12 +1042,14 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { ThresholdValue: apiAtr.ThresholdValue, Recurrent: apiAtr.Recurrent, MinSleep: minSleep, + ExpirationDate: expTime, + ActivationDate: actTime, BalanceId: apiAtr.BalanceId, BalanceType: apiAtr.BalanceType, BalanceDirections: utils.ParseStringMap(apiAtr.BalanceDirections), BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds), BalanceWeight: apiAtr.BalanceWeight, - BalanceExpirationDate: expTime, + BalanceExpirationDate: balanceExpTime, BalanceRatingSubject: apiAtr.BalanceRatingSubject, BalanceCategories: utils.ParseStringMap(apiAtr.BalanceCategories), BalanceSharedGroups: utils.ParseStringMap(apiAtr.BalanceSharedGroups), diff --git a/utils/apitpdata.go b/utils/apitpdata.go index f235c33ed..9fdabdc9a 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -480,6 +480,8 @@ type TPActionTrigger struct { ThresholdValue float64 // Threshold Recurrent bool // reset executed flag each run MinSleep string // Minimum duration between two executions in case of recurrent triggers + ExpirationDate string // Trigger expiration + ActivationDate string // Trigger activation BalanceId string // The id of the balance in the account BalanceType string // Type of balance this trigger monitors BalanceDirections string // Traffic direction From ec2a7d438b5a1fee07cae6806493b5ead30a21c0 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 5 Feb 2016 12:35:36 +0200 Subject: [PATCH 047/199] triggers console commands (new triggers_set) --- console/trigger_add.go | 63 ++++++++++++++++++++++++++++++++++++++++++ console/trigger_set.go | 8 +++--- 2 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 console/trigger_add.go diff --git a/console/trigger_add.go b/console/trigger_add.go new file mode 100644 index 000000000..bef41f910 --- /dev/null +++ b/console/trigger_add.go @@ -0,0 +1,63 @@ +/* +Rating system designed to be used in VoIP Carriers World +Copyright (C) 2012-2015 ITsysCOM + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package console + +import "github.com/cgrates/cgrates/apier/v1" + +func init() { + c := &CmdAddTriggers{ + name: "triggers_add", + rpcMethod: "ApierV1.AddAccountActionTriggers", + rpcParams: &v1.AttrAddAccountActionTriggers{}, + } + commands[c.Name()] = c + c.CommandExecuter = &CommandExecuter{c} +} + +// Commander implementation +type CmdAddTriggers struct { + name string + rpcMethod string + rpcParams *v1.AttrAddAccountActionTriggers + *CommandExecuter +} + +func (self *CmdAddTriggers) Name() string { + return self.name +} + +func (self *CmdAddTriggers) RpcMethod() string { + return self.rpcMethod +} + +func (self *CmdAddTriggers) RpcParams(reset bool) interface{} { + if reset || self.rpcParams == nil { + self.rpcParams = &v1.AttrAddAccountActionTriggers{} + } + return self.rpcParams +} + +func (self *CmdAddTriggers) PostprocessRpcParams() error { + return nil +} + +func (self *CmdAddTriggers) RpcResult() interface{} { + var s string + return &s +} diff --git a/console/trigger_set.go b/console/trigger_set.go index fa7944eb7..2e9095d95 100644 --- a/console/trigger_set.go +++ b/console/trigger_set.go @@ -23,8 +23,8 @@ import "github.com/cgrates/cgrates/apier/v1" func init() { c := &CmdSetTriggers{ name: "triggers_set", - rpcMethod: "ApierV1.AddAccountActionTriggers", - rpcParams: &v1.AttrAddAccountActionTriggers{}, + rpcMethod: "ApierV1.SetAccountActionTriggers", + rpcParams: &v1.AttrSetAccountActionTriggers{}, } commands[c.Name()] = c c.CommandExecuter = &CommandExecuter{c} @@ -34,7 +34,7 @@ func init() { type CmdSetTriggers struct { name string rpcMethod string - rpcParams *v1.AttrAddAccountActionTriggers + rpcParams *v1.AttrSetAccountActionTriggers *CommandExecuter } @@ -48,7 +48,7 @@ func (self *CmdSetTriggers) RpcMethod() string { func (self *CmdSetTriggers) RpcParams(reset bool) interface{} { if reset || self.rpcParams == nil { - self.rpcParams = &v1.AttrAddAccountActionTriggers{} + self.rpcParams = &v1.AttrSetAccountActionTriggers{} } return self.rpcParams } From ceb6760cd481dcfb3b67eea7ed9d4a8516cd97c1 Mon Sep 17 00:00:00 2001 From: Eloy Coto Date: Fri, 5 Feb 2016 10:48:18 +0000 Subject: [PATCH 048/199] Added error details on connections --- engine/storage_utils.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/engine/storage_utils.go b/engine/storage_utils.go index ba093af58..8994020fd 100644 --- a/engine/storage_utils.go +++ b/engine/storage_utils.go @@ -20,6 +20,7 @@ package engine import ( "errors" + "fmt" "strconv" "github.com/cgrates/cgrates/utils" @@ -45,7 +46,8 @@ func ConfigureRatingStorage(db_type, host, port, name, user, pass, marshaler str d, err = NewMongoStorage(host, port, name, user, pass, nil) db = d.(RatingStorage) default: - err = errors.New("unknown db") + err = errors.New(fmt.Sprintf("Unknown db '%s' valid options are '%s' or '%s'", + db_type, utils.REDIS, utils.MONGO)) } if err != nil { return nil, err @@ -71,7 +73,8 @@ func ConfigureAccountingStorage(db_type, host, port, name, user, pass, marshaler d, err = NewMongoStorage(host, port, name, user, pass, nil) db = d.(AccountingStorage) default: - err = errors.New("unknown db") + err = errors.New(fmt.Sprintf("Unknown db '%s' valid options are '%s' or '%s'", + db_type, utils.REDIS, utils.MONGO)) } if err != nil { return nil, err @@ -102,7 +105,8 @@ func ConfigureLogStorage(db_type, host, port, name, user, pass, marshaler string case utils.MYSQL: d, err = NewMySQLStorage(host, port, name, user, pass, maxConn, maxIdleConn) default: - err = errors.New("unknown db") + err = errors.New(fmt.Sprintf("Unknown db '%s' valid options are [%s, %s, %s]", + db_type, utils.MYSQL, utils.MONGO, utils.POSTGRES)) } if err != nil { return nil, err @@ -120,7 +124,8 @@ func ConfigureLoadStorage(db_type, host, port, name, user, pass, marshaler strin case utils.MONGO: d, err = NewMongoStorage(host, port, name, user, pass, cdrsIndexes) default: - err = errors.New("unknown db") + err = errors.New(fmt.Sprintf("Unknown db '%s' valid options are [%s, %s, %s]", + db_type, utils.MYSQL, utils.MONGO, utils.POSTGRES)) } if err != nil { return nil, err @@ -138,7 +143,8 @@ func ConfigureCdrStorage(db_type, host, port, name, user, pass string, maxConn, case utils.MONGO: d, err = NewMongoStorage(host, port, name, user, pass, cdrsIndexes) default: - err = errors.New("unknown db") + err = errors.New(fmt.Sprintf("Unknown db '%s' valid options are [%s, %s, %s]", + db_type, utils.MYSQL, utils.MONGO, utils.POSTGRES)) } if err != nil { return nil, err From 65b8e3ed41e764f262733f1dadb3b2ee45e1ab2d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 5 Feb 2016 15:32:47 +0200 Subject: [PATCH 049/199] tests for set triggers --- apier/v1/apier_local_test.go | 35 +++++++++++++++++++++++++++ engine/account_test.go | 47 ++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 4f7211162..b66cdf880 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1074,6 +1074,41 @@ func TestApierRemAccountActionTriggers(t *testing.T) { } } +// Test here SetAccountActionTriggers +func TestApierSetAccountActionTriggers(t *testing.T) { + if !*testLocal { + return + } + // Test first get so we can steal the id which we need to remove + var reply engine.ActionTriggers + req := AttrAcntAction{Tenant: "cgrates.org", Account: "dan2"} + if err := rater.Call("ApierV1.GetAccountActionTriggers", req, &reply); err != nil { + t.Error("Got error on ApierV1.GetAccountActionTimings: ", err.Error()) + } else if len(reply) != 1 || reply[0].ActionsId != "LOG_BALANCE" { + for _, atr := range reply { + t.Logf("ATR: %+v", atr) + } + t.Errorf("Unexpected action triggers received %v", reply) + } + var setReply string + setReq := AttrSetAccountActionTriggers{Tenant: "cgrates.org", Account: "dan2", UniqueID: reply[0].UniqueID, ActivationDate: utils.StringPointer("2016-02-05T18:00:00Z")} + if err := rater.Call("ApierV1.ResetAccountActionTriggers", setReq, &setReply); err != nil { + t.Error("Got error on ApierV1.ResetActionTiming: ", err.Error()) + } else if setReply != OK { + t.Error("Unexpected answer received", setReply) + } + if err := rater.Call("ApierV1.SetAccountActionTriggers", setReq, &setReply); err != nil { + t.Error("Got error on ApierV1.RemoveActionTiming: ", err.Error()) + } else if setReply != OK { + t.Error("Unexpected answer received", setReply) + } + if err := rater.Call("ApierV1.GetAccountActionTriggers", req, &reply); err != nil { + t.Error("Got error on ApierV1.GetAccountActionTriggers: ", err.Error()) + } else if len(reply) != 1 || reply[0].ActivationDate != time.Date(2016, 2, 5, 18, 0, 0, 0, time.UTC) { + t.Errorf("Unexpected action triggers received %+v", reply[0]) + } +} + // Test here SetAccount func TestApierSetAccount(t *testing.T) { if !*testLocal { diff --git a/engine/account_test.go b/engine/account_test.go index 885a23c7d..11a40f65b 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -986,6 +986,42 @@ func TestAccountExpActionTrigger(t *testing.T) { } } +func TestAccountExpActionTriggerNotActivated(t *testing.T) { + ub := &Account{ + Id: "TEST_UB", + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + ActionTriggers: ActionTriggers{ + &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + }, + } + ub.ExecuteActionTriggers(nil) + if ub.BalanceMap[utils.MONETARY][0].IsExpired() || + ub.BalanceMap[utils.MONETARY][0].GetValue() != 100 || + ub.BalanceMap[utils.VOICE][0].GetValue() != 10 || + ub.ActionTriggers[0].Executed != false { + t.Log(ub.BalanceMap[utils.MONETARY][0].IsExpired()) + t.Error("Error executing triggered actions", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue(), len(ub.BalanceMap[utils.MONETARY])) + } +} + +func TestAccountExpActionTriggerExpired(t *testing.T) { + ub := &Account{ + Id: "TEST_UB", + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + ActionTriggers: ActionTriggers{ + &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + }, + } + ub.ExecuteActionTriggers(nil) + if ub.BalanceMap[utils.MONETARY][0].IsExpired() || + ub.BalanceMap[utils.MONETARY][0].GetValue() != 100 || + ub.BalanceMap[utils.VOICE][0].GetValue() != 10 || + len(ub.ActionTriggers) != 0 { + t.Log(ub.BalanceMap[utils.MONETARY][0].IsExpired()) + t.Error("Error executing triggered actions", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue(), len(ub.BalanceMap[utils.MONETARY])) + } +} + func TestCleanExpired(t *testing.T) { ub := &Account{ Id: "TEST_UB_OREDER", @@ -996,6 +1032,14 @@ func TestCleanExpired(t *testing.T) { &Balance{ExpirationDate: time.Date(2013, 7, 18, 14, 33, 0, 0, time.UTC)}, &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}, }}, + ActionTriggers: ActionTriggers{ + &ActionTrigger{ + ExpirationDate: time.Date(2013, 7, 18, 14, 33, 0, 0, time.UTC), + }, + &ActionTrigger{ + ActivationDate: time.Date(2013, 7, 18, 14, 33, 0, 0, time.UTC), + }, + }, } ub.CleanExpiredStuff() if len(ub.BalanceMap[utils.MONETARY]) != 2 { @@ -1004,6 +1048,9 @@ func TestCleanExpired(t *testing.T) { if len(ub.BalanceMap[utils.VOICE]) != 1 { t.Error("Error cleaning expired minute buckets!") } + if len(ub.ActionTriggers) != 1 { + t.Error("Error cleaning expired action triggers!") + } } func TestAccountUnitCounting(t *testing.T) { From c4d70d4fea3a1f5e08a38d9eb16f9f3681605547 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 5 Feb 2016 15:42:11 +0200 Subject: [PATCH 050/199] tests order --- apier/v1/apier_local_test.go | 70 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index b66cdf880..efbd485de 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1039,41 +1039,6 @@ func TestApierGetAccountActionTriggers(t *testing.T) { } } -// Test here RemAccountActionTriggers -func TestApierRemAccountActionTriggers(t *testing.T) { - if !*testLocal { - return - } - // Test first get so we can steal the id which we need to remove - var reply engine.ActionTriggers - req := AttrAcntAction{Tenant: "cgrates.org", Account: "dan2"} - if err := rater.Call("ApierV1.GetAccountActionTriggers", req, &reply); err != nil { - t.Error("Got error on ApierV1.GetAccountActionTimings: ", err.Error()) - } else if len(reply) != 1 || reply[0].ActionsId != "LOG_BALANCE" { - for _, atr := range reply { - t.Logf("ATR: %+v", atr) - } - t.Errorf("Unexpected action triggers received %v", reply) - } - var rmReply string - rmReq := AttrRemoveAccountActionTriggers{Tenant: "cgrates.org", Account: "dan2", UniqueID: reply[0].UniqueID} - if err := rater.Call("ApierV1.ResetAccountActionTriggers", rmReq, &rmReply); err != nil { - t.Error("Got error on ApierV1.ResetActionTiming: ", err.Error()) - } else if rmReply != OK { - t.Error("Unexpected answer received", rmReply) - } - if err := rater.Call("ApierV1.RemoveAccountActionTriggers", rmReq, &rmReply); err != nil { - t.Error("Got error on ApierV1.RemoveActionTiming: ", err.Error()) - } else if rmReply != OK { - t.Error("Unexpected answer received", rmReply) - } - if err := rater.Call("ApierV1.GetAccountActionTriggers", req, &reply); err != nil { - t.Error("Got error on ApierV1.GetAccountActionTriggers: ", err.Error()) - } else if len(reply) != 0 { - t.Errorf("Unexpected action triggers received %+v", reply[0]) - } -} - // Test here SetAccountActionTriggers func TestApierSetAccountActionTriggers(t *testing.T) { if !*testLocal { @@ -1109,6 +1074,41 @@ func TestApierSetAccountActionTriggers(t *testing.T) { } } +// Test here RemAccountActionTriggers +func TestApierRemAccountActionTriggers(t *testing.T) { + if !*testLocal { + return + } + // Test first get so we can steal the id which we need to remove + var reply engine.ActionTriggers + req := AttrAcntAction{Tenant: "cgrates.org", Account: "dan2"} + if err := rater.Call("ApierV1.GetAccountActionTriggers", req, &reply); err != nil { + t.Error("Got error on ApierV1.GetAccountActionTimings: ", err.Error()) + } else if len(reply) != 1 || reply[0].ActionsId != "LOG_BALANCE" { + for _, atr := range reply { + t.Logf("ATR: %+v", atr) + } + t.Errorf("Unexpected action triggers received %v", reply) + } + var rmReply string + rmReq := AttrRemoveAccountActionTriggers{Tenant: "cgrates.org", Account: "dan2", UniqueID: reply[0].UniqueID} + if err := rater.Call("ApierV1.ResetAccountActionTriggers", rmReq, &rmReply); err != nil { + t.Error("Got error on ApierV1.ResetActionTiming: ", err.Error()) + } else if rmReply != OK { + t.Error("Unexpected answer received", rmReply) + } + if err := rater.Call("ApierV1.RemoveAccountActionTriggers", rmReq, &rmReply); err != nil { + t.Error("Got error on ApierV1.RemoveActionTiming: ", err.Error()) + } else if rmReply != OK { + t.Error("Unexpected answer received", rmReply) + } + if err := rater.Call("ApierV1.GetAccountActionTriggers", req, &reply); err != nil { + t.Error("Got error on ApierV1.GetAccountActionTriggers: ", err.Error()) + } else if len(reply) != 0 { + t.Errorf("Unexpected action triggers received %+v", reply[0]) + } +} + // Test here SetAccount func TestApierSetAccount(t *testing.T) { if !*testLocal { From bab8f9d86d27140d685203c0b3cac95898b62270 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 5 Feb 2016 16:50:47 +0200 Subject: [PATCH 051/199] differenet function for parsing trigger dates --- apier/v1/triggers.go | 4 ++-- engine/tp_reader.go | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index 37b2b89b4..097269070 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -218,14 +218,14 @@ func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, at.MinSleep = minSleep } if attr.ExpirationDate != nil { - expTime, err := utils.ParseDate(*attr.ExpirationDate) + expTime, err := utils.ParseTimeDetectLayout(*attr.ExpirationDate) if err != nil { return 0, err } at.ExpirationDate = expTime } if attr.ActivationDate != nil { - actTime, err := utils.ParseDate(*attr.ActivationDate) + actTime, err := utils.ParseTimeDetectLayout(*attr.ActivationDate) if err != nil { return 0, err } diff --git a/engine/tp_reader.go b/engine/tp_reader.go index af4cae816..a27a4508b 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -797,8 +797,8 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error for idx, apiAtr := range atrsLst { minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep) balanceExpTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) - expTime, _ := utils.ParseDate(apiAtr.ExpirationDate) - actTime, _ := utils.ParseDate(apiAtr.ActivationDate) + expTime, _ := utils.ParseTimeDetectLayout(apiAtr.ExpirationDate, tpr.timezone) + actTime, _ := utils.ParseTimeDetectLayout(apiAtr.ActivationDate, tpr.timezone) if apiAtr.UniqueID == "" { apiAtr.UniqueID = utils.GenUUID() } @@ -1030,8 +1030,8 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { for idx, apiAtr := range atrsLst { minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep) balanceExpTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) - expTime, _ := utils.ParseDate(apiAtr.ExpirationDate) - actTime, _ := utils.ParseDate(apiAtr.ActivationDate) + expTime, _ := utils.ParseTimeDetectLayout(apiAtr.ExpirationDate, tpr.timezone) + actTime, _ := utils.ParseTimeDetectLayout(apiAtr.ActivationDate, tpr.timezone) if apiAtr.UniqueID == "" { apiAtr.UniqueID = utils.GenUUID() } From dd36c4e3ee8f4586fca6fdac8f6244b487b8c1a7 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 5 Feb 2016 16:59:50 +0200 Subject: [PATCH 052/199] fix compilation --- apier/v1/triggers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index 097269070..fe4f3ecd3 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -218,14 +218,14 @@ func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, at.MinSleep = minSleep } if attr.ExpirationDate != nil { - expTime, err := utils.ParseTimeDetectLayout(*attr.ExpirationDate) + expTime, err := utils.ParseTimeDetectLayout(*attr.ExpirationDate, self.Config.DefaultTimezone) if err != nil { return 0, err } at.ExpirationDate = expTime } if attr.ActivationDate != nil { - actTime, err := utils.ParseTimeDetectLayout(*attr.ActivationDate) + actTime, err := utils.ParseTimeDetectLayout(*attr.ActivationDate, self.Config.DefaultTimezone) if err != nil { return 0, err } From bce9131a6166c89ab6c7c8bb7e97abb3bccaab27 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 8 Feb 2016 12:00:10 +0200 Subject: [PATCH 053/199] added ActivationDate to AddAccountActionTrigger --- apier/v1/triggers.go | 6 ++++++ utils/coreutils.go | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index fe4f3ecd3..689cbac64 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -27,12 +27,17 @@ type AttrAddAccountActionTriggers struct { Account string ActionTriggerIDs *[]string ActionTriggerOverwrite bool + ActivationDate string } func (self *ApierV1) AddAccountActionTriggers(attr AttrAddAccountActionTriggers, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } + actTime, err := utils.ParseTimeDetectLayout(attr.ActivationDate, self.Config.DefaultTimezone) + if err != nil { + return err + } accID := utils.AccountKey(attr.Tenant, attr.Account) var account *engine.Account _, err := engine.Guardian.Guard(func() (interface{}, error) { @@ -59,6 +64,7 @@ func (self *ApierV1) AddAccountActionTriggers(attr AttrAddAccountActionTriggers, break } } + at.ActivationDate = actTime if !found { account.ActionTriggers = append(account.ActionTriggers, at) } diff --git a/utils/coreutils.go b/utils/coreutils.go index 549fa97ed..dd52569b3 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -128,6 +128,9 @@ func Round(x float64, prec int, method string) float64 { func ParseTimeDetectLayout(tmStr string, timezone string) (time.Time, error) { var nilTime time.Time + if len(tmStr) == 0 { + return nilTime + } loc, err := time.LoadLocation(timezone) if err != nil { return nilTime, err @@ -361,6 +364,10 @@ func Float64SlicePointer(slc []float64) *[]float64 { return &slc } +func StringMapPointer(sm StringMap) *StringMap { + return &sm +} + func ReflectFuncLocation(handler interface{}) (file string, line int) { f := runtime.FuncForPC(reflect.ValueOf(handler).Pointer()) entry := f.Entry() From 2b2c16976f725c73112958411c021389c36c0d1e Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 8 Feb 2016 12:31:37 +0200 Subject: [PATCH 054/199] fix compilation error --- utils/coreutils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/coreutils.go b/utils/coreutils.go index dd52569b3..29f501eda 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -129,7 +129,7 @@ func Round(x float64, prec int, method string) float64 { func ParseTimeDetectLayout(tmStr string, timezone string) (time.Time, error) { var nilTime time.Time if len(tmStr) == 0 { - return nilTime + return nilTime, nil } loc, err := time.LoadLocation(timezone) if err != nil { From 160dcbe950dad3ce7b501120779ed0201c30989c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 8 Feb 2016 12:43:21 +0200 Subject: [PATCH 055/199] fixed no new variable --- apier/v1/triggers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index 689cbac64..d2470db4f 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -40,7 +40,7 @@ func (self *ApierV1) AddAccountActionTriggers(attr AttrAddAccountActionTriggers, } accID := utils.AccountKey(attr.Tenant, attr.Account) var account *engine.Account - _, err := engine.Guardian.Guard(func() (interface{}, error) { + _, err = engine.Guardian.Guard(func() (interface{}, error) { if acc, err := self.AccountDb.GetAccount(accID); err == nil { account = acc } else { From 8a24738b85c4b322eebdf95158f688e9f5a096c8 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 9 Feb 2016 16:19:40 +0200 Subject: [PATCH 056/199] started action balance pointer filtering --- engine/account.go | 28 +++++---- engine/action.go | 129 +++++++++++++++++++++++++++++---------- engine/action_plan.go | 6 +- engine/action_trigger.go | 49 +++++++-------- engine/balances.go | 36 +++++------ engine/models.go | 14 ++--- engine/tp_reader.go | 67 ++++++++++++++++---- glide.lock | 34 ++++++++++- utils/apitpdata.go | 36 +++++------ utils/consts.go | 1 + utils/coreutils.go | 4 ++ utils/map.go | 3 + 12 files changed, 273 insertions(+), 134 deletions(-) diff --git a/engine/account.go b/engine/account.go index 287c3ab3a..c7f5f9d7b 100644 --- a/engine/account.go +++ b/engine/account.go @@ -93,7 +93,7 @@ func (ub *Account) setBalanceAction(a *Action) error { if a == nil { return errors.New("nil action") } - bClone := a.Balance.Clone() + bClone := a.Balance.CreateBalance() if bClone == nil { return errors.New("nil balance") } @@ -182,7 +182,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { if a == nil { return errors.New("nil action") } - bClone := a.Balance.Clone() + bClone := a.Balance.CreateBalance() if bClone == nil { return errors.New("nil balance") } @@ -261,6 +261,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { return nil } +/* func (ub *Account) enableDisableBalanceAction(a *Action) error { if a == nil { return errors.New("nil action") @@ -286,7 +287,7 @@ func (ub *Account) enableDisableBalanceAction(a *Action) error { } return nil } - +*/ func (ub *Account) getBalancesForPrefix(prefix, category, direction, tor string, sharedGroup string) BalanceChain { var balances BalanceChain balances = append(balances, ub.BalanceMap[tor]...) @@ -933,23 +934,24 @@ func (acc *Account) AsOldStructure() interface{} { } } for i, at := range acc.ActionTriggers { + b := at.Balance.CreateBalance() result.ActionTriggers[i] = &ActionTrigger{ Id: at.ID, ThresholdType: at.ThresholdType, ThresholdValue: at.ThresholdValue, Recurrent: at.Recurrent, MinSleep: at.MinSleep, - BalanceId: at.BalanceId, BalanceType: at.BalanceType, - BalanceDirection: at.BalanceDirections.String(), - BalanceDestinationIds: at.BalanceDestinationIds.String(), - BalanceWeight: at.BalanceWeight, - BalanceExpirationDate: at.BalanceExpirationDate, - BalanceTimingTags: at.BalanceTimingTags.String(), - BalanceRatingSubject: at.BalanceRatingSubject, - BalanceCategory: at.BalanceCategories.String(), - BalanceSharedGroup: at.BalanceSharedGroups.String(), - BalanceDisabled: at.BalanceDisabled, + BalanceId: b.Id, + BalanceDirection: b.Directions.String(), + BalanceDestinationIds: b.DestinationIds.String(), + BalanceWeight: b.Weight, + BalanceExpirationDate: b.ExpirationDate, + BalanceTimingTags: b.TimingIDs.String(), + BalanceRatingSubject: b.RatingSubject, + BalanceCategory: b.Categories.String(), + BalanceSharedGroup: b.SharedGroups.String(), + BalanceDisabled: b.Disabled, Weight: at.Weight, ActionsId: at.ActionsId, MinQueuedItems: at.MinQueuedItems, diff --git a/engine/action.go b/engine/action.go index 00e23bcb5..8e6264bd5 100644 --- a/engine/action.go +++ b/engine/action.go @@ -45,28 +45,93 @@ type Action struct { Filter string ExpirationString string // must stay as string because it can have relative values like 1month Weight float64 - Balance *Balance + Balance *BalancePointer +} + +type BalancePointer struct { + Uuid *string + Id *string + Value *float64 + Directions *utils.StringMap + ExpirationDate *time.Time + Weight *float64 + DestinationIds *utils.StringMap + RatingSubject *string + Categories *utils.StringMap + SharedGroups *utils.StringMap + TimingIDs *utils.StringMap + Timings []*RITiming + Disabled *bool + Factor *ValueFactor + Blocker *bool +} + +func (bp *BalancePointer) CreateBalance() *Balance { + b := &Balance{} + if bp.Uuid != nil { + b.Uuid = *bp.Uuid + } + if bp.Id != nil { + b.Id = *bp.Id + } + if bp.Value != nil { + b.Value = *bp.Value + } + if bp.Directions != nil { + b.Directions = *bp.Directions + } + if bp.ExpirationDate != nil { + b.ExpirationDate = *bp.ExpirationDate + } + if bp.Weight != nil { + b.Weight = *bp.Weight + } + if bp.DestinationIds != nil { + b.DestinationIds = *bp.DestinationIds + } + if bp.RatingSubject != nil { + b.RatingSubject = *bp.RatingSubject + } + if bp.Categories != nil { + b.Categories = *bp.Categories + } + if bp.SharedGroups != nil { + b.SharedGroups = *bp.SharedGroups + } + if bp.TimingIDs != nil { + b.TimingIDs = *bp.TimingIDs + } + if bp.Disabled != nil { + b.Disabled = *bp.Disabled + } + if bp.Factor != nil { + b.Factor = *bp.Factor + } + if bp.Blocker != nil { + b.Blocker = *bp.Blocker + } + return b.Clone() } const ( - LOG = "*log" - RESET_TRIGGERS = "*reset_triggers" - SET_RECURRENT = "*set_recurrent" - UNSET_RECURRENT = "*unset_recurrent" - ALLOW_NEGATIVE = "*allow_negative" - DENY_NEGATIVE = "*deny_negative" - RESET_ACCOUNT = "*reset_account" - REMOVE_ACCOUNT = "*remove_account" - SET_BALANCE = "*set_balance" // not ready for production until switching to pointers - REMOVE_BALANCE = "*remove_balance" - TOPUP_RESET = "*topup_reset" - TOPUP = "*topup" - DEBIT_RESET = "*debit_reset" - DEBIT = "*debit" - RESET_COUNTERS = "*reset_counters" - ENABLE_ACCOUNT = "*enable_account" - DISABLE_ACCOUNT = "*disable_account" - ENABLE_DISABLE_BALANCE = "*enable_disable_balance" + LOG = "*log" + RESET_TRIGGERS = "*reset_triggers" + SET_RECURRENT = "*set_recurrent" + UNSET_RECURRENT = "*unset_recurrent" + ALLOW_NEGATIVE = "*allow_negative" + DENY_NEGATIVE = "*deny_negative" + RESET_ACCOUNT = "*reset_account" + REMOVE_ACCOUNT = "*remove_account" + SET_BALANCE = "*set_balance" // not ready for production until switching to pointers + REMOVE_BALANCE = "*remove_balance" + TOPUP_RESET = "*topup_reset" + TOPUP = "*topup" + DEBIT_RESET = "*debit_reset" + DEBIT = "*debit" + RESET_COUNTERS = "*reset_counters" + ENABLE_ACCOUNT = "*enable_account" + DISABLE_ACCOUNT = "*disable_account" + //ENABLE_DISABLE_BALANCE = "*enable_disable_balance" CALL_URL = "*call_url" CALL_URL_ASYNC = "*call_url_async" MAIL_ASYNC = "*mail_async" @@ -84,7 +149,7 @@ func (a *Action) Clone() *Action { ExtraParameters: a.ExtraParameters, ExpirationString: a.ExpirationString, Weight: a.Weight, - Balance: a.Balance.Clone(), + Balance: a.Balance, } } @@ -122,8 +187,8 @@ func getActionFunc(typ string) (actionTypeFunc, bool) { return enableUserAction, true case DISABLE_ACCOUNT: return disableUserAction, true - case ENABLE_DISABLE_BALANCE: - return enableDisableBalanceAction, true + //case ENABLE_DISABLE_BALANCE: + // return enableDisableBalanceAction, true case CALL_URL: return callUrl, true case CALL_URL_ASYNC: @@ -167,6 +232,7 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) dta = new(utils.TenantAccount) // Init with empty values } var parsedValue string // Template values + b := action.Balance.CreateBalance() for _, rsrFld := range rsrFlds { switch rsrFld.Id { case "AccountID": @@ -184,17 +250,17 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) case "BalanceType": parsedValue += rsrFld.ParseValue(action.BalanceType) case "BalanceUUID": - parsedValue += rsrFld.ParseValue(action.Balance.Uuid) + parsedValue += rsrFld.ParseValue(b.Uuid) case "BalanceID": - parsedValue += rsrFld.ParseValue(action.Balance.Id) + parsedValue += rsrFld.ParseValue(b.Id) case "BalanceValue": - parsedValue += rsrFld.ParseValue(strconv.FormatFloat(action.Balance.GetValue(), 'f', -1, 64)) + parsedValue += rsrFld.ParseValue(strconv.FormatFloat(b.GetValue(), 'f', -1, 64)) case "DestinationIDs": - parsedValue += rsrFld.ParseValue(action.Balance.DestinationIds.String()) + parsedValue += rsrFld.ParseValue(b.DestinationIds.String()) case "ExtraParameters": parsedValue += rsrFld.ParseValue(action.ExtraParameters) case "RatingSubject": - parsedValue += rsrFld.ParseValue(action.Balance.RatingSubject) + parsedValue += rsrFld.ParseValue(b.RatingSubject) case utils.CATEGORY: parsedValue += rsrFld.ParseValue(action.Balance.Categories.String()) case "SharedGroups": @@ -370,8 +436,9 @@ func resetCountersAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac } func genericMakeNegative(a *Action) { - if a.Balance != nil && a.Balance.GetValue() >= 0 { // only apply if not allready negative - a.Balance.SetValue(-a.Balance.GetValue()) + b := a.Balance.CreateBalance() + if a.Balance != nil && b.GetValue() >= 0 { // only apply if not allready negative + b.SetValue(-b.GetValue()) } } @@ -401,13 +468,13 @@ func disableUserAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Acti return } -func enableDisableBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { +/*func enableDisableBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { return errors.New("nil account") } ub.enableDisableBalanceAction(a) return -} +}*/ func genericReset(ub *Account) error { for k, _ := range ub.BalanceMap { diff --git a/engine/action_plan.go b/engine/action_plan.go index 3b8069c64..0a9283d25 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -308,7 +308,8 @@ func (at *ActionTiming) Execute() (err error) { //return 0, fmt.Errorf("Account %s is disabled", accID) } if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.ExpirationDate.IsZero()) && parseErr == nil && !expDate.IsZero() { - a.Balance.ExpirationDate = expDate + a.Balance.ExpirationDate = &time.Time{} + *a.Balance.ExpirationDate = expDate } actionFunction, exists := getActionFunc(a.ActionType) @@ -339,7 +340,8 @@ func (at *ActionTiming) Execute() (err error) { if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.ExpirationDate.IsZero()) && parseErr == nil && !expDate.IsZero() { - a.Balance.ExpirationDate = expDate + a.Balance.ExpirationDate = &time.Time{} + *a.Balance.ExpirationDate = expDate } actionFunction, exists := getActionFunc(a.ActionType) diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 71c8aed6d..d9ae7b005 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -33,28 +33,18 @@ type ActionTrigger struct { UniqueID string // individual id ThresholdType string //*min_event_counter, *max_event_counter, *min_balance_counter, *max_balance_counter, *min_balance, *max_balance, *exp_balance // stats: *min_asr, *max_asr, *min_acd, *max_acd, *min_tcd, *max_tcd, *min_acc, *max_acc, *min_tcc, *max_tcc, *min_ddc, *max_ddc - ThresholdValue float64 - Recurrent bool // reset excuted flag each run - MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers - ExpirationDate time.Time - ActivationDate time.Time - BalanceId string - BalanceType string // *monetary/*voice etc - BalanceDirections utils.StringMap // filter for balance - BalanceDestinationIds utils.StringMap // filter for balance - BalanceWeight float64 // filter for balance - BalanceExpirationDate time.Time // filter for balance - BalanceTimingTags utils.StringMap // filter for balance - BalanceRatingSubject string // filter for balance - BalanceCategories utils.StringMap // filter for balance - BalanceSharedGroups utils.StringMap // filter for balance - BalanceBlocker bool - BalanceDisabled bool // filter for balance - Weight float64 - ActionsId string - MinQueuedItems int // Trigger actions only if this number is hit (stats only) - Executed bool - lastExecutionTime time.Time + ThresholdValue float64 + Recurrent bool // reset excuted flag each run + MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers + ExpirationDate time.Time + ActivationDate time.Time + BalanceType string // *monetary/*voice etc + Balance *BalancePointer + Weight float64 + ActionsId string + MinQueuedItems int // Trigger actions only if this number is hit (stats only) + Executed bool + lastExecutionTime time.Time } func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err error) { @@ -90,9 +80,12 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro } if a.Balance == nil { - a.Balance = &Balance{} + a.Balance = &BalancePointer{} + } + if a.ExpirationString != "" { + a.Balance.ExpirationDate = &time.Time{} + *a.Balance.ExpirationDate, _ = utils.ParseDate(a.ExpirationString) } - a.Balance.ExpirationDate, _ = utils.ParseDate(a.ExpirationString) actionFunction, exists := getActionFunc(a.ActionType) if !exists { utils.Logger.Err(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType)) @@ -131,7 +124,7 @@ func (at *ActionTrigger) Match(a *Action) bool { return match } id := a.BalanceType == "" || at.BalanceType == a.BalanceType - thresholdType, thresholdValue, direction, destinationId, weight, ratingSubject, categories, sharedGroup, timings, blocker, disabled := true, true, true, true, true, true, true, true, true, true, true + thresholdType, thresholdValue, direction, destinationID, weight, ratingSubject, categories, sharedGroup, timings, blocker, disabled := true, true, true, true, true, true, true, true, true, true, true if a.ExtraParameters != "" { t := struct { ThresholdType string @@ -149,8 +142,8 @@ func (at *ActionTrigger) Match(a *Action) bool { json.Unmarshal([]byte(a.ExtraParameters), &t) thresholdType = t.ThresholdType == "" || at.ThresholdType == t.ThresholdType thresholdValue = t.ThresholdValue == 0 || at.ThresholdValue == t.ThresholdValue - direction = len(t.BalanceDirections) == 0 || at.BalanceDirections.Equal(utils.ParseStringMap(t.BalanceDirections)) - destinationId = len(t.DestinationIds) == 0 || at.BalanceDestinationIds.Equal(utils.ParseStringMap(t.DestinationIds)) + direction = t.Balance.Directions == nil || at.Balance.Directions.Equal(utils.ParseStringMap(t.BalanceDirections)) + destinationID = len(t.DestinationIds) == 0 || at.BalanceDestinationIds.Equal(utils.ParseStringMap(t.DestinationIds)) categories = len(t.BalanceCategories) == 0 || at.BalanceCategories.Equal(utils.ParseStringMap(t.BalanceCategories)) timings = len(t.BalanceTimingTags) == 0 || at.BalanceTimingTags.Equal(utils.ParseStringMap(t.BalanceTimingTags)) sharedGroup = len(t.BalanceSharedGroups) == 0 || at.BalanceSharedGroups.Equal(utils.ParseStringMap(t.BalanceSharedGroups)) @@ -159,7 +152,7 @@ func (at *ActionTrigger) Match(a *Action) bool { blocker = at.BalanceBlocker == t.BalanceBlocker disabled = at.BalanceDisabled == t.BalanceDisabled } - return id && direction && thresholdType && thresholdValue && destinationId && weight && ratingSubject && categories && sharedGroup && timings && blocker && disabled + return id && direction && thresholdType && thresholdValue && destinationID && weight && ratingSubject && categories && sharedGroup && timings && blocker && disabled } // makes a shallow copy of the receiver diff --git a/engine/balances.go b/engine/balances.go index e1aa16777..b1a9fba96 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -72,32 +72,26 @@ func (b *Balance) Equal(o *Balance) bool { b.Blocker == o.Blocker } -func (b *Balance) MatchFilter(o *Balance, skipIds bool) bool { +func (b *Balance) MatchFilter(o *BalancePointer, skipIds bool) bool { if o == nil { return true } - if !skipIds && o.Uuid != "" { - return b.Uuid == o.Uuid + if !skipIds && o.Uuid != nil && *o.Uuid != "" { + return b.Uuid == *o.Uuid } - if !skipIds && o.Id != "" { - return b.Id == o.Id + if !skipIds && o.Id != nil && *o.Id != "" { + return b.Id == *o.Id } - if len(b.DestinationIds) == 0 { - b.DestinationIds = utils.StringMap{utils.ANY: true} - } - if len(o.DestinationIds) == 0 { - o.DestinationIds = utils.StringMap{utils.ANY: true} - } - return (o.ExpirationDate.IsZero() || b.ExpirationDate.Equal(o.ExpirationDate)) && - (o.Weight == 0 || b.Weight == o.Weight) && - (b.Blocker == o.Blocker) && - (b.Disabled == o.Disabled) && - (len(o.DestinationIds) == 0 || b.DestinationIds.Includes(o.DestinationIds)) && - (len(o.Directions) == 0 || b.Directions.Includes(o.Directions)) && - (len(o.Categories) == 0 || b.Categories.Includes(o.Categories)) && - (len(o.TimingIDs) == 0 || b.TimingIDs.Includes(o.TimingIDs)) && - (len(o.SharedGroups) == 0 || b.SharedGroups.Includes(o.SharedGroups)) && - (o.RatingSubject == "" || b.RatingSubject == o.RatingSubject) + return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && + (o.Weight == nil || b.Weight == *o.Weight) && + (o.Blocker != nil || b.Blocker == *o.Blocker) && + (o.Disabled == nil || b.Disabled == *o.Disabled) && + (o.DestinationIds == nil || b.DestinationIds.Includes(*o.DestinationIds)) && + (o.Directions == nil || b.Directions.Includes(*o.Directions)) && + (o.Categories == nil || b.Categories.Includes(*o.Categories)) && + (o.TimingIDs == nil || b.TimingIDs.Includes(*o.TimingIDs)) && + (o.SharedGroups == nil || b.SharedGroups.Includes(*o.SharedGroups)) && + (o.RatingSubject == nil || b.RatingSubject == *o.RatingSubject) } func (b *Balance) MatchCCFilter(cc *CallCost) bool { diff --git a/engine/models.go b/engine/models.go index d16916891..a37102094 100644 --- a/engine/models.go +++ b/engine/models.go @@ -166,10 +166,10 @@ type TpAction struct { SharedGroups string `index:"10" re:"[0-9A-Za-z_;]*"` ExpiryTime string `index:"11" re:"\*\w+\s*|\+\d+[smh]\s*|\d+\s*"` TimingTags string `index:"12" re:"[0-9A-Za-z_;]*|\*any"` - Units float64 `index:"13" re:"\d+\s*"` - BalanceWeight float64 `index:"14" re:"\d+\.?\d*\s*"` - BalanceBlocker bool `index:"15" re:""` - BalanceDisabled bool `index:"16" re:""` + Units string `index:"13" re:"\d+\s*"` + BalanceWeight string `index:"14" re:"\d+\.?\d*\s*"` + BalanceBlocker string `index:"15" re:""` + BalanceDisabled string `index:"16" re:""` Weight float64 `index:"17" re:"\d+\.?\d*\s*"` CreatedAt time.Time } @@ -204,9 +204,9 @@ type TpActionTrigger struct { BalanceSharedGroups string `index:"14" re:"\w+|\*any"` BalanceExpiryTime string `index:"15" re:"\*\w+\s*|\+\d+[smh]\s*|\d+\s*"` BalanceTimingTags string `index:"16" re:"[0-9A-Za-z_;]*|\*any"` - BalanceWeight float64 `index:"17" re:"\d+\.?\d*"` - BalanceBlocker bool `index:"18" re:""` - BalanceDisabled bool `index:"19" re:""` + BalanceWeight string `index:"17" re:"\d+\.?\d*"` + BalanceBlocker string `index:"18" re:""` + BalanceDisabled string `index:"19" re:""` MinQueuedItems int `index:"20" re:"\d+"` ActionsTag string `index:"21" re:"\w+"` Weight float64 `index:"22" re:"\d+\.?\d*"` diff --git a/engine/tp_reader.go b/engine/tp_reader.go index a27a4508b..17c7706d1 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -521,20 +521,61 @@ func (tpr *TpReader) LoadActions() (err error) { ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &Balance{ - Id: tpact.BalanceId, - Value: tpact.Units, - Weight: tpact.BalanceWeight, - RatingSubject: tpact.RatingSubject, - Categories: utils.ParseStringMap(tpact.Categories), - Directions: utils.ParseStringMap(tpact.Directions), - DestinationIds: utils.ParseStringMap(tpact.DestinationIds), - SharedGroups: utils.ParseStringMap(tpact.SharedGroups), - TimingIDs: utils.ParseStringMap(tpact.TimingTags), - Blocker: tpact.BalanceBlocker, - Disabled: tpact.BalanceDisabled, - }, + Balance: &BalancePointer{}, } + if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { + acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + } + + if tpact.Units != "" && tpact.Units != utils.ANY { + u, err := strconv.ParseFloat(tpact.Units, 64) + if err != nil { + return err + } + acts[idx].Balance.Value = utils.Float64Pointer(u) + } + + if tpact.BalanceWeight != "" && tpact.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(tpact.BalanceWeight, 64) + if err != nil { + return err + } + acts[idx].Balance.Weight = utils.Float64Pointer(u) + } + if tpact.RatingSubject != "" && tpact.RatingSubject != utils.ANY { + acts[idx].Balance.RatingSubject = utils.StringPointer(tpact.RatingSubject) + } + + if tpact.Categories != "" && tpact.Categories != utils.ANY { + acts[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(tpact.Categories)) + } + if tpact.Directions != "" && tpact.Directions != utils.ANY { + acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) + } + if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { + acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + } + if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { + acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) + } + if tpact.TimingTags != "" && tpact.TimingTags != utils.ANY { + acts[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.TimingTags)) + } + if tpact.BalanceBlocker != "" && tpact.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceBlocker) + if err != nil { + return err + } + acts[idx].Balance.Blocker = utils.BoolPointer(u) + } + if tpact.BalanceDisabled != "" && tpact.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceDisabled) + if err != nil { + return err + } + acts[idx].Balance.Disabled = utils.BoolPointer(u) + } + // load action timings from tags if tpact.TimingTags != "" { timingIds := strings.Split(tpact.TimingTags, utils.INFIELD_SEP) diff --git a/glide.lock b/glide.lock index ac52fee0e..84c1f8c76 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 855bc23b0e58452edf1d31f228430476a2602b79d225397d33b805b252cebb83 -updated: 2016-01-21T12:30:17.296295607+02:00 +updated: 2016-02-05T16:54:55.433704333+02:00 imports: - name: github.com/cenkalti/hub version: 57d753b5f4856e77b3cf8ecce78c97215a7d324d @@ -25,6 +25,8 @@ imports: - diam/datatype - diam/dict - diam/sm + - diam/sm/smparser + - diam/sm/smpeer - name: github.com/go-sql-driver/mysql version: bb006fd699a123d3eb514561dbefc352e978949d - name: github.com/gorhill/cronexpr @@ -37,11 +39,15 @@ imports: version: f7ee69f31298ecbe5d2b349c711e2547a617d398 - name: github.com/lib/pq version: 11fc39a580a008f1f39bb3d11d984fb34ed778d9 + subpackages: + - hstore + - oid - name: github.com/mediocregopher/radix.v2 version: 91435107718b55ff544323a2b0f25fdd8475d283 subpackages: - /pool - redis + - pool - name: github.com/peterh/liner version: 3f1c20449d1836aa4cbe38731b96f95cdf89634d - name: github.com/ugorji/go @@ -50,18 +56,44 @@ imports: - /codec - name: golang.org/x/crypto version: 552e9d568fde9701ea1944fb01c8aadaceaa7353 + subpackages: + - ssh/terminal - name: golang.org/x/net version: 961116aeebe66bfb58bb4d51818c70d256acbbb8 subpackages: - /websocket + - context + - html/atom + - http2/hpack + - internal/iana + - ipv4 + - ipv6 + - webdav/internal/xml - name: golang.org/x/text version: cf4986612c83df6c55578ba198316d1684a9a287 + subpackages: + - encoding + - encoding/charmap + - encoding/htmlindex + - transform + - encoding/internal/identifier + - encoding/internal + - encoding/japanese + - encoding/korean + - encoding/simplifiedchinese + - encoding/traditionalchinese + - encoding/unicode + - language + - internal/utf8internal + - runes + - internal/tag - name: gopkg.in/fsnotify.v1 version: 508915b7500b6e42a87132e4afeb4729cebc7cbb - name: gopkg.in/mgo.v2 version: e30de8ac9ae3b30df7065f766c71f88bba7d4e49 subpackages: - bson + - internal/scram - name: gopkg.in/tomb.v2 version: 14b3d72120e8d10ea6e6b7f87f7175734b1faab8 devImports: [] diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 9fdabdc9a..cc4a47a53 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -273,22 +273,22 @@ type TPActions struct { } type TPAction struct { - Identifier string // Identifier mapped in the code - BalanceId string // Balance identification string (account scope) - BalanceType string // Type of balance the action will operate on - Directions string // Balance direction - Units float64 // Number of units to add/deduct - ExpiryTime string // Time when the units will expire - Filter string // The condition on balances that is checked before the action - TimingTags string // Timing when balance is active - DestinationIds string // Destination profile id - RatingSubject string // Reference a rate subject defined in RatingProfiles - Categories string // category filter for balances - SharedGroups string // Reference to a shared group - BalanceWeight float64 // Balance weight + Identifier string // Identifier mapped in the code + BalanceId string // Balance identification string (account scope) + BalanceType string // Type of balance the action will operate on + Directions string // Balance direction + Units string // Number of units to add/deduct + ExpiryTime string // Time when the units will expire + Filter string // The condition on balances that is checked before the action + TimingTags string // Timing when balance is active + DestinationIds string // Destination profile id + RatingSubject string // Reference a rate subject defined in RatingProfiles + Categories string // category filter for balances + SharedGroups string // Reference to a shared group + BalanceWeight string // Balance weight ExtraParameters string - BalanceBlocker bool - BalanceDisabled bool + BalanceBlocker string + BalanceDisabled string Weight float64 // Action's weight } @@ -486,14 +486,14 @@ type TPActionTrigger struct { BalanceType string // Type of balance this trigger monitors BalanceDirections string // Traffic direction BalanceDestinationIds string // filter for balance - BalanceWeight float64 // filter for balance + BalanceWeight string // filter for balance BalanceExpirationDate string // filter for balance BalanceTimingTags string // filter for balance BalanceRatingSubject string // filter for balance BalanceCategories string // filter for balance BalanceSharedGroups string // filter for balance - BalanceBlocker bool // filter for balance - BalanceDisabled bool // filter for balance + BalanceBlocker string // filter for balance + BalanceDisabled string // filter for balance MinQueuedItems int // Trigger actions only if this number is hit (stats only) ActionsId string // Actions which will execute on threshold reached Weight float64 // weight diff --git a/utils/consts.go b/utils/consts.go index 6f413f1f3..b1f0559f8 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -89,6 +89,7 @@ const ( ROUNDING_MIDDLE = "*middle" ROUNDING_DOWN = "*down" ANY = "*any" + ZERO = "*zero" ASAP = "*asap" USERS = "*users" COMMENT_CHAR = '#' diff --git a/utils/coreutils.go b/utils/coreutils.go index 29f501eda..091e3fc07 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -337,6 +337,10 @@ func Fib() func() time.Duration { // Utilities to provide pointers where we need to define ad-hoc func StringPointer(str string) *string { + if str == ZERO { + str = "" + return &str + } return &str } diff --git a/utils/map.go b/utils/map.go index d303efcaf..e45488935 100644 --- a/utils/map.go +++ b/utils/map.go @@ -74,6 +74,9 @@ func NewStringMap(s ...string) StringMap { } func ParseStringMap(s string) StringMap { + if s == ZERO { + return make(StringMap) + } return StringMapFromSlice(strings.Split(s, INFIELD_SEP)) } From 95604203c496eb30fe965e3faa8b1b8452f853a4 Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 10 Feb 2016 15:14:10 +0100 Subject: [PATCH 057/199] Users returning USERS_NOT_FOUND error for better error handling --- agents/dmtagent.go | 2 +- engine/users.go | 2 +- engine/users_test.go | 2 +- utils/consts.go | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index a3624a99a..8dfef11c9 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -139,7 +139,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro var unauthorizedResultCode bool if err != nil { utils.Logger.Debug(fmt.Sprintf("Received error from rater: %+v", err)) - if strings.HasSuffix(err.Error(), utils.ErrAccountNotFound.Error()) { // 5030 in case of AccountNotFound + if strings.HasSuffix(err.Error(), utils.ErrAccountNotFound.Error()) || strings.HasSuffix(err.Error(), utils.ErrUsersNotFound.Error()) { // 5030 in case of AccountNotFound if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "5030", false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) diff --git a/engine/users.go b/engine/users.go index 69dcc2ef4..caa435558 100644 --- a/engine/users.go +++ b/engine/users.go @@ -495,5 +495,5 @@ func LoadUserProfile(in interface{}, extraFields string) error { utils.SetMapExtraFields(in, m, extraFields) return nil } - return utils.ErrNotFound + return utils.ErrUsersNotFound } diff --git a/engine/users_test.go b/engine/users_test.go index e21a926dd..45df6a58e 100644 --- a/engine/users_test.go +++ b/engine/users_test.go @@ -708,7 +708,7 @@ func TestUsersExternalCDRGetLoadUserProfileExtraFieldsNotFound(t *testing.T) { } err := LoadUserProfile(ur, "ExtraFields") - if err != utils.ErrNotFound { + if err != utils.ErrUsersNotFound { t.Error("Error detecting err in loading user profile: ", err) } } diff --git a/utils/consts.go b/utils/consts.go index 6f413f1f3..9dd06ee16 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -28,6 +28,7 @@ var ( ErrUnauthorizedDestination = errors.New("UNAUTHORIZED_DESTINATION") ErrRatingPlanNotFound = errors.New("RATING_PLAN_NOT_FOUND") ErrAccountNotFound = errors.New("ACCOUNT_NOT_FOUND") + ErrUsersNotFound = errors.New("USERS_NOT_FOUND") ) const ( From 1f41970e86ac02cfb09ef68078754ec52f6ac054 Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 10 Feb 2016 15:19:39 +0100 Subject: [PATCH 058/199] USERS_NOT_FOUND -> USER_NOT_FOUND for consistency --- agents/dmtagent.go | 2 +- engine/users.go | 2 +- engine/users_test.go | 2 +- utils/consts.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 8dfef11c9..bdf119ba1 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -139,7 +139,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro var unauthorizedResultCode bool if err != nil { utils.Logger.Debug(fmt.Sprintf("Received error from rater: %+v", err)) - if strings.HasSuffix(err.Error(), utils.ErrAccountNotFound.Error()) || strings.HasSuffix(err.Error(), utils.ErrUsersNotFound.Error()) { // 5030 in case of AccountNotFound + if strings.HasSuffix(err.Error(), utils.ErrAccountNotFound.Error()) || strings.HasSuffix(err.Error(), utils.ErrUserNotFound.Error()) { // 5030 in case of AccountNotFound if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "5030", false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) diff --git a/engine/users.go b/engine/users.go index caa435558..66d573de2 100644 --- a/engine/users.go +++ b/engine/users.go @@ -495,5 +495,5 @@ func LoadUserProfile(in interface{}, extraFields string) error { utils.SetMapExtraFields(in, m, extraFields) return nil } - return utils.ErrUsersNotFound + return utils.ErrUserNotFound } diff --git a/engine/users_test.go b/engine/users_test.go index 45df6a58e..442b22411 100644 --- a/engine/users_test.go +++ b/engine/users_test.go @@ -708,7 +708,7 @@ func TestUsersExternalCDRGetLoadUserProfileExtraFieldsNotFound(t *testing.T) { } err := LoadUserProfile(ur, "ExtraFields") - if err != utils.ErrUsersNotFound { + if err != utils.ErrUserNotFound { t.Error("Error detecting err in loading user profile: ", err) } } diff --git a/utils/consts.go b/utils/consts.go index 9dd06ee16..c7d6e2d74 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -28,7 +28,7 @@ var ( ErrUnauthorizedDestination = errors.New("UNAUTHORIZED_DESTINATION") ErrRatingPlanNotFound = errors.New("RATING_PLAN_NOT_FOUND") ErrAccountNotFound = errors.New("ACCOUNT_NOT_FOUND") - ErrUsersNotFound = errors.New("USERS_NOT_FOUND") + ErrUserNotFound = errors.New("USER_NOT_FOUND") ) const ( From 7bf15bf825cd9a2249a78afc63bf29ca6f90184e Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 11 Feb 2016 10:26:43 +0200 Subject: [PATCH 059/199] engine compiling --- engine/account.go | 18 +- engine/account_test.go | 223 ++++++++++------ engine/action.go | 76 +++++- engine/action_trigger.go | 66 +---- engine/actions_local_test.go | 9 +- engine/actions_test.go | 376 +++++++++++++------------- engine/balances.go | 64 ++--- engine/balances_test.go | 28 +- engine/calldesc_test.go | 6 +- engine/loader_csv_test.go | 134 +++++----- engine/model_helpers_test.go | 20 +- engine/storage_test.go | 16 +- engine/tp_reader.go | 495 ++++++++++++++++++++++++++--------- engine/units_counter.go | 6 +- engine/units_counter_test.go | 394 ++++++++++++++++------------ utils/coreutils.go | 4 + 16 files changed, 1157 insertions(+), 778 deletions(-) diff --git a/engine/account.go b/engine/account.go index c7f5f9d7b..50650c546 100644 --- a/engine/account.go +++ b/engine/account.go @@ -111,7 +111,7 @@ func (ub *Account) setBalanceAction(a *Action) error { ub.BalanceMap = make(map[string]BalanceChain, 1) } found := false - balanceType := a.BalanceType + balanceType := a.Balance.GetType() var previousSharedGroups utils.StringMap // kept for comparison for _, b := range ub.BalanceMap[balanceType] { if b.IsExpired() { @@ -190,7 +190,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { ub.BalanceMap = make(map[string]BalanceChain, 1) } found := false - balanceType := a.BalanceType + balanceType := a.Balance.GetType() for _, b := range ub.BalanceMap[balanceType] { if b.IsExpired() { continue // just to be safe (cleaned expired balances above) @@ -592,7 +592,7 @@ func (acc *Account) ExecuteActionTriggers(a *Action) { } if strings.Contains(at.ThresholdType, "counter") { for _, uc := range acc.UnitCounters { - if uc.BalanceType == at.BalanceType && + if uc.BalanceType == at.Balance.GetType() && strings.Contains(at.ThresholdType, uc.CounterType[1:]) { for _, b := range uc.Balances { if strings.HasPrefix(at.ThresholdType, "*max") { @@ -608,7 +608,7 @@ func (acc *Account) ExecuteActionTriggers(a *Action) { } } } else { // BALANCE - for _, b := range acc.BalanceMap[at.BalanceType] { + for _, b := range acc.BalanceMap[at.Balance.GetType()] { if !b.dirty && at.ThresholdType != utils.TRIGGER_BALANCE_EXPIRED { // do not check clean balances continue } @@ -674,17 +674,17 @@ func (acc *Account) InitCounters() { ct = utils.COUNTER_BALANCE } - uc, exists := ucTempMap[at.BalanceType+ct] + uc, exists := ucTempMap[at.Balance.GetType()+ct] if !exists { uc = &UnitCounter{ - BalanceType: at.BalanceType, + BalanceType: at.Balance.GetType(), CounterType: ct, } - ucTempMap[at.BalanceType+ct] = uc + ucTempMap[at.Balance.GetType()+ct] = uc uc.Balances = BalanceChain{} acc.UnitCounters = append(acc.UnitCounters, uc) } - b := at.CreateBalance() + b := at.Balance.CreateBalance() if !uc.Balances.HasBalance(b) { uc.Balances = append(uc.Balances, b) } @@ -941,7 +941,7 @@ func (acc *Account) AsOldStructure() interface{} { ThresholdValue: at.ThresholdValue, Recurrent: at.Recurrent, MinSleep: at.MinSleep, - BalanceType: at.BalanceType, + BalanceType: at.Balance.GetType(), BalanceId: b.Id, BalanceDirection: b.Directions.String(), BalanceDestinationIds: b.DestinationIds.String(), diff --git a/engine/account_test.go b/engine/account_test.go index 11a40f65b..b603e135d 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -814,22 +814,32 @@ func TestAccountdebitBalance(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &Balance{Weight: 20, DestinationIds: utils.StringMap{"NEW": true}, Directions: utils.NewStringMap(utils.OUT)} - a := &Action{BalanceType: utils.VOICE, Balance: newMb} + newMb := &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Weight: utils.Float64Pointer(20), + DestinationIds: utils.StringMapPointer(utils.StringMap{"NEW": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } + a := &Action{Balance: newMb} ub.debitBalanceAction(a, false) - if len(ub.BalanceMap[utils.VOICE]) != 3 || !ub.BalanceMap[utils.VOICE][2].DestinationIds.Equal(newMb.DestinationIds) { + if len(ub.BalanceMap[utils.VOICE]) != 3 || !ub.BalanceMap[utils.VOICE][2].DestinationIds.Equal(*newMb.DestinationIds) { t.Errorf("Error adding minute bucket! %d %+v %+v", len(ub.BalanceMap[utils.VOICE]), ub.BalanceMap[utils.VOICE][2], newMb) } } -func TestAccountDisableBalance(t *testing.T) { +/*func TestAccountDisableBalance(t *testing.T) { ub := &Account{ Id: "rif", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT), Disabled: true} - a := &Action{BalanceType: utils.VOICE, Balance: newMb} + newMb := &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Weight: utils.Float64Pointer(20), + DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } + a := &Action{Balance: newMb} ub.enableDisableBalanceAction(a) if len(ub.BalanceMap[utils.VOICE]) != 2 || ub.BalanceMap[utils.VOICE][0].Disabled != true { for _, b := range ub.BalanceMap[utils.VOICE] { @@ -837,7 +847,7 @@ func TestAccountDisableBalance(t *testing.T) { } t.Errorf("Error disabling balance! %d %+v %+v", len(ub.BalanceMap[utils.VOICE]), ub.BalanceMap[utils.VOICE][0], newMb) } -} +}*/ func TestAccountdebitBalanceExists(t *testing.T) { @@ -846,8 +856,14 @@ func TestAccountdebitBalanceExists(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Value: 15, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &Balance{Value: -10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)} - a := &Action{BalanceType: utils.VOICE, Balance: newMb} + newMb := &BalancePointer{ + Value: utils.Float64Pointer(-10), + Type: utils.StringPointer(utils.VOICE), + Weight: utils.Float64Pointer(20), + DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } + a := &Action{Balance: newMb} ub.debitBalanceAction(a, false) if len(ub.BalanceMap[utils.VOICE]) != 2 || ub.BalanceMap[utils.VOICE][0].GetValue() != 25 { t.Error("Error adding minute bucket!") @@ -867,21 +883,36 @@ func TestAccountAddMinuteNil(t *testing.T) { } func TestAccountAddMinutBucketEmpty(t *testing.T) { - mb1 := &Balance{Value: -10, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)} - mb2 := &Balance{Value: -10, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)} - mb3 := &Balance{Value: -10, DestinationIds: utils.StringMap{"OTHER": true}, Directions: utils.NewStringMap(utils.OUT)} + mb1 := &BalancePointer{ + Value: utils.Float64Pointer(-10), + Type: utils.StringPointer(utils.VOICE), + DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } + mb2 := &BalancePointer{ + Value: utils.Float64Pointer(-10), + Type: utils.StringPointer(utils.VOICE), + DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } + mb3 := &BalancePointer{ + Value: utils.Float64Pointer(-10), + Type: utils.StringPointer(utils.VOICE), + DestinationIds: utils.StringMapPointer(utils.StringMap{"OTHER": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } ub := &Account{} - a := &Action{BalanceType: utils.VOICE, Balance: mb1} + a := &Action{Balance: mb1} ub.debitBalanceAction(a, false) if len(ub.BalanceMap[utils.VOICE]) != 1 { t.Error("Error adding minute bucket: ", ub.BalanceMap[utils.VOICE]) } - a = &Action{BalanceType: utils.VOICE, Balance: mb2} + a = &Action{Balance: mb2} ub.debitBalanceAction(a, false) if len(ub.BalanceMap[utils.VOICE]) != 1 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { t.Error("Error adding minute bucket: ", ub.BalanceMap[utils.VOICE]) } - a = &Action{BalanceType: utils.VOICE, Balance: mb3} + a = &Action{Balance: mb3} ub.debitBalanceAction(a, false) if len(ub.BalanceMap[utils.VOICE]) != 2 { t.Error("Error adding minute bucket: ", ub.BalanceMap[utils.VOICE]) @@ -893,7 +924,7 @@ func TestAccountExecuteTriggeredActions(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.StringMap{utils.OUT: true}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { @@ -917,7 +948,7 @@ func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, nil, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { @@ -930,7 +961,7 @@ func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { Id: "TEST_UB_OREDER", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) @@ -945,8 +976,8 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{UniqueID: "day_trigger", BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, - &ActionTrigger{UniqueID: "week_trigger", BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{UniqueID: "day_trigger", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{UniqueID: "week_trigger", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, }, } ub.InitCounters() @@ -961,7 +992,7 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { } // we can reset them - resetCountersAction(ub, nil, &Action{BalanceType: utils.MONETARY, Balance: &Balance{Id: "day_trigger"}}, nil) + resetCountersAction(ub, nil, &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("day_trigger")}}, nil) if ub.UnitCounters[0].Balances[0].Value != 0 || ub.UnitCounters[0].Balances[1].Value != 1 { t.Error("Error reseting both counters", ub.UnitCounters[0].Balances[0].Value, ub.UnitCounters[0].Balances[1].Value) @@ -973,7 +1004,7 @@ func TestAccountExpActionTrigger(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100, ExpirationDate: time.Date(2015, time.November, 9, 9, 48, 0, 0, time.UTC)}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -991,7 +1022,7 @@ func TestAccountExpActionTriggerNotActivated(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1009,7 +1040,7 @@ func TestAccountExpActionTriggerExpired(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1565,46 +1596,58 @@ func TestAccountInitCounters(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -1628,46 +1671,58 @@ func TestAccountDoubleInitCounters(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, }, } diff --git a/engine/action.go b/engine/action.go index 8e6264bd5..94ab9ba1e 100644 --- a/engine/action.go +++ b/engine/action.go @@ -38,9 +38,9 @@ import ( Structure to be filled for each tariff plan with the bonus value for received calls minutes. */ type Action struct { - Id string - ActionType string - BalanceType string + Id string + ActionType string + //BalanceType string ExtraParameters string Filter string ExpirationString string // must stay as string because it can have relative values like 1month @@ -51,6 +51,7 @@ type Action struct { type BalancePointer struct { Uuid *string Id *string + Type *string Value *float64 Directions *utils.StringMap ExpirationDate *time.Time @@ -113,6 +114,61 @@ func (bp *BalancePointer) CreateBalance() *Balance { return b.Clone() } +func (bp *BalancePointer) LoadFromBalance(b *Balance) { + if b.Uuid != "" { + bp.Uuid = &b.Uuid + } + if b.Id != "" { + bp.Id = &b.Id + } + if b.Value != 0 { + bp.Value = &b.Value + } + if len(b.Directions) != 0 { + bp.Directions = &b.Directions + } + if !b.ExpirationDate.IsZero() { + bp.ExpirationDate = &b.ExpirationDate + } + if b.Weight != 0 { + bp.Weight = &b.Weight + } + if len(b.DestinationIds) != 0 { + bp.DestinationIds = &b.DestinationIds + } + if b.RatingSubject != "" { + bp.RatingSubject = &b.RatingSubject + } + if len(b.Categories) != 0 { + bp.Categories = &b.Categories + } + if len(b.SharedGroups) != 0 { + bp.SharedGroups = &b.SharedGroups + } + if len(b.TimingIDs) != 0 { + bp.TimingIDs = &b.TimingIDs + } + if len(b.Factor) != 0 { + bp.Factor = &b.Factor + } + bp.Disabled = &b.Disabled + bp.Blocker = &b.Blocker +} + +func (bp *BalancePointer) GetType() string { + if bp.Type == nil { + return "" + } + return *bp.Type +} + +func (bp *BalancePointer) GetValue() float64 { + if bp.Value == nil { + return 0.0 + } + return *bp.Value +} + const ( LOG = "*log" RESET_TRIGGERS = "*reset_triggers" @@ -143,9 +199,9 @@ const ( func (a *Action) Clone() *Action { return &Action{ - Id: a.Id, - ActionType: a.ActionType, - BalanceType: a.BalanceType, + Id: a.Id, + ActionType: a.ActionType, + //BalanceType: a.BalanceType, ExtraParameters: a.ExtraParameters, ExpirationString: a.ExpirationString, Weight: a.Weight, @@ -248,7 +304,7 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) case "ActionType": parsedValue += rsrFld.ParseValue(action.ActionType) case "BalanceType": - parsedValue += rsrFld.ParseValue(action.BalanceType) + parsedValue += rsrFld.ParseValue(action.Balance.GetType()) case "BalanceUUID": parsedValue += rsrFld.ParseValue(b.Uuid) case "BalanceID": @@ -651,10 +707,10 @@ func removeAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac } func removeBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { - if _, exists := ub.BalanceMap[a.BalanceType]; !exists { + if _, exists := ub.BalanceMap[a.Balance.GetType()]; !exists { return utils.ErrNotFound } - bChain := ub.BalanceMap[a.BalanceType] + bChain := ub.BalanceMap[a.Balance.GetType()] found := false for i := 0; i < len(bChain); i++ { if bChain[i].MatchFilter(a.Balance, false) { @@ -665,7 +721,7 @@ func removeBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac found = true } } - ub.BalanceMap[a.BalanceType] = bChain + ub.BalanceMap[a.Balance.GetType()] = bChain if !found { return utils.ErrNotFound } diff --git a/engine/action_trigger.go b/engine/action_trigger.go index d9ae7b005..0fe22916c 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -19,9 +19,7 @@ along with this program. If not, see package engine import ( - "encoding/json" "fmt" - "regexp" "sort" "time" @@ -33,12 +31,12 @@ type ActionTrigger struct { UniqueID string // individual id ThresholdType string //*min_event_counter, *max_event_counter, *min_balance_counter, *max_balance_counter, *min_balance, *max_balance, *exp_balance // stats: *min_asr, *max_asr, *min_acd, *max_acd, *min_tcd, *max_tcd, *min_acc, *max_acc, *min_tcc, *max_tcc, *min_ddc, *max_ddc - ThresholdValue float64 - Recurrent bool // reset excuted flag each run - MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers - ExpirationDate time.Time - ActivationDate time.Time - BalanceType string // *monetary/*voice etc + ThresholdValue float64 + Recurrent bool // reset excuted flag each run + MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers + ExpirationDate time.Time + ActivationDate time.Time + //BalanceType string // *monetary/*voice etc Balance *BalancePointer Weight float64 ActionsId string @@ -118,41 +116,7 @@ func (at *ActionTrigger) Match(a *Action) bool { if a == nil { return true } - // if we have Id than we can draw an early conclusion - if a.Id != "" { - match, _ := regexp.MatchString(a.Id, at.ID) - return match - } - id := a.BalanceType == "" || at.BalanceType == a.BalanceType - thresholdType, thresholdValue, direction, destinationID, weight, ratingSubject, categories, sharedGroup, timings, blocker, disabled := true, true, true, true, true, true, true, true, true, true, true - if a.ExtraParameters != "" { - t := struct { - ThresholdType string - ThresholdValue float64 - DestinationIds string - BalanceDirections string - BalanceWeight float64 - BalanceRatingSubject string - BalanceCategories string - BalanceSharedGroups string - BalanceTimingTags string - BalanceBlocker bool - BalanceDisabled bool - }{} - json.Unmarshal([]byte(a.ExtraParameters), &t) - thresholdType = t.ThresholdType == "" || at.ThresholdType == t.ThresholdType - thresholdValue = t.ThresholdValue == 0 || at.ThresholdValue == t.ThresholdValue - direction = t.Balance.Directions == nil || at.Balance.Directions.Equal(utils.ParseStringMap(t.BalanceDirections)) - destinationID = len(t.DestinationIds) == 0 || at.BalanceDestinationIds.Equal(utils.ParseStringMap(t.DestinationIds)) - categories = len(t.BalanceCategories) == 0 || at.BalanceCategories.Equal(utils.ParseStringMap(t.BalanceCategories)) - timings = len(t.BalanceTimingTags) == 0 || at.BalanceTimingTags.Equal(utils.ParseStringMap(t.BalanceTimingTags)) - sharedGroup = len(t.BalanceSharedGroups) == 0 || at.BalanceSharedGroups.Equal(utils.ParseStringMap(t.BalanceSharedGroups)) - weight = t.BalanceWeight == 0 || at.BalanceWeight == t.BalanceWeight - ratingSubject = t.BalanceRatingSubject == "" || at.BalanceRatingSubject == t.BalanceRatingSubject - blocker = at.BalanceBlocker == t.BalanceBlocker - disabled = at.BalanceDisabled == t.BalanceDisabled - } - return id && direction && thresholdType && thresholdValue && destinationID && weight && ratingSubject && categories && sharedGroup && timings && blocker && disabled + return at.Balance.CreateBalance().MatchFilter(a.Balance, false) } // makes a shallow copy of the receiver @@ -162,22 +126,6 @@ func (at *ActionTrigger) Clone() *ActionTrigger { return clone } -func (at *ActionTrigger) CreateBalance() *Balance { - return &Balance{ - Id: at.UniqueID, - Directions: at.BalanceDirections, - ExpirationDate: at.BalanceExpirationDate, - DestinationIds: at.BalanceDestinationIds, - RatingSubject: at.BalanceRatingSubject, - Categories: at.BalanceCategories, - SharedGroups: at.BalanceSharedGroups, - TimingIDs: at.BalanceTimingTags, - Blocker: at.BalanceBlocker, - Disabled: at.BalanceDisabled, - Weight: at.BalanceWeight, - } -} - func (at *ActionTrigger) Equals(oat *ActionTrigger) bool { // ids only return at.ID == oat.ID && at.UniqueID == oat.UniqueID diff --git a/engine/actions_local_test.go b/engine/actions_local_test.go index 177fe19b0..bdc3b8b79 100644 --- a/engine/actions_local_test.go +++ b/engine/actions_local_test.go @@ -23,6 +23,7 @@ import ( "net/rpc" "net/rpc/jsonrpc" "path" + "strconv" "testing" "time" @@ -94,7 +95,7 @@ func TestActionsLocalSetCdrlogDebit(t *testing.T) { t.Errorf("Calling ApierV1.SetAccount received: %s", reply) } attrsAA := &utils.AttrSetActions{ActionsId: "ACTS_1", Actions: []*utils.TPAction{ - &utils.TPAction{Identifier: DEBIT, BalanceType: utils.MONETARY, Units: 5.0, ExpiryTime: UNLIMITED, Weight: 20.0}, + &utils.TPAction{Identifier: DEBIT, BalanceType: utils.MONETARY, Units: "5.0", ExpiryTime: UNLIMITED, Weight: 20.0}, &utils.TPAction{Identifier: CDRLOG}, }} if err := actsLclRpc.Call("ApierV1.SetActions", attrsAA, &reply); err != nil && err.Error() != utils.ErrExists.Error() { @@ -122,7 +123,7 @@ func TestActionsLocalSetCdrlogDebit(t *testing.T) { rcvedCdrs[0].Subject != "dan2904" || rcvedCdrs[0].Usage != "1" || rcvedCdrs[0].RunID != DEBIT || - rcvedCdrs[0].Cost != attrsAA.Actions[0].Units { + strconv.FormatFloat(rcvedCdrs[0].Cost, 'f', -1, 64) != attrsAA.Actions[0].Units { t.Errorf("Received: %+v", rcvedCdrs[0]) } } @@ -139,7 +140,7 @@ func TestActionsLocalSetCdrlogTopup(t *testing.T) { t.Errorf("Calling ApierV1.SetAccount received: %s", reply) } attrsAA := &utils.AttrSetActions{ActionsId: "ACTS_2", Actions: []*utils.TPAction{ - &utils.TPAction{Identifier: TOPUP, BalanceType: utils.MONETARY, Units: 5.0, ExpiryTime: UNLIMITED, Weight: 20.0}, + &utils.TPAction{Identifier: TOPUP, BalanceType: utils.MONETARY, Units: "5.0", ExpiryTime: UNLIMITED, Weight: 20.0}, &utils.TPAction{Identifier: CDRLOG}, }} if err := actsLclRpc.Call("ApierV1.SetActions", attrsAA, &reply); err != nil && err.Error() != utils.ErrExists.Error() { @@ -167,7 +168,7 @@ func TestActionsLocalSetCdrlogTopup(t *testing.T) { rcvedCdrs[0].Subject != "dan2905" || rcvedCdrs[0].Usage != "1" || rcvedCdrs[0].RunID != TOPUP || - rcvedCdrs[0].Cost != attrsAA.Actions[0].Units { + strconv.FormatFloat(rcvedCdrs[0].Cost, 'f', -1, 64) != attrsAA.Actions[0].Units { t.Errorf("Received: %+v", rcvedCdrs[0]) } } diff --git a/engine/actions_test.go b/engine/actions_test.go index 1eb5367d6..e0e4244db 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -409,9 +409,11 @@ func TestActionPlanCheckForASAP(t *testing.T) { func TestActionPlanLogFunction(t *testing.T) { a := &Action{ - ActionType: "*log", - BalanceType: "test", - Balance: &Balance{Value: 1.1}, + ActionType: "*log", + Balance: &BalancePointer{ + Type: utils.StringPointer("test"), + Value: utils.Float64Pointer(1.1), + }, } at := &ActionTiming{ actions: []*Action{a}, @@ -424,9 +426,11 @@ func TestActionPlanLogFunction(t *testing.T) { func TestActionPlanFunctionNotAvailable(t *testing.T) { a := &Action{ - ActionType: "VALID_FUNCTION_TYPE", - BalanceType: "test", - Balance: &Balance{Value: 1.1}, + ActionType: "VALID_FUNCTION_TYPE", + Balance: &BalancePointer{ + Type: utils.StringPointer("test"), + Value: utils.Float64Pointer(1.1), + }, } at := &ActionTiming{ accountIDs: utils.StringMap{"cgrates.org:dy": true}, @@ -527,10 +531,12 @@ func TestActionPlansRemoveMember(t *testing.T) { func TestActionTriggerMatchNil(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } var a *Action if !at.Match(a) { @@ -540,10 +546,12 @@ func TestActionTriggerMatchNil(t *testing.T) { func TestActionTriggerMatchAllBlank(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } a := &Action{} if !at.Match(a) { @@ -553,12 +561,14 @@ func TestActionTriggerMatchAllBlank(t *testing.T) { func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } - a := &Action{BalanceType: utils.MONETARY, ExtraParameters: `{"BalanceDirections":"*out"}`} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: `{"BalanceDirections":"*out"}`} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -566,10 +576,12 @@ func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) { func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } a := &Action{ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, utils.TRIGGER_MAX_BALANCE, 2)} if !at.Match(a) { @@ -579,12 +591,14 @@ func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { func TestActionTriggerMatchAllFull(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } - a := &Action{BalanceType: utils.MONETARY, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 2)} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 2)} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -592,12 +606,14 @@ func TestActionTriggerMatchAllFull(t *testing.T) { func TestActionTriggerMatchSomeFalse(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } - a := &Action{BalanceType: utils.MONETARY, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_BALANCE, 2)} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_BALANCE, 2)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -605,12 +621,14 @@ func TestActionTriggerMatchSomeFalse(t *testing.T) { func TestActionTriggerMatcBalanceFalse(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } - a := &Action{BalanceType: utils.MONETARY, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 3.0)} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 3.0)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -618,12 +636,14 @@ func TestActionTriggerMatcBalanceFalse(t *testing.T) { func TestActionTriggerMatcAllFalse(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } - a := &Action{BalanceType: utils.VOICE, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_EVENT_COUNTER, 3)} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_EVENT_COUNTER, 3)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -631,16 +651,18 @@ func TestActionTriggerMatcAllFalse(t *testing.T) { func TestActionTriggerMatchAll(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, - BalanceDestinationIds: utils.NewStringMap("NAT"), - BalanceWeight: 1.0, - BalanceRatingSubject: "test1", - BalanceSharedGroups: utils.NewStringMap("test2"), + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + RatingSubject: utils.StringPointer("test1"), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Value: utils.Float64Pointer(2), + Weight: utils.Float64Pointer(1.0), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + SharedGroups: utils.StringMapPointer(utils.NewStringMap("test2")), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, } - a := &Action{BalanceType: utils.MONETARY, ExtraParameters: fmt.Sprintf(`{"BalanceDirections":"*out", "ThresholdType":"%v", "ThresholdValue": %v, "DestinationIds": "%v", "BalanceWeight": %v, "BalanceRatingSubject": "%v", "BalanceSharedGroup": "%v"}`, utils.TRIGGER_MAX_BALANCE, 2, "NAT", 1.0, "test1", "test2")} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"BalanceDirections":"*out", "ThresholdType":"%v", "ThresholdValue": %v, "DestinationIds": "%v", "BalanceWeight": %v, "BalanceRatingSubject": "%v", "BalanceSharedGroup": "%v"}`, utils.TRIGGER_MAX_BALANCE, 2, "NAT", 1.0, "test1", "test2")} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -663,7 +685,7 @@ func TestActionResetTriggres(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { @@ -676,7 +698,7 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.BalanceMap[utils.MONETARY][0].GetValue() == 12 { @@ -689,9 +711,9 @@ func TestActionResetTriggresActionFilter(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - resetTriggersAction(ub, nil, &Action{BalanceType: utils.SMS}, nil) + resetTriggersAction(ub, nil, &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}}, nil) if ub.ActionTriggers[0].Executed == false || ub.ActionTriggers[1].Executed == false { t.Error("Reset triggers action failed!") } @@ -702,7 +724,7 @@ func TestActionSetPostpaid(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } allowNegativeAction(ub, nil, nil, nil) if !ub.AllowNegative { @@ -716,7 +738,7 @@ func TestActionSetPrepaid(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } denyNegativeAction(ub, nil, nil, nil) if ub.AllowNegative { @@ -730,7 +752,7 @@ func TestActionResetPrepaid(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) if !ub.AllowNegative || @@ -748,7 +770,7 @@ func TestActionResetPostpaid(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) if ub.BalanceMap[utils.MONETARY].GetTotalValue() != 0 || @@ -764,9 +786,9 @@ func TestActionTopupResetCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 10 || @@ -783,10 +805,10 @@ func TestActionTopupValueFactor(t *testing.T) { BalanceMap: map[string]BalanceChain{}, } a := &Action{ - BalanceType: utils.MONETARY, - Balance: &Balance{ - Value: 10, - Directions: utils.NewStringMap(utils.OUT), + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Value: utils.Float64Pointer(10), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ExtraParameters: `{"*monetary":2.0}`, } @@ -806,7 +828,7 @@ func TestActionTopupResetCreditId(t *testing.T) { }, }, } - a := &Action{BalanceType: utils.MONETARY, Balance: &Balance{Id: "TEST_B", Value: 10, Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("TEST_B"), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -825,7 +847,7 @@ func TestActionTopupResetCreditNoId(t *testing.T) { }, }, } - a := &Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 20 || @@ -841,9 +863,9 @@ func TestActionTopupResetMinutes(t *testing.T) { utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.VOICE, Balance: &Balance{Value: 5, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 5 || @@ -860,9 +882,9 @@ func TestActionTopupCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -878,9 +900,9 @@ func TestActionTopupMinutes(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.VOICE, Balance: &Balance{Value: 5, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 15 || @@ -897,9 +919,9 @@ func TestActionDebitCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 90 || @@ -915,9 +937,9 @@ func TestActionDebitMinutes(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.VOICE, Balance: &Balance{Value: 5, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE][0].GetValue() != 5 || @@ -939,7 +961,7 @@ func TestActionResetAllCounters(t *testing.T) { &Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT)}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, BalanceType: utils.MONETARY, ThresholdValue: 2, BalanceDestinationIds: utils.NewStringMap("NAT"), BalanceWeight: 20, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, } ub.InitCounters() resetCountersAction(ub, nil, nil, nil) @@ -967,9 +989,9 @@ func TestActionResetCounterOnlyDefault(t *testing.T) { BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.MONETARY} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}} ub.InitCounters() resetCountersAction(ub, nil, a, nil) if !ub.AllowNegative || @@ -998,9 +1020,9 @@ func TestActionResetCounterCredit(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}, &UnitCounter{BalanceType: utils.SMS, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.MONETARY} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}} resetCountersAction(ub, nil, a, nil) if !ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || @@ -1013,13 +1035,15 @@ func TestActionResetCounterCredit(t *testing.T) { func TestActionTriggerLogging(t *testing.T) { at := &ActionTrigger{ - ID: "some_uuid", - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - ThresholdValue: 100.0, - BalanceDestinationIds: utils.NewStringMap("NAT"), - Weight: 10.0, - ActionsId: "TEST_ACTIONS", + ID: "some_uuid", + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + }, + ThresholdValue: 100.0, + Weight: 10.0, + ActionsId: "TEST_ACTIONS", } as, err := ratingStorage.GetActions(at.ActionsId, false) if err != nil { @@ -1084,7 +1108,7 @@ func TestActionPlanLogging(t *testing.T) { } func TestActionMakeNegative(t *testing.T) { - a := &Action{Balance: &Balance{Value: 10}} + a := &Action{Balance: &BalancePointer{Value: utils.Float64Pointer(10)}} genericMakeNegative(a) if a.Balance.GetValue() > 0 { t.Error("Failed to make negative: ", a) @@ -1117,9 +1141,8 @@ func TestRemoveAction(t *testing.T) { func TestTopupAction(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minu") a := &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT), Weight: 20}, + ActionType: TOPUP, + Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1139,9 +1162,8 @@ func TestTopupAction(t *testing.T) { func TestTopupActionLoaded(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minitsboy") a := &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT), Weight: 20}, + ActionType: TOPUP, + Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1168,7 +1190,7 @@ func TestActionCdrlogEmpty(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1190,11 +1212,11 @@ func TestActionCdrlogWithParams(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1217,11 +1239,11 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1301,14 +1323,12 @@ func TestActionTransactionFuncType(t *testing.T) { Timing: &RateInterval{}, actions: []*Action{ &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Balance: &Balance{Value: 1.1}, + ActionType: TOPUP, + Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ - ActionType: "VALID_FUNCTION_TYPE", - BalanceType: "test", - Balance: &Balance{Value: 1.1}, + ActionType: "VALID_FUNCTION_TYPE", + Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer("test")}, }, }, } @@ -1339,14 +1359,12 @@ func TestActionTransactionBalanceType(t *testing.T) { Timing: &RateInterval{}, actions: []*Action{ &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Balance: &Balance{Value: 1.1}, + ActionType: TOPUP, + Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ - ActionType: TOPUP, - BalanceType: "test", - Balance: nil, + ActionType: TOPUP, + Balance: &BalancePointer{Type: utils.StringPointer("test")}, }, }, } @@ -1377,18 +1395,18 @@ func TestActionWithExpireWithoutExpire(t *testing.T) { Timing: &RateInterval{}, actions: []*Action{ &Action{ - ActionType: TOPUP, - BalanceType: utils.VOICE, - Balance: &Balance{ - Value: 15, + ActionType: TOPUP, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Value: utils.Float64Pointer(15), }, }, &Action{ - ActionType: TOPUP, - BalanceType: utils.VOICE, - Balance: &Balance{ - Value: 30, - ExpirationDate: time.Date(2025, time.November, 11, 22, 39, 0, 0, time.UTC), + ActionType: TOPUP, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Value: utils.Float64Pointer(30), + ExpirationDate: utils.TimePointer(time.Date(2025, time.November, 11, 22, 39, 0, 0, time.UTC)), }, }, }, @@ -1432,10 +1450,10 @@ func TestActionRemoveBalance(t *testing.T) { Timing: &RateInterval{}, actions: []*Action{ &Action{ - ActionType: REMOVE_BALANCE, - BalanceType: utils.MONETARY, - Balance: &Balance{ - DestinationIds: utils.NewStringMap("NAT", "RET"), + ActionType: REMOVE_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT", "RET")), }, }, }, @@ -1543,7 +1561,7 @@ func TestActionTransferMonetaryDefaultFilter(t *testing.T) { a := &Action{ ActionType: TRANSFER_MONETARY_DEFAULT, - Balance: &Balance{Weight: 20}, + Balance: &BalancePointer{Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1603,12 +1621,12 @@ func TestActionConditionalTopup(t *testing.T) { } a := &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Filter: `{"Type":"*monetary","Value":1,"Weight":10}`, - Balance: &Balance{ - Value: 11, - Weight: 30, + ActionType: TOPUP, + Filter: `{"Type":"*monetary","Value":1,"Weight":10}`, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Value: utils.Float64Pointer(11), + Weight: utils.Float64Pointer(30), }, } @@ -1667,12 +1685,12 @@ func TestActionConditionalTopupNoMatch(t *testing.T) { } a := &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Filter: `{"Type":"*monetary","Value":2,"Weight":10}`, - Balance: &Balance{ - Value: 11, - Weight: 30, + ActionType: TOPUP, + Filter: `{"Type":"*monetary","Value":2,"Weight":10}`, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Value: utils.Float64Pointer(11), + Weight: utils.Float64Pointer(30), }, } @@ -1731,12 +1749,12 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) { } a := &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Filter: `{"Type":"*voice","Value":{"*gte":100}}`, - Balance: &Balance{ - Value: 11, - Weight: 10, + ActionType: TOPUP, + Filter: `{"Type":"*voice","Value":{"*gte":100}}`, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Value: utils.Float64Pointer(11), + Weight: utils.Float64Pointer(10), }, } @@ -1832,45 +1850,45 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { } a1 := &Action{ - ActionType: "*enable_disable_balance", - BalanceType: "*sms", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &Balance{ - Weight: 10, - Disabled: true, + ActionType: "*enable_disable_balance", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &BalancePointer{ + Type: utils.StringPointer("*sms"), + Weight: utils.Float64Pointer(10), + Disabled: utils.BoolPointer(true), }, Weight: 9, } a2 := &Action{ - ActionType: "*enable_disable_balance", - BalanceType: "*sms", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &Balance{ - DestinationIds: utils.NewStringMap("FRANCE_NATIONAL"), - Weight: 10, - Disabled: true, + ActionType: "*enable_disable_balance", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &BalancePointer{ + Type: utils.StringPointer("*sms"), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), + Weight: utils.Float64Pointer(10), + Disabled: utils.BoolPointer(true), }, Weight: 8, } a3 := &Action{ - ActionType: "*enable_disable_balance", - BalanceType: "*data", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &Balance{ - RatingSubject: "for_v3hsillmilld500m_data_forfait", - Weight: 10, - Disabled: true, + ActionType: "*enable_disable_balance", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &BalancePointer{ + Type: utils.StringPointer("*data"), + RatingSubject: utils.StringPointer("for_v3hsillmilld500m_data_forfait"), + Weight: utils.Float64Pointer(10), + Disabled: utils.BoolPointer(true), }, Weight: 7, } a4 := &Action{ - ActionType: "*enable_disable_balance", - BalanceType: "*voice", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &Balance{ - DestinationIds: utils.NewStringMap("FRANCE_NATIONAL"), - Weight: 10, - Disabled: true, + ActionType: "*enable_disable_balance", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &BalancePointer{ + Type: utils.StringPointer("*voice"), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), + Weight: utils.Float64Pointer(10), + Disabled: utils.BoolPointer(true), }, Weight: 6, } @@ -1937,12 +1955,12 @@ func TestActionSetBalance(t *testing.T) { } a := &Action{ - ActionType: SET_BALANCE, - BalanceType: utils.MONETARY, - Balance: &Balance{ - Id: "m2", - Value: 11, - Weight: 10, + ActionType: SET_BALANCE, + Balance: &BalancePointer{ + Id: utils.StringPointer("m2"), + Type: utils.StringPointer(utils.MONETARY), + Value: utils.Float64Pointer(11), + Weight: utils.Float64Pointer(10), }, } diff --git a/engine/balances.go b/engine/balances.go index b1a9fba96..38dd1e8e4 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -94,6 +94,28 @@ func (b *Balance) MatchFilter(o *BalancePointer, skipIds bool) bool { (o.RatingSubject == nil || b.RatingSubject == *o.RatingSubject) } +func (b *Balance) HardMatchFilter(o *BalancePointer, skipIds bool) bool { + if o == nil { + return true + } + if !skipIds && o.Uuid != nil && *o.Uuid != "" { + return b.Uuid == *o.Uuid + } + if !skipIds && o.Id != nil && *o.Id != "" { + return b.Id == *o.Id + } + return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && + (o.Weight == nil || b.Weight == *o.Weight) && + (o.Blocker != nil || b.Blocker == *o.Blocker) && + (o.Disabled == nil || b.Disabled == *o.Disabled) && + (o.DestinationIds == nil || b.DestinationIds.Equal(*o.DestinationIds)) && + (o.Directions == nil || b.Directions.Equal(*o.Directions)) && + (o.Categories == nil || b.Categories.Equal(*o.Categories)) && + (o.TimingIDs == nil || b.TimingIDs.Equal(*o.TimingIDs)) && + (o.SharedGroups == nil || b.SharedGroups.Equal(*o.SharedGroups)) && + (o.RatingSubject == nil || b.RatingSubject == *o.RatingSubject) +} + func (b *Balance) MatchCCFilter(cc *CallCost) bool { if len(b.Categories) > 0 && cc.Category != "" && b.Categories[cc.Category] == false { return false @@ -174,47 +196,7 @@ func (b *Balance) MatchDestination(destinationId string) bool { } func (b *Balance) MatchActionTrigger(at *ActionTrigger) bool { - if at.BalanceId != "" { - return b.Id == at.BalanceId - } - matchesDestination := true - if len(at.BalanceDestinationIds) != 0 { - matchesDestination = (b.DestinationIds.Equal(at.BalanceDestinationIds)) - } - matchesDirection := true - if len(at.BalanceDirections) != 0 { - matchesDirection = (b.Directions.Equal(at.BalanceDirections)) - } - matchesExpirationDate := true - if !at.BalanceExpirationDate.IsZero() { - matchesExpirationDate = (at.BalanceExpirationDate.Equal(b.ExpirationDate)) - } - matchesWeight := true - if at.BalanceWeight > 0 { - matchesWeight = (at.BalanceWeight == b.Weight) - } - matchesRatingSubject := true - if at.BalanceRatingSubject != "" { - matchesRatingSubject = (at.BalanceRatingSubject == b.RatingSubject) - } - - matchesSharedGroup := true - if len(at.BalanceSharedGroups) != 0 { - matchesSharedGroup = at.BalanceSharedGroups.Equal(b.SharedGroups) - } - - matchesTiming := true - if len(at.BalanceTimingTags) != 0 { - matchesTiming = at.BalanceTimingTags.Equal(b.TimingIDs) - } - - return matchesDestination && - matchesDirection && - matchesExpirationDate && - matchesWeight && - matchesRatingSubject && - matchesSharedGroup && - matchesTiming + return b.HardMatchFilter(at.Balance, false) } func (b *Balance) Clone() *Balance { diff --git a/engine/balances_test.go b/engine/balances_test.go index 163ab79ec..09c9dd17f 100644 --- a/engine/balances_test.go +++ b/engine/balances_test.go @@ -90,7 +90,7 @@ func TestBalanceEqual(t *testing.T) { func TestBalanceMatchFilter(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &Balance{Weight: 1, precision: 1, RatingSubject: "", DestinationIds: utils.StringMap{}} + mb2 := &BalancePointer{Weight: utils.Float64Pointer(1), RatingSubject: nil, DestinationIds: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -98,7 +98,7 @@ func TestBalanceMatchFilter(t *testing.T) { func TestBalanceMatchFilterEmpty(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &Balance{} + mb2 := &BalancePointer{} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -106,7 +106,7 @@ func TestBalanceMatchFilterEmpty(t *testing.T) { func TestBalanceMatchFilterId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 2, precision: 2, RatingSubject: "2", DestinationIds: utils.NewStringMap("NAT")} - mb2 := &Balance{Id: "T1", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} + mb2 := &BalancePointer{Id: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -114,7 +114,7 @@ func TestBalanceMatchFilterId(t *testing.T) { func TestBalanceMatchFilterDiffId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &Balance{Id: "T2", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} + mb2 := &BalancePointer{Id: utils.StringPointer("T2"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} if mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v != %+v", mb1, mb2) } @@ -129,7 +129,7 @@ func TestBalanceClone(t *testing.T) { } func TestBalanceMatchActionTriggerId(t *testing.T) { - at := &ActionTrigger{BalanceId: "test"} + at := &ActionTrigger{Balance: &BalancePointer{Id: utils.StringPointer("test")}} b := &Balance{Id: "test"} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -143,14 +143,14 @@ func TestBalanceMatchActionTriggerId(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.Id = "test" - at.BalanceId = "" + at.Balance.Id = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } } func TestBalanceMatchActionTriggerDestination(t *testing.T) { - at := &ActionTrigger{BalanceDestinationIds: utils.NewStringMap("test")} + at := &ActionTrigger{Balance: &BalancePointer{DestinationIds: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{DestinationIds: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -164,14 +164,14 @@ func TestBalanceMatchActionTriggerDestination(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.DestinationIds = utils.NewStringMap("test") - at.BalanceDestinationIds = utils.NewStringMap("") + at.Balance.DestinationIds = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } } func TestBalanceMatchActionTriggerWeight(t *testing.T) { - at := &ActionTrigger{BalanceWeight: 1} + at := &ActionTrigger{Balance: &BalancePointer{Weight: utils.Float64Pointer(1)}} b := &Balance{Weight: 1} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -185,14 +185,14 @@ func TestBalanceMatchActionTriggerWeight(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.Weight = 1 - at.BalanceWeight = 0 + at.Balance.Weight = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } } func TestBalanceMatchActionTriggerRatingSubject(t *testing.T) { - at := &ActionTrigger{BalanceRatingSubject: "test"} + at := &ActionTrigger{Balance: &BalancePointer{RatingSubject: utils.StringPointer("test")}} b := &Balance{RatingSubject: "test"} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -206,14 +206,14 @@ func TestBalanceMatchActionTriggerRatingSubject(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.RatingSubject = "test" - at.BalanceRatingSubject = "" + at.Balance.RatingSubject = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } } func TestBalanceMatchActionTriggerSharedGroup(t *testing.T) { - at := &ActionTrigger{BalanceSharedGroups: utils.NewStringMap("test")} + at := &ActionTrigger{Balance: &BalancePointer{SharedGroups: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{SharedGroups: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -227,7 +227,7 @@ func TestBalanceMatchActionTriggerSharedGroup(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.SharedGroups = utils.NewStringMap("test") - at.BalanceSharedGroups = utils.NewStringMap("") + at.Balance.SharedGroups = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index f470910ae..e4d5cdc9e 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -41,12 +41,12 @@ func init() { func populateDB() { ats := []*Action{ - &Action{ActionType: "*topup", BalanceType: utils.MONETARY, Balance: &Balance{Value: 10}}, - &Action{ActionType: "*topup", BalanceType: utils.VOICE, Balance: &Balance{Weight: 20, Value: 10, DestinationIds: utils.NewStringMap("NAT")}}, + &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}}, + &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: utils.Float64Pointer(10), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, } ats1 := []*Action{ - &Action{ActionType: "*topup", BalanceType: utils.MONETARY, Balance: &Balance{Value: 10}, Weight: 10}, + &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}, Weight: 10}, &Action{ActionType: "*reset_account", Weight: 20}, } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index ffb85bbae..4cb27daf0 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -823,38 +823,38 @@ func TestLoadActions(t *testing.T) { &Action{ Id: "MINI0", ActionType: TOPUP_RESET, - BalanceType: utils.MONETARY, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, - Balance: &Balance{ + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), Uuid: as1[0].Balance.Uuid, - Directions: utils.NewStringMap(utils.OUT), - Value: 10, - Weight: 10, - DestinationIds: utils.StringMap{}, - TimingIDs: utils.StringMap{}, - SharedGroups: utils.StringMap{}, - Categories: utils.StringMap{}, + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Value: utils.Float64Pointer(10), + Weight: utils.Float64Pointer(10), + DestinationIds: nil, + TimingIDs: nil, + SharedGroups: nil, + Categories: nil, }, }, &Action{ Id: "MINI1", ActionType: TOPUP, - BalanceType: utils.VOICE, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, - Balance: &Balance{ + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), Uuid: as1[1].Balance.Uuid, - Directions: utils.NewStringMap(utils.OUT), - Value: 100, - Weight: 10, - RatingSubject: "test", - DestinationIds: utils.NewStringMap("NAT"), - TimingIDs: utils.StringMap{}, - SharedGroups: utils.StringMap{}, - Categories: utils.StringMap{}, + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Value: utils.Float64Pointer(100), + Weight: utils.Float64Pointer(10), + RatingSubject: utils.StringPointer("test"), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + TimingIDs: nil, + SharedGroups: nil, + Categories: nil, }, }, } @@ -866,18 +866,18 @@ func TestLoadActions(t *testing.T) { &Action{ Id: "SHARED0", ActionType: TOPUP, - BalanceType: utils.MONETARY, ExpirationString: UNLIMITED, Weight: 10, - Balance: &Balance{ - Directions: utils.NewStringMap(utils.OUT), - DestinationIds: utils.StringMap{}, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: nil, Uuid: as2[0].Balance.Uuid, - Value: 100, - Weight: 10, - SharedGroups: utils.NewStringMap("SG1"), - TimingIDs: utils.StringMap{}, - Categories: utils.StringMap{}, + Value: utils.Float64Pointer(100), + Weight: utils.Float64Pointer(10), + SharedGroups: utils.StringMapPointer(utils.NewStringMap("SG1")), + TimingIDs: nil, + Categories: nil, }, }, } @@ -891,14 +891,14 @@ func TestLoadActions(t *testing.T) { ActionType: CDRLOG, ExtraParameters: `{"Category":"^ddi","MediationRunId":"^did_run"}`, Weight: 10, - Balance: &Balance{ + Balance: &BalancePointer{ Uuid: as3[0].Balance.Uuid, - Directions: utils.StringMap{}, - DestinationIds: utils.StringMap{}, - TimingIDs: utils.StringMap{}, - Categories: utils.StringMap{}, - SharedGroups: utils.StringMap{}, - Blocker: false, + Directions: nil, + DestinationIds: nil, + TimingIDs: nil, + Categories: nil, + SharedGroups: nil, + Blocker: utils.BoolPointer(false), }, }, } @@ -1043,38 +1043,42 @@ func TestLoadActionTriggers(t *testing.T) { } atr := csvr.actionsTriggers["STANDARD_TRIGGER"][0] expected := &ActionTrigger{ - ID: "STANDARD_TRIGGER", - UniqueID: "st0", - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, - ThresholdValue: 10, - BalanceDestinationIds: utils.NewStringMap("GERMANY_O2"), - BalanceCategories: utils.StringMap{}, - BalanceTimingTags: utils.StringMap{}, - BalanceSharedGroups: utils.StringMap{}, - Weight: 10, - ActionsId: "SOME_1", - Executed: false, + ID: "STANDARD_TRIGGER", + UniqueID: "st0", + ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, + ThresholdValue: 10, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), + Categories: nil, + TimingIDs: nil, + SharedGroups: nil, + }, + Weight: 10, + ActionsId: "SOME_1", + Executed: false, } if !reflect.DeepEqual(atr, expected) { t.Errorf("Error loading action trigger: %+v", atr) } atr = csvr.actionsTriggers["STANDARD_TRIGGER"][1] expected = &ActionTrigger{ - ID: "STANDARD_TRIGGER", - UniqueID: "st1", - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 200, - BalanceDestinationIds: utils.NewStringMap("GERMANY"), - BalanceCategories: utils.StringMap{}, - BalanceTimingTags: utils.StringMap{}, - BalanceSharedGroups: utils.StringMap{}, - Weight: 10, - ActionsId: "SOME_2", - Executed: false, + ID: "STANDARD_TRIGGER", + UniqueID: "st1", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 200, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY")), + Categories: nil, + TimingIDs: nil, + SharedGroups: nil, + }, + Weight: 10, + ActionsId: "SOME_2", + Executed: false, } if !reflect.DeepEqual(atr, expected) { t.Errorf("Error loading action trigger: %+v", atr) @@ -1098,9 +1102,9 @@ func TestLoadAccountActions(t *testing.T) { Value: 0, Directions: utils.NewStringMap("*out"), DestinationIds: utils.NewStringMap("GERMANY_O2"), - SharedGroups: utils.StringMap{}, - Categories: utils.StringMap{}, - TimingIDs: utils.StringMap{}, + SharedGroups: nil, + Categories: nil, + TimingIDs: nil, }, }, }, @@ -1134,7 +1138,7 @@ func TestLoadDerivedChargers(t *testing.T) { t.Error("Failed to load derivedChargers: ", csvr.derivedChargers) } expCharger1 := &utils.DerivedChargers{ - DestinationIDs: utils.StringMap{}, + DestinationIDs: nil, Chargers: []*utils.DerivedCharger{ &utils.DerivedCharger{RunID: "extra1", RunFilters: "^filteredHeader1/filterValue1/", RequestTypeField: "^prepaid", DirectionField: utils.META_DEFAULT, TenantField: utils.META_DEFAULT, CategoryField: utils.META_DEFAULT, AccountField: "rif", SubjectField: "rif", DestinationField: utils.META_DEFAULT, diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 90f1986c3..085012a37 100644 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -240,26 +240,26 @@ func TestTPActionsAsExportSlice(t *testing.T) { Identifier: "*topup_reset", BalanceType: "*monetary", Directions: utils.OUT, - Units: 5.0, + Units: "5.0", ExpiryTime: "*never", DestinationIds: "*any", RatingSubject: "special1", Categories: "call", SharedGroups: "GROUP1", - BalanceWeight: 10.0, + BalanceWeight: "10.0", ExtraParameters: "", Weight: 10.0}, &utils.TPAction{ Identifier: "*http_post", BalanceType: "", Directions: "", - Units: 0.0, + Units: "0.0", ExpiryTime: "", DestinationIds: "", RatingSubject: "", Categories: "", SharedGroups: "", - BalanceWeight: 0.0, + BalanceWeight: "0.0", ExtraParameters: "http://localhost/¶m1=value1", Weight: 20.0}, }, @@ -561,14 +561,14 @@ func TestTPActionPlanAsExportSlice(t *testing.T) { BalanceType: "*monetary", BalanceDirections: "*out", BalanceDestinationIds: "", - BalanceWeight: 0.0, + BalanceWeight: "0.0", BalanceExpirationDate: "*never", BalanceTimingTags: "T1", BalanceRatingSubject: "special1", BalanceCategories: "call", BalanceSharedGroups: "SHARED_1", - BalanceBlocker: false, - BalanceDisabled: false, + BalanceBlocker: "false", + BalanceDisabled: "false", MinQueuedItems: 0, ActionsId: "LOG_WARNING", Weight: 10}, @@ -583,14 +583,14 @@ func TestTPActionPlanAsExportSlice(t *testing.T) { BalanceType: "*monetary", BalanceDirections: "*out", BalanceDestinationIds: "FS_USERS", - BalanceWeight: 0.0, + BalanceWeight: "0.0", BalanceExpirationDate: "*never", BalanceTimingTags: "T1", BalanceRatingSubject: "special1", BalanceCategories: "call", BalanceSharedGroups: "SHARED_1", - BalanceBlocker: false, - BalanceDisabled: false, + BalanceBlocker: "false", + BalanceDisabled: "false", MinQueuedItems: 0, ActionsId: "LOG_WARNING", Weight: 10}, diff --git a/engine/storage_test.go b/engine/storage_test.go index 98a312739..1a38f6bd2 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -315,13 +315,15 @@ func GetUB() *Account { Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, } at := &ActionTrigger{ - ID: "some_uuid", - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - ThresholdValue: 100.0, - BalanceDestinationIds: utils.NewStringMap("NAT"), - Weight: 10.0, - ActionsId: "Commando", + ID: "some_uuid", + ThresholdValue: 100.0, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + }, + Weight: 10.0, + ActionsId: "Commando", } var zeroTime time.Time zeroTime = zeroTime.UTC() // for deep equal to find location diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 17c7706d1..27075be55 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -514,9 +514,9 @@ func (tpr *TpReader) LoadActions() (err error) { } } acts[idx] = &Action{ - Id: tag + strconv.Itoa(idx), - ActionType: tpact.Identifier, - BalanceType: tpact.BalanceType, + Id: tag + strconv.Itoa(idx), + ActionType: tpact.Identifier, + //BalanceType: tpact.BalanceType, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, @@ -526,6 +526,9 @@ func (tpr *TpReader) LoadActions() (err error) { if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) } + if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { + acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) + } if tpact.Units != "" && tpact.Units != utils.ANY { u, err := strconv.ParseFloat(tpact.Units, 64) @@ -542,6 +545,13 @@ func (tpr *TpReader) LoadActions() (err error) { } acts[idx].Balance.Weight = utils.Float64Pointer(u) } + if tpact.ExpiryTime != "" && tpact.ExpiryTime != utils.ANY { + u, err := utils.ParseTimeDetectLayout(tpact.ExpiryTime, tpr.timezone) + if err != nil { + return err + } + acts[idx].Balance.ExpirationDate = utils.TimePointer(u) + } if tpact.RatingSubject != "" && tpact.RatingSubject != utils.ANY { acts[idx].Balance.RatingSubject = utils.StringPointer(tpact.RatingSubject) } @@ -666,10 +676,6 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { for key, atrsLst := range storAts { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, atr := range atrsLst { - balanceExpirationDate, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) - if err != nil { - return err - } expirationDate, err := utils.ParseTimeDetectLayout(atr.ExpirationDate, tpr.timezone) if err != nil { return err @@ -686,29 +692,73 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { atr.UniqueID = utils.GenUUID() } atrs[idx] = &ActionTrigger{ - ID: key, - UniqueID: atr.UniqueID, - ThresholdType: atr.ThresholdType, - ThresholdValue: atr.ThresholdValue, - Recurrent: atr.Recurrent, - MinSleep: minSleep, - ExpirationDate: expirationDate, - ActivationDate: activationDate, - BalanceId: atr.BalanceId, - BalanceType: atr.BalanceType, - BalanceDirections: utils.ParseStringMap(atr.BalanceDirections), - BalanceDestinationIds: utils.ParseStringMap(atr.BalanceDestinationIds), - BalanceWeight: atr.BalanceWeight, - BalanceExpirationDate: balanceExpirationDate, - BalanceTimingTags: utils.ParseStringMap(atr.BalanceTimingTags), - BalanceRatingSubject: atr.BalanceRatingSubject, - BalanceCategories: utils.ParseStringMap(atr.BalanceCategories), - BalanceSharedGroups: utils.ParseStringMap(atr.BalanceSharedGroups), - BalanceBlocker: atr.BalanceBlocker, - BalanceDisabled: atr.BalanceDisabled, - Weight: atr.Weight, - ActionsId: atr.ActionsId, - MinQueuedItems: atr.MinQueuedItems, + ID: key, + UniqueID: atr.UniqueID, + ThresholdType: atr.ThresholdType, + ThresholdValue: atr.ThresholdValue, + Recurrent: atr.Recurrent, + MinSleep: minSleep, + ExpirationDate: expirationDate, + ActivationDate: activationDate, + Balance: &BalancePointer{}, + Weight: atr.Weight, + ActionsId: atr.ActionsId, + MinQueuedItems: atr.MinQueuedItems, + } + if atr.BalanceId != "" && atr.BalanceId != utils.ANY { + atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + } + + if atr.BalanceType != "" && atr.BalanceType != utils.ANY { + atrs[idx].Balance.Type = utils.StringPointer(atr.BalanceType) + } + + if atr.BalanceWeight != "" && atr.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(atr.BalanceWeight, 64) + if err != nil { + return err + } + atrs[idx].Balance.Weight = utils.Float64Pointer(u) + } + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) + if err != nil { + return err + } + atrs[idx].Balance.ExpirationDate = utils.TimePointer(u) + } + if atr.BalanceRatingSubject != "" && atr.BalanceRatingSubject != utils.ANY { + atrs[idx].Balance.RatingSubject = utils.StringPointer(atr.BalanceRatingSubject) + } + + if atr.BalanceCategories != "" && atr.BalanceCategories != utils.ANY { + atrs[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceCategories)) + } + if atr.BalanceDirections != "" && atr.BalanceDirections != utils.ANY { + atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) + } + if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { + atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + } + if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { + atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) + } + if atr.BalanceTimingTags != "" && atr.BalanceTimingTags != utils.ANY { + atrs[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceTimingTags)) + } + if atr.BalanceBlocker != "" && atr.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceBlocker) + if err != nil { + return err + } + atrs[idx].Balance.Blocker = utils.BoolPointer(u) + } + if atr.BalanceDisabled != "" && atr.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceDisabled) + if err != nil { + return err + } + atrs[idx].Balance.Disabled = utils.BoolPointer(u) } } tpr.actionsTriggers[key] = atrs @@ -835,37 +885,80 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error atrsMap := make(map[string][]*ActionTrigger) for key, atrsLst := range atrs { atrs := make([]*ActionTrigger, len(atrsLst)) - for idx, apiAtr := range atrsLst { - minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep) - balanceExpTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) - expTime, _ := utils.ParseTimeDetectLayout(apiAtr.ExpirationDate, tpr.timezone) - actTime, _ := utils.ParseTimeDetectLayout(apiAtr.ActivationDate, tpr.timezone) - if apiAtr.UniqueID == "" { - apiAtr.UniqueID = utils.GenUUID() + for idx, atr := range atrsLst { + minSleep, _ := utils.ParseDurationWithSecs(atr.MinSleep) + expTime, _ := utils.ParseTimeDetectLayout(atr.ExpirationDate, tpr.timezone) + actTime, _ := utils.ParseTimeDetectLayout(atr.ActivationDate, tpr.timezone) + if atr.UniqueID == "" { + atr.UniqueID = utils.GenUUID() } atrs[idx] = &ActionTrigger{ - ID: key, - UniqueID: apiAtr.UniqueID, - ThresholdType: apiAtr.ThresholdType, - ThresholdValue: apiAtr.ThresholdValue, - Recurrent: apiAtr.Recurrent, - MinSleep: minSleep, - ExpirationDate: expTime, - ActivationDate: actTime, - BalanceId: apiAtr.BalanceId, - BalanceType: apiAtr.BalanceType, - BalanceDirections: utils.ParseStringMap(apiAtr.BalanceDirections), - BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds), - BalanceWeight: apiAtr.BalanceWeight, - BalanceExpirationDate: balanceExpTime, - BalanceTimingTags: utils.ParseStringMap(apiAtr.BalanceTimingTags), - BalanceRatingSubject: apiAtr.BalanceRatingSubject, - BalanceCategories: utils.ParseStringMap(apiAtr.BalanceCategories), - BalanceSharedGroups: utils.ParseStringMap(apiAtr.BalanceSharedGroups), - BalanceBlocker: apiAtr.BalanceBlocker, - BalanceDisabled: apiAtr.BalanceDisabled, - Weight: apiAtr.Weight, - ActionsId: apiAtr.ActionsId, + ID: key, + UniqueID: atr.UniqueID, + ThresholdType: atr.ThresholdType, + ThresholdValue: atr.ThresholdValue, + Recurrent: atr.Recurrent, + MinSleep: minSleep, + ExpirationDate: expTime, + ActivationDate: actTime, + Balance: &BalancePointer{}, + Weight: atr.Weight, + ActionsId: atr.ActionsId, + } + if atr.BalanceId != "" && atr.BalanceId != utils.ANY { + atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + } + + if atr.BalanceType != "" && atr.BalanceType != utils.ANY { + atrs[idx].Balance.Type = utils.StringPointer(atr.BalanceType) + } + + if atr.BalanceWeight != "" && atr.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(atr.BalanceWeight, 64) + if err != nil { + return err + } + atrs[idx].Balance.Weight = utils.Float64Pointer(u) + } + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) + if err != nil { + return err + } + atrs[idx].Balance.ExpirationDate = utils.TimePointer(u) + } + if atr.BalanceRatingSubject != "" && atr.BalanceRatingSubject != utils.ANY { + atrs[idx].Balance.RatingSubject = utils.StringPointer(atr.BalanceRatingSubject) + } + + if atr.BalanceCategories != "" && atr.BalanceCategories != utils.ANY { + atrs[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceCategories)) + } + if atr.BalanceDirections != "" && atr.BalanceDirections != utils.ANY { + atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) + } + if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { + atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + } + if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { + atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) + } + if atr.BalanceTimingTags != "" && atr.BalanceTimingTags != utils.ANY { + atrs[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceTimingTags)) + } + if atr.BalanceBlocker != "" && atr.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceBlocker) + if err != nil { + return err + } + atrs[idx].Balance.Blocker = utils.BoolPointer(u) + } + if atr.BalanceDisabled != "" && atr.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceDisabled) + if err != nil { + return err + } + atrs[idx].Balance.Disabled = utils.BoolPointer(u) } } atrsMap[key] = atrs @@ -883,7 +976,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error } // actions - acts := make(map[string][]*Action) + facts := make(map[string][]*Action) for _, actId := range actionsIds { tpas, err := tpr.lr.GetTpActions(tpr.tpid, actId) if err != nil { @@ -894,7 +987,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error return err } for tag, tpacts := range as { - enacts := make([]*Action, len(tpacts)) + acts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { // check filter field if len(tpact.Filter) > 0 { @@ -902,34 +995,95 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error return fmt.Errorf("error parsing action %s filter field: %v", tag, err) } } - enacts[idx] = &Action{ - Id: tag + strconv.Itoa(idx), - ActionType: tpact.Identifier, - BalanceType: tpact.BalanceType, + acts[idx] = &Action{ + Id: tag + strconv.Itoa(idx), + ActionType: tpact.Identifier, + //BalanceType: tpact.BalanceType, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &Balance{ - Id: tpact.BalanceId, - Value: tpact.Units, - Weight: tpact.BalanceWeight, - RatingSubject: tpact.RatingSubject, - Categories: utils.ParseStringMap(tpact.Categories), - Directions: utils.ParseStringMap(tpact.Directions), - DestinationIds: utils.ParseStringMap(tpact.DestinationIds), - SharedGroups: utils.ParseStringMap(tpact.SharedGroups), - TimingIDs: utils.ParseStringMap(tpact.TimingTags), - Blocker: tpact.BalanceBlocker, - Disabled: tpact.BalanceDisabled, - }, + Balance: &BalancePointer{}, + } + if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { + acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + } + if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { + acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) + } + + if tpact.Units != "" && tpact.Units != utils.ANY { + u, err := strconv.ParseFloat(tpact.Units, 64) + if err != nil { + return err + } + acts[idx].Balance.Value = utils.Float64Pointer(u) + } + + if tpact.BalanceWeight != "" && tpact.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(tpact.BalanceWeight, 64) + if err != nil { + return err + } + acts[idx].Balance.Weight = utils.Float64Pointer(u) + } + if tpact.RatingSubject != "" && tpact.RatingSubject != utils.ANY { + acts[idx].Balance.RatingSubject = utils.StringPointer(tpact.RatingSubject) + } + + if tpact.Categories != "" && tpact.Categories != utils.ANY { + acts[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(tpact.Categories)) + } + if tpact.Directions != "" && tpact.Directions != utils.ANY { + acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) + } + if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { + acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + } + if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { + acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) + } + if tpact.TimingTags != "" && tpact.TimingTags != utils.ANY { + acts[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.TimingTags)) + } + if tpact.BalanceBlocker != "" && tpact.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceBlocker) + if err != nil { + return err + } + acts[idx].Balance.Blocker = utils.BoolPointer(u) + } + if tpact.BalanceDisabled != "" && tpact.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceDisabled) + if err != nil { + return err + } + acts[idx].Balance.Disabled = utils.BoolPointer(u) + } + // load action timings from tags + if tpact.TimingTags != "" { + timingIds := strings.Split(tpact.TimingTags, utils.INFIELD_SEP) + for _, timingID := range timingIds { + if timing, found := tpr.timings[timingID]; found { + acts[idx].Balance.Timings = append(acts[idx].Balance.Timings, &RITiming{ + Years: timing.Years, + Months: timing.Months, + MonthDays: timing.MonthDays, + WeekDays: timing.WeekDays, + StartTime: timing.StartTime, + EndTime: timing.EndTime, + }) + } else { + return fmt.Errorf("could not find timing: %v", timingID) + } + } } } - acts[tag] = enacts + facts[tag] = acts } } // write actions - for k, as := range acts { + for k, as := range facts { err = tpr.ratingStorage.SetActions(k, as) if err != nil { return err @@ -1068,35 +1222,80 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { for _, atrsLst := range atrsM { atrs := make([]*ActionTrigger, len(atrsLst)) - for idx, apiAtr := range atrsLst { - minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep) - balanceExpTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) - expTime, _ := utils.ParseTimeDetectLayout(apiAtr.ExpirationDate, tpr.timezone) - actTime, _ := utils.ParseTimeDetectLayout(apiAtr.ActivationDate, tpr.timezone) - if apiAtr.UniqueID == "" { - apiAtr.UniqueID = utils.GenUUID() + for idx, atr := range atrsLst { + minSleep, _ := utils.ParseDurationWithSecs(atr.MinSleep) + expTime, _ := utils.ParseTimeDetectLayout(atr.ExpirationDate, tpr.timezone) + actTime, _ := utils.ParseTimeDetectLayout(atr.ActivationDate, tpr.timezone) + if atr.UniqueID == "" { + atr.UniqueID = utils.GenUUID() } atrs[idx] = &ActionTrigger{ - ID: triggerTag, - UniqueID: apiAtr.UniqueID, - ThresholdType: apiAtr.ThresholdType, - ThresholdValue: apiAtr.ThresholdValue, - Recurrent: apiAtr.Recurrent, - MinSleep: minSleep, - ExpirationDate: expTime, - ActivationDate: actTime, - BalanceId: apiAtr.BalanceId, - BalanceType: apiAtr.BalanceType, - BalanceDirections: utils.ParseStringMap(apiAtr.BalanceDirections), - BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds), - BalanceWeight: apiAtr.BalanceWeight, - BalanceExpirationDate: balanceExpTime, - BalanceRatingSubject: apiAtr.BalanceRatingSubject, - BalanceCategories: utils.ParseStringMap(apiAtr.BalanceCategories), - BalanceSharedGroups: utils.ParseStringMap(apiAtr.BalanceSharedGroups), - BalanceTimingTags: utils.ParseStringMap(apiAtr.BalanceTimingTags), - Weight: apiAtr.Weight, - ActionsId: apiAtr.ActionsId, + ID: triggerTag, + UniqueID: atr.UniqueID, + ThresholdType: atr.ThresholdType, + ThresholdValue: atr.ThresholdValue, + Recurrent: atr.Recurrent, + MinSleep: minSleep, + ExpirationDate: expTime, + ActivationDate: actTime, + Balance: &BalancePointer{}, + Weight: atr.Weight, + ActionsId: atr.ActionsId, + } + if atr.BalanceId != "" && atr.BalanceId != utils.ANY { + atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + } + + if atr.BalanceType != "" && atr.BalanceType != utils.ANY { + atrs[idx].Balance.Type = utils.StringPointer(atr.BalanceType) + } + + if atr.BalanceWeight != "" && atr.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(atr.BalanceWeight, 64) + if err != nil { + return err + } + atrs[idx].Balance.Weight = utils.Float64Pointer(u) + } + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) + if err != nil { + return err + } + atrs[idx].Balance.ExpirationDate = utils.TimePointer(u) + } + if atr.BalanceRatingSubject != "" && atr.BalanceRatingSubject != utils.ANY { + atrs[idx].Balance.RatingSubject = utils.StringPointer(atr.BalanceRatingSubject) + } + + if atr.BalanceCategories != "" && atr.BalanceCategories != utils.ANY { + atrs[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceCategories)) + } + if atr.BalanceDirections != "" && atr.BalanceDirections != utils.ANY { + atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) + } + if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { + atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + } + if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { + atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) + } + if atr.BalanceTimingTags != "" && atr.BalanceTimingTags != utils.ANY { + atrs[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceTimingTags)) + } + if atr.BalanceBlocker != "" && atr.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceBlocker) + if err != nil { + return err + } + atrs[idx].Balance.Blocker = utils.BoolPointer(u) + } + if atr.BalanceDisabled != "" && atr.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceDisabled) + if err != nil { + return err + } + atrs[idx].Balance.Disabled = utils.BoolPointer(u) } } tpr.actionsTriggers[triggerTag] = atrs @@ -1134,7 +1333,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { return err } for tag, tpacts := range as { - enacts := make([]*Action, len(tpacts)) + acts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { // check filter field if len(tpact.Filter) > 0 { @@ -1142,30 +1341,74 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { return fmt.Errorf("error parsing action %s filter field: %v", tag, err) } } - enacts[idx] = &Action{ - Id: tag + strconv.Itoa(idx), - ActionType: tpact.Identifier, - BalanceType: tpact.BalanceType, + acts[idx] = &Action{ + Id: tag + strconv.Itoa(idx), + ActionType: tpact.Identifier, + //BalanceType: tpact.BalanceType, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &Balance{ - Id: tpact.BalanceId, - Value: tpact.Units, - Weight: tpact.BalanceWeight, - RatingSubject: tpact.RatingSubject, - Categories: utils.ParseStringMap(tpact.Categories), - Directions: utils.ParseStringMap(tpact.Directions), - DestinationIds: utils.ParseStringMap(tpact.DestinationIds), - SharedGroups: utils.ParseStringMap(tpact.SharedGroups), - TimingIDs: utils.ParseStringMap(tpact.TimingTags), - Blocker: tpact.BalanceBlocker, - Disabled: tpact.BalanceDisabled, - }, + Balance: &BalancePointer{}, } + if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { + acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + } + if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { + acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) + } + + if tpact.Units != "" && tpact.Units != utils.ANY { + u, err := strconv.ParseFloat(tpact.Units, 64) + if err != nil { + return err + } + acts[idx].Balance.Value = utils.Float64Pointer(u) + } + + if tpact.BalanceWeight != "" && tpact.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(tpact.BalanceWeight, 64) + if err != nil { + return err + } + acts[idx].Balance.Weight = utils.Float64Pointer(u) + } + if tpact.RatingSubject != "" && tpact.RatingSubject != utils.ANY { + acts[idx].Balance.RatingSubject = utils.StringPointer(tpact.RatingSubject) + } + + if tpact.Categories != "" && tpact.Categories != utils.ANY { + acts[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(tpact.Categories)) + } + if tpact.Directions != "" && tpact.Directions != utils.ANY { + acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) + } + if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { + acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + } + if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { + acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) + } + if tpact.TimingTags != "" && tpact.TimingTags != utils.ANY { + acts[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.TimingTags)) + } + if tpact.BalanceBlocker != "" && tpact.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceBlocker) + if err != nil { + return err + } + acts[idx].Balance.Blocker = utils.BoolPointer(u) + } + if tpact.BalanceDisabled != "" && tpact.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceDisabled) + if err != nil { + return err + } + acts[idx].Balance.Disabled = utils.BoolPointer(u) + } + } - tpr.actions[tag] = enacts + tpr.actions[tag] = acts } } } diff --git a/engine/units_counter.go b/engine/units_counter.go index 77aa21f31..f50532a2f 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -62,7 +62,9 @@ func (ucs UnitCounters) addUnits(amount float64, kind string, cc *CallCost, b *B bal.AddValue(amount) continue } - if uc.CounterType == utils.COUNTER_BALANCE && b != nil && b.MatchFilter(bal, true) { + bp := &BalancePointer{} + bp.LoadFromBalance(bal) + if uc.CounterType == utils.COUNTER_BALANCE && b != nil && b.MatchFilter(bp, true) { bal.AddValue(amount) continue } @@ -76,7 +78,7 @@ func (ucs UnitCounters) resetCounters(a *Action) { if uc == nil { // safeguard continue } - if a != nil && a.BalanceType != "" && a.BalanceType != uc.BalanceType { + if a != nil && a.Balance.Type != nil && a.Balance.GetType() != uc.BalanceType { continue } for _, b := range uc.Balances { diff --git a/engine/units_counter_test.go b/engine/units_counter_test.go index aeee72cdf..cd8331fb3 100644 --- a/engine/units_counter_test.go +++ b/engine/units_counter_test.go @@ -50,46 +50,58 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -114,46 +126,58 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 20, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(20), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -178,54 +202,68 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 20, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(20), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceDestinationIds: utils.NewStringMap("NAT"), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR22", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDestinationIds: utils.NewStringMap("RET"), - BalanceWeight: 10, + UniqueID: "TestTR22", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -250,54 +288,68 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 20, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(20), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceDestinationIds: utils.NewStringMap("NAT"), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR22", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDestinationIds: utils.NewStringMap("RET"), - BalanceWeight: 10, + UniqueID: "TestTR22", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -336,46 +388,58 @@ func TestUnitCountersResetCounterById(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -395,9 +459,9 @@ func TestUnitCountersResetCounterById(t *testing.T) { t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) } a.UnitCounters.resetCounters(&Action{ - BalanceType: utils.MONETARY, - Balance: &Balance{ - Id: "TestTR11", + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Id: utils.StringPointer("TestTR11"), }, }) if len(a.UnitCounters) != 4 || diff --git a/utils/coreutils.go b/utils/coreutils.go index 091e3fc07..4f857b07d 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -372,6 +372,10 @@ func StringMapPointer(sm StringMap) *StringMap { return &sm } +func TimePointer(t time.Time) *time.Time { + return &t +} + func ReflectFuncLocation(handler interface{}) (file string, line int) { f := runtime.FuncForPC(reflect.ValueOf(handler).Pointer()) entry := f.Entry() From cc96e6002346175661b5a76c6534074b0e894167 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 11 Feb 2016 13:40:37 +0200 Subject: [PATCH 060/199] refactoring --- engine/account.go | 5 +- engine/account_test.go | 56 +++++++------- engine/action.go | 24 +++--- engine/action_trigger.go | 5 +- engine/actions_test.go | 140 +++++++++++++++++------------------ engine/balances.go | 8 +- engine/balances_test.go | 18 ++--- engine/calldesc_test.go | 6 +- engine/loader_csv_test.go | 12 +-- engine/storage_test.go | 2 +- engine/tp_reader.go | 20 ++--- engine/units_counter.go | 10 ++- engine/units_counter_test.go | 66 ++++++++--------- utils/consts.go | 1 + 14 files changed, 195 insertions(+), 178 deletions(-) diff --git a/engine/account.go b/engine/account.go index 50650c546..7784208f3 100644 --- a/engine/account.go +++ b/engine/account.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "fmt" + "log" "time" "github.com/cgrates/cgrates/cache2go" @@ -587,6 +588,7 @@ func (acc *Account) ExecuteActionTriggers(a *Action) { // the next reset (see RESET_TRIGGERS action type) continue } + if !at.Match(a) { continue } @@ -673,9 +675,10 @@ func (acc *Account) InitCounters() { if strings.Contains(at.ThresholdType, "balance") { ct = utils.COUNTER_BALANCE } - + log.Print(at.Balance.GetType() + ct) uc, exists := ucTempMap[at.Balance.GetType()+ct] if !exists { + log.Print("HERE!") uc = &UnitCounter{ BalanceType: at.Balance.GetType(), CounterType: ct, diff --git a/engine/account_test.go b/engine/account_test.go index b603e135d..ae3dabd5e 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -814,7 +814,7 @@ func TestAccountdebitBalance(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &BalancePointer{ + newMb := &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.StringMap{"NEW": true}), @@ -833,7 +833,7 @@ func TestAccountdebitBalance(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &BalancePointer{ + newMb := &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), @@ -856,7 +856,7 @@ func TestAccountdebitBalanceExists(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Value: 15, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &BalancePointer{ + newMb := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), @@ -883,19 +883,19 @@ func TestAccountAddMinuteNil(t *testing.T) { } func TestAccountAddMinutBucketEmpty(t *testing.T) { - mb1 := &BalancePointer{ + mb1 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } - mb2 := &BalancePointer{ + mb2 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } - mb3 := &BalancePointer{ + mb3 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), DestinationIds: utils.StringMapPointer(utils.StringMap{"OTHER": true}), @@ -924,7 +924,7 @@ func TestAccountExecuteTriggeredActions(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.StringMap{utils.OUT: true}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { @@ -948,7 +948,7 @@ func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, nil, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { @@ -961,7 +961,7 @@ func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { Id: "TEST_UB_OREDER", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) @@ -976,11 +976,13 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{UniqueID: "day_trigger", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, - &ActionTrigger{UniqueID: "week_trigger", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{UniqueID: "day_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{UniqueID: "week_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, }, } + log.Print("==============") ub.InitCounters() + log.Print("==============") if len(ub.UnitCounters) != 1 || len(ub.UnitCounters[0].Balances) != 2 { log.Print("Error initializing counters: ", ub.UnitCounters[0].Balances[0]) } @@ -992,7 +994,7 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { } // we can reset them - resetCountersAction(ub, nil, &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("day_trigger")}}, nil) + resetCountersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("day_trigger")}}, nil) if ub.UnitCounters[0].Balances[0].Value != 0 || ub.UnitCounters[0].Balances[1].Value != 1 { t.Error("Error reseting both counters", ub.UnitCounters[0].Balances[0].Value, ub.UnitCounters[0].Balances[1].Value) @@ -1004,7 +1006,7 @@ func TestAccountExpActionTrigger(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100, ExpirationDate: time.Date(2015, time.November, 9, 9, 48, 0, 0, time.UTC)}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1022,7 +1024,7 @@ func TestAccountExpActionTriggerNotActivated(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1040,7 +1042,7 @@ func TestAccountExpActionTriggerExpired(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1598,7 +1600,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1607,7 +1609,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1616,7 +1618,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1625,7 +1627,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1634,7 +1636,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1643,7 +1645,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1673,7 +1675,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1682,7 +1684,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1691,7 +1693,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1700,7 +1702,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1709,7 +1711,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1718,7 +1720,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), diff --git a/engine/action.go b/engine/action.go index 94ab9ba1e..ce4ec1a09 100644 --- a/engine/action.go +++ b/engine/action.go @@ -45,10 +45,10 @@ type Action struct { Filter string ExpirationString string // must stay as string because it can have relative values like 1month Weight float64 - Balance *BalancePointer + Balance *BalanceFilter } -type BalancePointer struct { +type BalanceFilter struct { Uuid *string Id *string Type *string @@ -67,7 +67,7 @@ type BalancePointer struct { Blocker *bool } -func (bp *BalancePointer) CreateBalance() *Balance { +func (bp *BalanceFilter) CreateBalance() *Balance { b := &Balance{} if bp.Uuid != nil { b.Uuid = *bp.Uuid @@ -114,7 +114,7 @@ func (bp *BalancePointer) CreateBalance() *Balance { return b.Clone() } -func (bp *BalancePointer) LoadFromBalance(b *Balance) { +func (bp *BalanceFilter) LoadFromBalance(b *Balance) { if b.Uuid != "" { bp.Uuid = &b.Uuid } @@ -155,20 +155,27 @@ func (bp *BalancePointer) LoadFromBalance(b *Balance) { bp.Blocker = &b.Blocker } -func (bp *BalancePointer) GetType() string { +func (bp *BalanceFilter) GetType() string { if bp.Type == nil { return "" } return *bp.Type } -func (bp *BalancePointer) GetValue() float64 { +func (bp *BalanceFilter) GetValue() float64 { if bp.Value == nil { return 0.0 } return *bp.Value } +func (bp *BalanceFilter) SetValue(v float64) { + if bp.Value == nil { + bp.Value = new(float64) + } + *bp.Value = v +} + const ( LOG = "*log" RESET_TRIGGERS = "*reset_triggers" @@ -492,9 +499,8 @@ func resetCountersAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac } func genericMakeNegative(a *Action) { - b := a.Balance.CreateBalance() - if a.Balance != nil && b.GetValue() >= 0 { // only apply if not allready negative - b.SetValue(-b.GetValue()) + if a.Balance != nil && a.Balance.GetValue() >= 0 { // only apply if not allready negative + a.Balance.SetValue(-a.Balance.GetValue()) } } diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 0fe22916c..90e42099d 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -37,7 +37,7 @@ type ActionTrigger struct { ExpirationDate time.Time ActivationDate time.Time //BalanceType string // *monetary/*voice etc - Balance *BalancePointer + Balance *BalanceFilter Weight float64 ActionsId string MinQueuedItems int // Trigger actions only if this number is hit (stats only) @@ -76,9 +76,8 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro continue } } - if a.Balance == nil { - a.Balance = &BalancePointer{} + a.Balance = &BalanceFilter{} } if a.ExpirationString != "" { a.Balance.ExpirationDate = &time.Time{} diff --git a/engine/actions_test.go b/engine/actions_test.go index e0e4244db..9917759da 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -410,7 +410,7 @@ func TestActionPlanCheckForASAP(t *testing.T) { func TestActionPlanLogFunction(t *testing.T) { a := &Action{ ActionType: "*log", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("test"), Value: utils.Float64Pointer(1.1), }, @@ -427,7 +427,7 @@ func TestActionPlanLogFunction(t *testing.T) { func TestActionPlanFunctionNotAvailable(t *testing.T) { a := &Action{ ActionType: "VALID_FUNCTION_TYPE", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("test"), Value: utils.Float64Pointer(1.1), }, @@ -531,7 +531,7 @@ func TestActionPlansRemoveMember(t *testing.T) { func TestActionTriggerMatchNil(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, @@ -546,7 +546,7 @@ func TestActionTriggerMatchNil(t *testing.T) { func TestActionTriggerMatchAllBlank(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, @@ -561,14 +561,14 @@ func TestActionTriggerMatchAllBlank(t *testing.T) { func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: `{"BalanceDirections":"*out"}`} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: `{"BalanceDirections":"*out"}`} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -576,7 +576,7 @@ func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) { func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, @@ -591,14 +591,14 @@ func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { func TestActionTriggerMatchAllFull(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 2)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 2)} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -606,14 +606,14 @@ func TestActionTriggerMatchAllFull(t *testing.T) { func TestActionTriggerMatchSomeFalse(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_BALANCE, 2)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_BALANCE, 2)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -621,14 +621,14 @@ func TestActionTriggerMatchSomeFalse(t *testing.T) { func TestActionTriggerMatcBalanceFalse(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 3.0)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 3.0)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -636,14 +636,14 @@ func TestActionTriggerMatcBalanceFalse(t *testing.T) { func TestActionTriggerMatcAllFalse(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_EVENT_COUNTER, 3)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_EVENT_COUNTER, 3)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -651,7 +651,7 @@ func TestActionTriggerMatcAllFalse(t *testing.T) { func TestActionTriggerMatchAll(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), RatingSubject: utils.StringPointer("test1"), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), @@ -662,7 +662,7 @@ func TestActionTriggerMatchAll(t *testing.T) { }, ThresholdType: utils.TRIGGER_MAX_BALANCE, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"BalanceDirections":"*out", "ThresholdType":"%v", "ThresholdValue": %v, "DestinationIds": "%v", "BalanceWeight": %v, "BalanceRatingSubject": "%v", "BalanceSharedGroup": "%v"}`, utils.TRIGGER_MAX_BALANCE, 2, "NAT", 1.0, "test1", "test2")} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"BalanceDirections":"*out", "ThresholdType":"%v", "ThresholdValue": %v, "DestinationIds": "%v", "BalanceWeight": %v, "BalanceRatingSubject": "%v", "BalanceSharedGroup": "%v"}`, utils.TRIGGER_MAX_BALANCE, 2, "NAT", 1.0, "test1", "test2")} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -685,7 +685,7 @@ func TestActionResetTriggres(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { @@ -698,7 +698,7 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.BalanceMap[utils.MONETARY][0].GetValue() == 12 { @@ -711,9 +711,9 @@ func TestActionResetTriggresActionFilter(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - resetTriggersAction(ub, nil, &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}}, nil) + resetTriggersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}}, nil) if ub.ActionTriggers[0].Executed == false || ub.ActionTriggers[1].Executed == false { t.Error("Reset triggers action failed!") } @@ -724,7 +724,7 @@ func TestActionSetPostpaid(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } allowNegativeAction(ub, nil, nil, nil) if !ub.AllowNegative { @@ -738,7 +738,7 @@ func TestActionSetPrepaid(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } denyNegativeAction(ub, nil, nil, nil) if ub.AllowNegative { @@ -752,7 +752,7 @@ func TestActionResetPrepaid(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) if !ub.AllowNegative || @@ -770,7 +770,7 @@ func TestActionResetPostpaid(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) if ub.BalanceMap[utils.MONETARY].GetTotalValue() != 0 || @@ -786,9 +786,9 @@ func TestActionTopupResetCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 10 || @@ -805,7 +805,7 @@ func TestActionTopupValueFactor(t *testing.T) { BalanceMap: map[string]BalanceChain{}, } a := &Action{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), @@ -828,7 +828,7 @@ func TestActionTopupResetCreditId(t *testing.T) { }, }, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("TEST_B"), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("TEST_B"), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -847,7 +847,7 @@ func TestActionTopupResetCreditNoId(t *testing.T) { }, }, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 20 || @@ -863,9 +863,9 @@ func TestActionTopupResetMinutes(t *testing.T) { utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 5 || @@ -882,9 +882,9 @@ func TestActionTopupCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -900,9 +900,9 @@ func TestActionTopupMinutes(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 15 || @@ -919,9 +919,9 @@ func TestActionDebitCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 90 || @@ -937,9 +937,9 @@ func TestActionDebitMinutes(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE][0].GetValue() != 5 || @@ -961,7 +961,7 @@ func TestActionResetAllCounters(t *testing.T) { &Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT)}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, } ub.InitCounters() resetCountersAction(ub, nil, nil, nil) @@ -989,9 +989,9 @@ func TestActionResetCounterOnlyDefault(t *testing.T) { BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}} ub.InitCounters() resetCountersAction(ub, nil, a, nil) if !ub.AllowNegative || @@ -1020,9 +1020,9 @@ func TestActionResetCounterCredit(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}, &UnitCounter{BalanceType: utils.SMS, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}} resetCountersAction(ub, nil, a, nil) if !ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || @@ -1036,7 +1036,7 @@ func TestActionResetCounterCredit(t *testing.T) { func TestActionTriggerLogging(t *testing.T) { at := &ActionTrigger{ ID: "some_uuid", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), @@ -1108,7 +1108,7 @@ func TestActionPlanLogging(t *testing.T) { } func TestActionMakeNegative(t *testing.T) { - a := &Action{Balance: &BalancePointer{Value: utils.Float64Pointer(10)}} + a := &Action{Balance: &BalanceFilter{Value: utils.Float64Pointer(10)}} genericMakeNegative(a) if a.Balance.GetValue() > 0 { t.Error("Failed to make negative: ", a) @@ -1142,7 +1142,7 @@ func TestTopupAction(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minu") a := &Action{ ActionType: TOPUP, - Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1163,7 +1163,7 @@ func TestTopupActionLoaded(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minitsboy") a := &Action{ ActionType: TOPUP, - Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1190,7 +1190,7 @@ func TestActionCdrlogEmpty(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1212,11 +1212,11 @@ func TestActionCdrlogWithParams(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1239,11 +1239,11 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1324,11 +1324,11 @@ func TestActionTransactionFuncType(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ ActionType: "VALID_FUNCTION_TYPE", - Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer("test")}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer("test")}, }, }, } @@ -1360,11 +1360,11 @@ func TestActionTransactionBalanceType(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ ActionType: TOPUP, - Balance: &BalancePointer{Type: utils.StringPointer("test")}, + Balance: &BalanceFilter{Type: utils.StringPointer("test")}, }, }, } @@ -1396,14 +1396,14 @@ func TestActionWithExpireWithoutExpire(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(15), }, }, &Action{ ActionType: TOPUP, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(30), ExpirationDate: utils.TimePointer(time.Date(2025, time.November, 11, 22, 39, 0, 0, time.UTC)), @@ -1451,7 +1451,7 @@ func TestActionRemoveBalance(t *testing.T) { actions: []*Action{ &Action{ ActionType: REMOVE_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT", "RET")), }, @@ -1561,7 +1561,7 @@ func TestActionTransferMonetaryDefaultFilter(t *testing.T) { a := &Action{ ActionType: TRANSFER_MONETARY_DEFAULT, - Balance: &BalancePointer{Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1623,7 +1623,7 @@ func TestActionConditionalTopup(t *testing.T) { a := &Action{ ActionType: TOPUP, Filter: `{"Type":"*monetary","Value":1,"Weight":10}`, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(11), Weight: utils.Float64Pointer(30), @@ -1687,7 +1687,7 @@ func TestActionConditionalTopupNoMatch(t *testing.T) { a := &Action{ ActionType: TOPUP, Filter: `{"Type":"*monetary","Value":2,"Weight":10}`, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(11), Weight: utils.Float64Pointer(30), @@ -1751,7 +1751,7 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) { a := &Action{ ActionType: TOPUP, Filter: `{"Type":"*voice","Value":{"*gte":100}}`, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(11), Weight: utils.Float64Pointer(10), @@ -1852,7 +1852,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { a1 := &Action{ ActionType: "*enable_disable_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("*sms"), Weight: utils.Float64Pointer(10), Disabled: utils.BoolPointer(true), @@ -1862,7 +1862,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { a2 := &Action{ ActionType: "*enable_disable_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("*sms"), DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), Weight: utils.Float64Pointer(10), @@ -1873,7 +1873,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { a3 := &Action{ ActionType: "*enable_disable_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("*data"), RatingSubject: utils.StringPointer("for_v3hsillmilld500m_data_forfait"), Weight: utils.Float64Pointer(10), @@ -1884,7 +1884,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { a4 := &Action{ ActionType: "*enable_disable_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("*voice"), DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), Weight: utils.Float64Pointer(10), @@ -1956,7 +1956,7 @@ func TestActionSetBalance(t *testing.T) { a := &Action{ ActionType: SET_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Id: utils.StringPointer("m2"), Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(11), diff --git a/engine/balances.go b/engine/balances.go index 38dd1e8e4..80fc005d6 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -72,7 +72,7 @@ func (b *Balance) Equal(o *Balance) bool { b.Blocker == o.Blocker } -func (b *Balance) MatchFilter(o *BalancePointer, skipIds bool) bool { +func (b *Balance) MatchFilter(o *BalanceFilter, skipIds bool) bool { if o == nil { return true } @@ -84,7 +84,7 @@ func (b *Balance) MatchFilter(o *BalancePointer, skipIds bool) bool { } return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && (o.Weight == nil || b.Weight == *o.Weight) && - (o.Blocker != nil || b.Blocker == *o.Blocker) && + (o.Blocker == nil || b.Blocker == *o.Blocker) && (o.Disabled == nil || b.Disabled == *o.Disabled) && (o.DestinationIds == nil || b.DestinationIds.Includes(*o.DestinationIds)) && (o.Directions == nil || b.Directions.Includes(*o.Directions)) && @@ -94,7 +94,7 @@ func (b *Balance) MatchFilter(o *BalancePointer, skipIds bool) bool { (o.RatingSubject == nil || b.RatingSubject == *o.RatingSubject) } -func (b *Balance) HardMatchFilter(o *BalancePointer, skipIds bool) bool { +func (b *Balance) HardMatchFilter(o *BalanceFilter, skipIds bool) bool { if o == nil { return true } @@ -106,7 +106,7 @@ func (b *Balance) HardMatchFilter(o *BalancePointer, skipIds bool) bool { } return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && (o.Weight == nil || b.Weight == *o.Weight) && - (o.Blocker != nil || b.Blocker == *o.Blocker) && + (o.Blocker == nil || b.Blocker == *o.Blocker) && (o.Disabled == nil || b.Disabled == *o.Disabled) && (o.DestinationIds == nil || b.DestinationIds.Equal(*o.DestinationIds)) && (o.Directions == nil || b.Directions.Equal(*o.Directions)) && diff --git a/engine/balances_test.go b/engine/balances_test.go index 09c9dd17f..93a9f23db 100644 --- a/engine/balances_test.go +++ b/engine/balances_test.go @@ -90,7 +90,7 @@ func TestBalanceEqual(t *testing.T) { func TestBalanceMatchFilter(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &BalancePointer{Weight: utils.Float64Pointer(1), RatingSubject: nil, DestinationIds: nil} + mb2 := &BalanceFilter{Weight: utils.Float64Pointer(1), RatingSubject: nil, DestinationIds: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -98,7 +98,7 @@ func TestBalanceMatchFilter(t *testing.T) { func TestBalanceMatchFilterEmpty(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &BalancePointer{} + mb2 := &BalanceFilter{} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -106,7 +106,7 @@ func TestBalanceMatchFilterEmpty(t *testing.T) { func TestBalanceMatchFilterId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 2, precision: 2, RatingSubject: "2", DestinationIds: utils.NewStringMap("NAT")} - mb2 := &BalancePointer{Id: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} + mb2 := &BalanceFilter{Id: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -114,7 +114,7 @@ func TestBalanceMatchFilterId(t *testing.T) { func TestBalanceMatchFilterDiffId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &BalancePointer{Id: utils.StringPointer("T2"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} + mb2 := &BalanceFilter{Id: utils.StringPointer("T2"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} if mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v != %+v", mb1, mb2) } @@ -129,7 +129,7 @@ func TestBalanceClone(t *testing.T) { } func TestBalanceMatchActionTriggerId(t *testing.T) { - at := &ActionTrigger{Balance: &BalancePointer{Id: utils.StringPointer("test")}} + at := &ActionTrigger{Balance: &BalanceFilter{Id: utils.StringPointer("test")}} b := &Balance{Id: "test"} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -150,7 +150,7 @@ func TestBalanceMatchActionTriggerId(t *testing.T) { } func TestBalanceMatchActionTriggerDestination(t *testing.T) { - at := &ActionTrigger{Balance: &BalancePointer{DestinationIds: utils.StringMapPointer(utils.NewStringMap("test"))}} + at := &ActionTrigger{Balance: &BalanceFilter{DestinationIds: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{DestinationIds: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -171,7 +171,7 @@ func TestBalanceMatchActionTriggerDestination(t *testing.T) { } func TestBalanceMatchActionTriggerWeight(t *testing.T) { - at := &ActionTrigger{Balance: &BalancePointer{Weight: utils.Float64Pointer(1)}} + at := &ActionTrigger{Balance: &BalanceFilter{Weight: utils.Float64Pointer(1)}} b := &Balance{Weight: 1} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -192,7 +192,7 @@ func TestBalanceMatchActionTriggerWeight(t *testing.T) { } func TestBalanceMatchActionTriggerRatingSubject(t *testing.T) { - at := &ActionTrigger{Balance: &BalancePointer{RatingSubject: utils.StringPointer("test")}} + at := &ActionTrigger{Balance: &BalanceFilter{RatingSubject: utils.StringPointer("test")}} b := &Balance{RatingSubject: "test"} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -213,7 +213,7 @@ func TestBalanceMatchActionTriggerRatingSubject(t *testing.T) { } func TestBalanceMatchActionTriggerSharedGroup(t *testing.T) { - at := &ActionTrigger{Balance: &BalancePointer{SharedGroups: utils.StringMapPointer(utils.NewStringMap("test"))}} + at := &ActionTrigger{Balance: &BalanceFilter{SharedGroups: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{SharedGroups: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index e4d5cdc9e..cbac46036 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -41,12 +41,12 @@ func init() { func populateDB() { ats := []*Action{ - &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}}, - &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: utils.Float64Pointer(10), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, + &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}}, + &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: utils.Float64Pointer(10), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, } ats1 := []*Action{ - &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}, Weight: 10}, + &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}, Weight: 10}, &Action{ActionType: "*reset_account", Weight: 20}, } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 4cb27daf0..9c6073051 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -826,7 +826,7 @@ func TestLoadActions(t *testing.T) { ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Uuid: as1[0].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), @@ -844,7 +844,7 @@ func TestLoadActions(t *testing.T) { ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Uuid: as1[1].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), @@ -868,7 +868,7 @@ func TestLoadActions(t *testing.T) { ActionType: TOPUP, ExpirationString: UNLIMITED, Weight: 10, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: nil, @@ -891,7 +891,7 @@ func TestLoadActions(t *testing.T) { ActionType: CDRLOG, ExtraParameters: `{"Category":"^ddi","MediationRunId":"^did_run"}`, Weight: 10, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Uuid: as3[0].Balance.Uuid, Directions: nil, DestinationIds: nil, @@ -1047,7 +1047,7 @@ func TestLoadActionTriggers(t *testing.T) { UniqueID: "st0", ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ThresholdValue: 10, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), @@ -1068,7 +1068,7 @@ func TestLoadActionTriggers(t *testing.T) { UniqueID: "st1", ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 200, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY")), diff --git a/engine/storage_test.go b/engine/storage_test.go index 1a38f6bd2..2c1b9b808 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -317,7 +317,7 @@ func GetUB() *Account { at := &ActionTrigger{ ID: "some_uuid", ThresholdValue: 100.0, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 27075be55..bfa6615d6 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -521,7 +521,7 @@ func (tpr *TpReader) LoadActions() (err error) { ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) @@ -545,7 +545,7 @@ func (tpr *TpReader) LoadActions() (err error) { } acts[idx].Balance.Weight = utils.Float64Pointer(u) } - if tpact.ExpiryTime != "" && tpact.ExpiryTime != utils.ANY { + if tpact.ExpiryTime != "" && tpact.ExpiryTime != utils.ANY && tpact.ExpiryTime != utils.UNLIMITED { u, err := utils.ParseTimeDetectLayout(tpact.ExpiryTime, tpr.timezone) if err != nil { return err @@ -700,7 +700,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { MinSleep: minSleep, ExpirationDate: expirationDate, ActivationDate: activationDate, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, Weight: atr.Weight, ActionsId: atr.ActionsId, MinQueuedItems: atr.MinQueuedItems, @@ -720,7 +720,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { } atrs[idx].Balance.Weight = utils.Float64Pointer(u) } - if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY && atr.ExpirationDate != utils.UNLIMITED { u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) if err != nil { return err @@ -901,7 +901,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error MinSleep: minSleep, ExpirationDate: expTime, ActivationDate: actTime, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, Weight: atr.Weight, ActionsId: atr.ActionsId, } @@ -920,7 +920,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error } atrs[idx].Balance.Weight = utils.Float64Pointer(u) } - if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY && atr.ExpirationDate != utils.UNLIMITED { u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) if err != nil { return err @@ -1003,7 +1003,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) @@ -1238,7 +1238,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { MinSleep: minSleep, ExpirationDate: expTime, ActivationDate: actTime, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, Weight: atr.Weight, ActionsId: atr.ActionsId, } @@ -1257,7 +1257,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { } atrs[idx].Balance.Weight = utils.Float64Pointer(u) } - if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY && atr.ExpirationDate != utils.UNLIMITED { u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) if err != nil { return err @@ -1349,7 +1349,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) diff --git a/engine/units_counter.go b/engine/units_counter.go index f50532a2f..7854aa9ec 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -18,7 +18,11 @@ along with this program. If not, see package engine -import "github.com/cgrates/cgrates/utils" +import ( + "log" + + "github.com/cgrates/cgrates/utils" +) // Amount of a trafic of a certain type type UnitCounter struct { @@ -58,11 +62,13 @@ func (ucs UnitCounters) addUnits(amount float64, kind string, cc *CallCost, b *B uc.CounterType = utils.COUNTER_EVENT } for _, bal := range uc.Balances { + log.Print(b) if uc.CounterType == utils.COUNTER_EVENT && cc != nil && bal.MatchCCFilter(cc) { + log.Print("HERE") bal.AddValue(amount) continue } - bp := &BalancePointer{} + bp := &BalanceFilter{} bp.LoadFromBalance(bal) if uc.CounterType == utils.COUNTER_BALANCE && b != nil && b.MatchFilter(bp, true) { bal.AddValue(amount) diff --git a/engine/units_counter_test.go b/engine/units_counter_test.go index cd8331fb3..4ca1ec849 100644 --- a/engine/units_counter_test.go +++ b/engine/units_counter_test.go @@ -52,7 +52,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -61,7 +61,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -70,7 +70,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -79,7 +79,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -88,7 +88,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -97,7 +97,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -128,7 +128,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -137,7 +137,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20), @@ -146,7 +146,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -155,7 +155,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -164,7 +164,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -173,7 +173,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -204,7 +204,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -213,7 +213,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20), @@ -222,7 +222,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), @@ -232,7 +232,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR22", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(10), @@ -241,7 +241,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -250,7 +250,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -259,7 +259,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -290,7 +290,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -299,7 +299,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20), @@ -308,7 +308,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), @@ -318,7 +318,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR22", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(10), @@ -327,7 +327,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -336,7 +336,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -345,7 +345,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -390,7 +390,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -399,7 +399,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -408,7 +408,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -417,7 +417,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -426,7 +426,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -435,7 +435,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -459,7 +459,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) } a.UnitCounters.resetCounters(&Action{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("TestTR11"), }, diff --git a/utils/consts.go b/utils/consts.go index b1f0559f8..ec397c55d 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -89,6 +89,7 @@ const ( ROUNDING_MIDDLE = "*middle" ROUNDING_DOWN = "*down" ANY = "*any" + UNLIMITED = "*unlimited" ZERO = "*zero" ASAP = "*asap" USERS = "*users" From 9b8adb7c83b53c7fd2e087159e77185567c1935a Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 11 Feb 2016 20:42:05 +0100 Subject: [PATCH 061/199] Fix handling of account and user not found in diameter code --- agents/dmtagent.go | 2 +- agents/dmtagent_it_test.go | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index bdf119ba1..65933da0f 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -157,7 +157,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro } } - if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && maxUsage == 0 { // Not enough balance, RFC demands 4012 + if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && maxUsage == 0 && !unauthorizedResultCode { // Not enough balance, RFC demands 4012 if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "4012", false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 6d1dc1fe0..559043d95 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -500,6 +500,40 @@ func TestDmtAgentSendCCRSMSWrongAccount(t *testing.T) { } } +// cgr-console 'cost Category="call" Tenant="cgrates.org" Subject="1001" Destination="1004" TimeStart="2015-11-07T08:42:26Z" TimeEnd="2015-11-07T08:47:26Z"' +func TestDmtAgentSendCCRInitWrongAccount(t *testing.T) { + if !*testIntegration { + return + } + cdr := &engine.CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, + OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", + Tenant: "cgrates.org", Category: "call", Account: "non_existent", Subject: "non_existent", Destination: "1004", Supplier: "SUPPL1", + SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, + Usage: time.Duration(0) * time.Second, PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, + } + ccr := storedCdrToCCR(cdr, "UNIT_TEST", daCfg.DiameterAgentCfg().OriginRealm, daCfg.DiameterAgentCfg().VendorId, + daCfg.DiameterAgentCfg().ProductName, utils.DIAMETER_FIRMWARE_REVISION, daCfg.DiameterAgentCfg().DebitInterval, false) + m, err := ccr.AsDiameterMessage() + if err != nil { + t.Error(err) + } + if err := dmtClient.SendMessage(m); err != nil { + t.Error(err) + } + time.Sleep(time.Duration(100) * time.Millisecond) + msg := dmtClient.ReceivedMessage() // Discard the received message so we can test next one + if msg == nil { + t.Fatal("No message returned") + } + if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Result-Code") + } else if strResult := avpValAsString(avps[0]); strResult != "5030" { // Result-Code set in the template + t.Errorf("Expecting 5030, received: %s", strResult) + } +} + func TestDmtAgentCdrs(t *testing.T) { if !*testIntegration { return From 84f9af6043be9e6214225327996439dfbf41df63 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 11 Feb 2016 21:10:21 +0100 Subject: [PATCH 062/199] Diameter SMS - credit insufficient should produce 4012 --- agents/dmtagent.go | 19 +++++++++++++------ sessionmanager/smgeneric.go | 2 +- utils/consts.go | 1 + 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 65933da0f..904d105f8 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -136,7 +136,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro } } } - var unauthorizedResultCode bool + var populatedResultCode bool if err != nil { utils.Logger.Debug(fmt.Sprintf("Received error from rater: %+v", err)) if strings.HasSuffix(err.Error(), utils.ErrAccountNotFound.Error()) || strings.HasSuffix(err.Error(), utils.ErrUserNotFound.Error()) { // 5030 in case of AccountNotFound @@ -145,7 +145,14 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) return nil } - unauthorizedResultCode = true + populatedResultCode = true + } else if strings.HasSuffix(err.Error(), utils.ErrCreditInsufficient.Error()) { + if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "4012", + false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) + return nil + } + populatedResultCode = true } else { if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { @@ -157,21 +164,21 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro } } - if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && maxUsage == 0 && !unauthorizedResultCode { // Not enough balance, RFC demands 4012 + if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && maxUsage == 0 && !populatedResultCode { // Not enough balance, RFC demands 4012 if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "4012", false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) return nil } - unauthorizedResultCode = true - } else if !unauthorizedResultCode { + populatedResultCode = true + } else if !populatedResultCode { if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(diam.Success), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) return nil } } - if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && !unauthorizedResultCode { // For terminate or previously marked unauthorized, we don't add granted-service-unit AVP + if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && !populatedResultCode { // For terminate or previously marked unauthorized, we don't add granted-service-unit AVP if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Granted-Service-Unit", "CC-Time"}, strconv.FormatFloat(maxUsage, 'f', 0, 64), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Granted-Service-Unit, error: %s", ccr.diamMessage, err)) diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index 59ac17337..bdc79807b 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -220,7 +220,7 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu } sR.CallCosts = append(sR.CallCosts, cc) // Save it so we can revert on issues if ccDur := cc.GetDuration(); ccDur == 0 { - err = errors.New("INSUFFICIENT_FUNDS") + err = utils.ErrCreditInsufficient break } else if ccDur < maxDur { maxDur = ccDur diff --git a/utils/consts.go b/utils/consts.go index c7d6e2d74..b8ede4078 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -29,6 +29,7 @@ var ( ErrRatingPlanNotFound = errors.New("RATING_PLAN_NOT_FOUND") ErrAccountNotFound = errors.New("ACCOUNT_NOT_FOUND") ErrUserNotFound = errors.New("USER_NOT_FOUND") + ErrCreditInsufficient = errors.New("CREDIT_INSUFFICIENT") ) const ( From da4c1ca2da916c0ac0bac6b12d5e7d214e421458 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 15 Feb 2016 23:05:35 +0200 Subject: [PATCH 063/199] most engine tests passing ResetAccountPending --- engine/account.go | 118 +++++++++++-------- engine/account_test.go | 109 ++++++++--------- engine/action.go | 130 +-------------------- engine/action_plan.go | 5 +- engine/action_trigger.go | 32 ++++- engine/actions_test.go | 193 +++++++++++++++++++----------- engine/balance_filter.go | 220 +++++++++++++++++++++++++++++++++++ engine/balances.go | 46 +------- engine/balances_test.go | 14 +-- engine/callcost.go | 38 ++++++ engine/calldesc_test.go | 2 +- engine/loader_csv_test.go | 60 ++++++---- engine/model_helpers_test.go | 8 +- engine/storage_test.go | 7 +- engine/tp_reader.go | 24 ++-- engine/units_counter.go | 81 +++++++------ engine/units_counter_test.go | 160 ++++++++++++++----------- 17 files changed, 746 insertions(+), 501 deletions(-) create mode 100644 engine/balance_filter.go diff --git a/engine/account.go b/engine/account.go index 7784208f3..0130f1fb7 100644 --- a/engine/account.go +++ b/engine/account.go @@ -22,7 +22,6 @@ import ( "encoding/json" "errors" "fmt" - "log" "time" "github.com/cgrates/cgrates/cache2go" @@ -258,6 +257,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { return err } } + ub.InitCounters() ub.ExecuteActionTriggers(nil) return nil } @@ -588,22 +588,27 @@ func (acc *Account) ExecuteActionTriggers(a *Action) { // the next reset (see RESET_TRIGGERS action type) continue } - if !at.Match(a) { continue } if strings.Contains(at.ThresholdType, "counter") { - for _, uc := range acc.UnitCounters { - if uc.BalanceType == at.Balance.GetType() && - strings.Contains(at.ThresholdType, uc.CounterType[1:]) { - for _, b := range uc.Balances { - if strings.HasPrefix(at.ThresholdType, "*max") { - if b.MatchActionTrigger(at) && b.GetValue() >= at.ThresholdValue { - at.Execute(acc, nil) - } - } else { //MIN - if b.MatchActionTrigger(at) && b.GetValue() <= at.ThresholdValue { - at.Execute(acc, nil) + if (at.Balance.ID == nil || *at.Balance.ID != "") && at.UniqueID != "" { + at.Balance.ID = utils.StringPointer(at.UniqueID) + } + for _, counters := range acc.UnitCounters { + for _, uc := range counters { + if strings.Contains(at.ThresholdType, uc.CounterType[1:]) { + for _, c := range uc.Counters { + //log.Print("C: ", utils.ToJSON(c)) + if strings.HasPrefix(at.ThresholdType, "*max") { + if c.Filter.Equal(at.Balance) && c.Value >= at.ThresholdValue { + //log.Print("HERE") + at.Execute(acc, nil) + } + } else { //MIN + if c.Filter.Equal(at.Balance) && c.Value <= at.ThresholdValue { + at.Execute(acc, nil) + } } } } @@ -643,7 +648,7 @@ func (acc *Account) ResetActionTriggers(a *Action) { } at.Executed = false } - acc.ExecuteActionTriggers(a) + acc.ExecuteActionTriggers(a) //will trigger infinite loop when executed from ExecuteActionTriggers } // Sets/Unsets recurrent flag for action triggers @@ -665,9 +670,10 @@ func (acc *Account) countUnits(amount float64, kind string, cc *CallCost, b *Bal // Create counters for all triggered actions func (acc *Account) InitCounters() { oldUcs := acc.UnitCounters - acc.UnitCounters = nil + acc.UnitCounters = make(UnitCounters) ucTempMap := make(map[string]*UnitCounter) for _, at := range acc.ActionTriggers { + //log.Print("AT: ", utils.ToJSON(at)) if !strings.Contains(at.ThresholdType, "counter") { continue } @@ -675,28 +681,36 @@ func (acc *Account) InitCounters() { if strings.Contains(at.ThresholdType, "balance") { ct = utils.COUNTER_BALANCE } - log.Print(at.Balance.GetType() + ct) uc, exists := ucTempMap[at.Balance.GetType()+ct] + //log.Print("CT: ", at.Balance.GetType()+ct) if !exists { - log.Print("HERE!") uc = &UnitCounter{ - BalanceType: at.Balance.GetType(), CounterType: ct, } ucTempMap[at.Balance.GetType()+ct] = uc - uc.Balances = BalanceChain{} - acc.UnitCounters = append(acc.UnitCounters, uc) + uc.Counters = make(CounterFilters, 0) + acc.UnitCounters[at.Balance.GetType()] = append(acc.UnitCounters[at.Balance.GetType()], uc) } - b := at.Balance.CreateBalance() - if !uc.Balances.HasBalance(b) { - uc.Balances = append(uc.Balances, b) + c := &CounterFilter{Filter: at.Balance} + if (c.Filter.ID == nil || *c.Filter.ID == "") && at.UniqueID != "" { + c.Filter.ID = utils.StringPointer(at.UniqueID) + } + //log.Print("C: ", utils.ToJSON(c)) + if !uc.Counters.HasCounter(c) { + uc.Counters = append(uc.Counters, c) } } // copy old counter values - for _, uc := range acc.UnitCounters { - for _, oldUc := range oldUcs { - if uc.CopyCounterValues(oldUc) { - break + for key, counters := range acc.UnitCounters { + oldCounters, found := oldUcs[key] + if !found { + continue + } + for _, uc := range counters { + for _, oldUc := range oldCounters { + if uc.CopyCounterValues(oldUc) { + break + } } } } @@ -908,30 +922,32 @@ func (acc *Account) AsOldStructure() interface{} { AllowNegative: acc.AllowNegative, Disabled: acc.Disabled, } - for i, uc := range acc.UnitCounters { - if uc == nil { - continue - } - result.UnitCounters[i] = &UnitsCounter{ - BalanceType: uc.BalanceType, - Balances: make(BalanceChain, len(uc.Balances)), - } - if len(uc.Balances) > 0 { - result.UnitCounters[i].Direction = uc.Balances[0].Directions.String() - for j, b := range uc.Balances { - result.UnitCounters[i].Balances[j] = &Balance{ - Uuid: b.Uuid, - Id: b.Id, - Value: b.Value, - ExpirationDate: b.ExpirationDate, - Weight: b.Weight, - DestinationIds: b.DestinationIds.String(), - RatingSubject: b.RatingSubject, - Category: b.Categories.String(), - SharedGroup: b.SharedGroups.String(), - Timings: b.Timings, - TimingIDs: b.TimingIDs.String(), - Disabled: b.Disabled, + for balanceType, counters := range acc.UnitCounters { + for i, uc := range counters { + if uc == nil { + continue + } + result.UnitCounters[i] = &UnitsCounter{ + BalanceType: balanceType, + Balances: make(BalanceChain, len(uc.Counters)), + } + if len(uc.Counters) > 0 { + result.UnitCounters[i].Direction = (*uc.Counters[0].Filter.Directions).String() + for j, c := range uc.Counters { + result.UnitCounters[i].Balances[j] = &Balance{ + Uuid: c.Filter.GetUuid(), + Id: c.Filter.GetID(), + Value: c.Filter.GetValue(), + ExpirationDate: c.Filter.GetExpirationDate(), + Weight: c.Filter.GetWeight(), + DestinationIds: c.Filter.GetDestinationIDs().String(), + RatingSubject: c.Filter.GetRatingSubject(), + Category: c.Filter.GetCategories().String(), + SharedGroup: c.Filter.GetSharedGroups().String(), + Timings: c.Filter.Timings, + TimingIDs: c.Filter.GetTimingIDs().String(), + Disabled: c.Filter.GetDisabled(), + } } } } diff --git a/engine/account_test.go b/engine/account_test.go index ae3dabd5e..7def0cc0b 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -19,7 +19,6 @@ along with this program. If not, see package engine import ( - "log" "testing" "time" @@ -817,12 +816,12 @@ func TestAccountdebitBalance(t *testing.T) { newMb := &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), - DestinationIds: utils.StringMapPointer(utils.StringMap{"NEW": true}), + DestinationIDs: utils.StringMapPointer(utils.StringMap{"NEW": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } a := &Action{Balance: newMb} ub.debitBalanceAction(a, false) - if len(ub.BalanceMap[utils.VOICE]) != 3 || !ub.BalanceMap[utils.VOICE][2].DestinationIds.Equal(*newMb.DestinationIds) { + if len(ub.BalanceMap[utils.VOICE]) != 3 || !ub.BalanceMap[utils.VOICE][2].DestinationIds.Equal(*newMb.DestinationIDs) { t.Errorf("Error adding minute bucket! %d %+v %+v", len(ub.BalanceMap[utils.VOICE]), ub.BalanceMap[utils.VOICE][2], newMb) } } @@ -860,7 +859,7 @@ func TestAccountdebitBalanceExists(t *testing.T) { Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), - DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } a := &Action{Balance: newMb} @@ -886,19 +885,19 @@ func TestAccountAddMinutBucketEmpty(t *testing.T) { mb1 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), - DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } mb2 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), - DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } mb3 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), - DestinationIds: utils.StringMapPointer(utils.StringMap{"OTHER": true}), + DestinationIDs: utils.StringMapPointer(utils.StringMap{"OTHER": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } ub := &Account{} @@ -923,7 +922,7 @@ func TestAccountExecuteTriggeredActions(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.StringMap{utils.OUT: true}}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.StringMap{utils.OUT: true})}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) @@ -947,7 +946,7 @@ func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Filter: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, Value: 1.0}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, nil, nil) @@ -960,14 +959,14 @@ func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { ub := &Account{ Id: "TEST_UB_OREDER", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Type: utils.StringPointer(utils.MONETARY)}}}}}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) if len(ub.BalanceMap[utils.MONETARY]) != 1 || ub.BalanceMap[utils.MONETARY][0].GetValue() != 10 { - t.Errorf("Error executing triggered actions in order %v BAL: %+v", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.MONETARY][1]) + t.Errorf("Error executing triggered actions in order %v", ub.BalanceMap[utils.MONETARY][0].GetValue()) } } @@ -980,24 +979,22 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { &ActionTrigger{UniqueID: "week_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, }, } - log.Print("==============") ub.InitCounters() - log.Print("==============") - if len(ub.UnitCounters) != 1 || len(ub.UnitCounters[0].Balances) != 2 { - log.Print("Error initializing counters: ", ub.UnitCounters[0].Balances[0]) + if len(ub.UnitCounters) != 1 || len(ub.UnitCounters[utils.MONETARY][0].Counters) != 2 { + t.Error("Error initializing counters: ", ub.UnitCounters[utils.MONETARY][0].Counters[0]) } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if ub.UnitCounters[0].Balances[0].Value != 1 || - ub.UnitCounters[0].Balances[1].Value != 1 { - t.Error("Error incrementing both counters", ub.UnitCounters[0].Balances[0].Value, ub.UnitCounters[0].Balances[1].Value) + if ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 1 || + ub.UnitCounters[utils.MONETARY][0].Counters[1].Value != 1 { + t.Error("Error incrementing both counters", ub.UnitCounters[utils.MONETARY][0].Counters[0].Value, ub.UnitCounters[utils.MONETARY][0].Counters[1].Value) } // we can reset them - resetCountersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("day_trigger")}}, nil) - if ub.UnitCounters[0].Balances[0].Value != 0 || - ub.UnitCounters[0].Balances[1].Value != 1 { - t.Error("Error reseting both counters", ub.UnitCounters[0].Balances[0].Value, ub.UnitCounters[0].Balances[1].Value) + resetCountersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), ID: utils.StringPointer("day_trigger")}}, nil) + if ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 0 || + ub.UnitCounters[utils.MONETARY][0].Counters[1].Value != 1 { + t.Error("Error reseting both counters", ub.UnitCounters[utils.MONETARY][0].Counters[0].Value, ub.UnitCounters[utils.MONETARY][0].Counters[1].Value) } } @@ -1087,45 +1084,45 @@ func TestCleanExpired(t *testing.T) { } func TestAccountUnitCounting(t *testing.T) { - ub := &Account{UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 0}}}}} + ub := &Account{UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 0}}}}}} ub.countUnits(10, utils.MONETARY, &CallCost{}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 10 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 { t.Error("Error counting units") } ub.countUnits(10, utils.MONETARY, &CallCost{}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 20 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 20 { t.Error("Error counting units") } } func TestAccountUnitCountingOutbound(t *testing.T) { - ub := &Account{UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 0, Directions: utils.NewStringMap(utils.OUT)}}}}} + ub := &Account{UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 0, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}} ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 10 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 { t.Error("Error counting units") } ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 20 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 20 { t.Error("Error counting units") } ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 30 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 30 { t.Error("Error counting units") } } func TestAccountUnitCountingOutboundInbound(t *testing.T) { - ub := &Account{UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 0, Directions: utils.NewStringMap(utils.OUT)}}}}} + ub := &Account{UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 0, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}} ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 10 { - t.Errorf("Error counting units: %+v", ub.UnitCounters[0].Balances[0]) + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 { + t.Errorf("Error counting units: %+v", ub.UnitCounters[utils.MONETARY][0].Counters[0]) } ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 20 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 20 { t.Error("Error counting units") } ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.IN}, nil) - if len(ub.UnitCounters) != 1 || (ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 20) { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 20 { t.Error("Error counting units") } } @@ -1654,15 +1651,18 @@ func TestAccountInitCounters(t *testing.T) { }, } a.InitCounters() - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - len(a.UnitCounters[1].Balances) != 1 || - len(a.UnitCounters[2].Balances) != 1 || - len(a.UnitCounters[3].Balances) != 1 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + len(a.UnitCounters[utils.VOICE][0].Counters) != 1 || + len(a.UnitCounters[utils.VOICE][1].Counters) != 1 || + len(a.UnitCounters[utils.SMS][0].Counters) != 1 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, c := range uc.Counters { + t.Logf("B: %+v", c) + } } } t.Errorf("Error Initializing unit counters: %v", len(a.UnitCounters)) @@ -1730,15 +1730,18 @@ func TestAccountDoubleInitCounters(t *testing.T) { } a.InitCounters() a.InitCounters() - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - len(a.UnitCounters[1].Balances) != 1 || - len(a.UnitCounters[2].Balances) != 1 || - len(a.UnitCounters[3].Balances) != 1 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + len(a.UnitCounters[utils.VOICE][0].Counters) != 1 || + len(a.UnitCounters[utils.VOICE][1].Counters) != 1 || + len(a.UnitCounters[utils.SMS][0].Counters) != 1 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, c := range uc.Counters { + t.Logf("B: %+v", c) + } } } t.Errorf("Error Initializing unit counters: %v", len(a.UnitCounters)) diff --git a/engine/action.go b/engine/action.go index ce4ec1a09..7dde51af3 100644 --- a/engine/action.go +++ b/engine/action.go @@ -48,134 +48,6 @@ type Action struct { Balance *BalanceFilter } -type BalanceFilter struct { - Uuid *string - Id *string - Type *string - Value *float64 - Directions *utils.StringMap - ExpirationDate *time.Time - Weight *float64 - DestinationIds *utils.StringMap - RatingSubject *string - Categories *utils.StringMap - SharedGroups *utils.StringMap - TimingIDs *utils.StringMap - Timings []*RITiming - Disabled *bool - Factor *ValueFactor - Blocker *bool -} - -func (bp *BalanceFilter) CreateBalance() *Balance { - b := &Balance{} - if bp.Uuid != nil { - b.Uuid = *bp.Uuid - } - if bp.Id != nil { - b.Id = *bp.Id - } - if bp.Value != nil { - b.Value = *bp.Value - } - if bp.Directions != nil { - b.Directions = *bp.Directions - } - if bp.ExpirationDate != nil { - b.ExpirationDate = *bp.ExpirationDate - } - if bp.Weight != nil { - b.Weight = *bp.Weight - } - if bp.DestinationIds != nil { - b.DestinationIds = *bp.DestinationIds - } - if bp.RatingSubject != nil { - b.RatingSubject = *bp.RatingSubject - } - if bp.Categories != nil { - b.Categories = *bp.Categories - } - if bp.SharedGroups != nil { - b.SharedGroups = *bp.SharedGroups - } - if bp.TimingIDs != nil { - b.TimingIDs = *bp.TimingIDs - } - if bp.Disabled != nil { - b.Disabled = *bp.Disabled - } - if bp.Factor != nil { - b.Factor = *bp.Factor - } - if bp.Blocker != nil { - b.Blocker = *bp.Blocker - } - return b.Clone() -} - -func (bp *BalanceFilter) LoadFromBalance(b *Balance) { - if b.Uuid != "" { - bp.Uuid = &b.Uuid - } - if b.Id != "" { - bp.Id = &b.Id - } - if b.Value != 0 { - bp.Value = &b.Value - } - if len(b.Directions) != 0 { - bp.Directions = &b.Directions - } - if !b.ExpirationDate.IsZero() { - bp.ExpirationDate = &b.ExpirationDate - } - if b.Weight != 0 { - bp.Weight = &b.Weight - } - if len(b.DestinationIds) != 0 { - bp.DestinationIds = &b.DestinationIds - } - if b.RatingSubject != "" { - bp.RatingSubject = &b.RatingSubject - } - if len(b.Categories) != 0 { - bp.Categories = &b.Categories - } - if len(b.SharedGroups) != 0 { - bp.SharedGroups = &b.SharedGroups - } - if len(b.TimingIDs) != 0 { - bp.TimingIDs = &b.TimingIDs - } - if len(b.Factor) != 0 { - bp.Factor = &b.Factor - } - bp.Disabled = &b.Disabled - bp.Blocker = &b.Blocker -} - -func (bp *BalanceFilter) GetType() string { - if bp.Type == nil { - return "" - } - return *bp.Type -} - -func (bp *BalanceFilter) GetValue() float64 { - if bp.Value == nil { - return 0.0 - } - return *bp.Value -} - -func (bp *BalanceFilter) SetValue(v float64) { - if bp.Value == nil { - bp.Value = new(float64) - } - *bp.Value = v -} - const ( LOG = "*log" RESET_TRIGGERS = "*reset_triggers" @@ -301,7 +173,7 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) case "AccountID": parsedValue += rsrFld.ParseValue(acnt.Id) case "Directions": - parsedValue += rsrFld.ParseValue(action.Balance.Directions.String()) + parsedValue += rsrFld.ParseValue(b.Directions.String()) case utils.TENANT: parsedValue += rsrFld.ParseValue(dta.Tenant) case utils.ACCOUNT: diff --git a/engine/action_plan.go b/engine/action_plan.go index 0a9283d25..a9081b90a 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -307,7 +307,7 @@ func (at *ActionTiming) Execute() (err error) { continue // disabled acocunts are not removed from action plan //return 0, fmt.Errorf("Account %s is disabled", accID) } - if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.ExpirationDate.IsZero()) && parseErr == nil && !expDate.IsZero() { + if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.HasExpirationDate()) && parseErr == nil && !expDate.IsZero() { a.Balance.ExpirationDate = &time.Time{} *a.Balance.ExpirationDate = expDate } @@ -337,8 +337,7 @@ func (at *ActionTiming) Execute() (err error) { } if len(at.accountIDs) == 0 { // action timing executing without accounts for _, a := range aac { - - if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.ExpirationDate.IsZero()) && + if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.HasExpirationDate()) && parseErr == nil && !expDate.IsZero() { a.Balance.ExpirationDate = &time.Time{} *a.Balance.ExpirationDate = expDate diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 90e42099d..afca42939 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -19,6 +19,7 @@ along with this program. If not, see package engine import ( + "encoding/json" "fmt" "sort" "time" @@ -112,10 +113,37 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro // returns true if the field of the action timing are equeal to the non empty // fields of the action func (at *ActionTrigger) Match(a *Action) bool { - if a == nil { + if a == nil || a.Balance == nil { return true } - return at.Balance.CreateBalance().MatchFilter(a.Balance, false) + if a.Balance.Type != nil && a.Balance.GetType() != at.Balance.GetType() { + return false + } + var thresholdType bool + if a.ExtraParameters != "" { + t := struct { + GroupID string + UniqueID string + ThresholdType string + }{} + json.Unmarshal([]byte(a.ExtraParameters), &t) + // check Ids first + if t.GroupID != "" { + return at.ID == t.GroupID + } + if t.UniqueID != "" { + return at.UniqueID == t.UniqueID + } + thresholdType = t.ThresholdType == "" || at.ThresholdType == t.ThresholdType + } + + return thresholdType && at.Balance.CreateBalance().MatchFilter(a.Balance, false) +} + +func (at *ActionTrigger) CreateBalance() *Balance { + b := at.Balance.CreateBalance() + b.Id = at.UniqueID + return b } // makes a shallow copy of the receiver diff --git a/engine/actions_test.go b/engine/actions_test.go index 9917759da..af03aa6be 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -613,7 +613,7 @@ func TestActionTriggerMatchSomeFalse(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_BALANCE, 2)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%s"}`, utils.TRIGGER_MAX_BALANCE_COUNTER)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -628,7 +628,7 @@ func TestActionTriggerMatcBalanceFalse(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 3.0)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ExtraParameters: fmt.Sprintf(`{"GroupID":"%s", "ThresholdType":"%s"}`, "TEST", utils.TRIGGER_MAX_BALANCE)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -643,7 +643,7 @@ func TestActionTriggerMatcAllFalse(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_EVENT_COUNTER, 3)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"UniqueID":"ZIP", "GroupID":"%s", "ThresholdType":"%s"}`, "TEST", utils.TRIGGER_MAX_BALANCE)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -651,18 +651,28 @@ func TestActionTriggerMatcAllFalse(t *testing.T) { func TestActionTriggerMatchAll(t *testing.T) { at := &ActionTrigger{ + ID: "TEST", + UniqueID: "ZIP", + ThresholdType: "TT", Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), RatingSubject: utils.StringPointer("test1"), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: utils.Float64Pointer(2), Weight: utils.Float64Pointer(1.0), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), SharedGroups: utils.StringMapPointer(utils.NewStringMap("test2")), }, - ThresholdType: utils.TRIGGER_MAX_BALANCE, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"BalanceDirections":"*out", "ThresholdType":"%v", "ThresholdValue": %v, "DestinationIds": "%v", "BalanceWeight": %v, "BalanceRatingSubject": "%v", "BalanceSharedGroup": "%v"}`, utils.TRIGGER_MAX_BALANCE, 2, "NAT", 1.0, "test1", "test2")} + a := &Action{Balance: &BalanceFilter{ + Type: utils.StringPointer(utils.MONETARY), + RatingSubject: utils.StringPointer("test1"), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Value: utils.Float64Pointer(2), + Weight: utils.Float64Pointer(1.0), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), + SharedGroups: utils.StringMapPointer(utils.NewStringMap("test2")), + }, ExtraParameters: fmt.Sprintf(`{"UniqueID":"ZIP", "GroupID":"TEST", "ThresholdType":"TT"}`)} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -684,7 +694,7 @@ func TestActionResetTriggres(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) @@ -697,7 +707,7 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) @@ -710,7 +720,7 @@ func TestActionResetTriggresActionFilter(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}}, nil) @@ -723,7 +733,7 @@ func TestActionSetPostpaid(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } allowNegativeAction(ub, nil, nil, nil) @@ -737,7 +747,7 @@ func TestActionSetPrepaid(t *testing.T) { Id: "TEST_UB", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } denyNegativeAction(ub, nil, nil, nil) @@ -751,7 +761,7 @@ func TestActionResetPrepaid(t *testing.T) { Id: "TEST_UB", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) @@ -761,7 +771,7 @@ func TestActionResetPrepaid(t *testing.T) { ub.BalanceMap[utils.VOICE][0].GetValue() != 0 || ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { t.Log(ub.BalanceMap) - t.Error("Reset prepaid action failed!") + t.Error("Reset account action failed!") } } @@ -769,7 +779,7 @@ func TestActionResetPostpaid(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) @@ -777,7 +787,7 @@ func TestActionResetPostpaid(t *testing.T) { len(ub.UnitCounters) != 0 || ub.BalanceMap[utils.VOICE][0].GetValue() != 0 || ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { - t.Error("Reset postpaid action failed!") + t.Error("Reset account action failed!") } } @@ -785,17 +795,17 @@ func TestActionTopupResetCredit(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 10 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || // InitCounters finds no counters len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { - t.Errorf("Topup reset action failed: %+v", ub.BalanceMap[utils.MONETARY][0]) + t.Errorf("Topup reset action failed: %+s", utils.ToIJSON(ub)) } } @@ -828,7 +838,7 @@ func TestActionTopupResetCreditId(t *testing.T) { }, }, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("TEST_B"), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), ID: utils.StringPointer("TEST_B"), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -862,15 +872,15 @@ func TestActionTopupResetMinutes(t *testing.T) { BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 5 || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { t.Errorf("Topup reset minutes action failed: %+v", ub.BalanceMap[utils.VOICE][0]) @@ -881,14 +891,14 @@ func TestActionTopupCredit(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { t.Error("Topup action failed!", ub.BalanceMap[utils.MONETARY].GetTotalValue()) @@ -899,15 +909,15 @@ func TestActionTopupMinutes(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 15 || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { t.Error("Topup minutes action failed!", ub.BalanceMap[utils.VOICE]) @@ -918,17 +928,17 @@ func TestActionDebitCredit(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 90 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { - t.Error("Debit action failed!", ub.BalanceMap[utils.MONETARY].GetTotalValue()) + t.Error("Debit action failed!", utils.ToIJSON(ub)) } } @@ -936,15 +946,15 @@ func TestActionDebitMinutes(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE][0].GetValue() != 5 || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { t.Error("Debit minutes action failed!", ub.BalanceMap[utils.VOICE][0]) @@ -961,24 +971,24 @@ func TestActionResetAllCounters(t *testing.T) { &Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT)}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, } ub.InitCounters() resetCountersAction(ub, nil, nil, nil) if !ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || - len(ub.UnitCounters[0].Balances) != 1 || + len(ub.UnitCounters[utils.MONETARY][0].Counters) != 1 || len(ub.BalanceMap[utils.MONETARY]) != 1 || ub.ActionTriggers[0].Executed != true { - t.Errorf("Reset counters action failed: %+v %+v %+v", ub.UnitCounters, ub.UnitCounters[0], ub.UnitCounters[0].Balances[0]) + t.Errorf("Reset counters action failed: %+v %+v %+v", ub.UnitCounters, ub.UnitCounters[utils.MONETARY][0], ub.UnitCounters[utils.MONETARY][0].Counters[0]) } if len(ub.UnitCounters) < 1 { t.FailNow() } - mb := ub.UnitCounters[0].Balances[0] - if mb.Weight != 20 || mb.GetValue() != 0 || mb.DestinationIds["NAT"] == false { - t.Errorf("Balance cloned incorrectly: %+v", mb) + c := ub.UnitCounters[utils.MONETARY][0].Counters[0] + if c.Filter.GetWeight() != 20 || c.Value != 0 || c.Filter.GetDestinationIDs()["NAT"] == false { + t.Errorf("Balance cloned incorrectly: %+v", c) } } @@ -997,20 +1007,20 @@ func TestActionResetCounterOnlyDefault(t *testing.T) { if !ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || - len(ub.UnitCounters[0].Balances) != 1 || + len(ub.UnitCounters[utils.MONETARY][0].Counters) != 1 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true { - for _, b := range ub.UnitCounters[0].Balances { + for _, b := range ub.UnitCounters[utils.MONETARY][0].Counters { t.Logf("B: %+v", b) } t.Errorf("Reset counters action failed: %+v", ub.UnitCounters) } - if len(ub.UnitCounters) < 1 || len(ub.UnitCounters[0].Balances) < 1 { + if len(ub.UnitCounters) < 1 || len(ub.UnitCounters[utils.MONETARY][0].Counters) < 1 { t.FailNow() } - mb := ub.UnitCounters[0].Balances[0] - if mb.Weight != 0 || mb.GetValue() != 0 || len(mb.DestinationIds) != 0 { - t.Errorf("Balance cloned incorrectly: %+v!", mb) + c := ub.UnitCounters[utils.MONETARY][0].Counters[0] + if c.Filter.GetWeight() != 0 || c.Value != 0 || len(c.Filter.GetDestinationIDs()) != 0 { + t.Errorf("Balance cloned incorrectly: %+v!", c) } } @@ -1019,7 +1029,7 @@ func TestActionResetCounterCredit(t *testing.T) { Id: "TEST_UB", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}, &UnitCounter{BalanceType: utils.SMS, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}, utils.SMS: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}} @@ -1039,7 +1049,7 @@ func TestActionTriggerLogging(t *testing.T) { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), }, ThresholdValue: 100.0, Weight: 10.0, @@ -1142,7 +1152,7 @@ func TestTopupAction(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minu") a := &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1163,7 +1173,7 @@ func TestTopupActionLoaded(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minitsboy") a := &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1190,7 +1200,7 @@ func TestActionCdrlogEmpty(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1212,11 +1222,11 @@ func TestActionCdrlogWithParams(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1239,11 +1249,11 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1373,7 +1383,43 @@ func TestActionTransactionBalanceType(t *testing.T) { if err != nil || acc == nil { t.Error("Error getting account: ", acc, err) } - if acc.BalanceMap[utils.MONETARY][0].Value != 10 { + if acc.BalanceMap[utils.MONETARY][0].Value != 11.1 { + t.Errorf("Transaction didn't work: %v", acc.BalanceMap[utils.MONETARY][0].Value) + } +} + +func TestActionTransactionBalanceNotType(t *testing.T) { + err := accountingStorage.SetAccount(&Account{ + Id: "cgrates.org:trans", + BalanceMap: map[string]BalanceChain{ + utils.MONETARY: BalanceChain{&Balance{ + Value: 10, + }}, + }, + }) + if err != nil { + t.Error("Error setting account: ", err) + } + at := &ActionTiming{ + accountIDs: utils.StringMap{"cgrates.org:trans": true}, + Timing: &RateInterval{}, + actions: []*Action{ + &Action{ + ActionType: TOPUP, + Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.VOICE)}, + }, + &Action{ + ActionType: TOPUP, + Balance: &BalanceFilter{Type: utils.StringPointer("test")}, + }, + }, + } + err = at.Execute() + acc, err := accountingStorage.GetAccount("cgrates.org:trans") + if err != nil || acc == nil { + t.Error("Error getting account: ", acc, err) + } + if acc.BalanceMap[utils.MONETARY][0].Value != 10.0 { t.Errorf("Transaction didn't work: %v", acc.BalanceMap[utils.MONETARY][0].Value) } } @@ -1453,7 +1499,7 @@ func TestActionRemoveBalance(t *testing.T) { ActionType: REMOVE_BALANCE, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT", "RET")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT", "RET")), }, }, }, @@ -1850,43 +1896,58 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { } a1 := &Action{ - ActionType: "*enable_disable_balance", + ActionType: "*set_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*sms"), - Weight: utils.Float64Pointer(10), + ID: utils.StringPointer("for_v3hsillmilld500m_sms_ill"), Disabled: utils.BoolPointer(true), }, Weight: 9, } a2 := &Action{ - ActionType: "*enable_disable_balance", + ActionType: "*set_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*sms"), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), + ID: utils.StringPointer("for_v3hsillmilld500m_mms_ill"), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), Weight: utils.Float64Pointer(10), Disabled: utils.BoolPointer(true), }, Weight: 8, } a3 := &Action{ - ActionType: "*enable_disable_balance", + ActionType: "*set_balance", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &BalanceFilter{ + Type: utils.StringPointer("*sms"), + ID: utils.StringPointer("for_v3hsillmilld500m_sms_ill"), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), + Weight: utils.Float64Pointer(10), + Disabled: utils.BoolPointer(true), + }, + Weight: 8, + } + a4 := &Action{ + ActionType: "*set_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*data"), + Uuid: utils.StringPointer("fc927edb-1bd6-425e-a2a3-9fd8bafaa524"), RatingSubject: utils.StringPointer("for_v3hsillmilld500m_data_forfait"), Weight: utils.Float64Pointer(10), Disabled: utils.BoolPointer(true), }, Weight: 7, } - a4 := &Action{ - ActionType: "*enable_disable_balance", + a5 := &Action{ + ActionType: "*set_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*voice"), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), + ID: utils.StringPointer("for_v3hsillmilld500m_voice_3_h"), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), Weight: utils.Float64Pointer(10), Disabled: utils.BoolPointer(true), }, @@ -1895,7 +1956,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { at := &ActionTiming{ accountIDs: utils.StringMap{"cgrates.org:af": true}, - actions: Actions{a1, a2, a3, a4}, + actions: Actions{a1, a2, a3, a4, a5}, } at.Execute() @@ -1957,7 +2018,7 @@ func TestActionSetBalance(t *testing.T) { a := &Action{ ActionType: SET_BALANCE, Balance: &BalanceFilter{ - Id: utils.StringPointer("m2"), + ID: utils.StringPointer("m2"), Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(11), Weight: utils.Float64Pointer(10), diff --git a/engine/balance_filter.go b/engine/balance_filter.go new file mode 100644 index 000000000..e9f95e68c --- /dev/null +++ b/engine/balance_filter.go @@ -0,0 +1,220 @@ +package engine + +import ( + "reflect" + "time" + + "github.com/cgrates/cgrates/utils" +) + +type BalanceFilter struct { + Uuid *string + ID *string + Type *string + Value *float64 + Directions *utils.StringMap + ExpirationDate *time.Time + Weight *float64 + DestinationIDs *utils.StringMap + RatingSubject *string + Categories *utils.StringMap + SharedGroups *utils.StringMap + TimingIDs *utils.StringMap + Timings []*RITiming + Disabled *bool + Factor *ValueFactor + Blocker *bool +} + +func (bp *BalanceFilter) CreateBalance() *Balance { + b := &Balance{ + Uuid: bp.GetUuid(), + Id: bp.GetID(), + Value: bp.GetValue(), + Directions: bp.GetDirections(), + ExpirationDate: bp.GetExpirationDate(), + Weight: bp.GetWeight(), + DestinationIds: bp.GetDestinationIDs(), + RatingSubject: bp.GetRatingSubject(), + Categories: bp.GetCategories(), + SharedGroups: bp.GetSharedGroups(), + Timings: bp.Timings, + TimingIDs: bp.GetTimingIDs(), + Disabled: bp.GetDisabled(), + Factor: bp.GetFactor(), + Blocker: bp.GetBlocker(), + } + return b.Clone() +} + +func (bp *BalanceFilter) LoadFromBalance(b *Balance) *BalanceFilter { + if b.Uuid != "" { + bp.Uuid = &b.Uuid + } + if b.Id != "" { + bp.ID = &b.Id + } + if b.Value != 0 { + bp.Value = &b.Value + } + if len(b.Directions) != 0 { + bp.Directions = &b.Directions + } + if !b.ExpirationDate.IsZero() { + bp.ExpirationDate = &b.ExpirationDate + } + if b.Weight != 0 { + bp.Weight = &b.Weight + } + if len(b.DestinationIds) != 0 { + bp.DestinationIDs = &b.DestinationIds + } + if b.RatingSubject != "" { + bp.RatingSubject = &b.RatingSubject + } + if len(b.Categories) != 0 { + bp.Categories = &b.Categories + } + if len(b.SharedGroups) != 0 { + bp.SharedGroups = &b.SharedGroups + } + if len(b.TimingIDs) != 0 { + bp.TimingIDs = &b.TimingIDs + } + if len(b.Factor) != 0 { + bp.Factor = &b.Factor + } + if b.Disabled { + bp.Disabled = &b.Disabled + } + if b.Blocker { + bp.Blocker = &b.Blocker + } + return bp +} + +func (bp *BalanceFilter) Equal(o *BalanceFilter) bool { + if bp.ID != nil && o.ID != nil { + return *bp.ID == *o.ID + } + return reflect.DeepEqual(bp, o) +} + +func (bp *BalanceFilter) GetType() string { + if bp == nil || bp.Type == nil { + return "" + } + return *bp.Type +} + +func (bp *BalanceFilter) GetValue() float64 { + if bp == nil || bp.Value == nil { + return 0.0 + } + return *bp.Value +} + +func (bp *BalanceFilter) SetValue(v float64) { + if bp.Value == nil { + bp.Value = new(float64) + } + *bp.Value = v +} + +func (bp *BalanceFilter) GetUuid() string { + if bp == nil || bp.Uuid == nil { + return "" + } + return *bp.Uuid +} + +func (bp *BalanceFilter) GetID() string { + if bp == nil || bp.ID == nil { + return "" + } + return *bp.ID +} + +func (bp *BalanceFilter) GetDirections() utils.StringMap { + if bp == nil || bp.Directions == nil { + return utils.StringMap{} + } + return *bp.Directions +} + +func (bp *BalanceFilter) GetDestinationIDs() utils.StringMap { + if bp == nil || bp.DestinationIDs == nil { + return utils.StringMap{} + } + return *bp.DestinationIDs +} + +func (bp *BalanceFilter) GetCategories() utils.StringMap { + if bp == nil || bp.Categories == nil { + return utils.StringMap{} + } + return *bp.Categories +} + +func (bp *BalanceFilter) GetTimingIDs() utils.StringMap { + if bp == nil || bp.TimingIDs == nil { + return utils.StringMap{} + } + return *bp.TimingIDs +} + +func (bp *BalanceFilter) GetSharedGroups() utils.StringMap { + if bp == nil || bp.SharedGroups == nil { + return utils.StringMap{} + } + return *bp.SharedGroups +} + +func (bp *BalanceFilter) GetWeight() float64 { + if bp == nil || bp.Weight == nil { + return 0.0 + } + return *bp.Weight +} + +func (bp *BalanceFilter) GetRatingSubject() string { + if bp == nil || bp.RatingSubject == nil { + return "" + } + return *bp.RatingSubject +} + +func (bp *BalanceFilter) GetDisabled() bool { + if bp == nil || bp.Disabled == nil { + return false + } + return *bp.Disabled +} + +func (bp *BalanceFilter) GetBlocker() bool { + if bp == nil || bp.Blocker == nil { + return false + } + return *bp.Blocker +} + +func (bp *BalanceFilter) GetExpirationDate() time.Time { + if bp == nil || bp.ExpirationDate == nil { + return time.Time{} + } + return *bp.ExpirationDate +} + +func (bp *BalanceFilter) GetFactor() ValueFactor { + if bp == nil || bp.Factor == nil { + return ValueFactor{} + } + return *bp.Factor +} + +func (bp *BalanceFilter) HasExpirationDate() bool { + if bp.ExpirationDate == nil { + return true + } + return (*bp.ExpirationDate).IsZero() +} diff --git a/engine/balances.go b/engine/balances.go index 80fc005d6..633b17063 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -79,14 +79,14 @@ func (b *Balance) MatchFilter(o *BalanceFilter, skipIds bool) bool { if !skipIds && o.Uuid != nil && *o.Uuid != "" { return b.Uuid == *o.Uuid } - if !skipIds && o.Id != nil && *o.Id != "" { - return b.Id == *o.Id + if !skipIds && o.ID != nil && *o.ID != "" { + return b.Id == *o.ID } return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && (o.Weight == nil || b.Weight == *o.Weight) && (o.Blocker == nil || b.Blocker == *o.Blocker) && (o.Disabled == nil || b.Disabled == *o.Disabled) && - (o.DestinationIds == nil || b.DestinationIds.Includes(*o.DestinationIds)) && + (o.DestinationIDs == nil || b.DestinationIds.Includes(*o.DestinationIDs)) && (o.Directions == nil || b.Directions.Includes(*o.Directions)) && (o.Categories == nil || b.Categories.Includes(*o.Categories)) && (o.TimingIDs == nil || b.TimingIDs.Includes(*o.TimingIDs)) && @@ -101,14 +101,14 @@ func (b *Balance) HardMatchFilter(o *BalanceFilter, skipIds bool) bool { if !skipIds && o.Uuid != nil && *o.Uuid != "" { return b.Uuid == *o.Uuid } - if !skipIds && o.Id != nil && *o.Id != "" { - return b.Id == *o.Id + if !skipIds && o.ID != nil && *o.ID != "" { + return b.Id == *o.ID } return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && (o.Weight == nil || b.Weight == *o.Weight) && (o.Blocker == nil || b.Blocker == *o.Blocker) && (o.Disabled == nil || b.Disabled == *o.Disabled) && - (o.DestinationIds == nil || b.DestinationIds.Equal(*o.DestinationIds)) && + (o.DestinationIDs == nil || b.DestinationIds.Equal(*o.DestinationIDs)) && (o.Directions == nil || b.Directions.Equal(*o.Directions)) && (o.Categories == nil || b.Categories.Equal(*o.Categories)) && (o.TimingIDs == nil || b.TimingIDs.Equal(*o.TimingIDs)) && @@ -116,40 +116,6 @@ func (b *Balance) HardMatchFilter(o *BalanceFilter, skipIds bool) bool { (o.RatingSubject == nil || b.RatingSubject == *o.RatingSubject) } -func (b *Balance) MatchCCFilter(cc *CallCost) bool { - if len(b.Categories) > 0 && cc.Category != "" && b.Categories[cc.Category] == false { - return false - } - if len(b.Directions) > 0 && cc.Direction != "" && b.Directions[cc.Direction] == false { - return false - } - - // match destination ids - foundMatchingDestId := false - if len(b.DestinationIds) > 0 && cc.Destination != "" { - for _, p := range utils.SplitPrefix(cc.Destination, MIN_PREFIX_MATCH) { - if x, err := cache2go.Get(utils.DESTINATION_PREFIX + p); err == nil { - destIds := x.(map[interface{}]struct{}) - for filterDestId := range b.DestinationIds { - if _, ok := destIds[filterDestId]; ok { - foundMatchingDestId = true - break - } - } - } - if foundMatchingDestId { - break - } - } - } else { - foundMatchingDestId = true - } - if !foundMatchingDestId { - return false - } - return true -} - // the default balance has standard Id func (b *Balance) IsDefault() bool { return b.Id == utils.META_DEFAULT diff --git a/engine/balances_test.go b/engine/balances_test.go index 93a9f23db..ec95fad73 100644 --- a/engine/balances_test.go +++ b/engine/balances_test.go @@ -90,7 +90,7 @@ func TestBalanceEqual(t *testing.T) { func TestBalanceMatchFilter(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &BalanceFilter{Weight: utils.Float64Pointer(1), RatingSubject: nil, DestinationIds: nil} + mb2 := &BalanceFilter{Weight: utils.Float64Pointer(1), RatingSubject: nil, DestinationIDs: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -106,7 +106,7 @@ func TestBalanceMatchFilterEmpty(t *testing.T) { func TestBalanceMatchFilterId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 2, precision: 2, RatingSubject: "2", DestinationIds: utils.NewStringMap("NAT")} - mb2 := &BalanceFilter{Id: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} + mb2 := &BalanceFilter{ID: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIDs: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -114,7 +114,7 @@ func TestBalanceMatchFilterId(t *testing.T) { func TestBalanceMatchFilterDiffId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &BalanceFilter{Id: utils.StringPointer("T2"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} + mb2 := &BalanceFilter{ID: utils.StringPointer("T2"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIDs: nil} if mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v != %+v", mb1, mb2) } @@ -129,7 +129,7 @@ func TestBalanceClone(t *testing.T) { } func TestBalanceMatchActionTriggerId(t *testing.T) { - at := &ActionTrigger{Balance: &BalanceFilter{Id: utils.StringPointer("test")}} + at := &ActionTrigger{Balance: &BalanceFilter{ID: utils.StringPointer("test")}} b := &Balance{Id: "test"} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -143,14 +143,14 @@ func TestBalanceMatchActionTriggerId(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.Id = "test" - at.Balance.Id = nil + at.Balance.ID = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } } func TestBalanceMatchActionTriggerDestination(t *testing.T) { - at := &ActionTrigger{Balance: &BalanceFilter{DestinationIds: utils.StringMapPointer(utils.NewStringMap("test"))}} + at := &ActionTrigger{Balance: &BalanceFilter{DestinationIDs: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{DestinationIds: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -164,7 +164,7 @@ func TestBalanceMatchActionTriggerDestination(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.DestinationIds = utils.NewStringMap("test") - at.Balance.DestinationIds = nil + at.Balance.DestinationIDs = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } diff --git a/engine/callcost.go b/engine/callcost.go index 98984333c..85c8a3c4b 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -21,6 +21,7 @@ import ( "errors" "time" + "github.com/cgrates/cgrates/cache2go" "github.com/cgrates/cgrates/utils" ) @@ -180,3 +181,40 @@ func (cc *CallCost) updateCost() { } cc.Cost = cost } + +func (cc *CallCost) MatchCCFilter(bf *BalanceFilter) bool { + if bf == nil { + return true + } + if bf.Categories != nil && cc.Category != "" && (*bf.Categories)[cc.Category] == false { + return false + } + if bf.Directions != nil && cc.Direction != "" && (*bf.Directions)[cc.Direction] == false { + return false + } + + // match destination ids + foundMatchingDestID := false + if bf.DestinationIDs != nil && cc.Destination != "" { + for _, p := range utils.SplitPrefix(cc.Destination, MIN_PREFIX_MATCH) { + if x, err := cache2go.Get(utils.DESTINATION_PREFIX + p); err == nil { + destIds := x.(map[interface{}]struct{}) + for filterDestID := range *bf.DestinationIDs { + if _, ok := destIds[filterDestID]; ok { + foundMatchingDestID = true + break + } + } + } + if foundMatchingDestID { + break + } + } + } else { + foundMatchingDestID = true + } + if !foundMatchingDestID { + return false + } + return true +} diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index cbac46036..852b91a79 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -42,7 +42,7 @@ func init() { func populateDB() { ats := []*Action{ &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}}, - &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: utils.Float64Pointer(10), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, + &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, } ats1 := []*Action{ diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 9c6073051..69f090c8d 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -832,10 +832,12 @@ func TestLoadActions(t *testing.T) { Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: utils.Float64Pointer(10), Weight: utils.Float64Pointer(10), - DestinationIds: nil, + DestinationIDs: nil, TimingIDs: nil, SharedGroups: nil, Categories: nil, + Disabled: utils.BoolPointer(false), + Blocker: utils.BoolPointer(false), }, }, &Action{ @@ -851,10 +853,12 @@ func TestLoadActions(t *testing.T) { Value: utils.Float64Pointer(100), Weight: utils.Float64Pointer(10), RatingSubject: utils.StringPointer("test"), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), TimingIDs: nil, SharedGroups: nil, Categories: nil, + Disabled: utils.BoolPointer(false), + Blocker: utils.BoolPointer(false), }, }, } @@ -871,13 +875,15 @@ func TestLoadActions(t *testing.T) { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: nil, + DestinationIDs: nil, Uuid: as2[0].Balance.Uuid, Value: utils.Float64Pointer(100), Weight: utils.Float64Pointer(10), SharedGroups: utils.StringMapPointer(utils.NewStringMap("SG1")), TimingIDs: nil, Categories: nil, + Disabled: utils.BoolPointer(false), + Blocker: utils.BoolPointer(false), }, }, } @@ -894,11 +900,12 @@ func TestLoadActions(t *testing.T) { Balance: &BalanceFilter{ Uuid: as3[0].Balance.Uuid, Directions: nil, - DestinationIds: nil, + DestinationIDs: nil, TimingIDs: nil, Categories: nil, SharedGroups: nil, Blocker: utils.BoolPointer(false), + Disabled: utils.BoolPointer(false), }, }, } @@ -1048,19 +1055,22 @@ func TestLoadActionTriggers(t *testing.T) { ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ThresholdValue: 10, Balance: &BalanceFilter{ + ID: utils.StringPointer("st0"), Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), Categories: nil, TimingIDs: nil, SharedGroups: nil, + Disabled: nil, + Blocker: nil, }, Weight: 10, ActionsId: "SOME_1", Executed: false, } if !reflect.DeepEqual(atr, expected) { - t.Errorf("Error loading action trigger: %+v", atr) + t.Errorf("Error loading action trigger: %+v", utils.ToIJSON(atr.Balance)) } atr = csvr.actionsTriggers["STANDARD_TRIGGER"][1] expected = &ActionTrigger{ @@ -1071,7 +1081,7 @@ func TestLoadActionTriggers(t *testing.T) { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY")), Categories: nil, TimingIDs: nil, SharedGroups: nil, @@ -1093,18 +1103,22 @@ func TestLoadAccountActions(t *testing.T) { expected := &Account{ Id: "vdf:minitsboy", UnitCounters: UnitCounters{ - &UnitCounter{ - BalanceType: "*voice", - CounterType: "*event", - Balances: BalanceChain{ - &Balance{ - Id: "2c2ce3c9-d62b-49dc-82a5-2a17bdc6eb4e", - Value: 0, - Directions: utils.NewStringMap("*out"), - DestinationIds: utils.NewStringMap("GERMANY_O2"), - SharedGroups: nil, - Categories: nil, - TimingIDs: nil, + utils.VOICE: []*UnitCounter{ + &UnitCounter{ + CounterType: "*event", + Counters: CounterFilters{ + &CounterFilter{ + Value: 0, + Filter: &BalanceFilter{ + ID: utils.StringPointer("st0"), + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap("*out")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), + SharedGroups: nil, + Categories: nil, + TimingIDs: nil, + }, + }, }, }, }, @@ -1115,11 +1129,11 @@ func TestLoadAccountActions(t *testing.T) { for i, atr := range aa.ActionTriggers { csvr.actionsTriggers["STANDARD_TRIGGER"][i].ID = atr.ID } - for i, b := range aa.UnitCounters[0].Balances { - expected.UnitCounters[0].Balances[i].Id = b.Id + for i, b := range aa.UnitCounters[utils.VOICE][0].Counters { + expected.UnitCounters[utils.VOICE][0].Counters[i].Filter.ID = b.Filter.ID } - if !reflect.DeepEqual(aa.UnitCounters[0].Balances[0], expected.UnitCounters[0].Balances[0]) { - t.Errorf("Error loading account action: %+v \n %+v", aa.UnitCounters[0].Balances[0], expected.UnitCounters[0].Balances[0]) + if !reflect.DeepEqual(aa.UnitCounters[utils.VOICE][0].Counters[0], expected.UnitCounters[utils.VOICE][0].Counters[0]) { + t.Errorf("Error loading account action: %+v", utils.ToIJSON(aa.UnitCounters[utils.VOICE][0].Counters[0].Filter)) } // test that it does not overwrite balances existing, err := accountingStorage.GetAccount(aa.Id) diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 085012a37..94147a6a9 100644 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -265,8 +265,8 @@ func TestTPActionsAsExportSlice(t *testing.T) { }, } expectedSlc := [][]string{ - []string{"TEST_ACTIONS", "*topup_reset", "", "", "", "*monetary", utils.OUT, "call", "*any", "special1", "GROUP1", "*never", "", "5", "10", "false", "false", "10"}, - []string{"TEST_ACTIONS", "*http_post", "http://localhost/¶m1=value1", "", "", "", "", "", "", "", "", "", "", "0", "0", "false", "false", "20"}, + []string{"TEST_ACTIONS", "*topup_reset", "", "", "", "*monetary", utils.OUT, "call", "*any", "special1", "GROUP1", "*never", "", "5.0", "10.0", "", "", "10"}, + []string{"TEST_ACTIONS", "*http_post", "http://localhost/¶m1=value1", "", "", "", "", "", "", "", "", "", "", "0.0", "0.0", "", "", "20"}, } ms := APItoModelAction(tpActs) @@ -597,8 +597,8 @@ func TestTPActionPlanAsExportSlice(t *testing.T) { }, } expectedSlc := [][]string{ - []string{"STANDARD_TRIGGERS", "1", "*min_balance", "2", "false", "0", "", "", "b1", "*monetary", "*out", "call", "", "special1", "SHARED_1", "*never", "T1", "0", "false", "false", "0", "LOG_WARNING", "10"}, - []string{"STANDARD_TRIGGERS", "2", "*max_event_counter", "5", "false", "0", "", "", "b2", "*monetary", "*out", "call", "FS_USERS", "special1", "SHARED_1", "*never", "T1", "0", "false", "false", "0", "LOG_WARNING", "10"}, + []string{"STANDARD_TRIGGERS", "1", "*min_balance", "2", "false", "0", "", "", "b1", "*monetary", "*out", "call", "", "special1", "SHARED_1", "*never", "T1", "0.0", "false", "false", "0", "LOG_WARNING", "10"}, + []string{"STANDARD_TRIGGERS", "2", "*max_event_counter", "5", "false", "0", "", "", "b2", "*monetary", "*out", "call", "FS_USERS", "special1", "SHARED_1", "*never", "T1", "0.0", "false", "false", "0", "LOG_WARNING", "10"}, } ms := APItoModelActionTrigger(at) var slc [][]string diff --git a/engine/storage_test.go b/engine/storage_test.go index 2c1b9b808..ee8ed42cd 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -311,8 +311,7 @@ func TestStorageTask(t *testing.T) { func GetUB() *Account { uc := &UnitCounter{ - BalanceType: utils.SMS, - Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, + Counters: CounterFilters{&CounterFilter{Value: 1}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET"))}}}, } at := &ActionTrigger{ ID: "some_uuid", @@ -320,7 +319,7 @@ func GetUB() *Account { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), }, Weight: 10.0, ActionsId: "Commando", @@ -331,7 +330,7 @@ func GetUB() *Account { Id: "rif", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, utils.DATA: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{uc, uc}, + UnitCounters: UnitCounters{utils.SMS: []*UnitCounter{uc, uc}}, ActionTriggers: ActionTriggers{at, at, at}, } return ub diff --git a/engine/tp_reader.go b/engine/tp_reader.go index bfa6615d6..469165aba 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -524,7 +524,7 @@ func (tpr *TpReader) LoadActions() (err error) { Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { - acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + acts[idx].Balance.ID = utils.StringPointer(tpact.BalanceId) } if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) @@ -563,7 +563,7 @@ func (tpr *TpReader) LoadActions() (err error) { acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) } if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { - acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + acts[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) } if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) @@ -706,7 +706,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { MinQueuedItems: atr.MinQueuedItems, } if atr.BalanceId != "" && atr.BalanceId != utils.ANY { - atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + atrs[idx].Balance.ID = utils.StringPointer(atr.BalanceId) } if atr.BalanceType != "" && atr.BalanceType != utils.ANY { @@ -738,7 +738,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) } if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { - atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + atrs[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) } if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) @@ -906,7 +906,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error ActionsId: atr.ActionsId, } if atr.BalanceId != "" && atr.BalanceId != utils.ANY { - atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + atrs[idx].Balance.ID = utils.StringPointer(atr.BalanceId) } if atr.BalanceType != "" && atr.BalanceType != utils.ANY { @@ -938,7 +938,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) } if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { - atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + atrs[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) } if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) @@ -1006,7 +1006,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { - acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + acts[idx].Balance.ID = utils.StringPointer(tpact.BalanceId) } if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) @@ -1038,7 +1038,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) } if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { - acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + acts[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) } if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) @@ -1243,7 +1243,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { ActionsId: atr.ActionsId, } if atr.BalanceId != "" && atr.BalanceId != utils.ANY { - atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + atrs[idx].Balance.ID = utils.StringPointer(atr.BalanceId) } if atr.BalanceType != "" && atr.BalanceType != utils.ANY { @@ -1275,7 +1275,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) } if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { - atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + atrs[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) } if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) @@ -1352,7 +1352,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { - acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + acts[idx].Balance.ID = utils.StringPointer(tpact.BalanceId) } if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) @@ -1384,7 +1384,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) } if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { - acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + acts[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) } if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) diff --git a/engine/units_counter.go b/engine/units_counter.go index 7854aa9ec..b4e7f6696 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -18,29 +18,40 @@ along with this program. If not, see package engine -import ( - "log" - - "github.com/cgrates/cgrates/utils" -) +import "github.com/cgrates/cgrates/utils" // Amount of a trafic of a certain type type UnitCounter struct { - BalanceType string // *monetary/*voice/*sms/etc - CounterType string // *event or *balance - Balances BalanceChain // first balance is the general one (no destination) + CounterType string // *event or *balance + Counters CounterFilters // first balance is the general one (no destination) +} + +type CounterFilter struct { + Value float64 + Filter *BalanceFilter +} + +type CounterFilters []*CounterFilter + +func (cfs CounterFilters) HasCounter(cf *CounterFilter) bool { + for _, c := range cfs { + if c.Filter.Equal(cf.Filter) { + return true + } + } + return false } // Returns true if the counters were of the same type // Copies the value from old balances func (uc *UnitCounter) CopyCounterValues(oldUc *UnitCounter) bool { - if uc.BalanceType+uc.CounterType != oldUc.BalanceType+oldUc.CounterType { // type check + if uc.CounterType != oldUc.CounterType { // type check return false } - for _, b := range uc.Balances { - for _, oldB := range oldUc.Balances { - if b.Equal(oldB) { - b.Value = oldB.Value + for _, c := range uc.Counters { + for _, oldC := range oldUc.Counters { + if c.Filter.Equal(oldC.Filter) { + c.Value = oldC.Value break } } @@ -48,30 +59,28 @@ func (uc *UnitCounter) CopyCounterValues(oldUc *UnitCounter) bool { return true } -type UnitCounters []*UnitCounter +type UnitCounters map[string][]*UnitCounter func (ucs UnitCounters) addUnits(amount float64, kind string, cc *CallCost, b *Balance) { - for _, uc := range ucs { + counters, found := ucs[kind] + if !found { + return + } + for _, uc := range counters { if uc == nil { // safeguard continue } - if uc.BalanceType != kind { - continue - } if uc.CounterType == "" { uc.CounterType = utils.COUNTER_EVENT } - for _, bal := range uc.Balances { - log.Print(b) - if uc.CounterType == utils.COUNTER_EVENT && cc != nil && bal.MatchCCFilter(cc) { - log.Print("HERE") - bal.AddValue(amount) + for _, c := range uc.Counters { + if uc.CounterType == utils.COUNTER_EVENT && cc != nil && cc.MatchCCFilter(c.Filter) { + c.Value += amount continue } - bp := &BalanceFilter{} - bp.LoadFromBalance(bal) - if uc.CounterType == utils.COUNTER_BALANCE && b != nil && b.MatchFilter(bp, true) { - bal.AddValue(amount) + + if uc.CounterType == utils.COUNTER_BALANCE && b != nil && b.MatchFilter(c.Filter, true) { + c.Value += amount continue } } @@ -80,16 +89,18 @@ func (ucs UnitCounters) addUnits(amount float64, kind string, cc *CallCost, b *B } func (ucs UnitCounters) resetCounters(a *Action) { - for _, uc := range ucs { - if uc == nil { // safeguard + for key, counters := range ucs { + if a != nil && a.Balance.Type != nil && a.Balance.GetType() != key { continue } - if a != nil && a.Balance.Type != nil && a.Balance.GetType() != uc.BalanceType { - continue - } - for _, b := range uc.Balances { - if a == nil || a.Balance == nil || b.MatchFilter(a.Balance, false) { - b.Value = 0 + for _, c := range counters { + if c == nil { // safeguard + continue + } + for _, cf := range c.Counters { + if a == nil || a.Balance == nil || cf.Filter.Equal(a.Balance) { + cf.Value = 0 + } } } } diff --git a/engine/units_counter_test.go b/engine/units_counter_test.go index 4ca1ec849..4e80006c0 100644 --- a/engine/units_counter_test.go +++ b/engine/units_counter_test.go @@ -26,22 +26,20 @@ import ( func TestUnitsCounterAddBalance(t *testing.T) { uc := &UnitCounter{ - BalanceType: utils.SMS, - Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, + Counters: CounterFilters{&CounterFilter{Value: 1}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET"))}}}, } - UnitCounters{uc}.addUnits(20, utils.SMS, &CallCost{Destination: "test"}, nil) - if len(uc.Balances) != 3 { - t.Error("Error adding minute bucket: ", uc.Balances) + UnitCounters{utils.SMS: []*UnitCounter{uc}}.addUnits(20, utils.SMS, &CallCost{Destination: "test"}, nil) + if len(uc.Counters) != 3 { + t.Error("Error adding minute bucket: ", uc.Counters) } } func TestUnitsCounterAddBalanceExists(t *testing.T) { uc := &UnitCounter{ - BalanceType: utils.SMS, - Balances: BalanceChain{&Balance{Value: 1}, &Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, + Counters: CounterFilters{&CounterFilter{Value: 1}, &CounterFilter{Value: 10, Filter: &BalanceFilter{Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET"))}}}, } - UnitCounters{uc}.addUnits(5, utils.SMS, &CallCost{Destination: "0723"}, nil) - if len(uc.Balances) != 3 || uc.Balances[1].GetValue() != 15 { + UnitCounters{utils.SMS: []*UnitCounter{uc}}.addUnits(5, utils.SMS, &CallCost{Destination: "0723"}, nil) + if len(uc.Counters) != 3 || uc.Counters[1].Value != 15 { t.Error("Error adding minute bucket!") } } @@ -108,14 +106,17 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { a.InitCounters() a.UnitCounters.addUnits(10, utils.MONETARY, &CallCost{}, nil) - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - a.UnitCounters[0].Balances[0].Value != 10 || - a.UnitCounters[0].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + a.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 || + a.UnitCounters[utils.MONETARY][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) @@ -183,15 +184,17 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { } a.InitCounters() a.UnitCounters.addUnits(10, utils.MONETARY, nil, &Balance{Weight: 20, Directions: utils.NewStringMap(utils.OUT)}) - - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - a.UnitCounters[0].Balances[0].Value != 0 || - a.UnitCounters[0].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + a.UnitCounters[utils.MONETARY][0].Counters[0].Value != 0 || + a.UnitCounters[utils.MONETARY][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error adding unit counters: %v", len(a.UnitCounters)) @@ -225,7 +228,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(10), }, }, @@ -234,7 +237,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(10), }, }, @@ -270,14 +273,17 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { a.InitCounters() a.UnitCounters.addUnits(10, utils.VOICE, &CallCost{Destination: "0723045326"}, nil) - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[1].Balances) != 2 || - a.UnitCounters[1].Balances[0].Value != 10 || - a.UnitCounters[1].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.VOICE][0].Counters) != 2 || + a.UnitCounters[utils.VOICE][0].Counters[0].Value != 10 || + a.UnitCounters[utils.VOICE][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error adding unit counters: %v", len(a.UnitCounters)) @@ -311,7 +317,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(10), }, }, @@ -320,7 +326,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(10), }, }, @@ -356,28 +362,34 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { a.InitCounters() a.UnitCounters.addUnits(10, utils.VOICE, &CallCost{Destination: "0723045326"}, nil) - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[1].Balances) != 2 || - a.UnitCounters[1].Balances[0].Value != 10 || - a.UnitCounters[1].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.VOICE][0].Counters) != 2 || + a.UnitCounters[utils.VOICE][0].Counters[0].Value != 10 || + a.UnitCounters[utils.VOICE][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error adding unit counters: %v", len(a.UnitCounters)) } a.InitCounters() - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[1].Balances) != 2 || - a.UnitCounters[1].Balances[0].Value != 10 || - a.UnitCounters[1].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.VOICE][0].Counters) != 2 || + a.UnitCounters[utils.VOICE][0].Counters[0].Value != 10 || + a.UnitCounters[utils.VOICE][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error keeping counter values after init: %v", len(a.UnitCounters)) @@ -446,14 +458,17 @@ func TestUnitCountersResetCounterById(t *testing.T) { a.InitCounters() a.UnitCounters.addUnits(10, utils.MONETARY, &CallCost{}, nil) - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - a.UnitCounters[0].Balances[0].Value != 10 || - a.UnitCounters[0].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + a.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 || + a.UnitCounters[utils.MONETARY][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) @@ -461,17 +476,20 @@ func TestUnitCountersResetCounterById(t *testing.T) { a.UnitCounters.resetCounters(&Action{ Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), - Id: utils.StringPointer("TestTR11"), + ID: utils.StringPointer("TestTR11"), }, }) - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - a.UnitCounters[0].Balances[0].Value != 10 || - a.UnitCounters[0].Balances[1].Value != 0 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + a.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 || + a.UnitCounters[utils.MONETARY][0].Counters[1].Value != 0 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) From db787e3e355417117504e678c01bd12f78f89d70 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 16 Feb 2016 11:30:52 +0200 Subject: [PATCH 064/199] execute action trigger loop protection --- engine/account.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/engine/account.go b/engine/account.go index 0130f1fb7..b1cbb463b 100644 --- a/engine/account.go +++ b/engine/account.go @@ -36,12 +36,13 @@ Structure containing information about user's credit (minutes, cents, sms...).' This can represent a user or a shared group. */ type Account struct { - Id string - BalanceMap map[string]BalanceChain - UnitCounters UnitCounters - ActionTriggers ActionTriggers - AllowNegative bool - Disabled bool + Id string + BalanceMap map[string]BalanceChain + UnitCounters UnitCounters + ActionTriggers ActionTriggers + AllowNegative bool + Disabled bool + executingTriggers bool } // User's available minutes for the specified destination @@ -572,6 +573,14 @@ func (ub *Account) refundIncrement(increment *Increment, cd *CallDescriptor, cou // Scans the action trigers and execute the actions for which trigger is met func (acc *Account) ExecuteActionTriggers(a *Action) { + if acc.executingTriggers { + return + } + acc.executingTriggers = true + defer func() { + acc.executingTriggers = false + }() + acc.ActionTriggers.Sort() for _, at := range acc.ActionTriggers { // check is effective @@ -648,7 +657,7 @@ func (acc *Account) ResetActionTriggers(a *Action) { } at.Executed = false } - acc.ExecuteActionTriggers(a) //will trigger infinite loop when executed from ExecuteActionTriggers + acc.ExecuteActionTriggers(a) } // Sets/Unsets recurrent flag for action triggers From e30fe4f6ce0d518d70bc50ad883eebfd8c50ffef Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 16 Feb 2016 22:45:23 +0200 Subject: [PATCH 065/199] unit tests passing --- apier/v1/accounts.go | 343 +++++++++++++++++---------------- apier/v1/apier.go | 131 ++++--------- apier/v1/apier_local_test.go | 8 +- apier/v1/triggers.go | 24 +-- engine/account.go | 3 + engine/action.go | 2 +- general_tests/acntacts_test.go | 2 +- 7 files changed, 232 insertions(+), 281 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index bed4ce81c..d6273b517 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -353,21 +353,21 @@ func (self *ApierV1) GetAccount(attr *utils.AttrGetAccount, reply *interface{}) type AttrAddBalance struct { Tenant string Account string - BalanceUuid string - BalanceId string + BalanceUuid *string + BalanceId *string BalanceType string - Directions string + Directions *string Value float64 - ExpiryTime string - RatingSubject string - Categories string - DestinationIds string - TimingIds string - Weight float64 - SharedGroups string + ExpiryTime *string + RatingSubject *string + Categories *string + DestinationIds *string + TimingIds *string + Weight *float64 + SharedGroups *string Overwrite bool // When true it will reset if the balance is already there - Blocker bool - Disabled bool + Blocker *bool + Disabled *bool } func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { @@ -378,13 +378,17 @@ func (self *ApierV1) DebitBalance(attr *AttrAddBalance, reply *string) error { } func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *string) error { - if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { + if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType", "Value"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) - if err != nil { - *reply = err.Error() - return err + var expTime *time.Time + if attr.ExpiryTime != nil { + expTimeVal, err := utils.ParseTimeDetectLayout(*attr.ExpiryTime, self.Config.DefaultTimezone) + if err != nil { + *reply = err.Error() + return err + } + expTime = &expTimeVal } accID := utils.AccountKey(attr.Tenant, attr.Account) if _, err := self.AccountDb.GetAccount(accID); err != nil { @@ -403,27 +407,36 @@ func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *st if attr.Overwrite { aType += "_reset" // => *topup_reset/*debit_reset } - at.SetActions(engine.Actions{ - &engine.Action{ - ActionType: aType, - BalanceType: attr.BalanceType, - Balance: &engine.Balance{ - Uuid: attr.BalanceUuid, - Id: attr.BalanceId, - Value: attr.Value, - ExpirationDate: expTime, - RatingSubject: attr.RatingSubject, - Directions: utils.ParseStringMap(attr.Directions), - DestinationIds: utils.ParseStringMap(attr.DestinationIds), - Categories: utils.ParseStringMap(attr.Categories), - Weight: attr.Weight, - SharedGroups: utils.ParseStringMap(attr.SharedGroups), - TimingIDs: utils.ParseStringMap(attr.TimingIds), - Blocker: attr.Blocker, - Disabled: attr.Disabled, - }, + a := &engine.Action{ + ActionType: aType, + Balance: &engine.BalanceFilter{ + Uuid: attr.BalanceUuid, + ID: attr.BalanceId, + Type: utils.StringPointer(attr.BalanceType), + Value: utils.Float64Pointer(attr.Value), + ExpirationDate: expTime, + RatingSubject: attr.RatingSubject, + Weight: attr.Weight, + Blocker: attr.Blocker, + Disabled: attr.Disabled, }, - }) + } + if attr.Directions != nil { + a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions)) + } + if attr.DestinationIds != nil { + a.Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.DestinationIds)) + } + if attr.Categories != nil { + a.Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(*attr.Categories)) + } + if attr.SharedGroups != nil { + a.Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(*attr.SharedGroups)) + } + if attr.TimingIds != nil { + a.Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.TimingIds)) + } + at.SetActions(engine.Actions{a}) if err := at.Execute(); err != nil { *reply = err.Error() return err @@ -432,7 +445,95 @@ func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *st return nil } -func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) error { +type AttrSetBalance struct { + Tenant string + Account string + BalanceType string + BalanceUUID *string + BalanceID *string + Directions *string + Value *float64 + ExpiryTime *string + RatingSubject *string + Categories *string + DestinationIds *string + TimingIds *string + Weight *float64 + SharedGroups *string + Blocker *bool + Disabled *bool +} + +func (self *ApierV1) SetBalance(aType string, attr *AttrSetBalance, reply *string) error { + if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if (attr.BalanceID == nil || *attr.BalanceID == "") && + (attr.BalanceUUID == nil || *attr.BalanceUUID == "") { + return utils.NewErrMandatoryIeMissing("BalanceID", "or", "BalanceUUID") + } + var expTime *time.Time + if attr.ExpiryTime != nil { + expTimeVal, err := utils.ParseTimeDetectLayout(*attr.ExpiryTime, self.Config.DefaultTimezone) + if err != nil { + *reply = err.Error() + return err + } + expTime = &expTimeVal + } + accID := utils.AccountKey(attr.Tenant, attr.Account) + if _, err := self.AccountDb.GetAccount(accID); err != nil { + // create account if not exists + account := &engine.Account{ + Id: accID, + } + if err := self.AccountDb.SetAccount(account); err != nil { + *reply = err.Error() + return err + } + } + at := &engine.ActionTiming{} + at.SetAccountIDs(utils.StringMap{accID: true}) + + a := &engine.Action{ + ActionType: engine.SET_BALANCE, + Balance: &engine.BalanceFilter{ + Uuid: attr.BalanceUUID, + ID: attr.BalanceID, + Type: utils.StringPointer(attr.BalanceType), + Value: attr.Value, + ExpirationDate: expTime, + RatingSubject: attr.RatingSubject, + Weight: attr.Weight, + Blocker: attr.Blocker, + Disabled: attr.Disabled, + }, + } + if attr.Directions != nil { + a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions)) + } + if attr.DestinationIds != nil { + a.Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.DestinationIds)) + } + if attr.Categories != nil { + a.Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(*attr.Categories)) + } + if attr.SharedGroups != nil { + a.Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(*attr.SharedGroups)) + } + if attr.TimingIds != nil { + a.Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.TimingIds)) + } + at.SetActions(engine.Actions{a}) + if err := at.Execute(); err != nil { + *reply = err.Error() + return err + } + *reply = OK + return nil +} + +/*func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } @@ -475,16 +576,20 @@ func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) e } *reply = OK return nil -} +}*/ -func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error { +func (self *ApierV1) RemoveBalances(attr *AttrSetBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) - if err != nil { - *reply = err.Error() - return err + var expTime *time.Time + if attr.ExpiryTime != nil { + expTimeVal, err := utils.ParseTimeDetectLayout(*attr.ExpiryTime, self.Config.DefaultTimezone) + if err != nil { + *reply = err.Error() + return err + } + expTime = &expTimeVal } accID := utils.AccountKey(attr.Tenant, attr.Account) if _, err := self.AccountDb.GetAccount(accID); err != nil { @@ -493,138 +598,36 @@ func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error { at := &engine.ActionTiming{} at.SetAccountIDs(utils.StringMap{accID: true}) - at.SetActions(engine.Actions{ - &engine.Action{ - ActionType: engine.REMOVE_BALANCE, - BalanceType: attr.BalanceType, - Balance: &engine.Balance{ - Uuid: attr.BalanceUuid, - Id: attr.BalanceId, - Value: attr.Value, - ExpirationDate: expTime, - RatingSubject: attr.RatingSubject, - Directions: utils.ParseStringMap(attr.Directions), - DestinationIds: utils.ParseStringMap(attr.DestinationIds), - Categories: utils.ParseStringMap(attr.Categories), - Weight: attr.Weight, - SharedGroups: utils.ParseStringMap(attr.SharedGroups), - TimingIDs: utils.ParseStringMap(attr.TimingIds), - Blocker: attr.Blocker, - Disabled: attr.Disabled, - }, + a := &engine.Action{ + ActionType: engine.SET_BALANCE, + Balance: &engine.BalanceFilter{ + Uuid: attr.BalanceUUID, + ID: attr.BalanceID, + Type: utils.StringPointer(attr.BalanceType), + Value: attr.Value, + ExpirationDate: expTime, + RatingSubject: attr.RatingSubject, + Weight: attr.Weight, + Blocker: attr.Blocker, + Disabled: attr.Disabled, }, - }) - if err := at.Execute(); err != nil { - *reply = err.Error() - return err - } - *reply = OK - return nil -} - -type AttrSetBalance struct { - Tenant string - Account string - BalanceType string - BalanceUUID *string - BalanceID *string - Directions *[]string - Value *float64 - ExpiryTime *string - RatingSubject *string - Categories *[]string - DestinationIDs *[]string - SharedGroups *[]string - TimingIDs *[]string - Weight *float64 - Blocker *bool - Disabled *bool - expTime time.Time -} - -func (attr *AttrSetBalance) SetBalance(b *engine.Balance) { - if b == nil { - return } if attr.Directions != nil { - b.Directions = utils.StringMapFromSlice(*attr.Directions) + a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions)) } - if attr.Value != nil { - b.Value = *attr.Value - } - if attr.ExpiryTime != nil { - b.ExpirationDate = attr.expTime - } - if attr.RatingSubject != nil { - b.RatingSubject = *attr.RatingSubject + if attr.DestinationIds != nil { + a.Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.DestinationIds)) } if attr.Categories != nil { - b.Categories = utils.StringMapFromSlice(*attr.Categories) - } - if attr.DestinationIDs != nil { - b.DestinationIds = utils.StringMapFromSlice(*attr.DestinationIDs) + a.Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(*attr.Categories)) } if attr.SharedGroups != nil { - b.SharedGroups = utils.StringMapFromSlice(*attr.SharedGroups) + a.Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(*attr.SharedGroups)) } - if attr.TimingIDs != nil { - b.TimingIDs = utils.StringMapFromSlice(*attr.TimingIDs) + if attr.TimingIds != nil { + a.Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.TimingIds)) } - if attr.Weight != nil { - b.Weight = *attr.Weight - } - if attr.Blocker != nil { - b.Blocker = *attr.Blocker - } - if attr.Disabled != nil { - b.Disabled = *attr.Disabled - } - b.SetDirty() // Mark the balance as dirty since we have modified and it should be checked by action triggers -} - -/* // SetAccount api using action and action timing to set balance, -//to be uncommented when using pointers in action.balance -func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { - if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { - return utils.NewErrMandatoryIeMissing(missing...) - } - if attr.BalanceID == "" && attr.BalanceUUID == "" { - return utils.NewErrMandatoryIeMissing("BalanceID", "or", "BalanceUUID") - } - expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) - if err != nil { - *reply = err.Error() - return err - } - accID := utils.ConcatenatedKey(attr.Tenant, attr.Account) - if _, err := self.AccountDb.GetAccount(accID); err != nil { - return utils.ErrNotFound - } - - at := &engine.ActionTiming{} - at.SetAccountIDs(utils.StringMap{accID: true}) - - at.SetActions(engine.Actions{ - &engine.Action{ - ActionType: engine.SET_BALANCE, - BalanceType: attr.BalanceType, - Balance: &engine.Balance{ - Uuuid: attr.BalanceUUID, - ID: attr.BalanceID, - Value: attr.Value, - ExpirationDate: expTime, - RatingSubject: attr.RatingSubject, - Directions: utils.ParseStringMap(attr.Directions), - DestinationIDs: utils.ParseStringMap(attr.DestinationIDs), - Categories: utils.ParseStringMap(attr.Categories), - Weight: attr.Weight, - SharedGroups: utils.ParseStringMap(attr.SharedGroups), - TimingIDs: utils.ParseStringMap(attr.TimingIDs), - Blocker: true, - Disabled: attr.Disabled, - }, - }, - }) + at.SetActions(engine.Actions{a}) if err := at.Execute(); err != nil { *reply = err.Error() return err @@ -632,16 +635,13 @@ func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { *reply = OK return nil } -*/ +/* To be removed after the above one proves reliable func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - if (attr.BalanceID == nil || *attr.BalanceID == "") && - (attr.BalanceUUID == nil || *attr.BalanceUUID == "") { - return utils.NewErrMandatoryIeMissing("BalanceID", "or", "BalanceUUID") - } + var err error if attr.ExpiryTime != nil { attr.expTime, err = utils.ParseTimeDetectLayout(*attr.ExpiryTime, self.Config.DefaultTimezone) @@ -727,3 +727,4 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { *reply = utils.OK return nil } +*/ diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 2b4f4b576..2497ac17a 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -19,12 +19,12 @@ along with this program. If not, see package v1 import ( - "encoding/json" "errors" "fmt" "log" "os" "path" + "strconv" "strings" "time" @@ -502,23 +502,37 @@ func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) error } storeActions := make(engine.Actions, len(attrs.Actions)) for idx, apiAct := range attrs.Actions { + var units *float64 + if x, err := strconv.ParseFloat(apiAct.Units, 64); err == nil { + units = &x + } else { + return err + } + + var weight *float64 + if x, err := strconv.ParseFloat(apiAct.BalanceWeight, 64); err == nil { + weight = &x + } else { + return err + } + a := &engine.Action{ Id: utils.GenUUID(), ActionType: apiAct.Identifier, - BalanceType: apiAct.BalanceType, Weight: apiAct.Weight, ExpirationString: apiAct.ExpiryTime, ExtraParameters: apiAct.ExtraParameters, Filter: apiAct.Filter, - Balance: &engine.Balance{ - Uuid: utils.GenUUID(), - Id: apiAct.BalanceId, - Value: apiAct.Units, - Weight: apiAct.BalanceWeight, - Directions: utils.ParseStringMap(apiAct.Directions), - DestinationIds: utils.ParseStringMap(apiAct.DestinationIds), - RatingSubject: apiAct.RatingSubject, - SharedGroups: utils.ParseStringMap(apiAct.SharedGroups), + Balance: &engine.BalanceFilter{ // TODO: update this part + Uuid: utils.StringPointer(utils.GenUUID()), + ID: utils.StringPointer(apiAct.BalanceId), + Type: utils.StringPointer(apiAct.BalanceType), + Value: units, + Weight: weight, + Directions: utils.StringMapPointer(utils.ParseStringMap(apiAct.Directions)), + DestinationIDs: utils.StringMapPointer(utils.ParseStringMap(apiAct.DestinationIds)), + RatingSubject: utils.StringPointer(apiAct.RatingSubject), + SharedGroups: utils.StringMapPointer(utils.ParseStringMap(apiAct.SharedGroups)), }, } storeActions[idx] = a @@ -543,24 +557,25 @@ func (self *ApierV1) GetActions(actsId string, reply *[]*utils.TPAction) error { } for _, engAct := range engActs { act := &utils.TPAction{Identifier: engAct.ActionType, - BalanceType: engAct.BalanceType, ExpiryTime: engAct.ExpirationString, ExtraParameters: engAct.ExtraParameters, Filter: engAct.Filter, Weight: engAct.Weight, } - if engAct.Balance != nil { - act.Units = engAct.Balance.GetValue() - act.Directions = engAct.Balance.Directions.String() - act.DestinationIds = engAct.Balance.DestinationIds.String() - act.RatingSubject = engAct.Balance.RatingSubject - act.SharedGroups = engAct.Balance.SharedGroups.String() - act.BalanceWeight = engAct.Balance.Weight - act.TimingTags = engAct.Balance.TimingIDs.String() - act.BalanceId = engAct.Balance.Id - act.Categories = engAct.Balance.Categories.String() - act.BalanceBlocker = engAct.Balance.Blocker - act.BalanceDisabled = engAct.Balance.Disabled + bf := engAct.Balance + if bf != nil { + act.BalanceType = bf.GetType() + act.Units = strconv.FormatFloat(bf.GetValue(), 'f', -1, 64) + act.Directions = bf.GetDirections().String() + act.DestinationIds = bf.GetDestinationIDs().String() + act.RatingSubject = bf.GetRatingSubject() + act.SharedGroups = bf.GetSharedGroups().String() + act.BalanceWeight = strconv.FormatFloat(bf.GetWeight(), 'f', -1, 64) + act.TimingTags = bf.GetTimingIDs().String() + act.BalanceId = bf.GetID() + act.Categories = bf.GetCategories().String() + act.BalanceBlocker = strconv.FormatBool(bf.GetBlocker()) + act.BalanceDisabled = strconv.FormatBool(bf.GetDisabled()) } acts = append(acts, act) } @@ -663,74 +678,6 @@ func (self *ApierV1) GetActionPlan(attr AttrGetActionPlan, reply *[]*engine.Acti return nil } -type AttrResetTriggeredAction struct { - Id string - Tenant string - Account string - Directions string - BalanceType string - ThresholdType string - ThresholdValue float64 - DestinationId string - BalanceWeight float64 - BalanceRatingSubject string - BalanceSharedGroup string -} - -func (self *ApierV1) ResetTriggeredActions(attr AttrResetTriggeredAction, reply *string) error { - var a *engine.Action - if attr.Id != "" { - // we can identify the trigger by the id - a = &engine.Action{Id: attr.Id} - } else { - extraParameters, err := json.Marshal(struct { - ThresholdType string - ThresholdValue float64 - DestinationId string - BalanceWeight float64 - BalanceRatingSubject string - BalanceDirections string - BalanceSharedGroup string - }{ - attr.ThresholdType, - attr.ThresholdValue, - attr.DestinationId, - attr.BalanceWeight, - attr.Directions, - attr.BalanceRatingSubject, - attr.BalanceSharedGroup, - }) - if err != nil { - *reply = err.Error() - return err - } - a = &engine.Action{ - BalanceType: attr.BalanceType, - ExtraParameters: string(extraParameters), - } - } - accID := utils.AccountKey(attr.Tenant, attr.Account) - _, err := engine.Guardian.Guard(func() (interface{}, error) { - acc, err := self.AccountDb.GetAccount(accID) - if err != nil { - return 0, err - } - - acc.ResetActionTriggers(a) - - if err = self.AccountDb.SetAccount(acc); err != nil { - return 0, err - } - return 0, nil - }, 0, accID) - if err != nil { - *reply = err.Error() - return err - } - *reply = OK - return nil -} - // Process dependencies and load a specific AccountActions profile from storDb into dataDb. func (self *ApierV1) LoadAccountActions(attrs utils.TPAccountActions, reply *string) error { if len(attrs.TPid) == 0 { diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index efbd485de..dd7a445d9 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -488,8 +488,8 @@ func TestApierTPActions(t *testing.T) { } reply := "" act := &utils.TPActions{TPid: utils.TEST_SQL, ActionsId: "PREPAID_10", Actions: []*utils.TPAction{ - &utils.TPAction{Identifier: "*topup_reset", BalanceType: "*monetary", Directions: "*out", Units: 10, ExpiryTime: "*unlimited", - DestinationIds: "*any", BalanceWeight: 10, Weight: 10}, + &utils.TPAction{Identifier: "*topup_reset", BalanceType: "*monetary", Directions: "*out", Units: "10", ExpiryTime: "*unlimited", + DestinationIds: "*any", BalanceWeight: "10", Weight: 10}, }} actWarn := &utils.TPActions{TPid: utils.TEST_SQL, ActionsId: "WARN_VIA_HTTP", Actions: []*utils.TPAction{ &utils.TPAction{Identifier: "*call_url", ExtraParameters: "http://localhost:8000", Weight: 10}, @@ -955,7 +955,7 @@ func TestApierSetActions(t *testing.T) { if !*testLocal { return } - act1 := &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: 75.0, ExpiryTime: engine.UNLIMITED, Weight: 20.0} + act1 := &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75.0", ExpiryTime: engine.UNLIMITED, Weight: 20.0} attrs1 := &utils.AttrSetActions{ActionsId: "ACTS_1", Actions: []*utils.TPAction{act1}} reply1 := "" if err := rater.Call("ApierV1.SetActions", attrs1, &reply1); err != nil { @@ -974,7 +974,7 @@ func TestApierGetActions(t *testing.T) { return } expectActs := []*utils.TPAction{ - &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: 75.0, ExpiryTime: engine.UNLIMITED, Weight: 20.0}} + &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75.0", ExpiryTime: engine.UNLIMITED, Weight: 20.0}} var reply []*utils.TPAction if err := rater.Call("ApierV1.GetActions", "ACTS_1", &reply); err != nil { diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index d2470db4f..ab71455f3 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -238,44 +238,44 @@ func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, at.ActivationDate = actTime } if attr.BalanceId != nil { - at.BalanceId = *attr.BalanceId + at.Balance.ID = attr.BalanceId } if attr.BalanceType != nil { - at.BalanceType = *attr.BalanceType + at.Balance.Type = attr.BalanceType } if attr.BalanceDirections != nil { - at.BalanceDirections = utils.NewStringMap(*attr.BalanceDirections...) + at.Balance.Directions = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDirections...)) } if attr.BalanceDestinationIds != nil { - at.BalanceDestinationIds = utils.NewStringMap(*attr.BalanceDestinationIds...) + at.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...)) } if attr.BalanceWeight != nil { - at.BalanceWeight = *attr.BalanceWeight + at.Balance.Weight = attr.BalanceWeight } if attr.BalanceExpirationDate != nil { balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate) if err != nil { return 0, err } - at.BalanceExpirationDate = balanceExpTime + at.Balance.ExpirationDate = &balanceExpTime } if attr.BalanceTimingTags != nil { - at.BalanceTimingTags = utils.NewStringMap(*attr.BalanceTimingTags...) + at.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...)) } if attr.BalanceRatingSubject != nil { - at.BalanceRatingSubject = *attr.BalanceRatingSubject + at.Balance.RatingSubject = attr.BalanceRatingSubject } if attr.BalanceCategories != nil { - at.BalanceCategories = utils.NewStringMap(*attr.BalanceCategories...) + at.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...)) } if attr.BalanceSharedGroups != nil { - at.BalanceSharedGroups = utils.NewStringMap(*attr.BalanceSharedGroups...) + at.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...)) } if attr.BalanceBlocker != nil { - at.BalanceBlocker = *attr.BalanceBlocker + at.Balance.Blocker = attr.BalanceBlocker } if attr.BalanceDisabled != nil { - at.BalanceDisabled = *attr.BalanceDisabled + at.Balance.Disabled = attr.BalanceDisabled } if attr.MinQueuedItems != nil { at.MinQueuedItems = *attr.MinQueuedItems diff --git a/engine/account.go b/engine/account.go index b1cbb463b..4888b536d 100644 --- a/engine/account.go +++ b/engine/account.go @@ -723,6 +723,9 @@ func (acc *Account) InitCounters() { } } } + if len(acc.UnitCounters) == 0 { + acc.UnitCounters = nil // leave it nil if empty + } } func (acc *Account) CleanExpiredStuff() { diff --git a/engine/action.go b/engine/action.go index 7dde51af3..edd7cfda6 100644 --- a/engine/action.go +++ b/engine/action.go @@ -57,7 +57,7 @@ const ( DENY_NEGATIVE = "*deny_negative" RESET_ACCOUNT = "*reset_account" REMOVE_ACCOUNT = "*remove_account" - SET_BALANCE = "*set_balance" // not ready for production until switching to pointers + SET_BALANCE = "*set_balance" REMOVE_BALANCE = "*remove_balance" TOPUP_RESET = "*topup_reset" TOPUP = "*topup" diff --git a/general_tests/acntacts_test.go b/general_tests/acntacts_test.go index 524e0fb1e..50ec258cf 100644 --- a/general_tests/acntacts_test.go +++ b/general_tests/acntacts_test.go @@ -86,7 +86,7 @@ func TestAcntActsDisableAcnt(t *testing.T) { if acnt, err := acntDbAcntActs.GetAccount(acnt1Tag); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectAcnt, acnt) { - t.Errorf("Expecting: %+v, received: %+v", expectAcnt, acnt) + t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(expectAcnt), utils.ToJSON(acnt)) } } From 059c82fd471802a7c2876ff53f3a590b3e1bd2a1 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 17 Feb 2016 14:19:38 +0200 Subject: [PATCH 066/199] added last day of month function --- utils/coreutils.go | 15 +++++++++++++++ utils/utils_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/utils/coreutils.go b/utils/coreutils.go index 4f857b07d..b89804af0 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -518,3 +518,18 @@ func CastIfToString(iface interface{}) (strVal string, casts bool) { } return strVal, casts } + +func GetEndOfMonth(ref time.Time) time.Time { + if ref.IsZero() { + return time.Now() + } + year, month, _ := ref.Date() + if month == time.December { + year++ + month = time.January + } else { + month++ + } + eom := time.Date(year, month, 1, 0, 0, 0, 0, ref.Location()) + return eom.Add(-time.Second) +} diff --git a/utils/utils_test.go b/utils/utils_test.go index 6c66c8a0d..adca2dfad 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -600,3 +600,31 @@ func TestCastIfToString(t *testing.T) { t.Errorf("Received: %+v", sOut) } } + +func TestEndOfMonth(t *testing.T) { + eom := GetEndOfMonth(time.Date(2016, time.February, 5, 10, 1, 2, 3, time.UTC)) + expected := time.Date(2016, time.February, 29, 23, 59, 59, 0, time.UTC) + if !eom.Equal(expected) { + t.Errorf("Expected %v was %v", expected, eom) + } + eom = GetEndOfMonth(time.Date(2015, time.February, 5, 10, 1, 2, 3, time.UTC)) + expected = time.Date(2015, time.February, 28, 23, 59, 59, 0, time.UTC) + if !eom.Equal(expected) { + t.Errorf("Expected %v was %v", expected, eom) + } + eom = GetEndOfMonth(time.Date(2016, time.January, 31, 10, 1, 2, 3, time.UTC)) + expected = time.Date(2016, time.January, 31, 23, 59, 59, 0, time.UTC) + if !eom.Equal(expected) { + t.Errorf("Expected %v was %v", expected, eom) + } + eom = GetEndOfMonth(time.Date(2016, time.December, 31, 10, 1, 2, 3, time.UTC)) + expected = time.Date(2016, time.December, 31, 23, 59, 59, 0, time.UTC) + if !eom.Equal(expected) { + t.Errorf("Expected %v was %v", expected, eom) + } + eom = GetEndOfMonth(time.Date(2016, time.July, 31, 23, 59, 59, 0, time.UTC)) + expected = time.Date(2016, time.July, 31, 23, 59, 59, 0, time.UTC) + if !eom.Equal(expected) { + t.Errorf("Expected %v was %v", expected, eom) + } +} From 1707faf1e04e4b5988be58dc32512aca78fc3585 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 17 Feb 2016 14:21:19 +0200 Subject: [PATCH 067/199] added *end_month to ParseDate function --- utils/coreutils.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/coreutils.go b/utils/coreutils.go index b89804af0..3719f776f 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -193,6 +193,8 @@ func ParseDate(date string) (expDate time.Time, err error) { expDate = time.Now().AddDate(0, 1, 0) // add one month case date == "*yearly": expDate = time.Now().AddDate(1, 0, 0) // add one year + case date == "*end_month": + expDate = GetEndOfMonth(time.Now()) case strings.HasSuffix(date, "Z"): expDate, err = time.Parse(time.RFC3339, date) default: From 9fe980d42cc2065cd83fb3cdbe6351a2d257cfac Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 17 Feb 2016 17:16:07 +0200 Subject: [PATCH 068/199] renmaed *end_month to *month_end --- utils/coreutils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/coreutils.go b/utils/coreutils.go index 3719f776f..4268700cc 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -193,7 +193,7 @@ func ParseDate(date string) (expDate time.Time, err error) { expDate = time.Now().AddDate(0, 1, 0) // add one month case date == "*yearly": expDate = time.Now().AddDate(1, 0, 0) // add one year - case date == "*end_month": + case date == "*month_end": expDate = GetEndOfMonth(time.Now()) case strings.HasSuffix(date, "Z"): expDate, err = time.Parse(time.RFC3339, date) From f04d8082ba7f34c35b37e8d6e8eab42f631e0cd8 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Feb 2016 18:33:38 +0200 Subject: [PATCH 069/199] updated migrator --- cmd/cgr-loader/migrator_rc8.go | 255 +++++++++++++++++++++++---------- 1 file changed, 180 insertions(+), 75 deletions(-) diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index 25aebd1e0..fbc660684 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -227,51 +227,107 @@ func (mig MigratorRC8) migrateAccounts() error { // unit counters for _, oldUc := range oldAcc.UnitCounters { newUc := &engine.UnitCounter{ - BalanceType: oldUc.BalanceType, - Balances: make(engine.BalanceChain, len(oldUc.Balances)), + Counters: make(engine.CounterFilters, len(oldUc.Balances)), } for index, oldUcBal := range oldUc.Balances { - newUc.Balances[index] = &engine.Balance{ - Uuid: oldUcBal.Uuid, - Id: oldUcBal.Id, - Value: oldUcBal.Value, - Directions: utils.ParseStringMap(oldUc.Direction), - ExpirationDate: oldUcBal.ExpirationDate, - Weight: oldUcBal.Weight, - DestinationIds: utils.ParseStringMap(oldUcBal.DestinationIds), - RatingSubject: oldUcBal.RatingSubject, - Categories: utils.ParseStringMap(oldUcBal.Category), - SharedGroups: utils.ParseStringMap(oldUcBal.SharedGroup), - Timings: oldUcBal.Timings, - TimingIDs: utils.ParseStringMap(oldUcBal.TimingIDs), - Disabled: oldUcBal.Disabled, + bf := &engine.BalanceFilter{} + if oldUcBal.Uuid != "" { + bf.Uuid = utils.StringPointer(oldUcBal.Uuid) } + if oldUcBal.Id != "" { + bf.ID = utils.StringPointer(oldUcBal.Id) + } + if oldUc.BalanceType != "" { + bf.Type = utils.StringPointer(oldUc.BalanceType) + } + // the value was used for counter value + /*if oldUcBal.Value != 0 { + bf.Value = utils.Float64Pointer(oldUcBal.Value) + }*/ + if oldUc.Direction != "" { + bf.Directions = utils.StringMapPointer(utils.ParseStringMap(oldUc.Direction)) + } + if !oldUcBal.ExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldUcBal.ExpirationDate) + } + if oldUcBal.Weight != 0 { + bf.Weight = utils.Float64Pointer(oldUcBal.Weight) + } + if oldUcBal.DestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.DestinationIds)) + } + if oldUcBal.RatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldUcBal.RatingSubject) + } + if oldUcBal.Category != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.Category)) + } + if oldUcBal.SharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.SharedGroup)) + } + if oldUcBal.TimingIDs != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.TimingIDs)) + } + if oldUcBal.Disabled != false { + bf.Disabled = utils.BoolPointer(oldUcBal.Disabled) + } + bf.Timings = oldUcBal.Timings + cf := &engine.CounterFilter{ + Value: oldUcBal.Value, + Filter: bf, + } + newUc.Counters[index] = cf } } // action triggers for index, oldAtr := range oldAcc.ActionTriggers { - newAcc.ActionTriggers[index] = &engine.ActionTrigger{ - UniqueID: oldAtr.Id, - ThresholdType: oldAtr.ThresholdType, - ThresholdValue: oldAtr.ThresholdValue, - Recurrent: oldAtr.Recurrent, - MinSleep: oldAtr.MinSleep, - BalanceId: oldAtr.BalanceId, - BalanceType: oldAtr.BalanceType, - BalanceDirections: utils.ParseStringMap(oldAtr.BalanceDirection), - BalanceDestinationIds: utils.ParseStringMap(oldAtr.BalanceDestinationIds), - BalanceWeight: oldAtr.BalanceWeight, - BalanceExpirationDate: oldAtr.BalanceExpirationDate, - BalanceTimingTags: utils.ParseStringMap(oldAtr.BalanceTimingTags), - BalanceRatingSubject: oldAtr.BalanceRatingSubject, - BalanceCategories: utils.ParseStringMap(oldAtr.BalanceCategory), - BalanceSharedGroups: utils.ParseStringMap(oldAtr.BalanceSharedGroup), - BalanceDisabled: oldAtr.BalanceDisabled, - Weight: oldAtr.Weight, - ActionsId: oldAtr.ActionsId, - MinQueuedItems: oldAtr.MinQueuedItems, - Executed: oldAtr.Executed, + at := &engine.ActionTrigger{ + UniqueID: oldAtr.Id, + ThresholdType: oldAtr.ThresholdType, + ThresholdValue: oldAtr.ThresholdValue, + Recurrent: oldAtr.Recurrent, + MinSleep: oldAtr.MinSleep, + Weight: oldAtr.Weight, + ActionsId: oldAtr.ActionsId, + MinQueuedItems: oldAtr.MinQueuedItems, + Executed: oldAtr.Executed, } + bf := &engine.BalanceFilter{} + if oldAtr.BalanceId != "" { + bf.ID = utils.StringPointer(oldAtr.BalanceId) + } + if oldAtr.BalanceType != "" { + bf.Type = utils.StringPointer(oldAtr.BalanceType) + } + if oldAtr.BalanceRatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldAtr.BalanceRatingSubject) + } + if oldAtr.BalanceDirection != "" { + bf.Directions = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDirection)) + } + if oldAtr.BalanceDestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDestinationIds)) + } + if oldAtr.BalanceTimingTags != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceTimingTags)) + } + if oldAtr.BalanceCategory != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceCategory)) + } + if oldAtr.BalanceSharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceSharedGroup)) + } + if oldAtr.BalanceWeight != 0 { + bf.Weight = utils.Float64Pointer(oldAtr.BalanceWeight) + } + if oldAtr.BalanceDisabled != false { + bf.Disabled = utils.BoolPointer(oldAtr.BalanceDisabled) + } + if !oldAtr.BalanceExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldAtr.BalanceExpirationDate) + } + at.Balance = bf + newAcc.ActionTriggers[index] = at if newAcc.ActionTriggers[index].ThresholdType == "*min_counter" || newAcc.ActionTriggers[index].ThresholdType == "*max_counter" { newAcc.ActionTriggers[index].ThresholdType = strings.Replace(newAcc.ActionTriggers[index].ThresholdType, "_", "_event_", 1) @@ -322,28 +378,53 @@ func (mig MigratorRC8) migrateActionTriggers() error { } newAtrs := make(engine.ActionTriggers, len(oldAtrs)) for index, oldAtr := range oldAtrs { - newAtrs[index] = &engine.ActionTrigger{ - UniqueID: oldAtr.Id, - ThresholdType: oldAtr.ThresholdType, - ThresholdValue: oldAtr.ThresholdValue, - Recurrent: oldAtr.Recurrent, - MinSleep: oldAtr.MinSleep, - BalanceId: oldAtr.BalanceId, - BalanceType: oldAtr.BalanceType, - BalanceDirections: utils.ParseStringMap(oldAtr.BalanceDirection), - BalanceDestinationIds: utils.ParseStringMap(oldAtr.BalanceDestinationIds), - BalanceWeight: oldAtr.BalanceWeight, - BalanceExpirationDate: oldAtr.BalanceExpirationDate, - BalanceTimingTags: utils.ParseStringMap(oldAtr.BalanceTimingTags), - BalanceRatingSubject: oldAtr.BalanceRatingSubject, - BalanceCategories: utils.ParseStringMap(oldAtr.BalanceCategory), - BalanceSharedGroups: utils.ParseStringMap(oldAtr.BalanceSharedGroup), - BalanceDisabled: oldAtr.BalanceDisabled, - Weight: oldAtr.Weight, - ActionsId: oldAtr.ActionsId, - MinQueuedItems: oldAtr.MinQueuedItems, - Executed: oldAtr.Executed, + at := &engine.ActionTrigger{ + UniqueID: oldAtr.Id, + ThresholdType: oldAtr.ThresholdType, + ThresholdValue: oldAtr.ThresholdValue, + Recurrent: oldAtr.Recurrent, + MinSleep: oldAtr.MinSleep, + Weight: oldAtr.Weight, + ActionsId: oldAtr.ActionsId, + MinQueuedItems: oldAtr.MinQueuedItems, + Executed: oldAtr.Executed, } + bf := &engine.BalanceFilter{} + if oldAtr.BalanceId != "" { + bf.ID = utils.StringPointer(oldAtr.BalanceId) + } + if oldAtr.BalanceType != "" { + bf.Type = utils.StringPointer(oldAtr.BalanceType) + } + if oldAtr.BalanceRatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldAtr.BalanceRatingSubject) + } + if oldAtr.BalanceDirection != "" { + bf.Directions = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDirection)) + } + if oldAtr.BalanceDestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDestinationIds)) + } + if oldAtr.BalanceTimingTags != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceTimingTags)) + } + if oldAtr.BalanceCategory != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceCategory)) + } + if oldAtr.BalanceSharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceSharedGroup)) + } + if oldAtr.BalanceWeight != 0 { + bf.Weight = utils.Float64Pointer(oldAtr.BalanceWeight) + } + if oldAtr.BalanceDisabled != false { + bf.Disabled = utils.BoolPointer(oldAtr.BalanceDisabled) + } + if !oldAtr.BalanceExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldAtr.BalanceExpirationDate) + } + at.Balance = bf + newAtrs[index] = at if newAtrs[index].ThresholdType == "*min_counter" || newAtrs[index].ThresholdType == "*max_counter" { newAtrs[index].ThresholdType = strings.Replace(newAtrs[index].ThresholdType, "_", "_event_", 1) @@ -381,29 +462,53 @@ func (mig MigratorRC8) migrateActions() error { } newAcs := make(engine.Actions, len(oldAcs)) for index, oldAc := range oldAcs { - newAcs[index] = &engine.Action{ + a := &engine.Action{ Id: oldAc.Id, ActionType: oldAc.ActionType, - BalanceType: oldAc.BalanceType, ExtraParameters: oldAc.ExtraParameters, ExpirationString: oldAc.ExpirationString, Weight: oldAc.Weight, - Balance: &engine.Balance{ - Uuid: oldAc.Balance.Uuid, - Id: oldAc.Balance.Id, - Value: oldAc.Balance.Value, - Directions: utils.ParseStringMap(oldAc.Direction), - ExpirationDate: oldAc.Balance.ExpirationDate, - Weight: oldAc.Balance.Weight, - DestinationIds: utils.ParseStringMap(oldAc.Balance.DestinationIds), - RatingSubject: oldAc.Balance.RatingSubject, - Categories: utils.ParseStringMap(oldAc.Balance.Category), - SharedGroups: utils.ParseStringMap(oldAc.Balance.SharedGroup), - Timings: oldAc.Balance.Timings, - TimingIDs: utils.ParseStringMap(oldAc.Balance.TimingIDs), - Disabled: oldAc.Balance.Disabled, - }, + Balance: &engine.BalanceFilter{}, } + bf := a.Balance + if oldAc.Balance.Uuid != "" { + bf.Uuid = utils.StringPointer(oldAc.Balance.Uuid) + } + if oldAc.Balance.Id != "" { + bf.ID = utils.StringPointer(oldAc.Balance.Id) + } + if oldAc.BalanceType != "" { + bf.Type = utils.StringPointer(oldAc.BalanceType) + } + if oldAc.Balance.Value != 0 { + bf.Value = utils.Float64Pointer(oldAc.Balance.Value) + } + if oldAc.Balance.RatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldAc.Balance.RatingSubject) + } + if oldAc.Balance.DestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.DestinationIds)) + } + if oldAc.Balance.TimingIDs != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.TimingIDs)) + } + if oldAc.Balance.Category != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.Category)) + } + if oldAc.Balance.SharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.SharedGroup)) + } + if oldAc.Balance.Weight != 0 { + bf.Weight = utils.Float64Pointer(oldAc.Balance.Weight) + } + if oldAc.Balance.Disabled != false { + bf.Disabled = utils.BoolPointer(oldAc.Balance.Disabled) + } + if !oldAc.Balance.ExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldAc.Balance.ExpirationDate) + } + bf.Timings = oldAc.Balance.Timings + newAcs[index] = a } newAcsMap[key] = newAcs } From 5a2495e088cec0dcb387edb55111898245848334 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Feb 2016 18:44:39 +0200 Subject: [PATCH 070/199] preserve counters --- cmd/cgr-loader/migrator_rc8.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index fbc660684..1c9386a53 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -278,6 +278,7 @@ func (mig MigratorRC8) migrateAccounts() error { } newUc.Counters[index] = cf } + newAcc.UnitCounters[oldUc.BalanceType] = append(newAcc.UnitCounters[oldUc.BalanceType], newUc) } // action triggers for index, oldAtr := range oldAcc.ActionTriggers { From f958419d5bedb39165b604e7c4e34e3756f7f889 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Feb 2016 19:25:26 +0200 Subject: [PATCH 071/199] sql data types fixes --- apier/v1/apier.go | 20 +++++++++++-------- .../mysql/create_tariffplan_tables.sql | 14 ++++++------- .../postgres/create_tariffplan_tables.sql | 16 +++++++-------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 2497ac17a..9afe5cf63 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -503,17 +503,21 @@ func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) error storeActions := make(engine.Actions, len(attrs.Actions)) for idx, apiAct := range attrs.Actions { var units *float64 - if x, err := strconv.ParseFloat(apiAct.Units, 64); err == nil { - units = &x - } else { - return err + if apiAct.Units != "" { + if x, err := strconv.ParseFloat(apiAct.Units, 64); err == nil { + units = &x + } else { + return err + } } var weight *float64 - if x, err := strconv.ParseFloat(apiAct.BalanceWeight, 64); err == nil { - weight = &x - } else { - return err + if apiAct.BalanceWeight != "" { + if x, err := strconv.ParseFloat(apiAct.BalanceWeight, 64); err == nil { + weight = &x + } else { + return err + } } a := &engine.Action{ diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 0a640135a..b6240cdc5 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -152,16 +152,16 @@ CREATE TABLE `tp_actions` ( `balance_tag` varchar(64) NOT NULL, `balance_type` varchar(24) NOT NULL, `directions` varchar(8) NOT NULL, - `units` DECIMAL(20,4) NOT NULL, + `units` varchar(24) NOT NULL, `expiry_time` varchar(24) NOT NULL, `timing_tags` varchar(128) NOT NULL, `destination_tags` varchar(64) NOT NULL, `rating_subject` varchar(64) NOT NULL, `categories` varchar(32) NOT NULL, `shared_groups` varchar(64) NOT NULL, - `balance_weight` DECIMAL(8,2) NOT NULL, - `balance_blocker` BOOLEAN NOT NULL, - `balance_disabled` BOOLEAN NOT NULL, + `balance_weight` varchar(10) NOT NULL, + `balance_blocker` varchar(5) NOT NULL, + `balance_disabled` varchar(24) NOT NULL, `extra_parameters` varchar(256) NOT NULL, `filter` varchar(256) NOT NULL, `weight` DECIMAL(8,2) NOT NULL, @@ -214,9 +214,9 @@ CREATE TABLE `tp_action_triggers` ( `balance_shared_groups` varchar(64) NOT NULL, `balance_expiry_time` varchar(24) NOT NULL, `balance_timing_tags` varchar(128) NOT NULL, - `balance_weight` DECIMAL(8,2) NOT NULL, - `balance_blocker` BOOL NOT NULL, - `balance_disabled` BOOL NOT NULL, + `balance_weight` varchar(10) NOT NULL, + `balance_blocker` varchar(5) NOT NULL, + `balance_disabled` varchar(5) NOT NULL, `min_queued_items` int(11) NOT NULL, `actions_tag` varchar(64) NOT NULL, `weight` DECIMAL(8,2) NOT NULL, diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index e4d3aaee8..b5d8193c8 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -109,7 +109,7 @@ CREATE TABLE tp_rating_profiles ( activation_time VARCHAR(24) NOT NULL, rating_plan_tag VARCHAR(64) NOT NULL, fallback_subjects VARCHAR(64), - cdr_stat_queue_ids varchar(64), + cdr_stat_queue_ids VARCHAR(64), created_at TIMESTAMP, UNIQUE (tpid, loadid, tenant, category, direction, subject, activation_time) ); @@ -147,16 +147,16 @@ CREATE TABLE tp_actions ( balance_tag VARCHAR(64) NOT NULL, balance_type VARCHAR(24) NOT NULL, directions VARCHAR(8) NOT NULL, - units NUMERIC(20,4) NOT NULL, + units VARCHAR(10) NOT NULL, expiry_time VARCHAR(24) NOT NULL, timing_tags VARCHAR(128) NOT NULL, destination_tags VARCHAR(64) NOT NULL, rating_subject VARCHAR(64) NOT NULL, categories VARCHAR(32) NOT NULL, shared_groups VARCHAR(64) NOT NULL, - balance_weight NUMERIC(8,2) NOT NULL, - balance_blocker BOOLEAN NOT NULL, - balance_disabled BOOLEAN NOT NULL, + balance_weight VARCHAR(10) NOT NULL, + balance_blocker VARCHAR(5) NOT NULL, + balance_disabled VARCHAR(5) NOT NULL, extra_parameters VARCHAR(256) NOT NULL, filter VARCHAR(256) NOT NULL, weight NUMERIC(8,2) NOT NULL, @@ -209,9 +209,9 @@ CREATE TABLE tp_action_triggers ( balance_shared_groups VARCHAR(64) NOT NULL, balance_expiry_time VARCHAR(24) NOT NULL, balance_timing_tags VARCHAR(128) NOT NULL, - balance_weight NUMERIC(8,2) NOT NULL, - balance_blocker BOOL NOT NULL, - balance_disabled BOOL NOT NULL, + balance_weight VARCHAR(10) NOT NULL, + balance_blocker VARCHAR(5) NOT NULL, + balance_disabled VARCHAR(5) NOT NULL, min_queued_items INTEGER NOT NULL, actions_tag VARCHAR(64) NOT NULL, weight NUMERIC(8,2) NOT NULL, From 1511fbe808bf56d78c9eade4f6b84add9f81dd4e Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Feb 2016 19:34:35 +0200 Subject: [PATCH 072/199] small fixes --- apier/v1/apier_local_test.go | 2 +- engine/actions_local_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index dd7a445d9..04d847cb9 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -980,7 +980,7 @@ func TestApierGetActions(t *testing.T) { if err := rater.Call("ApierV1.GetActions", "ACTS_1", &reply); err != nil { t.Error("Got error on ApierV1.GetActions: ", err.Error()) } else if !reflect.DeepEqual(expectActs, reply) { - t.Errorf("Expected: %v, received: %v", expectActs, reply) + t.Errorf("Expected: %v, received: %v", utils.ToJSON(expectActs), utils.ToJSON(reply)) } } diff --git a/engine/actions_local_test.go b/engine/actions_local_test.go index bdc3b8b79..b4aa17869 100644 --- a/engine/actions_local_test.go +++ b/engine/actions_local_test.go @@ -95,7 +95,7 @@ func TestActionsLocalSetCdrlogDebit(t *testing.T) { t.Errorf("Calling ApierV1.SetAccount received: %s", reply) } attrsAA := &utils.AttrSetActions{ActionsId: "ACTS_1", Actions: []*utils.TPAction{ - &utils.TPAction{Identifier: DEBIT, BalanceType: utils.MONETARY, Units: "5.0", ExpiryTime: UNLIMITED, Weight: 20.0}, + &utils.TPAction{Identifier: DEBIT, BalanceType: utils.MONETARY, Units: "5", ExpiryTime: UNLIMITED, Weight: 20.0}, &utils.TPAction{Identifier: CDRLOG}, }} if err := actsLclRpc.Call("ApierV1.SetActions", attrsAA, &reply); err != nil && err.Error() != utils.ErrExists.Error() { @@ -140,7 +140,7 @@ func TestActionsLocalSetCdrlogTopup(t *testing.T) { t.Errorf("Calling ApierV1.SetAccount received: %s", reply) } attrsAA := &utils.AttrSetActions{ActionsId: "ACTS_2", Actions: []*utils.TPAction{ - &utils.TPAction{Identifier: TOPUP, BalanceType: utils.MONETARY, Units: "5.0", ExpiryTime: UNLIMITED, Weight: 20.0}, + &utils.TPAction{Identifier: TOPUP, BalanceType: utils.MONETARY, Units: "5", ExpiryTime: UNLIMITED, Weight: 20.0}, &utils.TPAction{Identifier: CDRLOG}, }} if err := actsLclRpc.Call("ApierV1.SetActions", attrsAA, &reply); err != nil && err.Error() != utils.ErrExists.Error() { From f565eb350909233ccf8f8252247242fad9da6d29 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 18 Feb 2016 19:23:22 +0100 Subject: [PATCH 073/199] Adding simpa event test --- agents/dmtagent_it_test.go | 75 +++++++++++++++++++ .../samples/dmtagent/diameter_processors.json | 21 ++++++ 2 files changed, 96 insertions(+) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 559043d95..0340be100 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -534,6 +534,81 @@ func TestDmtAgentSendCCRInitWrongAccount(t *testing.T) { } } +func TestDmtAgentSendCCRSimpaEvent(t *testing.T) { + if !*testIntegration { + return + } + ccr := diam.NewRequest(diam.CreditControl, 4, nil) + ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("cgrates;1451911932;00084")) + ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) + ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) + ccr.NewAVP(avp.DestinationRealm, avp.Mbit, 0, datatype.DiameterIdentity("routing1.huawei.com")) + ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) + ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("simpa@huawei.com")) + ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(4)) + ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(0)) + ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 13, 16, 47, 58, 0, time.UTC))) + ccr.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)), + diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), // Subscription-Id-Data + }}) + ccr.NewAVP(avp.ServiceIdentifier, avp.Mbit, 0, datatype.Unsigned32(0)) + ccr.NewAVP(avp.RequestedAction, avp.Mbit, 0, datatype.Enumerated(1)) + ccr.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.CCMoney, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.UnitValue, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(10000)), + diam.NewAVP(avp.Exponent, avp.Mbit, 0, datatype.Integer32(-5)), + }, + }), + diam.NewAVP(avp.CurrencyCode, avp.Mbit, 0, datatype.Unsigned32(33)), + }, + }), + }, + }) + ccr.NewAVP(873, avp.Mbit, 10415, &diam.GroupedAVP{ // Service-Information + AVP: []*diam.AVP{ + diam.NewAVP(20300, avp.Mbit, 2011, &diam.GroupedAVP{ // IN-Information + AVP: []*diam.AVP{ + diam.NewAVP(20302, avp.Mbit, 2011, datatype.UTF8String("22509")), // Calling-Vlr-Number + diam.NewAVP(20385, avp.Mbit, 2011, datatype.UTF8String("4002")), // Called-Party-NP + }, + }), + diam.NewAVP(29000, avp.Mbit, 2011, &diam.GroupedAVP{ // MC-Information + AVP: []*diam.AVP{ + diam.NewAVP(29001, avp.Mbit, 2011, datatype.OctetString("0x38924012914528")), // HighLayerCharacteristics + diam.NewAVP(29002, avp.Mbit, 2011, datatype.UTF8String("12928471313847173")), // MC-Service-Id + diam.NewAVP(29003, avp.Mbit, 2011, datatype.UTF8String("SPV123456012123")), // TransparentData + diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // MC-Information + AVP: []*diam.AVP{ + diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(0)), // Address-Type + diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("33780029555")), // Address-Data + }, + }), + }, + }), + }}) + if err := dmtClient.SendMessage(ccr); err != nil { + t.Error(err) + } + time.Sleep(time.Duration(100) * time.Millisecond) + msg := dmtClient.ReceivedMessage() // Discard the received message so we can test next one + if msg == nil { + t.Fatal("No message returned") + } + if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Result-Code") + } else if strResult := avpValAsString(avps[0]); strResult != "5030" { // Result-Code set in the template + t.Errorf("Expecting 5030, received: %s", strResult) + } +} + func TestDmtAgentCdrs(t *testing.T) { if !*testIntegration { return diff --git a/data/conf/samples/dmtagent/diameter_processors.json b/data/conf/samples/dmtagent/diameter_processors.json index 465dba558..1484908c1 100644 --- a/data/conf/samples/dmtagent/diameter_processors.json +++ b/data/conf/samples/dmtagent/diameter_processors.json @@ -85,6 +85,27 @@ {"tag": "Usage", "field_id": "Usage", "type": "*composed", "value": "Requested-Service-Unit>CC-Time", "mandatory": true}, ], }, + { + "id": "simpa", // formal identifier of this processor + "dry_run": false, // do not send the events to SMG, just log them + "request_filter": "Service-Context-Id(^simpa)", // filter requests processed by this processor + "continue_on_success": false, // continue to the next template if executed + "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*generic", "mandatory": true}, + {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, + {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*prepaid", "mandatory": true}, + {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, + {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true}, + {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, + {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, + {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, + {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>SMS-Information>Recipient-Address>Address-Data", "mandatory": true}, + {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "Usage", "field_id": "Usage", "type": "*composed", "value": "Requested-Service-Unit>CC-Time", "mandatory": true}, + ], + }, + ], }, From d6d504507342ff0715b9da9f7988b430e9dd9069 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Feb 2016 21:11:43 +0200 Subject: [PATCH 074/199] test fixes --- apier/v1/apier_local_test.go | 4 ++-- engine/actions_local_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 04d847cb9..c9f58cf05 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -955,7 +955,7 @@ func TestApierSetActions(t *testing.T) { if !*testLocal { return } - act1 := &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75.0", ExpiryTime: engine.UNLIMITED, Weight: 20.0} + act1 := &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75", ExpiryTime: engine.UNLIMITED, Weight: 20.0} attrs1 := &utils.AttrSetActions{ActionsId: "ACTS_1", Actions: []*utils.TPAction{act1}} reply1 := "" if err := rater.Call("ApierV1.SetActions", attrs1, &reply1); err != nil { @@ -974,7 +974,7 @@ func TestApierGetActions(t *testing.T) { return } expectActs := []*utils.TPAction{ - &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75.0", ExpiryTime: engine.UNLIMITED, Weight: 20.0}} + &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75", BalanceWeight: "0", BalanceBlocker: "false", BalanceDisabled: "false", ExpiryTime: engine.UNLIMITED, Weight: 20.0}} var reply []*utils.TPAction if err := rater.Call("ApierV1.GetActions", "ACTS_1", &reply); err != nil { diff --git a/engine/actions_local_test.go b/engine/actions_local_test.go index b4aa17869..b074fc630 100644 --- a/engine/actions_local_test.go +++ b/engine/actions_local_test.go @@ -168,7 +168,7 @@ func TestActionsLocalSetCdrlogTopup(t *testing.T) { rcvedCdrs[0].Subject != "dan2905" || rcvedCdrs[0].Usage != "1" || rcvedCdrs[0].RunID != TOPUP || - strconv.FormatFloat(rcvedCdrs[0].Cost, 'f', -1, 64) != attrsAA.Actions[0].Units { + strconv.FormatFloat(-rcvedCdrs[0].Cost, 'f', -1, 64) != attrsAA.Actions[0].Units { t.Errorf("Received: %+v", rcvedCdrs[0]) } } From e085667054300d77b425695959f76a5618285d67 Mon Sep 17 00:00:00 2001 From: Brice Heppner Date: Fri, 19 Feb 2016 08:51:56 +0000 Subject: [PATCH 075/199] fix compressed TimeSpan cost calculation --- engine/timespans.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/timespans.go b/engine/timespans.go index b7f530d59..58769dc5a 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -328,7 +328,7 @@ func (ts *TimeSpan) CalculateCost() float64 { } return ts.RateInterval.GetCost(ts.GetDuration(), ts.GetGroupStart()) } else { - return ts.Increments.GetTotalCost() + return ts.Increments.GetTotalCost() * float64(ts.GetCompressFactor()) } } From 2690802100b29a6bb3ad3888d4f60791ff8ac6fd Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 19 Feb 2016 12:58:14 +0200 Subject: [PATCH 076/199] triggers loading improvement --- engine/account.go | 2 +- engine/balance_filter.go | 61 +++++++++++++++++++++++++++++++++++++++ engine/loader_csv_test.go | 4 +-- utils/map.go | 14 ++++----- 4 files changed, 70 insertions(+), 11 deletions(-) diff --git a/engine/account.go b/engine/account.go index 4888b536d..912e4de08 100644 --- a/engine/account.go +++ b/engine/account.go @@ -700,7 +700,7 @@ func (acc *Account) InitCounters() { uc.Counters = make(CounterFilters, 0) acc.UnitCounters[at.Balance.GetType()] = append(acc.UnitCounters[at.Balance.GetType()], uc) } - c := &CounterFilter{Filter: at.Balance} + c := &CounterFilter{Filter: at.Balance.Clone()} if (c.Filter.ID == nil || *c.Filter.ID == "") && at.UniqueID != "" { c.Filter.ID = utils.StringPointer(at.UniqueID) } diff --git a/engine/balance_filter.go b/engine/balance_filter.go index e9f95e68c..28d9c912f 100644 --- a/engine/balance_filter.go +++ b/engine/balance_filter.go @@ -47,6 +47,67 @@ func (bp *BalanceFilter) CreateBalance() *Balance { return b.Clone() } +func (bf *BalanceFilter) Clone() *BalanceFilter { + result := &BalanceFilter{} + if bf.Uuid != nil { + result.Uuid = new(string) + *result.Uuid = *bf.Uuid + } + if bf.ID != nil { + result.ID = new(string) + *result.ID = *bf.ID + } + if bf.Value != nil { + result.Value = new(float64) + *result.Value = *bf.Value + } + if bf.RatingSubject != nil { + result.RatingSubject = new(string) + *result.RatingSubject = *bf.RatingSubject + } + if bf.Type != nil { + result.Type = new(string) + *result.Type = *bf.Type + } + if bf.ExpirationDate != nil { + result.ExpirationDate = new(time.Time) + *result.ExpirationDate = *bf.ExpirationDate + } + if bf.Weight != nil { + result.Weight = new(float64) + *result.Weight = *bf.Weight + } + if bf.Disabled != nil { + result.Disabled = new(bool) + *result.Disabled = *bf.Disabled + } + if bf.Blocker != nil { + result.Blocker = new(bool) + *result.Blocker = *bf.Blocker + } + if bf.Factor != nil { + result.Factor = new(ValueFactor) + *result.Factor = *bf.Factor + } + if bf.Directions != nil { + result.Directions = utils.StringMapPointer(bf.Directions.Clone()) + } + if bf.DestinationIDs != nil { + result.DestinationIDs = utils.StringMapPointer(bf.DestinationIDs.Clone()) + } + if bf.Categories != nil { + result.Categories = utils.StringMapPointer(bf.Categories.Clone()) + } + if bf.SharedGroups != nil { + result.SharedGroups = utils.StringMapPointer(bf.SharedGroups.Clone()) + } + if bf.TimingIDs != nil { + result.TimingIDs = utils.StringMapPointer(bf.TimingIDs.Clone()) + } + + return result +} + func (bp *BalanceFilter) LoadFromBalance(b *Balance) *BalanceFilter { if b.Uuid != "" { bp.Uuid = &b.Uuid diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 69f090c8d..3f4fbf87c 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -1055,7 +1055,7 @@ func TestLoadActionTriggers(t *testing.T) { ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ThresholdValue: 10, Balance: &BalanceFilter{ - ID: utils.StringPointer("st0"), + ID: nil, Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), @@ -1133,7 +1133,7 @@ func TestLoadAccountActions(t *testing.T) { expected.UnitCounters[utils.VOICE][0].Counters[i].Filter.ID = b.Filter.ID } if !reflect.DeepEqual(aa.UnitCounters[utils.VOICE][0].Counters[0], expected.UnitCounters[utils.VOICE][0].Counters[0]) { - t.Errorf("Error loading account action: %+v", utils.ToIJSON(aa.UnitCounters[utils.VOICE][0].Counters[0].Filter)) + t.Errorf("Error loading account action: %+v %+v", utils.ToIJSON(aa.UnitCounters[utils.VOICE][0].Counters[0].Filter), utils.ToIJSON(expected.UnitCounters[utils.VOICE][0].Counters[0].Filter)) } // test that it does not overwrite balances existing, err := accountingStorage.GetAccount(aa.Id) diff --git a/utils/map.go b/utils/map.go index e45488935..07f2d280b 100644 --- a/utils/map.go +++ b/utils/map.go @@ -107,14 +107,6 @@ func (sm StringMap) Includes(om StringMap) bool { return true } -func (sm StringMap) Clone() StringMap { - result := make(StringMap, len(sm)) - for k := range sm { - result[k] = true - } - return result -} - func (sm StringMap) Slice() []string { result := make([]string, len(sm)) i := 0 @@ -142,6 +134,12 @@ func (sm StringMap) Copy(o StringMap) { } } +func (sm StringMap) Clone() StringMap { + result := make(StringMap, len(sm)) + result.Copy(sm) + return result +} + func (sm StringMap) String() string { return strings.Join(sm.Slice(), INFIELD_SEP) } From 7fc0e4ff502271b7b0746a083fa6749b321e605c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 9 Feb 2016 16:19:40 +0200 Subject: [PATCH 077/199] started action balance pointer filtering --- engine/account.go | 28 +++++---- engine/action.go | 129 +++++++++++++++++++++++++++++---------- engine/action_plan.go | 6 +- engine/action_trigger.go | 49 +++++++-------- engine/balances.go | 36 +++++------ engine/models.go | 14 ++--- engine/tp_reader.go | 67 ++++++++++++++++---- glide.lock | 34 ++++++++++- utils/apitpdata.go | 36 +++++------ utils/consts.go | 1 + utils/coreutils.go | 4 ++ utils/map.go | 3 + 12 files changed, 273 insertions(+), 134 deletions(-) diff --git a/engine/account.go b/engine/account.go index 287c3ab3a..c7f5f9d7b 100644 --- a/engine/account.go +++ b/engine/account.go @@ -93,7 +93,7 @@ func (ub *Account) setBalanceAction(a *Action) error { if a == nil { return errors.New("nil action") } - bClone := a.Balance.Clone() + bClone := a.Balance.CreateBalance() if bClone == nil { return errors.New("nil balance") } @@ -182,7 +182,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { if a == nil { return errors.New("nil action") } - bClone := a.Balance.Clone() + bClone := a.Balance.CreateBalance() if bClone == nil { return errors.New("nil balance") } @@ -261,6 +261,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { return nil } +/* func (ub *Account) enableDisableBalanceAction(a *Action) error { if a == nil { return errors.New("nil action") @@ -286,7 +287,7 @@ func (ub *Account) enableDisableBalanceAction(a *Action) error { } return nil } - +*/ func (ub *Account) getBalancesForPrefix(prefix, category, direction, tor string, sharedGroup string) BalanceChain { var balances BalanceChain balances = append(balances, ub.BalanceMap[tor]...) @@ -933,23 +934,24 @@ func (acc *Account) AsOldStructure() interface{} { } } for i, at := range acc.ActionTriggers { + b := at.Balance.CreateBalance() result.ActionTriggers[i] = &ActionTrigger{ Id: at.ID, ThresholdType: at.ThresholdType, ThresholdValue: at.ThresholdValue, Recurrent: at.Recurrent, MinSleep: at.MinSleep, - BalanceId: at.BalanceId, BalanceType: at.BalanceType, - BalanceDirection: at.BalanceDirections.String(), - BalanceDestinationIds: at.BalanceDestinationIds.String(), - BalanceWeight: at.BalanceWeight, - BalanceExpirationDate: at.BalanceExpirationDate, - BalanceTimingTags: at.BalanceTimingTags.String(), - BalanceRatingSubject: at.BalanceRatingSubject, - BalanceCategory: at.BalanceCategories.String(), - BalanceSharedGroup: at.BalanceSharedGroups.String(), - BalanceDisabled: at.BalanceDisabled, + BalanceId: b.Id, + BalanceDirection: b.Directions.String(), + BalanceDestinationIds: b.DestinationIds.String(), + BalanceWeight: b.Weight, + BalanceExpirationDate: b.ExpirationDate, + BalanceTimingTags: b.TimingIDs.String(), + BalanceRatingSubject: b.RatingSubject, + BalanceCategory: b.Categories.String(), + BalanceSharedGroup: b.SharedGroups.String(), + BalanceDisabled: b.Disabled, Weight: at.Weight, ActionsId: at.ActionsId, MinQueuedItems: at.MinQueuedItems, diff --git a/engine/action.go b/engine/action.go index 00e23bcb5..8e6264bd5 100644 --- a/engine/action.go +++ b/engine/action.go @@ -45,28 +45,93 @@ type Action struct { Filter string ExpirationString string // must stay as string because it can have relative values like 1month Weight float64 - Balance *Balance + Balance *BalancePointer +} + +type BalancePointer struct { + Uuid *string + Id *string + Value *float64 + Directions *utils.StringMap + ExpirationDate *time.Time + Weight *float64 + DestinationIds *utils.StringMap + RatingSubject *string + Categories *utils.StringMap + SharedGroups *utils.StringMap + TimingIDs *utils.StringMap + Timings []*RITiming + Disabled *bool + Factor *ValueFactor + Blocker *bool +} + +func (bp *BalancePointer) CreateBalance() *Balance { + b := &Balance{} + if bp.Uuid != nil { + b.Uuid = *bp.Uuid + } + if bp.Id != nil { + b.Id = *bp.Id + } + if bp.Value != nil { + b.Value = *bp.Value + } + if bp.Directions != nil { + b.Directions = *bp.Directions + } + if bp.ExpirationDate != nil { + b.ExpirationDate = *bp.ExpirationDate + } + if bp.Weight != nil { + b.Weight = *bp.Weight + } + if bp.DestinationIds != nil { + b.DestinationIds = *bp.DestinationIds + } + if bp.RatingSubject != nil { + b.RatingSubject = *bp.RatingSubject + } + if bp.Categories != nil { + b.Categories = *bp.Categories + } + if bp.SharedGroups != nil { + b.SharedGroups = *bp.SharedGroups + } + if bp.TimingIDs != nil { + b.TimingIDs = *bp.TimingIDs + } + if bp.Disabled != nil { + b.Disabled = *bp.Disabled + } + if bp.Factor != nil { + b.Factor = *bp.Factor + } + if bp.Blocker != nil { + b.Blocker = *bp.Blocker + } + return b.Clone() } const ( - LOG = "*log" - RESET_TRIGGERS = "*reset_triggers" - SET_RECURRENT = "*set_recurrent" - UNSET_RECURRENT = "*unset_recurrent" - ALLOW_NEGATIVE = "*allow_negative" - DENY_NEGATIVE = "*deny_negative" - RESET_ACCOUNT = "*reset_account" - REMOVE_ACCOUNT = "*remove_account" - SET_BALANCE = "*set_balance" // not ready for production until switching to pointers - REMOVE_BALANCE = "*remove_balance" - TOPUP_RESET = "*topup_reset" - TOPUP = "*topup" - DEBIT_RESET = "*debit_reset" - DEBIT = "*debit" - RESET_COUNTERS = "*reset_counters" - ENABLE_ACCOUNT = "*enable_account" - DISABLE_ACCOUNT = "*disable_account" - ENABLE_DISABLE_BALANCE = "*enable_disable_balance" + LOG = "*log" + RESET_TRIGGERS = "*reset_triggers" + SET_RECURRENT = "*set_recurrent" + UNSET_RECURRENT = "*unset_recurrent" + ALLOW_NEGATIVE = "*allow_negative" + DENY_NEGATIVE = "*deny_negative" + RESET_ACCOUNT = "*reset_account" + REMOVE_ACCOUNT = "*remove_account" + SET_BALANCE = "*set_balance" // not ready for production until switching to pointers + REMOVE_BALANCE = "*remove_balance" + TOPUP_RESET = "*topup_reset" + TOPUP = "*topup" + DEBIT_RESET = "*debit_reset" + DEBIT = "*debit" + RESET_COUNTERS = "*reset_counters" + ENABLE_ACCOUNT = "*enable_account" + DISABLE_ACCOUNT = "*disable_account" + //ENABLE_DISABLE_BALANCE = "*enable_disable_balance" CALL_URL = "*call_url" CALL_URL_ASYNC = "*call_url_async" MAIL_ASYNC = "*mail_async" @@ -84,7 +149,7 @@ func (a *Action) Clone() *Action { ExtraParameters: a.ExtraParameters, ExpirationString: a.ExpirationString, Weight: a.Weight, - Balance: a.Balance.Clone(), + Balance: a.Balance, } } @@ -122,8 +187,8 @@ func getActionFunc(typ string) (actionTypeFunc, bool) { return enableUserAction, true case DISABLE_ACCOUNT: return disableUserAction, true - case ENABLE_DISABLE_BALANCE: - return enableDisableBalanceAction, true + //case ENABLE_DISABLE_BALANCE: + // return enableDisableBalanceAction, true case CALL_URL: return callUrl, true case CALL_URL_ASYNC: @@ -167,6 +232,7 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) dta = new(utils.TenantAccount) // Init with empty values } var parsedValue string // Template values + b := action.Balance.CreateBalance() for _, rsrFld := range rsrFlds { switch rsrFld.Id { case "AccountID": @@ -184,17 +250,17 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) case "BalanceType": parsedValue += rsrFld.ParseValue(action.BalanceType) case "BalanceUUID": - parsedValue += rsrFld.ParseValue(action.Balance.Uuid) + parsedValue += rsrFld.ParseValue(b.Uuid) case "BalanceID": - parsedValue += rsrFld.ParseValue(action.Balance.Id) + parsedValue += rsrFld.ParseValue(b.Id) case "BalanceValue": - parsedValue += rsrFld.ParseValue(strconv.FormatFloat(action.Balance.GetValue(), 'f', -1, 64)) + parsedValue += rsrFld.ParseValue(strconv.FormatFloat(b.GetValue(), 'f', -1, 64)) case "DestinationIDs": - parsedValue += rsrFld.ParseValue(action.Balance.DestinationIds.String()) + parsedValue += rsrFld.ParseValue(b.DestinationIds.String()) case "ExtraParameters": parsedValue += rsrFld.ParseValue(action.ExtraParameters) case "RatingSubject": - parsedValue += rsrFld.ParseValue(action.Balance.RatingSubject) + parsedValue += rsrFld.ParseValue(b.RatingSubject) case utils.CATEGORY: parsedValue += rsrFld.ParseValue(action.Balance.Categories.String()) case "SharedGroups": @@ -370,8 +436,9 @@ func resetCountersAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac } func genericMakeNegative(a *Action) { - if a.Balance != nil && a.Balance.GetValue() >= 0 { // only apply if not allready negative - a.Balance.SetValue(-a.Balance.GetValue()) + b := a.Balance.CreateBalance() + if a.Balance != nil && b.GetValue() >= 0 { // only apply if not allready negative + b.SetValue(-b.GetValue()) } } @@ -401,13 +468,13 @@ func disableUserAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Acti return } -func enableDisableBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { +/*func enableDisableBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) (err error) { if ub == nil { return errors.New("nil account") } ub.enableDisableBalanceAction(a) return -} +}*/ func genericReset(ub *Account) error { for k, _ := range ub.BalanceMap { diff --git a/engine/action_plan.go b/engine/action_plan.go index 3b8069c64..0a9283d25 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -308,7 +308,8 @@ func (at *ActionTiming) Execute() (err error) { //return 0, fmt.Errorf("Account %s is disabled", accID) } if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.ExpirationDate.IsZero()) && parseErr == nil && !expDate.IsZero() { - a.Balance.ExpirationDate = expDate + a.Balance.ExpirationDate = &time.Time{} + *a.Balance.ExpirationDate = expDate } actionFunction, exists := getActionFunc(a.ActionType) @@ -339,7 +340,8 @@ func (at *ActionTiming) Execute() (err error) { if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.ExpirationDate.IsZero()) && parseErr == nil && !expDate.IsZero() { - a.Balance.ExpirationDate = expDate + a.Balance.ExpirationDate = &time.Time{} + *a.Balance.ExpirationDate = expDate } actionFunction, exists := getActionFunc(a.ActionType) diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 71c8aed6d..d9ae7b005 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -33,28 +33,18 @@ type ActionTrigger struct { UniqueID string // individual id ThresholdType string //*min_event_counter, *max_event_counter, *min_balance_counter, *max_balance_counter, *min_balance, *max_balance, *exp_balance // stats: *min_asr, *max_asr, *min_acd, *max_acd, *min_tcd, *max_tcd, *min_acc, *max_acc, *min_tcc, *max_tcc, *min_ddc, *max_ddc - ThresholdValue float64 - Recurrent bool // reset excuted flag each run - MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers - ExpirationDate time.Time - ActivationDate time.Time - BalanceId string - BalanceType string // *monetary/*voice etc - BalanceDirections utils.StringMap // filter for balance - BalanceDestinationIds utils.StringMap // filter for balance - BalanceWeight float64 // filter for balance - BalanceExpirationDate time.Time // filter for balance - BalanceTimingTags utils.StringMap // filter for balance - BalanceRatingSubject string // filter for balance - BalanceCategories utils.StringMap // filter for balance - BalanceSharedGroups utils.StringMap // filter for balance - BalanceBlocker bool - BalanceDisabled bool // filter for balance - Weight float64 - ActionsId string - MinQueuedItems int // Trigger actions only if this number is hit (stats only) - Executed bool - lastExecutionTime time.Time + ThresholdValue float64 + Recurrent bool // reset excuted flag each run + MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers + ExpirationDate time.Time + ActivationDate time.Time + BalanceType string // *monetary/*voice etc + Balance *BalancePointer + Weight float64 + ActionsId string + MinQueuedItems int // Trigger actions only if this number is hit (stats only) + Executed bool + lastExecutionTime time.Time } func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err error) { @@ -90,9 +80,12 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro } if a.Balance == nil { - a.Balance = &Balance{} + a.Balance = &BalancePointer{} + } + if a.ExpirationString != "" { + a.Balance.ExpirationDate = &time.Time{} + *a.Balance.ExpirationDate, _ = utils.ParseDate(a.ExpirationString) } - a.Balance.ExpirationDate, _ = utils.ParseDate(a.ExpirationString) actionFunction, exists := getActionFunc(a.ActionType) if !exists { utils.Logger.Err(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType)) @@ -131,7 +124,7 @@ func (at *ActionTrigger) Match(a *Action) bool { return match } id := a.BalanceType == "" || at.BalanceType == a.BalanceType - thresholdType, thresholdValue, direction, destinationId, weight, ratingSubject, categories, sharedGroup, timings, blocker, disabled := true, true, true, true, true, true, true, true, true, true, true + thresholdType, thresholdValue, direction, destinationID, weight, ratingSubject, categories, sharedGroup, timings, blocker, disabled := true, true, true, true, true, true, true, true, true, true, true if a.ExtraParameters != "" { t := struct { ThresholdType string @@ -149,8 +142,8 @@ func (at *ActionTrigger) Match(a *Action) bool { json.Unmarshal([]byte(a.ExtraParameters), &t) thresholdType = t.ThresholdType == "" || at.ThresholdType == t.ThresholdType thresholdValue = t.ThresholdValue == 0 || at.ThresholdValue == t.ThresholdValue - direction = len(t.BalanceDirections) == 0 || at.BalanceDirections.Equal(utils.ParseStringMap(t.BalanceDirections)) - destinationId = len(t.DestinationIds) == 0 || at.BalanceDestinationIds.Equal(utils.ParseStringMap(t.DestinationIds)) + direction = t.Balance.Directions == nil || at.Balance.Directions.Equal(utils.ParseStringMap(t.BalanceDirections)) + destinationID = len(t.DestinationIds) == 0 || at.BalanceDestinationIds.Equal(utils.ParseStringMap(t.DestinationIds)) categories = len(t.BalanceCategories) == 0 || at.BalanceCategories.Equal(utils.ParseStringMap(t.BalanceCategories)) timings = len(t.BalanceTimingTags) == 0 || at.BalanceTimingTags.Equal(utils.ParseStringMap(t.BalanceTimingTags)) sharedGroup = len(t.BalanceSharedGroups) == 0 || at.BalanceSharedGroups.Equal(utils.ParseStringMap(t.BalanceSharedGroups)) @@ -159,7 +152,7 @@ func (at *ActionTrigger) Match(a *Action) bool { blocker = at.BalanceBlocker == t.BalanceBlocker disabled = at.BalanceDisabled == t.BalanceDisabled } - return id && direction && thresholdType && thresholdValue && destinationId && weight && ratingSubject && categories && sharedGroup && timings && blocker && disabled + return id && direction && thresholdType && thresholdValue && destinationID && weight && ratingSubject && categories && sharedGroup && timings && blocker && disabled } // makes a shallow copy of the receiver diff --git a/engine/balances.go b/engine/balances.go index e1aa16777..b1a9fba96 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -72,32 +72,26 @@ func (b *Balance) Equal(o *Balance) bool { b.Blocker == o.Blocker } -func (b *Balance) MatchFilter(o *Balance, skipIds bool) bool { +func (b *Balance) MatchFilter(o *BalancePointer, skipIds bool) bool { if o == nil { return true } - if !skipIds && o.Uuid != "" { - return b.Uuid == o.Uuid + if !skipIds && o.Uuid != nil && *o.Uuid != "" { + return b.Uuid == *o.Uuid } - if !skipIds && o.Id != "" { - return b.Id == o.Id + if !skipIds && o.Id != nil && *o.Id != "" { + return b.Id == *o.Id } - if len(b.DestinationIds) == 0 { - b.DestinationIds = utils.StringMap{utils.ANY: true} - } - if len(o.DestinationIds) == 0 { - o.DestinationIds = utils.StringMap{utils.ANY: true} - } - return (o.ExpirationDate.IsZero() || b.ExpirationDate.Equal(o.ExpirationDate)) && - (o.Weight == 0 || b.Weight == o.Weight) && - (b.Blocker == o.Blocker) && - (b.Disabled == o.Disabled) && - (len(o.DestinationIds) == 0 || b.DestinationIds.Includes(o.DestinationIds)) && - (len(o.Directions) == 0 || b.Directions.Includes(o.Directions)) && - (len(o.Categories) == 0 || b.Categories.Includes(o.Categories)) && - (len(o.TimingIDs) == 0 || b.TimingIDs.Includes(o.TimingIDs)) && - (len(o.SharedGroups) == 0 || b.SharedGroups.Includes(o.SharedGroups)) && - (o.RatingSubject == "" || b.RatingSubject == o.RatingSubject) + return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && + (o.Weight == nil || b.Weight == *o.Weight) && + (o.Blocker != nil || b.Blocker == *o.Blocker) && + (o.Disabled == nil || b.Disabled == *o.Disabled) && + (o.DestinationIds == nil || b.DestinationIds.Includes(*o.DestinationIds)) && + (o.Directions == nil || b.Directions.Includes(*o.Directions)) && + (o.Categories == nil || b.Categories.Includes(*o.Categories)) && + (o.TimingIDs == nil || b.TimingIDs.Includes(*o.TimingIDs)) && + (o.SharedGroups == nil || b.SharedGroups.Includes(*o.SharedGroups)) && + (o.RatingSubject == nil || b.RatingSubject == *o.RatingSubject) } func (b *Balance) MatchCCFilter(cc *CallCost) bool { diff --git a/engine/models.go b/engine/models.go index d16916891..a37102094 100644 --- a/engine/models.go +++ b/engine/models.go @@ -166,10 +166,10 @@ type TpAction struct { SharedGroups string `index:"10" re:"[0-9A-Za-z_;]*"` ExpiryTime string `index:"11" re:"\*\w+\s*|\+\d+[smh]\s*|\d+\s*"` TimingTags string `index:"12" re:"[0-9A-Za-z_;]*|\*any"` - Units float64 `index:"13" re:"\d+\s*"` - BalanceWeight float64 `index:"14" re:"\d+\.?\d*\s*"` - BalanceBlocker bool `index:"15" re:""` - BalanceDisabled bool `index:"16" re:""` + Units string `index:"13" re:"\d+\s*"` + BalanceWeight string `index:"14" re:"\d+\.?\d*\s*"` + BalanceBlocker string `index:"15" re:""` + BalanceDisabled string `index:"16" re:""` Weight float64 `index:"17" re:"\d+\.?\d*\s*"` CreatedAt time.Time } @@ -204,9 +204,9 @@ type TpActionTrigger struct { BalanceSharedGroups string `index:"14" re:"\w+|\*any"` BalanceExpiryTime string `index:"15" re:"\*\w+\s*|\+\d+[smh]\s*|\d+\s*"` BalanceTimingTags string `index:"16" re:"[0-9A-Za-z_;]*|\*any"` - BalanceWeight float64 `index:"17" re:"\d+\.?\d*"` - BalanceBlocker bool `index:"18" re:""` - BalanceDisabled bool `index:"19" re:""` + BalanceWeight string `index:"17" re:"\d+\.?\d*"` + BalanceBlocker string `index:"18" re:""` + BalanceDisabled string `index:"19" re:""` MinQueuedItems int `index:"20" re:"\d+"` ActionsTag string `index:"21" re:"\w+"` Weight float64 `index:"22" re:"\d+\.?\d*"` diff --git a/engine/tp_reader.go b/engine/tp_reader.go index a27a4508b..17c7706d1 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -521,20 +521,61 @@ func (tpr *TpReader) LoadActions() (err error) { ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &Balance{ - Id: tpact.BalanceId, - Value: tpact.Units, - Weight: tpact.BalanceWeight, - RatingSubject: tpact.RatingSubject, - Categories: utils.ParseStringMap(tpact.Categories), - Directions: utils.ParseStringMap(tpact.Directions), - DestinationIds: utils.ParseStringMap(tpact.DestinationIds), - SharedGroups: utils.ParseStringMap(tpact.SharedGroups), - TimingIDs: utils.ParseStringMap(tpact.TimingTags), - Blocker: tpact.BalanceBlocker, - Disabled: tpact.BalanceDisabled, - }, + Balance: &BalancePointer{}, } + if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { + acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + } + + if tpact.Units != "" && tpact.Units != utils.ANY { + u, err := strconv.ParseFloat(tpact.Units, 64) + if err != nil { + return err + } + acts[idx].Balance.Value = utils.Float64Pointer(u) + } + + if tpact.BalanceWeight != "" && tpact.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(tpact.BalanceWeight, 64) + if err != nil { + return err + } + acts[idx].Balance.Weight = utils.Float64Pointer(u) + } + if tpact.RatingSubject != "" && tpact.RatingSubject != utils.ANY { + acts[idx].Balance.RatingSubject = utils.StringPointer(tpact.RatingSubject) + } + + if tpact.Categories != "" && tpact.Categories != utils.ANY { + acts[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(tpact.Categories)) + } + if tpact.Directions != "" && tpact.Directions != utils.ANY { + acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) + } + if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { + acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + } + if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { + acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) + } + if tpact.TimingTags != "" && tpact.TimingTags != utils.ANY { + acts[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.TimingTags)) + } + if tpact.BalanceBlocker != "" && tpact.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceBlocker) + if err != nil { + return err + } + acts[idx].Balance.Blocker = utils.BoolPointer(u) + } + if tpact.BalanceDisabled != "" && tpact.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceDisabled) + if err != nil { + return err + } + acts[idx].Balance.Disabled = utils.BoolPointer(u) + } + // load action timings from tags if tpact.TimingTags != "" { timingIds := strings.Split(tpact.TimingTags, utils.INFIELD_SEP) diff --git a/glide.lock b/glide.lock index ac52fee0e..84c1f8c76 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 855bc23b0e58452edf1d31f228430476a2602b79d225397d33b805b252cebb83 -updated: 2016-01-21T12:30:17.296295607+02:00 +updated: 2016-02-05T16:54:55.433704333+02:00 imports: - name: github.com/cenkalti/hub version: 57d753b5f4856e77b3cf8ecce78c97215a7d324d @@ -25,6 +25,8 @@ imports: - diam/datatype - diam/dict - diam/sm + - diam/sm/smparser + - diam/sm/smpeer - name: github.com/go-sql-driver/mysql version: bb006fd699a123d3eb514561dbefc352e978949d - name: github.com/gorhill/cronexpr @@ -37,11 +39,15 @@ imports: version: f7ee69f31298ecbe5d2b349c711e2547a617d398 - name: github.com/lib/pq version: 11fc39a580a008f1f39bb3d11d984fb34ed778d9 + subpackages: + - hstore + - oid - name: github.com/mediocregopher/radix.v2 version: 91435107718b55ff544323a2b0f25fdd8475d283 subpackages: - /pool - redis + - pool - name: github.com/peterh/liner version: 3f1c20449d1836aa4cbe38731b96f95cdf89634d - name: github.com/ugorji/go @@ -50,18 +56,44 @@ imports: - /codec - name: golang.org/x/crypto version: 552e9d568fde9701ea1944fb01c8aadaceaa7353 + subpackages: + - ssh/terminal - name: golang.org/x/net version: 961116aeebe66bfb58bb4d51818c70d256acbbb8 subpackages: - /websocket + - context + - html/atom + - http2/hpack + - internal/iana + - ipv4 + - ipv6 + - webdav/internal/xml - name: golang.org/x/text version: cf4986612c83df6c55578ba198316d1684a9a287 + subpackages: + - encoding + - encoding/charmap + - encoding/htmlindex + - transform + - encoding/internal/identifier + - encoding/internal + - encoding/japanese + - encoding/korean + - encoding/simplifiedchinese + - encoding/traditionalchinese + - encoding/unicode + - language + - internal/utf8internal + - runes + - internal/tag - name: gopkg.in/fsnotify.v1 version: 508915b7500b6e42a87132e4afeb4729cebc7cbb - name: gopkg.in/mgo.v2 version: e30de8ac9ae3b30df7065f766c71f88bba7d4e49 subpackages: - bson + - internal/scram - name: gopkg.in/tomb.v2 version: 14b3d72120e8d10ea6e6b7f87f7175734b1faab8 devImports: [] diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 9fdabdc9a..cc4a47a53 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -273,22 +273,22 @@ type TPActions struct { } type TPAction struct { - Identifier string // Identifier mapped in the code - BalanceId string // Balance identification string (account scope) - BalanceType string // Type of balance the action will operate on - Directions string // Balance direction - Units float64 // Number of units to add/deduct - ExpiryTime string // Time when the units will expire - Filter string // The condition on balances that is checked before the action - TimingTags string // Timing when balance is active - DestinationIds string // Destination profile id - RatingSubject string // Reference a rate subject defined in RatingProfiles - Categories string // category filter for balances - SharedGroups string // Reference to a shared group - BalanceWeight float64 // Balance weight + Identifier string // Identifier mapped in the code + BalanceId string // Balance identification string (account scope) + BalanceType string // Type of balance the action will operate on + Directions string // Balance direction + Units string // Number of units to add/deduct + ExpiryTime string // Time when the units will expire + Filter string // The condition on balances that is checked before the action + TimingTags string // Timing when balance is active + DestinationIds string // Destination profile id + RatingSubject string // Reference a rate subject defined in RatingProfiles + Categories string // category filter for balances + SharedGroups string // Reference to a shared group + BalanceWeight string // Balance weight ExtraParameters string - BalanceBlocker bool - BalanceDisabled bool + BalanceBlocker string + BalanceDisabled string Weight float64 // Action's weight } @@ -486,14 +486,14 @@ type TPActionTrigger struct { BalanceType string // Type of balance this trigger monitors BalanceDirections string // Traffic direction BalanceDestinationIds string // filter for balance - BalanceWeight float64 // filter for balance + BalanceWeight string // filter for balance BalanceExpirationDate string // filter for balance BalanceTimingTags string // filter for balance BalanceRatingSubject string // filter for balance BalanceCategories string // filter for balance BalanceSharedGroups string // filter for balance - BalanceBlocker bool // filter for balance - BalanceDisabled bool // filter for balance + BalanceBlocker string // filter for balance + BalanceDisabled string // filter for balance MinQueuedItems int // Trigger actions only if this number is hit (stats only) ActionsId string // Actions which will execute on threshold reached Weight float64 // weight diff --git a/utils/consts.go b/utils/consts.go index b8ede4078..62feda060 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -91,6 +91,7 @@ const ( ROUNDING_MIDDLE = "*middle" ROUNDING_DOWN = "*down" ANY = "*any" + ZERO = "*zero" ASAP = "*asap" USERS = "*users" COMMENT_CHAR = '#' diff --git a/utils/coreutils.go b/utils/coreutils.go index 29f501eda..091e3fc07 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -337,6 +337,10 @@ func Fib() func() time.Duration { // Utilities to provide pointers where we need to define ad-hoc func StringPointer(str string) *string { + if str == ZERO { + str = "" + return &str + } return &str } diff --git a/utils/map.go b/utils/map.go index d303efcaf..e45488935 100644 --- a/utils/map.go +++ b/utils/map.go @@ -74,6 +74,9 @@ func NewStringMap(s ...string) StringMap { } func ParseStringMap(s string) StringMap { + if s == ZERO { + return make(StringMap) + } return StringMapFromSlice(strings.Split(s, INFIELD_SEP)) } From 097c0073a0b27d18c8b4dc331726edb2e25bf308 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 11 Feb 2016 10:26:43 +0200 Subject: [PATCH 078/199] engine compiling --- engine/account.go | 18 +- engine/account_test.go | 223 ++++++++++------ engine/action.go | 76 +++++- engine/action_trigger.go | 66 +---- engine/actions_local_test.go | 9 +- engine/actions_test.go | 376 +++++++++++++------------- engine/balances.go | 64 ++--- engine/balances_test.go | 28 +- engine/calldesc_test.go | 6 +- engine/loader_csv_test.go | 134 +++++----- engine/model_helpers_test.go | 20 +- engine/storage_test.go | 16 +- engine/tp_reader.go | 495 ++++++++++++++++++++++++++--------- engine/units_counter.go | 6 +- engine/units_counter_test.go | 394 ++++++++++++++++------------ utils/coreutils.go | 4 + 16 files changed, 1157 insertions(+), 778 deletions(-) diff --git a/engine/account.go b/engine/account.go index c7f5f9d7b..50650c546 100644 --- a/engine/account.go +++ b/engine/account.go @@ -111,7 +111,7 @@ func (ub *Account) setBalanceAction(a *Action) error { ub.BalanceMap = make(map[string]BalanceChain, 1) } found := false - balanceType := a.BalanceType + balanceType := a.Balance.GetType() var previousSharedGroups utils.StringMap // kept for comparison for _, b := range ub.BalanceMap[balanceType] { if b.IsExpired() { @@ -190,7 +190,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { ub.BalanceMap = make(map[string]BalanceChain, 1) } found := false - balanceType := a.BalanceType + balanceType := a.Balance.GetType() for _, b := range ub.BalanceMap[balanceType] { if b.IsExpired() { continue // just to be safe (cleaned expired balances above) @@ -592,7 +592,7 @@ func (acc *Account) ExecuteActionTriggers(a *Action) { } if strings.Contains(at.ThresholdType, "counter") { for _, uc := range acc.UnitCounters { - if uc.BalanceType == at.BalanceType && + if uc.BalanceType == at.Balance.GetType() && strings.Contains(at.ThresholdType, uc.CounterType[1:]) { for _, b := range uc.Balances { if strings.HasPrefix(at.ThresholdType, "*max") { @@ -608,7 +608,7 @@ func (acc *Account) ExecuteActionTriggers(a *Action) { } } } else { // BALANCE - for _, b := range acc.BalanceMap[at.BalanceType] { + for _, b := range acc.BalanceMap[at.Balance.GetType()] { if !b.dirty && at.ThresholdType != utils.TRIGGER_BALANCE_EXPIRED { // do not check clean balances continue } @@ -674,17 +674,17 @@ func (acc *Account) InitCounters() { ct = utils.COUNTER_BALANCE } - uc, exists := ucTempMap[at.BalanceType+ct] + uc, exists := ucTempMap[at.Balance.GetType()+ct] if !exists { uc = &UnitCounter{ - BalanceType: at.BalanceType, + BalanceType: at.Balance.GetType(), CounterType: ct, } - ucTempMap[at.BalanceType+ct] = uc + ucTempMap[at.Balance.GetType()+ct] = uc uc.Balances = BalanceChain{} acc.UnitCounters = append(acc.UnitCounters, uc) } - b := at.CreateBalance() + b := at.Balance.CreateBalance() if !uc.Balances.HasBalance(b) { uc.Balances = append(uc.Balances, b) } @@ -941,7 +941,7 @@ func (acc *Account) AsOldStructure() interface{} { ThresholdValue: at.ThresholdValue, Recurrent: at.Recurrent, MinSleep: at.MinSleep, - BalanceType: at.BalanceType, + BalanceType: at.Balance.GetType(), BalanceId: b.Id, BalanceDirection: b.Directions.String(), BalanceDestinationIds: b.DestinationIds.String(), diff --git a/engine/account_test.go b/engine/account_test.go index 11a40f65b..b603e135d 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -814,22 +814,32 @@ func TestAccountdebitBalance(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &Balance{Weight: 20, DestinationIds: utils.StringMap{"NEW": true}, Directions: utils.NewStringMap(utils.OUT)} - a := &Action{BalanceType: utils.VOICE, Balance: newMb} + newMb := &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Weight: utils.Float64Pointer(20), + DestinationIds: utils.StringMapPointer(utils.StringMap{"NEW": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } + a := &Action{Balance: newMb} ub.debitBalanceAction(a, false) - if len(ub.BalanceMap[utils.VOICE]) != 3 || !ub.BalanceMap[utils.VOICE][2].DestinationIds.Equal(newMb.DestinationIds) { + if len(ub.BalanceMap[utils.VOICE]) != 3 || !ub.BalanceMap[utils.VOICE][2].DestinationIds.Equal(*newMb.DestinationIds) { t.Errorf("Error adding minute bucket! %d %+v %+v", len(ub.BalanceMap[utils.VOICE]), ub.BalanceMap[utils.VOICE][2], newMb) } } -func TestAccountDisableBalance(t *testing.T) { +/*func TestAccountDisableBalance(t *testing.T) { ub := &Account{ Id: "rif", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT), Disabled: true} - a := &Action{BalanceType: utils.VOICE, Balance: newMb} + newMb := &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Weight: utils.Float64Pointer(20), + DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } + a := &Action{Balance: newMb} ub.enableDisableBalanceAction(a) if len(ub.BalanceMap[utils.VOICE]) != 2 || ub.BalanceMap[utils.VOICE][0].Disabled != true { for _, b := range ub.BalanceMap[utils.VOICE] { @@ -837,7 +847,7 @@ func TestAccountDisableBalance(t *testing.T) { } t.Errorf("Error disabling balance! %d %+v %+v", len(ub.BalanceMap[utils.VOICE]), ub.BalanceMap[utils.VOICE][0], newMb) } -} +}*/ func TestAccountdebitBalanceExists(t *testing.T) { @@ -846,8 +856,14 @@ func TestAccountdebitBalanceExists(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Value: 15, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &Balance{Value: -10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)} - a := &Action{BalanceType: utils.VOICE, Balance: newMb} + newMb := &BalancePointer{ + Value: utils.Float64Pointer(-10), + Type: utils.StringPointer(utils.VOICE), + Weight: utils.Float64Pointer(20), + DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } + a := &Action{Balance: newMb} ub.debitBalanceAction(a, false) if len(ub.BalanceMap[utils.VOICE]) != 2 || ub.BalanceMap[utils.VOICE][0].GetValue() != 25 { t.Error("Error adding minute bucket!") @@ -867,21 +883,36 @@ func TestAccountAddMinuteNil(t *testing.T) { } func TestAccountAddMinutBucketEmpty(t *testing.T) { - mb1 := &Balance{Value: -10, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)} - mb2 := &Balance{Value: -10, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)} - mb3 := &Balance{Value: -10, DestinationIds: utils.StringMap{"OTHER": true}, Directions: utils.NewStringMap(utils.OUT)} + mb1 := &BalancePointer{ + Value: utils.Float64Pointer(-10), + Type: utils.StringPointer(utils.VOICE), + DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } + mb2 := &BalancePointer{ + Value: utils.Float64Pointer(-10), + Type: utils.StringPointer(utils.VOICE), + DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } + mb3 := &BalancePointer{ + Value: utils.Float64Pointer(-10), + Type: utils.StringPointer(utils.VOICE), + DestinationIds: utils.StringMapPointer(utils.StringMap{"OTHER": true}), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + } ub := &Account{} - a := &Action{BalanceType: utils.VOICE, Balance: mb1} + a := &Action{Balance: mb1} ub.debitBalanceAction(a, false) if len(ub.BalanceMap[utils.VOICE]) != 1 { t.Error("Error adding minute bucket: ", ub.BalanceMap[utils.VOICE]) } - a = &Action{BalanceType: utils.VOICE, Balance: mb2} + a = &Action{Balance: mb2} ub.debitBalanceAction(a, false) if len(ub.BalanceMap[utils.VOICE]) != 1 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { t.Error("Error adding minute bucket: ", ub.BalanceMap[utils.VOICE]) } - a = &Action{BalanceType: utils.VOICE, Balance: mb3} + a = &Action{Balance: mb3} ub.debitBalanceAction(a, false) if len(ub.BalanceMap[utils.VOICE]) != 2 { t.Error("Error adding minute bucket: ", ub.BalanceMap[utils.VOICE]) @@ -893,7 +924,7 @@ func TestAccountExecuteTriggeredActions(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.StringMap{utils.OUT: true}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { @@ -917,7 +948,7 @@ func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, nil, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { @@ -930,7 +961,7 @@ func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { Id: "TEST_UB_OREDER", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) @@ -945,8 +976,8 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{UniqueID: "day_trigger", BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, - &ActionTrigger{UniqueID: "week_trigger", BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{UniqueID: "day_trigger", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{UniqueID: "week_trigger", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, }, } ub.InitCounters() @@ -961,7 +992,7 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { } // we can reset them - resetCountersAction(ub, nil, &Action{BalanceType: utils.MONETARY, Balance: &Balance{Id: "day_trigger"}}, nil) + resetCountersAction(ub, nil, &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("day_trigger")}}, nil) if ub.UnitCounters[0].Balances[0].Value != 0 || ub.UnitCounters[0].Balances[1].Value != 1 { t.Error("Error reseting both counters", ub.UnitCounters[0].Balances[0].Value, ub.UnitCounters[0].Balances[1].Value) @@ -973,7 +1004,7 @@ func TestAccountExpActionTrigger(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100, ExpirationDate: time.Date(2015, time.November, 9, 9, 48, 0, 0, time.UTC)}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -991,7 +1022,7 @@ func TestAccountExpActionTriggerNotActivated(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1009,7 +1040,7 @@ func TestAccountExpActionTriggerExpired(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), BalanceType: utils.MONETARY, BalanceDirections: utils.StringMap{utils.OUT: true}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1565,46 +1596,58 @@ func TestAccountInitCounters(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -1628,46 +1671,58 @@ func TestAccountDoubleInitCounters(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, }, } diff --git a/engine/action.go b/engine/action.go index 8e6264bd5..94ab9ba1e 100644 --- a/engine/action.go +++ b/engine/action.go @@ -38,9 +38,9 @@ import ( Structure to be filled for each tariff plan with the bonus value for received calls minutes. */ type Action struct { - Id string - ActionType string - BalanceType string + Id string + ActionType string + //BalanceType string ExtraParameters string Filter string ExpirationString string // must stay as string because it can have relative values like 1month @@ -51,6 +51,7 @@ type Action struct { type BalancePointer struct { Uuid *string Id *string + Type *string Value *float64 Directions *utils.StringMap ExpirationDate *time.Time @@ -113,6 +114,61 @@ func (bp *BalancePointer) CreateBalance() *Balance { return b.Clone() } +func (bp *BalancePointer) LoadFromBalance(b *Balance) { + if b.Uuid != "" { + bp.Uuid = &b.Uuid + } + if b.Id != "" { + bp.Id = &b.Id + } + if b.Value != 0 { + bp.Value = &b.Value + } + if len(b.Directions) != 0 { + bp.Directions = &b.Directions + } + if !b.ExpirationDate.IsZero() { + bp.ExpirationDate = &b.ExpirationDate + } + if b.Weight != 0 { + bp.Weight = &b.Weight + } + if len(b.DestinationIds) != 0 { + bp.DestinationIds = &b.DestinationIds + } + if b.RatingSubject != "" { + bp.RatingSubject = &b.RatingSubject + } + if len(b.Categories) != 0 { + bp.Categories = &b.Categories + } + if len(b.SharedGroups) != 0 { + bp.SharedGroups = &b.SharedGroups + } + if len(b.TimingIDs) != 0 { + bp.TimingIDs = &b.TimingIDs + } + if len(b.Factor) != 0 { + bp.Factor = &b.Factor + } + bp.Disabled = &b.Disabled + bp.Blocker = &b.Blocker +} + +func (bp *BalancePointer) GetType() string { + if bp.Type == nil { + return "" + } + return *bp.Type +} + +func (bp *BalancePointer) GetValue() float64 { + if bp.Value == nil { + return 0.0 + } + return *bp.Value +} + const ( LOG = "*log" RESET_TRIGGERS = "*reset_triggers" @@ -143,9 +199,9 @@ const ( func (a *Action) Clone() *Action { return &Action{ - Id: a.Id, - ActionType: a.ActionType, - BalanceType: a.BalanceType, + Id: a.Id, + ActionType: a.ActionType, + //BalanceType: a.BalanceType, ExtraParameters: a.ExtraParameters, ExpirationString: a.ExpirationString, Weight: a.Weight, @@ -248,7 +304,7 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) case "ActionType": parsedValue += rsrFld.ParseValue(action.ActionType) case "BalanceType": - parsedValue += rsrFld.ParseValue(action.BalanceType) + parsedValue += rsrFld.ParseValue(action.Balance.GetType()) case "BalanceUUID": parsedValue += rsrFld.ParseValue(b.Uuid) case "BalanceID": @@ -651,10 +707,10 @@ func removeAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac } func removeBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { - if _, exists := ub.BalanceMap[a.BalanceType]; !exists { + if _, exists := ub.BalanceMap[a.Balance.GetType()]; !exists { return utils.ErrNotFound } - bChain := ub.BalanceMap[a.BalanceType] + bChain := ub.BalanceMap[a.Balance.GetType()] found := false for i := 0; i < len(bChain); i++ { if bChain[i].MatchFilter(a.Balance, false) { @@ -665,7 +721,7 @@ func removeBalanceAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac found = true } } - ub.BalanceMap[a.BalanceType] = bChain + ub.BalanceMap[a.Balance.GetType()] = bChain if !found { return utils.ErrNotFound } diff --git a/engine/action_trigger.go b/engine/action_trigger.go index d9ae7b005..0fe22916c 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -19,9 +19,7 @@ along with this program. If not, see package engine import ( - "encoding/json" "fmt" - "regexp" "sort" "time" @@ -33,12 +31,12 @@ type ActionTrigger struct { UniqueID string // individual id ThresholdType string //*min_event_counter, *max_event_counter, *min_balance_counter, *max_balance_counter, *min_balance, *max_balance, *exp_balance // stats: *min_asr, *max_asr, *min_acd, *max_acd, *min_tcd, *max_tcd, *min_acc, *max_acc, *min_tcc, *max_tcc, *min_ddc, *max_ddc - ThresholdValue float64 - Recurrent bool // reset excuted flag each run - MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers - ExpirationDate time.Time - ActivationDate time.Time - BalanceType string // *monetary/*voice etc + ThresholdValue float64 + Recurrent bool // reset excuted flag each run + MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers + ExpirationDate time.Time + ActivationDate time.Time + //BalanceType string // *monetary/*voice etc Balance *BalancePointer Weight float64 ActionsId string @@ -118,41 +116,7 @@ func (at *ActionTrigger) Match(a *Action) bool { if a == nil { return true } - // if we have Id than we can draw an early conclusion - if a.Id != "" { - match, _ := regexp.MatchString(a.Id, at.ID) - return match - } - id := a.BalanceType == "" || at.BalanceType == a.BalanceType - thresholdType, thresholdValue, direction, destinationID, weight, ratingSubject, categories, sharedGroup, timings, blocker, disabled := true, true, true, true, true, true, true, true, true, true, true - if a.ExtraParameters != "" { - t := struct { - ThresholdType string - ThresholdValue float64 - DestinationIds string - BalanceDirections string - BalanceWeight float64 - BalanceRatingSubject string - BalanceCategories string - BalanceSharedGroups string - BalanceTimingTags string - BalanceBlocker bool - BalanceDisabled bool - }{} - json.Unmarshal([]byte(a.ExtraParameters), &t) - thresholdType = t.ThresholdType == "" || at.ThresholdType == t.ThresholdType - thresholdValue = t.ThresholdValue == 0 || at.ThresholdValue == t.ThresholdValue - direction = t.Balance.Directions == nil || at.Balance.Directions.Equal(utils.ParseStringMap(t.BalanceDirections)) - destinationID = len(t.DestinationIds) == 0 || at.BalanceDestinationIds.Equal(utils.ParseStringMap(t.DestinationIds)) - categories = len(t.BalanceCategories) == 0 || at.BalanceCategories.Equal(utils.ParseStringMap(t.BalanceCategories)) - timings = len(t.BalanceTimingTags) == 0 || at.BalanceTimingTags.Equal(utils.ParseStringMap(t.BalanceTimingTags)) - sharedGroup = len(t.BalanceSharedGroups) == 0 || at.BalanceSharedGroups.Equal(utils.ParseStringMap(t.BalanceSharedGroups)) - weight = t.BalanceWeight == 0 || at.BalanceWeight == t.BalanceWeight - ratingSubject = t.BalanceRatingSubject == "" || at.BalanceRatingSubject == t.BalanceRatingSubject - blocker = at.BalanceBlocker == t.BalanceBlocker - disabled = at.BalanceDisabled == t.BalanceDisabled - } - return id && direction && thresholdType && thresholdValue && destinationID && weight && ratingSubject && categories && sharedGroup && timings && blocker && disabled + return at.Balance.CreateBalance().MatchFilter(a.Balance, false) } // makes a shallow copy of the receiver @@ -162,22 +126,6 @@ func (at *ActionTrigger) Clone() *ActionTrigger { return clone } -func (at *ActionTrigger) CreateBalance() *Balance { - return &Balance{ - Id: at.UniqueID, - Directions: at.BalanceDirections, - ExpirationDate: at.BalanceExpirationDate, - DestinationIds: at.BalanceDestinationIds, - RatingSubject: at.BalanceRatingSubject, - Categories: at.BalanceCategories, - SharedGroups: at.BalanceSharedGroups, - TimingIDs: at.BalanceTimingTags, - Blocker: at.BalanceBlocker, - Disabled: at.BalanceDisabled, - Weight: at.BalanceWeight, - } -} - func (at *ActionTrigger) Equals(oat *ActionTrigger) bool { // ids only return at.ID == oat.ID && at.UniqueID == oat.UniqueID diff --git a/engine/actions_local_test.go b/engine/actions_local_test.go index 177fe19b0..bdc3b8b79 100644 --- a/engine/actions_local_test.go +++ b/engine/actions_local_test.go @@ -23,6 +23,7 @@ import ( "net/rpc" "net/rpc/jsonrpc" "path" + "strconv" "testing" "time" @@ -94,7 +95,7 @@ func TestActionsLocalSetCdrlogDebit(t *testing.T) { t.Errorf("Calling ApierV1.SetAccount received: %s", reply) } attrsAA := &utils.AttrSetActions{ActionsId: "ACTS_1", Actions: []*utils.TPAction{ - &utils.TPAction{Identifier: DEBIT, BalanceType: utils.MONETARY, Units: 5.0, ExpiryTime: UNLIMITED, Weight: 20.0}, + &utils.TPAction{Identifier: DEBIT, BalanceType: utils.MONETARY, Units: "5.0", ExpiryTime: UNLIMITED, Weight: 20.0}, &utils.TPAction{Identifier: CDRLOG}, }} if err := actsLclRpc.Call("ApierV1.SetActions", attrsAA, &reply); err != nil && err.Error() != utils.ErrExists.Error() { @@ -122,7 +123,7 @@ func TestActionsLocalSetCdrlogDebit(t *testing.T) { rcvedCdrs[0].Subject != "dan2904" || rcvedCdrs[0].Usage != "1" || rcvedCdrs[0].RunID != DEBIT || - rcvedCdrs[0].Cost != attrsAA.Actions[0].Units { + strconv.FormatFloat(rcvedCdrs[0].Cost, 'f', -1, 64) != attrsAA.Actions[0].Units { t.Errorf("Received: %+v", rcvedCdrs[0]) } } @@ -139,7 +140,7 @@ func TestActionsLocalSetCdrlogTopup(t *testing.T) { t.Errorf("Calling ApierV1.SetAccount received: %s", reply) } attrsAA := &utils.AttrSetActions{ActionsId: "ACTS_2", Actions: []*utils.TPAction{ - &utils.TPAction{Identifier: TOPUP, BalanceType: utils.MONETARY, Units: 5.0, ExpiryTime: UNLIMITED, Weight: 20.0}, + &utils.TPAction{Identifier: TOPUP, BalanceType: utils.MONETARY, Units: "5.0", ExpiryTime: UNLIMITED, Weight: 20.0}, &utils.TPAction{Identifier: CDRLOG}, }} if err := actsLclRpc.Call("ApierV1.SetActions", attrsAA, &reply); err != nil && err.Error() != utils.ErrExists.Error() { @@ -167,7 +168,7 @@ func TestActionsLocalSetCdrlogTopup(t *testing.T) { rcvedCdrs[0].Subject != "dan2905" || rcvedCdrs[0].Usage != "1" || rcvedCdrs[0].RunID != TOPUP || - rcvedCdrs[0].Cost != attrsAA.Actions[0].Units { + strconv.FormatFloat(rcvedCdrs[0].Cost, 'f', -1, 64) != attrsAA.Actions[0].Units { t.Errorf("Received: %+v", rcvedCdrs[0]) } } diff --git a/engine/actions_test.go b/engine/actions_test.go index 1eb5367d6..e0e4244db 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -409,9 +409,11 @@ func TestActionPlanCheckForASAP(t *testing.T) { func TestActionPlanLogFunction(t *testing.T) { a := &Action{ - ActionType: "*log", - BalanceType: "test", - Balance: &Balance{Value: 1.1}, + ActionType: "*log", + Balance: &BalancePointer{ + Type: utils.StringPointer("test"), + Value: utils.Float64Pointer(1.1), + }, } at := &ActionTiming{ actions: []*Action{a}, @@ -424,9 +426,11 @@ func TestActionPlanLogFunction(t *testing.T) { func TestActionPlanFunctionNotAvailable(t *testing.T) { a := &Action{ - ActionType: "VALID_FUNCTION_TYPE", - BalanceType: "test", - Balance: &Balance{Value: 1.1}, + ActionType: "VALID_FUNCTION_TYPE", + Balance: &BalancePointer{ + Type: utils.StringPointer("test"), + Value: utils.Float64Pointer(1.1), + }, } at := &ActionTiming{ accountIDs: utils.StringMap{"cgrates.org:dy": true}, @@ -527,10 +531,12 @@ func TestActionPlansRemoveMember(t *testing.T) { func TestActionTriggerMatchNil(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } var a *Action if !at.Match(a) { @@ -540,10 +546,12 @@ func TestActionTriggerMatchNil(t *testing.T) { func TestActionTriggerMatchAllBlank(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } a := &Action{} if !at.Match(a) { @@ -553,12 +561,14 @@ func TestActionTriggerMatchAllBlank(t *testing.T) { func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } - a := &Action{BalanceType: utils.MONETARY, ExtraParameters: `{"BalanceDirections":"*out"}`} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: `{"BalanceDirections":"*out"}`} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -566,10 +576,12 @@ func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) { func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } a := &Action{ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, utils.TRIGGER_MAX_BALANCE, 2)} if !at.Match(a) { @@ -579,12 +591,14 @@ func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { func TestActionTriggerMatchAllFull(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } - a := &Action{BalanceType: utils.MONETARY, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 2)} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 2)} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -592,12 +606,14 @@ func TestActionTriggerMatchAllFull(t *testing.T) { func TestActionTriggerMatchSomeFalse(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } - a := &Action{BalanceType: utils.MONETARY, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_BALANCE, 2)} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_BALANCE, 2)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -605,12 +621,14 @@ func TestActionTriggerMatchSomeFalse(t *testing.T) { func TestActionTriggerMatcBalanceFalse(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } - a := &Action{BalanceType: utils.MONETARY, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 3.0)} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 3.0)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -618,12 +636,14 @@ func TestActionTriggerMatcBalanceFalse(t *testing.T) { func TestActionTriggerMatcAllFalse(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 2, } - a := &Action{BalanceType: utils.VOICE, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_EVENT_COUNTER, 3)} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_EVENT_COUNTER, 3)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -631,16 +651,18 @@ func TestActionTriggerMatcAllFalse(t *testing.T) { func TestActionTriggerMatchAll(t *testing.T) { at := &ActionTrigger{ - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceType: utils.MONETARY, - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 2, - BalanceDestinationIds: utils.NewStringMap("NAT"), - BalanceWeight: 1.0, - BalanceRatingSubject: "test1", - BalanceSharedGroups: utils.NewStringMap("test2"), + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + RatingSubject: utils.StringPointer("test1"), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Value: utils.Float64Pointer(2), + Weight: utils.Float64Pointer(1.0), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + SharedGroups: utils.StringMapPointer(utils.NewStringMap("test2")), + }, + ThresholdType: utils.TRIGGER_MAX_BALANCE, } - a := &Action{BalanceType: utils.MONETARY, ExtraParameters: fmt.Sprintf(`{"BalanceDirections":"*out", "ThresholdType":"%v", "ThresholdValue": %v, "DestinationIds": "%v", "BalanceWeight": %v, "BalanceRatingSubject": "%v", "BalanceSharedGroup": "%v"}`, utils.TRIGGER_MAX_BALANCE, 2, "NAT", 1.0, "test1", "test2")} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"BalanceDirections":"*out", "ThresholdType":"%v", "ThresholdValue": %v, "DestinationIds": "%v", "BalanceWeight": %v, "BalanceRatingSubject": "%v", "BalanceSharedGroup": "%v"}`, utils.TRIGGER_MAX_BALANCE, 2, "NAT", 1.0, "test1", "test2")} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -663,7 +685,7 @@ func TestActionResetTriggres(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { @@ -676,7 +698,7 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.BalanceMap[utils.MONETARY][0].GetValue() == 12 { @@ -689,9 +711,9 @@ func TestActionResetTriggresActionFilter(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - resetTriggersAction(ub, nil, &Action{BalanceType: utils.SMS}, nil) + resetTriggersAction(ub, nil, &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}}, nil) if ub.ActionTriggers[0].Executed == false || ub.ActionTriggers[1].Executed == false { t.Error("Reset triggers action failed!") } @@ -702,7 +724,7 @@ func TestActionSetPostpaid(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } allowNegativeAction(ub, nil, nil, nil) if !ub.AllowNegative { @@ -716,7 +738,7 @@ func TestActionSetPrepaid(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } denyNegativeAction(ub, nil, nil, nil) if ub.AllowNegative { @@ -730,7 +752,7 @@ func TestActionResetPrepaid(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) if !ub.AllowNegative || @@ -748,7 +770,7 @@ func TestActionResetPostpaid(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) if ub.BalanceMap[utils.MONETARY].GetTotalValue() != 0 || @@ -764,9 +786,9 @@ func TestActionTopupResetCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 10 || @@ -783,10 +805,10 @@ func TestActionTopupValueFactor(t *testing.T) { BalanceMap: map[string]BalanceChain{}, } a := &Action{ - BalanceType: utils.MONETARY, - Balance: &Balance{ - Value: 10, - Directions: utils.NewStringMap(utils.OUT), + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Value: utils.Float64Pointer(10), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ExtraParameters: `{"*monetary":2.0}`, } @@ -806,7 +828,7 @@ func TestActionTopupResetCreditId(t *testing.T) { }, }, } - a := &Action{BalanceType: utils.MONETARY, Balance: &Balance{Id: "TEST_B", Value: 10, Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("TEST_B"), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -825,7 +847,7 @@ func TestActionTopupResetCreditNoId(t *testing.T) { }, }, } - a := &Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 20 || @@ -841,9 +863,9 @@ func TestActionTopupResetMinutes(t *testing.T) { utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.VOICE, Balance: &Balance{Value: 5, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 5 || @@ -860,9 +882,9 @@ func TestActionTopupCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -878,9 +900,9 @@ func TestActionTopupMinutes(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.VOICE, Balance: &Balance{Value: 5, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 15 || @@ -897,9 +919,9 @@ func TestActionDebitCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.MONETARY, Balance: &Balance{Value: 10, Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 90 || @@ -915,9 +937,9 @@ func TestActionDebitMinutes(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceType: utils.MONETARY, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.VOICE, Balance: &Balance{Value: 5, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE][0].GetValue() != 5 || @@ -939,7 +961,7 @@ func TestActionResetAllCounters(t *testing.T) { &Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT)}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, BalanceType: utils.MONETARY, ThresholdValue: 2, BalanceDestinationIds: utils.NewStringMap("NAT"), BalanceWeight: 20, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, } ub.InitCounters() resetCountersAction(ub, nil, nil, nil) @@ -967,9 +989,9 @@ func TestActionResetCounterOnlyDefault(t *testing.T) { BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.MONETARY} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}} ub.InitCounters() resetCountersAction(ub, nil, a, nil) if !ub.AllowNegative || @@ -998,9 +1020,9 @@ func TestActionResetCounterCredit(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}, &UnitCounter{BalanceType: utils.SMS, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{BalanceType: utils.MONETARY, BalanceDirections: utils.NewStringMap(utils.OUT), ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceType: utils.MONETARY} + a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}} resetCountersAction(ub, nil, a, nil) if !ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || @@ -1013,13 +1035,15 @@ func TestActionResetCounterCredit(t *testing.T) { func TestActionTriggerLogging(t *testing.T) { at := &ActionTrigger{ - ID: "some_uuid", - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - ThresholdValue: 100.0, - BalanceDestinationIds: utils.NewStringMap("NAT"), - Weight: 10.0, - ActionsId: "TEST_ACTIONS", + ID: "some_uuid", + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + }, + ThresholdValue: 100.0, + Weight: 10.0, + ActionsId: "TEST_ACTIONS", } as, err := ratingStorage.GetActions(at.ActionsId, false) if err != nil { @@ -1084,7 +1108,7 @@ func TestActionPlanLogging(t *testing.T) { } func TestActionMakeNegative(t *testing.T) { - a := &Action{Balance: &Balance{Value: 10}} + a := &Action{Balance: &BalancePointer{Value: utils.Float64Pointer(10)}} genericMakeNegative(a) if a.Balance.GetValue() > 0 { t.Error("Failed to make negative: ", a) @@ -1117,9 +1141,8 @@ func TestRemoveAction(t *testing.T) { func TestTopupAction(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minu") a := &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT), Weight: 20}, + ActionType: TOPUP, + Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1139,9 +1162,8 @@ func TestTopupAction(t *testing.T) { func TestTopupActionLoaded(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minitsboy") a := &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT), Weight: 20}, + ActionType: TOPUP, + Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1168,7 +1190,7 @@ func TestActionCdrlogEmpty(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1190,11 +1212,11 @@ func TestActionCdrlogWithParams(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1217,11 +1239,11 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &Balance{Value: 25, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1301,14 +1323,12 @@ func TestActionTransactionFuncType(t *testing.T) { Timing: &RateInterval{}, actions: []*Action{ &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Balance: &Balance{Value: 1.1}, + ActionType: TOPUP, + Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ - ActionType: "VALID_FUNCTION_TYPE", - BalanceType: "test", - Balance: &Balance{Value: 1.1}, + ActionType: "VALID_FUNCTION_TYPE", + Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer("test")}, }, }, } @@ -1339,14 +1359,12 @@ func TestActionTransactionBalanceType(t *testing.T) { Timing: &RateInterval{}, actions: []*Action{ &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Balance: &Balance{Value: 1.1}, + ActionType: TOPUP, + Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ - ActionType: TOPUP, - BalanceType: "test", - Balance: nil, + ActionType: TOPUP, + Balance: &BalancePointer{Type: utils.StringPointer("test")}, }, }, } @@ -1377,18 +1395,18 @@ func TestActionWithExpireWithoutExpire(t *testing.T) { Timing: &RateInterval{}, actions: []*Action{ &Action{ - ActionType: TOPUP, - BalanceType: utils.VOICE, - Balance: &Balance{ - Value: 15, + ActionType: TOPUP, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Value: utils.Float64Pointer(15), }, }, &Action{ - ActionType: TOPUP, - BalanceType: utils.VOICE, - Balance: &Balance{ - Value: 30, - ExpirationDate: time.Date(2025, time.November, 11, 22, 39, 0, 0, time.UTC), + ActionType: TOPUP, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Value: utils.Float64Pointer(30), + ExpirationDate: utils.TimePointer(time.Date(2025, time.November, 11, 22, 39, 0, 0, time.UTC)), }, }, }, @@ -1432,10 +1450,10 @@ func TestActionRemoveBalance(t *testing.T) { Timing: &RateInterval{}, actions: []*Action{ &Action{ - ActionType: REMOVE_BALANCE, - BalanceType: utils.MONETARY, - Balance: &Balance{ - DestinationIds: utils.NewStringMap("NAT", "RET"), + ActionType: REMOVE_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT", "RET")), }, }, }, @@ -1543,7 +1561,7 @@ func TestActionTransferMonetaryDefaultFilter(t *testing.T) { a := &Action{ ActionType: TRANSFER_MONETARY_DEFAULT, - Balance: &Balance{Weight: 20}, + Balance: &BalancePointer{Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1603,12 +1621,12 @@ func TestActionConditionalTopup(t *testing.T) { } a := &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Filter: `{"Type":"*monetary","Value":1,"Weight":10}`, - Balance: &Balance{ - Value: 11, - Weight: 30, + ActionType: TOPUP, + Filter: `{"Type":"*monetary","Value":1,"Weight":10}`, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Value: utils.Float64Pointer(11), + Weight: utils.Float64Pointer(30), }, } @@ -1667,12 +1685,12 @@ func TestActionConditionalTopupNoMatch(t *testing.T) { } a := &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Filter: `{"Type":"*monetary","Value":2,"Weight":10}`, - Balance: &Balance{ - Value: 11, - Weight: 30, + ActionType: TOPUP, + Filter: `{"Type":"*monetary","Value":2,"Weight":10}`, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Value: utils.Float64Pointer(11), + Weight: utils.Float64Pointer(30), }, } @@ -1731,12 +1749,12 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) { } a := &Action{ - ActionType: TOPUP, - BalanceType: utils.MONETARY, - Filter: `{"Type":"*voice","Value":{"*gte":100}}`, - Balance: &Balance{ - Value: 11, - Weight: 10, + ActionType: TOPUP, + Filter: `{"Type":"*voice","Value":{"*gte":100}}`, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Value: utils.Float64Pointer(11), + Weight: utils.Float64Pointer(10), }, } @@ -1832,45 +1850,45 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { } a1 := &Action{ - ActionType: "*enable_disable_balance", - BalanceType: "*sms", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &Balance{ - Weight: 10, - Disabled: true, + ActionType: "*enable_disable_balance", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &BalancePointer{ + Type: utils.StringPointer("*sms"), + Weight: utils.Float64Pointer(10), + Disabled: utils.BoolPointer(true), }, Weight: 9, } a2 := &Action{ - ActionType: "*enable_disable_balance", - BalanceType: "*sms", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &Balance{ - DestinationIds: utils.NewStringMap("FRANCE_NATIONAL"), - Weight: 10, - Disabled: true, + ActionType: "*enable_disable_balance", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &BalancePointer{ + Type: utils.StringPointer("*sms"), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), + Weight: utils.Float64Pointer(10), + Disabled: utils.BoolPointer(true), }, Weight: 8, } a3 := &Action{ - ActionType: "*enable_disable_balance", - BalanceType: "*data", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &Balance{ - RatingSubject: "for_v3hsillmilld500m_data_forfait", - Weight: 10, - Disabled: true, + ActionType: "*enable_disable_balance", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &BalancePointer{ + Type: utils.StringPointer("*data"), + RatingSubject: utils.StringPointer("for_v3hsillmilld500m_data_forfait"), + Weight: utils.Float64Pointer(10), + Disabled: utils.BoolPointer(true), }, Weight: 7, } a4 := &Action{ - ActionType: "*enable_disable_balance", - BalanceType: "*voice", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &Balance{ - DestinationIds: utils.NewStringMap("FRANCE_NATIONAL"), - Weight: 10, - Disabled: true, + ActionType: "*enable_disable_balance", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &BalancePointer{ + Type: utils.StringPointer("*voice"), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), + Weight: utils.Float64Pointer(10), + Disabled: utils.BoolPointer(true), }, Weight: 6, } @@ -1937,12 +1955,12 @@ func TestActionSetBalance(t *testing.T) { } a := &Action{ - ActionType: SET_BALANCE, - BalanceType: utils.MONETARY, - Balance: &Balance{ - Id: "m2", - Value: 11, - Weight: 10, + ActionType: SET_BALANCE, + Balance: &BalancePointer{ + Id: utils.StringPointer("m2"), + Type: utils.StringPointer(utils.MONETARY), + Value: utils.Float64Pointer(11), + Weight: utils.Float64Pointer(10), }, } diff --git a/engine/balances.go b/engine/balances.go index b1a9fba96..38dd1e8e4 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -94,6 +94,28 @@ func (b *Balance) MatchFilter(o *BalancePointer, skipIds bool) bool { (o.RatingSubject == nil || b.RatingSubject == *o.RatingSubject) } +func (b *Balance) HardMatchFilter(o *BalancePointer, skipIds bool) bool { + if o == nil { + return true + } + if !skipIds && o.Uuid != nil && *o.Uuid != "" { + return b.Uuid == *o.Uuid + } + if !skipIds && o.Id != nil && *o.Id != "" { + return b.Id == *o.Id + } + return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && + (o.Weight == nil || b.Weight == *o.Weight) && + (o.Blocker != nil || b.Blocker == *o.Blocker) && + (o.Disabled == nil || b.Disabled == *o.Disabled) && + (o.DestinationIds == nil || b.DestinationIds.Equal(*o.DestinationIds)) && + (o.Directions == nil || b.Directions.Equal(*o.Directions)) && + (o.Categories == nil || b.Categories.Equal(*o.Categories)) && + (o.TimingIDs == nil || b.TimingIDs.Equal(*o.TimingIDs)) && + (o.SharedGroups == nil || b.SharedGroups.Equal(*o.SharedGroups)) && + (o.RatingSubject == nil || b.RatingSubject == *o.RatingSubject) +} + func (b *Balance) MatchCCFilter(cc *CallCost) bool { if len(b.Categories) > 0 && cc.Category != "" && b.Categories[cc.Category] == false { return false @@ -174,47 +196,7 @@ func (b *Balance) MatchDestination(destinationId string) bool { } func (b *Balance) MatchActionTrigger(at *ActionTrigger) bool { - if at.BalanceId != "" { - return b.Id == at.BalanceId - } - matchesDestination := true - if len(at.BalanceDestinationIds) != 0 { - matchesDestination = (b.DestinationIds.Equal(at.BalanceDestinationIds)) - } - matchesDirection := true - if len(at.BalanceDirections) != 0 { - matchesDirection = (b.Directions.Equal(at.BalanceDirections)) - } - matchesExpirationDate := true - if !at.BalanceExpirationDate.IsZero() { - matchesExpirationDate = (at.BalanceExpirationDate.Equal(b.ExpirationDate)) - } - matchesWeight := true - if at.BalanceWeight > 0 { - matchesWeight = (at.BalanceWeight == b.Weight) - } - matchesRatingSubject := true - if at.BalanceRatingSubject != "" { - matchesRatingSubject = (at.BalanceRatingSubject == b.RatingSubject) - } - - matchesSharedGroup := true - if len(at.BalanceSharedGroups) != 0 { - matchesSharedGroup = at.BalanceSharedGroups.Equal(b.SharedGroups) - } - - matchesTiming := true - if len(at.BalanceTimingTags) != 0 { - matchesTiming = at.BalanceTimingTags.Equal(b.TimingIDs) - } - - return matchesDestination && - matchesDirection && - matchesExpirationDate && - matchesWeight && - matchesRatingSubject && - matchesSharedGroup && - matchesTiming + return b.HardMatchFilter(at.Balance, false) } func (b *Balance) Clone() *Balance { diff --git a/engine/balances_test.go b/engine/balances_test.go index 163ab79ec..09c9dd17f 100644 --- a/engine/balances_test.go +++ b/engine/balances_test.go @@ -90,7 +90,7 @@ func TestBalanceEqual(t *testing.T) { func TestBalanceMatchFilter(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &Balance{Weight: 1, precision: 1, RatingSubject: "", DestinationIds: utils.StringMap{}} + mb2 := &BalancePointer{Weight: utils.Float64Pointer(1), RatingSubject: nil, DestinationIds: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -98,7 +98,7 @@ func TestBalanceMatchFilter(t *testing.T) { func TestBalanceMatchFilterEmpty(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &Balance{} + mb2 := &BalancePointer{} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -106,7 +106,7 @@ func TestBalanceMatchFilterEmpty(t *testing.T) { func TestBalanceMatchFilterId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 2, precision: 2, RatingSubject: "2", DestinationIds: utils.NewStringMap("NAT")} - mb2 := &Balance{Id: "T1", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} + mb2 := &BalancePointer{Id: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -114,7 +114,7 @@ func TestBalanceMatchFilterId(t *testing.T) { func TestBalanceMatchFilterDiffId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &Balance{Id: "T2", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} + mb2 := &BalancePointer{Id: utils.StringPointer("T2"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} if mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v != %+v", mb1, mb2) } @@ -129,7 +129,7 @@ func TestBalanceClone(t *testing.T) { } func TestBalanceMatchActionTriggerId(t *testing.T) { - at := &ActionTrigger{BalanceId: "test"} + at := &ActionTrigger{Balance: &BalancePointer{Id: utils.StringPointer("test")}} b := &Balance{Id: "test"} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -143,14 +143,14 @@ func TestBalanceMatchActionTriggerId(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.Id = "test" - at.BalanceId = "" + at.Balance.Id = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } } func TestBalanceMatchActionTriggerDestination(t *testing.T) { - at := &ActionTrigger{BalanceDestinationIds: utils.NewStringMap("test")} + at := &ActionTrigger{Balance: &BalancePointer{DestinationIds: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{DestinationIds: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -164,14 +164,14 @@ func TestBalanceMatchActionTriggerDestination(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.DestinationIds = utils.NewStringMap("test") - at.BalanceDestinationIds = utils.NewStringMap("") + at.Balance.DestinationIds = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } } func TestBalanceMatchActionTriggerWeight(t *testing.T) { - at := &ActionTrigger{BalanceWeight: 1} + at := &ActionTrigger{Balance: &BalancePointer{Weight: utils.Float64Pointer(1)}} b := &Balance{Weight: 1} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -185,14 +185,14 @@ func TestBalanceMatchActionTriggerWeight(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.Weight = 1 - at.BalanceWeight = 0 + at.Balance.Weight = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } } func TestBalanceMatchActionTriggerRatingSubject(t *testing.T) { - at := &ActionTrigger{BalanceRatingSubject: "test"} + at := &ActionTrigger{Balance: &BalancePointer{RatingSubject: utils.StringPointer("test")}} b := &Balance{RatingSubject: "test"} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -206,14 +206,14 @@ func TestBalanceMatchActionTriggerRatingSubject(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.RatingSubject = "test" - at.BalanceRatingSubject = "" + at.Balance.RatingSubject = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } } func TestBalanceMatchActionTriggerSharedGroup(t *testing.T) { - at := &ActionTrigger{BalanceSharedGroups: utils.NewStringMap("test")} + at := &ActionTrigger{Balance: &BalancePointer{SharedGroups: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{SharedGroups: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -227,7 +227,7 @@ func TestBalanceMatchActionTriggerSharedGroup(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.SharedGroups = utils.NewStringMap("test") - at.BalanceSharedGroups = utils.NewStringMap("") + at.Balance.SharedGroups = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index f470910ae..e4d5cdc9e 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -41,12 +41,12 @@ func init() { func populateDB() { ats := []*Action{ - &Action{ActionType: "*topup", BalanceType: utils.MONETARY, Balance: &Balance{Value: 10}}, - &Action{ActionType: "*topup", BalanceType: utils.VOICE, Balance: &Balance{Weight: 20, Value: 10, DestinationIds: utils.NewStringMap("NAT")}}, + &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}}, + &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: utils.Float64Pointer(10), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, } ats1 := []*Action{ - &Action{ActionType: "*topup", BalanceType: utils.MONETARY, Balance: &Balance{Value: 10}, Weight: 10}, + &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}, Weight: 10}, &Action{ActionType: "*reset_account", Weight: 20}, } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index ffb85bbae..4cb27daf0 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -823,38 +823,38 @@ func TestLoadActions(t *testing.T) { &Action{ Id: "MINI0", ActionType: TOPUP_RESET, - BalanceType: utils.MONETARY, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, - Balance: &Balance{ + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), Uuid: as1[0].Balance.Uuid, - Directions: utils.NewStringMap(utils.OUT), - Value: 10, - Weight: 10, - DestinationIds: utils.StringMap{}, - TimingIDs: utils.StringMap{}, - SharedGroups: utils.StringMap{}, - Categories: utils.StringMap{}, + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Value: utils.Float64Pointer(10), + Weight: utils.Float64Pointer(10), + DestinationIds: nil, + TimingIDs: nil, + SharedGroups: nil, + Categories: nil, }, }, &Action{ Id: "MINI1", ActionType: TOPUP, - BalanceType: utils.VOICE, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, - Balance: &Balance{ + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), Uuid: as1[1].Balance.Uuid, - Directions: utils.NewStringMap(utils.OUT), - Value: 100, - Weight: 10, - RatingSubject: "test", - DestinationIds: utils.NewStringMap("NAT"), - TimingIDs: utils.StringMap{}, - SharedGroups: utils.StringMap{}, - Categories: utils.StringMap{}, + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Value: utils.Float64Pointer(100), + Weight: utils.Float64Pointer(10), + RatingSubject: utils.StringPointer("test"), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + TimingIDs: nil, + SharedGroups: nil, + Categories: nil, }, }, } @@ -866,18 +866,18 @@ func TestLoadActions(t *testing.T) { &Action{ Id: "SHARED0", ActionType: TOPUP, - BalanceType: utils.MONETARY, ExpirationString: UNLIMITED, Weight: 10, - Balance: &Balance{ - Directions: utils.NewStringMap(utils.OUT), - DestinationIds: utils.StringMap{}, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: nil, Uuid: as2[0].Balance.Uuid, - Value: 100, - Weight: 10, - SharedGroups: utils.NewStringMap("SG1"), - TimingIDs: utils.StringMap{}, - Categories: utils.StringMap{}, + Value: utils.Float64Pointer(100), + Weight: utils.Float64Pointer(10), + SharedGroups: utils.StringMapPointer(utils.NewStringMap("SG1")), + TimingIDs: nil, + Categories: nil, }, }, } @@ -891,14 +891,14 @@ func TestLoadActions(t *testing.T) { ActionType: CDRLOG, ExtraParameters: `{"Category":"^ddi","MediationRunId":"^did_run"}`, Weight: 10, - Balance: &Balance{ + Balance: &BalancePointer{ Uuid: as3[0].Balance.Uuid, - Directions: utils.StringMap{}, - DestinationIds: utils.StringMap{}, - TimingIDs: utils.StringMap{}, - Categories: utils.StringMap{}, - SharedGroups: utils.StringMap{}, - Blocker: false, + Directions: nil, + DestinationIds: nil, + TimingIDs: nil, + Categories: nil, + SharedGroups: nil, + Blocker: utils.BoolPointer(false), }, }, } @@ -1043,38 +1043,42 @@ func TestLoadActionTriggers(t *testing.T) { } atr := csvr.actionsTriggers["STANDARD_TRIGGER"][0] expected := &ActionTrigger{ - ID: "STANDARD_TRIGGER", - UniqueID: "st0", - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, - ThresholdValue: 10, - BalanceDestinationIds: utils.NewStringMap("GERMANY_O2"), - BalanceCategories: utils.StringMap{}, - BalanceTimingTags: utils.StringMap{}, - BalanceSharedGroups: utils.StringMap{}, - Weight: 10, - ActionsId: "SOME_1", - Executed: false, + ID: "STANDARD_TRIGGER", + UniqueID: "st0", + ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, + ThresholdValue: 10, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), + Categories: nil, + TimingIDs: nil, + SharedGroups: nil, + }, + Weight: 10, + ActionsId: "SOME_1", + Executed: false, } if !reflect.DeepEqual(atr, expected) { t.Errorf("Error loading action trigger: %+v", atr) } atr = csvr.actionsTriggers["STANDARD_TRIGGER"][1] expected = &ActionTrigger{ - ID: "STANDARD_TRIGGER", - UniqueID: "st1", - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - ThresholdType: utils.TRIGGER_MAX_BALANCE, - ThresholdValue: 200, - BalanceDestinationIds: utils.NewStringMap("GERMANY"), - BalanceCategories: utils.StringMap{}, - BalanceTimingTags: utils.StringMap{}, - BalanceSharedGroups: utils.StringMap{}, - Weight: 10, - ActionsId: "SOME_2", - Executed: false, + ID: "STANDARD_TRIGGER", + UniqueID: "st1", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + ThresholdValue: 200, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY")), + Categories: nil, + TimingIDs: nil, + SharedGroups: nil, + }, + Weight: 10, + ActionsId: "SOME_2", + Executed: false, } if !reflect.DeepEqual(atr, expected) { t.Errorf("Error loading action trigger: %+v", atr) @@ -1098,9 +1102,9 @@ func TestLoadAccountActions(t *testing.T) { Value: 0, Directions: utils.NewStringMap("*out"), DestinationIds: utils.NewStringMap("GERMANY_O2"), - SharedGroups: utils.StringMap{}, - Categories: utils.StringMap{}, - TimingIDs: utils.StringMap{}, + SharedGroups: nil, + Categories: nil, + TimingIDs: nil, }, }, }, @@ -1134,7 +1138,7 @@ func TestLoadDerivedChargers(t *testing.T) { t.Error("Failed to load derivedChargers: ", csvr.derivedChargers) } expCharger1 := &utils.DerivedChargers{ - DestinationIDs: utils.StringMap{}, + DestinationIDs: nil, Chargers: []*utils.DerivedCharger{ &utils.DerivedCharger{RunID: "extra1", RunFilters: "^filteredHeader1/filterValue1/", RequestTypeField: "^prepaid", DirectionField: utils.META_DEFAULT, TenantField: utils.META_DEFAULT, CategoryField: utils.META_DEFAULT, AccountField: "rif", SubjectField: "rif", DestinationField: utils.META_DEFAULT, diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 90f1986c3..085012a37 100644 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -240,26 +240,26 @@ func TestTPActionsAsExportSlice(t *testing.T) { Identifier: "*topup_reset", BalanceType: "*monetary", Directions: utils.OUT, - Units: 5.0, + Units: "5.0", ExpiryTime: "*never", DestinationIds: "*any", RatingSubject: "special1", Categories: "call", SharedGroups: "GROUP1", - BalanceWeight: 10.0, + BalanceWeight: "10.0", ExtraParameters: "", Weight: 10.0}, &utils.TPAction{ Identifier: "*http_post", BalanceType: "", Directions: "", - Units: 0.0, + Units: "0.0", ExpiryTime: "", DestinationIds: "", RatingSubject: "", Categories: "", SharedGroups: "", - BalanceWeight: 0.0, + BalanceWeight: "0.0", ExtraParameters: "http://localhost/¶m1=value1", Weight: 20.0}, }, @@ -561,14 +561,14 @@ func TestTPActionPlanAsExportSlice(t *testing.T) { BalanceType: "*monetary", BalanceDirections: "*out", BalanceDestinationIds: "", - BalanceWeight: 0.0, + BalanceWeight: "0.0", BalanceExpirationDate: "*never", BalanceTimingTags: "T1", BalanceRatingSubject: "special1", BalanceCategories: "call", BalanceSharedGroups: "SHARED_1", - BalanceBlocker: false, - BalanceDisabled: false, + BalanceBlocker: "false", + BalanceDisabled: "false", MinQueuedItems: 0, ActionsId: "LOG_WARNING", Weight: 10}, @@ -583,14 +583,14 @@ func TestTPActionPlanAsExportSlice(t *testing.T) { BalanceType: "*monetary", BalanceDirections: "*out", BalanceDestinationIds: "FS_USERS", - BalanceWeight: 0.0, + BalanceWeight: "0.0", BalanceExpirationDate: "*never", BalanceTimingTags: "T1", BalanceRatingSubject: "special1", BalanceCategories: "call", BalanceSharedGroups: "SHARED_1", - BalanceBlocker: false, - BalanceDisabled: false, + BalanceBlocker: "false", + BalanceDisabled: "false", MinQueuedItems: 0, ActionsId: "LOG_WARNING", Weight: 10}, diff --git a/engine/storage_test.go b/engine/storage_test.go index 98a312739..1a38f6bd2 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -315,13 +315,15 @@ func GetUB() *Account { Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, } at := &ActionTrigger{ - ID: "some_uuid", - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - ThresholdValue: 100.0, - BalanceDestinationIds: utils.NewStringMap("NAT"), - Weight: 10.0, - ActionsId: "Commando", + ID: "some_uuid", + ThresholdValue: 100.0, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + }, + Weight: 10.0, + ActionsId: "Commando", } var zeroTime time.Time zeroTime = zeroTime.UTC() // for deep equal to find location diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 17c7706d1..27075be55 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -514,9 +514,9 @@ func (tpr *TpReader) LoadActions() (err error) { } } acts[idx] = &Action{ - Id: tag + strconv.Itoa(idx), - ActionType: tpact.Identifier, - BalanceType: tpact.BalanceType, + Id: tag + strconv.Itoa(idx), + ActionType: tpact.Identifier, + //BalanceType: tpact.BalanceType, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, @@ -526,6 +526,9 @@ func (tpr *TpReader) LoadActions() (err error) { if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) } + if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { + acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) + } if tpact.Units != "" && tpact.Units != utils.ANY { u, err := strconv.ParseFloat(tpact.Units, 64) @@ -542,6 +545,13 @@ func (tpr *TpReader) LoadActions() (err error) { } acts[idx].Balance.Weight = utils.Float64Pointer(u) } + if tpact.ExpiryTime != "" && tpact.ExpiryTime != utils.ANY { + u, err := utils.ParseTimeDetectLayout(tpact.ExpiryTime, tpr.timezone) + if err != nil { + return err + } + acts[idx].Balance.ExpirationDate = utils.TimePointer(u) + } if tpact.RatingSubject != "" && tpact.RatingSubject != utils.ANY { acts[idx].Balance.RatingSubject = utils.StringPointer(tpact.RatingSubject) } @@ -666,10 +676,6 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { for key, atrsLst := range storAts { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, atr := range atrsLst { - balanceExpirationDate, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) - if err != nil { - return err - } expirationDate, err := utils.ParseTimeDetectLayout(atr.ExpirationDate, tpr.timezone) if err != nil { return err @@ -686,29 +692,73 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { atr.UniqueID = utils.GenUUID() } atrs[idx] = &ActionTrigger{ - ID: key, - UniqueID: atr.UniqueID, - ThresholdType: atr.ThresholdType, - ThresholdValue: atr.ThresholdValue, - Recurrent: atr.Recurrent, - MinSleep: minSleep, - ExpirationDate: expirationDate, - ActivationDate: activationDate, - BalanceId: atr.BalanceId, - BalanceType: atr.BalanceType, - BalanceDirections: utils.ParseStringMap(atr.BalanceDirections), - BalanceDestinationIds: utils.ParseStringMap(atr.BalanceDestinationIds), - BalanceWeight: atr.BalanceWeight, - BalanceExpirationDate: balanceExpirationDate, - BalanceTimingTags: utils.ParseStringMap(atr.BalanceTimingTags), - BalanceRatingSubject: atr.BalanceRatingSubject, - BalanceCategories: utils.ParseStringMap(atr.BalanceCategories), - BalanceSharedGroups: utils.ParseStringMap(atr.BalanceSharedGroups), - BalanceBlocker: atr.BalanceBlocker, - BalanceDisabled: atr.BalanceDisabled, - Weight: atr.Weight, - ActionsId: atr.ActionsId, - MinQueuedItems: atr.MinQueuedItems, + ID: key, + UniqueID: atr.UniqueID, + ThresholdType: atr.ThresholdType, + ThresholdValue: atr.ThresholdValue, + Recurrent: atr.Recurrent, + MinSleep: minSleep, + ExpirationDate: expirationDate, + ActivationDate: activationDate, + Balance: &BalancePointer{}, + Weight: atr.Weight, + ActionsId: atr.ActionsId, + MinQueuedItems: atr.MinQueuedItems, + } + if atr.BalanceId != "" && atr.BalanceId != utils.ANY { + atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + } + + if atr.BalanceType != "" && atr.BalanceType != utils.ANY { + atrs[idx].Balance.Type = utils.StringPointer(atr.BalanceType) + } + + if atr.BalanceWeight != "" && atr.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(atr.BalanceWeight, 64) + if err != nil { + return err + } + atrs[idx].Balance.Weight = utils.Float64Pointer(u) + } + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) + if err != nil { + return err + } + atrs[idx].Balance.ExpirationDate = utils.TimePointer(u) + } + if atr.BalanceRatingSubject != "" && atr.BalanceRatingSubject != utils.ANY { + atrs[idx].Balance.RatingSubject = utils.StringPointer(atr.BalanceRatingSubject) + } + + if atr.BalanceCategories != "" && atr.BalanceCategories != utils.ANY { + atrs[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceCategories)) + } + if atr.BalanceDirections != "" && atr.BalanceDirections != utils.ANY { + atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) + } + if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { + atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + } + if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { + atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) + } + if atr.BalanceTimingTags != "" && atr.BalanceTimingTags != utils.ANY { + atrs[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceTimingTags)) + } + if atr.BalanceBlocker != "" && atr.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceBlocker) + if err != nil { + return err + } + atrs[idx].Balance.Blocker = utils.BoolPointer(u) + } + if atr.BalanceDisabled != "" && atr.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceDisabled) + if err != nil { + return err + } + atrs[idx].Balance.Disabled = utils.BoolPointer(u) } } tpr.actionsTriggers[key] = atrs @@ -835,37 +885,80 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error atrsMap := make(map[string][]*ActionTrigger) for key, atrsLst := range atrs { atrs := make([]*ActionTrigger, len(atrsLst)) - for idx, apiAtr := range atrsLst { - minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep) - balanceExpTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) - expTime, _ := utils.ParseTimeDetectLayout(apiAtr.ExpirationDate, tpr.timezone) - actTime, _ := utils.ParseTimeDetectLayout(apiAtr.ActivationDate, tpr.timezone) - if apiAtr.UniqueID == "" { - apiAtr.UniqueID = utils.GenUUID() + for idx, atr := range atrsLst { + minSleep, _ := utils.ParseDurationWithSecs(atr.MinSleep) + expTime, _ := utils.ParseTimeDetectLayout(atr.ExpirationDate, tpr.timezone) + actTime, _ := utils.ParseTimeDetectLayout(atr.ActivationDate, tpr.timezone) + if atr.UniqueID == "" { + atr.UniqueID = utils.GenUUID() } atrs[idx] = &ActionTrigger{ - ID: key, - UniqueID: apiAtr.UniqueID, - ThresholdType: apiAtr.ThresholdType, - ThresholdValue: apiAtr.ThresholdValue, - Recurrent: apiAtr.Recurrent, - MinSleep: minSleep, - ExpirationDate: expTime, - ActivationDate: actTime, - BalanceId: apiAtr.BalanceId, - BalanceType: apiAtr.BalanceType, - BalanceDirections: utils.ParseStringMap(apiAtr.BalanceDirections), - BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds), - BalanceWeight: apiAtr.BalanceWeight, - BalanceExpirationDate: balanceExpTime, - BalanceTimingTags: utils.ParseStringMap(apiAtr.BalanceTimingTags), - BalanceRatingSubject: apiAtr.BalanceRatingSubject, - BalanceCategories: utils.ParseStringMap(apiAtr.BalanceCategories), - BalanceSharedGroups: utils.ParseStringMap(apiAtr.BalanceSharedGroups), - BalanceBlocker: apiAtr.BalanceBlocker, - BalanceDisabled: apiAtr.BalanceDisabled, - Weight: apiAtr.Weight, - ActionsId: apiAtr.ActionsId, + ID: key, + UniqueID: atr.UniqueID, + ThresholdType: atr.ThresholdType, + ThresholdValue: atr.ThresholdValue, + Recurrent: atr.Recurrent, + MinSleep: minSleep, + ExpirationDate: expTime, + ActivationDate: actTime, + Balance: &BalancePointer{}, + Weight: atr.Weight, + ActionsId: atr.ActionsId, + } + if atr.BalanceId != "" && atr.BalanceId != utils.ANY { + atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + } + + if atr.BalanceType != "" && atr.BalanceType != utils.ANY { + atrs[idx].Balance.Type = utils.StringPointer(atr.BalanceType) + } + + if atr.BalanceWeight != "" && atr.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(atr.BalanceWeight, 64) + if err != nil { + return err + } + atrs[idx].Balance.Weight = utils.Float64Pointer(u) + } + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) + if err != nil { + return err + } + atrs[idx].Balance.ExpirationDate = utils.TimePointer(u) + } + if atr.BalanceRatingSubject != "" && atr.BalanceRatingSubject != utils.ANY { + atrs[idx].Balance.RatingSubject = utils.StringPointer(atr.BalanceRatingSubject) + } + + if atr.BalanceCategories != "" && atr.BalanceCategories != utils.ANY { + atrs[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceCategories)) + } + if atr.BalanceDirections != "" && atr.BalanceDirections != utils.ANY { + atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) + } + if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { + atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + } + if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { + atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) + } + if atr.BalanceTimingTags != "" && atr.BalanceTimingTags != utils.ANY { + atrs[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceTimingTags)) + } + if atr.BalanceBlocker != "" && atr.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceBlocker) + if err != nil { + return err + } + atrs[idx].Balance.Blocker = utils.BoolPointer(u) + } + if atr.BalanceDisabled != "" && atr.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceDisabled) + if err != nil { + return err + } + atrs[idx].Balance.Disabled = utils.BoolPointer(u) } } atrsMap[key] = atrs @@ -883,7 +976,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error } // actions - acts := make(map[string][]*Action) + facts := make(map[string][]*Action) for _, actId := range actionsIds { tpas, err := tpr.lr.GetTpActions(tpr.tpid, actId) if err != nil { @@ -894,7 +987,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error return err } for tag, tpacts := range as { - enacts := make([]*Action, len(tpacts)) + acts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { // check filter field if len(tpact.Filter) > 0 { @@ -902,34 +995,95 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error return fmt.Errorf("error parsing action %s filter field: %v", tag, err) } } - enacts[idx] = &Action{ - Id: tag + strconv.Itoa(idx), - ActionType: tpact.Identifier, - BalanceType: tpact.BalanceType, + acts[idx] = &Action{ + Id: tag + strconv.Itoa(idx), + ActionType: tpact.Identifier, + //BalanceType: tpact.BalanceType, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &Balance{ - Id: tpact.BalanceId, - Value: tpact.Units, - Weight: tpact.BalanceWeight, - RatingSubject: tpact.RatingSubject, - Categories: utils.ParseStringMap(tpact.Categories), - Directions: utils.ParseStringMap(tpact.Directions), - DestinationIds: utils.ParseStringMap(tpact.DestinationIds), - SharedGroups: utils.ParseStringMap(tpact.SharedGroups), - TimingIDs: utils.ParseStringMap(tpact.TimingTags), - Blocker: tpact.BalanceBlocker, - Disabled: tpact.BalanceDisabled, - }, + Balance: &BalancePointer{}, + } + if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { + acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + } + if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { + acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) + } + + if tpact.Units != "" && tpact.Units != utils.ANY { + u, err := strconv.ParseFloat(tpact.Units, 64) + if err != nil { + return err + } + acts[idx].Balance.Value = utils.Float64Pointer(u) + } + + if tpact.BalanceWeight != "" && tpact.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(tpact.BalanceWeight, 64) + if err != nil { + return err + } + acts[idx].Balance.Weight = utils.Float64Pointer(u) + } + if tpact.RatingSubject != "" && tpact.RatingSubject != utils.ANY { + acts[idx].Balance.RatingSubject = utils.StringPointer(tpact.RatingSubject) + } + + if tpact.Categories != "" && tpact.Categories != utils.ANY { + acts[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(tpact.Categories)) + } + if tpact.Directions != "" && tpact.Directions != utils.ANY { + acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) + } + if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { + acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + } + if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { + acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) + } + if tpact.TimingTags != "" && tpact.TimingTags != utils.ANY { + acts[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.TimingTags)) + } + if tpact.BalanceBlocker != "" && tpact.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceBlocker) + if err != nil { + return err + } + acts[idx].Balance.Blocker = utils.BoolPointer(u) + } + if tpact.BalanceDisabled != "" && tpact.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceDisabled) + if err != nil { + return err + } + acts[idx].Balance.Disabled = utils.BoolPointer(u) + } + // load action timings from tags + if tpact.TimingTags != "" { + timingIds := strings.Split(tpact.TimingTags, utils.INFIELD_SEP) + for _, timingID := range timingIds { + if timing, found := tpr.timings[timingID]; found { + acts[idx].Balance.Timings = append(acts[idx].Balance.Timings, &RITiming{ + Years: timing.Years, + Months: timing.Months, + MonthDays: timing.MonthDays, + WeekDays: timing.WeekDays, + StartTime: timing.StartTime, + EndTime: timing.EndTime, + }) + } else { + return fmt.Errorf("could not find timing: %v", timingID) + } + } } } - acts[tag] = enacts + facts[tag] = acts } } // write actions - for k, as := range acts { + for k, as := range facts { err = tpr.ratingStorage.SetActions(k, as) if err != nil { return err @@ -1068,35 +1222,80 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { for _, atrsLst := range atrsM { atrs := make([]*ActionTrigger, len(atrsLst)) - for idx, apiAtr := range atrsLst { - minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep) - balanceExpTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) - expTime, _ := utils.ParseTimeDetectLayout(apiAtr.ExpirationDate, tpr.timezone) - actTime, _ := utils.ParseTimeDetectLayout(apiAtr.ActivationDate, tpr.timezone) - if apiAtr.UniqueID == "" { - apiAtr.UniqueID = utils.GenUUID() + for idx, atr := range atrsLst { + minSleep, _ := utils.ParseDurationWithSecs(atr.MinSleep) + expTime, _ := utils.ParseTimeDetectLayout(atr.ExpirationDate, tpr.timezone) + actTime, _ := utils.ParseTimeDetectLayout(atr.ActivationDate, tpr.timezone) + if atr.UniqueID == "" { + atr.UniqueID = utils.GenUUID() } atrs[idx] = &ActionTrigger{ - ID: triggerTag, - UniqueID: apiAtr.UniqueID, - ThresholdType: apiAtr.ThresholdType, - ThresholdValue: apiAtr.ThresholdValue, - Recurrent: apiAtr.Recurrent, - MinSleep: minSleep, - ExpirationDate: expTime, - ActivationDate: actTime, - BalanceId: apiAtr.BalanceId, - BalanceType: apiAtr.BalanceType, - BalanceDirections: utils.ParseStringMap(apiAtr.BalanceDirections), - BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds), - BalanceWeight: apiAtr.BalanceWeight, - BalanceExpirationDate: balanceExpTime, - BalanceRatingSubject: apiAtr.BalanceRatingSubject, - BalanceCategories: utils.ParseStringMap(apiAtr.BalanceCategories), - BalanceSharedGroups: utils.ParseStringMap(apiAtr.BalanceSharedGroups), - BalanceTimingTags: utils.ParseStringMap(apiAtr.BalanceTimingTags), - Weight: apiAtr.Weight, - ActionsId: apiAtr.ActionsId, + ID: triggerTag, + UniqueID: atr.UniqueID, + ThresholdType: atr.ThresholdType, + ThresholdValue: atr.ThresholdValue, + Recurrent: atr.Recurrent, + MinSleep: minSleep, + ExpirationDate: expTime, + ActivationDate: actTime, + Balance: &BalancePointer{}, + Weight: atr.Weight, + ActionsId: atr.ActionsId, + } + if atr.BalanceId != "" && atr.BalanceId != utils.ANY { + atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + } + + if atr.BalanceType != "" && atr.BalanceType != utils.ANY { + atrs[idx].Balance.Type = utils.StringPointer(atr.BalanceType) + } + + if atr.BalanceWeight != "" && atr.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(atr.BalanceWeight, 64) + if err != nil { + return err + } + atrs[idx].Balance.Weight = utils.Float64Pointer(u) + } + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) + if err != nil { + return err + } + atrs[idx].Balance.ExpirationDate = utils.TimePointer(u) + } + if atr.BalanceRatingSubject != "" && atr.BalanceRatingSubject != utils.ANY { + atrs[idx].Balance.RatingSubject = utils.StringPointer(atr.BalanceRatingSubject) + } + + if atr.BalanceCategories != "" && atr.BalanceCategories != utils.ANY { + atrs[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceCategories)) + } + if atr.BalanceDirections != "" && atr.BalanceDirections != utils.ANY { + atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) + } + if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { + atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + } + if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { + atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) + } + if atr.BalanceTimingTags != "" && atr.BalanceTimingTags != utils.ANY { + atrs[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceTimingTags)) + } + if atr.BalanceBlocker != "" && atr.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceBlocker) + if err != nil { + return err + } + atrs[idx].Balance.Blocker = utils.BoolPointer(u) + } + if atr.BalanceDisabled != "" && atr.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(atr.BalanceDisabled) + if err != nil { + return err + } + atrs[idx].Balance.Disabled = utils.BoolPointer(u) } } tpr.actionsTriggers[triggerTag] = atrs @@ -1134,7 +1333,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { return err } for tag, tpacts := range as { - enacts := make([]*Action, len(tpacts)) + acts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { // check filter field if len(tpact.Filter) > 0 { @@ -1142,30 +1341,74 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { return fmt.Errorf("error parsing action %s filter field: %v", tag, err) } } - enacts[idx] = &Action{ - Id: tag + strconv.Itoa(idx), - ActionType: tpact.Identifier, - BalanceType: tpact.BalanceType, + acts[idx] = &Action{ + Id: tag + strconv.Itoa(idx), + ActionType: tpact.Identifier, + //BalanceType: tpact.BalanceType, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &Balance{ - Id: tpact.BalanceId, - Value: tpact.Units, - Weight: tpact.BalanceWeight, - RatingSubject: tpact.RatingSubject, - Categories: utils.ParseStringMap(tpact.Categories), - Directions: utils.ParseStringMap(tpact.Directions), - DestinationIds: utils.ParseStringMap(tpact.DestinationIds), - SharedGroups: utils.ParseStringMap(tpact.SharedGroups), - TimingIDs: utils.ParseStringMap(tpact.TimingTags), - Blocker: tpact.BalanceBlocker, - Disabled: tpact.BalanceDisabled, - }, + Balance: &BalancePointer{}, } + if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { + acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + } + if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { + acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) + } + + if tpact.Units != "" && tpact.Units != utils.ANY { + u, err := strconv.ParseFloat(tpact.Units, 64) + if err != nil { + return err + } + acts[idx].Balance.Value = utils.Float64Pointer(u) + } + + if tpact.BalanceWeight != "" && tpact.BalanceWeight != utils.ANY { + u, err := strconv.ParseFloat(tpact.BalanceWeight, 64) + if err != nil { + return err + } + acts[idx].Balance.Weight = utils.Float64Pointer(u) + } + if tpact.RatingSubject != "" && tpact.RatingSubject != utils.ANY { + acts[idx].Balance.RatingSubject = utils.StringPointer(tpact.RatingSubject) + } + + if tpact.Categories != "" && tpact.Categories != utils.ANY { + acts[idx].Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(tpact.Categories)) + } + if tpact.Directions != "" && tpact.Directions != utils.ANY { + acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) + } + if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { + acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + } + if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { + acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) + } + if tpact.TimingTags != "" && tpact.TimingTags != utils.ANY { + acts[idx].Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.TimingTags)) + } + if tpact.BalanceBlocker != "" && tpact.BalanceBlocker != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceBlocker) + if err != nil { + return err + } + acts[idx].Balance.Blocker = utils.BoolPointer(u) + } + if tpact.BalanceDisabled != "" && tpact.BalanceDisabled != utils.ANY { + u, err := strconv.ParseBool(tpact.BalanceDisabled) + if err != nil { + return err + } + acts[idx].Balance.Disabled = utils.BoolPointer(u) + } + } - tpr.actions[tag] = enacts + tpr.actions[tag] = acts } } } diff --git a/engine/units_counter.go b/engine/units_counter.go index 77aa21f31..f50532a2f 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -62,7 +62,9 @@ func (ucs UnitCounters) addUnits(amount float64, kind string, cc *CallCost, b *B bal.AddValue(amount) continue } - if uc.CounterType == utils.COUNTER_BALANCE && b != nil && b.MatchFilter(bal, true) { + bp := &BalancePointer{} + bp.LoadFromBalance(bal) + if uc.CounterType == utils.COUNTER_BALANCE && b != nil && b.MatchFilter(bp, true) { bal.AddValue(amount) continue } @@ -76,7 +78,7 @@ func (ucs UnitCounters) resetCounters(a *Action) { if uc == nil { // safeguard continue } - if a != nil && a.BalanceType != "" && a.BalanceType != uc.BalanceType { + if a != nil && a.Balance.Type != nil && a.Balance.GetType() != uc.BalanceType { continue } for _, b := range uc.Balances { diff --git a/engine/units_counter_test.go b/engine/units_counter_test.go index aeee72cdf..cd8331fb3 100644 --- a/engine/units_counter_test.go +++ b/engine/units_counter_test.go @@ -50,46 +50,58 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -114,46 +126,58 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 20, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(20), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -178,54 +202,68 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 20, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(20), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceDestinationIds: utils.NewStringMap("NAT"), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR22", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDestinationIds: utils.NewStringMap("RET"), - BalanceWeight: 10, + UniqueID: "TestTR22", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -250,54 +288,68 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 20, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(20), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceDestinationIds: utils.NewStringMap("NAT"), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR22", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDestinationIds: utils.NewStringMap("RET"), - BalanceWeight: 10, + UniqueID: "TestTR22", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -336,46 +388,58 @@ func TestUnitCountersResetCounterById(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ - UniqueID: "TestTR1", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR1", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR11", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.MONETARY, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR11", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR2", - ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR2", + ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR3", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.VOICE, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR3", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR4", - ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR4", + ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, &ActionTrigger{ - UniqueID: "TestTR5", - ThresholdType: utils.TRIGGER_MAX_BALANCE, - BalanceType: utils.SMS, - BalanceDirections: utils.NewStringMap(utils.OUT, utils.IN), - BalanceWeight: 10, + UniqueID: "TestTR5", + ThresholdType: utils.TRIGGER_MAX_BALANCE, + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.SMS), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), + Weight: utils.Float64Pointer(10), + }, }, }, } @@ -395,9 +459,9 @@ func TestUnitCountersResetCounterById(t *testing.T) { t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) } a.UnitCounters.resetCounters(&Action{ - BalanceType: utils.MONETARY, - Balance: &Balance{ - Id: "TestTR11", + Balance: &BalancePointer{ + Type: utils.StringPointer(utils.MONETARY), + Id: utils.StringPointer("TestTR11"), }, }) if len(a.UnitCounters) != 4 || diff --git a/utils/coreutils.go b/utils/coreutils.go index 091e3fc07..4f857b07d 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -372,6 +372,10 @@ func StringMapPointer(sm StringMap) *StringMap { return &sm } +func TimePointer(t time.Time) *time.Time { + return &t +} + func ReflectFuncLocation(handler interface{}) (file string, line int) { f := runtime.FuncForPC(reflect.ValueOf(handler).Pointer()) entry := f.Entry() From b2253e63857ed8a6b6359e2a299767d3e58c7b92 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 11 Feb 2016 13:40:37 +0200 Subject: [PATCH 079/199] refactoring --- engine/account.go | 5 +- engine/account_test.go | 56 +++++++------- engine/action.go | 24 +++--- engine/action_trigger.go | 5 +- engine/actions_test.go | 140 +++++++++++++++++------------------ engine/balances.go | 8 +- engine/balances_test.go | 18 ++--- engine/calldesc_test.go | 6 +- engine/loader_csv_test.go | 12 +-- engine/storage_test.go | 2 +- engine/tp_reader.go | 20 ++--- engine/units_counter.go | 10 ++- engine/units_counter_test.go | 66 ++++++++--------- utils/consts.go | 1 + 14 files changed, 195 insertions(+), 178 deletions(-) diff --git a/engine/account.go b/engine/account.go index 50650c546..7784208f3 100644 --- a/engine/account.go +++ b/engine/account.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" "fmt" + "log" "time" "github.com/cgrates/cgrates/cache2go" @@ -587,6 +588,7 @@ func (acc *Account) ExecuteActionTriggers(a *Action) { // the next reset (see RESET_TRIGGERS action type) continue } + if !at.Match(a) { continue } @@ -673,9 +675,10 @@ func (acc *Account) InitCounters() { if strings.Contains(at.ThresholdType, "balance") { ct = utils.COUNTER_BALANCE } - + log.Print(at.Balance.GetType() + ct) uc, exists := ucTempMap[at.Balance.GetType()+ct] if !exists { + log.Print("HERE!") uc = &UnitCounter{ BalanceType: at.Balance.GetType(), CounterType: ct, diff --git a/engine/account_test.go b/engine/account_test.go index b603e135d..ae3dabd5e 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -814,7 +814,7 @@ func TestAccountdebitBalance(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &BalancePointer{ + newMb := &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.StringMap{"NEW": true}), @@ -833,7 +833,7 @@ func TestAccountdebitBalance(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &BalancePointer{ + newMb := &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), @@ -856,7 +856,7 @@ func TestAccountdebitBalanceExists(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Value: 15, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, } - newMb := &BalancePointer{ + newMb := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), @@ -883,19 +883,19 @@ func TestAccountAddMinuteNil(t *testing.T) { } func TestAccountAddMinutBucketEmpty(t *testing.T) { - mb1 := &BalancePointer{ + mb1 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } - mb2 := &BalancePointer{ + mb2 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } - mb3 := &BalancePointer{ + mb3 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), DestinationIds: utils.StringMapPointer(utils.StringMap{"OTHER": true}), @@ -924,7 +924,7 @@ func TestAccountExecuteTriggeredActions(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.StringMap{utils.OUT: true}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { @@ -948,7 +948,7 @@ func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, nil, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { @@ -961,7 +961,7 @@ func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { Id: "TEST_UB_OREDER", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) @@ -976,11 +976,13 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{UniqueID: "day_trigger", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, - &ActionTrigger{UniqueID: "week_trigger", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{UniqueID: "day_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{UniqueID: "week_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, }, } + log.Print("==============") ub.InitCounters() + log.Print("==============") if len(ub.UnitCounters) != 1 || len(ub.UnitCounters[0].Balances) != 2 { log.Print("Error initializing counters: ", ub.UnitCounters[0].Balances[0]) } @@ -992,7 +994,7 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { } // we can reset them - resetCountersAction(ub, nil, &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("day_trigger")}}, nil) + resetCountersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("day_trigger")}}, nil) if ub.UnitCounters[0].Balances[0].Value != 0 || ub.UnitCounters[0].Balances[1].Value != 1 { t.Error("Error reseting both counters", ub.UnitCounters[0].Balances[0].Value, ub.UnitCounters[0].Balances[1].Value) @@ -1004,7 +1006,7 @@ func TestAccountExpActionTrigger(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100, ExpirationDate: time.Date(2015, time.November, 9, 9, 48, 0, 0, time.UTC)}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1022,7 +1024,7 @@ func TestAccountExpActionTriggerNotActivated(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1040,7 +1042,7 @@ func TestAccountExpActionTriggerExpired(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1598,7 +1600,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1607,7 +1609,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1616,7 +1618,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1625,7 +1627,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1634,7 +1636,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1643,7 +1645,7 @@ func TestAccountInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1673,7 +1675,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1682,7 +1684,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1691,7 +1693,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1700,7 +1702,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1709,7 +1711,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -1718,7 +1720,7 @@ func TestAccountDoubleInitCounters(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), diff --git a/engine/action.go b/engine/action.go index 94ab9ba1e..ce4ec1a09 100644 --- a/engine/action.go +++ b/engine/action.go @@ -45,10 +45,10 @@ type Action struct { Filter string ExpirationString string // must stay as string because it can have relative values like 1month Weight float64 - Balance *BalancePointer + Balance *BalanceFilter } -type BalancePointer struct { +type BalanceFilter struct { Uuid *string Id *string Type *string @@ -67,7 +67,7 @@ type BalancePointer struct { Blocker *bool } -func (bp *BalancePointer) CreateBalance() *Balance { +func (bp *BalanceFilter) CreateBalance() *Balance { b := &Balance{} if bp.Uuid != nil { b.Uuid = *bp.Uuid @@ -114,7 +114,7 @@ func (bp *BalancePointer) CreateBalance() *Balance { return b.Clone() } -func (bp *BalancePointer) LoadFromBalance(b *Balance) { +func (bp *BalanceFilter) LoadFromBalance(b *Balance) { if b.Uuid != "" { bp.Uuid = &b.Uuid } @@ -155,20 +155,27 @@ func (bp *BalancePointer) LoadFromBalance(b *Balance) { bp.Blocker = &b.Blocker } -func (bp *BalancePointer) GetType() string { +func (bp *BalanceFilter) GetType() string { if bp.Type == nil { return "" } return *bp.Type } -func (bp *BalancePointer) GetValue() float64 { +func (bp *BalanceFilter) GetValue() float64 { if bp.Value == nil { return 0.0 } return *bp.Value } +func (bp *BalanceFilter) SetValue(v float64) { + if bp.Value == nil { + bp.Value = new(float64) + } + *bp.Value = v +} + const ( LOG = "*log" RESET_TRIGGERS = "*reset_triggers" @@ -492,9 +499,8 @@ func resetCountersAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac } func genericMakeNegative(a *Action) { - b := a.Balance.CreateBalance() - if a.Balance != nil && b.GetValue() >= 0 { // only apply if not allready negative - b.SetValue(-b.GetValue()) + if a.Balance != nil && a.Balance.GetValue() >= 0 { // only apply if not allready negative + a.Balance.SetValue(-a.Balance.GetValue()) } } diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 0fe22916c..90e42099d 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -37,7 +37,7 @@ type ActionTrigger struct { ExpirationDate time.Time ActivationDate time.Time //BalanceType string // *monetary/*voice etc - Balance *BalancePointer + Balance *BalanceFilter Weight float64 ActionsId string MinQueuedItems int // Trigger actions only if this number is hit (stats only) @@ -76,9 +76,8 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro continue } } - if a.Balance == nil { - a.Balance = &BalancePointer{} + a.Balance = &BalanceFilter{} } if a.ExpirationString != "" { a.Balance.ExpirationDate = &time.Time{} diff --git a/engine/actions_test.go b/engine/actions_test.go index e0e4244db..9917759da 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -410,7 +410,7 @@ func TestActionPlanCheckForASAP(t *testing.T) { func TestActionPlanLogFunction(t *testing.T) { a := &Action{ ActionType: "*log", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("test"), Value: utils.Float64Pointer(1.1), }, @@ -427,7 +427,7 @@ func TestActionPlanLogFunction(t *testing.T) { func TestActionPlanFunctionNotAvailable(t *testing.T) { a := &Action{ ActionType: "VALID_FUNCTION_TYPE", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("test"), Value: utils.Float64Pointer(1.1), }, @@ -531,7 +531,7 @@ func TestActionPlansRemoveMember(t *testing.T) { func TestActionTriggerMatchNil(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, @@ -546,7 +546,7 @@ func TestActionTriggerMatchNil(t *testing.T) { func TestActionTriggerMatchAllBlank(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, @@ -561,14 +561,14 @@ func TestActionTriggerMatchAllBlank(t *testing.T) { func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: `{"BalanceDirections":"*out"}`} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: `{"BalanceDirections":"*out"}`} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -576,7 +576,7 @@ func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) { func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, @@ -591,14 +591,14 @@ func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { func TestActionTriggerMatchAllFull(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 2)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 2)} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -606,14 +606,14 @@ func TestActionTriggerMatchAllFull(t *testing.T) { func TestActionTriggerMatchSomeFalse(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_BALANCE, 2)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_BALANCE, 2)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -621,14 +621,14 @@ func TestActionTriggerMatchSomeFalse(t *testing.T) { func TestActionTriggerMatcBalanceFalse(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 3.0)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 3.0)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -636,14 +636,14 @@ func TestActionTriggerMatcBalanceFalse(t *testing.T) { func TestActionTriggerMatcAllFalse(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), }, ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_EVENT_COUNTER, 3)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_EVENT_COUNTER, 3)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -651,7 +651,7 @@ func TestActionTriggerMatcAllFalse(t *testing.T) { func TestActionTriggerMatchAll(t *testing.T) { at := &ActionTrigger{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), RatingSubject: utils.StringPointer("test1"), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), @@ -662,7 +662,7 @@ func TestActionTriggerMatchAll(t *testing.T) { }, ThresholdType: utils.TRIGGER_MAX_BALANCE, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"BalanceDirections":"*out", "ThresholdType":"%v", "ThresholdValue": %v, "DestinationIds": "%v", "BalanceWeight": %v, "BalanceRatingSubject": "%v", "BalanceSharedGroup": "%v"}`, utils.TRIGGER_MAX_BALANCE, 2, "NAT", 1.0, "test1", "test2")} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"BalanceDirections":"*out", "ThresholdType":"%v", "ThresholdValue": %v, "DestinationIds": "%v", "BalanceWeight": %v, "BalanceRatingSubject": "%v", "BalanceSharedGroup": "%v"}`, utils.TRIGGER_MAX_BALANCE, 2, "NAT", 1.0, "test1", "test2")} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -685,7 +685,7 @@ func TestActionResetTriggres(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { @@ -698,7 +698,7 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.BalanceMap[utils.MONETARY][0].GetValue() == 12 { @@ -711,9 +711,9 @@ func TestActionResetTriggresActionFilter(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - resetTriggersAction(ub, nil, &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}}, nil) + resetTriggersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}}, nil) if ub.ActionTriggers[0].Executed == false || ub.ActionTriggers[1].Executed == false { t.Error("Reset triggers action failed!") } @@ -724,7 +724,7 @@ func TestActionSetPostpaid(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } allowNegativeAction(ub, nil, nil, nil) if !ub.AllowNegative { @@ -738,7 +738,7 @@ func TestActionSetPrepaid(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } denyNegativeAction(ub, nil, nil, nil) if ub.AllowNegative { @@ -752,7 +752,7 @@ func TestActionResetPrepaid(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) if !ub.AllowNegative || @@ -770,7 +770,7 @@ func TestActionResetPostpaid(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) if ub.BalanceMap[utils.MONETARY].GetTotalValue() != 0 || @@ -786,9 +786,9 @@ func TestActionTopupResetCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 10 || @@ -805,7 +805,7 @@ func TestActionTopupValueFactor(t *testing.T) { BalanceMap: map[string]BalanceChain{}, } a := &Action{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), @@ -828,7 +828,7 @@ func TestActionTopupResetCreditId(t *testing.T) { }, }, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("TEST_B"), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("TEST_B"), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -847,7 +847,7 @@ func TestActionTopupResetCreditNoId(t *testing.T) { }, }, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 20 || @@ -863,9 +863,9 @@ func TestActionTopupResetMinutes(t *testing.T) { utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 5 || @@ -882,9 +882,9 @@ func TestActionTopupCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -900,9 +900,9 @@ func TestActionTopupMinutes(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 15 || @@ -919,9 +919,9 @@ func TestActionDebitCredit(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 90 || @@ -937,9 +937,9 @@ func TestActionDebitMinutes(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE][0].GetValue() != 5 || @@ -961,7 +961,7 @@ func TestActionResetAllCounters(t *testing.T) { &Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT)}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, } ub.InitCounters() resetCountersAction(ub, nil, nil, nil) @@ -989,9 +989,9 @@ func TestActionResetCounterOnlyDefault(t *testing.T) { BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}} ub.InitCounters() resetCountersAction(ub, nil, a, nil) if !ub.AllowNegative || @@ -1020,9 +1020,9 @@ func TestActionResetCounterCredit(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}, &UnitCounter{BalanceType: utils.SMS, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY)}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}} resetCountersAction(ub, nil, a, nil) if !ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || @@ -1036,7 +1036,7 @@ func TestActionResetCounterCredit(t *testing.T) { func TestActionTriggerLogging(t *testing.T) { at := &ActionTrigger{ ID: "some_uuid", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), @@ -1108,7 +1108,7 @@ func TestActionPlanLogging(t *testing.T) { } func TestActionMakeNegative(t *testing.T) { - a := &Action{Balance: &BalancePointer{Value: utils.Float64Pointer(10)}} + a := &Action{Balance: &BalanceFilter{Value: utils.Float64Pointer(10)}} genericMakeNegative(a) if a.Balance.GetValue() > 0 { t.Error("Failed to make negative: ", a) @@ -1142,7 +1142,7 @@ func TestTopupAction(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minu") a := &Action{ ActionType: TOPUP, - Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1163,7 +1163,7 @@ func TestTopupActionLoaded(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minitsboy") a := &Action{ ActionType: TOPUP, - Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1190,7 +1190,7 @@ func TestActionCdrlogEmpty(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1212,11 +1212,11 @@ func TestActionCdrlogWithParams(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1239,11 +1239,11 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalancePointer{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1324,11 +1324,11 @@ func TestActionTransactionFuncType(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ ActionType: "VALID_FUNCTION_TYPE", - Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer("test")}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer("test")}, }, }, } @@ -1360,11 +1360,11 @@ func TestActionTransactionBalanceType(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalancePointer{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ ActionType: TOPUP, - Balance: &BalancePointer{Type: utils.StringPointer("test")}, + Balance: &BalanceFilter{Type: utils.StringPointer("test")}, }, }, } @@ -1396,14 +1396,14 @@ func TestActionWithExpireWithoutExpire(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(15), }, }, &Action{ ActionType: TOPUP, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(30), ExpirationDate: utils.TimePointer(time.Date(2025, time.November, 11, 22, 39, 0, 0, time.UTC)), @@ -1451,7 +1451,7 @@ func TestActionRemoveBalance(t *testing.T) { actions: []*Action{ &Action{ ActionType: REMOVE_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT", "RET")), }, @@ -1561,7 +1561,7 @@ func TestActionTransferMonetaryDefaultFilter(t *testing.T) { a := &Action{ ActionType: TRANSFER_MONETARY_DEFAULT, - Balance: &BalancePointer{Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1623,7 +1623,7 @@ func TestActionConditionalTopup(t *testing.T) { a := &Action{ ActionType: TOPUP, Filter: `{"Type":"*monetary","Value":1,"Weight":10}`, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(11), Weight: utils.Float64Pointer(30), @@ -1687,7 +1687,7 @@ func TestActionConditionalTopupNoMatch(t *testing.T) { a := &Action{ ActionType: TOPUP, Filter: `{"Type":"*monetary","Value":2,"Weight":10}`, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(11), Weight: utils.Float64Pointer(30), @@ -1751,7 +1751,7 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) { a := &Action{ ActionType: TOPUP, Filter: `{"Type":"*voice","Value":{"*gte":100}}`, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(11), Weight: utils.Float64Pointer(10), @@ -1852,7 +1852,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { a1 := &Action{ ActionType: "*enable_disable_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("*sms"), Weight: utils.Float64Pointer(10), Disabled: utils.BoolPointer(true), @@ -1862,7 +1862,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { a2 := &Action{ ActionType: "*enable_disable_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("*sms"), DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), Weight: utils.Float64Pointer(10), @@ -1873,7 +1873,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { a3 := &Action{ ActionType: "*enable_disable_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("*data"), RatingSubject: utils.StringPointer("for_v3hsillmilld500m_data_forfait"), Weight: utils.Float64Pointer(10), @@ -1884,7 +1884,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { a4 := &Action{ ActionType: "*enable_disable_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer("*voice"), DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), Weight: utils.Float64Pointer(10), @@ -1956,7 +1956,7 @@ func TestActionSetBalance(t *testing.T) { a := &Action{ ActionType: SET_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Id: utils.StringPointer("m2"), Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(11), diff --git a/engine/balances.go b/engine/balances.go index 38dd1e8e4..80fc005d6 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -72,7 +72,7 @@ func (b *Balance) Equal(o *Balance) bool { b.Blocker == o.Blocker } -func (b *Balance) MatchFilter(o *BalancePointer, skipIds bool) bool { +func (b *Balance) MatchFilter(o *BalanceFilter, skipIds bool) bool { if o == nil { return true } @@ -84,7 +84,7 @@ func (b *Balance) MatchFilter(o *BalancePointer, skipIds bool) bool { } return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && (o.Weight == nil || b.Weight == *o.Weight) && - (o.Blocker != nil || b.Blocker == *o.Blocker) && + (o.Blocker == nil || b.Blocker == *o.Blocker) && (o.Disabled == nil || b.Disabled == *o.Disabled) && (o.DestinationIds == nil || b.DestinationIds.Includes(*o.DestinationIds)) && (o.Directions == nil || b.Directions.Includes(*o.Directions)) && @@ -94,7 +94,7 @@ func (b *Balance) MatchFilter(o *BalancePointer, skipIds bool) bool { (o.RatingSubject == nil || b.RatingSubject == *o.RatingSubject) } -func (b *Balance) HardMatchFilter(o *BalancePointer, skipIds bool) bool { +func (b *Balance) HardMatchFilter(o *BalanceFilter, skipIds bool) bool { if o == nil { return true } @@ -106,7 +106,7 @@ func (b *Balance) HardMatchFilter(o *BalancePointer, skipIds bool) bool { } return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && (o.Weight == nil || b.Weight == *o.Weight) && - (o.Blocker != nil || b.Blocker == *o.Blocker) && + (o.Blocker == nil || b.Blocker == *o.Blocker) && (o.Disabled == nil || b.Disabled == *o.Disabled) && (o.DestinationIds == nil || b.DestinationIds.Equal(*o.DestinationIds)) && (o.Directions == nil || b.Directions.Equal(*o.Directions)) && diff --git a/engine/balances_test.go b/engine/balances_test.go index 09c9dd17f..93a9f23db 100644 --- a/engine/balances_test.go +++ b/engine/balances_test.go @@ -90,7 +90,7 @@ func TestBalanceEqual(t *testing.T) { func TestBalanceMatchFilter(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &BalancePointer{Weight: utils.Float64Pointer(1), RatingSubject: nil, DestinationIds: nil} + mb2 := &BalanceFilter{Weight: utils.Float64Pointer(1), RatingSubject: nil, DestinationIds: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -98,7 +98,7 @@ func TestBalanceMatchFilter(t *testing.T) { func TestBalanceMatchFilterEmpty(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &BalancePointer{} + mb2 := &BalanceFilter{} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -106,7 +106,7 @@ func TestBalanceMatchFilterEmpty(t *testing.T) { func TestBalanceMatchFilterId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 2, precision: 2, RatingSubject: "2", DestinationIds: utils.NewStringMap("NAT")} - mb2 := &BalancePointer{Id: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} + mb2 := &BalanceFilter{Id: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -114,7 +114,7 @@ func TestBalanceMatchFilterId(t *testing.T) { func TestBalanceMatchFilterDiffId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &BalancePointer{Id: utils.StringPointer("T2"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} + mb2 := &BalanceFilter{Id: utils.StringPointer("T2"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} if mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v != %+v", mb1, mb2) } @@ -129,7 +129,7 @@ func TestBalanceClone(t *testing.T) { } func TestBalanceMatchActionTriggerId(t *testing.T) { - at := &ActionTrigger{Balance: &BalancePointer{Id: utils.StringPointer("test")}} + at := &ActionTrigger{Balance: &BalanceFilter{Id: utils.StringPointer("test")}} b := &Balance{Id: "test"} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -150,7 +150,7 @@ func TestBalanceMatchActionTriggerId(t *testing.T) { } func TestBalanceMatchActionTriggerDestination(t *testing.T) { - at := &ActionTrigger{Balance: &BalancePointer{DestinationIds: utils.StringMapPointer(utils.NewStringMap("test"))}} + at := &ActionTrigger{Balance: &BalanceFilter{DestinationIds: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{DestinationIds: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -171,7 +171,7 @@ func TestBalanceMatchActionTriggerDestination(t *testing.T) { } func TestBalanceMatchActionTriggerWeight(t *testing.T) { - at := &ActionTrigger{Balance: &BalancePointer{Weight: utils.Float64Pointer(1)}} + at := &ActionTrigger{Balance: &BalanceFilter{Weight: utils.Float64Pointer(1)}} b := &Balance{Weight: 1} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -192,7 +192,7 @@ func TestBalanceMatchActionTriggerWeight(t *testing.T) { } func TestBalanceMatchActionTriggerRatingSubject(t *testing.T) { - at := &ActionTrigger{Balance: &BalancePointer{RatingSubject: utils.StringPointer("test")}} + at := &ActionTrigger{Balance: &BalanceFilter{RatingSubject: utils.StringPointer("test")}} b := &Balance{RatingSubject: "test"} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -213,7 +213,7 @@ func TestBalanceMatchActionTriggerRatingSubject(t *testing.T) { } func TestBalanceMatchActionTriggerSharedGroup(t *testing.T) { - at := &ActionTrigger{Balance: &BalancePointer{SharedGroups: utils.StringMapPointer(utils.NewStringMap("test"))}} + at := &ActionTrigger{Balance: &BalanceFilter{SharedGroups: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{SharedGroups: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index e4d5cdc9e..cbac46036 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -41,12 +41,12 @@ func init() { func populateDB() { ats := []*Action{ - &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}}, - &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: utils.Float64Pointer(10), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, + &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}}, + &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: utils.Float64Pointer(10), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, } ats1 := []*Action{ - &Action{ActionType: "*topup", Balance: &BalancePointer{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}, Weight: 10}, + &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}, Weight: 10}, &Action{ActionType: "*reset_account", Weight: 20}, } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 4cb27daf0..9c6073051 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -826,7 +826,7 @@ func TestLoadActions(t *testing.T) { ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Uuid: as1[0].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), @@ -844,7 +844,7 @@ func TestLoadActions(t *testing.T) { ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Uuid: as1[1].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), @@ -868,7 +868,7 @@ func TestLoadActions(t *testing.T) { ActionType: TOPUP, ExpirationString: UNLIMITED, Weight: 10, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: nil, @@ -891,7 +891,7 @@ func TestLoadActions(t *testing.T) { ActionType: CDRLOG, ExtraParameters: `{"Category":"^ddi","MediationRunId":"^did_run"}`, Weight: 10, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Uuid: as3[0].Balance.Uuid, Directions: nil, DestinationIds: nil, @@ -1047,7 +1047,7 @@ func TestLoadActionTriggers(t *testing.T) { UniqueID: "st0", ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ThresholdValue: 10, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), @@ -1068,7 +1068,7 @@ func TestLoadActionTriggers(t *testing.T) { UniqueID: "st1", ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 200, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY")), diff --git a/engine/storage_test.go b/engine/storage_test.go index 1a38f6bd2..2c1b9b808 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -317,7 +317,7 @@ func GetUB() *Account { at := &ActionTrigger{ ID: "some_uuid", ThresholdValue: 100.0, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 27075be55..bfa6615d6 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -521,7 +521,7 @@ func (tpr *TpReader) LoadActions() (err error) { ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) @@ -545,7 +545,7 @@ func (tpr *TpReader) LoadActions() (err error) { } acts[idx].Balance.Weight = utils.Float64Pointer(u) } - if tpact.ExpiryTime != "" && tpact.ExpiryTime != utils.ANY { + if tpact.ExpiryTime != "" && tpact.ExpiryTime != utils.ANY && tpact.ExpiryTime != utils.UNLIMITED { u, err := utils.ParseTimeDetectLayout(tpact.ExpiryTime, tpr.timezone) if err != nil { return err @@ -700,7 +700,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { MinSleep: minSleep, ExpirationDate: expirationDate, ActivationDate: activationDate, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, Weight: atr.Weight, ActionsId: atr.ActionsId, MinQueuedItems: atr.MinQueuedItems, @@ -720,7 +720,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { } atrs[idx].Balance.Weight = utils.Float64Pointer(u) } - if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY && atr.ExpirationDate != utils.UNLIMITED { u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) if err != nil { return err @@ -901,7 +901,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error MinSleep: minSleep, ExpirationDate: expTime, ActivationDate: actTime, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, Weight: atr.Weight, ActionsId: atr.ActionsId, } @@ -920,7 +920,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error } atrs[idx].Balance.Weight = utils.Float64Pointer(u) } - if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY && atr.ExpirationDate != utils.UNLIMITED { u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) if err != nil { return err @@ -1003,7 +1003,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) @@ -1238,7 +1238,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { MinSleep: minSleep, ExpirationDate: expTime, ActivationDate: actTime, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, Weight: atr.Weight, ActionsId: atr.ActionsId, } @@ -1257,7 +1257,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { } atrs[idx].Balance.Weight = utils.Float64Pointer(u) } - if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY { + if atr.BalanceExpirationDate != "" && atr.BalanceExpirationDate != utils.ANY && atr.ExpirationDate != utils.UNLIMITED { u, err := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) if err != nil { return err @@ -1349,7 +1349,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, - Balance: &BalancePointer{}, + Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) diff --git a/engine/units_counter.go b/engine/units_counter.go index f50532a2f..7854aa9ec 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -18,7 +18,11 @@ along with this program. If not, see package engine -import "github.com/cgrates/cgrates/utils" +import ( + "log" + + "github.com/cgrates/cgrates/utils" +) // Amount of a trafic of a certain type type UnitCounter struct { @@ -58,11 +62,13 @@ func (ucs UnitCounters) addUnits(amount float64, kind string, cc *CallCost, b *B uc.CounterType = utils.COUNTER_EVENT } for _, bal := range uc.Balances { + log.Print(b) if uc.CounterType == utils.COUNTER_EVENT && cc != nil && bal.MatchCCFilter(cc) { + log.Print("HERE") bal.AddValue(amount) continue } - bp := &BalancePointer{} + bp := &BalanceFilter{} bp.LoadFromBalance(bal) if uc.CounterType == utils.COUNTER_BALANCE && b != nil && b.MatchFilter(bp, true) { bal.AddValue(amount) diff --git a/engine/units_counter_test.go b/engine/units_counter_test.go index cd8331fb3..4ca1ec849 100644 --- a/engine/units_counter_test.go +++ b/engine/units_counter_test.go @@ -52,7 +52,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -61,7 +61,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -70,7 +70,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -79,7 +79,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -88,7 +88,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -97,7 +97,7 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -128,7 +128,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -137,7 +137,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20), @@ -146,7 +146,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -155,7 +155,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -164,7 +164,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -173,7 +173,7 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -204,7 +204,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -213,7 +213,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20), @@ -222,7 +222,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), @@ -232,7 +232,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR22", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(10), @@ -241,7 +241,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -250,7 +250,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -259,7 +259,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -290,7 +290,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -299,7 +299,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20), @@ -308,7 +308,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), @@ -318,7 +318,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR22", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(10), @@ -327,7 +327,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -336,7 +336,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -345,7 +345,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), @@ -390,7 +390,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -399,7 +399,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -408,7 +408,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -417,7 +417,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -426,7 +426,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -435,7 +435,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), @@ -459,7 +459,7 @@ func TestUnitCountersResetCounterById(t *testing.T) { t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) } a.UnitCounters.resetCounters(&Action{ - Balance: &BalancePointer{ + Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("TestTR11"), }, diff --git a/utils/consts.go b/utils/consts.go index 62feda060..45d6e2034 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -91,6 +91,7 @@ const ( ROUNDING_MIDDLE = "*middle" ROUNDING_DOWN = "*down" ANY = "*any" + UNLIMITED = "*unlimited" ZERO = "*zero" ASAP = "*asap" USERS = "*users" From 418a9cf12f60dc1ca243a6999594ff54bbf14540 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 15 Feb 2016 23:05:35 +0200 Subject: [PATCH 080/199] most engine tests passing ResetAccountPending --- engine/account.go | 118 +++++++++++-------- engine/account_test.go | 109 ++++++++--------- engine/action.go | 130 +-------------------- engine/action_plan.go | 5 +- engine/action_trigger.go | 32 ++++- engine/actions_test.go | 193 +++++++++++++++++++----------- engine/balance_filter.go | 220 +++++++++++++++++++++++++++++++++++ engine/balances.go | 46 +------- engine/balances_test.go | 14 +-- engine/callcost.go | 38 ++++++ engine/calldesc_test.go | 2 +- engine/loader_csv_test.go | 60 ++++++---- engine/model_helpers_test.go | 8 +- engine/storage_test.go | 7 +- engine/tp_reader.go | 24 ++-- engine/units_counter.go | 81 +++++++------ engine/units_counter_test.go | 160 ++++++++++++++----------- 17 files changed, 746 insertions(+), 501 deletions(-) create mode 100644 engine/balance_filter.go diff --git a/engine/account.go b/engine/account.go index 7784208f3..0130f1fb7 100644 --- a/engine/account.go +++ b/engine/account.go @@ -22,7 +22,6 @@ import ( "encoding/json" "errors" "fmt" - "log" "time" "github.com/cgrates/cgrates/cache2go" @@ -258,6 +257,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { return err } } + ub.InitCounters() ub.ExecuteActionTriggers(nil) return nil } @@ -588,22 +588,27 @@ func (acc *Account) ExecuteActionTriggers(a *Action) { // the next reset (see RESET_TRIGGERS action type) continue } - if !at.Match(a) { continue } if strings.Contains(at.ThresholdType, "counter") { - for _, uc := range acc.UnitCounters { - if uc.BalanceType == at.Balance.GetType() && - strings.Contains(at.ThresholdType, uc.CounterType[1:]) { - for _, b := range uc.Balances { - if strings.HasPrefix(at.ThresholdType, "*max") { - if b.MatchActionTrigger(at) && b.GetValue() >= at.ThresholdValue { - at.Execute(acc, nil) - } - } else { //MIN - if b.MatchActionTrigger(at) && b.GetValue() <= at.ThresholdValue { - at.Execute(acc, nil) + if (at.Balance.ID == nil || *at.Balance.ID != "") && at.UniqueID != "" { + at.Balance.ID = utils.StringPointer(at.UniqueID) + } + for _, counters := range acc.UnitCounters { + for _, uc := range counters { + if strings.Contains(at.ThresholdType, uc.CounterType[1:]) { + for _, c := range uc.Counters { + //log.Print("C: ", utils.ToJSON(c)) + if strings.HasPrefix(at.ThresholdType, "*max") { + if c.Filter.Equal(at.Balance) && c.Value >= at.ThresholdValue { + //log.Print("HERE") + at.Execute(acc, nil) + } + } else { //MIN + if c.Filter.Equal(at.Balance) && c.Value <= at.ThresholdValue { + at.Execute(acc, nil) + } } } } @@ -643,7 +648,7 @@ func (acc *Account) ResetActionTriggers(a *Action) { } at.Executed = false } - acc.ExecuteActionTriggers(a) + acc.ExecuteActionTriggers(a) //will trigger infinite loop when executed from ExecuteActionTriggers } // Sets/Unsets recurrent flag for action triggers @@ -665,9 +670,10 @@ func (acc *Account) countUnits(amount float64, kind string, cc *CallCost, b *Bal // Create counters for all triggered actions func (acc *Account) InitCounters() { oldUcs := acc.UnitCounters - acc.UnitCounters = nil + acc.UnitCounters = make(UnitCounters) ucTempMap := make(map[string]*UnitCounter) for _, at := range acc.ActionTriggers { + //log.Print("AT: ", utils.ToJSON(at)) if !strings.Contains(at.ThresholdType, "counter") { continue } @@ -675,28 +681,36 @@ func (acc *Account) InitCounters() { if strings.Contains(at.ThresholdType, "balance") { ct = utils.COUNTER_BALANCE } - log.Print(at.Balance.GetType() + ct) uc, exists := ucTempMap[at.Balance.GetType()+ct] + //log.Print("CT: ", at.Balance.GetType()+ct) if !exists { - log.Print("HERE!") uc = &UnitCounter{ - BalanceType: at.Balance.GetType(), CounterType: ct, } ucTempMap[at.Balance.GetType()+ct] = uc - uc.Balances = BalanceChain{} - acc.UnitCounters = append(acc.UnitCounters, uc) + uc.Counters = make(CounterFilters, 0) + acc.UnitCounters[at.Balance.GetType()] = append(acc.UnitCounters[at.Balance.GetType()], uc) } - b := at.Balance.CreateBalance() - if !uc.Balances.HasBalance(b) { - uc.Balances = append(uc.Balances, b) + c := &CounterFilter{Filter: at.Balance} + if (c.Filter.ID == nil || *c.Filter.ID == "") && at.UniqueID != "" { + c.Filter.ID = utils.StringPointer(at.UniqueID) + } + //log.Print("C: ", utils.ToJSON(c)) + if !uc.Counters.HasCounter(c) { + uc.Counters = append(uc.Counters, c) } } // copy old counter values - for _, uc := range acc.UnitCounters { - for _, oldUc := range oldUcs { - if uc.CopyCounterValues(oldUc) { - break + for key, counters := range acc.UnitCounters { + oldCounters, found := oldUcs[key] + if !found { + continue + } + for _, uc := range counters { + for _, oldUc := range oldCounters { + if uc.CopyCounterValues(oldUc) { + break + } } } } @@ -908,30 +922,32 @@ func (acc *Account) AsOldStructure() interface{} { AllowNegative: acc.AllowNegative, Disabled: acc.Disabled, } - for i, uc := range acc.UnitCounters { - if uc == nil { - continue - } - result.UnitCounters[i] = &UnitsCounter{ - BalanceType: uc.BalanceType, - Balances: make(BalanceChain, len(uc.Balances)), - } - if len(uc.Balances) > 0 { - result.UnitCounters[i].Direction = uc.Balances[0].Directions.String() - for j, b := range uc.Balances { - result.UnitCounters[i].Balances[j] = &Balance{ - Uuid: b.Uuid, - Id: b.Id, - Value: b.Value, - ExpirationDate: b.ExpirationDate, - Weight: b.Weight, - DestinationIds: b.DestinationIds.String(), - RatingSubject: b.RatingSubject, - Category: b.Categories.String(), - SharedGroup: b.SharedGroups.String(), - Timings: b.Timings, - TimingIDs: b.TimingIDs.String(), - Disabled: b.Disabled, + for balanceType, counters := range acc.UnitCounters { + for i, uc := range counters { + if uc == nil { + continue + } + result.UnitCounters[i] = &UnitsCounter{ + BalanceType: balanceType, + Balances: make(BalanceChain, len(uc.Counters)), + } + if len(uc.Counters) > 0 { + result.UnitCounters[i].Direction = (*uc.Counters[0].Filter.Directions).String() + for j, c := range uc.Counters { + result.UnitCounters[i].Balances[j] = &Balance{ + Uuid: c.Filter.GetUuid(), + Id: c.Filter.GetID(), + Value: c.Filter.GetValue(), + ExpirationDate: c.Filter.GetExpirationDate(), + Weight: c.Filter.GetWeight(), + DestinationIds: c.Filter.GetDestinationIDs().String(), + RatingSubject: c.Filter.GetRatingSubject(), + Category: c.Filter.GetCategories().String(), + SharedGroup: c.Filter.GetSharedGroups().String(), + Timings: c.Filter.Timings, + TimingIDs: c.Filter.GetTimingIDs().String(), + Disabled: c.Filter.GetDisabled(), + } } } } diff --git a/engine/account_test.go b/engine/account_test.go index ae3dabd5e..7def0cc0b 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -19,7 +19,6 @@ along with this program. If not, see package engine import ( - "log" "testing" "time" @@ -817,12 +816,12 @@ func TestAccountdebitBalance(t *testing.T) { newMb := &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), - DestinationIds: utils.StringMapPointer(utils.StringMap{"NEW": true}), + DestinationIDs: utils.StringMapPointer(utils.StringMap{"NEW": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } a := &Action{Balance: newMb} ub.debitBalanceAction(a, false) - if len(ub.BalanceMap[utils.VOICE]) != 3 || !ub.BalanceMap[utils.VOICE][2].DestinationIds.Equal(*newMb.DestinationIds) { + if len(ub.BalanceMap[utils.VOICE]) != 3 || !ub.BalanceMap[utils.VOICE][2].DestinationIds.Equal(*newMb.DestinationIDs) { t.Errorf("Error adding minute bucket! %d %+v %+v", len(ub.BalanceMap[utils.VOICE]), ub.BalanceMap[utils.VOICE][2], newMb) } } @@ -860,7 +859,7 @@ func TestAccountdebitBalanceExists(t *testing.T) { Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), - DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } a := &Action{Balance: newMb} @@ -886,19 +885,19 @@ func TestAccountAddMinutBucketEmpty(t *testing.T) { mb1 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), - DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } mb2 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), - DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } mb3 := &BalanceFilter{ Value: utils.Float64Pointer(-10), Type: utils.StringPointer(utils.VOICE), - DestinationIds: utils.StringMapPointer(utils.StringMap{"OTHER": true}), + DestinationIDs: utils.StringMapPointer(utils.StringMap{"OTHER": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } ub := &Account{} @@ -923,7 +922,7 @@ func TestAccountExecuteTriggeredActions(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.StringMap{utils.OUT: true}}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.StringMap{utils.OUT: true})}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) @@ -947,7 +946,7 @@ func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Filter: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, Value: 1.0}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, nil, nil) @@ -960,14 +959,14 @@ func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { ub := &Account{ Id: "TEST_UB_OREDER", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Type: utils.StringPointer(utils.MONETARY)}}}}}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) if len(ub.BalanceMap[utils.MONETARY]) != 1 || ub.BalanceMap[utils.MONETARY][0].GetValue() != 10 { - t.Errorf("Error executing triggered actions in order %v BAL: %+v", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.MONETARY][1]) + t.Errorf("Error executing triggered actions in order %v", ub.BalanceMap[utils.MONETARY][0].GetValue()) } } @@ -980,24 +979,22 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { &ActionTrigger{UniqueID: "week_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, }, } - log.Print("==============") ub.InitCounters() - log.Print("==============") - if len(ub.UnitCounters) != 1 || len(ub.UnitCounters[0].Balances) != 2 { - log.Print("Error initializing counters: ", ub.UnitCounters[0].Balances[0]) + if len(ub.UnitCounters) != 1 || len(ub.UnitCounters[utils.MONETARY][0].Counters) != 2 { + t.Error("Error initializing counters: ", ub.UnitCounters[utils.MONETARY][0].Counters[0]) } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if ub.UnitCounters[0].Balances[0].Value != 1 || - ub.UnitCounters[0].Balances[1].Value != 1 { - t.Error("Error incrementing both counters", ub.UnitCounters[0].Balances[0].Value, ub.UnitCounters[0].Balances[1].Value) + if ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 1 || + ub.UnitCounters[utils.MONETARY][0].Counters[1].Value != 1 { + t.Error("Error incrementing both counters", ub.UnitCounters[utils.MONETARY][0].Counters[0].Value, ub.UnitCounters[utils.MONETARY][0].Counters[1].Value) } // we can reset them - resetCountersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("day_trigger")}}, nil) - if ub.UnitCounters[0].Balances[0].Value != 0 || - ub.UnitCounters[0].Balances[1].Value != 1 { - t.Error("Error reseting both counters", ub.UnitCounters[0].Balances[0].Value, ub.UnitCounters[0].Balances[1].Value) + resetCountersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), ID: utils.StringPointer("day_trigger")}}, nil) + if ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 0 || + ub.UnitCounters[utils.MONETARY][0].Counters[1].Value != 1 { + t.Error("Error reseting both counters", ub.UnitCounters[utils.MONETARY][0].Counters[0].Value, ub.UnitCounters[utils.MONETARY][0].Counters[1].Value) } } @@ -1087,45 +1084,45 @@ func TestCleanExpired(t *testing.T) { } func TestAccountUnitCounting(t *testing.T) { - ub := &Account{UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 0}}}}} + ub := &Account{UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 0}}}}}} ub.countUnits(10, utils.MONETARY, &CallCost{}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 10 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 { t.Error("Error counting units") } ub.countUnits(10, utils.MONETARY, &CallCost{}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 20 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 20 { t.Error("Error counting units") } } func TestAccountUnitCountingOutbound(t *testing.T) { - ub := &Account{UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 0, Directions: utils.NewStringMap(utils.OUT)}}}}} + ub := &Account{UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 0, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}} ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 10 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 { t.Error("Error counting units") } ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 20 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 20 { t.Error("Error counting units") } ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 30 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 30 { t.Error("Error counting units") } } func TestAccountUnitCountingOutboundInbound(t *testing.T) { - ub := &Account{UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 0, Directions: utils.NewStringMap(utils.OUT)}}}}} + ub := &Account{UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 0, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}} ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 10 { - t.Errorf("Error counting units: %+v", ub.UnitCounters[0].Balances[0]) + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 { + t.Errorf("Error counting units: %+v", ub.UnitCounters[utils.MONETARY][0].Counters[0]) } ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 20 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 20 { t.Error("Error counting units") } ub.countUnits(10, utils.MONETARY, &CallCost{Direction: utils.IN}, nil) - if len(ub.UnitCounters) != 1 || (ub.UnitCounters[0].BalanceType != utils.MONETARY || ub.UnitCounters[0].Balances[0].GetValue() != 20) { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 20 { t.Error("Error counting units") } } @@ -1654,15 +1651,18 @@ func TestAccountInitCounters(t *testing.T) { }, } a.InitCounters() - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - len(a.UnitCounters[1].Balances) != 1 || - len(a.UnitCounters[2].Balances) != 1 || - len(a.UnitCounters[3].Balances) != 1 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + len(a.UnitCounters[utils.VOICE][0].Counters) != 1 || + len(a.UnitCounters[utils.VOICE][1].Counters) != 1 || + len(a.UnitCounters[utils.SMS][0].Counters) != 1 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, c := range uc.Counters { + t.Logf("B: %+v", c) + } } } t.Errorf("Error Initializing unit counters: %v", len(a.UnitCounters)) @@ -1730,15 +1730,18 @@ func TestAccountDoubleInitCounters(t *testing.T) { } a.InitCounters() a.InitCounters() - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - len(a.UnitCounters[1].Balances) != 1 || - len(a.UnitCounters[2].Balances) != 1 || - len(a.UnitCounters[3].Balances) != 1 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + len(a.UnitCounters[utils.VOICE][0].Counters) != 1 || + len(a.UnitCounters[utils.VOICE][1].Counters) != 1 || + len(a.UnitCounters[utils.SMS][0].Counters) != 1 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, c := range uc.Counters { + t.Logf("B: %+v", c) + } } } t.Errorf("Error Initializing unit counters: %v", len(a.UnitCounters)) diff --git a/engine/action.go b/engine/action.go index ce4ec1a09..7dde51af3 100644 --- a/engine/action.go +++ b/engine/action.go @@ -48,134 +48,6 @@ type Action struct { Balance *BalanceFilter } -type BalanceFilter struct { - Uuid *string - Id *string - Type *string - Value *float64 - Directions *utils.StringMap - ExpirationDate *time.Time - Weight *float64 - DestinationIds *utils.StringMap - RatingSubject *string - Categories *utils.StringMap - SharedGroups *utils.StringMap - TimingIDs *utils.StringMap - Timings []*RITiming - Disabled *bool - Factor *ValueFactor - Blocker *bool -} - -func (bp *BalanceFilter) CreateBalance() *Balance { - b := &Balance{} - if bp.Uuid != nil { - b.Uuid = *bp.Uuid - } - if bp.Id != nil { - b.Id = *bp.Id - } - if bp.Value != nil { - b.Value = *bp.Value - } - if bp.Directions != nil { - b.Directions = *bp.Directions - } - if bp.ExpirationDate != nil { - b.ExpirationDate = *bp.ExpirationDate - } - if bp.Weight != nil { - b.Weight = *bp.Weight - } - if bp.DestinationIds != nil { - b.DestinationIds = *bp.DestinationIds - } - if bp.RatingSubject != nil { - b.RatingSubject = *bp.RatingSubject - } - if bp.Categories != nil { - b.Categories = *bp.Categories - } - if bp.SharedGroups != nil { - b.SharedGroups = *bp.SharedGroups - } - if bp.TimingIDs != nil { - b.TimingIDs = *bp.TimingIDs - } - if bp.Disabled != nil { - b.Disabled = *bp.Disabled - } - if bp.Factor != nil { - b.Factor = *bp.Factor - } - if bp.Blocker != nil { - b.Blocker = *bp.Blocker - } - return b.Clone() -} - -func (bp *BalanceFilter) LoadFromBalance(b *Balance) { - if b.Uuid != "" { - bp.Uuid = &b.Uuid - } - if b.Id != "" { - bp.Id = &b.Id - } - if b.Value != 0 { - bp.Value = &b.Value - } - if len(b.Directions) != 0 { - bp.Directions = &b.Directions - } - if !b.ExpirationDate.IsZero() { - bp.ExpirationDate = &b.ExpirationDate - } - if b.Weight != 0 { - bp.Weight = &b.Weight - } - if len(b.DestinationIds) != 0 { - bp.DestinationIds = &b.DestinationIds - } - if b.RatingSubject != "" { - bp.RatingSubject = &b.RatingSubject - } - if len(b.Categories) != 0 { - bp.Categories = &b.Categories - } - if len(b.SharedGroups) != 0 { - bp.SharedGroups = &b.SharedGroups - } - if len(b.TimingIDs) != 0 { - bp.TimingIDs = &b.TimingIDs - } - if len(b.Factor) != 0 { - bp.Factor = &b.Factor - } - bp.Disabled = &b.Disabled - bp.Blocker = &b.Blocker -} - -func (bp *BalanceFilter) GetType() string { - if bp.Type == nil { - return "" - } - return *bp.Type -} - -func (bp *BalanceFilter) GetValue() float64 { - if bp.Value == nil { - return 0.0 - } - return *bp.Value -} - -func (bp *BalanceFilter) SetValue(v float64) { - if bp.Value == nil { - bp.Value = new(float64) - } - *bp.Value = v -} - const ( LOG = "*log" RESET_TRIGGERS = "*reset_triggers" @@ -301,7 +173,7 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) case "AccountID": parsedValue += rsrFld.ParseValue(acnt.Id) case "Directions": - parsedValue += rsrFld.ParseValue(action.Balance.Directions.String()) + parsedValue += rsrFld.ParseValue(b.Directions.String()) case utils.TENANT: parsedValue += rsrFld.ParseValue(dta.Tenant) case utils.ACCOUNT: diff --git a/engine/action_plan.go b/engine/action_plan.go index 0a9283d25..a9081b90a 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -307,7 +307,7 @@ func (at *ActionTiming) Execute() (err error) { continue // disabled acocunts are not removed from action plan //return 0, fmt.Errorf("Account %s is disabled", accID) } - if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.ExpirationDate.IsZero()) && parseErr == nil && !expDate.IsZero() { + if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.HasExpirationDate()) && parseErr == nil && !expDate.IsZero() { a.Balance.ExpirationDate = &time.Time{} *a.Balance.ExpirationDate = expDate } @@ -337,8 +337,7 @@ func (at *ActionTiming) Execute() (err error) { } if len(at.accountIDs) == 0 { // action timing executing without accounts for _, a := range aac { - - if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.ExpirationDate.IsZero()) && + if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.HasExpirationDate()) && parseErr == nil && !expDate.IsZero() { a.Balance.ExpirationDate = &time.Time{} *a.Balance.ExpirationDate = expDate diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 90e42099d..afca42939 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -19,6 +19,7 @@ along with this program. If not, see package engine import ( + "encoding/json" "fmt" "sort" "time" @@ -112,10 +113,37 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro // returns true if the field of the action timing are equeal to the non empty // fields of the action func (at *ActionTrigger) Match(a *Action) bool { - if a == nil { + if a == nil || a.Balance == nil { return true } - return at.Balance.CreateBalance().MatchFilter(a.Balance, false) + if a.Balance.Type != nil && a.Balance.GetType() != at.Balance.GetType() { + return false + } + var thresholdType bool + if a.ExtraParameters != "" { + t := struct { + GroupID string + UniqueID string + ThresholdType string + }{} + json.Unmarshal([]byte(a.ExtraParameters), &t) + // check Ids first + if t.GroupID != "" { + return at.ID == t.GroupID + } + if t.UniqueID != "" { + return at.UniqueID == t.UniqueID + } + thresholdType = t.ThresholdType == "" || at.ThresholdType == t.ThresholdType + } + + return thresholdType && at.Balance.CreateBalance().MatchFilter(a.Balance, false) +} + +func (at *ActionTrigger) CreateBalance() *Balance { + b := at.Balance.CreateBalance() + b.Id = at.UniqueID + return b } // makes a shallow copy of the receiver diff --git a/engine/actions_test.go b/engine/actions_test.go index 9917759da..af03aa6be 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -613,7 +613,7 @@ func TestActionTriggerMatchSomeFalse(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_BALANCE, 2)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%s"}`, utils.TRIGGER_MAX_BALANCE_COUNTER)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -628,7 +628,7 @@ func TestActionTriggerMatcBalanceFalse(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*out"}`, utils.TRIGGER_MAX_BALANCE, 3.0)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ExtraParameters: fmt.Sprintf(`{"GroupID":"%s", "ThresholdType":"%s"}`, "TEST", utils.TRIGGER_MAX_BALANCE)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -643,7 +643,7 @@ func TestActionTriggerMatcAllFalse(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v, "BalanceDirections":"*in"}`, utils.TRIGGER_MAX_EVENT_COUNTER, 3)} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"UniqueID":"ZIP", "GroupID":"%s", "ThresholdType":"%s"}`, "TEST", utils.TRIGGER_MAX_BALANCE)} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -651,18 +651,28 @@ func TestActionTriggerMatcAllFalse(t *testing.T) { func TestActionTriggerMatchAll(t *testing.T) { at := &ActionTrigger{ + ID: "TEST", + UniqueID: "ZIP", + ThresholdType: "TT", Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), RatingSubject: utils.StringPointer("test1"), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: utils.Float64Pointer(2), Weight: utils.Float64Pointer(1.0), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), SharedGroups: utils.StringMapPointer(utils.NewStringMap("test2")), }, - ThresholdType: utils.TRIGGER_MAX_BALANCE, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"BalanceDirections":"*out", "ThresholdType":"%v", "ThresholdValue": %v, "DestinationIds": "%v", "BalanceWeight": %v, "BalanceRatingSubject": "%v", "BalanceSharedGroup": "%v"}`, utils.TRIGGER_MAX_BALANCE, 2, "NAT", 1.0, "test1", "test2")} + a := &Action{Balance: &BalanceFilter{ + Type: utils.StringPointer(utils.MONETARY), + RatingSubject: utils.StringPointer("test1"), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), + Value: utils.Float64Pointer(2), + Weight: utils.Float64Pointer(1.0), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), + SharedGroups: utils.StringMapPointer(utils.NewStringMap("test2")), + }, ExtraParameters: fmt.Sprintf(`{"UniqueID":"ZIP", "GroupID":"TEST", "ThresholdType":"TT"}`)} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -684,7 +694,7 @@ func TestActionResetTriggres(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) @@ -697,7 +707,7 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) @@ -710,7 +720,7 @@ func TestActionResetTriggresActionFilter(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}}, nil) @@ -723,7 +733,7 @@ func TestActionSetPostpaid(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } allowNegativeAction(ub, nil, nil, nil) @@ -737,7 +747,7 @@ func TestActionSetPrepaid(t *testing.T) { Id: "TEST_UB", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } denyNegativeAction(ub, nil, nil, nil) @@ -751,7 +761,7 @@ func TestActionResetPrepaid(t *testing.T) { Id: "TEST_UB", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) @@ -761,7 +771,7 @@ func TestActionResetPrepaid(t *testing.T) { ub.BalanceMap[utils.VOICE][0].GetValue() != 0 || ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { t.Log(ub.BalanceMap) - t.Error("Reset prepaid action failed!") + t.Error("Reset account action failed!") } } @@ -769,7 +779,7 @@ func TestActionResetPostpaid(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) @@ -777,7 +787,7 @@ func TestActionResetPostpaid(t *testing.T) { len(ub.UnitCounters) != 0 || ub.BalanceMap[utils.VOICE][0].GetValue() != 0 || ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { - t.Error("Reset postpaid action failed!") + t.Error("Reset account action failed!") } } @@ -785,17 +795,17 @@ func TestActionTopupResetCredit(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 10 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || // InitCounters finds no counters len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { - t.Errorf("Topup reset action failed: %+v", ub.BalanceMap[utils.MONETARY][0]) + t.Errorf("Topup reset action failed: %+s", utils.ToIJSON(ub)) } } @@ -828,7 +838,7 @@ func TestActionTopupResetCreditId(t *testing.T) { }, }, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Id: utils.StringPointer("TEST_B"), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), ID: utils.StringPointer("TEST_B"), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -862,15 +872,15 @@ func TestActionTopupResetMinutes(t *testing.T) { BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 5 || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { t.Errorf("Topup reset minutes action failed: %+v", ub.BalanceMap[utils.VOICE][0]) @@ -881,14 +891,14 @@ func TestActionTopupCredit(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { t.Error("Topup action failed!", ub.BalanceMap[utils.MONETARY].GetTotalValue()) @@ -899,15 +909,15 @@ func TestActionTopupMinutes(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE].GetTotalValue() != 15 || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { t.Error("Topup minutes action failed!", ub.BalanceMap[utils.VOICE]) @@ -918,17 +928,17 @@ func TestActionDebitCredit(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 90 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { - t.Error("Debit action failed!", ub.BalanceMap[utils.MONETARY].GetTotalValue()) + t.Error("Debit action failed!", utils.ToIJSON(ub)) } } @@ -936,15 +946,15 @@ func TestActionDebitMinutes(t *testing.T) { ub := &Account{ Id: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.VOICE][0].GetValue() != 5 || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || - len(ub.UnitCounters) != 1 || + len(ub.UnitCounters) != 0 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { t.Error("Debit minutes action failed!", ub.BalanceMap[utils.VOICE][0]) @@ -961,24 +971,24 @@ func TestActionResetAllCounters(t *testing.T) { &Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT)}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, } ub.InitCounters() resetCountersAction(ub, nil, nil, nil) if !ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || - len(ub.UnitCounters[0].Balances) != 1 || + len(ub.UnitCounters[utils.MONETARY][0].Counters) != 1 || len(ub.BalanceMap[utils.MONETARY]) != 1 || ub.ActionTriggers[0].Executed != true { - t.Errorf("Reset counters action failed: %+v %+v %+v", ub.UnitCounters, ub.UnitCounters[0], ub.UnitCounters[0].Balances[0]) + t.Errorf("Reset counters action failed: %+v %+v %+v", ub.UnitCounters, ub.UnitCounters[utils.MONETARY][0], ub.UnitCounters[utils.MONETARY][0].Counters[0]) } if len(ub.UnitCounters) < 1 { t.FailNow() } - mb := ub.UnitCounters[0].Balances[0] - if mb.Weight != 20 || mb.GetValue() != 0 || mb.DestinationIds["NAT"] == false { - t.Errorf("Balance cloned incorrectly: %+v", mb) + c := ub.UnitCounters[utils.MONETARY][0].Counters[0] + if c.Filter.GetWeight() != 20 || c.Value != 0 || c.Filter.GetDestinationIDs()["NAT"] == false { + t.Errorf("Balance cloned incorrectly: %+v", c) } } @@ -997,20 +1007,20 @@ func TestActionResetCounterOnlyDefault(t *testing.T) { if !ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || - len(ub.UnitCounters[0].Balances) != 1 || + len(ub.UnitCounters[utils.MONETARY][0].Counters) != 1 || len(ub.BalanceMap[utils.VOICE]) != 2 || ub.ActionTriggers[0].Executed != true { - for _, b := range ub.UnitCounters[0].Balances { + for _, b := range ub.UnitCounters[utils.MONETARY][0].Counters { t.Logf("B: %+v", b) } t.Errorf("Reset counters action failed: %+v", ub.UnitCounters) } - if len(ub.UnitCounters) < 1 || len(ub.UnitCounters[0].Balances) < 1 { + if len(ub.UnitCounters) < 1 || len(ub.UnitCounters[utils.MONETARY][0].Counters) < 1 { t.FailNow() } - mb := ub.UnitCounters[0].Balances[0] - if mb.Weight != 0 || mb.GetValue() != 0 || len(mb.DestinationIds) != 0 { - t.Errorf("Balance cloned incorrectly: %+v!", mb) + c := ub.UnitCounters[utils.MONETARY][0].Counters[0] + if c.Filter.GetWeight() != 0 || c.Value != 0 || len(c.Filter.GetDestinationIDs()) != 0 { + t.Errorf("Balance cloned incorrectly: %+v!", c) } } @@ -1019,7 +1029,7 @@ func TestActionResetCounterCredit(t *testing.T) { Id: "TEST_UB", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{&UnitCounter{BalanceType: utils.MONETARY, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}, &UnitCounter{BalanceType: utils.SMS, Balances: BalanceChain{&Balance{Value: 1, Directions: utils.NewStringMap(utils.OUT)}}}}, + UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}, utils.SMS: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}} @@ -1039,7 +1049,7 @@ func TestActionTriggerLogging(t *testing.T) { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), }, ThresholdValue: 100.0, Weight: 10.0, @@ -1142,7 +1152,7 @@ func TestTopupAction(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minu") a := &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1163,7 +1173,7 @@ func TestTopupActionLoaded(t *testing.T) { initialUb, _ := accountingStorage.GetAccount("vdf:minitsboy") a := &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1190,7 +1200,7 @@ func TestActionCdrlogEmpty(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1212,11 +1222,11 @@ func TestActionCdrlogWithParams(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1239,11 +1249,11 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: utils.Float64Pointer(25), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1373,7 +1383,43 @@ func TestActionTransactionBalanceType(t *testing.T) { if err != nil || acc == nil { t.Error("Error getting account: ", acc, err) } - if acc.BalanceMap[utils.MONETARY][0].Value != 10 { + if acc.BalanceMap[utils.MONETARY][0].Value != 11.1 { + t.Errorf("Transaction didn't work: %v", acc.BalanceMap[utils.MONETARY][0].Value) + } +} + +func TestActionTransactionBalanceNotType(t *testing.T) { + err := accountingStorage.SetAccount(&Account{ + Id: "cgrates.org:trans", + BalanceMap: map[string]BalanceChain{ + utils.MONETARY: BalanceChain{&Balance{ + Value: 10, + }}, + }, + }) + if err != nil { + t.Error("Error setting account: ", err) + } + at := &ActionTiming{ + accountIDs: utils.StringMap{"cgrates.org:trans": true}, + Timing: &RateInterval{}, + actions: []*Action{ + &Action{ + ActionType: TOPUP, + Balance: &BalanceFilter{Value: utils.Float64Pointer(1.1), Type: utils.StringPointer(utils.VOICE)}, + }, + &Action{ + ActionType: TOPUP, + Balance: &BalanceFilter{Type: utils.StringPointer("test")}, + }, + }, + } + err = at.Execute() + acc, err := accountingStorage.GetAccount("cgrates.org:trans") + if err != nil || acc == nil { + t.Error("Error getting account: ", acc, err) + } + if acc.BalanceMap[utils.MONETARY][0].Value != 10.0 { t.Errorf("Transaction didn't work: %v", acc.BalanceMap[utils.MONETARY][0].Value) } } @@ -1453,7 +1499,7 @@ func TestActionRemoveBalance(t *testing.T) { ActionType: REMOVE_BALANCE, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT", "RET")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT", "RET")), }, }, }, @@ -1850,43 +1896,58 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { } a1 := &Action{ - ActionType: "*enable_disable_balance", + ActionType: "*set_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*sms"), - Weight: utils.Float64Pointer(10), + ID: utils.StringPointer("for_v3hsillmilld500m_sms_ill"), Disabled: utils.BoolPointer(true), }, Weight: 9, } a2 := &Action{ - ActionType: "*enable_disable_balance", + ActionType: "*set_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*sms"), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), + ID: utils.StringPointer("for_v3hsillmilld500m_mms_ill"), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), Weight: utils.Float64Pointer(10), Disabled: utils.BoolPointer(true), }, Weight: 8, } a3 := &Action{ - ActionType: "*enable_disable_balance", + ActionType: "*set_balance", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Balance: &BalanceFilter{ + Type: utils.StringPointer("*sms"), + ID: utils.StringPointer("for_v3hsillmilld500m_sms_ill"), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), + Weight: utils.Float64Pointer(10), + Disabled: utils.BoolPointer(true), + }, + Weight: 8, + } + a4 := &Action{ + ActionType: "*set_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*data"), + Uuid: utils.StringPointer("fc927edb-1bd6-425e-a2a3-9fd8bafaa524"), RatingSubject: utils.StringPointer("for_v3hsillmilld500m_data_forfait"), Weight: utils.Float64Pointer(10), Disabled: utils.BoolPointer(true), }, Weight: 7, } - a4 := &Action{ - ActionType: "*enable_disable_balance", + a5 := &Action{ + ActionType: "*set_balance", Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*voice"), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), + ID: utils.StringPointer("for_v3hsillmilld500m_voice_3_h"), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("FRANCE_NATIONAL")), Weight: utils.Float64Pointer(10), Disabled: utils.BoolPointer(true), }, @@ -1895,7 +1956,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { at := &ActionTiming{ accountIDs: utils.StringMap{"cgrates.org:af": true}, - actions: Actions{a1, a2, a3, a4}, + actions: Actions{a1, a2, a3, a4, a5}, } at.Execute() @@ -1957,7 +2018,7 @@ func TestActionSetBalance(t *testing.T) { a := &Action{ ActionType: SET_BALANCE, Balance: &BalanceFilter{ - Id: utils.StringPointer("m2"), + ID: utils.StringPointer("m2"), Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(11), Weight: utils.Float64Pointer(10), diff --git a/engine/balance_filter.go b/engine/balance_filter.go new file mode 100644 index 000000000..e9f95e68c --- /dev/null +++ b/engine/balance_filter.go @@ -0,0 +1,220 @@ +package engine + +import ( + "reflect" + "time" + + "github.com/cgrates/cgrates/utils" +) + +type BalanceFilter struct { + Uuid *string + ID *string + Type *string + Value *float64 + Directions *utils.StringMap + ExpirationDate *time.Time + Weight *float64 + DestinationIDs *utils.StringMap + RatingSubject *string + Categories *utils.StringMap + SharedGroups *utils.StringMap + TimingIDs *utils.StringMap + Timings []*RITiming + Disabled *bool + Factor *ValueFactor + Blocker *bool +} + +func (bp *BalanceFilter) CreateBalance() *Balance { + b := &Balance{ + Uuid: bp.GetUuid(), + Id: bp.GetID(), + Value: bp.GetValue(), + Directions: bp.GetDirections(), + ExpirationDate: bp.GetExpirationDate(), + Weight: bp.GetWeight(), + DestinationIds: bp.GetDestinationIDs(), + RatingSubject: bp.GetRatingSubject(), + Categories: bp.GetCategories(), + SharedGroups: bp.GetSharedGroups(), + Timings: bp.Timings, + TimingIDs: bp.GetTimingIDs(), + Disabled: bp.GetDisabled(), + Factor: bp.GetFactor(), + Blocker: bp.GetBlocker(), + } + return b.Clone() +} + +func (bp *BalanceFilter) LoadFromBalance(b *Balance) *BalanceFilter { + if b.Uuid != "" { + bp.Uuid = &b.Uuid + } + if b.Id != "" { + bp.ID = &b.Id + } + if b.Value != 0 { + bp.Value = &b.Value + } + if len(b.Directions) != 0 { + bp.Directions = &b.Directions + } + if !b.ExpirationDate.IsZero() { + bp.ExpirationDate = &b.ExpirationDate + } + if b.Weight != 0 { + bp.Weight = &b.Weight + } + if len(b.DestinationIds) != 0 { + bp.DestinationIDs = &b.DestinationIds + } + if b.RatingSubject != "" { + bp.RatingSubject = &b.RatingSubject + } + if len(b.Categories) != 0 { + bp.Categories = &b.Categories + } + if len(b.SharedGroups) != 0 { + bp.SharedGroups = &b.SharedGroups + } + if len(b.TimingIDs) != 0 { + bp.TimingIDs = &b.TimingIDs + } + if len(b.Factor) != 0 { + bp.Factor = &b.Factor + } + if b.Disabled { + bp.Disabled = &b.Disabled + } + if b.Blocker { + bp.Blocker = &b.Blocker + } + return bp +} + +func (bp *BalanceFilter) Equal(o *BalanceFilter) bool { + if bp.ID != nil && o.ID != nil { + return *bp.ID == *o.ID + } + return reflect.DeepEqual(bp, o) +} + +func (bp *BalanceFilter) GetType() string { + if bp == nil || bp.Type == nil { + return "" + } + return *bp.Type +} + +func (bp *BalanceFilter) GetValue() float64 { + if bp == nil || bp.Value == nil { + return 0.0 + } + return *bp.Value +} + +func (bp *BalanceFilter) SetValue(v float64) { + if bp.Value == nil { + bp.Value = new(float64) + } + *bp.Value = v +} + +func (bp *BalanceFilter) GetUuid() string { + if bp == nil || bp.Uuid == nil { + return "" + } + return *bp.Uuid +} + +func (bp *BalanceFilter) GetID() string { + if bp == nil || bp.ID == nil { + return "" + } + return *bp.ID +} + +func (bp *BalanceFilter) GetDirections() utils.StringMap { + if bp == nil || bp.Directions == nil { + return utils.StringMap{} + } + return *bp.Directions +} + +func (bp *BalanceFilter) GetDestinationIDs() utils.StringMap { + if bp == nil || bp.DestinationIDs == nil { + return utils.StringMap{} + } + return *bp.DestinationIDs +} + +func (bp *BalanceFilter) GetCategories() utils.StringMap { + if bp == nil || bp.Categories == nil { + return utils.StringMap{} + } + return *bp.Categories +} + +func (bp *BalanceFilter) GetTimingIDs() utils.StringMap { + if bp == nil || bp.TimingIDs == nil { + return utils.StringMap{} + } + return *bp.TimingIDs +} + +func (bp *BalanceFilter) GetSharedGroups() utils.StringMap { + if bp == nil || bp.SharedGroups == nil { + return utils.StringMap{} + } + return *bp.SharedGroups +} + +func (bp *BalanceFilter) GetWeight() float64 { + if bp == nil || bp.Weight == nil { + return 0.0 + } + return *bp.Weight +} + +func (bp *BalanceFilter) GetRatingSubject() string { + if bp == nil || bp.RatingSubject == nil { + return "" + } + return *bp.RatingSubject +} + +func (bp *BalanceFilter) GetDisabled() bool { + if bp == nil || bp.Disabled == nil { + return false + } + return *bp.Disabled +} + +func (bp *BalanceFilter) GetBlocker() bool { + if bp == nil || bp.Blocker == nil { + return false + } + return *bp.Blocker +} + +func (bp *BalanceFilter) GetExpirationDate() time.Time { + if bp == nil || bp.ExpirationDate == nil { + return time.Time{} + } + return *bp.ExpirationDate +} + +func (bp *BalanceFilter) GetFactor() ValueFactor { + if bp == nil || bp.Factor == nil { + return ValueFactor{} + } + return *bp.Factor +} + +func (bp *BalanceFilter) HasExpirationDate() bool { + if bp.ExpirationDate == nil { + return true + } + return (*bp.ExpirationDate).IsZero() +} diff --git a/engine/balances.go b/engine/balances.go index 80fc005d6..633b17063 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -79,14 +79,14 @@ func (b *Balance) MatchFilter(o *BalanceFilter, skipIds bool) bool { if !skipIds && o.Uuid != nil && *o.Uuid != "" { return b.Uuid == *o.Uuid } - if !skipIds && o.Id != nil && *o.Id != "" { - return b.Id == *o.Id + if !skipIds && o.ID != nil && *o.ID != "" { + return b.Id == *o.ID } return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && (o.Weight == nil || b.Weight == *o.Weight) && (o.Blocker == nil || b.Blocker == *o.Blocker) && (o.Disabled == nil || b.Disabled == *o.Disabled) && - (o.DestinationIds == nil || b.DestinationIds.Includes(*o.DestinationIds)) && + (o.DestinationIDs == nil || b.DestinationIds.Includes(*o.DestinationIDs)) && (o.Directions == nil || b.Directions.Includes(*o.Directions)) && (o.Categories == nil || b.Categories.Includes(*o.Categories)) && (o.TimingIDs == nil || b.TimingIDs.Includes(*o.TimingIDs)) && @@ -101,14 +101,14 @@ func (b *Balance) HardMatchFilter(o *BalanceFilter, skipIds bool) bool { if !skipIds && o.Uuid != nil && *o.Uuid != "" { return b.Uuid == *o.Uuid } - if !skipIds && o.Id != nil && *o.Id != "" { - return b.Id == *o.Id + if !skipIds && o.ID != nil && *o.ID != "" { + return b.Id == *o.ID } return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && (o.Weight == nil || b.Weight == *o.Weight) && (o.Blocker == nil || b.Blocker == *o.Blocker) && (o.Disabled == nil || b.Disabled == *o.Disabled) && - (o.DestinationIds == nil || b.DestinationIds.Equal(*o.DestinationIds)) && + (o.DestinationIDs == nil || b.DestinationIds.Equal(*o.DestinationIDs)) && (o.Directions == nil || b.Directions.Equal(*o.Directions)) && (o.Categories == nil || b.Categories.Equal(*o.Categories)) && (o.TimingIDs == nil || b.TimingIDs.Equal(*o.TimingIDs)) && @@ -116,40 +116,6 @@ func (b *Balance) HardMatchFilter(o *BalanceFilter, skipIds bool) bool { (o.RatingSubject == nil || b.RatingSubject == *o.RatingSubject) } -func (b *Balance) MatchCCFilter(cc *CallCost) bool { - if len(b.Categories) > 0 && cc.Category != "" && b.Categories[cc.Category] == false { - return false - } - if len(b.Directions) > 0 && cc.Direction != "" && b.Directions[cc.Direction] == false { - return false - } - - // match destination ids - foundMatchingDestId := false - if len(b.DestinationIds) > 0 && cc.Destination != "" { - for _, p := range utils.SplitPrefix(cc.Destination, MIN_PREFIX_MATCH) { - if x, err := cache2go.Get(utils.DESTINATION_PREFIX + p); err == nil { - destIds := x.(map[interface{}]struct{}) - for filterDestId := range b.DestinationIds { - if _, ok := destIds[filterDestId]; ok { - foundMatchingDestId = true - break - } - } - } - if foundMatchingDestId { - break - } - } - } else { - foundMatchingDestId = true - } - if !foundMatchingDestId { - return false - } - return true -} - // the default balance has standard Id func (b *Balance) IsDefault() bool { return b.Id == utils.META_DEFAULT diff --git a/engine/balances_test.go b/engine/balances_test.go index 93a9f23db..ec95fad73 100644 --- a/engine/balances_test.go +++ b/engine/balances_test.go @@ -90,7 +90,7 @@ func TestBalanceEqual(t *testing.T) { func TestBalanceMatchFilter(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &BalanceFilter{Weight: utils.Float64Pointer(1), RatingSubject: nil, DestinationIds: nil} + mb2 := &BalanceFilter{Weight: utils.Float64Pointer(1), RatingSubject: nil, DestinationIDs: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -106,7 +106,7 @@ func TestBalanceMatchFilterEmpty(t *testing.T) { func TestBalanceMatchFilterId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 2, precision: 2, RatingSubject: "2", DestinationIds: utils.NewStringMap("NAT")} - mb2 := &BalanceFilter{Id: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} + mb2 := &BalanceFilter{ID: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIDs: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) } @@ -114,7 +114,7 @@ func TestBalanceMatchFilterId(t *testing.T) { func TestBalanceMatchFilterDiffId(t *testing.T) { mb1 := &Balance{Id: "T1", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &BalanceFilter{Id: utils.StringPointer("T2"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIds: nil} + mb2 := &BalanceFilter{ID: utils.StringPointer("T2"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIDs: nil} if mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v != %+v", mb1, mb2) } @@ -129,7 +129,7 @@ func TestBalanceClone(t *testing.T) { } func TestBalanceMatchActionTriggerId(t *testing.T) { - at := &ActionTrigger{Balance: &BalanceFilter{Id: utils.StringPointer("test")}} + at := &ActionTrigger{Balance: &BalanceFilter{ID: utils.StringPointer("test")}} b := &Balance{Id: "test"} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -143,14 +143,14 @@ func TestBalanceMatchActionTriggerId(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.Id = "test" - at.Balance.Id = nil + at.Balance.ID = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } } func TestBalanceMatchActionTriggerDestination(t *testing.T) { - at := &ActionTrigger{Balance: &BalanceFilter{DestinationIds: utils.StringMapPointer(utils.NewStringMap("test"))}} + at := &ActionTrigger{Balance: &BalanceFilter{DestinationIDs: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{DestinationIds: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -164,7 +164,7 @@ func TestBalanceMatchActionTriggerDestination(t *testing.T) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.DestinationIds = utils.NewStringMap("test") - at.Balance.DestinationIds = nil + at.Balance.DestinationIDs = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } diff --git a/engine/callcost.go b/engine/callcost.go index 98984333c..85c8a3c4b 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -21,6 +21,7 @@ import ( "errors" "time" + "github.com/cgrates/cgrates/cache2go" "github.com/cgrates/cgrates/utils" ) @@ -180,3 +181,40 @@ func (cc *CallCost) updateCost() { } cc.Cost = cost } + +func (cc *CallCost) MatchCCFilter(bf *BalanceFilter) bool { + if bf == nil { + return true + } + if bf.Categories != nil && cc.Category != "" && (*bf.Categories)[cc.Category] == false { + return false + } + if bf.Directions != nil && cc.Direction != "" && (*bf.Directions)[cc.Direction] == false { + return false + } + + // match destination ids + foundMatchingDestID := false + if bf.DestinationIDs != nil && cc.Destination != "" { + for _, p := range utils.SplitPrefix(cc.Destination, MIN_PREFIX_MATCH) { + if x, err := cache2go.Get(utils.DESTINATION_PREFIX + p); err == nil { + destIds := x.(map[interface{}]struct{}) + for filterDestID := range *bf.DestinationIDs { + if _, ok := destIds[filterDestID]; ok { + foundMatchingDestID = true + break + } + } + } + if foundMatchingDestID { + break + } + } + } else { + foundMatchingDestID = true + } + if !foundMatchingDestID { + return false + } + return true +} diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index cbac46036..852b91a79 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -42,7 +42,7 @@ func init() { func populateDB() { ats := []*Action{ &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10)}}, - &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: utils.Float64Pointer(10), DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, + &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, } ats1 := []*Action{ diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 9c6073051..69f090c8d 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -832,10 +832,12 @@ func TestLoadActions(t *testing.T) { Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: utils.Float64Pointer(10), Weight: utils.Float64Pointer(10), - DestinationIds: nil, + DestinationIDs: nil, TimingIDs: nil, SharedGroups: nil, Categories: nil, + Disabled: utils.BoolPointer(false), + Blocker: utils.BoolPointer(false), }, }, &Action{ @@ -851,10 +853,12 @@ func TestLoadActions(t *testing.T) { Value: utils.Float64Pointer(100), Weight: utils.Float64Pointer(10), RatingSubject: utils.StringPointer("test"), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), TimingIDs: nil, SharedGroups: nil, Categories: nil, + Disabled: utils.BoolPointer(false), + Blocker: utils.BoolPointer(false), }, }, } @@ -871,13 +875,15 @@ func TestLoadActions(t *testing.T) { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: nil, + DestinationIDs: nil, Uuid: as2[0].Balance.Uuid, Value: utils.Float64Pointer(100), Weight: utils.Float64Pointer(10), SharedGroups: utils.StringMapPointer(utils.NewStringMap("SG1")), TimingIDs: nil, Categories: nil, + Disabled: utils.BoolPointer(false), + Blocker: utils.BoolPointer(false), }, }, } @@ -894,11 +900,12 @@ func TestLoadActions(t *testing.T) { Balance: &BalanceFilter{ Uuid: as3[0].Balance.Uuid, Directions: nil, - DestinationIds: nil, + DestinationIDs: nil, TimingIDs: nil, Categories: nil, SharedGroups: nil, Blocker: utils.BoolPointer(false), + Disabled: utils.BoolPointer(false), }, }, } @@ -1048,19 +1055,22 @@ func TestLoadActionTriggers(t *testing.T) { ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ThresholdValue: 10, Balance: &BalanceFilter{ + ID: utils.StringPointer("st0"), Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), Categories: nil, TimingIDs: nil, SharedGroups: nil, + Disabled: nil, + Blocker: nil, }, Weight: 10, ActionsId: "SOME_1", Executed: false, } if !reflect.DeepEqual(atr, expected) { - t.Errorf("Error loading action trigger: %+v", atr) + t.Errorf("Error loading action trigger: %+v", utils.ToIJSON(atr.Balance)) } atr = csvr.actionsTriggers["STANDARD_TRIGGER"][1] expected = &ActionTrigger{ @@ -1071,7 +1081,7 @@ func TestLoadActionTriggers(t *testing.T) { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("GERMANY")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY")), Categories: nil, TimingIDs: nil, SharedGroups: nil, @@ -1093,18 +1103,22 @@ func TestLoadAccountActions(t *testing.T) { expected := &Account{ Id: "vdf:minitsboy", UnitCounters: UnitCounters{ - &UnitCounter{ - BalanceType: "*voice", - CounterType: "*event", - Balances: BalanceChain{ - &Balance{ - Id: "2c2ce3c9-d62b-49dc-82a5-2a17bdc6eb4e", - Value: 0, - Directions: utils.NewStringMap("*out"), - DestinationIds: utils.NewStringMap("GERMANY_O2"), - SharedGroups: nil, - Categories: nil, - TimingIDs: nil, + utils.VOICE: []*UnitCounter{ + &UnitCounter{ + CounterType: "*event", + Counters: CounterFilters{ + &CounterFilter{ + Value: 0, + Filter: &BalanceFilter{ + ID: utils.StringPointer("st0"), + Type: utils.StringPointer(utils.VOICE), + Directions: utils.StringMapPointer(utils.NewStringMap("*out")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), + SharedGroups: nil, + Categories: nil, + TimingIDs: nil, + }, + }, }, }, }, @@ -1115,11 +1129,11 @@ func TestLoadAccountActions(t *testing.T) { for i, atr := range aa.ActionTriggers { csvr.actionsTriggers["STANDARD_TRIGGER"][i].ID = atr.ID } - for i, b := range aa.UnitCounters[0].Balances { - expected.UnitCounters[0].Balances[i].Id = b.Id + for i, b := range aa.UnitCounters[utils.VOICE][0].Counters { + expected.UnitCounters[utils.VOICE][0].Counters[i].Filter.ID = b.Filter.ID } - if !reflect.DeepEqual(aa.UnitCounters[0].Balances[0], expected.UnitCounters[0].Balances[0]) { - t.Errorf("Error loading account action: %+v \n %+v", aa.UnitCounters[0].Balances[0], expected.UnitCounters[0].Balances[0]) + if !reflect.DeepEqual(aa.UnitCounters[utils.VOICE][0].Counters[0], expected.UnitCounters[utils.VOICE][0].Counters[0]) { + t.Errorf("Error loading account action: %+v", utils.ToIJSON(aa.UnitCounters[utils.VOICE][0].Counters[0].Filter)) } // test that it does not overwrite balances existing, err := accountingStorage.GetAccount(aa.Id) diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 085012a37..94147a6a9 100644 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -265,8 +265,8 @@ func TestTPActionsAsExportSlice(t *testing.T) { }, } expectedSlc := [][]string{ - []string{"TEST_ACTIONS", "*topup_reset", "", "", "", "*monetary", utils.OUT, "call", "*any", "special1", "GROUP1", "*never", "", "5", "10", "false", "false", "10"}, - []string{"TEST_ACTIONS", "*http_post", "http://localhost/¶m1=value1", "", "", "", "", "", "", "", "", "", "", "0", "0", "false", "false", "20"}, + []string{"TEST_ACTIONS", "*topup_reset", "", "", "", "*monetary", utils.OUT, "call", "*any", "special1", "GROUP1", "*never", "", "5.0", "10.0", "", "", "10"}, + []string{"TEST_ACTIONS", "*http_post", "http://localhost/¶m1=value1", "", "", "", "", "", "", "", "", "", "", "0.0", "0.0", "", "", "20"}, } ms := APItoModelAction(tpActs) @@ -597,8 +597,8 @@ func TestTPActionPlanAsExportSlice(t *testing.T) { }, } expectedSlc := [][]string{ - []string{"STANDARD_TRIGGERS", "1", "*min_balance", "2", "false", "0", "", "", "b1", "*monetary", "*out", "call", "", "special1", "SHARED_1", "*never", "T1", "0", "false", "false", "0", "LOG_WARNING", "10"}, - []string{"STANDARD_TRIGGERS", "2", "*max_event_counter", "5", "false", "0", "", "", "b2", "*monetary", "*out", "call", "FS_USERS", "special1", "SHARED_1", "*never", "T1", "0", "false", "false", "0", "LOG_WARNING", "10"}, + []string{"STANDARD_TRIGGERS", "1", "*min_balance", "2", "false", "0", "", "", "b1", "*monetary", "*out", "call", "", "special1", "SHARED_1", "*never", "T1", "0.0", "false", "false", "0", "LOG_WARNING", "10"}, + []string{"STANDARD_TRIGGERS", "2", "*max_event_counter", "5", "false", "0", "", "", "b2", "*monetary", "*out", "call", "FS_USERS", "special1", "SHARED_1", "*never", "T1", "0.0", "false", "false", "0", "LOG_WARNING", "10"}, } ms := APItoModelActionTrigger(at) var slc [][]string diff --git a/engine/storage_test.go b/engine/storage_test.go index 2c1b9b808..ee8ed42cd 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -311,8 +311,7 @@ func TestStorageTask(t *testing.T) { func GetUB() *Account { uc := &UnitCounter{ - BalanceType: utils.SMS, - Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, + Counters: CounterFilters{&CounterFilter{Value: 1}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET"))}}}, } at := &ActionTrigger{ ID: "some_uuid", @@ -320,7 +319,7 @@ func GetUB() *Account { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), }, Weight: 10.0, ActionsId: "Commando", @@ -331,7 +330,7 @@ func GetUB() *Account { Id: "rif", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, utils.DATA: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, - UnitCounters: UnitCounters{uc, uc}, + UnitCounters: UnitCounters{utils.SMS: []*UnitCounter{uc, uc}}, ActionTriggers: ActionTriggers{at, at, at}, } return ub diff --git a/engine/tp_reader.go b/engine/tp_reader.go index bfa6615d6..469165aba 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -524,7 +524,7 @@ func (tpr *TpReader) LoadActions() (err error) { Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { - acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + acts[idx].Balance.ID = utils.StringPointer(tpact.BalanceId) } if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) @@ -563,7 +563,7 @@ func (tpr *TpReader) LoadActions() (err error) { acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) } if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { - acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + acts[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) } if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) @@ -706,7 +706,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { MinQueuedItems: atr.MinQueuedItems, } if atr.BalanceId != "" && atr.BalanceId != utils.ANY { - atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + atrs[idx].Balance.ID = utils.StringPointer(atr.BalanceId) } if atr.BalanceType != "" && atr.BalanceType != utils.ANY { @@ -738,7 +738,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) } if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { - atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + atrs[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) } if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) @@ -906,7 +906,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error ActionsId: atr.ActionsId, } if atr.BalanceId != "" && atr.BalanceId != utils.ANY { - atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + atrs[idx].Balance.ID = utils.StringPointer(atr.BalanceId) } if atr.BalanceType != "" && atr.BalanceType != utils.ANY { @@ -938,7 +938,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) } if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { - atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + atrs[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) } if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) @@ -1006,7 +1006,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { - acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + acts[idx].Balance.ID = utils.StringPointer(tpact.BalanceId) } if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) @@ -1038,7 +1038,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) } if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { - acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + acts[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) } if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) @@ -1243,7 +1243,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { ActionsId: atr.ActionsId, } if atr.BalanceId != "" && atr.BalanceId != utils.ANY { - atrs[idx].Balance.Id = utils.StringPointer(atr.BalanceId) + atrs[idx].Balance.ID = utils.StringPointer(atr.BalanceId) } if atr.BalanceType != "" && atr.BalanceType != utils.ANY { @@ -1275,7 +1275,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { atrs[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDirections)) } if atr.BalanceDestinationIds != "" && atr.BalanceDestinationIds != utils.ANY { - atrs[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) + atrs[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceDestinationIds)) } if atr.BalanceSharedGroups != "" && atr.BalanceSharedGroups != utils.ANY { atrs[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(atr.BalanceSharedGroups)) @@ -1352,7 +1352,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { Balance: &BalanceFilter{}, } if tpact.BalanceId != "" && tpact.BalanceId != utils.ANY { - acts[idx].Balance.Id = utils.StringPointer(tpact.BalanceId) + acts[idx].Balance.ID = utils.StringPointer(tpact.BalanceId) } if tpact.BalanceType != "" && tpact.BalanceType != utils.ANY { acts[idx].Balance.Type = utils.StringPointer(tpact.BalanceType) @@ -1384,7 +1384,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { acts[idx].Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(tpact.Directions)) } if tpact.DestinationIds != "" && tpact.DestinationIds != utils.ANY { - acts[idx].Balance.DestinationIds = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) + acts[idx].Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(tpact.DestinationIds)) } if tpact.SharedGroups != "" && tpact.SharedGroups != utils.ANY { acts[idx].Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(tpact.SharedGroups)) diff --git a/engine/units_counter.go b/engine/units_counter.go index 7854aa9ec..b4e7f6696 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -18,29 +18,40 @@ along with this program. If not, see package engine -import ( - "log" - - "github.com/cgrates/cgrates/utils" -) +import "github.com/cgrates/cgrates/utils" // Amount of a trafic of a certain type type UnitCounter struct { - BalanceType string // *monetary/*voice/*sms/etc - CounterType string // *event or *balance - Balances BalanceChain // first balance is the general one (no destination) + CounterType string // *event or *balance + Counters CounterFilters // first balance is the general one (no destination) +} + +type CounterFilter struct { + Value float64 + Filter *BalanceFilter +} + +type CounterFilters []*CounterFilter + +func (cfs CounterFilters) HasCounter(cf *CounterFilter) bool { + for _, c := range cfs { + if c.Filter.Equal(cf.Filter) { + return true + } + } + return false } // Returns true if the counters were of the same type // Copies the value from old balances func (uc *UnitCounter) CopyCounterValues(oldUc *UnitCounter) bool { - if uc.BalanceType+uc.CounterType != oldUc.BalanceType+oldUc.CounterType { // type check + if uc.CounterType != oldUc.CounterType { // type check return false } - for _, b := range uc.Balances { - for _, oldB := range oldUc.Balances { - if b.Equal(oldB) { - b.Value = oldB.Value + for _, c := range uc.Counters { + for _, oldC := range oldUc.Counters { + if c.Filter.Equal(oldC.Filter) { + c.Value = oldC.Value break } } @@ -48,30 +59,28 @@ func (uc *UnitCounter) CopyCounterValues(oldUc *UnitCounter) bool { return true } -type UnitCounters []*UnitCounter +type UnitCounters map[string][]*UnitCounter func (ucs UnitCounters) addUnits(amount float64, kind string, cc *CallCost, b *Balance) { - for _, uc := range ucs { + counters, found := ucs[kind] + if !found { + return + } + for _, uc := range counters { if uc == nil { // safeguard continue } - if uc.BalanceType != kind { - continue - } if uc.CounterType == "" { uc.CounterType = utils.COUNTER_EVENT } - for _, bal := range uc.Balances { - log.Print(b) - if uc.CounterType == utils.COUNTER_EVENT && cc != nil && bal.MatchCCFilter(cc) { - log.Print("HERE") - bal.AddValue(amount) + for _, c := range uc.Counters { + if uc.CounterType == utils.COUNTER_EVENT && cc != nil && cc.MatchCCFilter(c.Filter) { + c.Value += amount continue } - bp := &BalanceFilter{} - bp.LoadFromBalance(bal) - if uc.CounterType == utils.COUNTER_BALANCE && b != nil && b.MatchFilter(bp, true) { - bal.AddValue(amount) + + if uc.CounterType == utils.COUNTER_BALANCE && b != nil && b.MatchFilter(c.Filter, true) { + c.Value += amount continue } } @@ -80,16 +89,18 @@ func (ucs UnitCounters) addUnits(amount float64, kind string, cc *CallCost, b *B } func (ucs UnitCounters) resetCounters(a *Action) { - for _, uc := range ucs { - if uc == nil { // safeguard + for key, counters := range ucs { + if a != nil && a.Balance.Type != nil && a.Balance.GetType() != key { continue } - if a != nil && a.Balance.Type != nil && a.Balance.GetType() != uc.BalanceType { - continue - } - for _, b := range uc.Balances { - if a == nil || a.Balance == nil || b.MatchFilter(a.Balance, false) { - b.Value = 0 + for _, c := range counters { + if c == nil { // safeguard + continue + } + for _, cf := range c.Counters { + if a == nil || a.Balance == nil || cf.Filter.Equal(a.Balance) { + cf.Value = 0 + } } } } diff --git a/engine/units_counter_test.go b/engine/units_counter_test.go index 4ca1ec849..4e80006c0 100644 --- a/engine/units_counter_test.go +++ b/engine/units_counter_test.go @@ -26,22 +26,20 @@ import ( func TestUnitsCounterAddBalance(t *testing.T) { uc := &UnitCounter{ - BalanceType: utils.SMS, - Balances: BalanceChain{&Balance{Value: 1}, &Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, + Counters: CounterFilters{&CounterFilter{Value: 1}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET"))}}}, } - UnitCounters{uc}.addUnits(20, utils.SMS, &CallCost{Destination: "test"}, nil) - if len(uc.Balances) != 3 { - t.Error("Error adding minute bucket: ", uc.Balances) + UnitCounters{utils.SMS: []*UnitCounter{uc}}.addUnits(20, utils.SMS, &CallCost{Destination: "test"}, nil) + if len(uc.Counters) != 3 { + t.Error("Error adding minute bucket: ", uc.Counters) } } func TestUnitsCounterAddBalanceExists(t *testing.T) { uc := &UnitCounter{ - BalanceType: utils.SMS, - Balances: BalanceChain{&Balance{Value: 1}, &Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}, + Counters: CounterFilters{&CounterFilter{Value: 1}, &CounterFilter{Value: 10, Filter: &BalanceFilter{Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET"))}}}, } - UnitCounters{uc}.addUnits(5, utils.SMS, &CallCost{Destination: "0723"}, nil) - if len(uc.Balances) != 3 || uc.Balances[1].GetValue() != 15 { + UnitCounters{utils.SMS: []*UnitCounter{uc}}.addUnits(5, utils.SMS, &CallCost{Destination: "0723"}, nil) + if len(uc.Counters) != 3 || uc.Counters[1].Value != 15 { t.Error("Error adding minute bucket!") } } @@ -108,14 +106,17 @@ func TestUnitCountersCountAllMonetary(t *testing.T) { a.InitCounters() a.UnitCounters.addUnits(10, utils.MONETARY, &CallCost{}, nil) - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - a.UnitCounters[0].Balances[0].Value != 10 || - a.UnitCounters[0].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + a.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 || + a.UnitCounters[utils.MONETARY][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) @@ -183,15 +184,17 @@ func TestUnitCountersCountAllMonetaryId(t *testing.T) { } a.InitCounters() a.UnitCounters.addUnits(10, utils.MONETARY, nil, &Balance{Weight: 20, Directions: utils.NewStringMap(utils.OUT)}) - - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - a.UnitCounters[0].Balances[0].Value != 0 || - a.UnitCounters[0].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + a.UnitCounters[utils.MONETARY][0].Counters[0].Value != 0 || + a.UnitCounters[utils.MONETARY][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error adding unit counters: %v", len(a.UnitCounters)) @@ -225,7 +228,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(10), }, }, @@ -234,7 +237,7 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(10), }, }, @@ -270,14 +273,17 @@ func TestUnitCountersCountAllVoiceDestinationEvent(t *testing.T) { a.InitCounters() a.UnitCounters.addUnits(10, utils.VOICE, &CallCost{Destination: "0723045326"}, nil) - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[1].Balances) != 2 || - a.UnitCounters[1].Balances[0].Value != 10 || - a.UnitCounters[1].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.VOICE][0].Counters) != 2 || + a.UnitCounters[utils.VOICE][0].Counters[0].Value != 10 || + a.UnitCounters[utils.VOICE][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error adding unit counters: %v", len(a.UnitCounters)) @@ -311,7 +317,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("NAT")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(10), }, }, @@ -320,7 +326,7 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), - DestinationIds: utils.StringMapPointer(utils.NewStringMap("RET")), + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(10), }, }, @@ -356,28 +362,34 @@ func TestUnitCountersKeepValuesAfterInit(t *testing.T) { a.InitCounters() a.UnitCounters.addUnits(10, utils.VOICE, &CallCost{Destination: "0723045326"}, nil) - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[1].Balances) != 2 || - a.UnitCounters[1].Balances[0].Value != 10 || - a.UnitCounters[1].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.VOICE][0].Counters) != 2 || + a.UnitCounters[utils.VOICE][0].Counters[0].Value != 10 || + a.UnitCounters[utils.VOICE][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error adding unit counters: %v", len(a.UnitCounters)) } a.InitCounters() - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[1].Balances) != 2 || - a.UnitCounters[1].Balances[0].Value != 10 || - a.UnitCounters[1].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.VOICE][0].Counters) != 2 || + a.UnitCounters[utils.VOICE][0].Counters[0].Value != 10 || + a.UnitCounters[utils.VOICE][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error keeping counter values after init: %v", len(a.UnitCounters)) @@ -446,14 +458,17 @@ func TestUnitCountersResetCounterById(t *testing.T) { a.InitCounters() a.UnitCounters.addUnits(10, utils.MONETARY, &CallCost{}, nil) - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - a.UnitCounters[0].Balances[0].Value != 10 || - a.UnitCounters[0].Balances[1].Value != 10 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + a.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 || + a.UnitCounters[utils.MONETARY][0].Counters[1].Value != 10 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) @@ -461,17 +476,20 @@ func TestUnitCountersResetCounterById(t *testing.T) { a.UnitCounters.resetCounters(&Action{ Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), - Id: utils.StringPointer("TestTR11"), + ID: utils.StringPointer("TestTR11"), }, }) - if len(a.UnitCounters) != 4 || - len(a.UnitCounters[0].Balances) != 2 || - a.UnitCounters[0].Balances[0].Value != 10 || - a.UnitCounters[0].Balances[1].Value != 0 { - for _, uc := range a.UnitCounters { - t.Logf("UC: %+v", uc) - for _, b := range uc.Balances { - t.Logf("B: %+v", b) + if len(a.UnitCounters) != 3 || + len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || + a.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 || + a.UnitCounters[utils.MONETARY][0].Counters[1].Value != 0 { + for key, counters := range a.UnitCounters { + t.Log(key) + for _, uc := range counters { + t.Logf("UC: %+v", uc) + for _, b := range uc.Counters { + t.Logf("B: %+v", b) + } } } t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) From 1acbaa968f976c3c1702d0a740bda020d1f10e5a Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 16 Feb 2016 11:30:52 +0200 Subject: [PATCH 081/199] execute action trigger loop protection --- engine/account.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/engine/account.go b/engine/account.go index 0130f1fb7..b1cbb463b 100644 --- a/engine/account.go +++ b/engine/account.go @@ -36,12 +36,13 @@ Structure containing information about user's credit (minutes, cents, sms...).' This can represent a user or a shared group. */ type Account struct { - Id string - BalanceMap map[string]BalanceChain - UnitCounters UnitCounters - ActionTriggers ActionTriggers - AllowNegative bool - Disabled bool + Id string + BalanceMap map[string]BalanceChain + UnitCounters UnitCounters + ActionTriggers ActionTriggers + AllowNegative bool + Disabled bool + executingTriggers bool } // User's available minutes for the specified destination @@ -572,6 +573,14 @@ func (ub *Account) refundIncrement(increment *Increment, cd *CallDescriptor, cou // Scans the action trigers and execute the actions for which trigger is met func (acc *Account) ExecuteActionTriggers(a *Action) { + if acc.executingTriggers { + return + } + acc.executingTriggers = true + defer func() { + acc.executingTriggers = false + }() + acc.ActionTriggers.Sort() for _, at := range acc.ActionTriggers { // check is effective @@ -648,7 +657,7 @@ func (acc *Account) ResetActionTriggers(a *Action) { } at.Executed = false } - acc.ExecuteActionTriggers(a) //will trigger infinite loop when executed from ExecuteActionTriggers + acc.ExecuteActionTriggers(a) } // Sets/Unsets recurrent flag for action triggers From fb8aacb2347db5c938db3d3d14fc89ab2869742d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 16 Feb 2016 22:45:23 +0200 Subject: [PATCH 082/199] unit tests passing --- apier/v1/accounts.go | 343 +++++++++++++++++---------------- apier/v1/apier.go | 131 ++++--------- apier/v1/apier_local_test.go | 8 +- apier/v1/triggers.go | 24 +-- engine/account.go | 3 + engine/action.go | 2 +- general_tests/acntacts_test.go | 2 +- 7 files changed, 232 insertions(+), 281 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index bed4ce81c..d6273b517 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -353,21 +353,21 @@ func (self *ApierV1) GetAccount(attr *utils.AttrGetAccount, reply *interface{}) type AttrAddBalance struct { Tenant string Account string - BalanceUuid string - BalanceId string + BalanceUuid *string + BalanceId *string BalanceType string - Directions string + Directions *string Value float64 - ExpiryTime string - RatingSubject string - Categories string - DestinationIds string - TimingIds string - Weight float64 - SharedGroups string + ExpiryTime *string + RatingSubject *string + Categories *string + DestinationIds *string + TimingIds *string + Weight *float64 + SharedGroups *string Overwrite bool // When true it will reset if the balance is already there - Blocker bool - Disabled bool + Blocker *bool + Disabled *bool } func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { @@ -378,13 +378,17 @@ func (self *ApierV1) DebitBalance(attr *AttrAddBalance, reply *string) error { } func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *string) error { - if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { + if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType", "Value"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) - if err != nil { - *reply = err.Error() - return err + var expTime *time.Time + if attr.ExpiryTime != nil { + expTimeVal, err := utils.ParseTimeDetectLayout(*attr.ExpiryTime, self.Config.DefaultTimezone) + if err != nil { + *reply = err.Error() + return err + } + expTime = &expTimeVal } accID := utils.AccountKey(attr.Tenant, attr.Account) if _, err := self.AccountDb.GetAccount(accID); err != nil { @@ -403,27 +407,36 @@ func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *st if attr.Overwrite { aType += "_reset" // => *topup_reset/*debit_reset } - at.SetActions(engine.Actions{ - &engine.Action{ - ActionType: aType, - BalanceType: attr.BalanceType, - Balance: &engine.Balance{ - Uuid: attr.BalanceUuid, - Id: attr.BalanceId, - Value: attr.Value, - ExpirationDate: expTime, - RatingSubject: attr.RatingSubject, - Directions: utils.ParseStringMap(attr.Directions), - DestinationIds: utils.ParseStringMap(attr.DestinationIds), - Categories: utils.ParseStringMap(attr.Categories), - Weight: attr.Weight, - SharedGroups: utils.ParseStringMap(attr.SharedGroups), - TimingIDs: utils.ParseStringMap(attr.TimingIds), - Blocker: attr.Blocker, - Disabled: attr.Disabled, - }, + a := &engine.Action{ + ActionType: aType, + Balance: &engine.BalanceFilter{ + Uuid: attr.BalanceUuid, + ID: attr.BalanceId, + Type: utils.StringPointer(attr.BalanceType), + Value: utils.Float64Pointer(attr.Value), + ExpirationDate: expTime, + RatingSubject: attr.RatingSubject, + Weight: attr.Weight, + Blocker: attr.Blocker, + Disabled: attr.Disabled, }, - }) + } + if attr.Directions != nil { + a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions)) + } + if attr.DestinationIds != nil { + a.Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.DestinationIds)) + } + if attr.Categories != nil { + a.Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(*attr.Categories)) + } + if attr.SharedGroups != nil { + a.Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(*attr.SharedGroups)) + } + if attr.TimingIds != nil { + a.Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.TimingIds)) + } + at.SetActions(engine.Actions{a}) if err := at.Execute(); err != nil { *reply = err.Error() return err @@ -432,7 +445,95 @@ func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *st return nil } -func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) error { +type AttrSetBalance struct { + Tenant string + Account string + BalanceType string + BalanceUUID *string + BalanceID *string + Directions *string + Value *float64 + ExpiryTime *string + RatingSubject *string + Categories *string + DestinationIds *string + TimingIds *string + Weight *float64 + SharedGroups *string + Blocker *bool + Disabled *bool +} + +func (self *ApierV1) SetBalance(aType string, attr *AttrSetBalance, reply *string) error { + if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if (attr.BalanceID == nil || *attr.BalanceID == "") && + (attr.BalanceUUID == nil || *attr.BalanceUUID == "") { + return utils.NewErrMandatoryIeMissing("BalanceID", "or", "BalanceUUID") + } + var expTime *time.Time + if attr.ExpiryTime != nil { + expTimeVal, err := utils.ParseTimeDetectLayout(*attr.ExpiryTime, self.Config.DefaultTimezone) + if err != nil { + *reply = err.Error() + return err + } + expTime = &expTimeVal + } + accID := utils.AccountKey(attr.Tenant, attr.Account) + if _, err := self.AccountDb.GetAccount(accID); err != nil { + // create account if not exists + account := &engine.Account{ + Id: accID, + } + if err := self.AccountDb.SetAccount(account); err != nil { + *reply = err.Error() + return err + } + } + at := &engine.ActionTiming{} + at.SetAccountIDs(utils.StringMap{accID: true}) + + a := &engine.Action{ + ActionType: engine.SET_BALANCE, + Balance: &engine.BalanceFilter{ + Uuid: attr.BalanceUUID, + ID: attr.BalanceID, + Type: utils.StringPointer(attr.BalanceType), + Value: attr.Value, + ExpirationDate: expTime, + RatingSubject: attr.RatingSubject, + Weight: attr.Weight, + Blocker: attr.Blocker, + Disabled: attr.Disabled, + }, + } + if attr.Directions != nil { + a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions)) + } + if attr.DestinationIds != nil { + a.Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.DestinationIds)) + } + if attr.Categories != nil { + a.Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(*attr.Categories)) + } + if attr.SharedGroups != nil { + a.Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(*attr.SharedGroups)) + } + if attr.TimingIds != nil { + a.Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.TimingIds)) + } + at.SetActions(engine.Actions{a}) + if err := at.Execute(); err != nil { + *reply = err.Error() + return err + } + *reply = OK + return nil +} + +/*func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } @@ -475,16 +576,20 @@ func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) e } *reply = OK return nil -} +}*/ -func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error { +func (self *ApierV1) RemoveBalances(attr *AttrSetBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) - if err != nil { - *reply = err.Error() - return err + var expTime *time.Time + if attr.ExpiryTime != nil { + expTimeVal, err := utils.ParseTimeDetectLayout(*attr.ExpiryTime, self.Config.DefaultTimezone) + if err != nil { + *reply = err.Error() + return err + } + expTime = &expTimeVal } accID := utils.AccountKey(attr.Tenant, attr.Account) if _, err := self.AccountDb.GetAccount(accID); err != nil { @@ -493,138 +598,36 @@ func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error { at := &engine.ActionTiming{} at.SetAccountIDs(utils.StringMap{accID: true}) - at.SetActions(engine.Actions{ - &engine.Action{ - ActionType: engine.REMOVE_BALANCE, - BalanceType: attr.BalanceType, - Balance: &engine.Balance{ - Uuid: attr.BalanceUuid, - Id: attr.BalanceId, - Value: attr.Value, - ExpirationDate: expTime, - RatingSubject: attr.RatingSubject, - Directions: utils.ParseStringMap(attr.Directions), - DestinationIds: utils.ParseStringMap(attr.DestinationIds), - Categories: utils.ParseStringMap(attr.Categories), - Weight: attr.Weight, - SharedGroups: utils.ParseStringMap(attr.SharedGroups), - TimingIDs: utils.ParseStringMap(attr.TimingIds), - Blocker: attr.Blocker, - Disabled: attr.Disabled, - }, + a := &engine.Action{ + ActionType: engine.SET_BALANCE, + Balance: &engine.BalanceFilter{ + Uuid: attr.BalanceUUID, + ID: attr.BalanceID, + Type: utils.StringPointer(attr.BalanceType), + Value: attr.Value, + ExpirationDate: expTime, + RatingSubject: attr.RatingSubject, + Weight: attr.Weight, + Blocker: attr.Blocker, + Disabled: attr.Disabled, }, - }) - if err := at.Execute(); err != nil { - *reply = err.Error() - return err - } - *reply = OK - return nil -} - -type AttrSetBalance struct { - Tenant string - Account string - BalanceType string - BalanceUUID *string - BalanceID *string - Directions *[]string - Value *float64 - ExpiryTime *string - RatingSubject *string - Categories *[]string - DestinationIDs *[]string - SharedGroups *[]string - TimingIDs *[]string - Weight *float64 - Blocker *bool - Disabled *bool - expTime time.Time -} - -func (attr *AttrSetBalance) SetBalance(b *engine.Balance) { - if b == nil { - return } if attr.Directions != nil { - b.Directions = utils.StringMapFromSlice(*attr.Directions) + a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions)) } - if attr.Value != nil { - b.Value = *attr.Value - } - if attr.ExpiryTime != nil { - b.ExpirationDate = attr.expTime - } - if attr.RatingSubject != nil { - b.RatingSubject = *attr.RatingSubject + if attr.DestinationIds != nil { + a.Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.DestinationIds)) } if attr.Categories != nil { - b.Categories = utils.StringMapFromSlice(*attr.Categories) - } - if attr.DestinationIDs != nil { - b.DestinationIds = utils.StringMapFromSlice(*attr.DestinationIDs) + a.Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(*attr.Categories)) } if attr.SharedGroups != nil { - b.SharedGroups = utils.StringMapFromSlice(*attr.SharedGroups) + a.Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(*attr.SharedGroups)) } - if attr.TimingIDs != nil { - b.TimingIDs = utils.StringMapFromSlice(*attr.TimingIDs) + if attr.TimingIds != nil { + a.Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.TimingIds)) } - if attr.Weight != nil { - b.Weight = *attr.Weight - } - if attr.Blocker != nil { - b.Blocker = *attr.Blocker - } - if attr.Disabled != nil { - b.Disabled = *attr.Disabled - } - b.SetDirty() // Mark the balance as dirty since we have modified and it should be checked by action triggers -} - -/* // SetAccount api using action and action timing to set balance, -//to be uncommented when using pointers in action.balance -func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { - if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { - return utils.NewErrMandatoryIeMissing(missing...) - } - if attr.BalanceID == "" && attr.BalanceUUID == "" { - return utils.NewErrMandatoryIeMissing("BalanceID", "or", "BalanceUUID") - } - expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) - if err != nil { - *reply = err.Error() - return err - } - accID := utils.ConcatenatedKey(attr.Tenant, attr.Account) - if _, err := self.AccountDb.GetAccount(accID); err != nil { - return utils.ErrNotFound - } - - at := &engine.ActionTiming{} - at.SetAccountIDs(utils.StringMap{accID: true}) - - at.SetActions(engine.Actions{ - &engine.Action{ - ActionType: engine.SET_BALANCE, - BalanceType: attr.BalanceType, - Balance: &engine.Balance{ - Uuuid: attr.BalanceUUID, - ID: attr.BalanceID, - Value: attr.Value, - ExpirationDate: expTime, - RatingSubject: attr.RatingSubject, - Directions: utils.ParseStringMap(attr.Directions), - DestinationIDs: utils.ParseStringMap(attr.DestinationIDs), - Categories: utils.ParseStringMap(attr.Categories), - Weight: attr.Weight, - SharedGroups: utils.ParseStringMap(attr.SharedGroups), - TimingIDs: utils.ParseStringMap(attr.TimingIDs), - Blocker: true, - Disabled: attr.Disabled, - }, - }, - }) + at.SetActions(engine.Actions{a}) if err := at.Execute(); err != nil { *reply = err.Error() return err @@ -632,16 +635,13 @@ func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { *reply = OK return nil } -*/ +/* To be removed after the above one proves reliable func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - if (attr.BalanceID == nil || *attr.BalanceID == "") && - (attr.BalanceUUID == nil || *attr.BalanceUUID == "") { - return utils.NewErrMandatoryIeMissing("BalanceID", "or", "BalanceUUID") - } + var err error if attr.ExpiryTime != nil { attr.expTime, err = utils.ParseTimeDetectLayout(*attr.ExpiryTime, self.Config.DefaultTimezone) @@ -727,3 +727,4 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { *reply = utils.OK return nil } +*/ diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 2b4f4b576..2497ac17a 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -19,12 +19,12 @@ along with this program. If not, see package v1 import ( - "encoding/json" "errors" "fmt" "log" "os" "path" + "strconv" "strings" "time" @@ -502,23 +502,37 @@ func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) error } storeActions := make(engine.Actions, len(attrs.Actions)) for idx, apiAct := range attrs.Actions { + var units *float64 + if x, err := strconv.ParseFloat(apiAct.Units, 64); err == nil { + units = &x + } else { + return err + } + + var weight *float64 + if x, err := strconv.ParseFloat(apiAct.BalanceWeight, 64); err == nil { + weight = &x + } else { + return err + } + a := &engine.Action{ Id: utils.GenUUID(), ActionType: apiAct.Identifier, - BalanceType: apiAct.BalanceType, Weight: apiAct.Weight, ExpirationString: apiAct.ExpiryTime, ExtraParameters: apiAct.ExtraParameters, Filter: apiAct.Filter, - Balance: &engine.Balance{ - Uuid: utils.GenUUID(), - Id: apiAct.BalanceId, - Value: apiAct.Units, - Weight: apiAct.BalanceWeight, - Directions: utils.ParseStringMap(apiAct.Directions), - DestinationIds: utils.ParseStringMap(apiAct.DestinationIds), - RatingSubject: apiAct.RatingSubject, - SharedGroups: utils.ParseStringMap(apiAct.SharedGroups), + Balance: &engine.BalanceFilter{ // TODO: update this part + Uuid: utils.StringPointer(utils.GenUUID()), + ID: utils.StringPointer(apiAct.BalanceId), + Type: utils.StringPointer(apiAct.BalanceType), + Value: units, + Weight: weight, + Directions: utils.StringMapPointer(utils.ParseStringMap(apiAct.Directions)), + DestinationIDs: utils.StringMapPointer(utils.ParseStringMap(apiAct.DestinationIds)), + RatingSubject: utils.StringPointer(apiAct.RatingSubject), + SharedGroups: utils.StringMapPointer(utils.ParseStringMap(apiAct.SharedGroups)), }, } storeActions[idx] = a @@ -543,24 +557,25 @@ func (self *ApierV1) GetActions(actsId string, reply *[]*utils.TPAction) error { } for _, engAct := range engActs { act := &utils.TPAction{Identifier: engAct.ActionType, - BalanceType: engAct.BalanceType, ExpiryTime: engAct.ExpirationString, ExtraParameters: engAct.ExtraParameters, Filter: engAct.Filter, Weight: engAct.Weight, } - if engAct.Balance != nil { - act.Units = engAct.Balance.GetValue() - act.Directions = engAct.Balance.Directions.String() - act.DestinationIds = engAct.Balance.DestinationIds.String() - act.RatingSubject = engAct.Balance.RatingSubject - act.SharedGroups = engAct.Balance.SharedGroups.String() - act.BalanceWeight = engAct.Balance.Weight - act.TimingTags = engAct.Balance.TimingIDs.String() - act.BalanceId = engAct.Balance.Id - act.Categories = engAct.Balance.Categories.String() - act.BalanceBlocker = engAct.Balance.Blocker - act.BalanceDisabled = engAct.Balance.Disabled + bf := engAct.Balance + if bf != nil { + act.BalanceType = bf.GetType() + act.Units = strconv.FormatFloat(bf.GetValue(), 'f', -1, 64) + act.Directions = bf.GetDirections().String() + act.DestinationIds = bf.GetDestinationIDs().String() + act.RatingSubject = bf.GetRatingSubject() + act.SharedGroups = bf.GetSharedGroups().String() + act.BalanceWeight = strconv.FormatFloat(bf.GetWeight(), 'f', -1, 64) + act.TimingTags = bf.GetTimingIDs().String() + act.BalanceId = bf.GetID() + act.Categories = bf.GetCategories().String() + act.BalanceBlocker = strconv.FormatBool(bf.GetBlocker()) + act.BalanceDisabled = strconv.FormatBool(bf.GetDisabled()) } acts = append(acts, act) } @@ -663,74 +678,6 @@ func (self *ApierV1) GetActionPlan(attr AttrGetActionPlan, reply *[]*engine.Acti return nil } -type AttrResetTriggeredAction struct { - Id string - Tenant string - Account string - Directions string - BalanceType string - ThresholdType string - ThresholdValue float64 - DestinationId string - BalanceWeight float64 - BalanceRatingSubject string - BalanceSharedGroup string -} - -func (self *ApierV1) ResetTriggeredActions(attr AttrResetTriggeredAction, reply *string) error { - var a *engine.Action - if attr.Id != "" { - // we can identify the trigger by the id - a = &engine.Action{Id: attr.Id} - } else { - extraParameters, err := json.Marshal(struct { - ThresholdType string - ThresholdValue float64 - DestinationId string - BalanceWeight float64 - BalanceRatingSubject string - BalanceDirections string - BalanceSharedGroup string - }{ - attr.ThresholdType, - attr.ThresholdValue, - attr.DestinationId, - attr.BalanceWeight, - attr.Directions, - attr.BalanceRatingSubject, - attr.BalanceSharedGroup, - }) - if err != nil { - *reply = err.Error() - return err - } - a = &engine.Action{ - BalanceType: attr.BalanceType, - ExtraParameters: string(extraParameters), - } - } - accID := utils.AccountKey(attr.Tenant, attr.Account) - _, err := engine.Guardian.Guard(func() (interface{}, error) { - acc, err := self.AccountDb.GetAccount(accID) - if err != nil { - return 0, err - } - - acc.ResetActionTriggers(a) - - if err = self.AccountDb.SetAccount(acc); err != nil { - return 0, err - } - return 0, nil - }, 0, accID) - if err != nil { - *reply = err.Error() - return err - } - *reply = OK - return nil -} - // Process dependencies and load a specific AccountActions profile from storDb into dataDb. func (self *ApierV1) LoadAccountActions(attrs utils.TPAccountActions, reply *string) error { if len(attrs.TPid) == 0 { diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index efbd485de..dd7a445d9 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -488,8 +488,8 @@ func TestApierTPActions(t *testing.T) { } reply := "" act := &utils.TPActions{TPid: utils.TEST_SQL, ActionsId: "PREPAID_10", Actions: []*utils.TPAction{ - &utils.TPAction{Identifier: "*topup_reset", BalanceType: "*monetary", Directions: "*out", Units: 10, ExpiryTime: "*unlimited", - DestinationIds: "*any", BalanceWeight: 10, Weight: 10}, + &utils.TPAction{Identifier: "*topup_reset", BalanceType: "*monetary", Directions: "*out", Units: "10", ExpiryTime: "*unlimited", + DestinationIds: "*any", BalanceWeight: "10", Weight: 10}, }} actWarn := &utils.TPActions{TPid: utils.TEST_SQL, ActionsId: "WARN_VIA_HTTP", Actions: []*utils.TPAction{ &utils.TPAction{Identifier: "*call_url", ExtraParameters: "http://localhost:8000", Weight: 10}, @@ -955,7 +955,7 @@ func TestApierSetActions(t *testing.T) { if !*testLocal { return } - act1 := &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: 75.0, ExpiryTime: engine.UNLIMITED, Weight: 20.0} + act1 := &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75.0", ExpiryTime: engine.UNLIMITED, Weight: 20.0} attrs1 := &utils.AttrSetActions{ActionsId: "ACTS_1", Actions: []*utils.TPAction{act1}} reply1 := "" if err := rater.Call("ApierV1.SetActions", attrs1, &reply1); err != nil { @@ -974,7 +974,7 @@ func TestApierGetActions(t *testing.T) { return } expectActs := []*utils.TPAction{ - &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: 75.0, ExpiryTime: engine.UNLIMITED, Weight: 20.0}} + &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75.0", ExpiryTime: engine.UNLIMITED, Weight: 20.0}} var reply []*utils.TPAction if err := rater.Call("ApierV1.GetActions", "ACTS_1", &reply); err != nil { diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index d2470db4f..ab71455f3 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -238,44 +238,44 @@ func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, at.ActivationDate = actTime } if attr.BalanceId != nil { - at.BalanceId = *attr.BalanceId + at.Balance.ID = attr.BalanceId } if attr.BalanceType != nil { - at.BalanceType = *attr.BalanceType + at.Balance.Type = attr.BalanceType } if attr.BalanceDirections != nil { - at.BalanceDirections = utils.NewStringMap(*attr.BalanceDirections...) + at.Balance.Directions = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDirections...)) } if attr.BalanceDestinationIds != nil { - at.BalanceDestinationIds = utils.NewStringMap(*attr.BalanceDestinationIds...) + at.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...)) } if attr.BalanceWeight != nil { - at.BalanceWeight = *attr.BalanceWeight + at.Balance.Weight = attr.BalanceWeight } if attr.BalanceExpirationDate != nil { balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate) if err != nil { return 0, err } - at.BalanceExpirationDate = balanceExpTime + at.Balance.ExpirationDate = &balanceExpTime } if attr.BalanceTimingTags != nil { - at.BalanceTimingTags = utils.NewStringMap(*attr.BalanceTimingTags...) + at.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...)) } if attr.BalanceRatingSubject != nil { - at.BalanceRatingSubject = *attr.BalanceRatingSubject + at.Balance.RatingSubject = attr.BalanceRatingSubject } if attr.BalanceCategories != nil { - at.BalanceCategories = utils.NewStringMap(*attr.BalanceCategories...) + at.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...)) } if attr.BalanceSharedGroups != nil { - at.BalanceSharedGroups = utils.NewStringMap(*attr.BalanceSharedGroups...) + at.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...)) } if attr.BalanceBlocker != nil { - at.BalanceBlocker = *attr.BalanceBlocker + at.Balance.Blocker = attr.BalanceBlocker } if attr.BalanceDisabled != nil { - at.BalanceDisabled = *attr.BalanceDisabled + at.Balance.Disabled = attr.BalanceDisabled } if attr.MinQueuedItems != nil { at.MinQueuedItems = *attr.MinQueuedItems diff --git a/engine/account.go b/engine/account.go index b1cbb463b..4888b536d 100644 --- a/engine/account.go +++ b/engine/account.go @@ -723,6 +723,9 @@ func (acc *Account) InitCounters() { } } } + if len(acc.UnitCounters) == 0 { + acc.UnitCounters = nil // leave it nil if empty + } } func (acc *Account) CleanExpiredStuff() { diff --git a/engine/action.go b/engine/action.go index 7dde51af3..edd7cfda6 100644 --- a/engine/action.go +++ b/engine/action.go @@ -57,7 +57,7 @@ const ( DENY_NEGATIVE = "*deny_negative" RESET_ACCOUNT = "*reset_account" REMOVE_ACCOUNT = "*remove_account" - SET_BALANCE = "*set_balance" // not ready for production until switching to pointers + SET_BALANCE = "*set_balance" REMOVE_BALANCE = "*remove_balance" TOPUP_RESET = "*topup_reset" TOPUP = "*topup" diff --git a/general_tests/acntacts_test.go b/general_tests/acntacts_test.go index 524e0fb1e..50ec258cf 100644 --- a/general_tests/acntacts_test.go +++ b/general_tests/acntacts_test.go @@ -86,7 +86,7 @@ func TestAcntActsDisableAcnt(t *testing.T) { if acnt, err := acntDbAcntActs.GetAccount(acnt1Tag); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectAcnt, acnt) { - t.Errorf("Expecting: %+v, received: %+v", expectAcnt, acnt) + t.Errorf("Expecting: %+v, received: %+v", utils.ToJSON(expectAcnt), utils.ToJSON(acnt)) } } From 9131e4965bd2ba9eb14b490156ac0e5ba47003f8 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 17 Feb 2016 14:19:38 +0200 Subject: [PATCH 083/199] added last day of month function --- utils/coreutils.go | 15 +++++++++++++++ utils/utils_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/utils/coreutils.go b/utils/coreutils.go index 4f857b07d..b89804af0 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -518,3 +518,18 @@ func CastIfToString(iface interface{}) (strVal string, casts bool) { } return strVal, casts } + +func GetEndOfMonth(ref time.Time) time.Time { + if ref.IsZero() { + return time.Now() + } + year, month, _ := ref.Date() + if month == time.December { + year++ + month = time.January + } else { + month++ + } + eom := time.Date(year, month, 1, 0, 0, 0, 0, ref.Location()) + return eom.Add(-time.Second) +} diff --git a/utils/utils_test.go b/utils/utils_test.go index 6c66c8a0d..adca2dfad 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -600,3 +600,31 @@ func TestCastIfToString(t *testing.T) { t.Errorf("Received: %+v", sOut) } } + +func TestEndOfMonth(t *testing.T) { + eom := GetEndOfMonth(time.Date(2016, time.February, 5, 10, 1, 2, 3, time.UTC)) + expected := time.Date(2016, time.February, 29, 23, 59, 59, 0, time.UTC) + if !eom.Equal(expected) { + t.Errorf("Expected %v was %v", expected, eom) + } + eom = GetEndOfMonth(time.Date(2015, time.February, 5, 10, 1, 2, 3, time.UTC)) + expected = time.Date(2015, time.February, 28, 23, 59, 59, 0, time.UTC) + if !eom.Equal(expected) { + t.Errorf("Expected %v was %v", expected, eom) + } + eom = GetEndOfMonth(time.Date(2016, time.January, 31, 10, 1, 2, 3, time.UTC)) + expected = time.Date(2016, time.January, 31, 23, 59, 59, 0, time.UTC) + if !eom.Equal(expected) { + t.Errorf("Expected %v was %v", expected, eom) + } + eom = GetEndOfMonth(time.Date(2016, time.December, 31, 10, 1, 2, 3, time.UTC)) + expected = time.Date(2016, time.December, 31, 23, 59, 59, 0, time.UTC) + if !eom.Equal(expected) { + t.Errorf("Expected %v was %v", expected, eom) + } + eom = GetEndOfMonth(time.Date(2016, time.July, 31, 23, 59, 59, 0, time.UTC)) + expected = time.Date(2016, time.July, 31, 23, 59, 59, 0, time.UTC) + if !eom.Equal(expected) { + t.Errorf("Expected %v was %v", expected, eom) + } +} From f278c646dac1074ab672da3d0baaf4101dda15ad Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 17 Feb 2016 14:21:19 +0200 Subject: [PATCH 084/199] added *end_month to ParseDate function --- utils/coreutils.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/coreutils.go b/utils/coreutils.go index b89804af0..3719f776f 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -193,6 +193,8 @@ func ParseDate(date string) (expDate time.Time, err error) { expDate = time.Now().AddDate(0, 1, 0) // add one month case date == "*yearly": expDate = time.Now().AddDate(1, 0, 0) // add one year + case date == "*end_month": + expDate = GetEndOfMonth(time.Now()) case strings.HasSuffix(date, "Z"): expDate, err = time.Parse(time.RFC3339, date) default: From 55b7e226428b9965e41fc5e5de6e4001fdc2b5a3 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 17 Feb 2016 17:16:07 +0200 Subject: [PATCH 085/199] renmaed *end_month to *month_end --- utils/coreutils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/coreutils.go b/utils/coreutils.go index 3719f776f..4268700cc 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -193,7 +193,7 @@ func ParseDate(date string) (expDate time.Time, err error) { expDate = time.Now().AddDate(0, 1, 0) // add one month case date == "*yearly": expDate = time.Now().AddDate(1, 0, 0) // add one year - case date == "*end_month": + case date == "*month_end": expDate = GetEndOfMonth(time.Now()) case strings.HasSuffix(date, "Z"): expDate, err = time.Parse(time.RFC3339, date) From 291d6f7421996f630bd8bf67a1cd86a116084a92 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Feb 2016 18:33:38 +0200 Subject: [PATCH 086/199] updated migrator --- cmd/cgr-loader/migrator_rc8.go | 255 +++++++++++++++++++++++---------- 1 file changed, 180 insertions(+), 75 deletions(-) diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index 25aebd1e0..fbc660684 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -227,51 +227,107 @@ func (mig MigratorRC8) migrateAccounts() error { // unit counters for _, oldUc := range oldAcc.UnitCounters { newUc := &engine.UnitCounter{ - BalanceType: oldUc.BalanceType, - Balances: make(engine.BalanceChain, len(oldUc.Balances)), + Counters: make(engine.CounterFilters, len(oldUc.Balances)), } for index, oldUcBal := range oldUc.Balances { - newUc.Balances[index] = &engine.Balance{ - Uuid: oldUcBal.Uuid, - Id: oldUcBal.Id, - Value: oldUcBal.Value, - Directions: utils.ParseStringMap(oldUc.Direction), - ExpirationDate: oldUcBal.ExpirationDate, - Weight: oldUcBal.Weight, - DestinationIds: utils.ParseStringMap(oldUcBal.DestinationIds), - RatingSubject: oldUcBal.RatingSubject, - Categories: utils.ParseStringMap(oldUcBal.Category), - SharedGroups: utils.ParseStringMap(oldUcBal.SharedGroup), - Timings: oldUcBal.Timings, - TimingIDs: utils.ParseStringMap(oldUcBal.TimingIDs), - Disabled: oldUcBal.Disabled, + bf := &engine.BalanceFilter{} + if oldUcBal.Uuid != "" { + bf.Uuid = utils.StringPointer(oldUcBal.Uuid) } + if oldUcBal.Id != "" { + bf.ID = utils.StringPointer(oldUcBal.Id) + } + if oldUc.BalanceType != "" { + bf.Type = utils.StringPointer(oldUc.BalanceType) + } + // the value was used for counter value + /*if oldUcBal.Value != 0 { + bf.Value = utils.Float64Pointer(oldUcBal.Value) + }*/ + if oldUc.Direction != "" { + bf.Directions = utils.StringMapPointer(utils.ParseStringMap(oldUc.Direction)) + } + if !oldUcBal.ExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldUcBal.ExpirationDate) + } + if oldUcBal.Weight != 0 { + bf.Weight = utils.Float64Pointer(oldUcBal.Weight) + } + if oldUcBal.DestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.DestinationIds)) + } + if oldUcBal.RatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldUcBal.RatingSubject) + } + if oldUcBal.Category != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.Category)) + } + if oldUcBal.SharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.SharedGroup)) + } + if oldUcBal.TimingIDs != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.TimingIDs)) + } + if oldUcBal.Disabled != false { + bf.Disabled = utils.BoolPointer(oldUcBal.Disabled) + } + bf.Timings = oldUcBal.Timings + cf := &engine.CounterFilter{ + Value: oldUcBal.Value, + Filter: bf, + } + newUc.Counters[index] = cf } } // action triggers for index, oldAtr := range oldAcc.ActionTriggers { - newAcc.ActionTriggers[index] = &engine.ActionTrigger{ - UniqueID: oldAtr.Id, - ThresholdType: oldAtr.ThresholdType, - ThresholdValue: oldAtr.ThresholdValue, - Recurrent: oldAtr.Recurrent, - MinSleep: oldAtr.MinSleep, - BalanceId: oldAtr.BalanceId, - BalanceType: oldAtr.BalanceType, - BalanceDirections: utils.ParseStringMap(oldAtr.BalanceDirection), - BalanceDestinationIds: utils.ParseStringMap(oldAtr.BalanceDestinationIds), - BalanceWeight: oldAtr.BalanceWeight, - BalanceExpirationDate: oldAtr.BalanceExpirationDate, - BalanceTimingTags: utils.ParseStringMap(oldAtr.BalanceTimingTags), - BalanceRatingSubject: oldAtr.BalanceRatingSubject, - BalanceCategories: utils.ParseStringMap(oldAtr.BalanceCategory), - BalanceSharedGroups: utils.ParseStringMap(oldAtr.BalanceSharedGroup), - BalanceDisabled: oldAtr.BalanceDisabled, - Weight: oldAtr.Weight, - ActionsId: oldAtr.ActionsId, - MinQueuedItems: oldAtr.MinQueuedItems, - Executed: oldAtr.Executed, + at := &engine.ActionTrigger{ + UniqueID: oldAtr.Id, + ThresholdType: oldAtr.ThresholdType, + ThresholdValue: oldAtr.ThresholdValue, + Recurrent: oldAtr.Recurrent, + MinSleep: oldAtr.MinSleep, + Weight: oldAtr.Weight, + ActionsId: oldAtr.ActionsId, + MinQueuedItems: oldAtr.MinQueuedItems, + Executed: oldAtr.Executed, } + bf := &engine.BalanceFilter{} + if oldAtr.BalanceId != "" { + bf.ID = utils.StringPointer(oldAtr.BalanceId) + } + if oldAtr.BalanceType != "" { + bf.Type = utils.StringPointer(oldAtr.BalanceType) + } + if oldAtr.BalanceRatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldAtr.BalanceRatingSubject) + } + if oldAtr.BalanceDirection != "" { + bf.Directions = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDirection)) + } + if oldAtr.BalanceDestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDestinationIds)) + } + if oldAtr.BalanceTimingTags != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceTimingTags)) + } + if oldAtr.BalanceCategory != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceCategory)) + } + if oldAtr.BalanceSharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceSharedGroup)) + } + if oldAtr.BalanceWeight != 0 { + bf.Weight = utils.Float64Pointer(oldAtr.BalanceWeight) + } + if oldAtr.BalanceDisabled != false { + bf.Disabled = utils.BoolPointer(oldAtr.BalanceDisabled) + } + if !oldAtr.BalanceExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldAtr.BalanceExpirationDate) + } + at.Balance = bf + newAcc.ActionTriggers[index] = at if newAcc.ActionTriggers[index].ThresholdType == "*min_counter" || newAcc.ActionTriggers[index].ThresholdType == "*max_counter" { newAcc.ActionTriggers[index].ThresholdType = strings.Replace(newAcc.ActionTriggers[index].ThresholdType, "_", "_event_", 1) @@ -322,28 +378,53 @@ func (mig MigratorRC8) migrateActionTriggers() error { } newAtrs := make(engine.ActionTriggers, len(oldAtrs)) for index, oldAtr := range oldAtrs { - newAtrs[index] = &engine.ActionTrigger{ - UniqueID: oldAtr.Id, - ThresholdType: oldAtr.ThresholdType, - ThresholdValue: oldAtr.ThresholdValue, - Recurrent: oldAtr.Recurrent, - MinSleep: oldAtr.MinSleep, - BalanceId: oldAtr.BalanceId, - BalanceType: oldAtr.BalanceType, - BalanceDirections: utils.ParseStringMap(oldAtr.BalanceDirection), - BalanceDestinationIds: utils.ParseStringMap(oldAtr.BalanceDestinationIds), - BalanceWeight: oldAtr.BalanceWeight, - BalanceExpirationDate: oldAtr.BalanceExpirationDate, - BalanceTimingTags: utils.ParseStringMap(oldAtr.BalanceTimingTags), - BalanceRatingSubject: oldAtr.BalanceRatingSubject, - BalanceCategories: utils.ParseStringMap(oldAtr.BalanceCategory), - BalanceSharedGroups: utils.ParseStringMap(oldAtr.BalanceSharedGroup), - BalanceDisabled: oldAtr.BalanceDisabled, - Weight: oldAtr.Weight, - ActionsId: oldAtr.ActionsId, - MinQueuedItems: oldAtr.MinQueuedItems, - Executed: oldAtr.Executed, + at := &engine.ActionTrigger{ + UniqueID: oldAtr.Id, + ThresholdType: oldAtr.ThresholdType, + ThresholdValue: oldAtr.ThresholdValue, + Recurrent: oldAtr.Recurrent, + MinSleep: oldAtr.MinSleep, + Weight: oldAtr.Weight, + ActionsId: oldAtr.ActionsId, + MinQueuedItems: oldAtr.MinQueuedItems, + Executed: oldAtr.Executed, } + bf := &engine.BalanceFilter{} + if oldAtr.BalanceId != "" { + bf.ID = utils.StringPointer(oldAtr.BalanceId) + } + if oldAtr.BalanceType != "" { + bf.Type = utils.StringPointer(oldAtr.BalanceType) + } + if oldAtr.BalanceRatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldAtr.BalanceRatingSubject) + } + if oldAtr.BalanceDirection != "" { + bf.Directions = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDirection)) + } + if oldAtr.BalanceDestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDestinationIds)) + } + if oldAtr.BalanceTimingTags != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceTimingTags)) + } + if oldAtr.BalanceCategory != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceCategory)) + } + if oldAtr.BalanceSharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceSharedGroup)) + } + if oldAtr.BalanceWeight != 0 { + bf.Weight = utils.Float64Pointer(oldAtr.BalanceWeight) + } + if oldAtr.BalanceDisabled != false { + bf.Disabled = utils.BoolPointer(oldAtr.BalanceDisabled) + } + if !oldAtr.BalanceExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldAtr.BalanceExpirationDate) + } + at.Balance = bf + newAtrs[index] = at if newAtrs[index].ThresholdType == "*min_counter" || newAtrs[index].ThresholdType == "*max_counter" { newAtrs[index].ThresholdType = strings.Replace(newAtrs[index].ThresholdType, "_", "_event_", 1) @@ -381,29 +462,53 @@ func (mig MigratorRC8) migrateActions() error { } newAcs := make(engine.Actions, len(oldAcs)) for index, oldAc := range oldAcs { - newAcs[index] = &engine.Action{ + a := &engine.Action{ Id: oldAc.Id, ActionType: oldAc.ActionType, - BalanceType: oldAc.BalanceType, ExtraParameters: oldAc.ExtraParameters, ExpirationString: oldAc.ExpirationString, Weight: oldAc.Weight, - Balance: &engine.Balance{ - Uuid: oldAc.Balance.Uuid, - Id: oldAc.Balance.Id, - Value: oldAc.Balance.Value, - Directions: utils.ParseStringMap(oldAc.Direction), - ExpirationDate: oldAc.Balance.ExpirationDate, - Weight: oldAc.Balance.Weight, - DestinationIds: utils.ParseStringMap(oldAc.Balance.DestinationIds), - RatingSubject: oldAc.Balance.RatingSubject, - Categories: utils.ParseStringMap(oldAc.Balance.Category), - SharedGroups: utils.ParseStringMap(oldAc.Balance.SharedGroup), - Timings: oldAc.Balance.Timings, - TimingIDs: utils.ParseStringMap(oldAc.Balance.TimingIDs), - Disabled: oldAc.Balance.Disabled, - }, + Balance: &engine.BalanceFilter{}, } + bf := a.Balance + if oldAc.Balance.Uuid != "" { + bf.Uuid = utils.StringPointer(oldAc.Balance.Uuid) + } + if oldAc.Balance.Id != "" { + bf.ID = utils.StringPointer(oldAc.Balance.Id) + } + if oldAc.BalanceType != "" { + bf.Type = utils.StringPointer(oldAc.BalanceType) + } + if oldAc.Balance.Value != 0 { + bf.Value = utils.Float64Pointer(oldAc.Balance.Value) + } + if oldAc.Balance.RatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldAc.Balance.RatingSubject) + } + if oldAc.Balance.DestinationIds != "" { + bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.DestinationIds)) + } + if oldAc.Balance.TimingIDs != "" { + bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.TimingIDs)) + } + if oldAc.Balance.Category != "" { + bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.Category)) + } + if oldAc.Balance.SharedGroup != "" { + bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.SharedGroup)) + } + if oldAc.Balance.Weight != 0 { + bf.Weight = utils.Float64Pointer(oldAc.Balance.Weight) + } + if oldAc.Balance.Disabled != false { + bf.Disabled = utils.BoolPointer(oldAc.Balance.Disabled) + } + if !oldAc.Balance.ExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldAc.Balance.ExpirationDate) + } + bf.Timings = oldAc.Balance.Timings + newAcs[index] = a } newAcsMap[key] = newAcs } From 54809cbd9f03d73c8dfd92ce3fc4eddf6f0b11a1 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Feb 2016 18:44:39 +0200 Subject: [PATCH 087/199] preserve counters --- cmd/cgr-loader/migrator_rc8.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index fbc660684..1c9386a53 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -278,6 +278,7 @@ func (mig MigratorRC8) migrateAccounts() error { } newUc.Counters[index] = cf } + newAcc.UnitCounters[oldUc.BalanceType] = append(newAcc.UnitCounters[oldUc.BalanceType], newUc) } // action triggers for index, oldAtr := range oldAcc.ActionTriggers { From 9f614f42a8d9180466339245bc63a9917c90fd93 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Feb 2016 19:25:26 +0200 Subject: [PATCH 088/199] sql data types fixes --- apier/v1/apier.go | 20 +++++++++++-------- .../mysql/create_tariffplan_tables.sql | 14 ++++++------- .../postgres/create_tariffplan_tables.sql | 16 +++++++-------- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 2497ac17a..9afe5cf63 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -503,17 +503,21 @@ func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) error storeActions := make(engine.Actions, len(attrs.Actions)) for idx, apiAct := range attrs.Actions { var units *float64 - if x, err := strconv.ParseFloat(apiAct.Units, 64); err == nil { - units = &x - } else { - return err + if apiAct.Units != "" { + if x, err := strconv.ParseFloat(apiAct.Units, 64); err == nil { + units = &x + } else { + return err + } } var weight *float64 - if x, err := strconv.ParseFloat(apiAct.BalanceWeight, 64); err == nil { - weight = &x - } else { - return err + if apiAct.BalanceWeight != "" { + if x, err := strconv.ParseFloat(apiAct.BalanceWeight, 64); err == nil { + weight = &x + } else { + return err + } } a := &engine.Action{ diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 0a640135a..b6240cdc5 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -152,16 +152,16 @@ CREATE TABLE `tp_actions` ( `balance_tag` varchar(64) NOT NULL, `balance_type` varchar(24) NOT NULL, `directions` varchar(8) NOT NULL, - `units` DECIMAL(20,4) NOT NULL, + `units` varchar(24) NOT NULL, `expiry_time` varchar(24) NOT NULL, `timing_tags` varchar(128) NOT NULL, `destination_tags` varchar(64) NOT NULL, `rating_subject` varchar(64) NOT NULL, `categories` varchar(32) NOT NULL, `shared_groups` varchar(64) NOT NULL, - `balance_weight` DECIMAL(8,2) NOT NULL, - `balance_blocker` BOOLEAN NOT NULL, - `balance_disabled` BOOLEAN NOT NULL, + `balance_weight` varchar(10) NOT NULL, + `balance_blocker` varchar(5) NOT NULL, + `balance_disabled` varchar(24) NOT NULL, `extra_parameters` varchar(256) NOT NULL, `filter` varchar(256) NOT NULL, `weight` DECIMAL(8,2) NOT NULL, @@ -214,9 +214,9 @@ CREATE TABLE `tp_action_triggers` ( `balance_shared_groups` varchar(64) NOT NULL, `balance_expiry_time` varchar(24) NOT NULL, `balance_timing_tags` varchar(128) NOT NULL, - `balance_weight` DECIMAL(8,2) NOT NULL, - `balance_blocker` BOOL NOT NULL, - `balance_disabled` BOOL NOT NULL, + `balance_weight` varchar(10) NOT NULL, + `balance_blocker` varchar(5) NOT NULL, + `balance_disabled` varchar(5) NOT NULL, `min_queued_items` int(11) NOT NULL, `actions_tag` varchar(64) NOT NULL, `weight` DECIMAL(8,2) NOT NULL, diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index e4d3aaee8..b5d8193c8 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -109,7 +109,7 @@ CREATE TABLE tp_rating_profiles ( activation_time VARCHAR(24) NOT NULL, rating_plan_tag VARCHAR(64) NOT NULL, fallback_subjects VARCHAR(64), - cdr_stat_queue_ids varchar(64), + cdr_stat_queue_ids VARCHAR(64), created_at TIMESTAMP, UNIQUE (tpid, loadid, tenant, category, direction, subject, activation_time) ); @@ -147,16 +147,16 @@ CREATE TABLE tp_actions ( balance_tag VARCHAR(64) NOT NULL, balance_type VARCHAR(24) NOT NULL, directions VARCHAR(8) NOT NULL, - units NUMERIC(20,4) NOT NULL, + units VARCHAR(10) NOT NULL, expiry_time VARCHAR(24) NOT NULL, timing_tags VARCHAR(128) NOT NULL, destination_tags VARCHAR(64) NOT NULL, rating_subject VARCHAR(64) NOT NULL, categories VARCHAR(32) NOT NULL, shared_groups VARCHAR(64) NOT NULL, - balance_weight NUMERIC(8,2) NOT NULL, - balance_blocker BOOLEAN NOT NULL, - balance_disabled BOOLEAN NOT NULL, + balance_weight VARCHAR(10) NOT NULL, + balance_blocker VARCHAR(5) NOT NULL, + balance_disabled VARCHAR(5) NOT NULL, extra_parameters VARCHAR(256) NOT NULL, filter VARCHAR(256) NOT NULL, weight NUMERIC(8,2) NOT NULL, @@ -209,9 +209,9 @@ CREATE TABLE tp_action_triggers ( balance_shared_groups VARCHAR(64) NOT NULL, balance_expiry_time VARCHAR(24) NOT NULL, balance_timing_tags VARCHAR(128) NOT NULL, - balance_weight NUMERIC(8,2) NOT NULL, - balance_blocker BOOL NOT NULL, - balance_disabled BOOL NOT NULL, + balance_weight VARCHAR(10) NOT NULL, + balance_blocker VARCHAR(5) NOT NULL, + balance_disabled VARCHAR(5) NOT NULL, min_queued_items INTEGER NOT NULL, actions_tag VARCHAR(64) NOT NULL, weight NUMERIC(8,2) NOT NULL, From 4800884a534f8c91167049a4ef6df8cb598e5727 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Feb 2016 19:34:35 +0200 Subject: [PATCH 089/199] small fixes --- apier/v1/apier_local_test.go | 2 +- engine/actions_local_test.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index dd7a445d9..04d847cb9 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -980,7 +980,7 @@ func TestApierGetActions(t *testing.T) { if err := rater.Call("ApierV1.GetActions", "ACTS_1", &reply); err != nil { t.Error("Got error on ApierV1.GetActions: ", err.Error()) } else if !reflect.DeepEqual(expectActs, reply) { - t.Errorf("Expected: %v, received: %v", expectActs, reply) + t.Errorf("Expected: %v, received: %v", utils.ToJSON(expectActs), utils.ToJSON(reply)) } } diff --git a/engine/actions_local_test.go b/engine/actions_local_test.go index bdc3b8b79..b4aa17869 100644 --- a/engine/actions_local_test.go +++ b/engine/actions_local_test.go @@ -95,7 +95,7 @@ func TestActionsLocalSetCdrlogDebit(t *testing.T) { t.Errorf("Calling ApierV1.SetAccount received: %s", reply) } attrsAA := &utils.AttrSetActions{ActionsId: "ACTS_1", Actions: []*utils.TPAction{ - &utils.TPAction{Identifier: DEBIT, BalanceType: utils.MONETARY, Units: "5.0", ExpiryTime: UNLIMITED, Weight: 20.0}, + &utils.TPAction{Identifier: DEBIT, BalanceType: utils.MONETARY, Units: "5", ExpiryTime: UNLIMITED, Weight: 20.0}, &utils.TPAction{Identifier: CDRLOG}, }} if err := actsLclRpc.Call("ApierV1.SetActions", attrsAA, &reply); err != nil && err.Error() != utils.ErrExists.Error() { @@ -140,7 +140,7 @@ func TestActionsLocalSetCdrlogTopup(t *testing.T) { t.Errorf("Calling ApierV1.SetAccount received: %s", reply) } attrsAA := &utils.AttrSetActions{ActionsId: "ACTS_2", Actions: []*utils.TPAction{ - &utils.TPAction{Identifier: TOPUP, BalanceType: utils.MONETARY, Units: "5.0", ExpiryTime: UNLIMITED, Weight: 20.0}, + &utils.TPAction{Identifier: TOPUP, BalanceType: utils.MONETARY, Units: "5", ExpiryTime: UNLIMITED, Weight: 20.0}, &utils.TPAction{Identifier: CDRLOG}, }} if err := actsLclRpc.Call("ApierV1.SetActions", attrsAA, &reply); err != nil && err.Error() != utils.ErrExists.Error() { From f63ff96d165fd5c5400b927f2f48cda28468221f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 18 Feb 2016 21:11:43 +0200 Subject: [PATCH 090/199] test fixes --- apier/v1/apier_local_test.go | 4 ++-- engine/actions_local_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 04d847cb9..c9f58cf05 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -955,7 +955,7 @@ func TestApierSetActions(t *testing.T) { if !*testLocal { return } - act1 := &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75.0", ExpiryTime: engine.UNLIMITED, Weight: 20.0} + act1 := &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75", ExpiryTime: engine.UNLIMITED, Weight: 20.0} attrs1 := &utils.AttrSetActions{ActionsId: "ACTS_1", Actions: []*utils.TPAction{act1}} reply1 := "" if err := rater.Call("ApierV1.SetActions", attrs1, &reply1); err != nil { @@ -974,7 +974,7 @@ func TestApierGetActions(t *testing.T) { return } expectActs := []*utils.TPAction{ - &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75.0", ExpiryTime: engine.UNLIMITED, Weight: 20.0}} + &utils.TPAction{Identifier: engine.TOPUP_RESET, BalanceType: utils.MONETARY, Directions: utils.OUT, Units: "75", BalanceWeight: "0", BalanceBlocker: "false", BalanceDisabled: "false", ExpiryTime: engine.UNLIMITED, Weight: 20.0}} var reply []*utils.TPAction if err := rater.Call("ApierV1.GetActions", "ACTS_1", &reply); err != nil { diff --git a/engine/actions_local_test.go b/engine/actions_local_test.go index b4aa17869..b074fc630 100644 --- a/engine/actions_local_test.go +++ b/engine/actions_local_test.go @@ -168,7 +168,7 @@ func TestActionsLocalSetCdrlogTopup(t *testing.T) { rcvedCdrs[0].Subject != "dan2905" || rcvedCdrs[0].Usage != "1" || rcvedCdrs[0].RunID != TOPUP || - strconv.FormatFloat(rcvedCdrs[0].Cost, 'f', -1, 64) != attrsAA.Actions[0].Units { + strconv.FormatFloat(-rcvedCdrs[0].Cost, 'f', -1, 64) != attrsAA.Actions[0].Units { t.Errorf("Received: %+v", rcvedCdrs[0]) } } From 2093ae5c6dbce9bb47af9a78a7ff6fcef75ce215 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 19 Feb 2016 13:43:15 +0200 Subject: [PATCH 091/199] merge error fixed --- engine/account.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/account.go b/engine/account.go index f8dfc1f3a..5043ba404 100644 --- a/engine/account.go +++ b/engine/account.go @@ -701,7 +701,7 @@ func (acc *Account) InitCounters() { acc.UnitCounters[at.Balance.GetType()] = append(acc.UnitCounters[at.Balance.GetType()], uc) } - c := &CounterFilter{Filter: at.Balance} + c := &CounterFilter{Filter: at.Balance.Clone()} if (c.Filter.ID == nil || *c.Filter.ID == "") && at.UniqueID != "" { c.Filter.ID = utils.StringPointer(at.UniqueID) } From 1954f98913fa4662ca278136dbaea3a73085014c Mon Sep 17 00:00:00 2001 From: Tom Braarup Cuykens Date: Fri, 19 Feb 2016 13:43:37 +0100 Subject: [PATCH 092/199] OpenSIPS repo update OpenSIPS changed its repo, see http://apt.opensips.org/packages.php --- docs/tut_opensips_installs.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tut_opensips_installs.rst b/docs/tut_opensips_installs.rst index 3b83a9aba..2c68ad126 100644 --- a/docs/tut_opensips_installs.rst +++ b/docs/tut_opensips_installs.rst @@ -9,11 +9,11 @@ OpenSIPS_ We got OpenSIPS_ installed via following commands: :: - wget -O - http://apt.opensips.org/key.asc | apt-key add - - echo "deb http://apt.opensips.org/debian/stable-2.1/jessie opensips-2.1-jessie main" > /etc/apt/sources.list.d/opensips.list + apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 5F2FBB7C + echo "deb http://apt.opensips.org jessie 2.1-releases" >>/etc/apt/sources.list apt-get update apt-get install opensips opensips-json-module opensips-restclient-module Once installed we proceed with loading the configuration out of specific tutorial cases bellow. -.. _OpenSIPS: http://www.opensips.org/ \ No newline at end of file +.. _OpenSIPS: http://www.opensips.org/ From d6238be9d5a7b8ad15bc7bb0072d0c7a94fc455c Mon Sep 17 00:00:00 2001 From: Tom Braarup Cuykens Date: Fri, 19 Feb 2016 13:46:15 +0100 Subject: [PATCH 093/199] Update CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 488bba6f3..b2a3821bc 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -41,6 +41,7 @@ information, please see the [`CONTRIBUTING.md`](CONTRIBUTING.md) file. | @rinor | Rinor Hoxha | | @bhepp | Brice Heppner | | @noahmehl | Noah Mehl | +| @elfranne | Tom Braarup Cuykens | From 019060d2408bdd885506bd8004bc155e97094635 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 19 Feb 2016 13:46:49 +0200 Subject: [PATCH 094/199] updated dependencies --- glide.lock | 43 ++++++------------------------------------- 1 file changed, 6 insertions(+), 37 deletions(-) diff --git a/glide.lock b/glide.lock index 84c1f8c76..e35ffe2f0 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 855bc23b0e58452edf1d31f228430476a2602b79d225397d33b805b252cebb83 -updated: 2016-02-05T16:54:55.433704333+02:00 +hash: fde526f5fcd62d6b306dd6c4e30c6aa29ee3bfd625ffef3906c4d032eda7e9ec +updated: 2016-02-19T13:46:35.353761268+02:00 imports: - name: github.com/cenkalti/hub version: 57d753b5f4856e77b3cf8ecce78c97215a7d324d @@ -20,7 +20,7 @@ imports: - name: github.com/fiorix/go-diameter version: 30569fd7282321ac213d8db9f39eac9321ab6bd5 subpackages: - - /diam + - diam - diam/avp - diam/datatype - diam/dict @@ -45,48 +45,19 @@ imports: - name: github.com/mediocregopher/radix.v2 version: 91435107718b55ff544323a2b0f25fdd8475d283 subpackages: - - /pool - - redis - pool + - redis - name: github.com/peterh/liner version: 3f1c20449d1836aa4cbe38731b96f95cdf89634d - name: github.com/ugorji/go version: 646ae4a518c1c3be0739df898118d9bccf993858 subpackages: - - /codec -- name: golang.org/x/crypto - version: 552e9d568fde9701ea1944fb01c8aadaceaa7353 - subpackages: - - ssh/terminal + - codec - name: golang.org/x/net version: 961116aeebe66bfb58bb4d51818c70d256acbbb8 subpackages: - - /websocket + - websocket - context - - html/atom - - http2/hpack - - internal/iana - - ipv4 - - ipv6 - - webdav/internal/xml -- name: golang.org/x/text - version: cf4986612c83df6c55578ba198316d1684a9a287 - subpackages: - - encoding - - encoding/charmap - - encoding/htmlindex - - transform - - encoding/internal/identifier - - encoding/internal - - encoding/japanese - - encoding/korean - - encoding/simplifiedchinese - - encoding/traditionalchinese - - encoding/unicode - - language - - internal/utf8internal - - runes - - internal/tag - name: gopkg.in/fsnotify.v1 version: 508915b7500b6e42a87132e4afeb4729cebc7cbb - name: gopkg.in/mgo.v2 @@ -94,6 +65,4 @@ imports: subpackages: - bson - internal/scram -- name: gopkg.in/tomb.v2 - version: 14b3d72120e8d10ea6e6b7f87f7175734b1faab8 devImports: [] From 124268ce2b823328348eb3aebb0191657802ae96 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 19 Feb 2016 18:14:27 +0200 Subject: [PATCH 095/199] added AddQeue to cdrstats fixes #370 --- apier/v1/cdrstatsv1.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apier/v1/cdrstatsv1.go b/apier/v1/cdrstatsv1.go index 1677021b0..d9ec3a213 100644 --- a/apier/v1/cdrstatsv1.go +++ b/apier/v1/cdrstatsv1.go @@ -49,6 +49,10 @@ func (sts *CDRStatsV1) GetQueue(id string, sq *engine.StatsQueue) error { return sts.CdrStats.GetQueue(id, sq) } +func (sts *CDRStatsV1) AddQueue(cs *engine.CdrStats, reply *int) error { + return sts.CdrStats.AddQueue(cs, reply) +} + func (sts *CDRStatsV1) GetQueueTriggers(id string, ats *engine.ActionTriggers) error { return sts.CdrStats.GetQueueTriggers(id, ats) } From e7229c13083b2ac25facdcc50715809a6fad1116 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 19 Feb 2016 19:01:07 +0200 Subject: [PATCH 096/199] added RemoveQueue to CDRStatsV1, fixes #370 --- apier/v1/cdrstatsv1.go | 4 ++++ engine/stats.go | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/apier/v1/cdrstatsv1.go b/apier/v1/cdrstatsv1.go index d9ec3a213..6b6409be0 100644 --- a/apier/v1/cdrstatsv1.go +++ b/apier/v1/cdrstatsv1.go @@ -53,6 +53,10 @@ func (sts *CDRStatsV1) AddQueue(cs *engine.CdrStats, reply *int) error { return sts.CdrStats.AddQueue(cs, reply) } +func (sts *CDRStatsV1) RemoveQueue(qID string, reply *int) error { + return sts.CdrStats.RemoveQueue(qID, reply) +} + func (sts *CDRStatsV1) GetQueueTriggers(id string, ats *engine.ActionTriggers) error { return sts.CdrStats.GetQueueTriggers(id, ats) } diff --git a/engine/stats.go b/engine/stats.go index 4894943d7..d07f72e41 100644 --- a/engine/stats.go +++ b/engine/stats.go @@ -34,6 +34,7 @@ type StatsInterface interface { GetQueueTriggers(string, *ActionTriggers) error AppendCDR(*CDR, *int) error AddQueue(*CdrStats, *int) error + RemoveQueue(string, *int) error ReloadQueues([]string, *int) error ResetQueues([]string, *int) error Stop(int, *int) error @@ -162,6 +163,22 @@ func (s *Stats) AddQueue(cs *CdrStats, out *int) error { return nil } +func (s *Stats) RemoveQueue(qID string, out *int) error { + s.mux.Lock() + defer s.mux.Unlock() + if s.queues == nil { + s.queues = make(map[string]*StatsQueue) + } + if s.queueSavers == nil { + s.queueSavers = make(map[string]*queueSaver) + } + + delete(s.queues, qID) + delete(s.queueSavers, qID) + + return nil +} + func (s *Stats) ReloadQueues(ids []string, out *int) error { if len(ids) == 0 { if css, err := s.ratingDb.GetAllCdrStats(); err == nil { @@ -322,6 +339,10 @@ func (ps *ProxyStats) AddQueue(cs *CdrStats, out *int) error { return ps.Client.Call("Stats.AddQueue", cs, out) } +func (ps *ProxyStats) RemoveQueue(qID string, out *int) error { + return ps.Client.Call("Stats.RemoveQueue", qID, out) +} + func (ps *ProxyStats) ReloadQueues(ids []string, out *int) error { return ps.Client.Call("Stats.ReloadQueues", ids, out) } From e037d745e0cc917953182e7f6ef1e0e23854952e Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 19 Feb 2016 19:11:29 +0100 Subject: [PATCH 097/199] Diameter MetaValueExponent, separated simpa templates --- agents/libdmt.go | 21 +++++++++++++ agents/libdmt_test.go | 28 +++++++++++++++++ .../samples/dmtagent/diameter_processors.json | 20 ------------- data/conf/samples/dmtagent/simpa.json | 30 +++++++++++++++++++ utils/consts.go | 1 + 5 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 data/conf/samples/dmtagent/simpa.json diff --git a/agents/libdmt.go b/agents/libdmt.go index fe32fab92..d96ac580c 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -51,6 +51,7 @@ func init() { const ( META_CCR_USAGE = "*ccr_usage" META_CCA_USAGE = "*cca_usage" + META_VALUE_EXPONENT = "*value_exponent" DIAMETER_CCR = "DIAMETER_CCR" DiameterRatingFailed = 5031 ) @@ -215,6 +216,24 @@ func metaHandler(m *diam.Message, tag, arg string, dur time.Duration) (string, e return "", nil } +func metaValueExponent(m *diam.Message, argsTpl utils.RSRFields, roundingDecimals int) (string, error) { + valStr := composedFieldvalue(m, argsTpl, 0) + handlerArgs := strings.Split(valStr, utils.HandlerArgSep) + if len(handlerArgs) != 2 { + return "", errors.New("Unexpected number of arguments") + } + val, err := strconv.ParseFloat(handlerArgs[0], 64) + if err != nil { + return "", err + } + exp, err := strconv.ParseFloat(handlerArgs[1], 64) + if err != nil { + return "", err + } + res := val * math.Exp(exp) + return strconv.FormatFloat(utils.Round(res, roundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil +} + // splitIntoInterface is used to split a string into []interface{} instead of []string func splitIntoInterface(content, sep string) []interface{} { spltStr := strings.Split(content, sep) @@ -342,6 +361,8 @@ func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interfa case utils.META_HANDLER: if cfgFld.HandlerId == META_CCA_USAGE { // Exception, usage is passed in the dur variable by CCA outVal = strconv.FormatFloat(extraParam.(float64), 'f', -1, 64) + } else if cfgFld.HandlerId == META_VALUE_EXPONENT { + outVal, err = metaValueExponent(m, cfgFld.Value, 10) // FixMe: add here configured number of decimals } else { outVal, err = metaHandler(m, cfgFld.HandlerId, cfgFld.Layout, extraParam.(time.Duration)) if err != nil { diff --git a/agents/libdmt_test.go b/agents/libdmt_test.go index 73297edd0..e56252a35 100644 --- a/agents/libdmt_test.go +++ b/agents/libdmt_test.go @@ -86,6 +86,34 @@ func TestAvpValAsString(t *testing.T) { } } +func TestMetaValueExponent(t *testing.T) { + m := diam.NewRequest(diam.CreditControl, 4, nil) + m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002")) + m.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.CCMoney, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.UnitValue, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(10000)), + diam.NewAVP(avp.Exponent, avp.Mbit, 0, datatype.Integer32(-5)), + }, + }), + diam.NewAVP(avp.CurrencyCode, avp.Mbit, 0, datatype.Unsigned32(33)), + }, + }), + }, + }) + if val, err := metaValueExponent(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err != nil { + t.Error(err) + } else if val != "67.3794699909" { + t.Error("Received: ", val) + } + if _, err = metaValueExponent(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err == nil { + t.Error("Should have received error") // Insufficient number arguments + } +} + func TestFieldOutVal(t *testing.T) { m := diam.NewRequest(diam.CreditControl, 4, nil) m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002")) diff --git a/data/conf/samples/dmtagent/diameter_processors.json b/data/conf/samples/dmtagent/diameter_processors.json index 1484908c1..57a6d90f1 100644 --- a/data/conf/samples/dmtagent/diameter_processors.json +++ b/data/conf/samples/dmtagent/diameter_processors.json @@ -85,26 +85,6 @@ {"tag": "Usage", "field_id": "Usage", "type": "*composed", "value": "Requested-Service-Unit>CC-Time", "mandatory": true}, ], }, - { - "id": "simpa", // formal identifier of this processor - "dry_run": false, // do not send the events to SMG, just log them - "request_filter": "Service-Context-Id(^simpa)", // filter requests processed by this processor - "continue_on_success": false, // continue to the next template if executed - "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value - {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*generic", "mandatory": true}, - {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, - {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*prepaid", "mandatory": true}, - {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, - {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true}, - {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, - {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, - {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, - {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>SMS-Information>Recipient-Address>Address-Data", "mandatory": true}, - {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, - {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, - {"tag": "Usage", "field_id": "Usage", "type": "*composed", "value": "Requested-Service-Unit>CC-Time", "mandatory": true}, - ], - }, ], }, diff --git a/data/conf/samples/dmtagent/simpa.json b/data/conf/samples/dmtagent/simpa.json new file mode 100644 index 000000000..2af7482bd --- /dev/null +++ b/data/conf/samples/dmtagent/simpa.json @@ -0,0 +1,30 @@ + +{ + +"diameter_agent": { + "request_processors": [ + { + "id": "simpa_event", // formal identifier of this processor + "dry_run": false, // do not send the events to SMG, just log them + "request_filter": "Service-Context-Id(^simpa);CC-RequestType(4)", // filter requests processed by this processor + "continue_on_success": false, // continue to the next template if executed + "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*generic", "mandatory": true}, + {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, + {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*prepaid", "mandatory": true}, + {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, + {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true}, + {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, + {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, + {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, + {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>SMS-Information>Recipient-Address>Address-Data", "mandatory": true}, + {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*value_exponent", + "value": "Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", "mandatory": true}, + ], + }, + ], +}, + +} \ No newline at end of file diff --git a/utils/consts.go b/utils/consts.go index b8ede4078..aa1dc3db4 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -267,6 +267,7 @@ const ( MetaRaw = "*raw" CreatedAt = "CreatedAt" UpdatedAt = "UpdatedAt" + HandlerArgSep = "|" ) var ( From 610f3925fabfa31b3c3f14c7edcde14fb36743fb Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 19 Feb 2016 20:49:11 +0100 Subject: [PATCH 098/199] Improved ParseDurationWithSecs --- utils/coreutils.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/utils/coreutils.go b/utils/coreutils.go index 4268700cc..0f05b1044 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -236,12 +236,10 @@ func CopyHour(src, dest time.Time) time.Time { // Parses duration, considers s as time unit if not provided, seconds as float to specify subunits func ParseDurationWithSecs(durStr string) (time.Duration, error) { - if durSecs, err := strconv.ParseFloat(durStr, 64); err == nil { // Seconds format considered - durNanosecs := int(durSecs * NANO_MULTIPLIER) - return time.Duration(durNanosecs), nil - } else { - return time.ParseDuration(durStr) + if _, err := strconv.ParseFloat(durStr, 64); err == nil { // Seconds format considered + durStr += "s" } + return time.ParseDuration(durStr) } func AccountKey(tenant, account string) string { From 0e9a2e493a0bfcc09bcfad40c2d1d091c2368e6a Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 22 Feb 2016 13:14:21 +0200 Subject: [PATCH 099/199] don't parse action expiry time on csv load --- engine/tp_reader.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 469165aba..f117afd65 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -545,13 +545,7 @@ func (tpr *TpReader) LoadActions() (err error) { } acts[idx].Balance.Weight = utils.Float64Pointer(u) } - if tpact.ExpiryTime != "" && tpact.ExpiryTime != utils.ANY && tpact.ExpiryTime != utils.UNLIMITED { - u, err := utils.ParseTimeDetectLayout(tpact.ExpiryTime, tpr.timezone) - if err != nil { - return err - } - acts[idx].Balance.ExpirationDate = utils.TimePointer(u) - } + if tpact.RatingSubject != "" && tpact.RatingSubject != utils.ANY { acts[idx].Balance.RatingSubject = utils.StringPointer(tpact.RatingSubject) } From 019a3cbb57838ebcfc7aac26f90452d495e48afc Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 22 Feb 2016 17:34:23 +0200 Subject: [PATCH 100/199] fixed SetBalance api also removed balance enabled/disabled console command --- apier/v1/accounts.go | 2 +- console/balance_enabledisable.go | 65 -------------------------------- 2 files changed, 1 insertion(+), 66 deletions(-) delete mode 100644 console/balance_enabledisable.go diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index d6273b517..1e34a1aad 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -464,7 +464,7 @@ type AttrSetBalance struct { Disabled *bool } -func (self *ApierV1) SetBalance(aType string, attr *AttrSetBalance, reply *string) error { +func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } diff --git a/console/balance_enabledisable.go b/console/balance_enabledisable.go deleted file mode 100644 index 975d19ae7..000000000 --- a/console/balance_enabledisable.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2012-2015 ITsysCOM - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package console - -import ( - "github.com/cgrates/cgrates/apier/v1" - "github.com/cgrates/cgrates/utils" -) - -func init() { - c := &CmdEnableDisableBalance{ - name: "balance_enabledisable", - rpcMethod: "ApierV1.EnableDisableBalance", - } - commands[c.Name()] = c - c.CommandExecuter = &CommandExecuter{c} -} - -// Commander implementation -type CmdEnableDisableBalance struct { - name string - rpcMethod string - rpcParams *v1.AttrAddBalance - *CommandExecuter -} - -func (self *CmdEnableDisableBalance) Name() string { - return self.name -} - -func (self *CmdEnableDisableBalance) RpcMethod() string { - return self.rpcMethod -} - -func (self *CmdEnableDisableBalance) RpcParams(reset bool) interface{} { - if reset || self.rpcParams == nil { - self.rpcParams = &v1.AttrAddBalance{BalanceType: utils.MONETARY, Overwrite: false} - } - return self.rpcParams -} - -func (self *CmdEnableDisableBalance) PostprocessRpcParams() error { - return nil -} - -func (self *CmdEnableDisableBalance) RpcResult() interface{} { - var s string - return &s -} From 4c0556f6c7106402f762aaa7cc2e52b66a882bde Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 22 Feb 2016 16:58:14 +0100 Subject: [PATCH 101/199] Diameter - SIMPA Event handling, own simpa.json sample config, tutorial files modified to add rate for generic category --- agents/dmtagent_it_test.go | 4 ++-- agents/libdmt.go | 2 ++ data/conf/samples/dmtagent/simpa.json | 5 ++--- data/tariffplans/tutorial/DestinationRates.csv | 1 + data/tariffplans/tutorial/Rates.csv | 1 + data/tariffplans/tutorial/RatingPlans.csv | 1 + data/tariffplans/tutorial/RatingProfiles.csv | 1 + 7 files changed, 10 insertions(+), 5 deletions(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 0340be100..8ec97b1ba 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -604,8 +604,8 @@ func TestDmtAgentSendCCRSimpaEvent(t *testing.T) { t.Error(err) } else if len(avps) == 0 { t.Error("Result-Code") - } else if strResult := avpValAsString(avps[0]); strResult != "5030" { // Result-Code set in the template - t.Errorf("Expecting 5030, received: %s", strResult) + } else if strResult := avpValAsString(avps[0]); strResult != "2001" { // Result-Code set in the template + t.Errorf("Expecting 2001, received: %s", strResult) } } diff --git a/agents/libdmt.go b/agents/libdmt.go index d96ac580c..471c6a9c5 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -216,6 +216,8 @@ func metaHandler(m *diam.Message, tag, arg string, dur time.Duration) (string, e return "", nil } +// metaValueExponent will multiply the float value with the exponent provided. +// Expects 2 arguments in template separated by | func metaValueExponent(m *diam.Message, argsTpl utils.RSRFields, roundingDecimals int) (string, error) { valStr := composedFieldvalue(m, argsTpl, 0) handlerArgs := strings.Split(valStr, utils.HandlerArgSep) diff --git a/data/conf/samples/dmtagent/simpa.json b/data/conf/samples/dmtagent/simpa.json index 2af7482bd..6a1fb9503 100644 --- a/data/conf/samples/dmtagent/simpa.json +++ b/data/conf/samples/dmtagent/simpa.json @@ -6,7 +6,7 @@ { "id": "simpa_event", // formal identifier of this processor "dry_run": false, // do not send the events to SMG, just log them - "request_filter": "Service-Context-Id(^simpa);CC-RequestType(4)", // filter requests processed by this processor + "request_filter": "Service-Context-Id(^simpa);CC-Request-Type(4)", // filter requests processed by this processor "continue_on_success": false, // continue to the next template if executed "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*generic", "mandatory": true}, @@ -14,10 +14,9 @@ {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*prepaid", "mandatory": true}, {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^cgrates.org", "mandatory": true}, - {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, + {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^generic", "mandatory": true}, {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "field_filter":"Subscription-Id>Subscription-Id-Type(0)", "mandatory": true}, - {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>SMS-Information>Recipient-Address>Address-Data", "mandatory": true}, {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, {"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*value_exponent", diff --git a/data/tariffplans/tutorial/DestinationRates.csv b/data/tariffplans/tutorial/DestinationRates.csv index 2602159d0..ee3352fc3 100644 --- a/data/tariffplans/tutorial/DestinationRates.csv +++ b/data/tariffplans/tutorial/DestinationRates.csv @@ -8,3 +8,4 @@ DR_FS_10CNT,DST_FS,RT_10CNT,*up,4,0, DR_SPECIAL_1002,DST_1002,RT_1CNT,*up,4,0, DR_1007_MAXCOST_DISC,DST_1007,RT_1CNT_PER_SEC,*up,4,0.62,*disconnect DR_1007_MAXCOST_FREE,DST_1007,RT_1CNT_PER_SEC,*up,4,0.62,*free +DR_GENERIC,*any,RT_GENERIC_1,*up,4,0, diff --git a/data/tariffplans/tutorial/Rates.csv b/data/tariffplans/tutorial/Rates.csv index c333bff3e..f4ca8a14f 100644 --- a/data/tariffplans/tutorial/Rates.csv +++ b/data/tariffplans/tutorial/Rates.csv @@ -7,3 +7,4 @@ RT_40CNT,0.8,0.4,60s,30s,0s RT_40CNT,0,0.2,60s,10s,60s RT_1CNT,0,0.01,60s,60s,0s RT_1CNT_PER_SEC,0,0.01,1s,1s,0s +RT_GENERIC_1,0,1,1,1,0 diff --git a/data/tariffplans/tutorial/RatingPlans.csv b/data/tariffplans/tutorial/RatingPlans.csv index 80bb48ee7..b983bf4ba 100644 --- a/data/tariffplans/tutorial/RatingPlans.csv +++ b/data/tariffplans/tutorial/RatingPlans.csv @@ -18,3 +18,4 @@ RP_RETAIL2,DR_FS_10CNT,OFFPEAK_EVENING,10 RP_RETAIL2,DR_FS_10CNT,OFFPEAK_WEEKEND,10 RP_RETAIL2,DR_1007_MAXCOST_FREE,ALWAYS,10 RP_SPECIAL_1002,DR_SPECIAL_1002,ALWAYS,10 +RP_GENERIC,DR_GENERIC,*any,10 \ No newline at end of file diff --git a/data/tariffplans/tutorial/RatingProfiles.csv b/data/tariffplans/tutorial/RatingProfiles.csv index db4434165..0d1d56064 100644 --- a/data/tariffplans/tutorial/RatingProfiles.csv +++ b/data/tariffplans/tutorial/RatingProfiles.csv @@ -7,3 +7,4 @@ *out,cgrates.org,lcr_profile2,suppl1,2014-01-14T00:00:00Z,RP_RETAIL2,,STATS_SUPPL1 *out,cgrates.org,lcr_profile2,suppl2,2014-01-14T00:00:00Z,RP_RETAIL1,,STATS_SUPPL2 *out,cgrates.org,lcr_profile2,suppl3,2014-01-14T00:00:00Z,RP_SPECIAL_1002,, +*out,cgrates.org,generic,*any,2014-01-14T00:00:00Z,RP_GENERIC,, From 4fa752a859abd7f3eb9d41b90c26f952051c5385 Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 22 Feb 2016 17:58:22 +0100 Subject: [PATCH 102/199] Updating cache tests for tutorial files --- apier/v1/smgenericv1_it_test.go | 2 +- general_tests/tut_smgeneric_it_test.go | 2 +- general_tests/tutorial_local_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apier/v1/smgenericv1_it_test.go b/apier/v1/smgenericv1_it_test.go index c1a8cda8b..56c019d3e 100644 --- a/apier/v1/smgenericv1_it_test.go +++ b/apier/v1/smgenericv1_it_test.go @@ -116,7 +116,7 @@ func TestSMGV1CacheStats(t *testing.T) { } var rcvStats *utils.CacheStats - expectedStats := &utils.CacheStats{Destinations: 4, RatingPlans: 3, RatingProfiles: 8, Actions: 7, ActionPlans: 4, SharedGroups: 1, Aliases: 1, + expectedStats := &utils.CacheStats{Destinations: 4, RatingPlans: 4, RatingProfiles: 9, Actions: 7, ActionPlans: 4, SharedGroups: 1, Aliases: 1, DerivedChargers: 1, LcrProfiles: 5, CdrStats: 6, Users: 3, LastLoadId: smgV1LoadInst.LoadId, LastLoadTime: smgV1LoadInst.LoadTime.Format(time.RFC3339)} var args utils.AttrCacheStats if err := smgV1Rpc.Call("ApierV2.GetCacheStats", args, &rcvStats); err != nil { diff --git a/general_tests/tut_smgeneric_it_test.go b/general_tests/tut_smgeneric_it_test.go index 0f666f5e7..889686b1f 100644 --- a/general_tests/tut_smgeneric_it_test.go +++ b/general_tests/tut_smgeneric_it_test.go @@ -114,7 +114,7 @@ func TestTutSMGCacheStats(t *testing.T) { } var rcvStats *utils.CacheStats - expectedStats := &utils.CacheStats{Destinations: 4, RatingPlans: 3, RatingProfiles: 8, Actions: 7, ActionPlans: 4, SharedGroups: 1, Aliases: 1, + expectedStats := &utils.CacheStats{Destinations: 4, RatingPlans: 4, RatingProfiles: 9, Actions: 7, ActionPlans: 4, SharedGroups: 1, Aliases: 1, DerivedChargers: 1, LcrProfiles: 5, CdrStats: 6, Users: 3, LastLoadId: smgLoadInst.LoadId, LastLoadTime: smgLoadInst.LoadTime.Format(time.RFC3339)} var args utils.AttrCacheStats if err := tutSMGRpc.Call("ApierV2.GetCacheStats", args, &rcvStats); err != nil { diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index 30409089c..0301e99a9 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -115,7 +115,7 @@ func TestTutLocalCacheStats(t *testing.T) { } var rcvStats *utils.CacheStats - expectedStats := &utils.CacheStats{Destinations: 4, RatingPlans: 3, RatingProfiles: 8, Actions: 7, ActionPlans: 4, SharedGroups: 1, Aliases: 1, + expectedStats := &utils.CacheStats{Destinations: 4, RatingPlans: 4, RatingProfiles: 9, Actions: 7, ActionPlans: 4, SharedGroups: 1, Aliases: 1, DerivedChargers: 1, LcrProfiles: 5, CdrStats: 6, Users: 3, LastLoadId: loadInst.LoadId, LastLoadTime: loadInst.LoadTime.Format(time.RFC3339)} var args utils.AttrCacheStats if err := tutLocalRpc.Call("ApierV2.GetCacheStats", args, &rcvStats); err != nil { From c057840c06777be8da4bcb6dbb6d934b985e99e3 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 22 Feb 2016 21:20:42 +0200 Subject: [PATCH 103/199] better set account action --- apier/v1/accounts.go | 47 +--------------------- engine/account.go | 86 +++++++++++++++++----------------------- engine/action_plan.go | 10 ++--- engine/balance_filter.go | 40 +++++++++++++++++++ 4 files changed, 83 insertions(+), 100 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 1e34a1aad..52885983e 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -20,6 +20,7 @@ package v1 import ( "fmt" + "log" "math" "strings" "time" @@ -482,6 +483,7 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { expTime = &expTimeVal } accID := utils.AccountKey(attr.Tenant, attr.Account) + log.Print("ACC: ", utils.ToIJSON(attr)) if _, err := self.AccountDb.GetAccount(accID); err != nil { // create account if not exists account := &engine.Account{ @@ -533,51 +535,6 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { return nil } -/*func (self *ApierV1) EnableDisableBalance(attr *AttrAddBalance, reply *string) error { - if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { - return utils.NewErrMandatoryIeMissing(missing...) - } - expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) - if err != nil { - *reply = err.Error() - return err - } - accID := utils.ConcatenatedKey(attr.Tenant, attr.Account) - if _, err := self.AccountDb.GetAccount(accID); err != nil { - return utils.ErrNotFound - } - at := &engine.ActionTiming{} - at.SetAccountIDs(utils.StringMap{accID: true}) - - at.SetActions(engine.Actions{ - &engine.Action{ - ActionType: engine.ENABLE_DISABLE_BALANCE, - BalanceType: attr.BalanceType, - Balance: &engine.Balance{ - Uuid: attr.BalanceUuid, - Id: attr.BalanceId, - Value: attr.Value, - ExpirationDate: expTime, - RatingSubject: attr.RatingSubject, - Categories: utils.ParseStringMap(attr.Categories), - Directions: utils.ParseStringMap(attr.Directions), - DestinationIds: utils.ParseStringMap(attr.DestinationIds), - Weight: attr.Weight, - SharedGroups: utils.ParseStringMap(attr.SharedGroups), - TimingIDs: utils.ParseStringMap(attr.TimingIds), - Blocker: attr.Blocker, - Disabled: attr.Disabled, - }, - }, - }) - if err := at.Execute(); err != nil { - *reply = err.Error() - return err - } - *reply = OK - return nil -}*/ - func (self *ApierV1) RemoveBalances(attr *AttrSetBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) diff --git a/engine/account.go b/engine/account.go index 5043ba404..ef3f6a88f 100644 --- a/engine/account.go +++ b/engine/account.go @@ -90,90 +90,76 @@ func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duratio } // sets all the fields of the balance -func (ub *Account) setBalanceAction(a *Action) error { +func (acc *Account) setBalanceAction(a *Action) error { if a == nil { return errors.New("nil action") } - bClone := a.Balance.CreateBalance() - if bClone == nil { - return errors.New("nil balance") + if a.Balance.Type == nil { + return errors.New("missing balance type") } - // load ValueFactor if defined in extra parametrs - if a.ExtraParameters != "" { - vf := ValueFactor{} - err := json.Unmarshal([]byte(a.ExtraParameters), &vf) - if err == nil { - bClone.Factor = vf - } else { - utils.Logger.Warning(fmt.Sprintf("Could load value factor from actions: extra parametrs: %s", a.ExtraParameters)) - } + balanceType := *a.Balance.Type + if acc.BalanceMap == nil { + acc.BalanceMap = make(map[string]BalanceChain, 1) } - if ub.BalanceMap == nil { - ub.BalanceMap = make(map[string]BalanceChain, 1) - } - found := false - balanceType := a.Balance.GetType() var previousSharedGroups utils.StringMap // kept for comparison - for _, b := range ub.BalanceMap[balanceType] { + var balance *Balance + var found bool + for _, b := range acc.BalanceMap[balanceType] { if b.IsExpired() { - continue // just to be safe (cleaned expired balances above) + continue } - b.account = ub - if b.MatchFilter(a.Balance, false) { // for Id or Uuid only - bClone.Id = b.Id - bClone.Uuid = b.Uuid + if (a.Balance.Uuid != nil && b.Uuid == *a.Balance.Uuid) || + (a.Balance.ID != nil && b.Id == *a.Balance.ID) { previousSharedGroups = b.SharedGroups - if bClone.Id != utils.META_DEFAULT { - *b = *bClone - } else { - b.Value = bClone.GetValue() - } + balance = b found = true break // only set one balance } } + // if it is not found then we add it to the list - if !found { - // check if the Id is *default (user trying to create the default balance) - // use only it's value value - if bClone.Id == utils.META_DEFAULT { - bClone = &Balance{ - Id: utils.META_DEFAULT, - Value: bClone.GetValue(), - } - } - bClone.dirty = true // Mark the balance as dirty since we have modified and it should be checked by action triggers - bClone.Uuid = utils.GenUUID() // alway overwrite the uuid for consistency - ub.BalanceMap[balanceType] = append(ub.BalanceMap[balanceType], bClone) + if balance == nil { + balance = &Balance{} + balance.Uuid = utils.GenUUID() // alway overwrite the uuid for consistency + acc.BalanceMap[balanceType] = append(acc.BalanceMap[balanceType], balance) } - if !found || !previousSharedGroups.Equal(bClone.SharedGroups) { + if a.Balance.ID != nil && *a.Balance.ID == utils.META_DEFAULT { + balance.Id = utils.META_DEFAULT + if a.Balance.Value != nil { + balance.Value = *a.Balance.Value + } + } else { + a.Balance.ModifyBalance(balance) + } + + if !found || !previousSharedGroups.Equal(balance.SharedGroups) { _, err := Guardian.Guard(func() (interface{}, error) { - for sgId := range bClone.SharedGroups { + for sgID := range balance.SharedGroups { // add shared group member - sg, err := ratingStorage.GetSharedGroup(sgId, false) + sg, err := ratingStorage.GetSharedGroup(sgID, false) if err != nil || sg == nil { //than is problem - utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) + utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgID)) } else { - if _, found := sg.MemberIds[ub.Id]; !found { + if _, found := sg.MemberIds[acc.Id]; !found { // add member and save if sg.MemberIds == nil { sg.MemberIds = make(utils.StringMap) } - sg.MemberIds[ub.Id] = true + sg.MemberIds[acc.Id] = true ratingStorage.SetSharedGroup(sg) } } } return 0, nil - }, 0, bClone.SharedGroups.Slice()...) + }, 0, balance.SharedGroups.Slice()...) if err != nil { return err } } - ub.InitCounters() - ub.ExecuteActionTriggers(nil) + acc.InitCounters() + acc.ExecuteActionTriggers(nil) return nil } diff --git a/engine/action_plan.go b/engine/action_plan.go index a9081b90a..b27f7e448 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -283,7 +283,7 @@ func (at *ActionTiming) Execute() (err error) { } for accID, _ := range at.accountIDs { _, err = Guardian.Guard(func() (interface{}, error) { - ub, err := accountingStorage.GetAccount(accID) + acc, err := accountingStorage.GetAccount(accID) if err != nil { utils.Logger.Warning(fmt.Sprintf("Could not get account id: %s. Skipping!", accID)) return 0, err @@ -294,7 +294,7 @@ func (at *ActionTiming) Execute() (err error) { //log.Print("A: ", utils.ToJSON(a)) // check action filter if len(a.Filter) > 0 { - matched, err := ub.matchActionFilter(a.Filter) + matched, err := acc.matchActionFilter(a.Filter) //log.Print("Checkng: ", a.Filter, matched) if err != nil { return 0, err @@ -303,7 +303,7 @@ func (at *ActionTiming) Execute() (err error) { continue } } - if ub.Disabled && a.ActionType != ENABLE_ACCOUNT { + if acc.Disabled && a.ActionType != ENABLE_ACCOUNT { continue // disabled acocunts are not removed from action plan //return 0, fmt.Errorf("Account %s is disabled", accID) } @@ -320,7 +320,7 @@ func (at *ActionTiming) Execute() (err error) { transactionFailed = true break } - if err := actionFunction(ub, nil, a, aac); err != nil { + if err := actionFunction(acc, nil, a, aac); err != nil { utils.Logger.Err(fmt.Sprintf("Error executing action %s: %v!", a.ActionType, err)) transactionFailed = true break @@ -330,7 +330,7 @@ func (at *ActionTiming) Execute() (err error) { } } if !transactionFailed && !removeAccountActionFound { - accountingStorage.SetAccount(ub) + accountingStorage.SetAccount(acc) } return 0, nil }, 0, accID) diff --git a/engine/balance_filter.go b/engine/balance_filter.go index 28d9c912f..1c8a214ba 100644 --- a/engine/balance_filter.go +++ b/engine/balance_filter.go @@ -279,3 +279,43 @@ func (bp *BalanceFilter) HasExpirationDate() bool { } return (*bp.ExpirationDate).IsZero() } + +func (bf *BalanceFilter) ModifyBalance(b *Balance) { + if b == nil { + return + } + if bf.Directions != nil { + b.Directions = *bf.Directions + } + if bf.Value != nil { + b.Value = *bf.Value + } + if bf.ExpirationDate != nil { + b.ExpirationDate = *bf.ExpirationDate + } + if bf.RatingSubject != nil { + b.RatingSubject = *bf.RatingSubject + } + if bf.Categories != nil { + b.Categories = *bf.Categories + } + if bf.DestinationIDs != nil { + b.DestinationIds = *bf.DestinationIDs + } + if bf.SharedGroups != nil { + b.SharedGroups = *bf.SharedGroups + } + if bf.TimingIDs != nil { + b.TimingIDs = *bf.TimingIDs + } + if bf.Weight != nil { + b.Weight = *bf.Weight + } + if bf.Blocker != nil { + b.Blocker = *bf.Blocker + } + if bf.Disabled != nil { + b.Disabled = *bf.Disabled + } + b.SetDirty() // Mark the balance as dirty since we have modified and it should be checked by action triggers +} From aeda5eb604accff170271dbb2be22f9ff4eeb702 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 23 Feb 2016 13:08:13 +0100 Subject: [PATCH 104/199] Diameter - fix for meta_handler *value_exponent --- agents/libdmt.go | 4 ++-- agents/libdmt_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agents/libdmt.go b/agents/libdmt.go index 471c6a9c5..9b89608cf 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -228,11 +228,11 @@ func metaValueExponent(m *diam.Message, argsTpl utils.RSRFields, roundingDecimal if err != nil { return "", err } - exp, err := strconv.ParseFloat(handlerArgs[1], 64) + exp, err := strconv.Atoi(handlerArgs[1]) if err != nil { return "", err } - res := val * math.Exp(exp) + res := val * math.Pow10(exp) return strconv.FormatFloat(utils.Round(res, roundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil } diff --git a/agents/libdmt_test.go b/agents/libdmt_test.go index e56252a35..0814f15ec 100644 --- a/agents/libdmt_test.go +++ b/agents/libdmt_test.go @@ -106,7 +106,7 @@ func TestMetaValueExponent(t *testing.T) { }) if val, err := metaValueExponent(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err != nil { t.Error(err) - } else if val != "67.3794699909" { + } else if val != "0.1" { t.Error("Received: ", val) } if _, err = metaValueExponent(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err == nil { From 2dcffd4dfdee05de354d99177eac05eaf55f29b0 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 23 Feb 2016 18:02:52 +0100 Subject: [PATCH 105/199] Diameter - remove hardcoded logic inside replies, expose processorVars in templates --- agents/dmtagent.go | 64 ++++++++++++------------------------------- agents/libdmt.go | 9 ++++-- agents/libdmt_test.go | 2 +- 3 files changed, 25 insertions(+), 50 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 904d105f8..31e03ba42 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -107,6 +107,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro } } var maxUsage float64 + processorVars := make(map[string]string) if reqProcessor.DryRun { // DryRun does not send over network utils.Logger.Info(fmt.Sprintf(" SMGenericEvent: %+v", smgEv)) if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(diam.LimitedSuccess), @@ -136,57 +137,28 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro } } } - var populatedResultCode bool + processorVars[CGRResultCode] = strconv.Itoa(diam.Success) if err != nil { - utils.Logger.Debug(fmt.Sprintf("Received error from rater: %+v", err)) - if strings.HasSuffix(err.Error(), utils.ErrAccountNotFound.Error()) || strings.HasSuffix(err.Error(), utils.ErrUserNotFound.Error()) { // 5030 in case of AccountNotFound - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "5030", - false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) - return nil - } - populatedResultCode = true - } else if strings.HasSuffix(err.Error(), utils.ErrCreditInsufficient.Error()) { - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "4012", - false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) - return nil - } - populatedResultCode = true - } else { - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), - false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) - return nil - } - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v, API error: %s", ccr.diamMessage, err)) - return cca - } - - } - if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && maxUsage == 0 && !populatedResultCode { // Not enough balance, RFC demands 4012 - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, "4012", - false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) - return nil - } - populatedResultCode = true - } else if !populatedResultCode { - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(diam.Success), - false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) - return nil + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v, API error: %s", ccr.diamMessage, err)) + switch { // Prettify some errors + case strings.HasSuffix(err.Error(), utils.ErrAccountNotFound.Error()): + processorVars[CGRError] = utils.ErrAccountNotFound.Error() + case strings.HasSuffix(err.Error(), utils.ErrUserNotFound.Error()): + processorVars[CGRError] = utils.ErrUserNotFound.Error() + case strings.HasSuffix(err.Error(), utils.ErrCreditInsufficient.Error()): + processorVars[CGRError] = utils.ErrCreditInsufficient.Error() + default: // Unknown error + processorVars[CGRError] = err.Error() + processorVars[CGRResultCode] = strconv.Itoa(DiameterRatingFailed) } } - if ccr.CCRequestType != 3 && ccr.CCRequestType != 4 && !populatedResultCode { // For terminate or previously marked unauthorized, we don't add granted-service-unit AVP - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Granted-Service-Unit", "CC-Time"}, strconv.FormatFloat(maxUsage, 'f', 0, 64), - false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Granted-Service-Unit, error: %s", ccr.diamMessage, err)) - return nil - } + if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, processorVars[CGRResultCode], + false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) + return nil } } - if err := cca.SetProcessorAVPs(reqProcessor, maxUsage); err != nil { + if err := cca.SetProcessorAVPs(reqProcessor, processorVars); err != nil { if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) diff --git a/agents/libdmt.go b/agents/libdmt.go index 9b89608cf..d1b3269e5 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -54,6 +54,9 @@ const ( META_VALUE_EXPONENT = "*value_exponent" DIAMETER_CCR = "DIAMETER_CCR" DiameterRatingFailed = 5031 + CGRError = "CGRError" + CGRMaxUsage = "CGRMaxUsage" + CGRResultCode = "CGRResultCode" ) func loadDictionaries(dictsDir, componentId string) error { @@ -644,11 +647,11 @@ func (self *CCA) AsDiameterMessage() *diam.Message { } // SetProcessorAVPs will add AVPs to self.diameterMessage based on template defined in processor.CCAFields -func (self *CCA) SetProcessorAVPs(reqProcessor *config.DARequestProcessor, maxUsage float64) error { +func (self *CCA) SetProcessorAVPs(reqProcessor *config.DARequestProcessor, processorVars map[string]string) error { for _, cfgFld := range reqProcessor.CCAFields { - fmtOut, err := fieldOutVal(self.ccrMessage, cfgFld, maxUsage) + fmtOut, err := fieldOutVal(self.ccrMessage, cfgFld, processorVars) if err == ErrFilterNotPassing { // Field not in or filter not passing, try match in answer - fmtOut, err = fieldOutVal(self.diamMessage, cfgFld, maxUsage) + fmtOut, err = fieldOutVal(self.diamMessage, cfgFld, processorVars) } if err != nil { if err == ErrFilterNotPassing { diff --git a/agents/libdmt_test.go b/agents/libdmt_test.go index 0814f15ec..3eb91584e 100644 --- a/agents/libdmt_test.go +++ b/agents/libdmt_test.go @@ -302,7 +302,7 @@ func TestCCASetProcessorAVPs(t *testing.T) { diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(0)), // Subscription-Id-Type diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("33708000003")), // Subscription-Id-Data }}) - if err := cca.SetProcessorAVPs(reqProcessor, 0); err != nil { + if err := cca.SetProcessorAVPs(reqProcessor, map[string]string{}); err != nil { t.Error(err) } else if ccaMsg := cca.AsDiameterMessage(); !reflect.DeepEqual(eMessage, ccaMsg) { t.Errorf("Expecting: %+v, received: %+v", eMessage, ccaMsg) From 2ea5be2d3a4b445523e1f35ac99f70ed9592a788 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 23 Feb 2016 19:41:16 +0100 Subject: [PATCH 106/199] Adding CGRMaxUsage to processor vars --- agents/dmtagent.go | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 31e03ba42..0c951e3af 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -108,13 +108,10 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro } var maxUsage float64 processorVars := make(map[string]string) + processorVars[CGRResultCode] = strconv.Itoa(diam.Success) if reqProcessor.DryRun { // DryRun does not send over network utils.Logger.Info(fmt.Sprintf(" SMGenericEvent: %+v", smgEv)) - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(diam.LimitedSuccess), - false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) - return nil - } + processorVars[CGRResultCode] = strconv.Itoa(diam.LimitedSuccess) } else { // Find out maxUsage over APIs switch ccr.CCRequestType { case 1: @@ -137,7 +134,6 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro } } } - processorVars[CGRResultCode] = strconv.Itoa(diam.Success) if err != nil { utils.Logger.Err(fmt.Sprintf(" Processing message: %+v, API error: %s", ccr.diamMessage, err)) switch { // Prettify some errors @@ -152,11 +148,12 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro processorVars[CGRResultCode] = strconv.Itoa(DiameterRatingFailed) } } - if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, processorVars[CGRResultCode], - false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) - return nil - } + processorVars[CGRMaxUsage] = strconv.FormatFloat(maxUsage, 'f', -1, 64) + } + if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, processorVars[CGRResultCode], + false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) + return nil } if err := cca.SetProcessorAVPs(reqProcessor, processorVars); err != nil { if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), From 1777a6e1a5de522f17a902bc52ec887bb92f6831 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 23 Feb 2016 18:00:05 +0200 Subject: [PATCH 107/199] renamed Id to ID in balance --- cmd/cgr-loader/migrator_rc8.go | 4 +- engine/account.go | 24 ++++----- engine/account_test.go | 94 +++++++++++++++++----------------- engine/action.go | 8 +-- engine/action_trigger.go | 2 +- engine/actions_test.go | 84 +++++++++++++++--------------- engine/balance_filter.go | 14 ++--- engine/balances.go | 56 ++++++++++---------- engine/balances_test.go | 34 ++++++------ engine/calldesc_test.go | 12 ++--- engine/responder_test.go | 8 +-- engine/storage_test.go | 2 +- 12 files changed, 171 insertions(+), 171 deletions(-) diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index 1c9386a53..405977ce6 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -206,12 +206,12 @@ func (mig MigratorRC8) migrateAccounts() error { } newAcc.BalanceMap[newBalKey][index] = &engine.Balance{ Uuid: oldBal.Uuid, - Id: oldBal.Id, + ID: oldBal.Id, Value: oldBal.Value, Directions: utils.ParseStringMap(newBalDirection), ExpirationDate: oldBal.ExpirationDate, Weight: oldBal.Weight, - DestinationIds: utils.ParseStringMap(oldBal.DestinationIds), + DestinationIDs: utils.ParseStringMap(oldBal.DestinationIds), RatingSubject: oldBal.RatingSubject, Categories: utils.ParseStringMap(oldBal.Category), SharedGroups: utils.ParseStringMap(oldBal.SharedGroup), diff --git a/engine/account.go b/engine/account.go index ef3f6a88f..b833ae866 100644 --- a/engine/account.go +++ b/engine/account.go @@ -109,7 +109,7 @@ func (acc *Account) setBalanceAction(a *Action) error { continue } if (a.Balance.Uuid != nil && b.Uuid == *a.Balance.Uuid) || - (a.Balance.ID != nil && b.Id == *a.Balance.ID) { + (a.Balance.ID != nil && b.ID == *a.Balance.ID) { previousSharedGroups = b.SharedGroups balance = b found = true @@ -125,7 +125,7 @@ func (acc *Account) setBalanceAction(a *Action) error { } if a.Balance.ID != nil && *a.Balance.ID == utils.META_DEFAULT { - balance.Id = utils.META_DEFAULT + balance.ID = utils.META_DEFAULT if a.Balance.Value != nil { balance.Value = *a.Balance.Value } @@ -196,9 +196,9 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { if !found { // check if the Id is *default (user trying to create the default balance) // use only it's value value - if bClone.Id == utils.META_DEFAULT { + if bClone.ID == utils.META_DEFAULT { bClone = &Balance{ - Id: utils.META_DEFAULT, + ID: utils.META_DEFAULT, Value: -bClone.GetValue(), } } else { @@ -300,12 +300,12 @@ func (ub *Account) getBalancesForPrefix(prefix, category, direction, tor string, continue } b.account = ub - if len(b.DestinationIds) > 0 && b.DestinationIds[utils.ANY] == false { + if len(b.DestinationIDs) > 0 && b.DestinationIDs[utils.ANY] == false { for _, p := range utils.SplitPrefix(prefix, MIN_PREFIX_MATCH) { if x, err := cache2go.Get(utils.DESTINATION_PREFIX + p); err == nil { destIds := x.(map[interface{}]struct{}) for dId, _ := range destIds { - if b.DestinationIds[dId.(string)] == true { + if b.DestinationIDs[dId.(string)] == true { b.precision = len(p) usefulBalances = append(usefulBalances, b) break @@ -497,7 +497,7 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo &Balance{ Directions: utils.StringMap{leftCC.Direction: true}, Value: cost, - DestinationIds: utils.NewStringMap(leftCC.Destination), + DestinationIDs: utils.NewStringMap(leftCC.Destination), }) } } @@ -523,7 +523,7 @@ func (ub *Account) GetDefaultMoneyBalance() *Balance { // create default balance defaultBalance := &Balance{ Uuid: utils.GenUUID(), - Id: utils.META_DEFAULT, + ID: utils.META_DEFAULT, } // minimum weight if ub.BalanceMap == nil { ub.BalanceMap = make(map[string]BalanceChain) @@ -960,9 +960,9 @@ func (acc *Account) AsOldStructure() interface{} { Recurrent: at.Recurrent, MinSleep: at.MinSleep, BalanceType: at.Balance.GetType(), - BalanceId: b.Id, + BalanceId: b.ID, BalanceDirection: b.Directions.String(), - BalanceDestinationIds: b.DestinationIds.String(), + BalanceDestinationIds: b.DestinationIDs.String(), BalanceWeight: b.Weight, BalanceExpirationDate: b.ExpirationDate, BalanceTimingTags: b.TimingIDs.String(), @@ -983,11 +983,11 @@ func (acc *Account) AsOldStructure() interface{} { for i, b := range values { result.BalanceMap[key][i] = &Balance{ Uuid: b.Uuid, - Id: b.Id, + Id: b.ID, Value: b.Value, ExpirationDate: b.ExpirationDate, Weight: b.Weight, - DestinationIds: b.DestinationIds.String(), + DestinationIds: b.DestinationIDs.String(), RatingSubject: b.RatingSubject, Category: b.Categories.String(), SharedGroup: b.SharedGroups.String(), diff --git a/engine/account_test.go b/engine/account_test.go index 7def0cc0b..dc454c15d 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -83,8 +83,8 @@ func TestBalanceChainStoreRestore(t *testing.T) { } func TestAccountStorageStoreRestore(t *testing.T) { - b1 := &Balance{Value: 10, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}} - b2 := &Balance{Value: 100, Weight: 20, DestinationIds: utils.StringMap{"RET": true}} + b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} + b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} accountingStorage.SetAccount(rifsBalance) ub1, err := accountingStorage.GetAccount("other") @@ -95,8 +95,8 @@ func TestAccountStorageStoreRestore(t *testing.T) { } func TestGetSecondsForPrefix(t *testing.T) { - b1 := &Balance{Value: 10, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}} - b2 := &Balance{Value: 100, Weight: 20, DestinationIds: utils.StringMap{"RET": true}} + b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} + b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} ub1 := &Account{Id: "CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 200}}}} cd := &CallDescriptor{ Category: "0", @@ -118,8 +118,8 @@ func TestGetSecondsForPrefix(t *testing.T) { } func TestGetSpecialPricedSeconds(t *testing.T) { - b1 := &Balance{Value: 10, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "minu"} - b2 := &Balance{Value: 100, Weight: 20, DestinationIds: utils.StringMap{"RET": true}, RatingSubject: "minu"} + b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "minu"} + b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}, RatingSubject: "minu"} ub1 := &Account{ Id: "OUT:CUSTOMER_1:rif", @@ -150,8 +150,8 @@ func TestGetSpecialPricedSeconds(t *testing.T) { } func TestAccountStorageStore(t *testing.T) { - b1 := &Balance{Value: 10, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}} - b2 := &Balance{Value: 100, Weight: 20, DestinationIds: utils.StringMap{"RET": true}} + b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} + b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} accountingStorage.SetAccount(rifsBalance) result, err := accountingStorage.GetAccount(rifsBalance.Id) @@ -165,7 +165,7 @@ func TestAccountStorageStore(t *testing.T) { } func TestDebitCreditZeroSecond(t *testing.T) { - b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "*zero1s"} + b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1s"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -205,7 +205,7 @@ func TestDebitCreditZeroSecond(t *testing.T) { } func TestDebitCreditZeroMinute(t *testing.T) { - b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} + b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -250,8 +250,8 @@ func TestDebitCreditZeroMinute(t *testing.T) { } func TestDebitCreditZeroMixedMinute(t *testing.T) { - b1 := &Balance{Uuid: "testm", Value: 70, Weight: 5, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} - b2 := &Balance{Uuid: "tests", Value: 10, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "*zero1s"} + b1 := &Balance{Uuid: "testm", Value: 70, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} + b2 := &Balance{Uuid: "tests", Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1s"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -298,7 +298,7 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) { } func TestDebitCreditNoCredit(t *testing.T) { - b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} + b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -349,7 +349,7 @@ func TestDebitCreditNoCredit(t *testing.T) { } func TestDebitCreditHasCredit(t *testing.T) { - b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} + b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -402,7 +402,7 @@ func TestDebitCreditHasCredit(t *testing.T) { } func TestDebitCreditSplitMinutesMoney(t *testing.T) { - b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "*zero1s"} + b1 := &Balance{Uuid: "testb", Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1s"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -450,7 +450,7 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) { } func TestDebitCreditMoreTimespans(t *testing.T) { - b1 := &Balance{Uuid: "testb", Value: 150, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} + b1 := &Balance{Uuid: "testb", Value: 150, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -498,8 +498,8 @@ func TestDebitCreditMoreTimespans(t *testing.T) { } func TestDebitCreditMoreTimespansMixed(t *testing.T) { - b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} - b2 := &Balance{Uuid: "testa", Value: 150, Weight: 5, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "*zero1s"} + b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} + b2 := &Balance{Uuid: "testa", Value: 150, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1s"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -548,7 +548,7 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) { } func TestDebitCreditNoConectFeeCredit(t *testing.T) { - b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} + b1 := &Balance{Uuid: "testb", Value: 70, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1m"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -647,7 +647,7 @@ func TestDebitCreditMoneyOnly(t *testing.T) { } func TestDebitCreditSubjectMinutes(t *testing.T) { - b1 := &Balance{Uuid: "testb", Categories: utils.NewStringMap("0"), Value: 250, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "minu"} + b1 := &Balance{Uuid: "testb", Categories: utils.NewStringMap("0"), Value: 250, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "minu"} cc := &CallCost{ Tenant: "vdf", Category: "0", @@ -731,7 +731,7 @@ func TestDebitCreditSubjectMoney(t *testing.T) { testCallcost: cc, } rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 75, DestinationIds: utils.StringMap{"NAT": true}, RatingSubject: "minu"}}, + utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 75, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "minu"}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -811,7 +811,7 @@ func TestAccountdebitBalance(t *testing.T) { ub := &Account{ Id: "rif", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } newMb := &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), @@ -821,7 +821,7 @@ func TestAccountdebitBalance(t *testing.T) { } a := &Action{Balance: newMb} ub.debitBalanceAction(a, false) - if len(ub.BalanceMap[utils.VOICE]) != 3 || !ub.BalanceMap[utils.VOICE][2].DestinationIds.Equal(*newMb.DestinationIDs) { + if len(ub.BalanceMap[utils.VOICE]) != 3 || !ub.BalanceMap[utils.VOICE][2].DestinationIDs.Equal(*newMb.DestinationIDs) { t.Errorf("Error adding minute bucket! %d %+v %+v", len(ub.BalanceMap[utils.VOICE]), ub.BalanceMap[utils.VOICE][2], newMb) } } @@ -830,12 +830,12 @@ func TestAccountdebitBalance(t *testing.T) { ub := &Account{ Id: "rif", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } newMb := &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), - DestinationIds: utils.StringMapPointer(utils.StringMap{"NAT": true}), + DestinationIDs: utils.StringMapPointer(utils.StringMap{"NAT": true}), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), } a := &Action{Balance: newMb} @@ -853,7 +853,7 @@ func TestAccountdebitBalanceExists(t *testing.T) { ub := &Account{ Id: "rif", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Value: 15, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Value: 15, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } newMb := &BalanceFilter{ Value: utils.Float64Pointer(-10), @@ -873,7 +873,7 @@ func TestAccountAddMinuteNil(t *testing.T) { ub := &Account{ Id: "rif", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } ub.debitBalanceAction(nil, false) if len(ub.BalanceMap[utils.VOICE]) != 2 { @@ -921,7 +921,7 @@ func TestAccountAddMinutBucketEmpty(t *testing.T) { func TestAccountExecuteTriggeredActions(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.StringMap{utils.OUT: true})}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } @@ -945,7 +945,7 @@ func TestAccountExecuteTriggeredActions(t *testing.T) { func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Filter: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, Value: 1.0}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, } @@ -973,7 +973,7 @@ func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { func TestAccountExecuteTriggeredDayWeek(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ &ActionTrigger{UniqueID: "day_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, &ActionTrigger{UniqueID: "week_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, @@ -1001,7 +1001,7 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { func TestAccountExpActionTrigger(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100, ExpirationDate: time.Date(2015, time.November, 9, 9, 48, 0, 0, time.UTC)}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100, ExpirationDate: time.Date(2015, time.November, 9, 9, 48, 0, 0, time.UTC)}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ &ActionTrigger{ID: "check expired balances", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, @@ -1019,7 +1019,7 @@ func TestAccountExpActionTrigger(t *testing.T) { func TestAccountExpActionTriggerNotActivated(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, @@ -1037,7 +1037,7 @@ func TestAccountExpActionTriggerNotActivated(t *testing.T) { func TestAccountExpActionTriggerExpired(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIds: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, }, @@ -1134,8 +1134,8 @@ func TestAccountRefund(t *testing.T) { &Balance{Uuid: "moneya", Value: 100}, }, utils.VOICE: BalanceChain{ - &Balance{Uuid: "minutea", Value: 10, Weight: 20, DestinationIds: utils.StringMap{"NAT": true}}, - &Balance{Uuid: "minuteb", Value: 10, DestinationIds: utils.StringMap{"RET": true}}, + &Balance{Uuid: "minutea", Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, + &Balance{Uuid: "minuteb", Value: 10, DestinationIDs: utils.StringMap{"RET": true}}, }, }, } @@ -1325,7 +1325,7 @@ func TestDebitSMS(t *testing.T) { testCallcost: cc, } rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ - utils.SMS: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIds: utils.StringMap{"NAT": true}}}, + utils.SMS: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} var err error @@ -1368,7 +1368,7 @@ func TestDebitGeneric(t *testing.T) { testCallcost: cc, } rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ - utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIds: utils.StringMap{"NAT": true}}}, + utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} var err error @@ -1411,7 +1411,7 @@ func TestDebitGenericBalance(t *testing.T) { testCallcost: cc, } rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ - utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIds: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}}}, + utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}}}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} var err error @@ -1454,7 +1454,7 @@ func TestDebitGenericBalanceWithRatingSubject(t *testing.T) { testCallcost: cc, } rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ - utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIds: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}, RatingSubject: "free"}}, + utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}, RatingSubject: "free"}}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} var err error @@ -1504,7 +1504,7 @@ func TestDebitDataUnits(t *testing.T) { testCallcost: cc, } rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ - utils.DATA: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIds: utils.StringMap{"NAT": true}}}, + utils.DATA: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} var err error @@ -1558,7 +1558,7 @@ func TestDebitDataMoney(t *testing.T) { testCallcost: cc, } rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ - utils.DATA: BalanceChain{&Balance{Uuid: "testm", Value: 0, Weight: 5, DestinationIds: utils.StringMap{"NAT": true}}}, + utils.DATA: BalanceChain{&Balance{Uuid: "testm", Value: 0, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, utils.MONETARY: BalanceChain{&Balance{Value: 160}}, }} var err error @@ -1752,8 +1752,8 @@ func TestAccountDoubleInitCounters(t *testing.T) { func BenchmarkGetSecondForPrefix(b *testing.B) { b.StopTimer() - b1 := &Balance{Value: 10, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}} - b2 := &Balance{Value: 100, Weight: 20, DestinationIds: utils.StringMap{"RET": true}} + b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} + b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} ub1 := &Account{Id: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} cd := &CallDescriptor{ @@ -1766,8 +1766,8 @@ func BenchmarkGetSecondForPrefix(b *testing.B) { } func BenchmarkAccountStorageStoreRestore(b *testing.B) { - b1 := &Balance{Value: 10, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}} - b2 := &Balance{Value: 100, Weight: 20, DestinationIds: utils.StringMap{"RET": true}} + b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} + b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} for i := 0; i < b.N; i++ { accountingStorage.SetAccount(rifsBalance) @@ -1776,8 +1776,8 @@ func BenchmarkAccountStorageStoreRestore(b *testing.B) { } func BenchmarkGetSecondsForPrefix(b *testing.B) { - b1 := &Balance{Value: 10, Weight: 10, DestinationIds: utils.StringMap{"NAT": true}} - b2 := &Balance{Value: 100, Weight: 20, DestinationIds: utils.StringMap{"RET": true}} + b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} + b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} ub1 := &Account{Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} cd := &CallDescriptor{ Destination: "0723", diff --git a/engine/action.go b/engine/action.go index edd7cfda6..fc300f37f 100644 --- a/engine/action.go +++ b/engine/action.go @@ -187,11 +187,11 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) case "BalanceUUID": parsedValue += rsrFld.ParseValue(b.Uuid) case "BalanceID": - parsedValue += rsrFld.ParseValue(b.Id) + parsedValue += rsrFld.ParseValue(b.ID) case "BalanceValue": parsedValue += rsrFld.ParseValue(strconv.FormatFloat(b.GetValue(), 'f', -1, 64)) case "DestinationIDs": - parsedValue += rsrFld.ParseValue(b.DestinationIds.String()) + parsedValue += rsrFld.ParseValue(b.DestinationIDs.String()) case "ExtraParameters": parsedValue += rsrFld.ParseValue(action.ExtraParameters) case "RatingSubject": @@ -497,7 +497,7 @@ func setddestinations(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actio var ddcDestId string for _, bchain := range ub.BalanceMap { for _, b := range bchain { - for destId := range b.DestinationIds { + for destId := range b.DestinationIDs { if strings.HasPrefix(destId, "*ddc") { ddcDestId = destId break @@ -622,7 +622,7 @@ func transferMonetaryDefaultAction(acc *Account, sq *StatsQueueTriggered, a *Act bChain := acc.BalanceMap[utils.MONETARY] for _, balance := range bChain { if balance.Uuid != defaultBalance.Uuid && - balance.Id != defaultBalance.Id && // extra caution + balance.ID != defaultBalance.ID && // extra caution balance.MatchFilter(a.Balance, false) { if balance.Value > 0 { defaultBalance.Value += balance.Value diff --git a/engine/action_trigger.go b/engine/action_trigger.go index afca42939..0ab88a7bd 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -142,7 +142,7 @@ func (at *ActionTrigger) Match(a *Action) bool { func (at *ActionTrigger) CreateBalance() *Balance { b := at.Balance.CreateBalance() - b.Id = at.UniqueID + b.ID = at.UniqueID return b } diff --git a/engine/actions_test.go b/engine/actions_test.go index af03aa6be..6278b54e8 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -693,7 +693,7 @@ func TestActionTriggers(t *testing.T) { func TestActionResetTriggres(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -719,7 +719,7 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { func TestActionResetTriggresActionFilter(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -732,7 +732,7 @@ func TestActionResetTriggresActionFilter(t *testing.T) { func TestActionSetPostpaid(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -746,7 +746,7 @@ func TestActionSetPrepaid(t *testing.T) { ub := &Account{ Id: "TEST_UB", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -760,7 +760,7 @@ func TestActionResetPrepaid(t *testing.T) { ub := &Account{ Id: "TEST_UB", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -778,7 +778,7 @@ func TestActionResetPrepaid(t *testing.T) { func TestActionResetPostpaid(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -794,7 +794,7 @@ func TestActionResetPostpaid(t *testing.T) { func TestActionTopupResetCredit(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -834,7 +834,7 @@ func TestActionTopupResetCreditId(t *testing.T) { BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{Value: 100}, - &Balance{Id: "TEST_B", Value: 15}, + &Balance{ID: "TEST_B", Value: 15}, }, }, } @@ -853,7 +853,7 @@ func TestActionTopupResetCreditNoId(t *testing.T) { BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{Value: 100, Directions: utils.NewStringMap(utils.OUT)}, - &Balance{Id: "TEST_B", Value: 15, Directions: utils.NewStringMap(utils.OUT)}, + &Balance{ID: "TEST_B", Value: 15, Directions: utils.NewStringMap(utils.OUT)}, }, }, } @@ -871,7 +871,7 @@ func TestActionTopupResetMinutes(t *testing.T) { Id: "TEST_UB", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, - utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -890,7 +890,7 @@ func TestActionTopupResetMinutes(t *testing.T) { func TestActionTopupCredit(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -908,7 +908,7 @@ func TestActionTopupCredit(t *testing.T) { func TestActionTopupMinutes(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -927,7 +927,7 @@ func TestActionTopupMinutes(t *testing.T) { func TestActionDebitCredit(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -945,7 +945,7 @@ func TestActionDebitCredit(t *testing.T) { func TestActionDebitMinutes(t *testing.T) { ub := &Account{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -968,8 +968,8 @@ func TestActionResetAllCounters(t *testing.T) { BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{ - &Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, - &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT)}}}, + &Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, + &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT)}}}, ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -998,7 +998,7 @@ func TestActionResetCounterOnlyDefault(t *testing.T) { AllowNegative: true, BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, - utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}} @@ -1028,7 +1028,7 @@ func TestActionResetCounterCredit(t *testing.T) { ub := &Account{ Id: "TEST_UB", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}, utils.SMS: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } @@ -1274,7 +1274,7 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) { } func TestActionSetDDestination(t *testing.T) { - acc := &Account{BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{DestinationIds: utils.NewStringMap("*ddc_test")}}}} + acc := &Account{BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{DestinationIDs: utils.NewStringMap("*ddc_test")}}}} origD := &Destination{Id: "*ddc_test", Prefixes: []string{"111", "222"}} ratingStorage.SetDestination(origD) ratingStorage.CacheRatingPrefixValues(map[string][]string{utils.DESTINATION_PREFIX: []string{utils.DESTINATION_PREFIX + "*ddc_test"}}) @@ -1478,12 +1478,12 @@ func TestActionRemoveBalance(t *testing.T) { }, &Balance{ Value: 10, - DestinationIds: utils.NewStringMap("NAT", "RET"), + DestinationIDs: utils.NewStringMap("NAT", "RET"), ExpirationDate: time.Date(2025, time.November, 11, 22, 39, 0, 0, time.UTC), }, &Balance{ Value: 10, - DestinationIds: utils.NewStringMap("NAT", "RET"), + DestinationIDs: utils.NewStringMap("NAT", "RET"), }, }, }, @@ -1523,7 +1523,7 @@ func TestActionTransferMonetaryDefault(t *testing.T) { utils.MONETARY: BalanceChain{ &Balance{ Uuid: utils.GenUUID(), - Id: utils.META_DEFAULT, + ID: utils.META_DEFAULT, Value: 10, }, &Balance{ @@ -1579,7 +1579,7 @@ func TestActionTransferMonetaryDefaultFilter(t *testing.T) { utils.MONETARY: BalanceChain{ &Balance{ Uuid: utils.GenUUID(), - Id: utils.META_DEFAULT, + ID: utils.META_DEFAULT, Value: 10, Weight: 20, }, @@ -1640,7 +1640,7 @@ func TestActionConditionalTopup(t *testing.T) { utils.MONETARY: BalanceChain{ &Balance{ Uuid: utils.GenUUID(), - Id: utils.META_DEFAULT, + ID: utils.META_DEFAULT, Value: 10, Weight: 20, }, @@ -1704,7 +1704,7 @@ func TestActionConditionalTopupNoMatch(t *testing.T) { utils.MONETARY: BalanceChain{ &Balance{ Uuid: utils.GenUUID(), - Id: utils.META_DEFAULT, + ID: utils.META_DEFAULT, Value: 10, Weight: 20, }, @@ -1831,7 +1831,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { "*data": BalanceChain{ &Balance{ Uuid: "fc927edb-1bd6-425e-a2a3-9fd8bafaa524", - Id: "for_v3hsillmilld500m_data_500_m", + ID: "for_v3hsillmilld500m_data_500_m", Value: 5.242, Weight: 10, RatingSubject: "for_v3hsillmilld500m_data_forfait", @@ -1843,17 +1843,17 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { "*monetary": BalanceChain{ &Balance{ Uuid: "9fa1847a-f36a-41a7-8ec0-dfaab370141e", - Id: "*default", + ID: "*default", Value: -1.95001, }, }, "*sms": BalanceChain{ &Balance{ Uuid: "d348d15d-2988-4ee4-b847-6a552f94e2ec", - Id: "for_v3hsillmilld500m_mms_ill", + ID: "for_v3hsillmilld500m_mms_ill", Value: 20000, Weight: 10, - DestinationIds: utils.StringMap{ + DestinationIDs: utils.StringMap{ "FRANCE_NATIONAL": true, }, Categories: utils.StringMap{ @@ -1864,10 +1864,10 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { }, &Balance{ Uuid: "f4643517-31f6-4199-980f-04cf535471ed", - Id: "for_v3hsillmilld500m_sms_ill", + ID: "for_v3hsillmilld500m_sms_ill", Value: 20000, Weight: 10, - DestinationIds: utils.StringMap{ + DestinationIDs: utils.StringMap{ "FRANCE_NATIONAL": true, }, Categories: utils.StringMap{ @@ -1878,10 +1878,10 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { "*voice": BalanceChain{ &Balance{ Uuid: "079ab190-77f4-44f3-9c6f-3a0dd1a59dfd", - Id: "for_v3hsillmilld500m_voice_3_h", + ID: "for_v3hsillmilld500m_voice_3_h", Value: 10800, Weight: 10, - DestinationIds: utils.StringMap{ + DestinationIDs: utils.StringMap{ "FRANCE_NATIONAL": true, }, Categories: utils.StringMap{ @@ -1897,7 +1897,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { a1 := &Action{ ActionType: "*set_balance", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"ID\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*sms"), ID: utils.StringPointer("for_v3hsillmilld500m_sms_ill"), @@ -1907,7 +1907,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { } a2 := &Action{ ActionType: "*set_balance", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"ID\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*sms"), ID: utils.StringPointer("for_v3hsillmilld500m_mms_ill"), @@ -1919,7 +1919,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { } a3 := &Action{ ActionType: "*set_balance", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"ID\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*sms"), ID: utils.StringPointer("for_v3hsillmilld500m_sms_ill"), @@ -1931,7 +1931,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { } a4 := &Action{ ActionType: "*set_balance", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"ID\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*data"), Uuid: utils.StringPointer("fc927edb-1bd6-425e-a2a3-9fd8bafaa524"), @@ -1943,7 +1943,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { } a5 := &Action{ ActionType: "*set_balance", - Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"Id\":{\"*eq\":\"*default\"}}]}", + Filter: "{\"*and\":[{\"Value\":{\"*lt\":0}},{\"ID\":{\"*eq\":\"*default\"}}]}", Balance: &BalanceFilter{ Type: utils.StringPointer("*voice"), ID: utils.StringPointer("for_v3hsillmilld500m_voice_3_h"), @@ -1983,13 +1983,13 @@ func TestActionSetBalance(t *testing.T) { BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{ - Id: "m1", + ID: "m1", Uuid: utils.GenUUID(), Value: 1, Weight: 10, }, &Balance{ - Id: "m2", + ID: "m2", Uuid: utils.GenUUID(), Value: 6, Weight: 20, @@ -1997,13 +1997,13 @@ func TestActionSetBalance(t *testing.T) { }, utils.VOICE: BalanceChain{ &Balance{ - Id: "v1", + ID: "v1", Uuid: utils.GenUUID(), Value: 10, Weight: 10, }, &Balance{ - Id: "v2", + ID: "v2", Uuid: utils.GenUUID(), Value: 100, Weight: 20, diff --git a/engine/balance_filter.go b/engine/balance_filter.go index 1c8a214ba..c7010967e 100644 --- a/engine/balance_filter.go +++ b/engine/balance_filter.go @@ -29,12 +29,12 @@ type BalanceFilter struct { func (bp *BalanceFilter) CreateBalance() *Balance { b := &Balance{ Uuid: bp.GetUuid(), - Id: bp.GetID(), + ID: bp.GetID(), Value: bp.GetValue(), Directions: bp.GetDirections(), ExpirationDate: bp.GetExpirationDate(), Weight: bp.GetWeight(), - DestinationIds: bp.GetDestinationIDs(), + DestinationIDs: bp.GetDestinationIDs(), RatingSubject: bp.GetRatingSubject(), Categories: bp.GetCategories(), SharedGroups: bp.GetSharedGroups(), @@ -112,8 +112,8 @@ func (bp *BalanceFilter) LoadFromBalance(b *Balance) *BalanceFilter { if b.Uuid != "" { bp.Uuid = &b.Uuid } - if b.Id != "" { - bp.ID = &b.Id + if b.ID != "" { + bp.ID = &b.ID } if b.Value != 0 { bp.Value = &b.Value @@ -127,8 +127,8 @@ func (bp *BalanceFilter) LoadFromBalance(b *Balance) *BalanceFilter { if b.Weight != 0 { bp.Weight = &b.Weight } - if len(b.DestinationIds) != 0 { - bp.DestinationIDs = &b.DestinationIds + if len(b.DestinationIDs) != 0 { + bp.DestinationIDs = &b.DestinationIDs } if b.RatingSubject != "" { bp.RatingSubject = &b.RatingSubject @@ -300,7 +300,7 @@ func (bf *BalanceFilter) ModifyBalance(b *Balance) { b.Categories = *bf.Categories } if bf.DestinationIDs != nil { - b.DestinationIds = *bf.DestinationIDs + b.DestinationIDs = *bf.DestinationIDs } if bf.SharedGroups != nil { b.SharedGroups = *bf.SharedGroups diff --git a/engine/balances.go b/engine/balances.go index 633b17063..3b98b7b25 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -33,12 +33,12 @@ import ( // Can hold different units as seconds or monetary type Balance struct { Uuid string //system wide unique - Id string // account wide unique + ID string // account wide unique Value float64 Directions utils.StringMap ExpirationDate time.Time Weight float64 - DestinationIds utils.StringMap + DestinationIDs utils.StringMap RatingSubject string Categories utils.StringMap SharedGroups utils.StringMap @@ -53,17 +53,17 @@ type Balance struct { } func (b *Balance) Equal(o *Balance) bool { - if len(b.DestinationIds) == 0 { - b.DestinationIds = utils.StringMap{utils.ANY: true} + if len(b.DestinationIDs) == 0 { + b.DestinationIDs = utils.StringMap{utils.ANY: true} } - if len(o.DestinationIds) == 0 { - o.DestinationIds = utils.StringMap{utils.ANY: true} + if len(o.DestinationIDs) == 0 { + o.DestinationIDs = utils.StringMap{utils.ANY: true} } return b.Uuid == o.Uuid && - b.Id == o.Id && + b.ID == o.ID && b.ExpirationDate.Equal(o.ExpirationDate) && b.Weight == o.Weight && - b.DestinationIds.Equal(o.DestinationIds) && + b.DestinationIDs.Equal(o.DestinationIDs) && b.Directions.Equal(o.Directions) && b.RatingSubject == o.RatingSubject && b.Categories.Equal(o.Categories) && @@ -80,13 +80,13 @@ func (b *Balance) MatchFilter(o *BalanceFilter, skipIds bool) bool { return b.Uuid == *o.Uuid } if !skipIds && o.ID != nil && *o.ID != "" { - return b.Id == *o.ID + return b.ID == *o.ID } return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && (o.Weight == nil || b.Weight == *o.Weight) && (o.Blocker == nil || b.Blocker == *o.Blocker) && (o.Disabled == nil || b.Disabled == *o.Disabled) && - (o.DestinationIDs == nil || b.DestinationIds.Includes(*o.DestinationIDs)) && + (o.DestinationIDs == nil || b.DestinationIDs.Includes(*o.DestinationIDs)) && (o.Directions == nil || b.Directions.Includes(*o.Directions)) && (o.Categories == nil || b.Categories.Includes(*o.Categories)) && (o.TimingIDs == nil || b.TimingIDs.Includes(*o.TimingIDs)) && @@ -102,13 +102,13 @@ func (b *Balance) HardMatchFilter(o *BalanceFilter, skipIds bool) bool { return b.Uuid == *o.Uuid } if !skipIds && o.ID != nil && *o.ID != "" { - return b.Id == *o.ID + return b.ID == *o.ID } return (o.ExpirationDate == nil || b.ExpirationDate.Equal(*o.ExpirationDate)) && (o.Weight == nil || b.Weight == *o.Weight) && (o.Blocker == nil || b.Blocker == *o.Blocker) && (o.Disabled == nil || b.Disabled == *o.Disabled) && - (o.DestinationIDs == nil || b.DestinationIds.Equal(*o.DestinationIDs)) && + (o.DestinationIDs == nil || b.DestinationIDs.Equal(*o.DestinationIDs)) && (o.Directions == nil || b.Directions.Equal(*o.Directions)) && (o.Categories == nil || b.Categories.Equal(*o.Categories)) && (o.TimingIDs == nil || b.TimingIDs.Equal(*o.TimingIDs)) && @@ -118,7 +118,7 @@ func (b *Balance) HardMatchFilter(o *BalanceFilter, skipIds bool) bool { // the default balance has standard Id func (b *Balance) IsDefault() bool { - return b.Id == utils.META_DEFAULT + return b.ID == utils.META_DEFAULT } func (b *Balance) IsExpired() bool { @@ -150,15 +150,15 @@ func (b *Balance) MatchCategory(category string) bool { } func (b *Balance) HasDestination() bool { - return len(b.DestinationIds) > 0 && b.DestinationIds[utils.ANY] == false + return len(b.DestinationIDs) > 0 && b.DestinationIDs[utils.ANY] == false } func (b *Balance) HasDirection() bool { return len(b.Directions) > 0 } -func (b *Balance) MatchDestination(destinationId string) bool { - return !b.HasDestination() || b.DestinationIds[destinationId] == true +func (b *Balance) MatchDestination(destinationID string) bool { + return !b.HasDestination() || b.DestinationIDs[destinationID] == true } func (b *Balance) MatchActionTrigger(at *ActionTrigger) bool { @@ -171,7 +171,7 @@ func (b *Balance) Clone() *Balance { } n := &Balance{ Uuid: b.Uuid, - Id: b.Id, + ID: b.ID, Value: b.Value, // this value is in seconds ExpirationDate: b.ExpirationDate, Weight: b.Weight, @@ -184,8 +184,8 @@ func (b *Balance) Clone() *Balance { Disabled: b.Disabled, dirty: b.dirty, } - if b.DestinationIds != nil { - n.DestinationIds = b.DestinationIds.Clone() + if b.DestinationIDs != nil { + n.DestinationIDs = b.DestinationIDs.Clone() } if b.Directions != nil { n.Directions = b.Directions.Clone() @@ -193,14 +193,14 @@ func (b *Balance) Clone() *Balance { return n } -func (b *Balance) getMatchingPrefixAndDestId(dest string) (prefix, destId string) { - if len(b.DestinationIds) != 0 && b.DestinationIds[utils.ANY] == false { +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) { if x, err := cache2go.Get(utils.DESTINATION_PREFIX + p); err == nil { - destIds := x.(map[interface{}]struct{}) - for dId, _ := range destIds { - if b.DestinationIds[dId.(string)] == true { - return p, dId.(string) + destIDs := x.(map[interface{}]struct{}) + for dID := range destIDs { + if b.DestinationIDs[dID.(string)] == true { + return p, dID.(string) } } } @@ -331,7 +331,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala }, }, } - prefix, destid := b.getMatchingPrefixAndDestId(cd.Destination) + prefix, destid := b.getMatchingPrefixAndDestID(cd.Destination) if prefix == "" { prefix = cd.Destination } @@ -667,11 +667,11 @@ func (bc BalanceChain) SaveDirtyBalances(acc *Account) { Publish(CgrEvent{ "EventName": utils.EVT_ACCOUNT_BALANCE_MODIFIED, "Uuid": b.Uuid, - "Id": b.Id, + "Id": b.ID, "Value": strconv.FormatFloat(b.Value, 'f', -1, 64), "ExpirationDate": b.ExpirationDate.String(), "Weight": strconv.FormatFloat(b.Weight, 'f', -1, 64), - "DestinationIds": b.DestinationIds.String(), + "DestinationIDs": b.DestinationIDs.String(), "Directions": b.Directions.String(), "RatingSubject": b.RatingSubject, "Categories": b.Categories.String(), diff --git a/engine/balances_test.go b/engine/balances_test.go index ec95fad73..7c9069bc6 100644 --- a/engine/balances_test.go +++ b/engine/balances_test.go @@ -80,16 +80,16 @@ func TestBalanceSortWeightLess(t *testing.T) { } func TestBalanceEqual(t *testing.T) { - mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb2 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} - mb3 := &Balance{Weight: 1, precision: 1, RatingSubject: "2", DestinationIds: utils.StringMap{}} + mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIDs: utils.StringMap{}} + mb2 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIDs: utils.StringMap{}} + mb3 := &Balance{Weight: 1, precision: 1, RatingSubject: "2", DestinationIDs: utils.StringMap{}} if !mb1.Equal(mb2) || mb2.Equal(mb3) { t.Error("Equal failure!", mb1 == mb2, mb3) } } func TestBalanceMatchFilter(t *testing.T) { - mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} + mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIDs: utils.StringMap{}} mb2 := &BalanceFilter{Weight: utils.Float64Pointer(1), RatingSubject: nil, DestinationIDs: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) @@ -97,7 +97,7 @@ func TestBalanceMatchFilter(t *testing.T) { } func TestBalanceMatchFilterEmpty(t *testing.T) { - mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} + mb1 := &Balance{Weight: 1, precision: 1, RatingSubject: "1", DestinationIDs: utils.StringMap{}} mb2 := &BalanceFilter{} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) @@ -105,7 +105,7 @@ func TestBalanceMatchFilterEmpty(t *testing.T) { } func TestBalanceMatchFilterId(t *testing.T) { - mb1 := &Balance{Id: "T1", Weight: 2, precision: 2, RatingSubject: "2", DestinationIds: utils.NewStringMap("NAT")} + mb1 := &Balance{ID: "T1", Weight: 2, precision: 2, RatingSubject: "2", DestinationIDs: utils.NewStringMap("NAT")} mb2 := &BalanceFilter{ID: utils.StringPointer("T1"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIDs: nil} if !mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v == %+v", mb1, mb2) @@ -113,7 +113,7 @@ func TestBalanceMatchFilterId(t *testing.T) { } func TestBalanceMatchFilterDiffId(t *testing.T) { - mb1 := &Balance{Id: "T1", Weight: 1, precision: 1, RatingSubject: "1", DestinationIds: utils.StringMap{}} + mb1 := &Balance{ID: "T1", Weight: 1, precision: 1, RatingSubject: "1", DestinationIDs: utils.StringMap{}} mb2 := &BalanceFilter{ID: utils.StringPointer("T2"), Weight: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("1"), DestinationIDs: nil} if mb1.MatchFilter(mb2, false) { t.Errorf("Match filter failure: %+v != %+v", mb1, mb2) @@ -121,7 +121,7 @@ func TestBalanceMatchFilterDiffId(t *testing.T) { } func TestBalanceClone(t *testing.T) { - mb1 := &Balance{Value: 1, Weight: 2, RatingSubject: "test", DestinationIds: utils.NewStringMap("5")} + mb1 := &Balance{Value: 1, Weight: 2, RatingSubject: "test", DestinationIDs: utils.NewStringMap("5")} mb2 := mb1.Clone() if mb1 == mb2 || !mb1.Equal(mb2) { t.Errorf("Cloning failure: \n%+v\n%+v", mb1, mb2) @@ -130,19 +130,19 @@ func TestBalanceClone(t *testing.T) { func TestBalanceMatchActionTriggerId(t *testing.T) { at := &ActionTrigger{Balance: &BalanceFilter{ID: utils.StringPointer("test")}} - b := &Balance{Id: "test"} + b := &Balance{ID: "test"} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } - b.Id = "test1" + b.ID = "test1" if b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } - b.Id = "" + b.ID = "" if b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } - b.Id = "test" + b.ID = "test" at.Balance.ID = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -151,19 +151,19 @@ func TestBalanceMatchActionTriggerId(t *testing.T) { func TestBalanceMatchActionTriggerDestination(t *testing.T) { at := &ActionTrigger{Balance: &BalanceFilter{DestinationIDs: utils.StringMapPointer(utils.NewStringMap("test"))}} - b := &Balance{DestinationIds: utils.NewStringMap("test")} + b := &Balance{DestinationIDs: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } - b.DestinationIds = utils.NewStringMap("test1") + b.DestinationIDs = utils.NewStringMap("test1") if b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } - b.DestinationIds = utils.NewStringMap("") + b.DestinationIDs = utils.NewStringMap("") if b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } - b.DestinationIds = utils.NewStringMap("test") + b.DestinationIDs = utils.NewStringMap("test") at.Balance.DestinationIDs = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) @@ -238,7 +238,7 @@ func TestBalanceIsDefault(t *testing.T) { if b.IsDefault() { t.Errorf("Balance should not be default: %+v", b) } - b = &Balance{Id: utils.META_DEFAULT} + b = &Balance{ID: utils.META_DEFAULT} if !b.IsDefault() { t.Errorf("Balance should be default: %+v", b) } diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 852b91a79..288e9b222 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -55,16 +55,16 @@ func populateDB() { BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 50}}, utils.VOICE: BalanceChain{ - &Balance{Value: 200, DestinationIds: utils.NewStringMap("NAT"), Weight: 10}, - &Balance{Value: 100, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + &Balance{Value: 200, DestinationIDs: utils.NewStringMap("NAT"), Weight: 10}, + &Balance{Value: 100, DestinationIDs: utils.NewStringMap("RET"), Weight: 20}, }}, } broker := &Account{ Id: "vdf:broker", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{ - &Balance{Value: 20, DestinationIds: utils.NewStringMap("NAT"), Weight: 10, RatingSubject: "rif"}, - &Balance{Value: 100, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + &Balance{Value: 20, DestinationIDs: utils.NewStringMap("NAT"), Weight: 10, RatingSubject: "rif"}, + &Balance{Value: 100, DestinationIDs: utils.NewStringMap("RET"), Weight: 20}, }}, } luna := &Account{ @@ -79,8 +79,8 @@ func populateDB() { Id: "vdf:minitsboy", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{ - &Balance{Value: 20, DestinationIds: utils.NewStringMap("NAT"), Weight: 10, RatingSubject: "rif"}, - &Balance{Value: 100, DestinationIds: utils.NewStringMap("RET"), Weight: 20}, + &Balance{Value: 20, DestinationIDs: utils.NewStringMap("NAT"), Weight: 10, RatingSubject: "rif"}, + &Balance{Value: 100, DestinationIDs: utils.NewStringMap("RET"), Weight: 20}, }, utils.MONETARY: BalanceChain{ &Balance{Value: 100, Weight: 10}, diff --git a/engine/responder_test.go b/engine/responder_test.go index fc54bd46f..e3f0d7ae4 100644 --- a/engine/responder_test.go +++ b/engine/responder_test.go @@ -71,8 +71,8 @@ func TestResponderGetDerivedMaxSessionTime(t *testing.T) { if err := ratingStorage.SetDestination(deTMobile); err != nil { t.Error(err) } - b10 := &Balance{Value: 10, Weight: 10, DestinationIds: utils.NewStringMap("DE_TMOBILE")} - b20 := &Balance{Value: 20, Weight: 10, DestinationIds: utils.NewStringMap("DE_TMOBILE")} + b10 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.NewStringMap("DE_TMOBILE")} + b20 := &Balance{Value: 20, Weight: 10, DestinationIDs: utils.NewStringMap("DE_TMOBILE")} rifsAccount := &Account{Id: utils.ConcatenatedKey(testTenant, "rif"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b10}}} dansAccount := &Account{Id: utils.ConcatenatedKey(testTenant, "dan"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b20}}} if err := accountingStorage.SetAccount(rifsAccount); err != nil { @@ -435,8 +435,8 @@ func TestResponderGetLCR(t *testing.T) { } else if !reflect.DeepEqual(eLcLcr.SupplierCosts, lcrLc.SupplierCosts) { t.Errorf("Expecting: %+v, received: %+v", eLcLcr.SupplierCosts, lcrLc.SupplierCosts) } - bRif12 := &Balance{Value: 40, Weight: 10, DestinationIds: utils.NewStringMap(dstDe.Id)} - bIvo12 := &Balance{Value: 60, Weight: 10, DestinationIds: utils.NewStringMap(dstDe.Id)} + bRif12 := &Balance{Value: 40, Weight: 10, DestinationIDs: utils.NewStringMap(dstDe.Id)} + bIvo12 := &Balance{Value: 60, Weight: 10, DestinationIDs: utils.NewStringMap(dstDe.Id)} rif12sAccount := &Account{Id: utils.ConcatenatedKey("tenant12", "rif12"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{bRif12}}, AllowNegative: true} ivo12sAccount := &Account{Id: utils.ConcatenatedKey("tenant12", "ivo12"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{bIvo12}}, AllowNegative: true} for _, acnt := range []*Account{rif12sAccount, ivo12sAccount} { diff --git a/engine/storage_test.go b/engine/storage_test.go index ee8ed42cd..bdb245c3e 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -329,7 +329,7 @@ func GetUB() *Account { ub := &Account{ Id: "rif", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, utils.DATA: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIds: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIds: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, utils.DATA: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.SMS: []*UnitCounter{uc, uc}}, ActionTriggers: ActionTriggers{at, at, at}, } From c9badd722f87a4640047b1e4cc2dc85af123dbfd Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 23 Feb 2016 21:02:23 +0200 Subject: [PATCH 108/199] renamed Account Id to ID --- engine/account.go | 16 +++--- engine/account_test.go | 94 +++++++++++++++++----------------- engine/action.go | 10 ++-- engine/action_trigger.go | 4 +- engine/actions_test.go | 68 ++++++++++++------------ engine/balances.go | 16 +++--- engine/calldesc.go | 2 +- engine/calldesc_test.go | 12 ++--- engine/loader_csv_test.go | 6 +-- engine/responder_test.go | 8 +-- engine/sharedgroup.go | 4 +- engine/sharedgroup_test.go | 6 +-- engine/storage_map.go | 6 +-- engine/storage_mongo_datadb.go | 4 +- engine/storage_redis.go | 6 +-- engine/storage_test.go | 2 +- engine/tp_reader.go | 6 +-- 17 files changed, 135 insertions(+), 135 deletions(-) diff --git a/engine/account.go b/engine/account.go index b833ae866..40521dfd9 100644 --- a/engine/account.go +++ b/engine/account.go @@ -36,7 +36,7 @@ Structure containing information about user's credit (minutes, cents, sms...).' This can represent a user or a shared group. */ type Account struct { - Id string + ID string BalanceMap map[string]BalanceChain UnitCounters UnitCounters ActionTriggers ActionTriggers @@ -142,12 +142,12 @@ func (acc *Account) setBalanceAction(a *Action) error { //than is problem utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgID)) } else { - if _, found := sg.MemberIds[acc.Id]; !found { + if _, found := sg.MemberIds[acc.ID]; !found { // add member and save if sg.MemberIds == nil { sg.MemberIds = make(utils.StringMap) } - sg.MemberIds[acc.Id] = true + sg.MemberIds[acc.ID] = true ratingStorage.SetSharedGroup(sg) } } @@ -228,12 +228,12 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { //than is problem utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) } else { - if _, found := sg.MemberIds[ub.Id]; !found { + if _, found := sg.MemberIds[ub.ID]; !found { // add member and save if sg.MemberIds == nil { sg.MemberIds = make(utils.StringMap) } - sg.MemberIds[ub.Id] = true + sg.MemberIds[ub.ID] = true ratingStorage.SetSharedGroup(sg) } } @@ -487,7 +487,7 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo defaultBalance := ub.GetDefaultMoneyBalance() defaultBalance.SubstractValue(cost) increment.BalanceInfo.MoneyBalanceUuid = defaultBalance.Uuid - increment.BalanceInfo.AccountId = ub.Id + increment.BalanceInfo.AccountId = ub.ID increment.paid = true if count { ub.countUnits( @@ -787,7 +787,7 @@ type TenantAccount struct { func (acc *Account) Clone() *Account { newAcc := &Account{ - Id: acc.Id, + ID: acc.ID, BalanceMap: make(map[string]BalanceChain, len(acc.BalanceMap)), UnitCounters: nil, // not used when cloned (dryRun) ActionTriggers: nil, // not used when cloned (dryRun) @@ -914,7 +914,7 @@ func (acc *Account) AsOldStructure() interface{} { } result := &Account{ - Id: utils.OUT + ":" + acc.Id, + Id: utils.OUT + ":" + acc.ID, BalanceMap: make(map[string]BalanceChain, len(acc.BalanceMap)), UnitCounters: make([]*UnitsCounter, len(acc.UnitCounters)), ActionTriggers: make(ActionTriggers, len(acc.ActionTriggers)), diff --git a/engine/account_test.go b/engine/account_test.go index dc454c15d..5ea6d4e2a 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -85,7 +85,7 @@ func TestBalanceChainStoreRestore(t *testing.T) { func TestAccountStorageStoreRestore(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} accountingStorage.SetAccount(rifsBalance) ub1, err := accountingStorage.GetAccount("other") if err != nil || !ub1.BalanceMap[utils.MONETARY].Equal(rifsBalance.BalanceMap[utils.MONETARY]) { @@ -97,7 +97,7 @@ func TestAccountStorageStoreRestore(t *testing.T) { func TestGetSecondsForPrefix(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - ub1 := &Account{Id: "CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 200}}}} + ub1 := &Account{ID: "CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 200}}}} cd := &CallDescriptor{ Category: "0", Tenant: "vdf", @@ -122,7 +122,7 @@ func TestGetSpecialPricedSeconds(t *testing.T) { b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}, RatingSubject: "minu"} ub1 := &Account{ - Id: "OUT:CUSTOMER_1:rif", + ID: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, @@ -152,10 +152,10 @@ func TestGetSpecialPricedSeconds(t *testing.T) { func TestAccountStorageStore(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} accountingStorage.SetAccount(rifsBalance) - result, err := accountingStorage.GetAccount(rifsBalance.Id) - if err != nil || rifsBalance.Id != result.Id || + result, err := accountingStorage.GetAccount(rifsBalance.ID) + if err != nil || rifsBalance.ID != result.ID || len(rifsBalance.BalanceMap[utils.VOICE]) < 2 || len(result.BalanceMap[utils.VOICE]) < 2 || !(rifsBalance.BalanceMap[utils.VOICE][0].Equal(result.BalanceMap[utils.VOICE][0])) || !(rifsBalance.BalanceMap[utils.VOICE][1].Equal(result.BalanceMap[utils.VOICE][1])) || @@ -188,7 +188,7 @@ func TestDebitCreditZeroSecond(t *testing.T) { TOR: utils.VOICE, testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1}, utils.MONETARY: BalanceChain{&Balance{Categories: utils.NewStringMap("0"), Value: 21}}}} + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1}, utils.MONETARY: BalanceChain{&Balance{Categories: utils.NewStringMap("0"), Value: 21}}}} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) if err != nil { @@ -228,7 +228,7 @@ func TestDebitCreditZeroMinute(t *testing.T) { TOR: utils.VOICE, testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{b1}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} @@ -275,7 +275,7 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) { DurationIndex: cc.Timespans[0].GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} @@ -327,7 +327,7 @@ func TestDebitCreditNoCredit(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{b1}, }} var err error @@ -378,7 +378,7 @@ func TestDebitCreditHasCredit(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{b1}, utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 110}}, }} @@ -426,7 +426,7 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{b1}, utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 50}}, }} @@ -479,7 +479,7 @@ func TestDebitCreditMoreTimespans(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{b1}, }} var err error @@ -528,7 +528,7 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{b1, b2}, }} var err error @@ -578,7 +578,7 @@ func TestDebitCreditNoConectFeeCredit(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{b1}, }} var err error @@ -622,7 +622,7 @@ func TestDebitCreditMoneyOnly(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Uuid: "money", Value: 50}}, }} var err error @@ -675,7 +675,7 @@ func TestDebitCreditSubjectMinutes(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{b1}, utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 350}}, }} @@ -730,7 +730,7 @@ func TestDebitCreditSubjectMoney(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 75, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "minu"}}, }} var err error @@ -780,7 +780,7 @@ func TestDebitCreditSubjectMoney(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{b1}, utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 19500, RatingSubject: "minu"}}, }} @@ -809,7 +809,7 @@ func TestDebitCreditSubjectMoney(t *testing.T) { func TestAccountdebitBalance(t *testing.T) { ub := &Account{ - Id: "rif", + ID: "rif", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } @@ -851,7 +851,7 @@ func TestAccountdebitBalance(t *testing.T) { func TestAccountdebitBalanceExists(t *testing.T) { ub := &Account{ - Id: "rif", + ID: "rif", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Value: 15, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } @@ -871,7 +871,7 @@ func TestAccountdebitBalanceExists(t *testing.T) { func TestAccountAddMinuteNil(t *testing.T) { ub := &Account{ - Id: "rif", + ID: "rif", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } @@ -920,7 +920,7 @@ func TestAccountAddMinutBucketEmpty(t *testing.T) { func TestAccountExecuteTriggeredActions(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.StringMap{utils.OUT: true})}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, @@ -944,7 +944,7 @@ func TestAccountExecuteTriggeredActions(t *testing.T) { func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Filter: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, Value: 1.0}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, @@ -957,7 +957,7 @@ func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { ub := &Account{ - Id: "TEST_UB_OREDER", + ID: "TEST_UB_OREDER", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Type: utils.StringPointer(utils.MONETARY)}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, @@ -972,7 +972,7 @@ func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { func TestAccountExecuteTriggeredDayWeek(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ &ActionTrigger{UniqueID: "day_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, @@ -1000,7 +1000,7 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { func TestAccountExpActionTrigger(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100, ExpirationDate: time.Date(2015, time.November, 9, 9, 48, 0, 0, time.UTC)}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ &ActionTrigger{ID: "check expired balances", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, @@ -1018,7 +1018,7 @@ func TestAccountExpActionTrigger(t *testing.T) { func TestAccountExpActionTriggerNotActivated(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, @@ -1036,7 +1036,7 @@ func TestAccountExpActionTriggerNotActivated(t *testing.T) { func TestAccountExpActionTriggerExpired(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, @@ -1054,7 +1054,7 @@ func TestAccountExpActionTriggerExpired(t *testing.T) { func TestCleanExpired(t *testing.T) { ub := &Account{ - Id: "TEST_UB_OREDER", + ID: "TEST_UB_OREDER", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{ &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}, &Balance{ExpirationDate: time.Date(2013, 7, 18, 14, 33, 0, 0, time.UTC)}, @@ -1181,14 +1181,14 @@ func TestDebitShared(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rif := &Account{Id: "rif", BalanceMap: map[string]BalanceChain{ + rif := &Account{ID: "rif", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 0, SharedGroups: utils.NewStringMap("SG_TEST")}}, }} - groupie := &Account{Id: "groupie", BalanceMap: map[string]BalanceChain{ + groupie := &Account{ID: "groupie", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Uuid: "moneyc", Value: 130, SharedGroups: utils.NewStringMap("SG_TEST")}}, }} - sg := &SharedGroup{Id: "SG_TEST", MemberIds: utils.NewStringMap(rif.Id, groupie.Id), AccountParameters: map[string]*SharingParameters{"*any": &SharingParameters{Strategy: STRATEGY_MINE_RANDOM}}} + sg := &SharedGroup{Id: "SG_TEST", MemberIds: utils.NewStringMap(rif.ID, groupie.ID), AccountParameters: map[string]*SharingParameters{"*any": &SharingParameters{Strategy: STRATEGY_MINE_RANDOM}}} accountingStorage.SetAccount(groupie) ratingStorage.SetSharedGroup(sg) @@ -1251,14 +1251,14 @@ func TestMaxDurationShared(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rif := &Account{Id: "rif", BalanceMap: map[string]BalanceChain{ + rif := &Account{ID: "rif", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 0, SharedGroups: utils.NewStringMap("SG_TEST")}}, }} - groupie := &Account{Id: "groupie", BalanceMap: map[string]BalanceChain{ + groupie := &Account{ID: "groupie", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Uuid: "moneyc", Value: 130, SharedGroups: utils.NewStringMap("SG_TEST")}}, }} - sg := &SharedGroup{Id: "SG_TEST", MemberIds: utils.NewStringMap(rif.Id, groupie.Id), AccountParameters: map[string]*SharingParameters{"*any": &SharingParameters{Strategy: STRATEGY_MINE_RANDOM}}} + sg := &SharedGroup{Id: "SG_TEST", MemberIds: utils.NewStringMap(rif.ID, groupie.ID), AccountParameters: map[string]*SharingParameters{"*any": &SharingParameters{Strategy: STRATEGY_MINE_RANDOM}}} accountingStorage.SetAccount(groupie) ratingStorage.SetSharedGroup(sg) @@ -1286,7 +1286,7 @@ func TestMaxDurationConnectFeeOnly(t *testing.T) { TOR: utils.VOICE, DurationIndex: 600, } - rif := &Account{Id: "rif", BalanceMap: map[string]BalanceChain{ + rif := &Account{ID: "rif", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 0.2}}, }} @@ -1324,7 +1324,7 @@ func TestDebitSMS(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.SMS: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} @@ -1367,7 +1367,7 @@ func TestDebitGeneric(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} @@ -1410,7 +1410,7 @@ func TestDebitGenericBalance(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}}}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} @@ -1453,7 +1453,7 @@ func TestDebitGenericBalanceWithRatingSubject(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}, RatingSubject: "free"}}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} @@ -1503,7 +1503,7 @@ func TestDebitDataUnits(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.DATA: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}, }} @@ -1557,7 +1557,7 @@ func TestDebitDataMoney(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{ + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ utils.DATA: BalanceChain{&Balance{Uuid: "testm", Value: 0, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, utils.MONETARY: BalanceChain{&Balance{Value: 160}}, }} @@ -1755,7 +1755,7 @@ func BenchmarkGetSecondForPrefix(b *testing.B) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - ub1 := &Account{Id: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} + ub1 := &Account{ID: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} cd := &CallDescriptor{ Destination: "0723", } @@ -1768,17 +1768,17 @@ func BenchmarkGetSecondForPrefix(b *testing.B) { func BenchmarkAccountStorageStoreRestore(b *testing.B) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - rifsBalance := &Account{Id: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} for i := 0; i < b.N; i++ { accountingStorage.SetAccount(rifsBalance) - accountingStorage.GetAccount(rifsBalance.Id) + accountingStorage.GetAccount(rifsBalance.ID) } } func BenchmarkGetSecondsForPrefix(b *testing.B) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - ub1 := &Account{Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} + ub1 := &Account{ID: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} cd := &CallDescriptor{ Destination: "0723", } diff --git a/engine/action.go b/engine/action.go index fc300f37f..26e20a5fa 100644 --- a/engine/action.go +++ b/engine/action.go @@ -161,7 +161,7 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) var err error var dta *utils.TenantAccount if acnt != nil { - dta, err = utils.NewTAFromAccountKey(acnt.Id) // Account information should be valid + dta, err = utils.NewTAFromAccountKey(acnt.ID) // Account information should be valid } if err != nil || acnt == nil { dta = new(utils.TenantAccount) // Init with empty values @@ -171,7 +171,7 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) for _, rsrFld := range rsrFlds { switch rsrFld.Id { case "AccountID": - parsedValue += rsrFld.ParseValue(acnt.Id) + parsedValue += rsrFld.ParseValue(acnt.ID) case "Directions": parsedValue += rsrFld.ParseValue(b.Directions.String()) case utils.TENANT: @@ -469,7 +469,7 @@ func mailAsync(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) err if err != nil { return err } - message = []byte(fmt.Sprintf("To: %s\r\nSubject: [CGR Notification] Threshold hit on Balance: %s\r\n\r\nTime: \r\n\t%s\r\n\r\nBalance:\r\n\t%s\r\n\r\nYours faithfully,\r\nCGR Balance Monitor\r\n", toAddrStr, ub.Id, time.Now(), balJsn)) + message = []byte(fmt.Sprintf("To: %s\r\nSubject: [CGR Notification] Threshold hit on Balance: %s\r\n\r\nTime: \r\n\t%s\r\n\r\nBalance:\r\n\t%s\r\n\r\nYours faithfully,\r\nCGR Balance Monitor\r\n", toAddrStr, ub.ID, time.Now(), balJsn)) } else if sq != nil { message = []byte(fmt.Sprintf("To: %s\r\nSubject: [CGR Notification] Threshold hit on StatsQueueId: %s\r\n\r\nTime: \r\n\t%s\r\n\r\nStatsQueueId:\r\n\t%s\r\n\r\nMetrics:\r\n\t%+v\r\n\r\nTrigger:\r\n\t%+v\r\n\r\nYours faithfully,\r\nCGR CDR Stats Monitor\r\n", toAddrStr, sq.Id, time.Now(), sq.Id, sq.Metrics, sq.Trigger)) @@ -481,7 +481,7 @@ func mailAsync(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) err break } else if i == 4 { if ub != nil { - utils.Logger.Warning(fmt.Sprintf(" WARNING: Failed emailing, params: [%s], error: [%s], BalanceId: %s", a.ExtraParameters, err.Error(), ub.Id)) + utils.Logger.Warning(fmt.Sprintf(" WARNING: Failed emailing, params: [%s], error: [%s], BalanceId: %s", a.ExtraParameters, err.Error(), ub.ID)) } else if sq != nil { utils.Logger.Warning(fmt.Sprintf(" WARNING: Failed emailing, params: [%s], error: [%s], StatsQueueTriggeredId: %s", a.ExtraParameters, err.Error(), sq.Id)) } @@ -534,7 +534,7 @@ func setddestinations(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actio func removeAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actions) error { var accID string if ub != nil { - accID = ub.Id + accID = ub.ID } else { accountInfo := struct { Tenant string diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 0ab88a7bd..cca4336c1 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -53,7 +53,7 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro } at.lastExecutionTime = time.Now() if ub != nil && ub.Disabled { - return fmt.Errorf("User %s is disabled and there are triggers in action!", ub.Id) + return fmt.Errorf("User %s is disabled and there are triggers in action!", ub.ID) } // does NOT need to Lock() because it is triggered from a method that took the Lock var aac Actions @@ -104,7 +104,7 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro at.Executed = false } if !transactionFailed && ub != nil && !removeAccountActionFound { - storageLogger.LogActionTrigger(ub.Id, utils.RATER_SOURCE, at, aac) + storageLogger.LogActionTrigger(ub.ID, utils.RATER_SOURCE, at, aac) accountingStorage.SetAccount(ub) } return diff --git a/engine/actions_test.go b/engine/actions_test.go index 6278b54e8..64579af8f 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -692,7 +692,7 @@ func TestActionTriggers(t *testing.T) { func TestActionResetTriggres(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, @@ -705,7 +705,7 @@ func TestActionResetTriggres(t *testing.T) { func TestActionResetTriggresExecutesThem(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, @@ -718,7 +718,7 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { func TestActionResetTriggresActionFilter(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, @@ -731,7 +731,7 @@ func TestActionResetTriggresActionFilter(t *testing.T) { func TestActionSetPostpaid(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, @@ -744,7 +744,7 @@ func TestActionSetPostpaid(t *testing.T) { func TestActionSetPrepaid(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, @@ -758,7 +758,7 @@ func TestActionSetPrepaid(t *testing.T) { func TestActionResetPrepaid(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, @@ -777,7 +777,7 @@ func TestActionResetPrepaid(t *testing.T) { func TestActionResetPostpaid(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, @@ -793,7 +793,7 @@ func TestActionResetPostpaid(t *testing.T) { func TestActionTopupResetCredit(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, @@ -811,7 +811,7 @@ func TestActionTopupResetCredit(t *testing.T) { func TestActionTopupValueFactor(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{}, } a := &Action{ @@ -830,7 +830,7 @@ func TestActionTopupValueFactor(t *testing.T) { func TestActionTopupResetCreditId(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{Value: 100}, @@ -849,7 +849,7 @@ func TestActionTopupResetCreditId(t *testing.T) { func TestActionTopupResetCreditNoId(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{Value: 100, Directions: utils.NewStringMap(utils.OUT)}, @@ -868,7 +868,7 @@ func TestActionTopupResetCreditNoId(t *testing.T) { func TestActionTopupResetMinutes(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, @@ -889,7 +889,7 @@ func TestActionTopupResetMinutes(t *testing.T) { func TestActionTopupCredit(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, @@ -907,7 +907,7 @@ func TestActionTopupCredit(t *testing.T) { func TestActionTopupMinutes(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, @@ -926,7 +926,7 @@ func TestActionTopupMinutes(t *testing.T) { func TestActionDebitCredit(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, @@ -944,7 +944,7 @@ func TestActionDebitCredit(t *testing.T) { func TestActionDebitMinutes(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, @@ -963,7 +963,7 @@ func TestActionDebitMinutes(t *testing.T) { func TestActionResetAllCounters(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", AllowNegative: true, BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, @@ -994,7 +994,7 @@ func TestActionResetAllCounters(t *testing.T) { func TestActionResetCounterOnlyDefault(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", AllowNegative: true, BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 100}}, @@ -1026,7 +1026,7 @@ func TestActionResetCounterOnlyDefault(t *testing.T) { func TestActionResetCounterCredit(t *testing.T) { ub := &Account{ - Id: "TEST_UB", + ID: "TEST_UB", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}, utils.SMS: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, @@ -1193,7 +1193,7 @@ func TestTopupActionLoaded(t *testing.T) { } func TestActionCdrlogEmpty(t *testing.T) { - acnt := &Account{Id: "cgrates.org:dan2904"} + acnt := &Account{ID: "cgrates.org:dan2904"} cdrlog := &Action{ ActionType: CDRLOG, } @@ -1214,7 +1214,7 @@ func TestActionCdrlogEmpty(t *testing.T) { } func TestActionCdrlogWithParams(t *testing.T) { - acnt := &Account{Id: "cgrates.org:dan2904"} + acnt := &Account{ID: "cgrates.org:dan2904"} cdrlog := &Action{ ActionType: CDRLOG, ExtraParameters: `{"ReqType":"^*pseudoprepaid","Subject":"^rif", "TOR":"~action_type:s/^\\*(.*)$/did_$1/"}`, @@ -1241,7 +1241,7 @@ func TestActionCdrlogWithParams(t *testing.T) { } func TestActionCdrLogParamsWithOverload(t *testing.T) { - acnt := &Account{Id: "cgrates.org:dan2904"} + acnt := &Account{ID: "cgrates.org:dan2904"} cdrlog := &Action{ ActionType: CDRLOG, ExtraParameters: `{"Subject":"^rif","Destination":"^1234","ToR":"~ActionTag:s/^at(.)$/0$1/","AccountID":"~AccountID:s/^\\*(.*)$/$1/"}`, @@ -1318,7 +1318,7 @@ func TestActionSetDDestination(t *testing.T) { func TestActionTransactionFuncType(t *testing.T) { err := accountingStorage.SetAccount(&Account{ - Id: "cgrates.org:trans", + ID: "cgrates.org:trans", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{ Value: 10, @@ -1354,7 +1354,7 @@ func TestActionTransactionFuncType(t *testing.T) { func TestActionTransactionBalanceType(t *testing.T) { err := accountingStorage.SetAccount(&Account{ - Id: "cgrates.org:trans", + ID: "cgrates.org:trans", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{ Value: 10, @@ -1390,7 +1390,7 @@ func TestActionTransactionBalanceType(t *testing.T) { func TestActionTransactionBalanceNotType(t *testing.T) { err := accountingStorage.SetAccount(&Account{ - Id: "cgrates.org:trans", + ID: "cgrates.org:trans", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{ Value: 10, @@ -1426,7 +1426,7 @@ func TestActionTransactionBalanceNotType(t *testing.T) { func TestActionWithExpireWithoutExpire(t *testing.T) { err := accountingStorage.SetAccount(&Account{ - Id: "cgrates.org:exp", + ID: "cgrates.org:exp", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{ Value: 10, @@ -1470,7 +1470,7 @@ func TestActionWithExpireWithoutExpire(t *testing.T) { func TestActionRemoveBalance(t *testing.T) { err := accountingStorage.SetAccount(&Account{ - Id: "cgrates.org:rembal", + ID: "cgrates.org:rembal", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{ @@ -1518,7 +1518,7 @@ func TestActionRemoveBalance(t *testing.T) { func TestActionTransferMonetaryDefault(t *testing.T) { err := accountingStorage.SetAccount( &Account{ - Id: "cgrates.org:trans", + ID: "cgrates.org:trans", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{ @@ -1574,7 +1574,7 @@ func TestActionTransferMonetaryDefault(t *testing.T) { func TestActionTransferMonetaryDefaultFilter(t *testing.T) { err := accountingStorage.SetAccount( &Account{ - Id: "cgrates.org:trans", + ID: "cgrates.org:trans", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{ @@ -1635,7 +1635,7 @@ func TestActionTransferMonetaryDefaultFilter(t *testing.T) { func TestActionConditionalTopup(t *testing.T) { err := accountingStorage.SetAccount( &Account{ - Id: "cgrates.org:cond", + ID: "cgrates.org:cond", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{ @@ -1699,7 +1699,7 @@ func TestActionConditionalTopup(t *testing.T) { func TestActionConditionalTopupNoMatch(t *testing.T) { err := accountingStorage.SetAccount( &Account{ - Id: "cgrates.org:cond", + ID: "cgrates.org:cond", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{ @@ -1762,7 +1762,7 @@ func TestActionConditionalTopupNoMatch(t *testing.T) { func TestActionConditionalTopupExistingBalance(t *testing.T) { err := accountingStorage.SetAccount( &Account{ - Id: "cgrates.org:cond", + ID: "cgrates.org:cond", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{ @@ -1826,7 +1826,7 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) { func TestActionConditionalDisabledIfNegative(t *testing.T) { err := accountingStorage.SetAccount( &Account{ - Id: "cgrates.org:af", + ID: "cgrates.org:af", BalanceMap: map[string]BalanceChain{ "*data": BalanceChain{ &Balance{ @@ -1979,7 +1979,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { func TestActionSetBalance(t *testing.T) { err := accountingStorage.SetAccount( &Account{ - Id: "cgrates.org:setb", + ID: "cgrates.org:setb", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{ diff --git a/engine/balances.go b/engine/balances.go index 3b98b7b25..43b5cb207 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -356,7 +356,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala if b.GetValue() >= amount { b.SubstractValue(amount) inc.BalanceInfo.UnitBalanceUuid = b.Uuid - inc.BalanceInfo.AccountId = ub.Id + inc.BalanceInfo.AccountId = ub.ID inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR} inc.Cost = 0 inc.paid = true @@ -427,7 +427,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala if strategy == utils.MAX_COST_FREE && cd.MaxCostSoFar >= maxCost { cost, inc.Cost = 0.0, 0.0 inc.BalanceInfo.MoneyBalanceUuid = b.Uuid - inc.BalanceInfo.AccountId = ub.Id + inc.BalanceInfo.AccountId = ub.ID inc.paid = true if count { ub.countUnits(cost, utils.MONETARY, cc, b) @@ -445,7 +445,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala if (cost == 0 || moneyBal != nil) && b.GetValue() >= amount { b.SubstractValue(amount) inc.BalanceInfo.UnitBalanceUuid = b.Uuid - inc.BalanceInfo.AccountId = ub.Id + inc.BalanceInfo.AccountId = ub.ID inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR} if cost != 0 { inc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid @@ -534,7 +534,7 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala if strategy == utils.MAX_COST_FREE && cd.MaxCostSoFar >= maxCost { amount, inc.Cost = 0.0, 0.0 inc.BalanceInfo.MoneyBalanceUuid = b.Uuid - inc.BalanceInfo.AccountId = ub.Id + inc.BalanceInfo.AccountId = ub.ID inc.paid = true if count { ub.countUnits(amount, utils.MONETARY, cc, b) @@ -549,7 +549,7 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala b.SubstractValue(amount) cd.MaxCostSoFar += amount inc.BalanceInfo.MoneyBalanceUuid = b.Uuid - inc.BalanceInfo.AccountId = ub.Id + inc.BalanceInfo.AccountId = ub.ID inc.paid = true if count { ub.countUnits(amount, utils.MONETARY, cc, b) @@ -661,7 +661,7 @@ func (bc BalanceChain) SaveDirtyBalances(acc *Account) { disabled := "" if b.account != nil { // only publish modifications for balances with account set //utils.LogStack() - accountId = b.account.Id + accountId = b.account.ID allowNegative = strconv.FormatBool(b.account.AllowNegative) disabled = strconv.FormatBool(b.account.Disabled) Publish(CgrEvent{ @@ -683,9 +683,9 @@ func (bc BalanceChain) SaveDirtyBalances(acc *Account) { }) } } - if b.account != nil && b.account != acc && b.dirty && savedAccounts[b.account.Id] == false { + if b.account != nil && b.account != acc && b.dirty && savedAccounts[b.account.ID] == false { accountingStorage.SetAccount(b.account) - savedAccounts[b.account.Id] = true + savedAccounts[b.account.ID] = true } } } diff --git a/engine/calldesc.go b/engine/calldesc.go index 2ead17ea5..2d55812bb 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -180,7 +180,7 @@ func (cd *CallDescriptor) getAccount() (ub *Account, err error) { cd.account, err = accountingStorage.GetAccount(cd.GetAccountKey()) } if cd.account != nil && cd.account.Disabled { - return nil, fmt.Errorf("User %s is disabled", cd.account.Id) + return nil, fmt.Errorf("User %s is disabled", cd.account.ID) } return cd.account, err } diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 288e9b222..9f174dc4f 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -51,7 +51,7 @@ func populateDB() { } minu := &Account{ - Id: "vdf:minu", + ID: "vdf:minu", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{&Balance{Value: 50}}, utils.VOICE: BalanceChain{ @@ -60,7 +60,7 @@ func populateDB() { }}, } broker := &Account{ - Id: "vdf:broker", + ID: "vdf:broker", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{ &Balance{Value: 20, DestinationIDs: utils.NewStringMap("NAT"), Weight: 10, RatingSubject: "rif"}, @@ -68,7 +68,7 @@ func populateDB() { }}, } luna := &Account{ - Id: "vdf:luna", + ID: "vdf:luna", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{Value: 0, Weight: 20}, @@ -76,7 +76,7 @@ func populateDB() { } // this is added to test if csv load tests account will not overwrite balances minitsboy := &Account{ - Id: "vdf:minitsboy", + ID: "vdf:minitsboy", BalanceMap: map[string]BalanceChain{ utils.VOICE: BalanceChain{ &Balance{Value: 20, DestinationIDs: utils.NewStringMap("NAT"), Weight: 10, RatingSubject: "rif"}, @@ -88,14 +88,14 @@ func populateDB() { }, } max := &Account{ - Id: "cgrates.org:max", + ID: "cgrates.org:max", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{Value: 11, Weight: 20}, }}, } money := &Account{ - Id: "cgrates.org:money", + ID: "cgrates.org:money", BalanceMap: map[string]BalanceChain{ utils.MONETARY: BalanceChain{ &Balance{Value: 10000, Weight: 10}, diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 9f9a62abd..5e1c10b32 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -1101,7 +1101,7 @@ func TestLoadAccountActions(t *testing.T) { } aa := csvr.accountActions["vdf:minitsboy"] expected := &Account{ - Id: "vdf:minitsboy", + ID: "vdf:minitsboy", UnitCounters: UnitCounters{ utils.VOICE: []*UnitCounter{ &UnitCounter{ @@ -1136,12 +1136,12 @@ func TestLoadAccountActions(t *testing.T) { t.Errorf("Error loading account action: %+v", utils.ToIJSON(aa.UnitCounters[utils.VOICE][0].Counters[0].Filter)) } // test that it does not overwrite balances - existing, err := accountingStorage.GetAccount(aa.Id) + existing, err := accountingStorage.GetAccount(aa.ID) if err != nil || len(existing.BalanceMap) != 2 { t.Errorf("The account was not set before load: %+v", existing) } accountingStorage.SetAccount(aa) - existing, err = accountingStorage.GetAccount(aa.Id) + existing, err = accountingStorage.GetAccount(aa.ID) if err != nil || len(existing.BalanceMap) != 2 { t.Errorf("The set account altered the balances: %+v", existing) } diff --git a/engine/responder_test.go b/engine/responder_test.go index e3f0d7ae4..7fc5d06c5 100644 --- a/engine/responder_test.go +++ b/engine/responder_test.go @@ -73,8 +73,8 @@ func TestResponderGetDerivedMaxSessionTime(t *testing.T) { } b10 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.NewStringMap("DE_TMOBILE")} b20 := &Balance{Value: 20, Weight: 10, DestinationIDs: utils.NewStringMap("DE_TMOBILE")} - rifsAccount := &Account{Id: utils.ConcatenatedKey(testTenant, "rif"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b10}}} - dansAccount := &Account{Id: utils.ConcatenatedKey(testTenant, "dan"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b20}}} + rifsAccount := &Account{ID: utils.ConcatenatedKey(testTenant, "rif"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b10}}} + dansAccount := &Account{ID: utils.ConcatenatedKey(testTenant, "dan"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b20}}} if err := accountingStorage.SetAccount(rifsAccount); err != nil { t.Error(err) } @@ -437,8 +437,8 @@ func TestResponderGetLCR(t *testing.T) { } bRif12 := &Balance{Value: 40, Weight: 10, DestinationIDs: utils.NewStringMap(dstDe.Id)} bIvo12 := &Balance{Value: 60, Weight: 10, DestinationIDs: utils.NewStringMap(dstDe.Id)} - rif12sAccount := &Account{Id: utils.ConcatenatedKey("tenant12", "rif12"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{bRif12}}, AllowNegative: true} - ivo12sAccount := &Account{Id: utils.ConcatenatedKey("tenant12", "ivo12"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{bIvo12}}, AllowNegative: true} + rif12sAccount := &Account{ID: utils.ConcatenatedKey("tenant12", "rif12"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{bRif12}}, AllowNegative: true} + ivo12sAccount := &Account{ID: utils.ConcatenatedKey("tenant12", "ivo12"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{bIvo12}}, AllowNegative: true} for _, acnt := range []*Account{rif12sAccount, ivo12sAccount} { if err := accountingStorage.SetAccount(acnt); err != nil { t.Error(err) diff --git a/engine/sharedgroup.go b/engine/sharedgroup.go index ab8e4750b..5bec228bc 100644 --- a/engine/sharedgroup.go +++ b/engine/sharedgroup.go @@ -51,7 +51,7 @@ type SharingParameters struct { func (sg *SharedGroup) SortBalancesByStrategy(myBalance *Balance, bc BalanceChain) BalanceChain { sharingParameters := sg.AccountParameters[utils.ANY] - if sp, hasParamsForAccount := sg.AccountParameters[myBalance.account.Id]; hasParamsForAccount { + if sp, hasParamsForAccount := sg.AccountParameters[myBalance.account.ID]; hasParamsForAccount { sharingParameters = sp } @@ -94,7 +94,7 @@ func (sg *SharedGroup) GetBalances(destination, category, direction, balanceType // if len(sg.members) == 0 { for ubId := range sg.MemberIds { var nUb *Account - if ubId == ub.Id { // skip the initiating user + if ubId == ub.ID { // skip the initiating user nUb = ub } else { nUb, _ = accountingStorage.GetAccount(ubId) diff --git a/engine/sharedgroup_test.go b/engine/sharedgroup_test.go index b74df0846..bbf6efc51 100644 --- a/engine/sharedgroup_test.go +++ b/engine/sharedgroup_test.go @@ -52,7 +52,7 @@ func TestSharedSetGet(t *testing.T) { func TestSharedPopBalanceByStrategyLow(t *testing.T) { bc := BalanceChain{ &Balance{Value: 2.0}, - &Balance{Uuid: "uuuu", Value: 1.0, account: &Account{Id: "test"}}, + &Balance{Uuid: "uuuu", Value: 1.0, account: &Account{ID: "test"}}, &Balance{Value: 3.0}, } sg := &SharedGroup{AccountParameters: map[string]*SharingParameters{ @@ -68,7 +68,7 @@ func TestSharedPopBalanceByStrategyLow(t *testing.T) { func TestSharedPopBalanceByStrategyHigh(t *testing.T) { bc := BalanceChain{ - &Balance{Uuid: "uuuu", Value: 2.0, account: &Account{Id: "test"}}, + &Balance{Uuid: "uuuu", Value: 2.0, account: &Account{ID: "test"}}, &Balance{Value: 1.0}, &Balance{Value: 3.0}, } @@ -85,7 +85,7 @@ func TestSharedPopBalanceByStrategyHigh(t *testing.T) { func TestSharedPopBalanceByStrategyMineHigh(t *testing.T) { bc := BalanceChain{ - &Balance{Uuid: "uuuu", Value: 2.0, account: &Account{Id: "test"}}, + &Balance{Uuid: "uuuu", Value: 2.0, account: &Account{ID: "test"}}, &Balance{Value: 1.0}, &Balance{Value: 3.0}, } diff --git a/engine/storage_map.go b/engine/storage_map.go index d1e788745..0e77e3ae1 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -464,7 +464,7 @@ func (ms *MapStorage) SetSharedGroup(sg *SharedGroup) (err error) { func (ms *MapStorage) GetAccount(key string) (ub *Account, err error) { if values, ok := ms.dict[utils.ACCOUNT_PREFIX+key]; ok { - ub = &Account{Id: key} + ub = &Account{ID: key} err = ms.ms.Unmarshal(values, ub) } else { return nil, utils.ErrNotFound @@ -477,7 +477,7 @@ func (ms *MapStorage) SetAccount(ub *Account) (err error) { // UPDATE: if all balances expired and were cleaned it makes // sense to write empty balance map if len(ub.BalanceMap) == 0 { - if ac, err := ms.GetAccount(ub.Id); err == nil && !ac.allBalancesExpired() { + if ac, err := ms.GetAccount(ub.ID); err == nil && !ac.allBalancesExpired() { ac.ActionTriggers = ub.ActionTriggers ac.UnitCounters = ub.UnitCounters ac.AllowNegative = ub.AllowNegative @@ -486,7 +486,7 @@ func (ms *MapStorage) SetAccount(ub *Account) (err error) { } } result, err := ms.ms.Marshal(ub) - ms.dict[utils.ACCOUNT_PREFIX+ub.Id] = result + ms.dict[utils.ACCOUNT_PREFIX+ub.ID] = result return } diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index 85cd4f7ef..4fb29e84b 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -870,7 +870,7 @@ func (ms *MongoStorage) SetAccount(acc *Account) error { // UPDATE: if all balances expired and were cleaned it makes // sense to write empty balance map if len(acc.BalanceMap) == 0 { - if ac, err := ms.GetAccount(acc.Id); err == nil && !ac.allBalancesExpired() { + if ac, err := ms.GetAccount(acc.ID); err == nil && !ac.allBalancesExpired() { ac.ActionTriggers = acc.ActionTriggers ac.UnitCounters = acc.UnitCounters ac.AllowNegative = acc.AllowNegative @@ -878,7 +878,7 @@ func (ms *MongoStorage) SetAccount(acc *Account) error { acc = ac } } - _, err := ms.db.C(colAcc).Upsert(bson.M{"id": acc.Id}, acc) + _, err := ms.db.C(colAcc).Upsert(bson.M{"id": acc.ID}, acc) return err } diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 9c9fe6a51..52d4bb3a7 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -621,7 +621,7 @@ func (rs *RedisStorage) GetAccount(key string) (*Account, error) { if err != nil { return nil, err } - ub := &Account{Id: key} + ub := &Account{ID: key} if err = rs.ms.Unmarshal(values, ub); err != nil { return nil, err } @@ -633,7 +633,7 @@ func (rs *RedisStorage) SetAccount(ub *Account) (err error) { // UPDATE: if all balances expired and were cleaned it makes // sense to write empty balance map if len(ub.BalanceMap) == 0 { - if ac, err := rs.GetAccount(ub.Id); err == nil && !ac.allBalancesExpired() { + if ac, err := rs.GetAccount(ub.ID); err == nil && !ac.allBalancesExpired() { ac.ActionTriggers = ub.ActionTriggers ac.UnitCounters = ub.UnitCounters ac.AllowNegative = ub.AllowNegative @@ -642,7 +642,7 @@ func (rs *RedisStorage) SetAccount(ub *Account) (err error) { } } result, err := rs.ms.Marshal(ub) - err = rs.db.Cmd("SET", utils.ACCOUNT_PREFIX+ub.Id, result).Err + err = rs.db.Cmd("SET", utils.ACCOUNT_PREFIX+ub.ID, result).Err return } diff --git a/engine/storage_test.go b/engine/storage_test.go index bdb245c3e..40273c415 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -327,7 +327,7 @@ func GetUB() *Account { var zeroTime time.Time zeroTime = zeroTime.UTC() // for deep equal to find location ub := &Account{ - Id: "rif", + ID: "rif", AllowNegative: true, BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, utils.DATA: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.SMS: []*UnitCounter{uc, uc}}, diff --git a/engine/tp_reader.go b/engine/tp_reader.go index f117afd65..439138a4f 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -1086,7 +1086,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error ub, err := tpr.accountingStorage.GetAccount(id) if err != nil { ub = &Account{ - Id: id, + ID: id, } } ub.ActionTriggers = actionTriggers @@ -1121,7 +1121,7 @@ func (tpr *TpReader) LoadAccountActions() (err error) { } } ub := &Account{ - Id: aa.KeyId(), + ID: aa.KeyId(), ActionTriggers: aTriggers, AllowNegative: aa.AllowNegative, Disabled: aa.Disabled, @@ -1763,7 +1763,7 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose bool) (err error) { return err } if verbose { - log.Println("\t", ub.Id) + log.Println("\t", ub.ID) } } if verbose { From ebe05d3863c8c75283f178a8267a813a0a03c858 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 23 Feb 2016 21:11:26 +0200 Subject: [PATCH 109/199] account ID fixes --- apier/v1/accounts.go | 6 +++--- apier/v1/accounts_test.go | 10 +++++----- apier/v2/accounts.go | 2 +- cmd/cgr-loader/migrator_rc8.go | 8 ++++---- general_tests/acntacts_test.go | 6 +++--- general_tests/tutorial_local_test.go | 12 ++++++------ 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 52885983e..366ab4961 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -144,7 +144,7 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error ub = bal } else { // Not found in db, create it here ub = &engine.Account{ - Id: accID, + ID: accID, } } if len(attr.ActionPlanId) != 0 { @@ -395,7 +395,7 @@ func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *st if _, err := self.AccountDb.GetAccount(accID); err != nil { // create account if not exists account := &engine.Account{ - Id: accID, + ID: accID, } if err := self.AccountDb.SetAccount(account); err != nil { *reply = err.Error() @@ -487,7 +487,7 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { if _, err := self.AccountDb.GetAccount(accID); err != nil { // create account if not exists account := &engine.Account{ - Id: accID, + ID: accID, } if err := self.AccountDb.SetAccount(account); err != nil { *reply = err.Error() diff --git a/apier/v1/accounts_test.go b/apier/v1/accounts_test.go index 51a5ce573..122d5cef1 100644 --- a/apier/v1/accounts_test.go +++ b/apier/v1/accounts_test.go @@ -41,15 +41,15 @@ func TestSetAccounts(t *testing.T) { cgrTenant := "cgrates.org" iscTenant := "itsyscom.com" b10 := &engine.Balance{Value: 10, Weight: 10} - cgrAcnt1 := &engine.Account{Id: utils.ConcatenatedKey(cgrTenant, "account1"), + cgrAcnt1 := &engine.Account{ID: utils.ConcatenatedKey(cgrTenant, "account1"), BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + utils.OUT: engine.BalanceChain{b10}}} - cgrAcnt2 := &engine.Account{Id: utils.ConcatenatedKey(cgrTenant, "account2"), + cgrAcnt2 := &engine.Account{ID: utils.ConcatenatedKey(cgrTenant, "account2"), BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + utils.OUT: engine.BalanceChain{b10}}} - cgrAcnt3 := &engine.Account{Id: utils.ConcatenatedKey(cgrTenant, "account3"), + cgrAcnt3 := &engine.Account{ID: utils.ConcatenatedKey(cgrTenant, "account3"), BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + utils.OUT: engine.BalanceChain{b10}}} - iscAcnt1 := &engine.Account{Id: utils.ConcatenatedKey(iscTenant, "account1"), + iscAcnt1 := &engine.Account{ID: utils.ConcatenatedKey(iscTenant, "account1"), BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + utils.OUT: engine.BalanceChain{b10}}} - iscAcnt2 := &engine.Account{Id: utils.ConcatenatedKey(iscTenant, "account2"), + iscAcnt2 := &engine.Account{ID: utils.ConcatenatedKey(iscTenant, "account2"), BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + utils.OUT: engine.BalanceChain{b10}}} for _, account := range []*engine.Account{cgrAcnt1, cgrAcnt2, cgrAcnt3, iscAcnt1, iscAcnt2} { if err := apierAcntsAcntStorage.SetAccount(account); err != nil { diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index e0e0f11b9..dc89590b9 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -102,7 +102,7 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { ub = bal } else { // Not found in db, create it here ub = &engine.Account{ - Id: accID, + ID: accID, } } if attr.ActionPlanIDs != nil { diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index 405977ce6..aea883a4c 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -173,7 +173,7 @@ func (mig MigratorRC8) migrateAccounts() error { } // transfer data into new structurse newAcc := &engine.Account{ - Id: oldAcc.Id, + ID: oldAcc.Id, BalanceMap: make(map[string]engine.BalanceChain, len(oldAcc.BalanceMap)), UnitCounters: make(engine.UnitCounters, len(oldAcc.UnitCounters)), ActionTriggers: make(engine.ActionTriggers, len(oldAcc.ActionTriggers)), @@ -181,12 +181,12 @@ func (mig MigratorRC8) migrateAccounts() error { Disabled: oldAcc.Disabled, } // fix id - idElements := strings.Split(newAcc.Id, utils.CONCATENATED_KEY_SEP) + idElements := strings.Split(newAcc.ID, utils.CONCATENATED_KEY_SEP) if len(idElements) != 3 { log.Printf("Malformed account ID %s", oldAcc.Id) continue } - newAcc.Id = fmt.Sprintf("%s:%s", idElements[1], idElements[2]) + newAcc.ID = fmt.Sprintf("%s:%s", idElements[1], idElements[2]) // balances balanceErr := false for oldBalKey, oldBalChain := range oldAcc.BalanceMap { @@ -344,7 +344,7 @@ func (mig MigratorRC8) migrateAccounts() error { if err != nil { return err } - if err := mig.db.Cmd("SET", utils.ACCOUNT_PREFIX+newAcc.Id, result).Err; err != nil { + if err := mig.db.Cmd("SET", utils.ACCOUNT_PREFIX+newAcc.ID, result).Err; err != nil { return err } } diff --git a/general_tests/acntacts_test.go b/general_tests/acntacts_test.go index 50ec258cf..060cef8db 100644 --- a/general_tests/acntacts_test.go +++ b/general_tests/acntacts_test.go @@ -63,7 +63,7 @@ ENABLE_ACNT,*enable_account,,,,,,,,,,,,,,false,false,10` csvr.WriteToDatabase(false, false) ratingDbAcntActs.CacheRatingAll() acntDbAcntActs.CacheAccountingAll() - expectAcnt := &engine.Account{Id: "cgrates.org:1"} + expectAcnt := &engine.Account{ID: "cgrates.org:1"} if acnt, err := acntDbAcntActs.GetAccount("cgrates.org:1"); err != nil { t.Error(err) } else if acnt == nil { @@ -82,7 +82,7 @@ func TestAcntActsDisableAcnt(t *testing.T) { if err := at.Execute(); err != nil { t.Error(err) } - expectAcnt := &engine.Account{Id: "cgrates.org:1", Disabled: true} + expectAcnt := &engine.Account{ID: "cgrates.org:1", Disabled: true} if acnt, err := acntDbAcntActs.GetAccount(acnt1Tag); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectAcnt, acnt) { @@ -99,7 +99,7 @@ func TestAcntActsEnableAcnt(t *testing.T) { if err := at.Execute(); err != nil { t.Error(err) } - expectAcnt := &engine.Account{Id: "cgrates.org:1", Disabled: false} + expectAcnt := &engine.Account{ID: "cgrates.org:1", Disabled: false} if acnt, err := acntDbAcntActs.GetAccount(acnt1Tag); err != nil { t.Error(err) } else if !reflect.DeepEqual(expectAcnt, acnt) { diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index 0301e99a9..8bf31bfd9 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -1164,9 +1164,9 @@ func TestTutLocalSetAccount(t *testing.T) { t.Errorf("Accounts received: %+v", acnts) } else { acnt := acnts[0] - dta, _ := utils.NewTAFromAccountKey(acnt.Id) + dta, _ := utils.NewTAFromAccountKey(acnt.ID) if dta.Tenant != attrs.Tenant || dta.Account != attrs.Account { - t.Error("Unexpected account id received: ", acnt.Id) + t.Error("Unexpected account id received: ", acnt.ID) } if balances := acnt.BalanceMap["*monetary"]; len(balances) != 1 { t.Errorf("Unexpected balances found: %+v", balances) @@ -1194,9 +1194,9 @@ func TestTutLocalSetAccount(t *testing.T) { t.Errorf("Accounts received: %+v", acnts) } else { acnt := acnts[0] - dta, _ := utils.NewTAFromAccountKey(acnt.Id) + dta, _ := utils.NewTAFromAccountKey(acnt.ID) if dta.Tenant != attrs.Tenant || dta.Account != attrs.Account { - t.Error("Unexpected account id received: ", acnt.Id) + t.Error("Unexpected account id received: ", acnt.ID) } if balances := acnt.BalanceMap["*monetary"]; len(balances) != 1 { t.Errorf("Unexpected balances found: %+v", balances) @@ -1225,9 +1225,9 @@ func TestTutLocalSetAccount(t *testing.T) { t.Errorf("Accounts received: %+v", acnts) } else { acnt := acnts[0] - dta, _ := utils.NewTAFromAccountKey(acnt.Id) + dta, _ := utils.NewTAFromAccountKey(acnt.ID) if dta.Tenant != attrs.Tenant || dta.Account != attrs.Account { - t.Error("Unexpected account id received: ", acnt.Id) + t.Error("Unexpected account id received: ", acnt.ID) } if balances := acnt.BalanceMap["*monetary"]; len(balances) != 1 { t.Errorf("Unexpected balances found: %+v", balances) From b039693102fa77b89b9cc37da292518daf193920 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 23 Feb 2016 20:38:52 +0100 Subject: [PATCH 110/199] Diameter enhancements for fmtOut function, separate sample for voice.json splitting into request type --- agents/dmtagent.go | 2 +- agents/libdmt.go | 26 ++++++++------- agents/libdmt_test.go | 21 +++++++++--- .../samples/dmtagent/diameter_processors.json | 24 -------------- data/conf/samples/dmtagent/voice.json | 33 +++++++++++++++++++ 5 files changed, 65 insertions(+), 41 deletions(-) create mode 100644 data/conf/samples/dmtagent/voice.json diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 0c951e3af..d5d7b7d39 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -72,7 +72,7 @@ func (self *DiameterAgent) handlers() diam.Handler { func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor) *CCA { passesAllFilters := true for _, fldFilter := range reqProcessor.RequestFilter { - if passes, _ := passesFieldFilter(ccr.diamMessage, fldFilter); !passes { + if passes, _ := passesFieldFilter(ccr.diamMessage, fldFilter, nil); !passes { passesAllFilters = false } } diff --git a/agents/libdmt.go b/agents/libdmt.go index d1b3269e5..30c3b2679 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -50,7 +50,6 @@ func init() { const ( META_CCR_USAGE = "*ccr_usage" - META_CCA_USAGE = "*cca_usage" META_VALUE_EXPONENT = "*value_exponent" DIAMETER_CCR = "DIAMETER_CCR" DiameterRatingFailed = 5031 @@ -255,15 +254,20 @@ func avpsWithPath(m *diam.Message, rsrFld *utils.RSRField) ([]*diam.AVP, error) } // Follows the implementation in the StorCdr -func passesFieldFilter(m *diam.Message, fieldFilter *utils.RSRField) (bool, int) { +func passesFieldFilter(m *diam.Message, fieldFilter *utils.RSRField, processorVars map[string]string) (bool, int) { if fieldFilter == nil { return true, 0 } avps, err := avpsWithPath(m, fieldFilter) if err != nil { + if strings.HasPrefix(err.Error(), "Could not find AVP") { + if val, hasIt := processorVars[fieldFilter.Id]; hasIt { // Could not find it in the message, try to match it in processorVars + if fieldFilter.FilterPasses(val) { + return true, -1 + } + } + } return false, 0 - } else if len(avps) == 0 { - return false, 0 // No AVPs with field filter ID } for avpIdx, avpVal := range avps { // First match wins due to index if fieldFilter.FilterPasses(avpValAsString(avpVal)) { @@ -340,13 +344,13 @@ func serializeAVPValueFromString(dictAVP *dict.AVP, valStr, timezone string) ([] var ErrFilterNotPassing = errors.New("Filter not passing") -func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interface{}) (fmtValOut string, err error) { +func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interface{}, processorVars map[string]string) (fmtValOut string, err error) { var outVal string passAtIndex := -1 passedAllFilters := true for _, fldFilter := range cfgFld.FieldFilter { var pass bool - if pass, passAtIndex = passesFieldFilter(m, fldFilter); !pass { + if pass, passAtIndex = passesFieldFilter(m, fldFilter, processorVars); !pass { passedAllFilters = false break } @@ -364,9 +368,7 @@ func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interfa case utils.META_CONSTANT: outVal = cfgFld.Value.Id() case utils.META_HANDLER: - if cfgFld.HandlerId == META_CCA_USAGE { // Exception, usage is passed in the dur variable by CCA - outVal = strconv.FormatFloat(extraParam.(float64), 'f', -1, 64) - } else if cfgFld.HandlerId == META_VALUE_EXPONENT { + if cfgFld.HandlerId == META_VALUE_EXPONENT { outVal, err = metaValueExponent(m, cfgFld.Value, 10) // FixMe: add here configured number of decimals } else { outVal, err = metaHandler(m, cfgFld.HandlerId, cfgFld.Layout, extraParam.(time.Duration)) @@ -582,7 +584,7 @@ func (self *CCR) AsSMGenericEvent(cfgFlds []*config.CfgCdrField) (sessionmanager outMap := make(map[string]string) // work with it so we can append values to keys outMap[utils.EVENT_NAME] = DIAMETER_CCR for _, cfgFld := range cfgFlds { - fmtOut, err := fieldOutVal(self.diamMessage, cfgFld, self.debitInterval) + fmtOut, err := fieldOutVal(self.diamMessage, cfgFld, self.debitInterval, nil) if err != nil { if err == ErrFilterNotPassing { continue // Do nothing in case of Filter not passing @@ -649,9 +651,9 @@ func (self *CCA) AsDiameterMessage() *diam.Message { // SetProcessorAVPs will add AVPs to self.diameterMessage based on template defined in processor.CCAFields func (self *CCA) SetProcessorAVPs(reqProcessor *config.DARequestProcessor, processorVars map[string]string) error { for _, cfgFld := range reqProcessor.CCAFields { - fmtOut, err := fieldOutVal(self.ccrMessage, cfgFld, processorVars) + fmtOut, err := fieldOutVal(self.ccrMessage, cfgFld, nil, processorVars) if err == ErrFilterNotPassing { // Field not in or filter not passing, try match in answer - fmtOut, err = fieldOutVal(self.diamMessage, cfgFld, processorVars) + fmtOut, err = fieldOutVal(self.diamMessage, cfgFld, nil, processorVars) } if err != nil { if err == ErrFilterNotPassing { diff --git a/agents/libdmt_test.go b/agents/libdmt_test.go index 3eb91584e..aeed0968a 100644 --- a/agents/libdmt_test.go +++ b/agents/libdmt_test.go @@ -134,7 +134,7 @@ func TestFieldOutVal(t *testing.T) { cfgFld := &config.CfgCdrField{Tag: "StaticTest", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("^*voice", utils.INFIELD_SEP), Mandatory: true} eOut := "*voice" - if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0)); err != nil { + if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0), nil); err != nil { t.Error(err) } else if fldOut != eOut { t.Errorf("Expecting: %s, received: %s", eOut, fldOut) @@ -142,7 +142,20 @@ func TestFieldOutVal(t *testing.T) { cfgFld = &config.CfgCdrField{Tag: "ComposedTest", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Time", utils.INFIELD_SEP), Mandatory: true} eOut = "360" - if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0)); err != nil { + if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0), nil); err != nil { + t.Error(err) + } else if fldOut != eOut { + t.Errorf("Expecting: %s, received: %s", eOut, fldOut) + } + // With filter on ProcessorVars + cfgFld = &config.CfgCdrField{Tag: "ComposedTestWithProcessorVarsFilter", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, + FieldFilter: utils.ParseRSRFieldsMustCompile("CGRError(INSUFFICIENT_CREDIT)", utils.INFIELD_SEP), + Value: utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Time", utils.INFIELD_SEP), Mandatory: true} + if _, err := fieldOutVal(m, cfgFld, time.Duration(0), nil); err == nil { + t.Error("Should have error") + } + eOut = "360" + if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0), map[string]string{"CGRError": "INSUFFICIENT_CREDIT"}); err != nil { t.Error(err) } else if fldOut != eOut { t.Errorf("Expecting: %s, received: %s", eOut, fldOut) @@ -151,7 +164,7 @@ func TestFieldOutVal(t *testing.T) { cfgFld = &config.CfgCdrField{Tag: "Grouped1", Type: utils.MetaGrouped, FieldId: "Account", Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true} eOut = "33708000003" - if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0)); err != nil { + if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0), nil); err != nil { t.Error(err) } else if fldOut != eOut { t.Errorf("Expecting: %s, received: %s", eOut, fldOut) @@ -161,7 +174,7 @@ func TestFieldOutVal(t *testing.T) { FieldFilter: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Type(1)", utils.INFIELD_SEP), Value: utils.ParseRSRFieldsMustCompile("Subscription-Id>Subscription-Id-Data", utils.INFIELD_SEP), Mandatory: true} eOut = "208708000003" - if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0)); err != nil { + if fldOut, err := fieldOutVal(m, cfgFld, time.Duration(0), nil); err != nil { t.Error(err) } else if fldOut != eOut { t.Errorf("Expecting: %s, received: %s", eOut, fldOut) diff --git a/data/conf/samples/dmtagent/diameter_processors.json b/data/conf/samples/dmtagent/diameter_processors.json index 57a6d90f1..ab906b0a7 100644 --- a/data/conf/samples/dmtagent/diameter_processors.json +++ b/data/conf/samples/dmtagent/diameter_processors.json @@ -41,30 +41,6 @@ {"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "300"}, ], }, - { - "id": "*default", // formal identifier of this processor - "dry_run": false, // do not send the events to SMG, just log them - "request_filter": "Service-Context-Id(^voice)", // filter requests processed by this processor - "continue_on_success": false, // continue to the next template if executed - "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value - {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true}, - {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, - {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*users", "mandatory": true}, - {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, - {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^*users", "mandatory": true}, - {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, - {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "^*users", "mandatory": true}, - {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "^*users", "mandatory": true}, - {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>IN-Information>Real-Called-Number", "mandatory": true}, - {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, - {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, - {"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*ccr_usage", "mandatory": true}, - {"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "mandatory": true}, - ], - "cca_fields":[ // fields returned in CCA - {"tag": "GrantedUnits", "field_id": "Granted-Service-Unit>CC-Time", "type": "*handler", "handler_id": "*cca_usage", "mandatory": true}, - ], - }, { "id": "message", // formal identifier of this processor "dry_run": false, // do not send the events to SMG, just log them diff --git a/data/conf/samples/dmtagent/voice.json b/data/conf/samples/dmtagent/voice.json new file mode 100644 index 000000000..24ab07879 --- /dev/null +++ b/data/conf/samples/dmtagent/voice.json @@ -0,0 +1,33 @@ + +{ + +"diameter_agent": { + "request_processors": [ + { + "id": "*default", // formal identifier of this processor + "dry_run": false, // do not send the events to SMG, just log them + "request_filter": "Service-Context-Id(^voice)", // filter requests processed by this processor + "continue_on_success": false, // continue to the next template if executed + "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true}, + {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, + {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, + {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, + {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>IN-Information>Real-Called-Number", "mandatory": true}, + {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*ccr_usage", "mandatory": true}, + {"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "mandatory": true}, + ], + "cca_fields":[ // fields returned in CCA + {"tag": "GrantedUnits", "field_id": "Granted-Service-Unit>CC-Time", "type": "*handler", "handler_id": "*cca_usage", "mandatory": true}, + ], + }, + ], +}, + +} \ No newline at end of file From 4f0efea0ddeb9d5b331d77be726baf07d2ea307c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 23 Feb 2016 22:16:50 +0200 Subject: [PATCH 111/199] added ForceDuration on MaxDebit --- engine/calldesc.go | 26 +++++++++++++++++--------- engine/calldesc_test.go | 25 +++++++++++++++++++++++++ utils/consts.go | 2 +- 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/engine/calldesc.go b/engine/calldesc.go index 2d55812bb..9d6e81c1d 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -150,13 +150,14 @@ type CallDescriptor struct { TOR string // used unit balances selector ExtraFields map[string]string // Extra fields, mostly used for user profile matching // session limits - MaxRate float64 - MaxRateUnit time.Duration - MaxCostSoFar float64 - CgrID string - RunID string - account *Account - testCallcost *CallCost // testing purpose only! + MaxRate float64 + MaxRateUnit time.Duration + MaxCostSoFar float64 + CgrID string + RunID string + ForceDuration bool // for Max debit if less than duration return err + account *Account + testCallcost *CallCost // testing purpose only! } func (cd *CallDescriptor) ValidateCallData() error { @@ -728,9 +729,16 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) { return nil, utils.ErrAccountNotFound } else { //log.Printf("ACC: %+v", account) - if memberIds, err := account.GetUniqueSharedGroupMembers(cd); err == nil { + if memberIDs, err := account.GetUniqueSharedGroupMembers(cd); err == nil { _, err = Guardian.Guard(func() (interface{}, error) { remainingDuration, err := cd.getMaxSessionDuration(account) + if err != nil && cd.GetDuration() > 0 { + return 0, err + } + // check ForceDuartion + if cd.ForceDuration && !account.AllowNegative && remainingDuration < cd.GetDuration() { + return 0, utils.ErrInsufficientCredit + } //log.Print("AFTER MAX SESSION: ", cd) if err != nil || remainingDuration == 0 { cc = cd.CreateCallCost() @@ -758,7 +766,7 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) { cc, err = cd.debit(account, false, true) //log.Print(balanceMap[0].Value, balanceMap[1].Value) return 0, err - }, 0, memberIds.Slice()...) + }, 0, memberIDs.Slice()...) if err != nil { return cc, err } diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 9f174dc4f..8e1fe8ac0 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -1271,6 +1271,31 @@ func TestMaxDebitZeroDefinedRate(t *testing.T) { } } +func TestMaxDebitForceDuration(t *testing.T) { + ap, _ := ratingStorage.GetActionPlan("TOPUP10_AT", false) + for _, at := range ap.ActionTimings { + at.accountIDs = ap.AccountIDs + at.Execute() + } + cd1 := &CallDescriptor{ + Direction: "*out", + Category: "call", + Tenant: "cgrates.org", + Subject: "12345", + Account: "12345", + Destination: "447956", + TimeStart: time.Date(2014, 3, 4, 6, 0, 0, 0, time.UTC), + TimeEnd: time.Date(2014, 3, 4, 6, 1, 40, 0, time.UTC), + LoopIndex: 0, + DurationIndex: 0, + ForceDuration: true, + } + _, err := cd1.MaxDebit() + if err != utils.ErrInsufficientCredit { + t.Fatal("Error forcing duration: ", err) + } +} + func TestMaxDebitZeroDefinedRateOnlyMinutes(t *testing.T) { ap, _ := ratingStorage.GetActionPlan("TOPUP10_AT", false) for _, at := range ap.ActionTimings { diff --git a/utils/consts.go b/utils/consts.go index 42ce00d04..904c177a4 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -29,7 +29,7 @@ var ( ErrRatingPlanNotFound = errors.New("RATING_PLAN_NOT_FOUND") ErrAccountNotFound = errors.New("ACCOUNT_NOT_FOUND") ErrUserNotFound = errors.New("USER_NOT_FOUND") - ErrCreditInsufficient = errors.New("CREDIT_INSUFFICIENT") + ErrInsufficientCredit = errors.New("INSUFFICENT_CREDIT") ) const ( From f3e5f0bfdeb814a7093d7e195ad5145e373ce2ea Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 24 Feb 2016 14:07:08 +0100 Subject: [PATCH 112/199] Diameter - completing field templates for individual messages --- agents/dmtagent.go | 1 + agents/libdmt.go | 25 ++++---- .../samples/dmtagent/diameter_processors.json | 10 +++ data/conf/samples/dmtagent/voice.json | 61 ++++++++++++++++++- utils/rsrfield.go | 3 + utils/rsrfield_test.go | 10 +++ 6 files changed, 96 insertions(+), 14 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index d5d7b7d39..0d3b0bfe1 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -109,6 +109,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro var maxUsage float64 processorVars := make(map[string]string) processorVars[CGRResultCode] = strconv.Itoa(diam.Success) + processorVars[CGRError] = "" if reqProcessor.DryRun { // DryRun does not send over network utils.Logger.Info(fmt.Sprintf(" SMGenericEvent: %+v", smgEv)) processorVars[CGRResultCode] = strconv.Itoa(diam.LimitedSuccess) diff --git a/agents/libdmt.go b/agents/libdmt.go index 30c3b2679..6d2ee67a8 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -221,7 +221,7 @@ func metaHandler(m *diam.Message, tag, arg string, dur time.Duration) (string, e // metaValueExponent will multiply the float value with the exponent provided. // Expects 2 arguments in template separated by | func metaValueExponent(m *diam.Message, argsTpl utils.RSRFields, roundingDecimals int) (string, error) { - valStr := composedFieldvalue(m, argsTpl, 0) + valStr := composedFieldvalue(m, argsTpl, 0, nil) handlerArgs := strings.Split(valStr, utils.HandlerArgSep) if len(handlerArgs) != 2 { return "", errors.New("Unexpected number of arguments") @@ -258,15 +258,14 @@ func passesFieldFilter(m *diam.Message, fieldFilter *utils.RSRField, processorVa if fieldFilter == nil { return true, 0 } + if val, hasIt := processorVars[fieldFilter.Id]; hasIt { // ProcessorVars have priority + if fieldFilter.FilterPasses(val) { + return true, 0 + } + return false, 0 + } avps, err := avpsWithPath(m, fieldFilter) if err != nil { - if strings.HasPrefix(err.Error(), "Could not find AVP") { - if val, hasIt := processorVars[fieldFilter.Id]; hasIt { // Could not find it in the message, try to match it in processorVars - if fieldFilter.FilterPasses(val) { - return true, -1 - } - } - } return false, 0 } for avpIdx, avpVal := range avps { // First match wins due to index @@ -277,12 +276,16 @@ func passesFieldFilter(m *diam.Message, fieldFilter *utils.RSRField, processorVa return false, 0 } -func composedFieldvalue(m *diam.Message, outTpl utils.RSRFields, avpIdx int) string { +func composedFieldvalue(m *diam.Message, outTpl utils.RSRFields, avpIdx int, processorVars map[string]string) string { var outVal string for _, rsrTpl := range outTpl { if rsrTpl.IsStatic() { outVal += rsrTpl.ParseValue("") } else { + if val, hasIt := processorVars[rsrTpl.Id]; hasIt { // ProcessorVars have priority + outVal += rsrTpl.ParseValue(val) + continue + } matchingAvps, err := avpsWithPath(m, rsrTpl) if err != nil || len(matchingAvps) == 0 { utils.Logger.Warning(fmt.Sprintf(" Cannot find AVP for field template with id: %s, ignoring.", rsrTpl.Id)) @@ -377,9 +380,9 @@ func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interfa } } case utils.META_COMPOSED: - outVal = composedFieldvalue(m, cfgFld.Value, 0) + outVal = composedFieldvalue(m, cfgFld.Value, 0, processorVars) case utils.MetaGrouped: // GroupedAVP - outVal = composedFieldvalue(m, cfgFld.Value, passAtIndex) + outVal = composedFieldvalue(m, cfgFld.Value, passAtIndex, processorVars) } if fmtValOut, err = utils.FmtFieldWidth(outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { utils.Logger.Warning(fmt.Sprintf(" Error when processing field template with tag: %s, error: %s", cfgFld.Tag, err.Error())) diff --git a/data/conf/samples/dmtagent/diameter_processors.json b/data/conf/samples/dmtagent/diameter_processors.json index ab906b0a7..df3d23737 100644 --- a/data/conf/samples/dmtagent/diameter_processors.json +++ b/data/conf/samples/dmtagent/diameter_processors.json @@ -2,6 +2,10 @@ "diameter_agent": { "request_processors": [ + { + "id": "*default", // formal identifier of this processor + "request_filter": "Service-Context-Id(nonexistent)", // cancel matching of this processor + }, { "id": "dryrun1", // formal identifier of this processor "dry_run": true, // do not send the events to SMG, just log them @@ -60,6 +64,12 @@ {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, {"tag": "Usage", "field_id": "Usage", "type": "*composed", "value": "Requested-Service-Unit>CC-Time", "mandatory": true}, ], + "cca_fields":[ + {"tag": "ResultCode", "field_filter":"CGRError(ACCOUNT_NOT_FOUND)", + "field_id": "Result-Code", "type": "*constant", "value": "5030"}, + {"tag": "ResultCode", "field_filter":"CGRError(USER_NOT_FOUND)", + "field_id": "Result-Code", "type": "*constant", "value": "5030"}, + ], }, ], diff --git a/data/conf/samples/dmtagent/voice.json b/data/conf/samples/dmtagent/voice.json index 24ab07879..a1b369ea4 100644 --- a/data/conf/samples/dmtagent/voice.json +++ b/data/conf/samples/dmtagent/voice.json @@ -4,9 +4,38 @@ "diameter_agent": { "request_processors": [ { - "id": "*default", // formal identifier of this processor + "id": "VoiceInit", // formal identifier of this processor "dry_run": false, // do not send the events to SMG, just log them - "request_filter": "Service-Context-Id(^voice)", // filter requests processed by this processor + "request_filter": "Service-Context-Id(^voice);CC-Request-Type(1)", // filter requests processed by this processor + "continue_on_success": false, // continue to the next template if executed + "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true}, + {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, + {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, + {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, + {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>IN-Information>Real-Called-Number", "mandatory": true}, + {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*ccr_usage", "mandatory": true}, + {"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "mandatory": true}, + ], + "cca_fields":[ + {"tag": "ResultCode", "field_filter":"CGRError(ACCOUNT_NOT_FOUND)", + "field_id": "Result-Code", "type": "*constant", "value": "5030"}, + {"tag": "ResultCode", "field_filter":"CGRError(USER_NOT_FOUND)", + "field_id": "Result-Code", "type": "*constant", "value": "5030"}, + {"tag": "GrantedUnits", "field_filter":"CGRError(^$)", + "field_id": "Granted-Service-Unit>CC-Time", "type": "*composed", "value": "CGRMaxUsage", "mandatory": true}, + ], + }, + { + "id": "VoiceUpdate", // formal identifier of this processor + "dry_run": false, // do not send the events to SMG, just log them + "request_filter": "Service-Context-Id(^voice);CC-Request-Type(2)", // filter requests processed by this processor "continue_on_success": false, // continue to the next template if executed "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true}, @@ -24,7 +53,33 @@ {"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "mandatory": true}, ], "cca_fields":[ // fields returned in CCA - {"tag": "GrantedUnits", "field_id": "Granted-Service-Unit>CC-Time", "type": "*handler", "handler_id": "*cca_usage", "mandatory": true}, + {"tag": "GrantedUnits", "field_filter":"CGRError(^$)", + "field_id": "Granted-Service-Unit>CC-Time", "type": "*composed", "value": "CGRMaxUsage", "mandatory": true}, + ], + }, + { + "id": "VoiceTerminate", // formal identifier of this processor + "dry_run": false, // do not send the events to SMG, just log them + "request_filter": "Service-Context-Id(^voice);CC-Request-Type(3)", // filter requests processed by this processor + "continue_on_success": false, // continue to the next template if executed + "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value + {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true}, + {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, + {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "^*out", "mandatory": true}, + {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "^call", "mandatory": true}, + {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Subject", "field_id": "Subject", "type": "*composed", "value": "^*users", "mandatory": true}, + {"tag": "Destination", "field_id": "Destination", "type": "*composed", "value": "Service-Information>IN-Information>Real-Called-Number", "mandatory": true}, + {"tag": "SetupTime", "field_id": "SetupTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "AnswerTime", "field_id": "AnswerTime", "type": "*composed", "value": "Event-Timestamp", "mandatory": true}, + {"tag": "Usage", "field_id": "Usage", "type": "*handler", "handler_id": "*ccr_usage", "mandatory": true}, + {"tag": "SubscriberID", "field_id": "SubscriberId", "type": "*composed", "value": "Subscription-Id>Subscription-Id-Data", "mandatory": true}, + ], + "cca_fields":[ // fields returned in CCA + {"tag": "GrantedUnits", "field_filter":"CGRError(^$)", + "field_id": "Granted-Service-Unit>CC-Time", "type": "*composed", "value": "CGRMaxUsage", "mandatory": true}, ], }, ], diff --git a/utils/rsrfield.go b/utils/rsrfield.go index b454500fb..d8ca48c2b 100644 --- a/utils/rsrfield.go +++ b/utils/rsrfield.go @@ -166,6 +166,9 @@ func (rsrFltr *RSRFilter) Pass(val string) bool { if rsrFltr.filterRule[:1] == REGEXP_PREFIX { return rsrFltr.fltrRgxp.MatchString(val) != rsrFltr.negative } + if rsrFltr.filterRule == "^$" { // Special case to test empty value + return len(val) == 0 != rsrFltr.negative + } if rsrFltr.filterRule[:1] == MatchStartPrefix { return strings.HasPrefix(val, rsrFltr.filterRule[1:]) != rsrFltr.negative } diff --git a/utils/rsrfield_test.go b/utils/rsrfield_test.go index 1da60b703..c98c32fb1 100644 --- a/utils/rsrfield_test.go +++ b/utils/rsrfield_test.go @@ -341,4 +341,14 @@ func TestRSRFilterPass(t *testing.T) { if fltr.Pass("CGRateS") { t.Error("Passing!") } + fltr, err = NewRSRFilter("^$") // Empty value + if err != nil { + t.Error(err) + } + if fltr.Pass("CGRateS") { + t.Error("Passing!") + } + if !fltr.Pass("") { + t.Error("Not passing!") + } } From 30dbad3f2482de69129503530afa12277201b3d2 Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 24 Feb 2016 14:31:23 +0100 Subject: [PATCH 113/199] Build fix --- agents/dmtagent.go | 4 ++-- sessionmanager/smgeneric.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 0d3b0bfe1..c1d4dff5a 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -142,8 +142,8 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro processorVars[CGRError] = utils.ErrAccountNotFound.Error() case strings.HasSuffix(err.Error(), utils.ErrUserNotFound.Error()): processorVars[CGRError] = utils.ErrUserNotFound.Error() - case strings.HasSuffix(err.Error(), utils.ErrCreditInsufficient.Error()): - processorVars[CGRError] = utils.ErrCreditInsufficient.Error() + case strings.HasSuffix(err.Error(), utils.ErrInsufficientCredit.Error()): + processorVars[CGRError] = utils.ErrInsufficientCredit.Error() default: // Unknown error processorVars[CGRError] = err.Error() processorVars[CGRResultCode] = strconv.Itoa(DiameterRatingFailed) diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index bdc79807b..f611fb317 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -220,7 +220,7 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu } sR.CallCosts = append(sR.CallCosts, cc) // Save it so we can revert on issues if ccDur := cc.GetDuration(); ccDur == 0 { - err = utils.ErrCreditInsufficient + err = utils.ErrInsufficientCredit break } else if ccDur < maxDur { maxDur = ccDur From 34971c3f5ad969657152a768b73611fe8a063047 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 24 Feb 2016 18:13:04 +0200 Subject: [PATCH 114/199] action triger ID refactoring --- apier/v1/accounts.go | 2 +- apier/v1/accounts_test.go | 10 +- apier/v1/apier_local_test.go | 6 +- apier/v1/triggers.go | 2 +- cmd/cgr-loader/migrator_rc8.go | 4 +- engine/account.go | 42 ++++---- engine/account_test.go | 192 ++++++++++++++++----------------- engine/action.go | 13 ++- engine/action_trigger.go | 4 +- engine/actions_test.go | 154 +++++++++++++------------- engine/balances.go | 28 ++--- engine/balances_test.go | 10 +- engine/calldesc_test.go | 28 ++--- engine/loader_csv_test.go | 4 +- engine/responder_test.go | 8 +- engine/sharedgroup.go | 36 +++---- engine/sharedgroup_test.go | 8 +- engine/storage_test.go | 4 +- engine/tp_reader.go | 20 ++-- utils/map.go | 6 ++ 20 files changed, 293 insertions(+), 288 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 366ab4961..d9b6a7c6c 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -615,7 +615,7 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { } if account.BalanceMap == nil { - account.BalanceMap = make(map[string]engine.BalanceChain, 1) + account.BalanceMap = make(map[string]engine.Balances, 1) } var previousSharedGroups utils.StringMap // kept for comparison var balance *engine.Balance diff --git a/apier/v1/accounts_test.go b/apier/v1/accounts_test.go index 122d5cef1..8520e6e3c 100644 --- a/apier/v1/accounts_test.go +++ b/apier/v1/accounts_test.go @@ -42,15 +42,15 @@ func TestSetAccounts(t *testing.T) { iscTenant := "itsyscom.com" b10 := &engine.Balance{Value: 10, Weight: 10} cgrAcnt1 := &engine.Account{ID: utils.ConcatenatedKey(cgrTenant, "account1"), - BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + utils.OUT: engine.BalanceChain{b10}}} + BalanceMap: map[string]engine.Balances{utils.MONETARY + utils.OUT: engine.Balances{b10}}} cgrAcnt2 := &engine.Account{ID: utils.ConcatenatedKey(cgrTenant, "account2"), - BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + utils.OUT: engine.BalanceChain{b10}}} + BalanceMap: map[string]engine.Balances{utils.MONETARY + utils.OUT: engine.Balances{b10}}} cgrAcnt3 := &engine.Account{ID: utils.ConcatenatedKey(cgrTenant, "account3"), - BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + utils.OUT: engine.BalanceChain{b10}}} + BalanceMap: map[string]engine.Balances{utils.MONETARY + utils.OUT: engine.Balances{b10}}} iscAcnt1 := &engine.Account{ID: utils.ConcatenatedKey(iscTenant, "account1"), - BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + utils.OUT: engine.BalanceChain{b10}}} + BalanceMap: map[string]engine.Balances{utils.MONETARY + utils.OUT: engine.Balances{b10}}} iscAcnt2 := &engine.Account{ID: utils.ConcatenatedKey(iscTenant, "account2"), - BalanceMap: map[string]engine.BalanceChain{utils.MONETARY + utils.OUT: engine.BalanceChain{b10}}} + BalanceMap: map[string]engine.Balances{utils.MONETARY + utils.OUT: engine.Balances{b10}}} for _, account := range []*engine.Account{cgrAcnt1, cgrAcnt2, cgrAcnt3, iscAcnt1, iscAcnt2} { if err := apierAcntsAcntStorage.SetAccount(account); err != nil { t.Error(err) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index c9f58cf05..47da716b5 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1034,7 +1034,7 @@ func TestApierGetAccountActionTriggers(t *testing.T) { req := AttrAcntAction{Tenant: "cgrates.org", Account: "dan2"} if err := rater.Call("ApierV1.GetAccountActionTriggers", req, &reply); err != nil { t.Error("Got error on ApierV1.GetAccountActionTimings: ", err.Error()) - } else if len(reply) != 1 || reply[0].ActionsId != "LOG_BALANCE" { + } else if len(reply) != 1 || reply[0].ActionsID != "LOG_BALANCE" { t.Errorf("Unexpected action triggers received %v", reply) } } @@ -1049,7 +1049,7 @@ func TestApierSetAccountActionTriggers(t *testing.T) { req := AttrAcntAction{Tenant: "cgrates.org", Account: "dan2"} if err := rater.Call("ApierV1.GetAccountActionTriggers", req, &reply); err != nil { t.Error("Got error on ApierV1.GetAccountActionTimings: ", err.Error()) - } else if len(reply) != 1 || reply[0].ActionsId != "LOG_BALANCE" { + } else if len(reply) != 1 || reply[0].ActionsID != "LOG_BALANCE" { for _, atr := range reply { t.Logf("ATR: %+v", atr) } @@ -1084,7 +1084,7 @@ func TestApierRemAccountActionTriggers(t *testing.T) { req := AttrAcntAction{Tenant: "cgrates.org", Account: "dan2"} if err := rater.Call("ApierV1.GetAccountActionTriggers", req, &reply); err != nil { t.Error("Got error on ApierV1.GetAccountActionTimings: ", err.Error()) - } else if len(reply) != 1 || reply[0].ActionsId != "LOG_BALANCE" { + } else if len(reply) != 1 || reply[0].ActionsID != "LOG_BALANCE" { for _, atr := range reply { t.Logf("ATR: %+v", atr) } diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index ab71455f3..ee77c0766 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -281,7 +281,7 @@ func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, at.MinQueuedItems = *attr.MinQueuedItems } if attr.ActionsId != nil { - at.ActionsId = *attr.ActionsId + at.ActionsID = *attr.ActionsId } } diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index aea883a4c..30f523540 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -174,7 +174,7 @@ func (mig MigratorRC8) migrateAccounts() error { // transfer data into new structurse newAcc := &engine.Account{ ID: oldAcc.Id, - BalanceMap: make(map[string]engine.BalanceChain, len(oldAcc.BalanceMap)), + BalanceMap: make(map[string]engine.Balances, len(oldAcc.BalanceMap)), UnitCounters: make(engine.UnitCounters, len(oldAcc.UnitCounters)), ActionTriggers: make(engine.ActionTriggers, len(oldAcc.ActionTriggers)), AllowNegative: oldAcc.AllowNegative, @@ -198,7 +198,7 @@ func (mig MigratorRC8) migrateAccounts() error { } newBalKey := "*" + keyElements[1] newBalDirection := "*" + keyElements[2] - newAcc.BalanceMap[newBalKey] = make(engine.BalanceChain, len(oldBalChain)) + newAcc.BalanceMap[newBalKey] = make(engine.Balances, len(oldBalChain)) for index, oldBal := range oldBalChain { // check default to set new id if oldBal.IsDefault() { diff --git a/engine/account.go b/engine/account.go index 40521dfd9..f33640cc9 100644 --- a/engine/account.go +++ b/engine/account.go @@ -37,7 +37,7 @@ This can represent a user or a shared group. */ type Account struct { ID string - BalanceMap map[string]BalanceChain + BalanceMap map[string]Balances UnitCounters UnitCounters ActionTriggers ActionTriggers AllowNegative bool @@ -46,12 +46,12 @@ type Account struct { } // User's available minutes for the specified destination -func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duration, credit float64, balances BalanceChain) { +func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duration, credit float64, balances Balances) { creditBalances := ub.getBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, utils.MONETARY, "") unitBalances := ub.getBalancesForPrefix(cd.Destination, cd.Category, cd.Direction, cd.TOR, "") // gather all balances from shared groups - var extendedCreditBalances BalanceChain + var extendedCreditBalances Balances for _, cb := range creditBalances { if len(cb.SharedGroups) > 0 { for sg := range cb.SharedGroups { @@ -65,7 +65,7 @@ func (ub *Account) getCreditForPrefix(cd *CallDescriptor) (duration time.Duratio extendedCreditBalances = append(extendedCreditBalances, cb) } } - var extendedMinuteBalances BalanceChain + var extendedMinuteBalances Balances for _, mb := range unitBalances { if len(mb.SharedGroups) > 0 { for sg := range mb.SharedGroups { @@ -99,7 +99,7 @@ func (acc *Account) setBalanceAction(a *Action) error { } balanceType := *a.Balance.Type if acc.BalanceMap == nil { - acc.BalanceMap = make(map[string]BalanceChain, 1) + acc.BalanceMap = make(map[string]Balances, 1) } var previousSharedGroups utils.StringMap // kept for comparison var balance *Balance @@ -174,7 +174,7 @@ func (ub *Account) debitBalanceAction(a *Action, reset bool) error { return errors.New("nil balance") } if ub.BalanceMap == nil { - ub.BalanceMap = make(map[string]BalanceChain, 1) + ub.BalanceMap = make(map[string]Balances, 1) } found := false balanceType := a.Balance.GetType() @@ -256,7 +256,7 @@ func (ub *Account) enableDisableBalanceAction(a *Action) error { } if ub.BalanceMap == nil { - ub.BalanceMap = make(map[string]BalanceChain) + ub.BalanceMap = make(map[string]Balances) } found := false id := a.BalanceType @@ -276,13 +276,13 @@ func (ub *Account) enableDisableBalanceAction(a *Action) error { return nil } */ -func (ub *Account) getBalancesForPrefix(prefix, category, direction, tor string, sharedGroup string) BalanceChain { - var balances BalanceChain +func (ub *Account) getBalancesForPrefix(prefix, category, direction, tor string, sharedGroup string) Balances { + var balances Balances balances = append(balances, ub.BalanceMap[tor]...) if tor != utils.MONETARY && tor != utils.GENERIC { balances = append(balances, ub.BalanceMap[utils.GENERIC]...) } - var usefulBalances BalanceChain + var usefulBalances Balances for _, b := range balances { if b.Disabled { continue @@ -333,7 +333,7 @@ func (ub *Account) getBalancesForPrefix(prefix, category, direction, tor string, } // like getBalancesForPrefix but expanding shared balances -func (account *Account) getAlldBalancesForPrefix(destination, category, direction, balanceType string) (bc BalanceChain) { +func (account *Account) getAlldBalancesForPrefix(destination, category, direction, balanceType string) (bc Balances) { balances := account.getBalancesForPrefix(destination, category, direction, balanceType, "") for _, b := range balances { if len(b.SharedGroups) > 0 { @@ -526,7 +526,7 @@ func (ub *Account) GetDefaultMoneyBalance() *Balance { ID: utils.META_DEFAULT, } // minimum weight if ub.BalanceMap == nil { - ub.BalanceMap = make(map[string]BalanceChain) + ub.BalanceMap = make(map[string]Balances) } ub.BalanceMap[utils.MONETARY] = append(ub.BalanceMap[utils.MONETARY], defaultBalance) return defaultBalance @@ -788,7 +788,7 @@ type TenantAccount struct { func (acc *Account) Clone() *Account { newAcc := &Account{ ID: acc.ID, - BalanceMap: make(map[string]BalanceChain, len(acc.BalanceMap)), + BalanceMap: make(map[string]Balances, len(acc.BalanceMap)), UnitCounters: nil, // not used when cloned (dryRun) ActionTriggers: nil, // not used when cloned (dryRun) AllowNegative: acc.AllowNegative, @@ -800,7 +800,7 @@ func (acc *Account) Clone() *Account { return newAcc } -func (acc *Account) DebitConnectionFee(cc *CallCost, usefulMoneyBalances BalanceChain, count bool) { +func (acc *Account) DebitConnectionFee(cc *CallCost, usefulMoneyBalances Balances, count bool) { if cc.deductConnectFee { connectFee := cc.GetConnectFee() //log.Print("CONNECT FEE: %f", connectFee) @@ -874,12 +874,12 @@ func (acc *Account) AsOldStructure() interface{} { account *Account dirty bool } - type BalanceChain []*Balance + type Balances []*Balance type UnitsCounter struct { Direction string BalanceType string // Units float64 - Balances BalanceChain // first balance is the general one (no destination) + Balances Balances // first balance is the general one (no destination) } type ActionTrigger struct { Id string @@ -906,7 +906,7 @@ func (acc *Account) AsOldStructure() interface{} { type ActionTriggers []*ActionTrigger type Account struct { Id string - BalanceMap map[string]BalanceChain + BalanceMap map[string]Balances UnitCounters []*UnitsCounter ActionTriggers ActionTriggers AllowNegative bool @@ -915,7 +915,7 @@ func (acc *Account) AsOldStructure() interface{} { result := &Account{ Id: utils.OUT + ":" + acc.ID, - BalanceMap: make(map[string]BalanceChain, len(acc.BalanceMap)), + BalanceMap: make(map[string]Balances, len(acc.BalanceMap)), UnitCounters: make([]*UnitsCounter, len(acc.UnitCounters)), ActionTriggers: make(ActionTriggers, len(acc.ActionTriggers)), AllowNegative: acc.AllowNegative, @@ -928,7 +928,7 @@ func (acc *Account) AsOldStructure() interface{} { } result.UnitCounters[i] = &UnitsCounter{ BalanceType: balanceType, - Balances: make(BalanceChain, len(uc.Counters)), + Balances: make(Balances, len(uc.Counters)), } if len(uc.Counters) > 0 { result.UnitCounters[i].Direction = (*uc.Counters[0].Filter.Directions).String() @@ -971,7 +971,7 @@ func (acc *Account) AsOldStructure() interface{} { BalanceSharedGroup: b.SharedGroups.String(), BalanceDisabled: b.Disabled, Weight: at.Weight, - ActionsId: at.ActionsId, + ActionsId: at.ActionsID, MinQueuedItems: at.MinQueuedItems, Executed: at.Executed, } @@ -979,7 +979,7 @@ func (acc *Account) AsOldStructure() interface{} { for key, values := range acc.BalanceMap { if len(values) > 0 { key += utils.OUT - result.BalanceMap[key] = make(BalanceChain, len(values)) + result.BalanceMap[key] = make(Balances, len(values)) for i, b := range values { result.BalanceMap[key][i] = &Balance{ Uuid: b.Uuid, diff --git a/engine/account_test.go b/engine/account_test.go index 5ea6d4e2a..3df23e059 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -66,13 +66,13 @@ func TestBalanceStoreRestoreZero(t *testing.T) { } } -func TestBalanceChainStoreRestore(t *testing.T) { - bc := BalanceChain{&Balance{Value: 14, ExpirationDate: time.Date(2013, time.July, 15, 17, 48, 0, 0, time.UTC)}, &Balance{Value: 1024}} +func TestBalancesStoreRestore(t *testing.T) { + bc := Balances{&Balance{Value: 14, ExpirationDate: time.Date(2013, time.July, 15, 17, 48, 0, 0, time.UTC)}, &Balance{Value: 1024}} output, err := marsh.Marshal(bc) if err != nil { t.Error("Error storing balance chain: ", err) } - bc1 := BalanceChain{} + bc1 := Balances{} err = marsh.Unmarshal(output, &bc1) if err != nil { t.Error("Error restoring balance chain: ", err) @@ -85,7 +85,7 @@ func TestBalanceChainStoreRestore(t *testing.T) { func TestAccountStorageStoreRestore(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{utils.VOICE: Balances{b1, b2}, utils.MONETARY: Balances{&Balance{Value: 21}}}} accountingStorage.SetAccount(rifsBalance) ub1, err := accountingStorage.GetAccount("other") if err != nil || !ub1.BalanceMap[utils.MONETARY].Equal(rifsBalance.BalanceMap[utils.MONETARY]) { @@ -97,7 +97,7 @@ func TestAccountStorageStoreRestore(t *testing.T) { func TestGetSecondsForPrefix(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - ub1 := &Account{ID: "CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 200}}}} + ub1 := &Account{ID: "CUSTOMER_1:rif", BalanceMap: map[string]Balances{utils.VOICE: Balances{b1, b2}, utils.MONETARY: Balances{&Balance{Value: 200}}}} cd := &CallDescriptor{ Category: "0", Tenant: "vdf", @@ -123,9 +123,9 @@ func TestGetSpecialPricedSeconds(t *testing.T) { ub1 := &Account{ ID: "OUT:CUSTOMER_1:rif", - BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{b1, b2}, - utils.MONETARY: BalanceChain{&Balance{Value: 21}}, + BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1, b2}, + utils.MONETARY: Balances{&Balance{Value: 21}}, }, } cd := &CallDescriptor{ @@ -152,7 +152,7 @@ func TestGetSpecialPricedSeconds(t *testing.T) { func TestAccountStorageStore(t *testing.T) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{utils.VOICE: Balances{b1, b2}, utils.MONETARY: Balances{&Balance{Value: 21}}}} accountingStorage.SetAccount(rifsBalance) result, err := accountingStorage.GetAccount(rifsBalance.ID) if err != nil || rifsBalance.ID != result.ID || @@ -188,7 +188,7 @@ func TestDebitCreditZeroSecond(t *testing.T) { TOR: utils.VOICE, testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1}, utils.MONETARY: BalanceChain{&Balance{Categories: utils.NewStringMap("0"), Value: 21}}}} + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{utils.VOICE: Balances{b1}, utils.MONETARY: Balances{&Balance{Categories: utils.NewStringMap("0"), Value: 21}}}} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) if err != nil { @@ -228,9 +228,9 @@ func TestDebitCreditZeroMinute(t *testing.T) { TOR: utils.VOICE, testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{b1}, - utils.MONETARY: BalanceChain{&Balance{Value: 21}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1}, + utils.MONETARY: Balances{&Balance{Value: 21}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -275,9 +275,9 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) { DurationIndex: cc.Timespans[0].GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{b1, b2}, - utils.MONETARY: BalanceChain{&Balance{Value: 21}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1, b2}, + utils.MONETARY: Balances{&Balance{Value: 21}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -327,8 +327,8 @@ func TestDebitCreditNoCredit(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{b1}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -378,9 +378,9 @@ func TestDebitCreditHasCredit(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{b1}, - utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 110}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1}, + utils.MONETARY: Balances{&Balance{Uuid: "moneya", Value: 110}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -426,9 +426,9 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{b1}, - utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 50}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1}, + utils.MONETARY: Balances{&Balance{Uuid: "moneya", Value: 50}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -479,8 +479,8 @@ func TestDebitCreditMoreTimespans(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{b1}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -528,8 +528,8 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{b1, b2}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1, b2}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -578,8 +578,8 @@ func TestDebitCreditNoConectFeeCredit(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{b1}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -622,8 +622,8 @@ func TestDebitCreditMoneyOnly(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Uuid: "money", Value: 50}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{Uuid: "money", Value: 50}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -675,9 +675,9 @@ func TestDebitCreditSubjectMinutes(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{b1}, - utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 350}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1}, + utils.MONETARY: Balances{&Balance{Uuid: "moneya", Value: 350}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -730,8 +730,8 @@ func TestDebitCreditSubjectMoney(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 75, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "minu"}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{Uuid: "moneya", Value: 75, DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "minu"}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -780,9 +780,9 @@ func TestDebitCreditSubjectMoney(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{b1}, - utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 19500, RatingSubject: "minu"}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1}, + utils.MONETARY: Balances{&Balance{Uuid: "moneya", Value: 19500, RatingSubject: "minu"}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -811,7 +811,7 @@ func TestAccountdebitBalance(t *testing.T) { ub := &Account{ ID: "rif", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]Balances{utils.SMS: Balances{&Balance{Value: 14}}, utils.DATA: Balances{&Balance{Value: 1204}}, utils.VOICE: Balances{&Balance{Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } newMb := &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), @@ -830,7 +830,7 @@ func TestAccountdebitBalance(t *testing.T) { ub := &Account{ Id: "rif", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1204}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]Balances{utils.SMS: Balances{&Balance{Value: 14}}, utils.DATA: Balances{&Balance{Value: 1204}}, utils.VOICE: Balances{&Balance{Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } newMb := &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), @@ -853,7 +853,7 @@ func TestAccountdebitBalanceExists(t *testing.T) { ub := &Account{ ID: "rif", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Value: 15, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]Balances{utils.SMS: Balances{&Balance{Value: 14}}, utils.DATA: Balances{&Balance{Value: 1024}}, utils.VOICE: Balances{&Balance{Value: 15, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } newMb := &BalanceFilter{ Value: utils.Float64Pointer(-10), @@ -873,7 +873,7 @@ func TestAccountAddMinuteNil(t *testing.T) { ub := &Account{ ID: "rif", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14}}, utils.DATA: BalanceChain{&Balance{Value: 1024}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]Balances{utils.SMS: Balances{&Balance{Value: 14}}, utils.DATA: Balances{&Balance{Value: 1024}}, utils.VOICE: Balances{&Balance{Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, } ub.debitBalanceAction(nil, false) if len(ub.BalanceMap[utils.VOICE]) != 2 { @@ -921,9 +921,9 @@ func TestAccountAddMinutBucketEmpty(t *testing.T) { func TestAccountExecuteTriggeredActions(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.StringMap{utils.OUT: true})}}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsID: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { @@ -945,9 +945,9 @@ func TestAccountExecuteTriggeredActions(t *testing.T) { func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: Balances{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Directions: utils.NewStringMap(utils.OUT), Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Filter: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, Value: 1.0}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ActionsID: "TEST_ACTIONS"}}, } ub.countUnits(1, utils.MONETARY, nil, nil) if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { @@ -958,9 +958,9 @@ func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { ub := &Account{ ID: "TEST_UB_OREDER", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Type: utils.StringPointer(utils.MONETARY)}}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsID: "TEST_ACTIONS_ORDER"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) @@ -973,10 +973,10 @@ func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { func TestAccountExecuteTriggeredDayWeek(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{UniqueID: "day_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, - &ActionTrigger{UniqueID: "week_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{UniqueID: "day_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsID: "TEST_ACTIONS"}, + &ActionTrigger{UniqueID: "week_trigger", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 100, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ActionsID: "TEST_ACTIONS"}, }, } ub.InitCounters() @@ -1001,9 +1001,9 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { func TestAccountExpActionTrigger(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100, ExpirationDate: time.Date(2015, time.November, 9, 9, 48, 0, 0, time.UTC)}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100, ExpirationDate: time.Date(2015, time.November, 9, 9, 48, 0, 0, time.UTC)}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsID: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1019,9 +1019,9 @@ func TestAccountExpActionTrigger(t *testing.T) { func TestAccountExpActionTriggerNotActivated(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", ActivationDate: time.Date(2116, 2, 5, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsID: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1037,9 +1037,9 @@ func TestAccountExpActionTriggerNotActivated(t *testing.T) { func TestAccountExpActionTriggerExpired(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}, Directions: utils.StringMap{utils.OUT: true}}, &Balance{Weight: 10, DestinationIDs: utils.StringMap{"RET": true}}}}, ActionTriggers: ActionTriggers{ - &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsId: "TEST_ACTIONS"}, + &ActionTrigger{ID: "check expired balances", ExpirationDate: time.Date(2016, 2, 4, 18, 0, 0, 0, time.UTC), Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 10, ThresholdType: utils.TRIGGER_BALANCE_EXPIRED, ActionsID: "TEST_ACTIONS"}, }, } ub.ExecuteActionTriggers(nil) @@ -1055,10 +1055,10 @@ func TestAccountExpActionTriggerExpired(t *testing.T) { func TestCleanExpired(t *testing.T) { ub := &Account{ ID: "TEST_UB_OREDER", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{utils.MONETARY: Balances{ &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}, &Balance{ExpirationDate: time.Date(2013, 7, 18, 14, 33, 0, 0, time.UTC)}, - &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}}, utils.VOICE: BalanceChain{ + &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}}, utils.VOICE: Balances{ &Balance{ExpirationDate: time.Date(2013, 7, 18, 14, 33, 0, 0, time.UTC)}, &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}, }}, @@ -1129,11 +1129,11 @@ func TestAccountUnitCountingOutboundInbound(t *testing.T) { func TestAccountRefund(t *testing.T) { ub := &Account{ - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{Uuid: "moneya", Value: 100}, }, - utils.VOICE: BalanceChain{ + utils.VOICE: Balances{ &Balance{Uuid: "minutea", Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, &Balance{Uuid: "minuteb", Value: 10, DestinationIDs: utils.StringMap{"RET": true}}, }, @@ -1181,11 +1181,11 @@ func TestDebitShared(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rif := &Account{ID: "rif", BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 0, SharedGroups: utils.NewStringMap("SG_TEST")}}, + rif := &Account{ID: "rif", BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{Uuid: "moneya", Value: 0, SharedGroups: utils.NewStringMap("SG_TEST")}}, }} - groupie := &Account{ID: "groupie", BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Uuid: "moneyc", Value: 130, SharedGroups: utils.NewStringMap("SG_TEST")}}, + groupie := &Account{ID: "groupie", BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{Uuid: "moneyc", Value: 130, SharedGroups: utils.NewStringMap("SG_TEST")}}, }} sg := &SharedGroup{Id: "SG_TEST", MemberIds: utils.NewStringMap(rif.ID, groupie.ID), AccountParameters: map[string]*SharingParameters{"*any": &SharingParameters{Strategy: STRATEGY_MINE_RANDOM}}} @@ -1251,11 +1251,11 @@ func TestMaxDurationShared(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rif := &Account{ID: "rif", BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 0, SharedGroups: utils.NewStringMap("SG_TEST")}}, + rif := &Account{ID: "rif", BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{Uuid: "moneya", Value: 0, SharedGroups: utils.NewStringMap("SG_TEST")}}, }} - groupie := &Account{ID: "groupie", BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Uuid: "moneyc", Value: 130, SharedGroups: utils.NewStringMap("SG_TEST")}}, + groupie := &Account{ID: "groupie", BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{Uuid: "moneyc", Value: 130, SharedGroups: utils.NewStringMap("SG_TEST")}}, }} sg := &SharedGroup{Id: "SG_TEST", MemberIds: utils.NewStringMap(rif.ID, groupie.ID), AccountParameters: map[string]*SharingParameters{"*any": &SharingParameters{Strategy: STRATEGY_MINE_RANDOM}}} @@ -1286,8 +1286,8 @@ func TestMaxDurationConnectFeeOnly(t *testing.T) { TOR: utils.VOICE, DurationIndex: 600, } - rif := &Account{ID: "rif", BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Uuid: "moneya", Value: 0.2}}, + rif := &Account{ID: "rif", BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{Uuid: "moneya", Value: 0.2}}, }} duration, err := cd.getMaxSessionDuration(rif) @@ -1324,9 +1324,9 @@ func TestDebitSMS(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.SMS: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, - utils.MONETARY: BalanceChain{&Balance{Value: 21}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.SMS: Balances{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, + utils.MONETARY: Balances{&Balance{Value: 21}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -1367,9 +1367,9 @@ func TestDebitGeneric(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, - utils.MONETARY: BalanceChain{&Balance{Value: 21}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.GENERIC: Balances{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, + utils.MONETARY: Balances{&Balance{Value: 21}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -1410,9 +1410,9 @@ func TestDebitGenericBalance(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}}}, - utils.MONETARY: BalanceChain{&Balance{Value: 21}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.GENERIC: Balances{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}}}, + utils.MONETARY: Balances{&Balance{Value: 21}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -1453,9 +1453,9 @@ func TestDebitGenericBalanceWithRatingSubject(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.GENERIC: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}, RatingSubject: "free"}}, - utils.MONETARY: BalanceChain{&Balance{Value: 21}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.GENERIC: Balances{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}, Factor: ValueFactor{utils.VOICE: 60.0}, RatingSubject: "free"}}, + utils.MONETARY: Balances{&Balance{Value: 21}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -1503,9 +1503,9 @@ func TestDebitDataUnits(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.DATA: BalanceChain{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, - utils.MONETARY: BalanceChain{&Balance{Value: 21}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.DATA: Balances{&Balance{Uuid: "testm", Value: 100, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, + utils.MONETARY: Balances{&Balance{Value: 21}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -1557,9 +1557,9 @@ func TestDebitDataMoney(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{ - utils.DATA: BalanceChain{&Balance{Uuid: "testm", Value: 0, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, - utils.MONETARY: BalanceChain{&Balance{Value: 160}}, + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ + utils.DATA: Balances{&Balance{Uuid: "testm", Value: 0, Weight: 5, DestinationIDs: utils.StringMap{"NAT": true}}}, + utils.MONETARY: Balances{&Balance{Value: 160}}, }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) @@ -1582,7 +1582,7 @@ func TestAccountGetDefaultMoneyBalanceEmpty(t *testing.T) { func TestAccountGetDefaultMoneyBalance(t *testing.T) { acc := &Account{} - acc.BalanceMap = make(map[string]BalanceChain) + acc.BalanceMap = make(map[string]Balances) tag := utils.MONETARY acc.BalanceMap[tag] = append(acc.BalanceMap[tag], &Balance{Weight: 10}) defBal := acc.GetDefaultMoneyBalance() @@ -1755,7 +1755,7 @@ func BenchmarkGetSecondForPrefix(b *testing.B) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - ub1 := &Account{ID: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} + ub1 := &Account{ID: "other", BalanceMap: map[string]Balances{utils.VOICE: Balances{b1, b2}, utils.MONETARY: Balances{&Balance{Value: 21}}}} cd := &CallDescriptor{ Destination: "0723", } @@ -1768,7 +1768,7 @@ func BenchmarkGetSecondForPrefix(b *testing.B) { func BenchmarkAccountStorageStoreRestore(b *testing.B) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - rifsBalance := &Account{ID: "other", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} + rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{utils.VOICE: Balances{b1, b2}, utils.MONETARY: Balances{&Balance{Value: 21}}}} for i := 0; i < b.N; i++ { accountingStorage.SetAccount(rifsBalance) accountingStorage.GetAccount(rifsBalance.ID) @@ -1778,7 +1778,7 @@ func BenchmarkAccountStorageStoreRestore(b *testing.B) { func BenchmarkGetSecondsForPrefix(b *testing.B) { b1 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}} b2 := &Balance{Value: 100, Weight: 20, DestinationIDs: utils.StringMap{"RET": true}} - ub1 := &Account{ID: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b1, b2}, utils.MONETARY: BalanceChain{&Balance{Value: 21}}}} + ub1 := &Account{ID: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]Balances{utils.VOICE: Balances{b1, b2}, utils.MONETARY: Balances{&Balance{Value: 21}}}} cd := &CallDescriptor{ Destination: "0723", } diff --git a/engine/action.go b/engine/action.go index 26e20a5fa..fff13356c 100644 --- a/engine/action.go +++ b/engine/action.go @@ -38,9 +38,8 @@ import ( Structure to be filled for each tariff plan with the bonus value for received calls minutes. */ type Action struct { - Id string - ActionType string - //BalanceType string + Id string + ActionType string ExtraParameters string Filter string ExpirationString string // must stay as string because it can have relative values like 1month @@ -326,7 +325,7 @@ func topupResetAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actio return errors.New("nil account") } if ub.BalanceMap == nil { // Init the map since otherwise will get error if nil - ub.BalanceMap = make(map[string]BalanceChain, 0) + ub.BalanceMap = make(map[string]Balances, 0) } c := a.Clone() genericMakeNegative(c) @@ -347,7 +346,7 @@ func debitResetAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Actio return errors.New("nil account") } if ub.BalanceMap == nil { // Init the map since otherwise will get error if nil - ub.BalanceMap = make(map[string]BalanceChain, 0) + ub.BalanceMap = make(map[string]Balances, 0) } return genericDebit(ub, a, true) } @@ -381,7 +380,7 @@ func genericDebit(ub *Account, a *Action, reset bool) (err error) { return errors.New("nil account") } if ub.BalanceMap == nil { - ub.BalanceMap = make(map[string]BalanceChain) + ub.BalanceMap = make(map[string]Balances) } return ub.debitBalanceAction(a, reset) } @@ -412,7 +411,7 @@ func disableUserAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Acti func genericReset(ub *Account) error { for k, _ := range ub.BalanceMap { - ub.BalanceMap[k] = BalanceChain{&Balance{Value: 0}} + ub.BalanceMap[k] = Balances{&Balance{Value: 0}} } ub.InitCounters() ub.ResetActionTriggers(nil) diff --git a/engine/action_trigger.go b/engine/action_trigger.go index cca4336c1..dc3a5423c 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -40,7 +40,7 @@ type ActionTrigger struct { //BalanceType string // *monetary/*voice etc Balance *BalanceFilter Weight float64 - ActionsId string + ActionsID string MinQueuedItems int // Trigger actions only if this number is hit (stats only) Executed bool lastExecutionTime time.Time @@ -57,7 +57,7 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro } // does NOT need to Lock() because it is triggered from a method that took the Lock var aac Actions - aac, err = ratingStorage.GetActions(at.ActionsId, false) + aac, err = ratingStorage.GetActions(at.ActionsID, false) aac.Sort() if err != nil { utils.Logger.Err(fmt.Sprintf("Failed to get actions: %v", err)) diff --git a/engine/actions_test.go b/engine/actions_test.go index 64579af8f..416fbc9ac 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -505,13 +505,13 @@ func TestActionPlansRemoveMember(t *testing.T) { Uuid: "some uuid", Id: "test", AccountIDs: []string{"one", "two", "three"}, - ActionsId: "TEST_ACTIONS", + ActionsID: "TEST_ACTIONS", } at2 := &ActionPlan{ Uuid: "some uuid22", Id: "test2", AccountIDs: []string{"three", "four"}, - ActionsId: "TEST_ACTIONS2", + ActionsID: "TEST_ACTIONS2", } ats := ActionPlans{at1, at2} if outAts := RemActionPlan(ats, "", "four"); len(outAts[1].AccountIds) != 1 { @@ -693,9 +693,9 @@ func TestActionTriggers(t *testing.T) { func TestActionResetTriggres(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 10}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { @@ -706,9 +706,9 @@ func TestActionResetTriggres(t *testing.T) { func TestActionResetTriggresExecutesThem(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 10}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.BalanceMap[utils.MONETARY][0].GetValue() == 12 { @@ -719,9 +719,9 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { func TestActionResetTriggresActionFilter(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 10}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 10}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}}, nil) if ub.ActionTriggers[0].Executed == false || ub.ActionTriggers[1].Executed == false { @@ -732,9 +732,9 @@ func TestActionResetTriggresActionFilter(t *testing.T) { func TestActionSetPostpaid(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } allowNegativeAction(ub, nil, nil, nil) if !ub.AllowNegative { @@ -746,9 +746,9 @@ func TestActionSetPrepaid(t *testing.T) { ub := &Account{ ID: "TEST_UB", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } denyNegativeAction(ub, nil, nil, nil) if ub.AllowNegative { @@ -760,9 +760,9 @@ func TestActionResetPrepaid(t *testing.T) { ub := &Account{ ID: "TEST_UB", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) if !ub.AllowNegative || @@ -778,9 +778,9 @@ func TestActionResetPrepaid(t *testing.T) { func TestActionResetPostpaid(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } resetAccountAction(ub, nil, nil, nil) if ub.BalanceMap[utils.MONETARY].GetTotalValue() != 0 || @@ -794,9 +794,9 @@ func TestActionResetPostpaid(t *testing.T) { func TestActionTopupResetCredit(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) @@ -812,7 +812,7 @@ func TestActionTopupResetCredit(t *testing.T) { func TestActionTopupValueFactor(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{}, + BalanceMap: map[string]Balances{}, } a := &Action{ Balance: &BalanceFilter{ @@ -831,8 +831,8 @@ func TestActionTopupValueFactor(t *testing.T) { func TestActionTopupResetCreditId(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{Value: 100}, &Balance{ID: "TEST_B", Value: 15}, }, @@ -850,8 +850,8 @@ func TestActionTopupResetCreditId(t *testing.T) { func TestActionTopupResetCreditNoId(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{Value: 100, Directions: utils.NewStringMap(utils.OUT)}, &Balance{ID: "TEST_B", Value: 15, Directions: utils.NewStringMap(utils.OUT)}, }, @@ -869,11 +869,11 @@ func TestActionTopupResetCreditNoId(t *testing.T) { func TestActionTopupResetMinutes(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Value: 100}}, - utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{Value: 100}}, + utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) @@ -890,9 +890,9 @@ func TestActionTopupResetMinutes(t *testing.T) { func TestActionTopupCredit(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) @@ -908,9 +908,9 @@ func TestActionTopupCredit(t *testing.T) { func TestActionTopupMinutes(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) @@ -927,9 +927,9 @@ func TestActionTopupMinutes(t *testing.T) { func TestActionDebitCredit(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: utils.Float64Pointer(10), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) @@ -945,9 +945,9 @@ func TestActionDebitCredit(t *testing.T) { func TestActionDebitMinutes(t *testing.T) { ub := &Account{ ID: "TEST_UB", - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}, &ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: utils.Float64Pointer(5), Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) @@ -965,13 +965,13 @@ func TestActionResetAllCounters(t *testing.T) { ub := &Account{ ID: "TEST_UB", AllowNegative: true, - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Value: 100}}, - utils.VOICE: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{Value: 100}}, + utils.VOICE: Balances{ &Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT"), Directions: utils.NewStringMap(utils.OUT)}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET"), Directions: utils.NewStringMap(utils.OUT)}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(20)}, ActionsID: "TEST_ACTIONS", Executed: true}}, } ub.InitCounters() resetCountersAction(ub, nil, nil, nil) @@ -996,10 +996,10 @@ func TestActionResetCounterOnlyDefault(t *testing.T) { ub := &Account{ ID: "TEST_UB", AllowNegative: true, - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Value: 100}}, - utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{Value: 100}}, + utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}} ub.InitCounters() @@ -1028,9 +1028,9 @@ func TestActionResetCounterCredit(t *testing.T) { ub := &Account{ ID: "TEST_UB", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{Value: 100}}, utils.VOICE: BalanceChain{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{Value: 100}}, utils.VOICE: Balances{&Balance{Value: 10, Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}, utils.SMS: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{Value: 1, Filter: &BalanceFilter{Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}}}}}}, - ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, + ActionTriggers: ActionTriggers{&ActionTrigger{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}, ThresholdValue: 2, ActionsID: "TEST_ACTIONS", Executed: true}}, } a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}} resetCountersAction(ub, nil, a, nil) @@ -1053,9 +1053,9 @@ func TestActionTriggerLogging(t *testing.T) { }, ThresholdValue: 100.0, Weight: 10.0, - ActionsId: "TEST_ACTIONS", + ActionsID: "TEST_ACTIONS", } - as, err := ratingStorage.GetActions(at.ActionsId, false) + as, err := ratingStorage.GetActions(at.ActionsID, false) if err != nil { t.Error("Error getting actions for the action timing: ", as, err) } @@ -1274,7 +1274,7 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) { } func TestActionSetDDestination(t *testing.T) { - acc := &Account{BalanceMap: map[string]BalanceChain{utils.MONETARY: BalanceChain{&Balance{DestinationIDs: utils.NewStringMap("*ddc_test")}}}} + acc := &Account{BalanceMap: map[string]Balances{utils.MONETARY: Balances{&Balance{DestinationIDs: utils.NewStringMap("*ddc_test")}}}} origD := &Destination{Id: "*ddc_test", Prefixes: []string{"111", "222"}} ratingStorage.SetDestination(origD) ratingStorage.CacheRatingPrefixValues(map[string][]string{utils.DESTINATION_PREFIX: []string{utils.DESTINATION_PREFIX + "*ddc_test"}}) @@ -1319,8 +1319,8 @@ func TestActionSetDDestination(t *testing.T) { func TestActionTransactionFuncType(t *testing.T) { err := accountingStorage.SetAccount(&Account{ ID: "cgrates.org:trans", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{ Value: 10, }}, }, @@ -1355,8 +1355,8 @@ func TestActionTransactionFuncType(t *testing.T) { func TestActionTransactionBalanceType(t *testing.T) { err := accountingStorage.SetAccount(&Account{ ID: "cgrates.org:trans", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{ Value: 10, }}, }, @@ -1391,8 +1391,8 @@ func TestActionTransactionBalanceType(t *testing.T) { func TestActionTransactionBalanceNotType(t *testing.T) { err := accountingStorage.SetAccount(&Account{ ID: "cgrates.org:trans", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{ Value: 10, }}, }, @@ -1427,8 +1427,8 @@ func TestActionTransactionBalanceNotType(t *testing.T) { func TestActionWithExpireWithoutExpire(t *testing.T) { err := accountingStorage.SetAccount(&Account{ ID: "cgrates.org:exp", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{ Value: 10, }}, }, @@ -1471,8 +1471,8 @@ func TestActionWithExpireWithoutExpire(t *testing.T) { func TestActionRemoveBalance(t *testing.T) { err := accountingStorage.SetAccount(&Account{ ID: "cgrates.org:rembal", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{ Value: 10, }, @@ -1519,8 +1519,8 @@ func TestActionTransferMonetaryDefault(t *testing.T) { err := accountingStorage.SetAccount( &Account{ ID: "cgrates.org:trans", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{ Uuid: utils.GenUUID(), ID: utils.META_DEFAULT, @@ -1575,8 +1575,8 @@ func TestActionTransferMonetaryDefaultFilter(t *testing.T) { err := accountingStorage.SetAccount( &Account{ ID: "cgrates.org:trans", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{ Uuid: utils.GenUUID(), ID: utils.META_DEFAULT, @@ -1636,8 +1636,8 @@ func TestActionConditionalTopup(t *testing.T) { err := accountingStorage.SetAccount( &Account{ ID: "cgrates.org:cond", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{ Uuid: utils.GenUUID(), ID: utils.META_DEFAULT, @@ -1700,8 +1700,8 @@ func TestActionConditionalTopupNoMatch(t *testing.T) { err := accountingStorage.SetAccount( &Account{ ID: "cgrates.org:cond", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{ Uuid: utils.GenUUID(), ID: utils.META_DEFAULT, @@ -1763,8 +1763,8 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) { err := accountingStorage.SetAccount( &Account{ ID: "cgrates.org:cond", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{ Uuid: utils.GenUUID(), Value: 1, @@ -1776,7 +1776,7 @@ func TestActionConditionalTopupExistingBalance(t *testing.T) { Weight: 20, }, }, - utils.VOICE: BalanceChain{ + utils.VOICE: Balances{ &Balance{ Uuid: utils.GenUUID(), Value: 10, @@ -1827,8 +1827,8 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { err := accountingStorage.SetAccount( &Account{ ID: "cgrates.org:af", - BalanceMap: map[string]BalanceChain{ - "*data": BalanceChain{ + BalanceMap: map[string]Balances{ + "*data": Balances{ &Balance{ Uuid: "fc927edb-1bd6-425e-a2a3-9fd8bafaa524", ID: "for_v3hsillmilld500m_data_500_m", @@ -1840,14 +1840,14 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { }, }, }, - "*monetary": BalanceChain{ + "*monetary": Balances{ &Balance{ Uuid: "9fa1847a-f36a-41a7-8ec0-dfaab370141e", ID: "*default", Value: -1.95001, }, }, - "*sms": BalanceChain{ + "*sms": Balances{ &Balance{ Uuid: "d348d15d-2988-4ee4-b847-6a552f94e2ec", ID: "for_v3hsillmilld500m_mms_ill", @@ -1875,7 +1875,7 @@ func TestActionConditionalDisabledIfNegative(t *testing.T) { }, }, }, - "*voice": BalanceChain{ + "*voice": Balances{ &Balance{ Uuid: "079ab190-77f4-44f3-9c6f-3a0dd1a59dfd", ID: "for_v3hsillmilld500m_voice_3_h", @@ -1980,8 +1980,8 @@ func TestActionSetBalance(t *testing.T) { err := accountingStorage.SetAccount( &Account{ ID: "cgrates.org:setb", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{ ID: "m1", Uuid: utils.GenUUID(), @@ -1995,7 +1995,7 @@ func TestActionSetBalance(t *testing.T) { Weight: 20, }, }, - utils.VOICE: BalanceChain{ + utils.VOICE: Balances{ &Balance{ ID: "v1", Uuid: utils.GenUUID(), diff --git a/engine/balances.go b/engine/balances.go index 43b5cb207..da0853858 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -305,7 +305,7 @@ func (b *Balance) SetDirty() { b.dirty = true } -func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances BalanceChain, count bool, dryRun, debitConnectFee bool) (cc *CallCost, err error) { +func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Balances, count bool, dryRun, debitConnectFee bool) (cc *CallCost, err error) { if !b.IsActiveAt(cd.TimeStart) || b.GetValue() <= 0 { return } @@ -480,7 +480,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala return } -func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances BalanceChain, count bool, dryRun, debitConnectFee bool) (cc *CallCost, err error) { +func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Balances, count bool, dryRun, debitConnectFee bool) (cc *CallCost, err error) { if !b.IsActiveAt(cd.TimeStart) || b.GetValue() <= 0 { return } @@ -581,28 +581,28 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala /* Structure to store minute buckets according to weight, precision or price. */ -type BalanceChain []*Balance +type Balances []*Balance -func (bc BalanceChain) Len() int { +func (bc Balances) Len() int { return len(bc) } -func (bc BalanceChain) Swap(i, j int) { +func (bc Balances) Swap(i, j int) { bc[i], bc[j] = bc[j], bc[i] } // we need the better ones at the beginning -func (bc BalanceChain) Less(j, i int) bool { +func (bc Balances) Less(j, i int) bool { return bc[i].precision < bc[j].precision || (bc[i].precision == bc[j].precision && bc[i].Weight < bc[j].Weight) } -func (bc BalanceChain) Sort() { +func (bc Balances) Sort() { sort.Sort(bc) } -func (bc BalanceChain) GetTotalValue() (total float64) { +func (bc Balances) GetTotalValue() (total float64) { for _, b := range bc { if !b.IsExpired() && b.IsActive() { total += b.GetValue() @@ -611,7 +611,7 @@ func (bc BalanceChain) GetTotalValue() (total float64) { return } -func (bc BalanceChain) Equal(o BalanceChain) bool { +func (bc Balances) Equal(o Balances) bool { if len(bc) != len(o) { return false } @@ -625,15 +625,15 @@ func (bc BalanceChain) Equal(o BalanceChain) bool { return true } -func (bc BalanceChain) Clone() BalanceChain { - var newChain BalanceChain +func (bc Balances) Clone() Balances { + var newChain Balances for _, b := range bc { newChain = append(newChain, b.Clone()) } return newChain } -func (bc BalanceChain) GetBalance(uuid string) *Balance { +func (bc Balances) GetBalance(uuid string) *Balance { for _, balance := range bc { if balance.Uuid == uuid { return balance @@ -642,7 +642,7 @@ func (bc BalanceChain) GetBalance(uuid string) *Balance { return nil } -func (bc BalanceChain) HasBalance(balance *Balance) bool { +func (bc Balances) HasBalance(balance *Balance) bool { for _, b := range bc { if b.Equal(balance) { return true @@ -651,7 +651,7 @@ func (bc BalanceChain) HasBalance(balance *Balance) bool { return false } -func (bc BalanceChain) SaveDirtyBalances(acc *Account) { +func (bc Balances) SaveDirtyBalances(acc *Account) { savedAccounts := make(map[string]bool) for _, b := range bc { if b.dirty { diff --git a/engine/balances_test.go b/engine/balances_test.go index 7c9069bc6..83c1abb1d 100644 --- a/engine/balances_test.go +++ b/engine/balances_test.go @@ -27,7 +27,7 @@ import ( func TestBalanceSortPrecision(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 2} mb2 := &Balance{Weight: 2, precision: 1} - var bs BalanceChain + var bs Balances bs = append(bs, mb2, mb1) bs.Sort() if bs[0] != mb1 || bs[1] != mb2 { @@ -38,7 +38,7 @@ func TestBalanceSortPrecision(t *testing.T) { func TestBalanceSortPrecisionWeightEqual(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 2} mb2 := &Balance{Weight: 1, precision: 1} - var bs BalanceChain + var bs Balances bs = append(bs, mb2, mb1) bs.Sort() if bs[0] != mb1 || bs[1] != mb2 { @@ -49,7 +49,7 @@ func TestBalanceSortPrecisionWeightEqual(t *testing.T) { func TestBalanceSortPrecisionWeightGreater(t *testing.T) { mb1 := &Balance{Weight: 2, precision: 2} mb2 := &Balance{Weight: 1, precision: 1} - var bs BalanceChain + var bs Balances bs = append(bs, mb2, mb1) bs.Sort() if bs[0] != mb1 || bs[1] != mb2 { @@ -60,7 +60,7 @@ func TestBalanceSortPrecisionWeightGreater(t *testing.T) { func TestBalanceSortWeight(t *testing.T) { mb1 := &Balance{Weight: 2, precision: 1} mb2 := &Balance{Weight: 1, precision: 1} - var bs BalanceChain + var bs Balances bs = append(bs, mb2, mb1) bs.Sort() if bs[0] != mb1 || bs[1] != mb2 { @@ -71,7 +71,7 @@ func TestBalanceSortWeight(t *testing.T) { func TestBalanceSortWeightLess(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1} mb2 := &Balance{Weight: 2, precision: 1} - var bs BalanceChain + var bs Balances bs = append(bs, mb2, mb1) bs.Sort() if bs[0] != mb2 || bs[1] != mb1 { diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 8e1fe8ac0..617a41e55 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -52,52 +52,52 @@ func populateDB() { minu := &Account{ ID: "vdf:minu", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{&Balance{Value: 50}}, - utils.VOICE: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{&Balance{Value: 50}}, + utils.VOICE: Balances{ &Balance{Value: 200, DestinationIDs: utils.NewStringMap("NAT"), Weight: 10}, &Balance{Value: 100, DestinationIDs: utils.NewStringMap("RET"), Weight: 20}, }}, } broker := &Account{ ID: "vdf:broker", - BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.VOICE: Balances{ &Balance{Value: 20, DestinationIDs: utils.NewStringMap("NAT"), Weight: 10, RatingSubject: "rif"}, &Balance{Value: 100, DestinationIDs: utils.NewStringMap("RET"), Weight: 20}, }}, } luna := &Account{ ID: "vdf:luna", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{Value: 0, Weight: 20}, }}, } // this is added to test if csv load tests account will not overwrite balances minitsboy := &Account{ ID: "vdf:minitsboy", - BalanceMap: map[string]BalanceChain{ - utils.VOICE: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.VOICE: Balances{ &Balance{Value: 20, DestinationIDs: utils.NewStringMap("NAT"), Weight: 10, RatingSubject: "rif"}, &Balance{Value: 100, DestinationIDs: utils.NewStringMap("RET"), Weight: 20}, }, - utils.MONETARY: BalanceChain{ + utils.MONETARY: Balances{ &Balance{Value: 100, Weight: 10}, }, }, } max := &Account{ ID: "cgrates.org:max", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{Value: 11, Weight: 20}, }}, } money := &Account{ ID: "cgrates.org:money", - BalanceMap: map[string]BalanceChain{ - utils.MONETARY: BalanceChain{ + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ &Balance{Value: 10000, Weight: 10}, }}, } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 5e1c10b32..0e0fdc49e 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -1066,7 +1066,7 @@ func TestLoadActionTriggers(t *testing.T) { Blocker: nil, }, Weight: 10, - ActionsId: "SOME_1", + ActionsID: "SOME_1", Executed: false, } if !reflect.DeepEqual(atr, expected) { @@ -1087,7 +1087,7 @@ func TestLoadActionTriggers(t *testing.T) { SharedGroups: nil, }, Weight: 10, - ActionsId: "SOME_2", + ActionsID: "SOME_2", Executed: false, } if !reflect.DeepEqual(atr, expected) { diff --git a/engine/responder_test.go b/engine/responder_test.go index 7fc5d06c5..34b573151 100644 --- a/engine/responder_test.go +++ b/engine/responder_test.go @@ -73,8 +73,8 @@ func TestResponderGetDerivedMaxSessionTime(t *testing.T) { } b10 := &Balance{Value: 10, Weight: 10, DestinationIDs: utils.NewStringMap("DE_TMOBILE")} b20 := &Balance{Value: 20, Weight: 10, DestinationIDs: utils.NewStringMap("DE_TMOBILE")} - rifsAccount := &Account{ID: utils.ConcatenatedKey(testTenant, "rif"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b10}}} - dansAccount := &Account{ID: utils.ConcatenatedKey(testTenant, "dan"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{b20}}} + rifsAccount := &Account{ID: utils.ConcatenatedKey(testTenant, "rif"), BalanceMap: map[string]Balances{utils.VOICE: Balances{b10}}} + dansAccount := &Account{ID: utils.ConcatenatedKey(testTenant, "dan"), BalanceMap: map[string]Balances{utils.VOICE: Balances{b20}}} if err := accountingStorage.SetAccount(rifsAccount); err != nil { t.Error(err) } @@ -437,8 +437,8 @@ func TestResponderGetLCR(t *testing.T) { } bRif12 := &Balance{Value: 40, Weight: 10, DestinationIDs: utils.NewStringMap(dstDe.Id)} bIvo12 := &Balance{Value: 60, Weight: 10, DestinationIDs: utils.NewStringMap(dstDe.Id)} - rif12sAccount := &Account{ID: utils.ConcatenatedKey("tenant12", "rif12"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{bRif12}}, AllowNegative: true} - ivo12sAccount := &Account{ID: utils.ConcatenatedKey("tenant12", "ivo12"), BalanceMap: map[string]BalanceChain{utils.VOICE: BalanceChain{bIvo12}}, AllowNegative: true} + rif12sAccount := &Account{ID: utils.ConcatenatedKey("tenant12", "rif12"), BalanceMap: map[string]Balances{utils.VOICE: Balances{bRif12}}, AllowNegative: true} + ivo12sAccount := &Account{ID: utils.ConcatenatedKey("tenant12", "ivo12"), BalanceMap: map[string]Balances{utils.VOICE: Balances{bIvo12}}, AllowNegative: true} for _, acnt := range []*Account{rif12sAccount, ivo12sAccount} { if err := accountingStorage.SetAccount(acnt); err != nil { t.Error(err) diff --git a/engine/sharedgroup.go b/engine/sharedgroup.go index 5bec228bc..3bdb9d88c 100644 --- a/engine/sharedgroup.go +++ b/engine/sharedgroup.go @@ -49,7 +49,7 @@ type SharingParameters struct { RatingSubject string } -func (sg *SharedGroup) SortBalancesByStrategy(myBalance *Balance, bc BalanceChain) BalanceChain { +func (sg *SharedGroup) SortBalancesByStrategy(myBalance *Balance, bc Balances) Balances { sharingParameters := sg.AccountParameters[utils.ANY] if sp, hasParamsForAccount := sg.AccountParameters[myBalance.account.ID]; hasParamsForAccount { sharingParameters = sp @@ -61,18 +61,18 @@ func (sg *SharedGroup) SortBalancesByStrategy(myBalance *Balance, bc BalanceChai } switch strategy { case STRATEGY_LOWEST, STRATEGY_MINE_LOWEST: - sort.Sort(LowestBalanceChainSorter(bc)) + sort.Sort(LowestBalancesSorter(bc)) case STRATEGY_HIGHEST, STRATEGY_MINE_HIGHEST: - sort.Sort(HighestBalanceChainSorter(bc)) + sort.Sort(HighestBalancesSorter(bc)) case STRATEGY_RANDOM, STRATEGY_MINE_RANDOM: - rbc := RandomBalanceChainSorter(bc) + rbc := RandomBalancesSorter(bc) (&rbc).Sort() - bc = BalanceChain(rbc) + bc = Balances(rbc) default: // use mine random for anything else strategy = STRATEGY_MINE_RANDOM - rbc := RandomBalanceChainSorter(bc) + rbc := RandomBalancesSorter(bc) (&rbc).Sort() - bc = BalanceChain(rbc) + bc = Balances(rbc) } if strings.HasPrefix(strategy, MINE_PREFIX) { // find index of my balance @@ -90,7 +90,7 @@ func (sg *SharedGroup) SortBalancesByStrategy(myBalance *Balance, bc BalanceChai } // Returns all shared group's balances collected from user accounts' -func (sg *SharedGroup) GetBalances(destination, category, direction, balanceType string, ub *Account) (bc BalanceChain) { +func (sg *SharedGroup) GetBalances(destination, category, direction, balanceType string, ub *Account) (bc Balances) { // if len(sg.members) == 0 { for ubId := range sg.MemberIds { var nUb *Account @@ -115,37 +115,37 @@ func (sg *SharedGroup) GetBalances(destination, category, direction, balanceType return } -type LowestBalanceChainSorter []*Balance +type LowestBalancesSorter []*Balance -func (lbcs LowestBalanceChainSorter) Len() int { +func (lbcs LowestBalancesSorter) Len() int { return len(lbcs) } -func (lbcs LowestBalanceChainSorter) Swap(i, j int) { +func (lbcs LowestBalancesSorter) Swap(i, j int) { lbcs[i], lbcs[j] = lbcs[j], lbcs[i] } -func (lbcs LowestBalanceChainSorter) Less(i, j int) bool { +func (lbcs LowestBalancesSorter) Less(i, j int) bool { return lbcs[i].GetValue() < lbcs[j].GetValue() } -type HighestBalanceChainSorter []*Balance +type HighestBalancesSorter []*Balance -func (hbcs HighestBalanceChainSorter) Len() int { +func (hbcs HighestBalancesSorter) Len() int { return len(hbcs) } -func (hbcs HighestBalanceChainSorter) Swap(i, j int) { +func (hbcs HighestBalancesSorter) Swap(i, j int) { hbcs[i], hbcs[j] = hbcs[j], hbcs[i] } -func (hbcs HighestBalanceChainSorter) Less(i, j int) bool { +func (hbcs HighestBalancesSorter) Less(i, j int) bool { return hbcs[i].GetValue() > hbcs[j].GetValue() } -type RandomBalanceChainSorter []*Balance +type RandomBalancesSorter []*Balance -func (rbcs *RandomBalanceChainSorter) Sort() { +func (rbcs *RandomBalancesSorter) Sort() { src := *rbcs // randomize balance chain dest := make([]*Balance, len(src)) diff --git a/engine/sharedgroup_test.go b/engine/sharedgroup_test.go index bbf6efc51..fd71f1531 100644 --- a/engine/sharedgroup_test.go +++ b/engine/sharedgroup_test.go @@ -50,7 +50,7 @@ func TestSharedSetGet(t *testing.T) { } func TestSharedPopBalanceByStrategyLow(t *testing.T) { - bc := BalanceChain{ + bc := Balances{ &Balance{Value: 2.0}, &Balance{Uuid: "uuuu", Value: 1.0, account: &Account{ID: "test"}}, &Balance{Value: 3.0}, @@ -67,7 +67,7 @@ func TestSharedPopBalanceByStrategyLow(t *testing.T) { } func TestSharedPopBalanceByStrategyHigh(t *testing.T) { - bc := BalanceChain{ + bc := Balances{ &Balance{Uuid: "uuuu", Value: 2.0, account: &Account{ID: "test"}}, &Balance{Value: 1.0}, &Balance{Value: 3.0}, @@ -84,7 +84,7 @@ func TestSharedPopBalanceByStrategyHigh(t *testing.T) { } func TestSharedPopBalanceByStrategyMineHigh(t *testing.T) { - bc := BalanceChain{ + bc := Balances{ &Balance{Uuid: "uuuu", Value: 2.0, account: &Account{ID: "test"}}, &Balance{Value: 1.0}, &Balance{Value: 3.0}, @@ -101,7 +101,7 @@ func TestSharedPopBalanceByStrategyMineHigh(t *testing.T) { } /*func TestSharedPopBalanceByStrategyRandomHigh(t *testing.T) { - bc := BalanceChain{ + bc := Balances{ &Balance{Uuid: "uuuu", Value: 2.0, account: &Account{Id: "test"}}, &Balance{Value: 1.0}, &Balance{Value: 3.0}, diff --git a/engine/storage_test.go b/engine/storage_test.go index 40273c415..cdb9d6802 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -322,14 +322,14 @@ func GetUB() *Account { DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), }, Weight: 10.0, - ActionsId: "Commando", + ActionsID: "Commando", } var zeroTime time.Time zeroTime = zeroTime.UTC() // for deep equal to find location ub := &Account{ ID: "rif", AllowNegative: true, - BalanceMap: map[string]BalanceChain{utils.SMS: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, utils.DATA: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}, utils.VOICE: BalanceChain{&Balance{Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, + BalanceMap: map[string]Balances{utils.SMS: Balances{&Balance{Value: 14, ExpirationDate: zeroTime}}, utils.DATA: Balances{&Balance{Value: 1024, ExpirationDate: zeroTime}}, utils.VOICE: Balances{&Balance{Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.SMS: []*UnitCounter{uc, uc}}, ActionTriggers: ActionTriggers{at, at, at}, } diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 439138a4f..4338a4521 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -696,7 +696,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { ActivationDate: activationDate, Balance: &BalanceFilter{}, Weight: atr.Weight, - ActionsId: atr.ActionsId, + ActionsID: atr.ActionsId, MinQueuedItems: atr.MinQueuedItems, } if atr.BalanceId != "" && atr.BalanceId != utils.ANY { @@ -772,7 +772,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error } for _, accountAction := range storAas { id := accountAction.KeyId() - var actionsIds []string // collects action ids + var actionIDs []string // collects action ids // action timings if accountAction.ActionPlanId != "" { // get old userBalanceIds @@ -837,7 +837,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error ActionsID: at.ActionsId, }) // collect action ids from timings - actionsIds = append(actionsIds, at.ActionsId) + actionIDs = append(actionIDs, at.ActionsId) exitingAccountIds[id] = true actionPlan.AccountIDs = exitingAccountIds } @@ -897,7 +897,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error ActivationDate: actTime, Balance: &BalanceFilter{}, Weight: atr.Weight, - ActionsId: atr.ActionsId, + ActionsID: atr.ActionsId, } if atr.BalanceId != "" && atr.BalanceId != utils.ANY { atrs[idx].Balance.ID = utils.StringPointer(atr.BalanceId) @@ -960,7 +960,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error actionTriggers = atrsMap[accountAction.ActionTriggersId] // collect action ids from triggers for _, atr := range actionTriggers { - actionsIds = append(actionsIds, atr.ActionsId) + actionIDs = append(actionIDs, atr.ActionsID) } // write action triggers err = tpr.ratingStorage.SetActionTriggers(accountAction.ActionTriggersId, actionTriggers) @@ -971,7 +971,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error // actions facts := make(map[string][]*Action) - for _, actId := range actionsIds { + for _, actId := range actionIDs { tpas, err := tpr.lr.GetTpActions(tpr.tpid, actId) if err != nil { return err @@ -1192,7 +1192,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { if err != nil { return err } - var actionsIds []string // collect action ids + var actionIDs []string // collect action ids for tag, tpStats := range storStats { for _, tpStat := range tpStats { var cs *CdrStats @@ -1234,7 +1234,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { ActivationDate: actTime, Balance: &BalanceFilter{}, Weight: atr.Weight, - ActionsId: atr.ActionsId, + ActionsID: atr.ActionsId, } if atr.BalanceId != "" && atr.BalanceId != utils.ANY { atrs[idx].Balance.ID = utils.StringPointer(atr.BalanceId) @@ -1297,7 +1297,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { } // collect action ids from triggers for _, atr := range tpr.actionsTriggers[triggerTag] { - actionsIds = append(actionsIds, atr.ActionsId) + actionIDs = append(actionIDs, atr.ActionsID) } } triggers, exists := tpr.actionsTriggers[triggerTag] @@ -1315,7 +1315,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { } } // actions - for _, actId := range actionsIds { + for _, actId := range actionIDs { _, exists := tpr.actions[actId] if !exists { tpas, err := tpr.lr.GetTpActions(tpr.tpid, actId) diff --git a/utils/map.go b/utils/map.go index 07f2d280b..0bf197a7a 100644 --- a/utils/map.go +++ b/utils/map.go @@ -117,6 +117,12 @@ func (sm StringMap) Slice() []string { return result } +func (sm StringMap) IsEmpty() bool { + return sm == nil || + len(sm) == 0 || + sm[ANY] == true +} + func StringMapFromSlice(s []string) StringMap { result := make(StringMap) for _, v := range s { From 3719d259e3ce8771c2f764233496b947c9c5218b Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 24 Feb 2016 18:38:30 +0200 Subject: [PATCH 115/199] fixed build error and made test.sh build project --- cmd/cgr-loader/migrator_rc8.go | 4 ++-- local_test.sh | 1 - test.sh | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index 30f523540..4905960b5 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -289,7 +289,7 @@ func (mig MigratorRC8) migrateAccounts() error { Recurrent: oldAtr.Recurrent, MinSleep: oldAtr.MinSleep, Weight: oldAtr.Weight, - ActionsId: oldAtr.ActionsId, + ActionsID: oldAtr.ActionsId, MinQueuedItems: oldAtr.MinQueuedItems, Executed: oldAtr.Executed, } @@ -386,7 +386,7 @@ func (mig MigratorRC8) migrateActionTriggers() error { Recurrent: oldAtr.Recurrent, MinSleep: oldAtr.MinSleep, Weight: oldAtr.Weight, - ActionsId: oldAtr.ActionsId, + ActionsID: oldAtr.ActionsId, MinQueuedItems: oldAtr.MinQueuedItems, Executed: oldAtr.Executed, } diff --git a/local_test.sh b/local_test.sh index b85f15363..05577d629 100755 --- a/local_test.sh +++ b/local_test.sh @@ -1,5 +1,4 @@ #! /usr/bin/env sh -./build.sh ./test.sh gen=$? echo 'go test github.com/cgrates/cgrates/apier/v1 -local' diff --git a/test.sh b/test.sh index 0c62795fc..d298e6a34 100755 --- a/test.sh +++ b/test.sh @@ -1,4 +1,5 @@ #! /usr/bin/env sh +./build.sh go test -i github.com/cgrates/cgrates/apier/v1 go test -i github.com/cgrates/cgrates/apier/v2 From eaad0329e70e2c631bd032955cb4199c0340ee70 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 25 Feb 2016 09:34:12 +0200 Subject: [PATCH 116/199] intermediary migrator --- cmd/cgr-loader/cgr-loader.go | 11 + cmd/cgr-loader/migrator_rc8int.go | 408 ++++++++++++++++++++++++++++++ engine/balance_filter.go | 43 ++-- 3 files changed, 441 insertions(+), 21 deletions(-) create mode 100644 cmd/cgr-loader/migrator_rc8int.go diff --git a/cmd/cgr-loader/cgr-loader.go b/cmd/cgr-loader/cgr-loader.go index 067cc6ae7..c818ac353 100644 --- a/cmd/cgr-loader/cgr-loader.go +++ b/cmd/cgr-loader/cgr-loader.go @@ -152,6 +152,17 @@ func main() { log.Print(err.Error()) } } + if strings.Contains(*migrateRC8, "int") { + if err := migratorRC8acc.migrateAccountsInt(); err != nil { + log.Print(err.Error()) + } + if err := migratorRC8rat.migrateActionTriggersInt(); err != nil { + log.Print(err.Error()) + } + if err := migratorRC8rat.migrateActionsInt(); err != nil { + log.Print(err.Error()) + } + } log.Print("Done!") return } diff --git a/cmd/cgr-loader/migrator_rc8int.go b/cmd/cgr-loader/migrator_rc8int.go new file mode 100644 index 000000000..32a364835 --- /dev/null +++ b/cmd/cgr-loader/migrator_rc8int.go @@ -0,0 +1,408 @@ +package main + +import ( + "log" + "time" + + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +type Account1 struct { + Id string + BalanceMap map[string]BalanceChain1 + UnitCounters UnitCounters1 + ActionTriggers ActionTriggers1 + AllowNegative bool + Disabled bool +} + +type Balance1 struct { + Uuid string //system wide unique + Id string // account wide unique + Value float64 + Directions utils.StringMap + ExpirationDate time.Time + Weight float64 + DestinationIds utils.StringMap + RatingSubject string + Categories utils.StringMap + SharedGroups utils.StringMap + Timings []*engine.RITiming + TimingIDs utils.StringMap + Disabled bool + Factor engine.ValueFactor + Blocker bool + precision int + account *Account // used to store ub reference for shared balances + dirty bool +} + +type BalanceChain1 []*Balance1 + +type UnitCounter1 struct { + BalanceType string // *monetary/*voice/*sms/etc + CounterType string // *event or *balance + Balances BalanceChain1 // first balance is the general one (no destination) +} + +type UnitCounters1 []*UnitCounter1 + +type Action1 struct { + Id string + ActionType string + BalanceType string + ExtraParameters string + Filter string + ExpirationString string // must stay as string because it can have relative values like 1month + Weight float64 + Balance *Balance1 +} + +type Actions1 []*Action1 + +type ActionTrigger1 struct { + ID string // original csv tag + UniqueID string // individual id + ThresholdType string + ThresholdValue float64 + Recurrent bool // reset eexcuted flag each run + MinSleep time.Duration // Minimum duration between two executions in case of recurrent triggers + BalanceId string + BalanceType string // *monetary/*voice etc + BalanceDirections utils.StringMap // filter for balance + BalanceDestinationIds utils.StringMap // filter for balance + BalanceWeight float64 // filter for balance + BalanceExpirationDate time.Time // filter for balance + BalanceTimingTags utils.StringMap // filter for balance + BalanceRatingSubject string // filter for balance + BalanceCategories utils.StringMap // filter for balance + BalanceSharedGroups utils.StringMap // filter for balance + BalanceBlocker bool + BalanceDisabled bool // filter for balance + Weight float64 + ActionsId string + MinQueuedItems int // Trigger actions only if this number is hit (stats only) + Executed bool +} +type ActionTriggers1 []*ActionTrigger1 + +func (mig MigratorRC8) migrateAccountsInt() error { + keys, err := mig.db.Cmd("KEYS", OLD_ACCOUNT_PREFIX+"*").List() + if err != nil { + return err + } + newAccounts := make([]*engine.Account, 0) + var migratedKeys []string + // get existing accounts + for _, key := range keys { + log.Printf("Migrating account: %s...", key) + values, err := mig.db.Cmd("GET", key).Bytes() + if err != nil { + continue + } + var oldAcc Account1 + if err = mig.ms.Unmarshal(values, &oldAcc); err != nil { + return err + } + // transfer data into new structurse + newAcc := &engine.Account{ + ID: oldAcc.Id, + BalanceMap: make(map[string]engine.Balances, len(oldAcc.BalanceMap)), + UnitCounters: make(engine.UnitCounters, len(oldAcc.UnitCounters)), + ActionTriggers: make(engine.ActionTriggers, len(oldAcc.ActionTriggers)), + AllowNegative: oldAcc.AllowNegative, + Disabled: oldAcc.Disabled, + } + // balances + balanceErr := false + for key, oldBalChain := range oldAcc.BalanceMap { + newAcc.BalanceMap[key] = make(engine.Balances, len(oldBalChain)) + for index, oldBal := range oldBalChain { + newAcc.BalanceMap[key][index] = &engine.Balance{ + Uuid: oldBal.Uuid, + ID: oldBal.Id, + Value: oldBal.Value, + Directions: oldBal.Directions, + ExpirationDate: oldBal.ExpirationDate, + Weight: oldBal.Weight, + DestinationIDs: oldBal.DestinationIds, + RatingSubject: oldBal.RatingSubject, + Categories: oldBal.Categories, + SharedGroups: oldBal.SharedGroups, + Timings: oldBal.Timings, + TimingIDs: oldBal.TimingIDs, + Disabled: oldBal.Disabled, + Factor: oldBal.Factor, + Blocker: oldBal.Blocker, + } + } + } + if balanceErr { + continue + } + // unit counters + for _, oldUc := range oldAcc.UnitCounters { + newUc := &engine.UnitCounter{ + Counters: make(engine.CounterFilters, len(oldUc.Balances)), + } + for index, oldUcBal := range oldUc.Balances { + b := &engine.Balance{ + Uuid: oldUcBal.Uuid, + ID: oldUcBal.Id, + Value: oldUcBal.Value, + Directions: oldUcBal.Directions, + ExpirationDate: oldUcBal.ExpirationDate, + Weight: oldUcBal.Weight, + DestinationIDs: oldUcBal.DestinationIds, + RatingSubject: oldUcBal.RatingSubject, + Categories: oldUcBal.Categories, + SharedGroups: oldUcBal.SharedGroups, + Timings: oldUcBal.Timings, + TimingIDs: oldUcBal.TimingIDs, + Disabled: oldUcBal.Disabled, + Factor: oldUcBal.Factor, + Blocker: oldUcBal.Blocker, + } + bf := &engine.BalanceFilter{} + bf.LoadFromBalance(b) + cf := &engine.CounterFilter{ + Value: oldUcBal.Value, + Filter: bf, + } + newUc.Counters[index] = cf + } + newAcc.UnitCounters[oldUc.BalanceType] = append(newAcc.UnitCounters[oldUc.BalanceType], newUc) + } + // action triggers + for _, oldAtr := range oldAcc.ActionTriggers { + at := &engine.ActionTrigger{ + ID: oldAtr.ID, + UniqueID: oldAtr.UniqueID, + ThresholdType: oldAtr.ThresholdType, + ThresholdValue: oldAtr.ThresholdValue, + Recurrent: oldAtr.Recurrent, + MinSleep: oldAtr.MinSleep, + Weight: oldAtr.Weight, + ActionsID: oldAtr.ActionsId, + MinQueuedItems: oldAtr.MinQueuedItems, + Executed: oldAtr.Executed, + } + bf := &engine.BalanceFilter{} + if oldAtr.BalanceId != "" { + bf.ID = utils.StringPointer(oldAtr.BalanceId) + } + if oldAtr.BalanceType != "" { + bf.Type = utils.StringPointer(oldAtr.BalanceType) + } + if oldAtr.BalanceRatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldAtr.BalanceRatingSubject) + } + if !oldAtr.BalanceDirections.IsEmpty() { + bf.Directions = utils.StringMapPointer(oldAtr.BalanceDirections) + } + if !oldAtr.BalanceDestinationIds.IsEmpty() { + bf.DestinationIDs = utils.StringMapPointer(oldAtr.BalanceDestinationIds) + } + if !oldAtr.BalanceTimingTags.IsEmpty() { + bf.TimingIDs = utils.StringMapPointer(oldAtr.BalanceTimingTags) + } + if !oldAtr.BalanceCategories.IsEmpty() { + bf.Categories = utils.StringMapPointer(oldAtr.BalanceCategories) + } + if !oldAtr.BalanceSharedGroups.IsEmpty() { + bf.SharedGroups = utils.StringMapPointer(oldAtr.BalanceSharedGroups) + } + if oldAtr.BalanceWeight != 0 { + bf.Weight = utils.Float64Pointer(oldAtr.BalanceWeight) + } + if oldAtr.BalanceDisabled != false { + bf.Disabled = utils.BoolPointer(oldAtr.BalanceDisabled) + } + if !oldAtr.BalanceExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldAtr.BalanceExpirationDate) + } + at.Balance = bf + } + newAcc.InitCounters() + newAccounts = append(newAccounts, newAcc) + migratedKeys = append(migratedKeys, key) + } + // write data back + for _, newAcc := range newAccounts { + result, err := mig.ms.Marshal(newAcc) + if err != nil { + return err + } + if err := mig.db.Cmd("SET", utils.ACCOUNT_PREFIX+newAcc.ID, result).Err; err != nil { + return err + } + } + notMigrated := len(keys) - len(migratedKeys) + if notMigrated > 0 { + log.Printf("WARNING: there are %d accounts that failed migration!", notMigrated) + } + return err +} + +func (mig MigratorRC8) migrateActionTriggersInt() error { + keys, err := mig.db.Cmd("KEYS", utils.ACTION_TRIGGER_PREFIX+"*").List() + if err != nil { + return err + } + newAtrsMap := make(map[string]engine.ActionTriggers, len(keys)) + for _, key := range keys { + log.Printf("Migrating action trigger: %s...", key) + var oldAtrs ActionTriggers1 + var values []byte + if values, err = mig.db.Cmd("GET", key).Bytes(); err == nil { + if err := mig.ms.Unmarshal(values, &oldAtrs); err != nil { + return err + } + } + newAtrs := make(engine.ActionTriggers, len(oldAtrs)) + for index, oldAtr := range oldAtrs { + at := &engine.ActionTrigger{ + ID: oldAtr.ID, + UniqueID: oldAtr.UniqueID, + ThresholdType: oldAtr.ThresholdType, + ThresholdValue: oldAtr.ThresholdValue, + Recurrent: oldAtr.Recurrent, + MinSleep: oldAtr.MinSleep, + Weight: oldAtr.Weight, + ActionsID: oldAtr.ActionsId, + MinQueuedItems: oldAtr.MinQueuedItems, + Executed: oldAtr.Executed, + } + bf := &engine.BalanceFilter{} + if oldAtr.BalanceId != "" { + bf.ID = utils.StringPointer(oldAtr.BalanceId) + } + if oldAtr.BalanceType != "" { + bf.Type = utils.StringPointer(oldAtr.BalanceType) + } + if oldAtr.BalanceRatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldAtr.BalanceRatingSubject) + } + if !oldAtr.BalanceDirections.IsEmpty() { + bf.Directions = utils.StringMapPointer(oldAtr.BalanceDirections) + } + if !oldAtr.BalanceDestinationIds.IsEmpty() { + bf.DestinationIDs = utils.StringMapPointer(oldAtr.BalanceDestinationIds) + } + if !oldAtr.BalanceTimingTags.IsEmpty() { + bf.TimingIDs = utils.StringMapPointer(oldAtr.BalanceTimingTags) + } + if !oldAtr.BalanceCategories.IsEmpty() { + bf.Categories = utils.StringMapPointer(oldAtr.BalanceCategories) + } + if !oldAtr.BalanceSharedGroups.IsEmpty() { + bf.SharedGroups = utils.StringMapPointer(oldAtr.BalanceSharedGroups) + } + if oldAtr.BalanceWeight != 0 { + bf.Weight = utils.Float64Pointer(oldAtr.BalanceWeight) + } + if oldAtr.BalanceDisabled != false { + bf.Disabled = utils.BoolPointer(oldAtr.BalanceDisabled) + } + if !oldAtr.BalanceExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldAtr.BalanceExpirationDate) + } + at.Balance = bf + newAtrs[index] = at + } + newAtrsMap[key] = newAtrs + } + // write data back + for key, atrs := range newAtrsMap { + result, err := mig.ms.Marshal(&atrs) + if err != nil { + return err + } + if err = mig.db.Cmd("SET", key, result).Err; err != nil { + return err + } + } + return nil +} + +func (mig MigratorRC8) migrateActionsInt() error { + keys, err := mig.db.Cmd("KEYS", utils.ACTION_PREFIX+"*").List() + if err != nil { + return err + } + newAcsMap := make(map[string]engine.Actions, len(keys)) + for _, key := range keys { + log.Printf("Migrating action: %s...", key) + var oldAcs Actions1 + var values []byte + if values, err = mig.db.Cmd("GET", key).Bytes(); err == nil { + if err := mig.ms.Unmarshal(values, &oldAcs); err != nil { + return err + } + } + newAcs := make(engine.Actions, len(oldAcs)) + for index, oldAc := range oldAcs { + a := &engine.Action{ + Id: oldAc.Id, + ActionType: oldAc.ActionType, + ExtraParameters: oldAc.ExtraParameters, + ExpirationString: oldAc.ExpirationString, + Filter: oldAc.Filter, + Weight: oldAc.Weight, + Balance: &engine.BalanceFilter{}, + } + bf := a.Balance + if oldAc.Balance.Uuid != "" { + bf.Uuid = utils.StringPointer(oldAc.Balance.Uuid) + } + if oldAc.Balance.Id != "" { + bf.ID = utils.StringPointer(oldAc.Balance.Id) + } + if oldAc.BalanceType != "" { + bf.Type = utils.StringPointer(oldAc.BalanceType) + } + if oldAc.Balance.Value != 0 { + bf.Value = utils.Float64Pointer(oldAc.Balance.Value) + } + if oldAc.Balance.RatingSubject != "" { + bf.RatingSubject = utils.StringPointer(oldAc.Balance.RatingSubject) + } + if !oldAc.Balance.DestinationIds.IsEmpty() { + bf.DestinationIDs = utils.StringMapPointer(oldAc.Balance.DestinationIds) + } + if !oldAc.Balance.TimingIDs.IsEmpty() { + bf.TimingIDs = utils.StringMapPointer(oldAc.Balance.TimingIDs) + } + if !oldAc.Balance.Categories.IsEmpty() { + bf.Categories = utils.StringMapPointer(oldAc.Balance.Categories) + } + if !oldAc.Balance.SharedGroups.IsEmpty() { + bf.SharedGroups = utils.StringMapPointer(oldAc.Balance.SharedGroups) + } + if oldAc.Balance.Weight != 0 { + bf.Weight = utils.Float64Pointer(oldAc.Balance.Weight) + } + if oldAc.Balance.Disabled != false { + bf.Disabled = utils.BoolPointer(oldAc.Balance.Disabled) + } + if !oldAc.Balance.ExpirationDate.IsZero() { + bf.ExpirationDate = utils.TimePointer(oldAc.Balance.ExpirationDate) + } + bf.Timings = oldAc.Balance.Timings + newAcs[index] = a + } + newAcsMap[key] = newAcs + } + // write data back + for key, acs := range newAcsMap { + result, err := mig.ms.Marshal(&acs) + if err != nil { + return err + } + if err = mig.db.Cmd("SET", key, result).Err; err != nil { + return err + } + } + return nil +} diff --git a/engine/balance_filter.go b/engine/balance_filter.go index c7010967e..e9557b75c 100644 --- a/engine/balance_filter.go +++ b/engine/balance_filter.go @@ -108,50 +108,51 @@ func (bf *BalanceFilter) Clone() *BalanceFilter { return result } -func (bp *BalanceFilter) LoadFromBalance(b *Balance) *BalanceFilter { +func (bf *BalanceFilter) LoadFromBalance(b *Balance) *BalanceFilter { if b.Uuid != "" { - bp.Uuid = &b.Uuid + bf.Uuid = &b.Uuid } if b.ID != "" { - bp.ID = &b.ID + bf.ID = &b.ID } if b.Value != 0 { - bp.Value = &b.Value + bf.Value = &b.Value } - if len(b.Directions) != 0 { - bp.Directions = &b.Directions + if !b.Directions.IsEmpty() { + bf.Directions = &b.Directions } if !b.ExpirationDate.IsZero() { - bp.ExpirationDate = &b.ExpirationDate + bf.ExpirationDate = &b.ExpirationDate } if b.Weight != 0 { - bp.Weight = &b.Weight + bf.Weight = &b.Weight } - if len(b.DestinationIDs) != 0 { - bp.DestinationIDs = &b.DestinationIDs + if !b.DestinationIDs.IsEmpty() { + bf.DestinationIDs = &b.DestinationIDs } if b.RatingSubject != "" { - bp.RatingSubject = &b.RatingSubject + bf.RatingSubject = &b.RatingSubject } - if len(b.Categories) != 0 { - bp.Categories = &b.Categories + if !b.Categories.IsEmpty() { + bf.Categories = &b.Categories } - if len(b.SharedGroups) != 0 { - bp.SharedGroups = &b.SharedGroups + if !b.SharedGroups.IsEmpty() { + bf.SharedGroups = &b.SharedGroups } - if len(b.TimingIDs) != 0 { - bp.TimingIDs = &b.TimingIDs + if !b.TimingIDs.IsEmpty() { + bf.TimingIDs = &b.TimingIDs } if len(b.Factor) != 0 { - bp.Factor = &b.Factor + bf.Factor = &b.Factor } if b.Disabled { - bp.Disabled = &b.Disabled + bf.Disabled = &b.Disabled } if b.Blocker { - bp.Blocker = &b.Blocker + bf.Blocker = &b.Blocker } - return bp + bf.Timings = b.Timings + return bf } func (bp *BalanceFilter) Equal(o *BalanceFilter) bool { From 3beacd913950484f28ccf8b08ceb71d6e07ecb0e Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 25 Feb 2016 14:18:49 +0200 Subject: [PATCH 117/199] account migrator fixes --- cmd/cgr-loader/migrator_rc8int.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/cgr-loader/migrator_rc8int.go b/cmd/cgr-loader/migrator_rc8int.go index 32a364835..41f55afb1 100644 --- a/cmd/cgr-loader/migrator_rc8int.go +++ b/cmd/cgr-loader/migrator_rc8int.go @@ -88,7 +88,7 @@ type ActionTrigger1 struct { type ActionTriggers1 []*ActionTrigger1 func (mig MigratorRC8) migrateAccountsInt() error { - keys, err := mig.db.Cmd("KEYS", OLD_ACCOUNT_PREFIX+"*").List() + keys, err := mig.db.Cmd("KEYS", utils.ACCOUNT_PREFIX+"*").List() if err != nil { return err } @@ -109,7 +109,7 @@ func (mig MigratorRC8) migrateAccountsInt() error { newAcc := &engine.Account{ ID: oldAcc.Id, BalanceMap: make(map[string]engine.Balances, len(oldAcc.BalanceMap)), - UnitCounters: make(engine.UnitCounters, len(oldAcc.UnitCounters)), + UnitCounters: make(engine.UnitCounters), ActionTriggers: make(engine.ActionTriggers, len(oldAcc.ActionTriggers)), AllowNegative: oldAcc.AllowNegative, Disabled: oldAcc.Disabled, @@ -175,7 +175,7 @@ func (mig MigratorRC8) migrateAccountsInt() error { newAcc.UnitCounters[oldUc.BalanceType] = append(newAcc.UnitCounters[oldUc.BalanceType], newUc) } // action triggers - for _, oldAtr := range oldAcc.ActionTriggers { + for index, oldAtr := range oldAcc.ActionTriggers { at := &engine.ActionTrigger{ ID: oldAtr.ID, UniqueID: oldAtr.UniqueID, @@ -223,6 +223,7 @@ func (mig MigratorRC8) migrateAccountsInt() error { bf.ExpirationDate = utils.TimePointer(oldAtr.BalanceExpirationDate) } at.Balance = bf + newAcc.ActionTriggers[index] = at } newAcc.InitCounters() newAccounts = append(newAccounts, newAcc) From e55b620a92e94e1b1628cce12809e76ebe87cdc9 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 25 Feb 2016 19:23:51 +0200 Subject: [PATCH 118/199] removed extra logging --- cmd/cgr-loader/migrator_rc8.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/cgr-loader/migrator_rc8.go b/cmd/cgr-loader/migrator_rc8.go index 4905960b5..9d085d2f6 100644 --- a/cmd/cgr-loader/migrator_rc8.go +++ b/cmd/cgr-loader/migrator_rc8.go @@ -581,7 +581,7 @@ func (mig MigratorRC8) migrateActionPlans() error { // fix id idElements := strings.Split(actionId, utils.CONCATENATED_KEY_SEP) if len(idElements) != 3 { - log.Printf("Malformed account ID %s", actionId) + //log.Printf("Malformed account ID %s", actionId) continue } apl.AccountIds[idx] = fmt.Sprintf("%s:%s", idElements[1], idElements[2]) From 80934092092b5feff6d692ee9330d5ff1f17c74c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 26 Feb 2016 16:33:54 +0200 Subject: [PATCH 119/199] fix bad offset, fixes #382 --- apier/v2/accounts.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index dc89590b9..5da921b9f 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -47,6 +47,12 @@ func (self *ApierV2) GetAccounts(attr utils.AttrGetAccounts, reply *[]*engine.Ac if len(accountKeys) == 0 { return nil } + if attr.Offset > len(accountKeys) { + attr.Offset = len(accountKeys) + } + if attr.Offset < 0 { + attr.Offset = 0 + } var limitedAccounts []string if attr.Limit != 0 { max := math.Min(float64(attr.Offset+attr.Limit), float64(len(accountKeys))) From a7024eb66c74b37dbef6d7bac1e9e79266ddc3be Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 29 Feb 2016 12:40:46 +0200 Subject: [PATCH 120/199] SetBalance to modify the ID too --- engine/balance_filter.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/balance_filter.go b/engine/balance_filter.go index e9557b75c..dc832cf0b 100644 --- a/engine/balance_filter.go +++ b/engine/balance_filter.go @@ -285,6 +285,9 @@ func (bf *BalanceFilter) ModifyBalance(b *Balance) { if b == nil { return } + if bf.ID != nil { + b.ID = *bf.ID + } if bf.Directions != nil { b.Directions = *bf.Directions } From e37da87bd5b3b06cc4525c2aeea7e24d598ed9c2 Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 29 Feb 2016 13:36:52 +0100 Subject: [PATCH 121/199] Diameter *sum handler --- agents/libdmt.go | 23 ++++++++++++++++++++--- agents/libdmt_test.go | 28 ++++++++++++++++++++++++++++ config/cfgcdrfield.go | 1 + 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/agents/libdmt.go b/agents/libdmt.go index 6d2ee67a8..47be00d5a 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -51,6 +51,7 @@ func init() { const ( META_CCR_USAGE = "*ccr_usage" META_VALUE_EXPONENT = "*value_exponent" + META_SUM = "*sum" DIAMETER_CCR = "DIAMETER_CCR" DiameterRatingFailed = 5031 CGRError = "CGRError" @@ -238,6 +239,20 @@ func metaValueExponent(m *diam.Message, argsTpl utils.RSRFields, roundingDecimal return strconv.FormatFloat(utils.Round(res, roundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil } +func metaSum(m *diam.Message, argsTpl utils.RSRFields, roundingDecimals int) (string, error) { + valStr := composedFieldvalue(m, argsTpl, 0, nil) + handlerArgs := strings.Split(valStr, utils.HandlerArgSep) + var summed float64 + for _, arg := range handlerArgs { + val, err := strconv.ParseFloat(arg, 64) + if err != nil { + return "", err + } + summed += val + } + return strconv.FormatFloat(utils.Round(summed, roundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil +} + // splitIntoInterface is used to split a string into []interface{} instead of []string func splitIntoInterface(content, sep string) []interface{} { spltStr := strings.Split(content, sep) @@ -253,7 +268,6 @@ func avpsWithPath(m *diam.Message, rsrFld *utils.RSRField) ([]*diam.AVP, error) return m.FindAVPsWithPath(splitIntoInterface(rsrFld.Id, utils.HIERARCHY_SEP), dict.UndefinedVendorID) } -// Follows the implementation in the StorCdr func passesFieldFilter(m *diam.Message, fieldFilter *utils.RSRField, processorVars map[string]string) (bool, int) { if fieldFilter == nil { return true, 0 @@ -371,9 +385,12 @@ func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interfa case utils.META_CONSTANT: outVal = cfgFld.Value.Id() case utils.META_HANDLER: - if cfgFld.HandlerId == META_VALUE_EXPONENT { + switch cfgFld.HandlerId { + case META_VALUE_EXPONENT: outVal, err = metaValueExponent(m, cfgFld.Value, 10) // FixMe: add here configured number of decimals - } else { + case META_SUM: + outVal, err = metaSum(m, cfgFld.Value, 10) + default: outVal, err = metaHandler(m, cfgFld.HandlerId, cfgFld.Layout, extraParam.(time.Duration)) if err != nil { utils.Logger.Warning(fmt.Sprintf(" Ignoring processing of metafunction: %s, error: %s", cfgFld.HandlerId, err.Error())) diff --git a/agents/libdmt_test.go b/agents/libdmt_test.go index aeed0968a..0c99b4a93 100644 --- a/agents/libdmt_test.go +++ b/agents/libdmt_test.go @@ -114,6 +114,34 @@ func TestMetaValueExponent(t *testing.T) { } } +func TestMetaSum(t *testing.T) { + m := diam.NewRequest(diam.CreditControl, 4, nil) + m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002")) + m.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.CCMoney, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.UnitValue, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(10000)), + diam.NewAVP(avp.Exponent, avp.Mbit, 0, datatype.Integer32(-5)), + }, + }), + diam.NewAVP(avp.CurrencyCode, avp.Mbit, 0, datatype.Unsigned32(33)), + }, + }), + }, + }) + if val, err := metaSum(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err != nil { + t.Error(err) + } else if val != "9995" { + t.Error("Received: ", val) + } + if _, err = metaSum(m, utils.ParseRSRFieldsMustCompile("Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", utils.INFIELD_SEP), 10); err == nil { + t.Error("Should have received error") // Insufficient number arguments + } +} + func TestFieldOutVal(t *testing.T) { m := diam.NewRequest(diam.CreditControl, 4, nil) m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002")) diff --git a/config/cfgcdrfield.go b/config/cfgcdrfield.go index 06b02e2d5..922115644 100644 --- a/config/cfgcdrfield.go +++ b/config/cfgcdrfield.go @@ -80,6 +80,7 @@ type CfgCdrField struct { Strip string Padding string Layout string + Flags utils.StringMap // Various flags to influence behavior Mandatory bool } From cbea3ec6033667412f5fcf55a582f8e4bbf30c9d Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 1 Mar 2016 13:02:45 +0100 Subject: [PATCH 122/199] Diameter config template - adding flags option --- config/cfgcdrfield.go | 1 - config/config_defaults.go | 9 +++++---- config/config_json_test.go | 1 + config/daconfig.go | 7 +++++++ config/libconfig_json.go | 1 + 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/config/cfgcdrfield.go b/config/cfgcdrfield.go index 922115644..06b02e2d5 100644 --- a/config/cfgcdrfield.go +++ b/config/cfgcdrfield.go @@ -80,7 +80,6 @@ type CfgCdrField struct { Strip string Padding string Layout string - Flags utils.StringMap // Various flags to influence behavior Mandatory bool } diff --git a/config/config_defaults.go b/config/config_defaults.go index 930333429..0ef467e70 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -278,10 +278,11 @@ const CGRATES_CFG_JSON = ` "product_name": "CGRateS", // diameter Product-Name AVP used in replies "request_processors": [ { - "id": "*default", // formal identifier of this processor - "dry_run": false, // do not send the events to SMG, just log them - "publish_event": false, // if enabled, it will publish internal event to pubsub - "request_filter": "Subscription-Id>Subscription-Id-Type(0)", // filter requests processed by this processor + "id": "*default", // formal identifier of this processor + "dry_run": false, // do not send the events to SMG, just log them + "publish_event": false, // if enabled, it will publish internal event to pubsub + "request_filter": "Subscription-Id>Subscription-Id-Type(0)", // filter requests processed by this processor + "flags": [], // flags to influence processing behavior "continue_on_success": false, // continue to the next template if executed "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true}, diff --git a/config/config_json_test.go b/config/config_json_test.go index 59637b796..e7ac7e927 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -441,6 +441,7 @@ func TestDiameterAgentJsonCfg(t *testing.T) { Dry_run: utils.BoolPointer(false), Publish_event: utils.BoolPointer(false), Request_filter: utils.StringPointer("Subscription-Id>Subscription-Id-Type(0)"), + Flags: utils.StringSlicePointer([]string{}), Continue_on_success: utils.BoolPointer(false), CCR_fields: &[]*CdrFieldJsonCfg{ &CdrFieldJsonCfg{Tag: utils.StringPointer("TOR"), Field_id: utils.StringPointer(utils.TOR), Type: utils.StringPointer(utils.META_COMPOSED), diff --git a/config/daconfig.go b/config/daconfig.go index b56fd73a3..ae92905b5 100644 --- a/config/daconfig.go +++ b/config/daconfig.go @@ -108,6 +108,7 @@ type DARequestProcessor struct { DryRun bool PublishEvent bool RequestFilter utils.RSRFields + Flags utils.StringMap // Various flags to influence behavior ContinueOnSuccess bool CCRFields []*CfgCdrField CCAFields []*CfgCdrField @@ -132,6 +133,12 @@ func (self *DARequestProcessor) loadFromJsonCfg(jsnCfg *DARequestProcessorJsnCfg return err } } + if jsnCfg.Flags != nil { + self.Flags = utils.StringMapFromSlice(*jsnCfg.Flags) + } + if jsnCfg.Continue_on_success != nil { + self.ContinueOnSuccess = *jsnCfg.Continue_on_success + } if jsnCfg.CCR_fields != nil { if self.CCRFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.CCR_fields); err != nil { return err diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 3facc6945..7ab6b093a 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -263,6 +263,7 @@ type DARequestProcessorJsnCfg struct { Dry_run *bool Publish_event *bool Request_filter *string + Flags *[]string Continue_on_success *bool CCR_fields *[]*CdrFieldJsonCfg CCA_fields *[]*CdrFieldJsonCfg From 425019ca92ec64280883682b6b80c369d37bf6a2 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 1 Mar 2016 15:07:01 +0200 Subject: [PATCH 123/199] execute actions on disabled accounts --- apier/v1/accounts.go | 2 -- engine/action_plan.go | 4 ---- 2 files changed, 6 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index d9b6a7c6c..21131ea16 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -20,7 +20,6 @@ package v1 import ( "fmt" - "log" "math" "strings" "time" @@ -483,7 +482,6 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { expTime = &expTimeVal } accID := utils.AccountKey(attr.Tenant, attr.Account) - log.Print("ACC: ", utils.ToIJSON(attr)) if _, err := self.AccountDb.GetAccount(accID); err != nil { // create account if not exists account := &engine.Account{ diff --git a/engine/action_plan.go b/engine/action_plan.go index b27f7e448..b9c40d519 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -303,10 +303,6 @@ func (at *ActionTiming) Execute() (err error) { continue } } - if acc.Disabled && a.ActionType != ENABLE_ACCOUNT { - continue // disabled acocunts are not removed from action plan - //return 0, fmt.Errorf("Account %s is disabled", accID) - } if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.HasExpirationDate()) && parseErr == nil && !expDate.IsZero() { a.Balance.ExpirationDate = &time.Time{} *a.Balance.ExpirationDate = expDate From 2ff2d0d781d8e3aebf065a7f680d28d22d855e69 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 1 Mar 2016 14:10:52 +0100 Subject: [PATCH 124/199] Responder.GetSessionRuns considering cgr_flags for CallDescriptor, Diameter adding flags to generic event --- agents/dmtagent.go | 3 +++ engine/responder.go | 9 ++++++++- utils/consts.go | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index c1d4dff5a..9aa43a31f 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -94,6 +94,9 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro utils.Logger.Err(fmt.Sprintf(" Processing message: %+v AsSMGenericEvent, error: %s", ccr.diamMessage, err)) return cca } + if len(reqProcessor.Flags) != 0 { + smgEv[utils.CGRFlags] = reqProcessor.Flags.String() // Populate CGRFlags automatically + } if reqProcessor.PublishEvent && self.pubsubs != nil { evt, err := smgEv.AsMapStringString() if err != nil { diff --git a/engine/responder.go b/engine/responder.go index ee6cc8843..a37c222f8 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -445,6 +445,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { }) return errors.New("Error parsing answer event end time") } + extraFields := ev.GetExtraFields() cd := &CallDescriptor{ CgrID: ev.GetCgrId(rs.Timezone), RunID: ev.RunID, @@ -457,7 +458,13 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { Destination: ev.GetDestination(dc.DestinationField), TimeStart: startTime, TimeEnd: endTime, - ExtraFields: ev.GetExtraFields()} + ExtraFields: extraFields} + if flagsStr, hasFlags := extraFields[utils.CGRFlags]; hasFlags { // Force duration from extra fields + flags := utils.StringMapFromSlice(strings.Split(flagsStr, utils.INFIELD_SEP)) + if _, hasFD := flags[utils.FlagForceDuration]; hasFD { + cd.ForceDuration = true + } + } sesRuns = append(sesRuns, &SessionRun{DerivedCharger: dc, CallDescriptor: cd}) } *sRuns = sesRuns diff --git a/utils/consts.go b/utils/consts.go index 904c177a4..579583ba8 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -229,6 +229,7 @@ const ( CGR_DISCONNECT_CAUSE = "cgr_disconnectcause" CGR_COMPUTELCR = "cgr_computelcr" CGR_SUPPLIERS = "cgr_suppliers" + CGRFlags = "cgr_flags" KAM_FLATSTORE = "kamailio_flatstore" OSIPS_FLATSTORE = "opensips_flatstore" MAX_DEBIT_CACHE_PREFIX = "MAX_DEBIT_" @@ -270,6 +271,7 @@ const ( CreatedAt = "CreatedAt" UpdatedAt = "UpdatedAt" HandlerArgSep = "|" + FlagForceDuration = "fd" ) var ( From 43bc4c3f304f69cf13d4371b2f6788061b872f74 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 1 Mar 2016 16:14:22 +0200 Subject: [PATCH 125/199] fix timespan compression crash --- engine/rateinterval.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/rateinterval.go b/engine/rateinterval.go index 268c150bd..0ddcb241d 100644 --- a/engine/rateinterval.go +++ b/engine/rateinterval.go @@ -304,6 +304,9 @@ func (i *RateInterval) Equal(o *RateInterval) bool { if i == nil && o == nil { return true } + if i == nil || o == nil { + return false // considering the earlier test + } if i.Weight != o.Weight { return false } From 9fce6a89b2a1234bb88fb911efc0eb286b2b7706 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 1 Mar 2016 17:02:46 +0200 Subject: [PATCH 126/199] fix local tests --- general_tests/tutorial_local_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index 8bf31bfd9..991ee3428 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -1229,7 +1229,7 @@ func TestTutLocalSetAccount(t *testing.T) { if dta.Tenant != attrs.Tenant || dta.Account != attrs.Account { t.Error("Unexpected account id received: ", acnt.ID) } - if balances := acnt.BalanceMap["*monetary"]; len(balances) != 1 { + if balances := acnt.BalanceMap["*monetary"]; len(balances) != 3 { t.Errorf("Unexpected balances found: %+v", balances) } if len(acnt.ActionTriggers) != 7 { From f9a499752eae31f403e48d658856d3f5a69e79d2 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 2 Mar 2016 16:09:45 +0200 Subject: [PATCH 127/199] fix RemActionTiming API --- apier/v1/accounts.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 21131ea16..200bf18a3 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -93,8 +93,9 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e return 0, utils.ErrNotFound } - if attrs.ActionPlanId != "" { // delete the entire action plan - ap.ActionTimings = nil // will delete the action plan + if attrs.Tenant != "" && attrs.Account != "" { + accID := utils.AccountKey(attrs.Tenant, attrs.Account) + delete(ap.AccountIDs, accID) return 0, self.RatingDb.SetActionPlan(ap.Id, ap, true) } @@ -109,9 +110,8 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e return 0, self.RatingDb.SetActionPlan(ap.Id, ap, true) } - if attrs.Tenant != "" && attrs.Account != "" { - accID := utils.AccountKey(attrs.Tenant, attrs.Account) - delete(ap.AccountIDs, accID) + if attrs.ActionPlanId != "" { // delete the entire action plan + ap.ActionTimings = nil // will delete the action plan return 0, self.RatingDb.SetActionPlan(ap.Id, ap, true) } From 70c8f34fa60093994bba4081be6edcd35d46adbc Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 2 Mar 2016 16:21:21 +0200 Subject: [PATCH 128/199] better error handling for RemActionTiming --- apier/v1/accounts.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 200bf18a3..6973cd84d 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -96,7 +96,8 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e if attrs.Tenant != "" && attrs.Account != "" { accID := utils.AccountKey(attrs.Tenant, attrs.Account) delete(ap.AccountIDs, accID) - return 0, self.RatingDb.SetActionPlan(ap.Id, ap, true) + err = self.RatingDb.SetActionPlan(ap.Id, ap, true) + goto UPDATE } if attrs.ActionTimingId != "" { // delete only a action timing from action plan @@ -107,14 +108,19 @@ func (self *ApierV1) RemActionTiming(attrs AttrRemActionTiming, reply *string) e break } } - return 0, self.RatingDb.SetActionPlan(ap.Id, ap, true) + err = self.RatingDb.SetActionPlan(ap.Id, ap, true) + goto UPDATE } if attrs.ActionPlanId != "" { // delete the entire action plan ap.ActionTimings = nil // will delete the action plan - return 0, self.RatingDb.SetActionPlan(ap.Id, ap, true) + err = self.RatingDb.SetActionPlan(ap.Id, ap, true) + goto UPDATE + } + UPDATE: + if err != nil { + return 0, err } - // update cache self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + attrs.ActionPlanId}}) return 0, nil From c6830314deea3566d2abe53fad99de9ea69ac2bd Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 2 Mar 2016 17:05:36 +0200 Subject: [PATCH 129/199] better error reporting for AddAcccountActionTriggers --- apier/v1/triggers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index ee77c0766..642c137c7 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -36,6 +36,7 @@ func (self *ApierV1) AddAccountActionTriggers(attr AttrAddAccountActionTriggers, } actTime, err := utils.ParseTimeDetectLayout(attr.ActivationDate, self.Config.DefaultTimezone) if err != nil { + *reply = err.Error() return err } accID := utils.AccountKey(attr.Tenant, attr.Account) From d76006cf3f4b7abe9a5b4c90e71f069497616b0e Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 2 Mar 2016 23:05:21 +0200 Subject: [PATCH 130/199] first rounding draft --- console/debit.go | 2 +- console/debit_fake.go | 67 ------------------ engine/account.go | 25 ------- engine/account_test.go | 31 +-------- engine/balances.go | 9 +++ engine/callcost.go | 50 ++++++++++++-- engine/calldesc.go | 142 ++++++++++++++++++++++++++------------- engine/calldesc_test.go | 57 ++++++++++++---- engine/responder.go | 37 +--------- engine/timespans.go | 31 ++++----- engine/timespans_test.go | 92 +++++++++++-------------- 11 files changed, 251 insertions(+), 292 deletions(-) delete mode 100644 console/debit_fake.go diff --git a/console/debit.go b/console/debit.go index bf5a2b185..19d8c308d 100644 --- a/console/debit.go +++ b/console/debit.go @@ -24,7 +24,7 @@ func init() { c := &CmdDebit{ name: "debit", rpcMethod: "Responder.Debit", - clientArgs: []string{"Direction", "Category", "TOR", "Tenant", "Subject", "Account", "Destination", "TimeStart", "TimeEnd", "CallDuration", "FallbackSubject"}, + clientArgs: []string{"Direction", "Category", "TOR", "Tenant", "Subject", "Account", "Destination", "TimeStart", "TimeEnd", "CallDuration", "FallbackSubject", "DryRun"}, } commands[c.Name()] = c c.CommandExecuter = &CommandExecuter{c} diff --git a/console/debit_fake.go b/console/debit_fake.go deleted file mode 100644 index 60c9e4efc..000000000 --- a/console/debit_fake.go +++ /dev/null @@ -1,67 +0,0 @@ -/* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2012-2015 ITsysCOM - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package console - -import "github.com/cgrates/cgrates/engine" - -func init() { - c := &CmdFakeDebit{ - name: "debit_fake", - rpcMethod: "Responder.FakeDebit", - clientArgs: []string{"Direction", "Category", "TOR", "Tenant", "Subject", "Account", "Destination", "TimeStart", "TimeEnd", "CallDuration", "FallbackSubject"}, - } - commands[c.Name()] = c - c.CommandExecuter = &CommandExecuter{c} -} - -// Commander implementation -type CmdFakeDebit struct { - name string - rpcMethod string - rpcParams *engine.CallDescriptor - clientArgs []string - *CommandExecuter -} - -func (self *CmdFakeDebit) Name() string { - return self.name -} - -func (self *CmdFakeDebit) RpcMethod() string { - return self.rpcMethod -} - -func (self *CmdFakeDebit) RpcParams(reset bool) interface{} { - if reset || self.rpcParams == nil { - self.rpcParams = &engine.CallDescriptor{Direction: "*out"} - } - return self.rpcParams -} - -func (self *CmdFakeDebit) PostprocessRpcParams() error { - return nil -} - -func (self *CmdFakeDebit) RpcResult() interface{} { - return &engine.CallCost{} -} - -func (self *CmdFakeDebit) ClientArgs() []string { - return self.clientArgs -} diff --git a/engine/account.go b/engine/account.go index f33640cc9..87c1f9e3c 100644 --- a/engine/account.go +++ b/engine/account.go @@ -532,31 +532,6 @@ func (ub *Account) GetDefaultMoneyBalance() *Balance { return defaultBalance } -func (ub *Account) refundIncrement(increment *Increment, cd *CallDescriptor, count bool) { - var balance *Balance - unitType := cd.TOR - cc := cd.CreateCallCost() - if increment.BalanceInfo.UnitBalanceUuid != "" { - if balance = ub.BalanceMap[unitType].GetBalance(increment.BalanceInfo.UnitBalanceUuid); balance == nil { - return - } - balance.AddValue(increment.Duration.Seconds()) - if count { - ub.countUnits(-increment.Duration.Seconds(), unitType, cc, balance) - } - } - // check money too - if increment.BalanceInfo.MoneyBalanceUuid != "" { - if balance = ub.BalanceMap[utils.MONETARY].GetBalance(increment.BalanceInfo.MoneyBalanceUuid); balance == nil { - return - } - balance.AddValue(increment.Cost) - if count { - ub.countUnits(-increment.Cost, utils.MONETARY, cc, balance) - } - } -} - // Scans the action trigers and execute the actions for which trigger is met func (acc *Account) ExecuteActionTriggers(a *Action) { if acc.executingTriggers { diff --git a/engine/account_test.go b/engine/account_test.go index 3df23e059..cb87a69af 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -1127,33 +1127,6 @@ func TestAccountUnitCountingOutboundInbound(t *testing.T) { } } -func TestAccountRefund(t *testing.T) { - ub := &Account{ - BalanceMap: map[string]Balances{ - utils.MONETARY: Balances{ - &Balance{Uuid: "moneya", Value: 100}, - }, - utils.VOICE: Balances{ - &Balance{Uuid: "minutea", Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, - &Balance{Uuid: "minuteb", Value: 10, DestinationIDs: utils.StringMap{"RET": true}}, - }, - }, - } - increments := Increments{ - &Increment{Cost: 2, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "", MoneyBalanceUuid: "moneya"}}, - &Increment{Cost: 2, Duration: 3 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minutea", MoneyBalanceUuid: "moneya"}}, - &Increment{Duration: 4 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minuteb", MoneyBalanceUuid: ""}}, - } - for _, increment := range increments { - ub.refundIncrement(increment, &CallDescriptor{TOR: utils.VOICE}, false) - } - if ub.BalanceMap[utils.MONETARY][0].GetValue() != 104 || - ub.BalanceMap[utils.VOICE][0].GetValue() != 13 || - ub.BalanceMap[utils.VOICE][1].GetValue() != 14 { - t.Error("Error refunding money: ", ub.BalanceMap[utils.VOICE][1].GetValue()) - } -} - func TestDebitShared(t *testing.T) { cc := &CallCost{ Tenant: "vdf", @@ -1422,7 +1395,7 @@ func TestDebitGenericBalance(t *testing.T) { if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } - if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.4999 || + if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.49999 || rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 21 { t.Logf("%+v", cc.Timespans[0].Increments[0]) t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[utils.GENERIC][0].GetValue(), rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) @@ -1465,7 +1438,7 @@ func TestDebitGenericBalanceWithRatingSubject(t *testing.T) { if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" { t.Error("Error setting balance id to increment: ", cc.Timespans[0]) } - if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.4999 || + if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.49999 || rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 21 { t.Logf("%+v", cc.Timespans[0].Increments[0]) t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[utils.GENERIC][0].GetValue(), rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) diff --git a/engine/balances.go b/engine/balances.go index da0853858..2b75d7a49 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -357,6 +357,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala b.SubstractValue(amount) inc.BalanceInfo.UnitBalanceUuid = b.Uuid inc.BalanceInfo.AccountId = ub.ID + inc.BalanceInfo.RateInterval = nil inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR} inc.Cost = 0 inc.paid = true @@ -428,6 +429,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala cost, inc.Cost = 0.0, 0.0 inc.BalanceInfo.MoneyBalanceUuid = b.Uuid inc.BalanceInfo.AccountId = ub.ID + inc.BalanceInfo.RateInterval = ts.RateInterval inc.paid = true if count { ub.countUnits(cost, utils.MONETARY, cc, b) @@ -446,6 +448,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala b.SubstractValue(amount) inc.BalanceInfo.UnitBalanceUuid = b.Uuid inc.BalanceInfo.AccountId = ub.ID + inc.BalanceInfo.RateInterval = nil inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR} if cost != 0 { inc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid @@ -535,6 +538,9 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala amount, inc.Cost = 0.0, 0.0 inc.BalanceInfo.MoneyBalanceUuid = b.Uuid inc.BalanceInfo.AccountId = ub.ID + if b.RatingSubject != "" { + inc.BalanceInfo.RateInterval = ts.RateInterval + } inc.paid = true if count { ub.countUnits(amount, utils.MONETARY, cc, b) @@ -550,6 +556,9 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala cd.MaxCostSoFar += amount inc.BalanceInfo.MoneyBalanceUuid = b.Uuid inc.BalanceInfo.AccountId = ub.ID + if b.RatingSubject != "" { + inc.BalanceInfo.RateInterval = ts.RateInterval + } inc.paid = true if count { ub.countUnits(amount, utils.MONETARY, cc, b) diff --git a/engine/callcost.go b/engine/callcost.go index 85c8a3c4b..8ced3397f 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -136,13 +136,12 @@ func (cc *CallCost) ToDataCost() (*DataCost, error) { dc.DataSpans[i].Increments = make([]*DataIncrement, len(ts.Increments)) for j, incr := range ts.Increments { dc.DataSpans[i].Increments[j] = &DataIncrement{ - Amount: incr.Duration.Seconds(), - Cost: incr.Cost, - BalanceInfo: incr.BalanceInfo, - BalanceRateInterval: incr.BalanceRateInterval, - UnitInfo: incr.UnitInfo, - CompressFactor: incr.CompressFactor, - paid: incr.paid, + Amount: incr.Duration.Seconds(), + Cost: incr.Cost, + BalanceInfo: incr.BalanceInfo, + UnitInfo: incr.UnitInfo, + CompressFactor: incr.CompressFactor, + paid: incr.paid, } } } @@ -182,6 +181,43 @@ func (cc *CallCost) updateCost() { cc.Cost = cost } +func (cc *CallCost) Round() { + if len(cc.Timespans) == 0 || cc.Timespans[0] == nil { + return + } + var totalCorrectionCost float64 + for _, ts := range cc.Timespans { + if len(ts.Increments) == 0 { + continue // safe check + } + inc := ts.Increments[0] + if inc.BalanceInfo.MoneyBalanceUuid == "" || inc.Cost == 0 { + // this is a unit payied timespan, nothing to round + continue + } + cost := ts.CalculateCost() + roundedCost := utils.Round(cost, ts.RateInterval.Rating.RoundingDecimals, + ts.RateInterval.Rating.RoundingMethod) + correctionCost := roundedCost - cost + //log.Print(cost, roundedCost, correctionCost) + roundInc := &Increment{ + Cost: correctionCost, + BalanceInfo: inc.BalanceInfo, + } + totalCorrectionCost += correctionCost + ts.Cost += correctionCost + ts.RoundIncrements = append(ts.RoundIncrements, roundInc) + } + cc.Cost += totalCorrectionCost +} + +func (cc *CallCost) GetRoundIncrements() (roundIncrements Increments) { + for _, ts := range cc.Timespans { + roundIncrements = append(roundIncrements, ts.RoundIncrements...) + } + return +} + func (cc *CallCost) MatchCCFilter(bf *BalanceFilter) bool { if bf == nil { return true diff --git a/engine/calldesc.go b/engine/calldesc.go index 022ce27fc..572261f8e 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -150,14 +150,16 @@ type CallDescriptor struct { TOR string // used unit balances selector ExtraFields map[string]string // Extra fields, mostly used for user profile matching // session limits - MaxRate float64 - MaxRateUnit time.Duration - MaxCostSoFar float64 - CgrID string - RunID string - ForceDuration bool // for Max debit if less than duration return err - account *Account - testCallcost *CallCost // testing purpose only! + MaxRate float64 + MaxRateUnit time.Duration + MaxCostSoFar float64 + CgrID string + RunID string + ForceDuration bool // for Max debit if less than duration return err + PerformRounding bool // flag for rating info rounding + DryRun bool + account *Account + testCallcost *CallCost // testing purpose only! } func (cd *CallDescriptor) ValidateCallData() error { @@ -612,6 +614,7 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura } } totalDuration += incr.Duration + //log.Print("INC: ", utils.ToJSON(incr)) if totalDuration >= initialDuration { // we have enough, return //utils.Logger.Debug(fmt.Sprintf("2_INIT DUR %v, TOTAL DUR: %v", initialDuration, totalDuration)) @@ -619,7 +622,8 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura } } } - //utils.Logger.Debug(fmt.Sprintf("3_INIT DUR %v, TOTAL DUR: %v", initialDuration, totalDuration)) + utils.Logger.Debug(fmt.Sprintf("3_INIT DUR %v, TOTAL DUR: %v", initialDuration, totalDuration)) + return utils.MinDuration(initialDuration, totalDuration), nil } @@ -660,9 +664,6 @@ func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool) } return cc, nil } - if !dryRun { - defer accountingStorage.SetAccount(account) - } if cd.TOR == "" { cd.TOR = utils.VOICE } @@ -676,6 +677,15 @@ func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool) cc.updateCost() cc.UpdateRatedUsage() cc.Timespans.Compress() + if !dryRun { + accountingStorage.SetAccount(account) + } + if cd.PerformRounding { + cc.Round() + rcd := cc.CreateCallDescriptor() + rcd.Increments = cc.GetRoundIncrements() + rcd.Round() + } //log.Printf("OUT CC: ", cc) return } @@ -689,26 +699,7 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) { } else { if memberIds, sgerr := account.GetUniqueSharedGroupMembers(cd); sgerr == nil { _, err = Guardian.Guard(func() (interface{}, error) { - cc, err = cd.debit(account, false, true) - return 0, err - }, 0, memberIds.Slice()...) - } else { - return nil, sgerr - } - return cc, err - } -} - -func (cd *CallDescriptor) FakeDebit() (cc *CallCost, err error) { - cd.account = nil // make sure it's not cached - // lock all group members - if account, err := cd.getAccount(); err != nil || account == nil { - utils.Logger.Err(fmt.Sprintf("Account: %s, not found", cd.GetAccountKey())) - return nil, utils.ErrAccountNotFound - } else { - if memberIds, sgerr := account.GetUniqueSharedGroupMembers(cd); sgerr == nil { - _, err = Guardian.Guard(func() (interface{}, error) { - cc, err = cd.debit(account, true, true) + cc, err = cd.debit(account, cd.DryRun, true) return 0, err }, 0, memberIds.Slice()...) } else { @@ -763,7 +754,8 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) { cd.TimeEnd = cd.TimeStart.Add(remainingDuration) cd.DurationIndex -= initialDuration - remainingDuration } - cc, err = cd.debit(account, false, true) + //log.Print("Remaining duration: ", remainingDuration) + cc, err = cd.debit(account, cd.DryRun, true) //log.Print(balanceMap[0].Value, balanceMap[1].Value) return 0, err }, 0, memberIDs.Slice()...) @@ -777,21 +769,17 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) { return cc, err } -func (cd *CallDescriptor) RefundIncrements() (left float64, err error) { - cd.account = nil // make sure it's not cached +func (cd *CallDescriptor) RefundIncrements() error { // get account list for locking // all must be locked in order to use cache - accMap := make(map[string]struct{}) - var accountIDs []string + accMap := make(utils.StringMap) cd.Increments.Decompress() for _, increment := range cd.Increments { - accMap[increment.BalanceInfo.AccountId] = struct{}{} - } - for key := range accMap { - accountIDs = append(accountIDs, key) + accMap[increment.BalanceInfo.AccountId] = true } + accountIDs := accMap.Slice() // start increment refunding loop - Guardian.Guard(func() (interface{}, error) { + _, err := Guardian.Guard(func() (interface{}, error) { accountsCache := make(map[string]*Account) for _, increment := range cd.Increments { account, found := accountsCache[increment.BalanceInfo.AccountId] @@ -808,11 +796,70 @@ func (cd *CallDescriptor) RefundIncrements() (left float64, err error) { continue } //utils.Logger.Info(fmt.Sprintf("Refunding increment %+v", increment)) - account.refundIncrement(increment, cd, true) + var balance *Balance + unitType := cd.TOR + cc := cd.CreateCallCost() + if increment.BalanceInfo.UnitBalanceUuid != "" { + if balance = account.BalanceMap[unitType].GetBalance(increment.BalanceInfo.UnitBalanceUuid); balance == nil { + return 0, nil + } + balance.AddValue(increment.Duration.Seconds()) + account.countUnits(-increment.Duration.Seconds(), unitType, cc, balance) + } + // check money too + if increment.BalanceInfo.MoneyBalanceUuid != "" { + if balance = account.BalanceMap[utils.MONETARY].GetBalance(increment.BalanceInfo.MoneyBalanceUuid); balance == nil { + return 0, nil + } + balance.AddValue(increment.Cost) + account.countUnits(-increment.Cost, utils.MONETARY, cc, balance) + } } - return 0, err + return 0, nil }, 0, accountIDs...) - return 0, err + return err +} + +func (cd *CallDescriptor) Round() error { + // get account list for locking + // all must be locked in order to use cache + accMap := make(utils.StringMap) + for _, inc := range cd.Increments { + accMap[inc.BalanceInfo.AccountId] = true + } + accountIDs := accMap.Slice() + // start increment refunding loop + _, err := Guardian.Guard(func() (interface{}, error) { + accountsCache := make(map[string]*Account) + for _, increment := range cd.Increments { + account, found := accountsCache[increment.BalanceInfo.AccountId] + if !found { + if acc, err := accountingStorage.GetAccount(increment.BalanceInfo.AccountId); err == nil && acc != nil { + account = acc + accountsCache[increment.BalanceInfo.AccountId] = account + // will save the account only once at the end of the function + defer accountingStorage.SetAccount(account) + } + } + if account == nil { + utils.Logger.Warning(fmt.Sprintf("Could not get the account to be refunded: %s", increment.BalanceInfo.AccountId)) + continue + } + cc := cd.CreateCallCost() + if increment.BalanceInfo.MoneyBalanceUuid != "" { + var balance *Balance + if balance = account.BalanceMap[utils.MONETARY].GetBalance(increment.BalanceInfo.MoneyBalanceUuid); balance == nil { + return 0, nil + } + //log.Print("BEFORE: ", balance.GetValue(), increment.Cost) + balance.AddValue(-increment.Cost) + //log.Print("AFTER: ", balance.GetValue(), increment.Cost) + account.countUnits(increment.Cost, utils.MONETARY, cc, balance) + } + } + return 0, nil + }, 0, accountIDs...) + return err } func (cd *CallDescriptor) FlushCache() (err error) { @@ -855,7 +902,10 @@ func (cd *CallDescriptor) Clone() *CallDescriptor { FallbackSubject: cd.FallbackSubject, //RatingInfos: cd.RatingInfos, //Increments: cd.Increments, - TOR: cd.TOR, + TOR: cd.TOR, + ForceDuration: cd.ForceDuration, + PerformRounding: cd.PerformRounding, + DryRun: cd.DryRun, } } diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 617a41e55..0ac51f88f 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -566,7 +566,7 @@ func TestGetMaxSessiontWithBlocker(t *testing.T) { MaxCostSoFar: 0, } result, err := cd.GetMaxSessionDuration() - expected := 985 * time.Second + expected := 30 * time.Minute if result != expected || err != nil { t.Errorf("Expected %v was %v (%v)", expected, result, err) } @@ -630,7 +630,7 @@ func TestGetCostRoundingIssue(t *testing.T) { MaxCostSoFar: 0, } cc, err := cd.GetCost() - expected := 0.39 + expected := 0.17 if cc.Cost != expected || err != nil { t.Log(utils.ToIJSON(cc)) t.Errorf("Expected %v was %+v", expected, cc) @@ -752,25 +752,27 @@ func TestGetCostMaxDebitRoundingIssue(t *testing.T) { at.Execute() } cd := &CallDescriptor{ - Direction: "*out", - Category: "call", - Tenant: "cgrates.org", - Subject: "dy", - Account: "dy", - Destination: "0723123113", - TimeStart: time.Date(2015, 10, 26, 13, 29, 27, 0, time.UTC), - TimeEnd: time.Date(2015, 10, 26, 13, 29, 51, 0, time.UTC), - MaxCostSoFar: 0, + Direction: "*out", + Category: "call", + Tenant: "cgrates.org", + Subject: "dy", + Account: "dy", + Destination: "0723123113", + TimeStart: time.Date(2015, 10, 26, 13, 29, 27, 0, time.UTC), + TimeEnd: time.Date(2015, 10, 26, 13, 29, 51, 0, time.UTC), + MaxCostSoFar: 0, + PerformRounding: true, } acc, err := accountingStorage.GetAccount("cgrates.org:dy") if err != nil || acc.BalanceMap[utils.MONETARY][0].Value != 1 { t.Errorf("Error getting account: %+v (%v)", utils.ToIJSON(acc), err) } + cc, err := cd.MaxDebit() - expected := 0.39 + expected := 0.17 if cc.Cost != expected || err != nil { t.Log(utils.ToIJSON(cc)) - t.Errorf("Expected %v was %+v", expected, cc) + t.Errorf("Expected %v was %+v (%v)", expected, cc, err) } acc, err = accountingStorage.GetAccount("cgrates.org:dy") if err != nil || acc.BalanceMap[utils.MONETARY][0].Value != 1-expected { @@ -1402,6 +1404,35 @@ func TestCDDataGetCost(t *testing.T) { } } +func TestCDRefundIncrements(t *testing.T) { + ub := &Account{ + ID: "test:ref", + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ + &Balance{Uuid: "moneya", Value: 100}, + }, + utils.VOICE: Balances{ + &Balance{Uuid: "minutea", Value: 10, Weight: 20, DestinationIDs: utils.StringMap{"NAT": true}}, + &Balance{Uuid: "minuteb", Value: 10, DestinationIDs: utils.StringMap{"RET": true}}, + }, + }, + } + accountingStorage.SetAccount(ub) + increments := Increments{ + &Increment{Cost: 2, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "", MoneyBalanceUuid: "moneya", AccountId: ub.ID}}, + &Increment{Cost: 2, Duration: 3 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minutea", MoneyBalanceUuid: "moneya", AccountId: ub.ID}}, + &Increment{Duration: 4 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minuteb", MoneyBalanceUuid: "", AccountId: ub.ID}}, + } + cd := &CallDescriptor{TOR: utils.VOICE, Increments: increments} + cd.RefundIncrements() + ub, _ = accountingStorage.GetAccount(ub.ID) + if ub.BalanceMap[utils.MONETARY][0].GetValue() != 104 || + ub.BalanceMap[utils.VOICE][0].GetValue() != 13 || + ub.BalanceMap[utils.VOICE][1].GetValue() != 14 { + t.Error("Error refunding money: ", ub.BalanceMap[utils.VOICE][1].GetValue()) + } +} + /*************** BENCHMARKS ********************/ func BenchmarkStorageGetting(b *testing.B) { b.StopTimer() diff --git a/engine/responder.go b/engine/responder.go index a37c222f8..0fc5f6f36 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -150,41 +150,6 @@ func (rs *Responder) Debit(arg *CallDescriptor, reply *CallCost) (err error) { return } -func (rs *Responder) FakeDebit(arg *CallDescriptor, reply *CallCost) (err error) { - if arg.Subject == "" { - arg.Subject = arg.Account - } - // replace aliases - if err := LoadAlias( - &AttrMatchingAlias{ - Destination: arg.Destination, - Direction: arg.Direction, - Tenant: arg.Tenant, - Category: arg.Category, - Account: arg.Account, - Subject: arg.Subject, - Context: utils.ALIAS_CONTEXT_RATING, - }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { - return err - } - // replace user profile fields - if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { - return err - } - if rs.Bal != nil { - r, e := rs.getCallCost(arg, "Responder.FakeDebit") - *reply, err = *r, e - } else { - r, e := arg.FakeDebit() - if e != nil { - return e - } else if r != nil { - *reply = *r - } - } - return -} - func (rs *Responder) MaxDebit(arg *CallDescriptor, reply *CallCost) (err error) { if item, err := rs.getCache().Get(utils.MAX_DEBIT_CACHE_PREFIX + arg.CgrID + arg.RunID); err == nil && item != nil { *reply = *(item.Value.(*CallCost)) @@ -259,7 +224,7 @@ func (rs *Responder) RefundIncrements(arg *CallDescriptor, reply *float64) (err if rs.Bal != nil { *reply, err = rs.callMethod(arg, "Responder.RefundIncrements") } else { - *reply, err = arg.RefundIncrements() + err = arg.RefundIncrements() } rs.getCache().Cache(utils.REFUND_INCR_CACHE_PREFIX+arg.CgrID+arg.RunID, &cache2go.CacheItem{ Value: reply, diff --git a/engine/timespans.go b/engine/timespans.go index 6535a2a73..4f0a74de5 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -37,18 +37,18 @@ type TimeSpan struct { RateInterval *RateInterval DurationIndex time.Duration // the call duration so far till TimeEnd Increments Increments + RoundIncrements Increments MatchedSubject, MatchedPrefix, MatchedDestId, RatingPlanId string CompressFactor int } type Increment struct { - Duration time.Duration - Cost float64 - BalanceInfo *BalanceInfo // need more than one for units with cost - BalanceRateInterval *RateInterval - UnitInfo *UnitInfo - CompressFactor int - paid bool + Duration time.Duration + Cost float64 + BalanceInfo *BalanceInfo // need more than one for units with cost + UnitInfo *UnitInfo + CompressFactor int + paid bool } // Holds the minute information related to a specified timespan @@ -69,12 +69,14 @@ func (mi *UnitInfo) Equal(other *UnitInfo) bool { type BalanceInfo struct { UnitBalanceUuid string MoneyBalanceUuid string + RateInterval *RateInterval AccountId string // used when debited from shared balance } func (bi *BalanceInfo) Equal(other *BalanceInfo) bool { return bi.UnitBalanceUuid == other.UnitBalanceUuid && bi.MoneyBalanceUuid == other.MoneyBalanceUuid && + reflect.DeepEqual(bi.RateInterval, other.RateInterval) && bi.AccountId == other.AccountId } @@ -213,11 +215,10 @@ func (tss *TimeSpans) Decompress() { // must be pointer receiver func (incr *Increment) Clone() *Increment { nIncr := &Increment{ - Duration: incr.Duration, - Cost: incr.Cost, - BalanceRateInterval: incr.BalanceRateInterval, - UnitInfo: incr.UnitInfo, - BalanceInfo: incr.BalanceInfo, + Duration: incr.Duration, + Cost: incr.Cost, + UnitInfo: incr.UnitInfo, + BalanceInfo: incr.BalanceInfo, } return nIncr } @@ -226,7 +227,6 @@ func (incr *Increment) Equal(other *Increment) bool { return incr.Duration == other.Duration && incr.Cost == other.Cost && ((incr.BalanceInfo == nil && other.BalanceInfo == nil) || incr.BalanceInfo.Equal(other.BalanceInfo)) && - ((incr.BalanceRateInterval == nil && other.BalanceRateInterval == nil) || reflect.DeepEqual(incr.BalanceRateInterval, other.BalanceRateInterval)) && ((incr.UnitInfo == nil && other.UnitInfo == nil) || incr.UnitInfo.Equal(other.UnitInfo)) } @@ -352,10 +352,7 @@ func (ts *TimeSpan) createIncrementsSlice() { //incrementCost := rate / rateUnit.Seconds() * rateIncrement.Seconds() nbIncrements := int(ts.GetDuration() / rateIncrement) incrementCost := ts.CalculateCost() / float64(nbIncrements) - // no more rounding at increment leve as it deviates from intended cost - // move rounding at highest point possible - //incrementCost = utils.Round(incrementCost, ts.RateInterval.Rating.RoundingDecimals, - // ts.RateInterval.Rating.RoundingMethod) + incrementCost = utils.Round(incrementCost, globalRoundingDecimals, utils.ROUNDING_MIDDLE) for s := 0; s < nbIncrements; s++ { inc := &Increment{ Duration: rateIncrement, diff --git a/engine/timespans_test.go b/engine/timespans_test.go index 225cba090..b797dff86 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -742,7 +742,7 @@ func TestTSTimespanCreateIncrements(t *testing.T) { if len(ts.Increments) != 3 { t.Error("Error creating increment slice: ", len(ts.Increments)) } - if len(ts.Increments) < 3 || ts.Increments[2].Cost != 20.07 { + if len(ts.Increments) < 3 || ts.Increments[2].Cost != 20.066667 { t.Error("Wrong second slice: ", ts.Increments[2].Cost) } } @@ -1510,39 +1510,34 @@ func TestTSIncrementsCompressDecompress(t *testing.T) { &TimeSpan{ Increments: Increments{ &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", "3"}, - BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", "3"}, - BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", "3"}, - BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", "3"}, - BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", "3"}, - BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, }, }, @@ -1562,39 +1557,34 @@ func TestTSMultipleIncrementsCompressDecompress(t *testing.T) { &TimeSpan{ Increments: Increments{ &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", "3"}, - BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", "3"}, - BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", "3"}, - BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", "3"}, - BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", "3"}, - BalanceRateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, }, }, From 8fb0b7a8b1c2991f7cb8a31411939fb4af1d0686 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 3 Mar 2016 09:06:47 +0100 Subject: [PATCH 131/199] Diameter fix Unsigned64 serialization with test attached --- agents/dmtagent_it_test.go | 32 ++++++++++++++++++++++++++++++++ agents/libdmt.go | 10 ++++++++-- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 8ec97b1ba..c9c500421 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -154,6 +154,38 @@ func TestDmtAgentCCRAsSMGenericEvent(t *testing.T) { } } +func TestDmtAgentPopulateCCTotalOctets(t *testing.T) { + if !*testIntegration { + return + } + daRP := &config.DARequestProcessor{CCAFields: []*config.CfgCdrField{ + &config.CfgCdrField{Tag: "GrantedUnit", FieldFilter: utils.ParseRSRFieldsMustCompile("CGRError(^$)", utils.INFIELD_SEP), + FieldId: "Multiple-Services-Credit-Control>Granted-Service-Unit>CC-Time", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("CGRMaxUsage", utils.INFIELD_SEP), Mandatory: true}, + &config.CfgCdrField{Tag: "GrantedOctet", FieldFilter: utils.ParseRSRFieldsMustCompile("CGRError(^$)", utils.INFIELD_SEP), + FieldId: "Multiple-Services-Credit-Control>Granted-Service-Unit>CC-Total-Octets", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("CGRMaxUsage", utils.INFIELD_SEP), Mandatory: true}, + }} + ccr := new(CCR) + ccr.diamMessage = ccr.AsBareDiameterMessage() + cca := NewBareCCAFromCCR(ccr, "cgr-da", "cgrates.org") + if err := cca.SetProcessorAVPs(daRP, map[string]string{CGRError: "", CGRMaxUsage: "153600"}); err != nil { + t.Error(err) + } + if avps, err := cca.diamMessage.FindAVPsWithPath([]interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Time"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Not found") + } else if strResult := avpValAsString(avps[0]); strResult != "153600" { // Result-Code set in the template + t.Errorf("Expecting 153600, received: %s", strResult) + } + if avps, err := cca.diamMessage.FindAVPsWithPath([]interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Total-Octets"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Not found") + } else if strResult := avpValAsString(avps[0]); strResult != "153600" { // Result-Code set in the template + t.Errorf("Expecting 153600, received: %s", strResult) + } +} + // Connect rpc client to rater func TestDmtAgentApierRpcConn(t *testing.T) { if !*testIntegration { diff --git a/agents/libdmt.go b/agents/libdmt.go index 47be00d5a..fd53bff95 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -326,12 +326,18 @@ func serializeAVPValueFromString(dictAVP *dict.AVP, valStr, timezone string) ([] return []byte(valStr), nil case datatype.AddressType: return []byte(net.ParseIP(valStr)), nil - case datatype.EnumeratedType, datatype.Integer32Type, datatype.Integer64Type, datatype.Unsigned32Type, datatype.Unsigned64Type: + case datatype.EnumeratedType, datatype.Integer32Type, datatype.Integer64Type, datatype.Unsigned32Type: i, err := strconv.Atoi(valStr) if err != nil { return nil, err } return datatype.Enumerated(i).Serialize(), nil + case datatype.Unsigned64Type: + i, err := strconv.ParseInt(valStr, 10, 64) + if err != nil { + return nil, err + } + return datatype.Unsigned64(i).Serialize(), nil case datatype.Float32Type: f, err := strconv.ParseFloat(valStr, 32) if err != nil { @@ -436,7 +442,7 @@ func messageSetAVPsWithPath(m *diam.Message, path []interface{}, avpValStr strin if err != nil { return err } - typeVal, err = datatype.Decode(dictAVPs[i].Data.Type, avpValByte) + typeVal, err = datatype.Decode(dictAVPs[i].Data.Type, avpValByte) // Check here if err != nil { return err } From f71e14d8d452872c9d2c9c4f78037f38fe0a043a Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 3 Mar 2016 14:42:03 +0200 Subject: [PATCH 132/199] integrated rounding in session managers --- engine/callcost.go | 1 + engine/calldesc.go | 4 +-- engine/responder.go | 62 ++++++++++++++++++++++++++++++++++ sessionmanager/session.go | 22 ++++++------ sessionmanager/session_test.go | 3 ++ sessionmanager/smg_session.go | 20 +++++------ sessionmanager/smgeneric.go | 8 +++++ utils/consts.go | 1 + 8 files changed, 97 insertions(+), 24 deletions(-) diff --git a/engine/callcost.go b/engine/callcost.go index 8ced3397f..502bd0754 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -90,6 +90,7 @@ func (cc *CallCost) CreateCallDescriptor() *CallDescriptor { Subject: cc.Subject, Account: cc.Account, Destination: cc.Destination, + TOR: cc.TOR, } } diff --git a/engine/calldesc.go b/engine/calldesc.go index 572261f8e..12dd637cc 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -684,7 +684,7 @@ func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool) cc.Round() rcd := cc.CreateCallDescriptor() rcd.Increments = cc.GetRoundIncrements() - rcd.Round() + rcd.RefundRounding() } //log.Printf("OUT CC: ", cc) return @@ -820,7 +820,7 @@ func (cd *CallDescriptor) RefundIncrements() error { return err } -func (cd *CallDescriptor) Round() error { +func (cd *CallDescriptor) RefundRounding() error { // get account list for locking // all must be locked in order to use cache accMap := make(utils.StringMap) diff --git a/engine/responder.go b/engine/responder.go index 0fc5f6f36..48f337ab2 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -233,6 +233,43 @@ func (rs *Responder) RefundIncrements(arg *CallDescriptor, reply *float64) (err return } +func (rs *Responder) RefundRounding(arg *CallDescriptor, reply *float64) (err error) { + if item, err := rs.getCache().Get(utils.REFUND_ROUND_CACHE_PREFIX + arg.CgrID + arg.RunID); err == nil && item != nil { + *reply = *(item.Value.(*float64)) + return item.Err + } + if arg.Subject == "" { + arg.Subject = arg.Account + } + // replace aliases + if err := LoadAlias( + &AttrMatchingAlias{ + Destination: arg.Destination, + Direction: arg.Direction, + Tenant: arg.Tenant, + Category: arg.Category, + Account: arg.Account, + Subject: arg.Subject, + Context: utils.ALIAS_CONTEXT_RATING, + }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { + return err + } + // replace user profile fields + if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { + return err + } + if rs.Bal != nil { + *reply, err = rs.callMethod(arg, "Responder.RefundRounding") + } else { + err = arg.RefundRounding() + } + rs.getCache().Cache(utils.REFUND_ROUND_CACHE_PREFIX+arg.CgrID+arg.RunID, &cache2go.CacheItem{ + Value: reply, + Err: err, + }) + return +} + func (rs *Responder) GetMaxSessionTime(arg *CallDescriptor, reply *float64) (err error) { if arg.Subject == "" { arg.Subject = arg.Account @@ -683,6 +720,7 @@ type Connector interface { Debit(*CallDescriptor, *CallCost) error MaxDebit(*CallDescriptor, *CallCost) error RefundIncrements(*CallDescriptor, *float64) error + RefundRounding(*CallDescriptor, *float64) error GetMaxSessionTime(*CallDescriptor, *float64) error GetDerivedChargers(*utils.AttrDerivedChargers, *utils.DerivedChargers) error GetDerivedMaxSessionTime(*CDR, *float64) error @@ -714,6 +752,10 @@ func (rcc *RPCClientConnector) RefundIncrements(cd *CallDescriptor, resp *float6 return rcc.Client.Call("Responder.RefundIncrements", cd, resp) } +func (rcc *RPCClientConnector) RefundRounding(cd *CallDescriptor, resp *float64) error { + return rcc.Client.Call("Responder.RefundRounding", cd, resp) +} + func (rcc *RPCClientConnector) GetMaxSessionTime(cd *CallDescriptor, resp *float64) error { return rcc.Client.Call("Responder.GetMaxSessionTime", cd, resp) } @@ -829,6 +871,26 @@ func (cp ConnectorPool) RefundIncrements(cd *CallDescriptor, resp *float64) erro return utils.ErrTimedOut } +func (cp ConnectorPool) RefundRounding(cd *CallDescriptor, resp *float64) error { + for _, con := range cp { + c := make(chan error, 1) + var r float64 + + var timeout time.Duration + con.GetTimeout(0, &timeout) + + go func() { c <- con.RefundRounding(cd, &r) }() + select { + case err := <-c: + *resp = r + return err + case <-time.After(timeout): + // call timed out, continue + } + } + return utils.ErrTimedOut +} + func (cp ConnectorPool) GetMaxSessionTime(cd *CallDescriptor, resp *float64) error { for _, con := range cp { c := make(chan error, 1) diff --git a/sessionmanager/session.go b/sessionmanager/session.go index 91fb952d2..0be6f663c 100644 --- a/sessionmanager/session.go +++ b/sessionmanager/session.go @@ -195,21 +195,12 @@ func (s *Session) Refund(lastCC *engine.CallCost, hangupTime time.Time) error { // show only what was actualy refunded (stopped in timespan) // utils.Logger.Info(fmt.Sprintf("Refund duration: %v", initialRefundDuration-refundDuration)) if len(refundIncrements) > 0 { - cd := &engine.CallDescriptor{ - Direction: lastCC.Direction, - Tenant: lastCC.Tenant, - Category: lastCC.Category, - Subject: lastCC.Subject, - Account: lastCC.Account, - Destination: lastCC.Destination, - TOR: lastCC.TOR, - Increments: refundIncrements, - } + cd := lastCC.CreateCallDescriptor() + cd.Increments = refundIncrements cd.Increments.Compress() utils.Logger.Info(fmt.Sprintf("Refunding duration %v with cd: %+v", refundDuration, cd)) var response float64 - err := s.sessionManager.Rater().RefundIncrements(cd, &response) - if err != nil { + if err := s.sessionManager.Rater().RefundIncrements(cd, &response); err != nil { return err } } @@ -238,6 +229,13 @@ func (s *Session) SaveOperations() { } firstCC.Timespans.Compress() + firstCC.Round() + cd := firstCC.CreateCallDescriptor() + cd.Increments = firstCC.GetRoundIncrements() + var response float64 + if err := s.sessionManager.Rater().RefundRounding(cd, &response); err != nil { + utils.Logger.Err(fmt.Sprintf(" ERROR failed to refund rounding: %v", err)) + } var reply string err := s.sessionManager.CdrSrv().LogCallCost(&engine.CallCostLog{ CgrId: s.eventStart.GetCgrId(s.sessionManager.Timezone()), diff --git a/sessionmanager/session_test.go b/sessionmanager/session_test.go index c391ad41b..209577806 100644 --- a/sessionmanager/session_test.go +++ b/sessionmanager/session_test.go @@ -91,6 +91,9 @@ func (mc *MockConnector) RefundIncrements(cd *engine.CallDescriptor, reply *floa mc.refundCd = cd return nil } +func (mc *MockConnector) RefundRounding(cd *engine.CallDescriptor, reply *float64) error { + return nil +} func (mc *MockConnector) GetMaxSessionTime(*engine.CallDescriptor, *float64) error { return nil } func (mc *MockConnector) GetDerivedChargers(*utils.AttrDerivedChargers, *utils.DerivedChargers) error { return nil diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index d097b91e5..019cc5cbc 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -147,16 +147,8 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { // show only what was actualy refunded (stopped in timespan) // utils.Logger.Info(fmt.Sprintf("Refund duration: %v", initialRefundDuration-refundDuration)) if len(refundIncrements) > 0 { - cd := &engine.CallDescriptor{ - Direction: lastCC.Direction, - Tenant: lastCC.Tenant, - Category: lastCC.Category, - Subject: lastCC.Subject, - Account: lastCC.Account, - Destination: lastCC.Destination, - TOR: lastCC.TOR, - Increments: refundIncrements, - } + cd := lastCC.CreateCallDescriptor() + cd.Increments = refundIncrements cd.Increments.Compress() utils.Logger.Info(fmt.Sprintf("Refunding duration %v with cd: %s", initialRefundDuration, utils.ToJSON(cd))) var response float64 @@ -212,6 +204,14 @@ func (self *SMGSession) saveOperations() error { firstCC.Merge(cc) } firstCC.Timespans.Compress() + firstCC.Round() + cd := firstCC.CreateCallDescriptor() + cd.Increments = firstCC.GetRoundIncrements() + var response float64 + if err := self.rater.RefundRounding(cd, &response); err != nil { + return err + } + var reply string err := self.cdrsrv.LogCallCost(&engine.CallCostLog{ CgrId: self.eventStart.GetCgrId(self.timezone), diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index f611fb317..62736581c 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -277,6 +277,14 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu cc.Merge(ccSR) } } + cc.Round() + cd := cc.CreateCallDescriptor() + cd.Increments = cc.GetRoundIncrements() + var response float64 + if err := self.rater.RefundRounding(cd, &response); err != nil { + utils.Logger.Err(fmt.Sprintf(" ERROR failed to refund rounding: %v", err)) + } + var reply string if err := self.cdrsrv.LogCallCost(&engine.CallCostLog{ CgrId: gev.GetCgrId(self.timezone), diff --git a/utils/consts.go b/utils/consts.go index 579583ba8..a2324b8e0 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -234,6 +234,7 @@ const ( OSIPS_FLATSTORE = "opensips_flatstore" MAX_DEBIT_CACHE_PREFIX = "MAX_DEBIT_" REFUND_INCR_CACHE_PREFIX = "REFUND_INCR_" + REFUND_ROUND_CACHE_PREFIX = "REFUND_ROUND_" GET_SESS_RUNS_CACHE_PREFIX = "GET_SESS_RUNS_" LOG_CALL_COST_CACHE_PREFIX = "LOG_CALL_COSTS_" ALIAS_CONTEXT_RATING = "*rating" From ce8f7bf927c576152e0ebabc6deb7dffe0ca4e48 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 4 Mar 2016 16:10:29 +0200 Subject: [PATCH 133/199] rounding unit tests --- engine/calldesc.go | 8 ++------ engine/calldesc_test.go | 37 ++++++++++++++++++++++++++++++++++++- engine/timespans.go | 2 +- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/engine/calldesc.go b/engine/calldesc.go index 12dd637cc..c70f8b4eb 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -777,7 +777,6 @@ func (cd *CallDescriptor) RefundIncrements() error { for _, increment := range cd.Increments { accMap[increment.BalanceInfo.AccountId] = true } - accountIDs := accMap.Slice() // start increment refunding loop _, err := Guardian.Guard(func() (interface{}, error) { accountsCache := make(map[string]*Account) @@ -816,7 +815,7 @@ func (cd *CallDescriptor) RefundIncrements() error { } } return 0, nil - }, 0, accountIDs...) + }, 0, accMap.Slice()...) return err } @@ -827,7 +826,6 @@ func (cd *CallDescriptor) RefundRounding() error { for _, inc := range cd.Increments { accMap[inc.BalanceInfo.AccountId] = true } - accountIDs := accMap.Slice() // start increment refunding loop _, err := Guardian.Guard(func() (interface{}, error) { accountsCache := make(map[string]*Account) @@ -851,14 +849,12 @@ func (cd *CallDescriptor) RefundRounding() error { if balance = account.BalanceMap[utils.MONETARY].GetBalance(increment.BalanceInfo.MoneyBalanceUuid); balance == nil { return 0, nil } - //log.Print("BEFORE: ", balance.GetValue(), increment.Cost) balance.AddValue(-increment.Cost) - //log.Print("AFTER: ", balance.GetValue(), increment.Cost) account.countUnits(increment.Cost, utils.MONETARY, cc, balance) } } return 0, nil - }, 0, accountIDs...) + }, 0, accMap.Slice()...) return err } diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 0ac51f88f..49b6cc26a 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -745,7 +745,7 @@ func TestMaxDebitUnknowDest(t *testing.T) { } } -func TestGetCostMaxDebitRoundingIssue(t *testing.T) { +func TestMaxDebitRoundingIssue(t *testing.T) { ap, _ := ratingStorage.GetActionPlan("TOPUP10_AT", false) for _, at := range ap.ActionTimings { at.accountIDs = ap.AccountIDs @@ -780,6 +780,41 @@ func TestGetCostMaxDebitRoundingIssue(t *testing.T) { } } +func TestDebitRoundingRefund(t *testing.T) { + ap, _ := ratingStorage.GetActionPlan("TOPUP10_AT", false) + for _, at := range ap.ActionTimings { + at.accountIDs = ap.AccountIDs + at.Execute() + } + cd := &CallDescriptor{ + Direction: "*out", + Category: "call", + Tenant: "cgrates.org", + Subject: "dy", + Account: "dy", + Destination: "0723123113", + TimeStart: time.Date(2016, 3, 4, 13, 50, 00, 0, time.UTC), + TimeEnd: time.Date(2016, 3, 4, 13, 53, 00, 0, time.UTC), + MaxCostSoFar: 0, + PerformRounding: true, + } + acc, err := accountingStorage.GetAccount("cgrates.org:dy") + if err != nil || acc.BalanceMap[utils.MONETARY][0].Value != 1 { + t.Errorf("Error getting account: %+v (%v)", utils.ToIJSON(acc), err) + } + + cc, err := cd.Debit() + expected := 0.3 + if cc.Cost != expected || err != nil { + t.Log(utils.ToIJSON(cc)) + t.Errorf("Expected %v was %+v (%v)", expected, cc, err) + } + acc, err = accountingStorage.GetAccount("cgrates.org:dy") + if err != nil || acc.BalanceMap[utils.MONETARY][0].Value != 1-expected { + t.Errorf("Error getting account: %+v (%v)", utils.ToIJSON(acc), err) + } +} + func TestMaxSessionTimeWithMaxCostFree(t *testing.T) { ap, _ := ratingStorage.GetActionPlan("TOPUP10_AT", false) for _, at := range ap.ActionTimings { diff --git a/engine/timespans.go b/engine/timespans.go index 4f0a74de5..dea4af215 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -33,13 +33,13 @@ A unit in which a call will be split that has a specific price related interval type TimeSpan struct { TimeStart, TimeEnd time.Time Cost float64 - ratingInfo *RatingInfo RateInterval *RateInterval DurationIndex time.Duration // the call duration so far till TimeEnd Increments Increments RoundIncrements Increments MatchedSubject, MatchedPrefix, MatchedDestId, RatingPlanId string CompressFactor int + ratingInfo *RatingInfo } type Increment struct { From 242bf47bc4a7c39eac95cf97abeccf76e92d25ec Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 4 Mar 2016 18:33:01 +0100 Subject: [PATCH 134/199] Diameter datatype serialization fix --- agents/dmtagent.go | 2 +- agents/libdmt.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 9aa43a31f..8d1751759 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -122,7 +122,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro err = self.smg.Call("SMGenericV1.SessionStart", smgEv, &maxUsage) case 2: err = self.smg.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage) - case 3, 4: + case 3, 4: // Handle them together since we generate CDR for them var rpl string if ccr.CCRequestType == 3 { err = self.smg.Call("SMGenericV1.SessionEnd", smgEv, &rpl) diff --git a/agents/libdmt.go b/agents/libdmt.go index fd53bff95..53c6bc084 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -326,13 +326,13 @@ func serializeAVPValueFromString(dictAVP *dict.AVP, valStr, timezone string) ([] return []byte(valStr), nil case datatype.AddressType: return []byte(net.ParseIP(valStr)), nil - case datatype.EnumeratedType, datatype.Integer32Type, datatype.Integer64Type, datatype.Unsigned32Type: + case datatype.EnumeratedType, datatype.Integer32Type, datatype.Unsigned32Type: i, err := strconv.Atoi(valStr) if err != nil { return nil, err } return datatype.Enumerated(i).Serialize(), nil - case datatype.Unsigned64Type: + case datatype.Unsigned64Type, datatype.Integer64Type: i, err := strconv.ParseInt(valStr, 10, 64) if err != nil { return nil, err From 47778f398d143135e7054c6205a14efbc091959a Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 4 Mar 2016 20:56:09 +0200 Subject: [PATCH 135/199] integration tests fixes --- agents/dmtagent_it_test.go | 2 +- apier/v1/smgenericv1_it_test.go | 2 +- engine/callcost.go | 17 ++++++++++------- engine/calldesc.go | 9 ++++++--- engine/timespans.go | 2 +- sessionmanager/session.go | 13 ++++++++----- sessionmanager/smg_session.go | 13 ++++++++----- sessionmanager/smgeneric.go | 13 ++++++++----- 8 files changed, 43 insertions(+), 28 deletions(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 8ec97b1ba..12d467125 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -623,7 +623,7 @@ func TestDmtAgentCdrs(t *testing.T) { if cdrs[0].Usage != "610" { t.Errorf("Unexpected CDR Usage received, cdr: %+v ", cdrs[0]) } - if cdrs[0].Cost != 0.795 { + if cdrs[0].Cost != 0.7565 { t.Errorf("Unexpected CDR Cost received, cdr: %+v ", cdrs[0]) } } diff --git a/apier/v1/smgenericv1_it_test.go b/apier/v1/smgenericv1_it_test.go index 56c019d3e..6afd87eb4 100644 --- a/apier/v1/smgenericv1_it_test.go +++ b/apier/v1/smgenericv1_it_test.go @@ -151,7 +151,7 @@ func TestSMGV1GetMaxUsage(t *testing.T) { var maxTime float64 if err := smgV1Rpc.Call("SMGenericV1.GetMaxUsage", setupReq, &maxTime); err != nil { t.Error(err) - } else if maxTime != 2690 { + } else if maxTime != 2700 { t.Errorf("Calling ApierV2.MaxUsage got maxTime: %f", maxTime) } } diff --git a/engine/callcost.go b/engine/callcost.go index 502bd0754..fcfd59006 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -201,20 +201,23 @@ func (cc *CallCost) Round() { ts.RateInterval.Rating.RoundingMethod) correctionCost := roundedCost - cost //log.Print(cost, roundedCost, correctionCost) - roundInc := &Increment{ - Cost: correctionCost, - BalanceInfo: inc.BalanceInfo, + if correctionCost != 0 { + ts.RoundIncrement = &Increment{ + Cost: correctionCost, + BalanceInfo: inc.BalanceInfo, + } + totalCorrectionCost += correctionCost + ts.Cost += correctionCost } - totalCorrectionCost += correctionCost - ts.Cost += correctionCost - ts.RoundIncrements = append(ts.RoundIncrements, roundInc) } cc.Cost += totalCorrectionCost } func (cc *CallCost) GetRoundIncrements() (roundIncrements Increments) { for _, ts := range cc.Timespans { - roundIncrements = append(roundIncrements, ts.RoundIncrements...) + if ts.RoundIncrement != nil && ts.RoundIncrement.Cost != 0 { + roundIncrements = append(roundIncrements, ts.RoundIncrement) + } } return } diff --git a/engine/calldesc.go b/engine/calldesc.go index c70f8b4eb..ce2bd49e0 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -682,9 +682,12 @@ func (cd *CallDescriptor) debit(account *Account, dryRun bool, goNegative bool) } if cd.PerformRounding { cc.Round() - rcd := cc.CreateCallDescriptor() - rcd.Increments = cc.GetRoundIncrements() - rcd.RefundRounding() + roundIncrements := cc.GetRoundIncrements() + if len(roundIncrements) != 0 { + rcd := cc.CreateCallDescriptor() + rcd.Increments = roundIncrements + rcd.RefundRounding() + } } //log.Printf("OUT CC: ", cc) return diff --git a/engine/timespans.go b/engine/timespans.go index dea4af215..34aec558b 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -36,7 +36,7 @@ type TimeSpan struct { RateInterval *RateInterval DurationIndex time.Duration // the call duration so far till TimeEnd Increments Increments - RoundIncrements Increments + RoundIncrement *Increment MatchedSubject, MatchedPrefix, MatchedDestId, RatingPlanId string CompressFactor int ratingInfo *RatingInfo diff --git a/sessionmanager/session.go b/sessionmanager/session.go index 0be6f663c..31942cfdf 100644 --- a/sessionmanager/session.go +++ b/sessionmanager/session.go @@ -230,11 +230,14 @@ func (s *Session) SaveOperations() { firstCC.Timespans.Compress() firstCC.Round() - cd := firstCC.CreateCallDescriptor() - cd.Increments = firstCC.GetRoundIncrements() - var response float64 - if err := s.sessionManager.Rater().RefundRounding(cd, &response); err != nil { - utils.Logger.Err(fmt.Sprintf(" ERROR failed to refund rounding: %v", err)) + roundIncrements := firstCC.GetRoundIncrements() + if len(roundIncrements) != 0 { + cd := firstCC.CreateCallDescriptor() + cd.Increments = roundIncrements + var response float64 + if err := s.sessionManager.Rater().RefundRounding(cd, &response); err != nil { + utils.Logger.Err(fmt.Sprintf(" ERROR failed to refund rounding: %v", err)) + } } var reply string err := s.sessionManager.CdrSrv().LogCallCost(&engine.CallCostLog{ diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 019cc5cbc..7d6238ec5 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -205,11 +205,14 @@ func (self *SMGSession) saveOperations() error { } firstCC.Timespans.Compress() firstCC.Round() - cd := firstCC.CreateCallDescriptor() - cd.Increments = firstCC.GetRoundIncrements() - var response float64 - if err := self.rater.RefundRounding(cd, &response); err != nil { - return err + roundIncrements := firstCC.GetRoundIncrements() + if len(roundIncrements) != 0 { + cd := firstCC.CreateCallDescriptor() + cd.Increments = roundIncrements + var response float64 + if err := self.rater.RefundRounding(cd, &response); err != nil { + return err + } } var reply string diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index 62736581c..f9e4e27a7 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -278,11 +278,14 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu } } cc.Round() - cd := cc.CreateCallDescriptor() - cd.Increments = cc.GetRoundIncrements() - var response float64 - if err := self.rater.RefundRounding(cd, &response); err != nil { - utils.Logger.Err(fmt.Sprintf(" ERROR failed to refund rounding: %v", err)) + roundIncrements := cc.GetRoundIncrements() + if len(roundIncrements) != 0 { + cd := cc.CreateCallDescriptor() + cd.Increments = roundIncrements + var response float64 + if err := self.rater.RefundRounding(cd, &response); err != nil { + utils.Logger.Err(fmt.Sprintf(" ERROR failed to refund rounding: %v", err)) + } } var reply string From 3123fed48052f8ff4357f81abff296975edf322f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 4 Mar 2016 21:46:45 +0200 Subject: [PATCH 136/199] more integration tests fixes --- agents/dmtagent_it_test.go | 8 ++++---- general_tests/tutorial_local_test.go | 10 +++++----- sessionmanager/smg_it_test.go | 4 ++-- utils/utils_test.go | 13 +++++++++++++ 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 12d467125..fa3463ed6 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -226,7 +226,7 @@ func TestDmtAgentSendCCRInit(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.484 + eAcntVal := 9.5008 if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { @@ -265,7 +265,7 @@ func TestDmtAgentSendCCRUpdate(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.214 + eAcntVal := 9.2518 if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { @@ -304,7 +304,7 @@ func TestDmtAgentSendCCRUpdate2(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 8.944000 + eAcntVal := 9.0028 if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if utils.Round(acnt.BalanceMap[utils.MONETARY].GetTotalValue(), 5, utils.ROUNDING_MIDDLE) != eAcntVal { @@ -345,7 +345,7 @@ func TestDmtAgentSendCCRTerminate(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.205 + eAcntVal := 9.2435 if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { // Should also consider derived charges which double the cost of 6m10s - 2x0.7584 diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index 991ee3428..9d67d1551 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -230,7 +230,7 @@ func TestTutLocalGetCosts(t *testing.T) { } if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil { t.Error("Got error on Responder.GetCost: ", err.Error()) - } else if cc.Cost != 1.3002 { + } else if cc.Cost != 1.3 { t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost) } tStart, _ = utils.ParseDate("2014-08-04T13:00:00Z") @@ -266,7 +266,7 @@ func TestTutLocalGetCosts(t *testing.T) { } if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil { t.Error("Got error on Responder.GetCost: ", err.Error()) - } else if cc.Cost != 1.3002 { + } else if cc.Cost != 1.3 { t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost) } tStart = time.Date(2014, 8, 4, 13, 0, 0, 0, time.UTC) @@ -342,7 +342,7 @@ func TestTutLocalGetCosts(t *testing.T) { } if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil { t.Error("Got error on Responder.GetCost: ", err.Error()) - } else if cc.Cost != 0.327 { // + } else if cc.Cost != 0.3249 { // t.Errorf("Calling Responder.GetCost got callcost: %s", cc.AsJSON()) } cd = engine.CallDescriptor{ @@ -357,7 +357,7 @@ func TestTutLocalGetCosts(t *testing.T) { } if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil { t.Error("Got error on Responder.GetCost: ", err.Error()) - } else if cc.Cost != 1.3002 { // + } else if cc.Cost != 1.3 { // t.Errorf("Calling Responder.GetCost got callcost: %s", cc.AsJSON()) } cd = engine.CallDescriptor{ @@ -372,7 +372,7 @@ func TestTutLocalGetCosts(t *testing.T) { } if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil { t.Error("Got error on Responder.GetCost: ", err.Error()) - } else if cc.Cost != 0.354 { // + } else if cc.Cost != 0.3498 { // t.Errorf("Calling Responder.GetCost got callcost: %s", cc.AsJSON()) } } diff --git a/sessionmanager/smg_it_test.go b/sessionmanager/smg_it_test.go index f29f5a598..6dd2a282d 100644 --- a/sessionmanager/smg_it_test.go +++ b/sessionmanager/smg_it_test.go @@ -138,7 +138,7 @@ func TestSMGMonetaryRefund(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 8.699800 + eAcntVal := 8.70001 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { @@ -268,7 +268,7 @@ func TestSMGMixedRefund(t *testing.T) { //var acnt *engine.Account //attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} eVoiceVal := 90.0 - eMoneyVal := 8.739 + eMoneyVal := 8.7399 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eVoiceVal || diff --git a/utils/utils_test.go b/utils/utils_test.go index adca2dfad..1edd0ada0 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -50,6 +50,19 @@ func TestRoundUp(t *testing.T) { } } +func TestRoundUpTwice(t *testing.T) { + result := Round(0.641666666667, 4, ROUNDING_UP) + expected := 0.6417 + if result != expected { + t.Errorf("Error rounding up: sould be %v was %v", expected, result) + } + result = Round(result, 4, ROUNDING_UP) + expected = 0.6417 + if result != expected { + t.Errorf("Error rounding up: sould be %v was %v", expected, result) + } +} + func TestRoundUpMiddle(t *testing.T) { result := Round(12.5, 0, ROUNDING_UP) expected := 13.0 From 25e8559568ca6e294187abd8fbba6d1c6e23985c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 4 Mar 2016 21:53:26 +0200 Subject: [PATCH 137/199] small fixes --- agents/dmtagent_it_test.go | 2 +- general_tests/tutorial_local_test.go | 2 +- sessionmanager/smg_it_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index fa3463ed6..85cc3c3d6 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -345,7 +345,7 @@ func TestDmtAgentSendCCRTerminate(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.2435 + eAcntVal := 9.243500 if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { // Should also consider derived charges which double the cost of 6m10s - 2x0.7584 diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index 9d67d1551..c48066596 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -194,7 +194,7 @@ func TestTutLocalGetCosts(t *testing.T) { } if err := tutLocalRpc.Call("Responder.GetCost", cd, &cc); err != nil { t.Error("Got error on Responder.GetCost: ", err.Error()) - } else if cc.Cost != 0.6425 { // 0.01 first minute, 0.04 25 seconds with RT_20CNT + } else if cc.Cost != 0.6418 { // 0.01 first minute, 0.04 25 seconds with RT_20CNT t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost) } tStart, _ = utils.ParseDate("2014-08-04T13:00:00Z") diff --git a/sessionmanager/smg_it_test.go b/sessionmanager/smg_it_test.go index 6dd2a282d..80df57462 100644 --- a/sessionmanager/smg_it_test.go +++ b/sessionmanager/smg_it_test.go @@ -138,7 +138,7 @@ func TestSMGMonetaryRefund(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 8.70001 + eAcntVal := 8.700010 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { From ca0d29d605bad49c00d72548699afb0519337294 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Sat, 5 Mar 2016 11:23:03 +0200 Subject: [PATCH 138/199] GetTotalValue rounding --- agents/dmtagent_it_test.go | 2 +- engine/balances.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 85cc3c3d6..4b014f85a 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -349,7 +349,7 @@ func TestDmtAgentSendCCRTerminate(t *testing.T) { if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { // Should also consider derived charges which double the cost of 6m10s - 2x0.7584 - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + t.Errorf("Expected: %v, received: %v", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) } } diff --git a/engine/balances.go b/engine/balances.go index 2b75d7a49..f089c6253 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -617,6 +617,7 @@ func (bc Balances) GetTotalValue() (total float64) { total += b.GetValue() } } + total = utils.Round(total, globalRoundingDecimals, utils.ROUNDING_MIDDLE) return } From f3c65f7614308ffc2a31138c20bb405fffb2fbc9 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Sat, 5 Mar 2016 11:42:50 +0200 Subject: [PATCH 139/199] postpaid rounding --- engine/cdrs.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/engine/cdrs.go b/engine/cdrs.go index f4a5b0714..1911477b6 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -352,16 +352,17 @@ func (self *CdrServer) getCostFromRater(cdr *CDR) (*CallCost, error) { timeStart = cdr.SetupTime } cd := &CallDescriptor{ - TOR: cdr.ToR, - Direction: cdr.Direction, - Tenant: cdr.Tenant, - Category: cdr.Category, - Subject: cdr.Subject, - Account: cdr.Account, - Destination: cdr.Destination, - TimeStart: timeStart, - TimeEnd: timeStart.Add(cdr.Usage), - DurationIndex: cdr.Usage, + TOR: cdr.ToR, + Direction: cdr.Direction, + Tenant: cdr.Tenant, + Category: cdr.Category, + Subject: cdr.Subject, + Account: cdr.Account, + Destination: cdr.Destination, + TimeStart: timeStart, + TimeEnd: timeStart.Add(cdr.Usage), + DurationIndex: cdr.Usage, + PerformRounding: true, } if utils.IsSliceMember([]string{utils.META_PSEUDOPREPAID, utils.META_POSTPAID, utils.META_PREPAID, utils.PSEUDOPREPAID, utils.POSTPAID, utils.PREPAID}, cdr.RequestType) { // Prepaid - Cost can be recalculated in case of missing records from SM err = self.rater.Debit(cd, cc) From a3a01f93ce6021f29008a90f3882b70d140c6141 Mon Sep 17 00:00:00 2001 From: DanB Date: Sun, 6 Mar 2016 19:39:48 +0100 Subject: [PATCH 140/199] Adding LastUsed in SMGEvent, SMG logic to follow --- sessionmanager/smg_event.go | 10 +++++++++- sessionmanager/smg_event_test.go | 6 ++++++ utils/consts.go | 1 + 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/sessionmanager/smg_event.go b/sessionmanager/smg_event.go index aefe28dfb..be0d319a7 100644 --- a/sessionmanager/smg_event.go +++ b/sessionmanager/smg_event.go @@ -161,6 +161,14 @@ func (self SMGenericEvent) GetUsage(fieldName string) (time.Duration, error) { return utils.ParseDurationWithSecs(result) } +func (self SMGenericEvent) GetLastUsed(fieldName string) (time.Duration, error) { + if fieldName == utils.META_DEFAULT { + fieldName = utils.LastUsed + } + result, _ := utils.ConvertIfaceToString(self[fieldName]) + return utils.ParseDurationWithSecs(result) +} + func (self SMGenericEvent) GetMaxUsage(fieldName string, cfgMaxUsage time.Duration) (time.Duration, error) { if fieldName == utils.META_DEFAULT { fieldName = utils.USAGE @@ -212,7 +220,7 @@ func (self SMGenericEvent) GetCdrSource() string { func (self SMGenericEvent) GetExtraFields() map[string]string { extraFields := make(map[string]string) for key, val := range self { - primaryFields := append(utils.PrimaryCdrFields, utils.EVENT_NAME) + primaryFields := append(utils.PrimaryCdrFields, utils.EVENT_NAME, utils.LastUsed) if utils.IsSliceMember(primaryFields, key) { continue } diff --git a/sessionmanager/smg_event_test.go b/sessionmanager/smg_event_test.go index 091453f65..4cc80781c 100644 --- a/sessionmanager/smg_event_test.go +++ b/sessionmanager/smg_event_test.go @@ -45,6 +45,7 @@ func TestSMGenericEventParseFields(t *testing.T) { smGev[utils.SETUP_TIME] = "2015-11-09 14:21:24" smGev[utils.ANSWER_TIME] = "2015-11-09 14:22:02" smGev[utils.USAGE] = "1m23s" + smGev[utils.LastUsed] = "21s" smGev[utils.PDD] = "300ms" smGev[utils.SUPPLIER] = "supplier1" smGev[utils.DISCONNECT_CAUSE] = "NORMAL_DISCONNECT" @@ -107,6 +108,11 @@ func TestSMGenericEventParseFields(t *testing.T) { } else if dur != time.Duration(83)*time.Second { t.Error("Unexpected: ", dur) } + if lastUsed, err := smGev.GetLastUsed(utils.META_DEFAULT); err != nil { + t.Error(err) + } else if lastUsed != time.Duration(21)*time.Second { + t.Error("Unexpected: ", lastUsed) + } if pdd, err := smGev.GetPdd(utils.META_DEFAULT); err != nil { t.Error(err) } else if pdd != time.Duration(300)*time.Millisecond { diff --git a/utils/consts.go b/utils/consts.go index a2324b8e0..e197a5083 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -124,6 +124,7 @@ const ( SETUP_TIME = "SetupTime" ANSWER_TIME = "AnswerTime" USAGE = "Usage" + LastUsed = "LastUsed" PDD = "PDD" SUPPLIER = "Supplier" MEDI_RUNID = "RunID" From a1f9bda028a8a832ef443293908180b386c90f9d Mon Sep 17 00:00:00 2001 From: DanB Date: Sun, 6 Mar 2016 20:49:13 +0100 Subject: [PATCH 141/199] SMG - Adding LastUsed variable processing, fixes #391 --- sessionmanager/smg_event.go | 6 +++++- sessionmanager/smg_session.go | 15 +++++++++++++-- sessionmanager/smgeneric.go | 12 +++++++----- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/sessionmanager/smg_event.go b/sessionmanager/smg_event.go index be0d319a7..0e710418c 100644 --- a/sessionmanager/smg_event.go +++ b/sessionmanager/smg_event.go @@ -165,7 +165,11 @@ func (self SMGenericEvent) GetLastUsed(fieldName string) (time.Duration, error) if fieldName == utils.META_DEFAULT { fieldName = utils.LastUsed } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + valStr, hasVal := self[fieldName] + if !hasVal { + return nilDuration, nil + } + result, _ := utils.ConvertIfaceToString(valStr) return utils.ParseDurationWithSecs(result) } diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 7d6238ec5..fc0032821 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -41,6 +41,7 @@ type SMGSession struct { sessionCds []*engine.CallDescriptor callCosts []*engine.CallCost extraDuration time.Duration // keeps the current duration debited on top of what heas been asked + lastUsage time.Duration // Keep record of the last debit for LastUsed functionality } // Called in case of automatic debits @@ -52,7 +53,7 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) { return default: } - if maxDebit, err := self.debit(debitInterval); err != nil { + if maxDebit, err := self.debit(debitInterval, nilDuration); err != nil { utils.Logger.Err(fmt.Sprintf(" Could not complete debit opperation on session: %s, error: %s", self.eventStart.GetUUID(), err.Error())) disconnectReason := SYSTEM_ERROR if err.Error() == utils.ErrUnauthorizedDestination.Error() { @@ -75,7 +76,17 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) { } // Attempts to debit a duration, returns maximum duration which can be debitted or error -func (self *SMGSession) debit(dur time.Duration) (time.Duration, error) { +func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.Duration, error) { + if lastUsed != nilDuration && + self.cd.DurationIndex != 0 && + self.lastUsage != lastUsed { + if self.lastUsage > lastUsed { // We have debitted more than we have used, refund in the duration debitted + dur -= self.lastUsage - lastUsed + } else { // We have debitted less than we have consumed, add the difference to duration debitted + dur += lastUsed - self.lastUsage + } + } + self.lastUsage = dur // Reset the lastUsage for later reference // apply correction from previous run dur -= self.extraDuration self.extraDuration = 0 diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index f9e4e27a7..8da95aa45 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -167,18 +167,20 @@ func (self *SMGeneric) GetLcrSuppliers(gev SMGenericEvent, clnt *rpc2.Client) ([ // Execute debits for usage/maxUsage func (self *SMGeneric) SessionUpdate(gev SMGenericEvent, clnt *rpc2.Client) (time.Duration, error) { + evLastUsed, err := gev.GetLastUsed(utils.META_DEFAULT) + if err != nil { + return nilDuration, err + } evMaxUsage, err := gev.GetMaxUsage(utils.META_DEFAULT, self.cgrCfg.MaxCallDuration) if err != nil { return nilDuration, err } evUuid := gev.GetUUID() for _, s := range self.getSession(evUuid) { - if maxDur, err := s.debit(evMaxUsage); err != nil { + if maxDur, err := s.debit(evMaxUsage, evLastUsed); err != nil { return nilDuration, err - } else { - if maxDur < evMaxUsage { - evMaxUsage = maxDur - } + } else if maxDur < evMaxUsage { + evMaxUsage = maxDur } } return evMaxUsage, nil From 68392af30a3fa9373855e3a5658a6b32dbd87b85 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 7 Mar 2016 17:51:25 +0200 Subject: [PATCH 142/199] updated dependencies --- data/conf/samples/cgradmin/cgradmin.json | 16 +++++++++++++++- glide.lock | 3 ++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/data/conf/samples/cgradmin/cgradmin.json b/data/conf/samples/cgradmin/cgradmin.json index e09f94c20..2f6f9e4a1 100644 --- a/data/conf/samples/cgradmin/cgradmin.json +++ b/data/conf/samples/cgradmin/cgradmin.json @@ -10,11 +10,25 @@ "http": ":2080", // HTTP listening address }, +//"tariffplan_db": { // database used to store offline tariff plans and CDRs +// "db_type": "mongo", // stor database type to use: +// "db_host": "127.0.0.1", // the host to connect to +// "db_port": 27017, // the port to reach the stordb +// "db_name": "tpdb", +//}, +// +//"data_db": { // database used to store offline tariff plans and CDRs +// "db_type": "mongo", // stor database type to use: +// "db_host": "127.0.0.1", // the host to connect to +// "db_port": 27017, // the port to reach the stordb +// "db_name": "datadb", +//}, + "stor_db": { // database used to store offline tariff plans and CDRs "db_type": "mongo", // stor database type to use: "db_host": "127.0.0.1", // the host to connect to "db_port": 27017, // the port to reach the stordb - "db_name": "cgrates", + "db_name": "stordb", }, "rater": { diff --git a/glide.lock b/glide.lock index e35ffe2f0..14633d978 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: fde526f5fcd62d6b306dd6c4e30c6aa29ee3bfd625ffef3906c4d032eda7e9ec -updated: 2016-02-19T13:46:35.353761268+02:00 +updated: 2016-03-07T10:29:17.780762339+02:00 imports: - name: github.com/cenkalti/hub version: 57d753b5f4856e77b3cf8ecce78c97215a7d324d @@ -64,5 +64,6 @@ imports: version: e30de8ac9ae3b30df7065f766c71f88bba7d4e49 subpackages: - bson + - internal/sasl - internal/scram devImports: [] From 48e7faf35daf7d3007912243b2fd93c75ac266e0 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 7 Mar 2016 22:34:46 +0200 Subject: [PATCH 143/199] Prefix matching for rating subject implements #388, config option pending --- engine/calldesc.go | 29 +++++++++++++++++------------ engine/ratingprofile.go | 19 +++++++++++++++++++ engine/ratingprofile_test.go | 14 ++++++++++++++ 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/engine/calldesc.go b/engine/calldesc.go index ce2bd49e0..a7a033393 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -70,16 +70,17 @@ func init() { } var ( - ratingStorage RatingStorage - accountingStorage AccountingStorage - storageLogger LogStorage - cdrStorage CdrStorage - debitPeriod = 10 * time.Second - globalRoundingDecimals = 6 - historyScribe history.Scribe - pubSubServer rpcclient.RpcClientConnection - userService UserService - aliasService AliasService + ratingStorage RatingStorage + accountingStorage AccountingStorage + storageLogger LogStorage + cdrStorage CdrStorage + debitPeriod = 10 * time.Second + globalRoundingDecimals = 6 + historyScribe history.Scribe + pubSubServer rpcclient.RpcClientConnection + userService UserService + aliasService AliasService + prefixMatchingRatingProfile bool ) // Exported method to set the storage getter. @@ -96,6 +97,10 @@ func SetRoundingDecimals(rd int) { globalRoundingDecimals = rd } +func SetPrefixmatchingRatingProfile(flag bool) { + prefixMatchingRatingProfile = flag +} + /* Sets the database for logging (can be de same as storage getter or different db) */ @@ -219,7 +224,7 @@ func (cd *CallDescriptor) getRatingPlansForPrefix(key string, recursionDepth int if recursionDepth > RECURSION_MAX_DEPTH { return utils.ErrMaxRecursionDepth, recursionDepth } - rpf, err := ratingStorage.GetRatingProfile(key, false) + rpf, err := PrefixMatchRatingProfileSubject(key) if err != nil || rpf == nil { return utils.ErrNotFound, recursionDepth } @@ -1034,7 +1039,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface, p *utils.Paginator) (*LCR continue } rpfKey := utils.ConcatenatedKey(ratingProfileSearchKey, supplier) - if rpf, err := ratingStorage.GetRatingProfile(rpfKey, false); err != nil { + if rpf, err := PrefixMatchRatingProfileSubject(rpfKey); err != nil { lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{ Supplier: fullSupplier, Error: fmt.Sprintf("Rating plan error: %s", err.Error()), diff --git a/engine/ratingprofile.go b/engine/ratingprofile.go index f802252aa..059ccf6f1 100644 --- a/engine/ratingprofile.go +++ b/engine/ratingprofile.go @@ -22,6 +22,7 @@ import ( "encoding/json" "fmt" "sort" + "strings" "time" "github.com/cgrates/cgrates/cache2go" @@ -244,3 +245,21 @@ func (rpf *RatingProfile) GetHistoryRecord(deleted bool) history.Record { type TenantRatingSubject struct { Tenant, Subject string } + +func PrefixMatchRatingProfileSubject(key string) (rp *RatingProfile, err error) { + if !prefixMatchingRatingProfile { + return ratingStorage.GetRatingProfile(key, false) + } + if rp, err = ratingStorage.GetRatingProfile(key, false); err == nil { + return rp, err + } + lastIndex := strings.LastIndex(key, utils.CONCATENATED_KEY_SEP) + baseKey := key[:lastIndex] + subject := key[lastIndex:] + for i := 1; i < len(subject)-1; i++ { + if rp, err = ratingStorage.GetRatingProfile(baseKey+subject[:len(subject)-i], false); err == nil { + return rp, err + } + } + return +} diff --git a/engine/ratingprofile_test.go b/engine/ratingprofile_test.go index 95ef342c0..a94598ad0 100644 --- a/engine/ratingprofile_test.go +++ b/engine/ratingprofile_test.go @@ -249,3 +249,17 @@ func TestRatingProfileRIforTSMidnight(t *testing.T) { t.Error("Wrong interval list: ", utils.ToIJSON(rIntervals)) } } + +func TestPrefixMatchRatingProfileSubject(t *testing.T) { + prefixMatchingRatingProfile = true + rp, err := PrefixMatchRatingProfileSubject("*out:cgrates.org:data:rif") + if rp == nil || err != nil { + t.Errorf("Error getting rating profile by prefix: %+v (%v)", rp, err) + } + + rp, err = PrefixMatchRatingProfileSubject("*out:cgrates.org:data:rifescu") + if rp == nil || err != nil { + t.Errorf("Error getting rating profile by prefix: %+v (%v)", rp, err) + } + prefixMatchingRatingProfile = false +} From 9fbd39ae6d65cec429ce7e3bf5e6812b35881f6b Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Mar 2016 11:49:08 +0200 Subject: [PATCH 144/199] Config for prefix matching for rating subject config for #388 --- config/config.go | 168 +++++++++++++++++---------------- config/config_defaults.go | 1 + config/config_json_test.go | 4 +- config/libconfig_json.go | 15 +-- data/conf/cgrates/cgrates.json | 3 +- engine/calldesc.go | 30 +++--- engine/ratingprofile.go | 4 +- engine/ratingprofile_test.go | 10 +- 8 files changed, 121 insertions(+), 114 deletions(-) diff --git a/config/config.go b/config/config.go index e8dcfc582..72a1bf98c 100644 --- a/config/config.go +++ b/config/config.go @@ -167,88 +167,89 @@ func NewCGRConfigFromFolder(cfgDir string) (*CGRConfig, error) { // Holds system configuration, defaults are overwritten with values from config file if found type CGRConfig struct { - TpDbType string - TpDbHost string // The host to connect to. Values that start with / are for UNIX domain sockets. - TpDbPort string // The port to bind to. - TpDbName string // The name of the database to connect to. - TpDbUser string // The user to sign in as. - TpDbPass string // The user's password. - DataDbType string - DataDbHost string // The host to connect to. Values that start with / are for UNIX domain sockets. - DataDbPort string // The port to bind to. - DataDbName string // The name of the database to connect to. - DataDbUser string // The user to sign in as. - DataDbPass string // The user's password. - LoadHistorySize int // Maximum number of records to archive in load history - StorDBType string // Should reflect the database type used to store logs - StorDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets. - StorDBPort string // Th e port to bind to. - StorDBName string // The name of the database to connect to. - StorDBUser string // The user to sign in as. - StorDBPass string // The user's password. - StorDBMaxOpenConns int // Maximum database connections opened - StorDBMaxIdleConns int // Maximum idle connections to keep opened - StorDBCDRSIndexes []string - DBDataEncoding string // The encoding used to store object data in strings: - RPCJSONListen string // RPC JSON listening address - RPCGOBListen string // RPC GOB listening address - HTTPListen string // HTTP listening address - DefaultReqType string // Use this request type if not defined on top - DefaultCategory string // set default type of record - DefaultTenant string // set default tenant - DefaultSubject string // set default rating subject, useful in case of fallback - DefaultTimezone string // default timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB> - Reconnects int // number of recconect attempts in case of connection lost <-1 for infinite | nb> - ConnectAttempts int // number of initial connection attempts before giving up - ResponseCacheTTL time.Duration // the life span of a cached response - InternalTtl time.Duration // maximum duration to wait for internal connections before giving up - RoundingDecimals int // Number of decimals to round end prices at - HttpSkipTlsVerify bool // If enabled Http Client will accept any TLS certificate - TpExportPath string // Path towards export folder for offline Tariff Plans - HttpFailedDir string // Directory path where we store failed http requests - MaxCallDuration time.Duration // The maximum call duration (used by responder when querying DerivedCharging) // ToDo: export it in configuration file - RaterEnabled bool // start standalone server (no balancer) - RaterBalancer string // balancer address host:port - RaterCdrStats string // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234> - RaterHistoryServer string - RaterPubSubServer string - RaterUserServer string - RaterAliasesServer string - BalancerEnabled bool - SchedulerEnabled bool - CDRSEnabled bool // Enable CDR Server service - CDRSExtraFields []*utils.RSRField // Extra fields to store in CDRs - CDRSStoreCdrs bool // store cdrs in storDb - CDRSRater string // address where to reach the Rater for cost calculation: <""|internal|x.y.z.y:1234> - CDRSPubSub string // address where to reach the pubsub service: <""|internal|x.y.z.y:1234> - CDRSUsers string // address where to reach the users service: <""|internal|x.y.z.y:1234> - CDRSAliases string // address where to reach the aliases service: <""|internal|x.y.z.y:1234> - CDRSStats string // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234> - CDRSCdrReplication []*CdrReplicationCfg // Replicate raw CDRs to a number of servers - CDRStatsEnabled bool // Enable CDR Stats service - CDRStatsSaveInterval time.Duration // Save interval duration - CdreProfiles map[string]*CdreConfig - CdrcProfiles map[string]map[string]*CdrcConfig // Number of CDRC instances running imports, format map[dirPath]map[instanceName]{Configs} - SmGenericConfig *SmGenericConfig - SmFsConfig *SmFsConfig // SM-FreeSWITCH configuration - SmKamConfig *SmKamConfig // SM-Kamailio Configuration - SmOsipsConfig *SmOsipsConfig // SM-OpenSIPS Configuration - diameterAgentCfg *DiameterAgentCfg // DiameterAgent configuration - HistoryServer string // Address where to reach the master history server: - HistoryServerEnabled bool // Starts History as server: . - HistoryDir string // Location on disk where to store history files. - HistorySaveInterval time.Duration // The timout duration between pubsub writes - PubSubServerEnabled bool // Starts PubSub as server: . - AliasesServerEnabled bool // Starts PubSub as server: . - UserServerEnabled bool // Starts User as server: - UserServerIndexes []string // List of user profile field indexes - MailerServer string // The server to use when sending emails out - MailerAuthUser string // Authenticate to email server using this user - MailerAuthPass string // Authenticate to email server with this password - MailerFromAddr string // From address used when sending emails out - DataFolderPath string // Path towards data folder, for tests internal usage, not loading out of .json options - sureTaxCfg *SureTaxCfg // Load here SureTax configuration, as pointer so we can have runtime reloads in the future - ConfigReloads map[string]chan struct{} // Signals to specific entities that a config reload should occur + TpDbType string + TpDbHost string // The host to connect to. Values that start with / are for UNIX domain sockets. + TpDbPort string // The port to bind to. + TpDbName string // The name of the database to connect to. + TpDbUser string // The user to sign in as. + TpDbPass string // The user's password. + DataDbType string + DataDbHost string // The host to connect to. Values that start with / are for UNIX domain sockets. + DataDbPort string // The port to bind to. + DataDbName string // The name of the database to connect to. + DataDbUser string // The user to sign in as. + DataDbPass string // The user's password. + LoadHistorySize int // Maximum number of records to archive in load history + StorDBType string // Should reflect the database type used to store logs + StorDBHost string // The host to connect to. Values that start with / are for UNIX domain sockets. + StorDBPort string // Th e port to bind to. + StorDBName string // The name of the database to connect to. + StorDBUser string // The user to sign in as. + StorDBPass string // The user's password. + StorDBMaxOpenConns int // Maximum database connections opened + StorDBMaxIdleConns int // Maximum idle connections to keep opened + StorDBCDRSIndexes []string + DBDataEncoding string // The encoding used to store object data in strings: + RPCJSONListen string // RPC JSON listening address + RPCGOBListen string // RPC GOB listening address + HTTPListen string // HTTP listening address + DefaultReqType string // Use this request type if not defined on top + DefaultCategory string // set default type of record + DefaultTenant string // set default tenant + DefaultSubject string // set default rating subject, useful in case of fallback + DefaultTimezone string // default timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB> + Reconnects int // number of recconect attempts in case of connection lost <-1 for infinite | nb> + ConnectAttempts int // number of initial connection attempts before giving up + ResponseCacheTTL time.Duration // the life span of a cached response + InternalTtl time.Duration // maximum duration to wait for internal connections before giving up + RoundingDecimals int // Number of decimals to round end prices at + HttpSkipTlsVerify bool // If enabled Http Client will accept any TLS certificate + TpExportPath string // Path towards export folder for offline Tariff Plans + HttpFailedDir string // Directory path where we store failed http requests + MaxCallDuration time.Duration // The maximum call duration (used by responder when querying DerivedCharging) // ToDo: export it in configuration file + RaterEnabled bool // start standalone server (no balancer) + RaterBalancer string // balancer address host:port + RaterCdrStats string // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234> + RaterHistoryServer string + RaterPubSubServer string + RaterUserServer string + RaterAliasesServer string + RpSubjectPrefixMatching bool // enables prefix matching for the rating profile subject + BalancerEnabled bool + SchedulerEnabled bool + CDRSEnabled bool // Enable CDR Server service + CDRSExtraFields []*utils.RSRField // Extra fields to store in CDRs + CDRSStoreCdrs bool // store cdrs in storDb + CDRSRater string // address where to reach the Rater for cost calculation: <""|internal|x.y.z.y:1234> + CDRSPubSub string // address where to reach the pubsub service: <""|internal|x.y.z.y:1234> + CDRSUsers string // address where to reach the users service: <""|internal|x.y.z.y:1234> + CDRSAliases string // address where to reach the aliases service: <""|internal|x.y.z.y:1234> + CDRSStats string // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234> + CDRSCdrReplication []*CdrReplicationCfg // Replicate raw CDRs to a number of servers + CDRStatsEnabled bool // Enable CDR Stats service + CDRStatsSaveInterval time.Duration // Save interval duration + CdreProfiles map[string]*CdreConfig + CdrcProfiles map[string]map[string]*CdrcConfig // Number of CDRC instances running imports, format map[dirPath]map[instanceName]{Configs} + SmGenericConfig *SmGenericConfig + SmFsConfig *SmFsConfig // SM-FreeSWITCH configuration + SmKamConfig *SmKamConfig // SM-Kamailio Configuration + SmOsipsConfig *SmOsipsConfig // SM-OpenSIPS Configuration + diameterAgentCfg *DiameterAgentCfg // DiameterAgent configuration + HistoryServer string // Address where to reach the master history server: + HistoryServerEnabled bool // Starts History as server: . + HistoryDir string // Location on disk where to store history files. + HistorySaveInterval time.Duration // The timout duration between pubsub writes + PubSubServerEnabled bool // Starts PubSub as server: . + AliasesServerEnabled bool // Starts PubSub as server: . + UserServerEnabled bool // Starts User as server: + UserServerIndexes []string // List of user profile field indexes + MailerServer string // The server to use when sending emails out + MailerAuthUser string // Authenticate to email server using this user + MailerAuthPass string // Authenticate to email server with this password + MailerFromAddr string // From address used when sending emails out + DataFolderPath string // Path towards data folder, for tests internal usage, not loading out of .json options + sureTaxCfg *SureTaxCfg // Load here SureTax configuration, as pointer so we can have runtime reloads in the future + ConfigReloads map[string]chan struct{} // Signals to specific entities that a config reload should occur // Cache defaults loaded from json and needing clones dfltCdreProfile *CdreConfig // Default cdreConfig profile dfltCdrcProfile *CdrcConfig // Default cdrcConfig profile @@ -666,6 +667,9 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error { if jsnRaterCfg.Users != nil { self.RaterUserServer = *jsnRaterCfg.Users } + if jsnRaterCfg.Rp_subject_prefix_matching != nil { + self.RpSubjectPrefixMatching = *jsnRaterCfg.Rp_subject_prefix_matching + } } if jsnBalancerCfg != nil && jsnBalancerCfg.Enabled != nil { diff --git a/config/config_defaults.go b/config/config_defaults.go index 0ef467e70..af1fdd041 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -99,6 +99,7 @@ const CGRATES_CFG_JSON = ` "pubsubs": "", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234> "users": "", // address where to reach the user service, empty to disable user profile functionality: <""|internal|x.y.z.y:1234> "aliases": "", // address where to reach the aliases service, empty to disable aliases functionality: <""|internal|x.y.z.y:1234> + "rp_subject_prefix_matching": false // enables prefix matching for the rating profile subject }, diff --git a/config/config_json_test.go b/config/config_json_test.go index e7ac7e927..462597e1f 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -56,7 +56,7 @@ func TestDfGeneralJsonCfg(t *testing.T) { if gCfg, err := dfCgrJsonCfg.GeneralJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, gCfg) { - t.Error("Received: ", gCfg) + t.Error("Received: ", utils.ToIJSON(gCfg)) } } @@ -129,7 +129,7 @@ func TestDfBalancerJsonCfg(t *testing.T) { func TestDfRaterJsonCfg(t *testing.T) { eCfg := &RaterJsonCfg{Enabled: utils.BoolPointer(false), Balancer: utils.StringPointer(""), Cdrstats: utils.StringPointer(""), - Historys: utils.StringPointer(""), Pubsubs: utils.StringPointer(""), Users: utils.StringPointer(""), Aliases: utils.StringPointer("")} + Historys: utils.StringPointer(""), Pubsubs: utils.StringPointer(""), Users: utils.StringPointer(""), Aliases: utils.StringPointer(""), Rp_subject_prefix_matching: utils.BoolPointer(false)} if cfg, err := dfCgrJsonCfg.RaterJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 7ab6b093a..58f25c529 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -64,13 +64,14 @@ type BalancerJsonCfg struct { // Rater config section type RaterJsonCfg struct { - Enabled *bool - Balancer *string - Cdrstats *string - Historys *string - Pubsubs *string - Aliases *string - Users *string + Enabled *bool + Balancer *string + Cdrstats *string + Historys *string + Pubsubs *string + Aliases *string + Users *string + Rp_subject_prefix_matching *bool } // Scheduler config section diff --git a/data/conf/cgrates/cgrates.json b/data/conf/cgrates/cgrates.json index e35dd57c7..8eec665c9 100644 --- a/data/conf/cgrates/cgrates.json +++ b/data/conf/cgrates/cgrates.json @@ -77,6 +77,7 @@ // "pubsubs": "", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234> // "users": "", // address where to reach the user service, empty to disable user profile functionality: <""|internal|x.y.z.y:1234> // "aliases": "", // address where to reach the aliases service, empty to disable aliases functionality: <""|internal|x.y.z.y:1234> +// "rp_subject_prefix_matching": false // enables prefix matching for the rating profile subject //}, @@ -334,4 +335,4 @@ // "tax_exemption_code_list": "", // template extracting tax exemption code list out of StoredCdr; <$RSRFields> //}, -} \ No newline at end of file +} diff --git a/engine/calldesc.go b/engine/calldesc.go index a7a033393..b5d2caffe 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -70,17 +70,17 @@ func init() { } var ( - ratingStorage RatingStorage - accountingStorage AccountingStorage - storageLogger LogStorage - cdrStorage CdrStorage - debitPeriod = 10 * time.Second - globalRoundingDecimals = 6 - historyScribe history.Scribe - pubSubServer rpcclient.RpcClientConnection - userService UserService - aliasService AliasService - prefixMatchingRatingProfile bool + ratingStorage RatingStorage + accountingStorage AccountingStorage + storageLogger LogStorage + cdrStorage CdrStorage + debitPeriod = 10 * time.Second + globalRoundingDecimals = 6 + historyScribe history.Scribe + pubSubServer rpcclient.RpcClientConnection + userService UserService + aliasService AliasService + rpSubjectPrefixMatching bool ) // Exported method to set the storage getter. @@ -97,8 +97,8 @@ func SetRoundingDecimals(rd int) { globalRoundingDecimals = rd } -func SetPrefixmatchingRatingProfile(flag bool) { - prefixMatchingRatingProfile = flag +func SetRpSubjectPrefixMatching(flag bool) { + rpSubjectPrefixMatching = flag } /* @@ -224,7 +224,7 @@ func (cd *CallDescriptor) getRatingPlansForPrefix(key string, recursionDepth int if recursionDepth > RECURSION_MAX_DEPTH { return utils.ErrMaxRecursionDepth, recursionDepth } - rpf, err := PrefixMatchRatingProfileSubject(key) + rpf, err := RatingProfileSubjectPrefixMatching(key) if err != nil || rpf == nil { return utils.ErrNotFound, recursionDepth } @@ -1039,7 +1039,7 @@ func (cd *CallDescriptor) GetLCR(stats StatsInterface, p *utils.Paginator) (*LCR continue } rpfKey := utils.ConcatenatedKey(ratingProfileSearchKey, supplier) - if rpf, err := PrefixMatchRatingProfileSubject(rpfKey); err != nil { + if rpf, err := RatingProfileSubjectPrefixMatching(rpfKey); err != nil { lcrCost.SupplierCosts = append(lcrCost.SupplierCosts, &LCRSupplierCost{ Supplier: fullSupplier, Error: fmt.Sprintf("Rating plan error: %s", err.Error()), diff --git a/engine/ratingprofile.go b/engine/ratingprofile.go index 059ccf6f1..9ef1cfd27 100644 --- a/engine/ratingprofile.go +++ b/engine/ratingprofile.go @@ -246,8 +246,8 @@ type TenantRatingSubject struct { Tenant, Subject string } -func PrefixMatchRatingProfileSubject(key string) (rp *RatingProfile, err error) { - if !prefixMatchingRatingProfile { +func RatingProfileSubjectPrefixMatching(key string) (rp *RatingProfile, err error) { + if !rpSubjectPrefixMatching || strings.HasSuffix(key, utils.ANY) { return ratingStorage.GetRatingProfile(key, false) } if rp, err = ratingStorage.GetRatingProfile(key, false); err == nil { diff --git a/engine/ratingprofile_test.go b/engine/ratingprofile_test.go index a94598ad0..4f929029a 100644 --- a/engine/ratingprofile_test.go +++ b/engine/ratingprofile_test.go @@ -250,16 +250,16 @@ func TestRatingProfileRIforTSMidnight(t *testing.T) { } } -func TestPrefixMatchRatingProfileSubject(t *testing.T) { - prefixMatchingRatingProfile = true - rp, err := PrefixMatchRatingProfileSubject("*out:cgrates.org:data:rif") +func TestRatingProfileSubjectPrefixMatching(t *testing.T) { + rpSubjectPrefixMatching = true + rp, err := RatingProfileSubjectPrefixMatching("*out:cgrates.org:data:rif") if rp == nil || err != nil { t.Errorf("Error getting rating profile by prefix: %+v (%v)", rp, err) } - rp, err = PrefixMatchRatingProfileSubject("*out:cgrates.org:data:rifescu") + rp, err = RatingProfileSubjectPrefixMatching("*out:cgrates.org:data:rifescu") if rp == nil || err != nil { t.Errorf("Error getting rating profile by prefix: %+v (%v)", rp, err) } - prefixMatchingRatingProfile = false + rpSubjectPrefixMatching = false } From 39bea78d599295209c058e4f0cbced77cdba0ab9 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Mar 2016 11:55:26 +0200 Subject: [PATCH 145/199] formatting --- data/conf/cgrates/cgrates.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/conf/cgrates/cgrates.json b/data/conf/cgrates/cgrates.json index 8eec665c9..bc740467c 100644 --- a/data/conf/cgrates/cgrates.json +++ b/data/conf/cgrates/cgrates.json @@ -77,7 +77,7 @@ // "pubsubs": "", // address where to reach the pubusb service, empty to disable pubsub functionality: <""|internal|x.y.z.y:1234> // "users": "", // address where to reach the user service, empty to disable user profile functionality: <""|internal|x.y.z.y:1234> // "aliases": "", // address where to reach the aliases service, empty to disable aliases functionality: <""|internal|x.y.z.y:1234> -// "rp_subject_prefix_matching": false // enables prefix matching for the rating profile subject +// "rp_subject_prefix_matching": false, // enables prefix matching for the rating profile subject //}, From 9dd6c6dc16e778dcb76f0f8912651e0f72cfff0e Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Mar 2016 16:37:22 +0200 Subject: [PATCH 146/199] generate new expdate on action timing execution fixes #363 --- data/tariffplans/testtp/Actions.csv | 3 ++- engine/action_plan.go | 7 +++++-- engine/action_trigger.go | 5 +++-- engine/actions_test.go | 23 +++++++++++++++++++++++ engine/balance_filter.go | 2 +- engine/loader_csv_test.go | 9 ++++++--- engine/tp_reader.go | 18 ++++++++++-------- 7 files changed, 50 insertions(+), 17 deletions(-) diff --git a/data/tariffplans/testtp/Actions.csv b/data/tariffplans/testtp/Actions.csv index 045fc0666..49f7f0e15 100644 --- a/data/tariffplans/testtp/Actions.csv +++ b/data/tariffplans/testtp/Actions.csv @@ -4,4 +4,5 @@ BONUS_1,*topup,,,,*monetary,*out,,*any,,,*unlimited,,1,10,false,false,10 LOG_BALANCE,*log,,,,,,,,,,,,,,false,false,10 CDRST_WARN_HTTP,*call_url,http://localhost:8080,,,,,,,,,,,,,false,false,10 CDRST_LOG,*log,,,,,,,,,,,,,,false,false,10 -TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 \ No newline at end of file +TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 +EXP,*topup,,,,*voice,*out,,,,,*monthly,*any,300,10,false,false,10 diff --git a/engine/action_plan.go b/engine/action_plan.go index b9c40d519..4c3b2078e 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -303,7 +303,10 @@ func (at *ActionTiming) Execute() (err error) { continue } } - if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.HasExpirationDate()) && parseErr == nil && !expDate.IsZero() { + if a.Balance == nil { + a.Balance = &BalanceFilter{} + } + if expDate, parseErr := utils.ParseDate(a.ExpirationString); parseErr == nil && !expDate.IsZero() { a.Balance.ExpirationDate = &time.Time{} *a.Balance.ExpirationDate = expDate } @@ -333,7 +336,7 @@ func (at *ActionTiming) Execute() (err error) { } if len(at.accountIDs) == 0 { // action timing executing without accounts for _, a := range aac { - if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.HasExpirationDate()) && + if expDate, parseErr := utils.ParseDate(a.ExpirationString); (a.Balance == nil || a.Balance.EmptyExpirationDate()) && parseErr == nil && !expDate.IsZero() { a.Balance.ExpirationDate = &time.Time{} *a.Balance.ExpirationDate = expDate diff --git a/engine/action_trigger.go b/engine/action_trigger.go index dc3a5423c..8e1dac8fd 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -80,10 +80,11 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro if a.Balance == nil { a.Balance = &BalanceFilter{} } - if a.ExpirationString != "" { + if expDate, parseErr := utils.ParseDate(a.ExpirationString); parseErr == nil && !expDate.IsZero() { a.Balance.ExpirationDate = &time.Time{} - *a.Balance.ExpirationDate, _ = utils.ParseDate(a.ExpirationString) + *a.Balance.ExpirationDate = expDate } + actionFunction, exists := getActionFunc(a.ActionType) if !exists { utils.Logger.Err(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType)) diff --git a/engine/actions_test.go b/engine/actions_test.go index 416fbc9ac..4cbe95279 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -2055,6 +2055,29 @@ func TestActionCSVFilter(t *testing.T) { } } +func TestActionExpirationTime(t *testing.T) { + if _, err := accountingStorage.GetAccount("cgrates.org:expo"); err != nil { + t.Errorf("account to be removed not found: %v", err) + } + a, err := ratingStorage.GetActions("EXP", false) + if err != nil || a == nil { + t.Error("Error getting actions: ", err) + } + + at := &ActionTiming{ + accountIDs: utils.StringMap{"cgrates.org:expo": true}, + actions: a, + } + for rep := 0; rep < 5; rep++ { + at.Execute() + afterUb, err := accountingStorage.GetAccount("cgrates.org:expo") + if err != nil || + len(afterUb.BalanceMap[utils.VOICE]) != rep+1 { + t.Error("error topuping expiration balance: ", utils.ToIJSON(afterUb)) + } + } +} + /**************** Benchmarks ********************************/ func BenchmarkUUID(b *testing.B) { diff --git a/engine/balance_filter.go b/engine/balance_filter.go index dc832cf0b..f9e408d39 100644 --- a/engine/balance_filter.go +++ b/engine/balance_filter.go @@ -274,7 +274,7 @@ func (bp *BalanceFilter) GetFactor() ValueFactor { return *bp.Factor } -func (bp *BalanceFilter) HasExpirationDate() bool { +func (bp *BalanceFilter) EmptyExpirationDate() bool { if bp.ExpirationDate == nil { return true } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 0e0fdc49e..e2bdd5be5 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -175,6 +175,7 @@ NEG,*allow_negative,,,,*monetary,*out,,,,,*unlimited,,0,10,false,false,10 BLOCK,*topup,,,bblocker,*monetary,*out,,NAT,,,*unlimited,,10,20,true,false,20 BLOCK,*topup,,,bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 FILTER,*topup,,"{""*and"":[{""Value"":{""*lt"":0}},{""Id"":{""*eq"":""*default""}}]}",bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 +EXP,*topup,,,,*voice,*out,,,,,*monthly,*any,300,10,false,false,10 ` actionPlans = ` MORE_MINUTES,MINI,ONE_TIME_RUN,10 @@ -186,6 +187,7 @@ TOPUP_SHARED10_AT,SE10,*asap,10 TOPUP_EMPTY_AT,EE0,*asap,10 POST_AT,NEG,*asap,10 BLOCK_AT,BLOCK,*asap,10 +EXP_AT,EXP,*asap,10 ` actionTriggers = ` @@ -213,6 +215,7 @@ vdf,emptyY,TOPUP_EMPTY_AT,,, vdf,post,POST_AT,,, cgrates.org,alodis,TOPUP_EMPTY_AT,,true,true cgrates.org,block,BLOCK_AT,,false,false +cgrates.org,expo,EXP_AT,,false,false ` derivedCharges = ` @@ -815,7 +818,7 @@ func TestLoadRatingProfiles(t *testing.T) { } func TestLoadActions(t *testing.T) { - if len(csvr.actions) != 11 { + if len(csvr.actions) != 12 { t.Error("Failed to load actions: ", len(csvr.actions)) } as1 := csvr.actions["MINI"] @@ -1001,7 +1004,7 @@ func TestLoadLCRs(t *testing.T) { } func TestLoadActionTimings(t *testing.T) { - if len(csvr.actionPlans) != 7 { + if len(csvr.actionPlans) != 8 { t.Error("Failed to load action timings: ", len(csvr.actionPlans)) } atm := csvr.actionPlans["MORE_MINUTES"] @@ -1096,7 +1099,7 @@ func TestLoadActionTriggers(t *testing.T) { } func TestLoadAccountActions(t *testing.T) { - if len(csvr.accountActions) != 12 { + if len(csvr.accountActions) != 13 { t.Error("Failed to load account actions: ", len(csvr.accountActions)) } aa := csvr.accountActions["vdf:minitsboy"] diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 4338a4521..82ac4d14f 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -1128,15 +1128,17 @@ func (tpr *TpReader) LoadAccountActions() (err error) { } ub.InitCounters() tpr.accountActions[aa.KeyId()] = ub - actionPlan, exists := tpr.actionPlans[aa.ActionPlanId] - if !exists { - log.Printf("could not get action plan for tag %v", aa.ActionPlanId) - // must not continue here + if aa.ActionPlanId != "" { + actionPlan, exists := tpr.actionPlans[aa.ActionPlanId] + if !exists { + log.Printf("could not get action plan for tag %v", aa.ActionPlanId) + // must not continue here + } + if actionPlan.AccountIDs == nil { + actionPlan.AccountIDs = make(utils.StringMap) + } + actionPlan.AccountIDs[aa.KeyId()] = true } - if actionPlan.AccountIDs == nil { - actionPlan.AccountIDs = make(utils.StringMap) - } - actionPlan.AccountIDs[aa.KeyId()] = true } return nil } From cc8a30b4cfd2b5521852889090de65a82765926f Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 8 Mar 2016 12:54:30 +0100 Subject: [PATCH 147/199] Further improvements in SMG for LastUsed functionality --- sessionmanager/smg_session.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index fc0032821..81c745a0a 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -77,20 +77,20 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) { // Attempts to debit a duration, returns maximum duration which can be debitted or error func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.Duration, error) { - if lastUsed != nilDuration && - self.cd.DurationIndex != 0 && - self.lastUsage != lastUsed { + self.lastUsage = dur // Reset the lastUsage for later reference + lastUsedCorrection := time.Duration(0) // Used if lastUsed influences the debit + if self.cd.DurationIndex != 0 { if self.lastUsage > lastUsed { // We have debitted more than we have used, refund in the duration debitted - dur -= self.lastUsage - lastUsed + lastUsedCorrection = -(self.lastUsage - lastUsed) } else { // We have debitted less than we have consumed, add the difference to duration debitted - dur += lastUsed - self.lastUsage + lastUsedCorrection = lastUsed - self.lastUsage } } - self.lastUsage = dur // Reset the lastUsage for later reference + // apply the lastUsed correction + dur += lastUsedCorrection // apply correction from previous run dur -= self.extraDuration self.extraDuration = 0 - if self.cd.LoopIndex > 0 { self.cd.TimeStart = self.cd.TimeEnd } @@ -107,6 +107,7 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D if ccDuration != dur { self.extraDuration = ccDuration - dur } + dur -= lastUsedCorrection // Revert the correction to return the real duration reserved self.cd.DurationIndex -= dur self.cd.DurationIndex += ccDuration self.cd.MaxCostSoFar += cc.Cost From ec95e86bc4f94c18611e53ae2819c47cf63da710 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Mar 2016 19:30:58 +0200 Subject: [PATCH 148/199] some integration tests fixes --- agents/dmtagent_it_test.go | 6 +++--- apier/v1/apier_local_test.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index d05035ce5..b558049aa 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -293,11 +293,11 @@ func TestDmtAgentSendCCRUpdate(t *testing.T) { } else if len(avps) == 0 { t.Error("Granted-Service-Unit not found") } else if strCCTime := avpValAsString(avps[0]); strCCTime != "300" { - t.Errorf("Expecting 300, received: %s", strCCTime) + t.Errorf("Expecting 300, received: %s, (%+v)", strCCTime, avps) } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.2518 + eAcntVal := 9.5008 if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { @@ -336,7 +336,7 @@ func TestDmtAgentSendCCRUpdate2(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.0028 + eAcntVal := 9.2016 if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if utils.Round(acnt.BalanceMap[utils.MONETARY].GetTotalValue(), 5, utils.ROUNDING_MIDDLE) != eAcntVal { diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 47da716b5..4e493d45c 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1297,7 +1297,7 @@ func TestApierResetDataAfterLoadFromFolder(t *testing.T) { if rcvStats.Destinations != 4 || rcvStats.RatingPlans != 3 || rcvStats.RatingProfiles != 3 || - rcvStats.Actions != 6 || + rcvStats.Actions != 7 || rcvStats.DerivedChargers != 2 { t.Errorf("Calling ApierV1.GetCacheStats received: %+v", rcvStats) } From efd159dd9a3a878747127a54cf7a378282c2d1bf Mon Sep 17 00:00:00 2001 From: cgrates Date: Tue, 8 Mar 2016 22:43:32 +0200 Subject: [PATCH 149/199] use go 1.6 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 404a07a24..936b99045 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: go go: - - 1.5 + - 1.6 script: $TRAVIS_BUILD_DIR/test.sh From db8cc4e651deb0c0a68c34ba26b636454a7d9f13 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Mar 2016 22:45:22 +0200 Subject: [PATCH 150/199] another integration test fix --- data/tariffplans/testtp/Actions.csv | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/tariffplans/testtp/Actions.csv b/data/tariffplans/testtp/Actions.csv index 49f7f0e15..045fc0666 100644 --- a/data/tariffplans/testtp/Actions.csv +++ b/data/tariffplans/testtp/Actions.csv @@ -4,5 +4,4 @@ BONUS_1,*topup,,,,*monetary,*out,,*any,,,*unlimited,,1,10,false,false,10 LOG_BALANCE,*log,,,,,,,,,,,,,,false,false,10 CDRST_WARN_HTTP,*call_url,http://localhost:8080,,,,,,,,,,,,,false,false,10 CDRST_LOG,*log,,,,,,,,,,,,,,false,false,10 -TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 -EXP,*topup,,,,*voice,*out,,,,,*monthly,*any,300,10,false,false,10 +TOPUP_EXE,*topup,,,,*monetary,*out,,*any,,,*unlimited,,5,10,false,false,10 \ No newline at end of file From 70cd4c6d9ad3fa222c656f94813b86dbeb5763cd Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Mar 2016 23:09:20 +0200 Subject: [PATCH 151/199] added sleep time for test scheduler execution --- general_tests/tutorial_local_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/general_tests/tutorial_local_test.go b/general_tests/tutorial_local_test.go index c48066596..5d4f08f58 100644 --- a/general_tests/tutorial_local_test.go +++ b/general_tests/tutorial_local_test.go @@ -1211,7 +1211,6 @@ func TestTutLocalSetAccount(t *testing.T) { t.Error("Disabled should be set") } } - attrs = &v2.AttrSetAccount{Tenant: "cgrates.org", Account: "tutacnt1", ActionPlanIDs: &[]string{"PACKAGE_1001"}, ActionTriggerIDs: &[]string{"CDRST1_WARN"}, AllowNegative: utils.BoolPointer(true), Disabled: utils.BoolPointer(true), ReloadScheduler: true} if err := tutLocalRpc.Call("ApierV2.SetAccount", attrs, &reply); err != nil { @@ -1219,6 +1218,7 @@ func TestTutLocalSetAccount(t *testing.T) { } else if reply != "OK" { t.Errorf("Calling ApierV2.SetAccount received: %s", reply) } + time.Sleep(100*time.Millisecond + time.Duration(*waitRater)*time.Millisecond) // Give time for scheduler to execute topups if err := tutLocalRpc.Call("ApierV2.GetAccounts", utils.AttrGetAccounts{Tenant: attrs.Tenant, AccountIds: []string{attrs.Account}}, &acnts); err != nil { t.Error(err) } else if len(acnts) != 1 { From a30a1ea11352cc6b67c65b06a99d276090c99245 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Mar 2016 23:17:58 +0200 Subject: [PATCH 152/199] updates --- apier/v1/apier_local_test.go | 2 +- glide.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index 4e493d45c..47da716b5 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -1297,7 +1297,7 @@ func TestApierResetDataAfterLoadFromFolder(t *testing.T) { if rcvStats.Destinations != 4 || rcvStats.RatingPlans != 3 || rcvStats.RatingProfiles != 3 || - rcvStats.Actions != 7 || + rcvStats.Actions != 6 || rcvStats.DerivedChargers != 2 { t.Errorf("Calling ApierV1.GetCacheStats received: %+v", rcvStats) } diff --git a/glide.lock b/glide.lock index 14633d978..06a89849e 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: fde526f5fcd62d6b306dd6c4e30c6aa29ee3bfd625ffef3906c4d032eda7e9ec -updated: 2016-03-07T10:29:17.780762339+02:00 +updated: 2016-03-08T23:15:54.553132116+02:00 imports: - name: github.com/cenkalti/hub version: 57d753b5f4856e77b3cf8ecce78c97215a7d324d From 143304d6ca2f68b7aa7c58cfc98478447ce31f8f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 8 Mar 2016 23:39:12 +0200 Subject: [PATCH 153/199] added fix and test for #261 --- engine/action_plan.go | 8 +++++--- engine/action_trigger.go | 8 +++++--- engine/actions_test.go | 25 ++++++++++++++++++++++--- engine/loader_csv_test.go | 6 ++++-- 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/engine/action_plan.go b/engine/action_plan.go index 4c3b2078e..79095227c 100644 --- a/engine/action_plan.go +++ b/engine/action_plan.go @@ -306,9 +306,11 @@ func (at *ActionTiming) Execute() (err error) { if a.Balance == nil { a.Balance = &BalanceFilter{} } - if expDate, parseErr := utils.ParseDate(a.ExpirationString); parseErr == nil && !expDate.IsZero() { - a.Balance.ExpirationDate = &time.Time{} - *a.Balance.ExpirationDate = expDate + if a.ExpirationString != "" { // if it's *unlimited then it has to be zero time + if expDate, parseErr := utils.ParseDate(a.ExpirationString); parseErr == nil { + a.Balance.ExpirationDate = &time.Time{} + *a.Balance.ExpirationDate = expDate + } } actionFunction, exists := getActionFunc(a.ActionType) diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 8e1dac8fd..0b4d4e03a 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -80,9 +80,11 @@ func (at *ActionTrigger) Execute(ub *Account, sq *StatsQueueTriggered) (err erro if a.Balance == nil { a.Balance = &BalanceFilter{} } - if expDate, parseErr := utils.ParseDate(a.ExpirationString); parseErr == nil && !expDate.IsZero() { - a.Balance.ExpirationDate = &time.Time{} - *a.Balance.ExpirationDate = expDate + if a.ExpirationString != "" { // if it's *unlimited then it has to be zero time' + if expDate, parseErr := utils.ParseDate(a.ExpirationString); parseErr == nil { + a.Balance.ExpirationDate = &time.Time{} + *a.Balance.ExpirationDate = expDate + } } actionFunction, exists := getActionFunc(a.ActionType) diff --git a/engine/actions_test.go b/engine/actions_test.go index 4cbe95279..1fea3f47d 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -2056,9 +2056,6 @@ func TestActionCSVFilter(t *testing.T) { } func TestActionExpirationTime(t *testing.T) { - if _, err := accountingStorage.GetAccount("cgrates.org:expo"); err != nil { - t.Errorf("account to be removed not found: %v", err) - } a, err := ratingStorage.GetActions("EXP", false) if err != nil || a == nil { t.Error("Error getting actions: ", err) @@ -2078,6 +2075,28 @@ func TestActionExpirationTime(t *testing.T) { } } +func TestActionExpNoExp(t *testing.T) { + exp, err := ratingStorage.GetActions("EXP", false) + if err != nil || exp == nil { + t.Error("Error getting actions: ", err) + } + noexp, err := ratingStorage.GetActions("NOEXP", false) + if err != nil || noexp == nil { + t.Error("Error getting actions: ", err) + } + exp = append(exp, noexp...) + at := &ActionTiming{ + accountIDs: utils.StringMap{"cgrates.org:expnoexp": true}, + actions: exp, + } + at.Execute() + afterUb, err := accountingStorage.GetAccount("cgrates.org:expnoexp") + if err != nil || + len(afterUb.BalanceMap[utils.VOICE]) != 2 { + t.Error("error topuping expiration balance: ", utils.ToIJSON(afterUb)) + } +} + /**************** Benchmarks ********************************/ func BenchmarkUUID(b *testing.B) { diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index e2bdd5be5..7c17f02fe 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -176,6 +176,7 @@ BLOCK,*topup,,,bblocker,*monetary,*out,,NAT,,,*unlimited,,10,20,true,false,20 BLOCK,*topup,,,bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 FILTER,*topup,,"{""*and"":[{""Value"":{""*lt"":0}},{""Id"":{""*eq"":""*default""}}]}",bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 EXP,*topup,,,,*voice,*out,,,,,*monthly,*any,300,10,false,false,10 +NOEXP,*topup,,,,*voice,*out,,,,,*unlimited,*any,50,10,false,false,10 ` actionPlans = ` MORE_MINUTES,MINI,ONE_TIME_RUN,10 @@ -216,6 +217,7 @@ vdf,post,POST_AT,,, cgrates.org,alodis,TOPUP_EMPTY_AT,,true,true cgrates.org,block,BLOCK_AT,,false,false cgrates.org,expo,EXP_AT,,false,false +cgrates.org,expnoexp,,,false,false ` derivedCharges = ` @@ -818,7 +820,7 @@ func TestLoadRatingProfiles(t *testing.T) { } func TestLoadActions(t *testing.T) { - if len(csvr.actions) != 12 { + if len(csvr.actions) != 13 { t.Error("Failed to load actions: ", len(csvr.actions)) } as1 := csvr.actions["MINI"] @@ -1099,7 +1101,7 @@ func TestLoadActionTriggers(t *testing.T) { } func TestLoadAccountActions(t *testing.T) { - if len(csvr.accountActions) != 13 { + if len(csvr.accountActions) != 14 { t.Error("Failed to load account actions: ", len(csvr.accountActions)) } aa := csvr.accountActions["vdf:minitsboy"] From 3b25c110d41ec3679366e9565afaf79f74785cda Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Mar 2016 00:05:25 +0200 Subject: [PATCH 154/199] updated to latest lib versions and fixed deps --- engine/cdrs.go | 4 ++-- engine/storage_sql.go | 2 +- glide.lock | 25 ++++++++++++------------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/engine/cdrs.go b/engine/cdrs.go index 1911477b6..bcdd7c7c0 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -113,7 +113,7 @@ func (self *CdrServer) LogCallCost(ccl *CallCostLog) error { if ccl.CheckDuplicate { _, err := self.guard.Guard(func() (interface{}, error) { cc, err := self.cdrDb.GetCallCostLog(ccl.CgrId, ccl.RunId) - if err != nil && err != gorm.RecordNotFound && err != mgov2.ErrNotFound { + if err != nil && err != gorm.ErrRecordNotFound && err != mgov2.ErrNotFound { return nil, err } if cc != nil { @@ -326,7 +326,7 @@ func (self *CdrServer) rateCDR(cdr *CDR) error { } time.Sleep(delay()) } - if err != nil && (err == gorm.RecordNotFound || err == mgov2.ErrNotFound) { //calculate CDR as for pseudoprepaid + if err != nil && (err == gorm.ErrRecordNotFound || err == mgov2.ErrNotFound) { //calculate CDR as for pseudoprepaid utils.Logger.Warning(fmt.Sprintf(" WARNING: Could not find CallCostLog for cgrid: %s, source: %s, runid: %s, will recalculate", cdr.CGRID, utils.SESSION_MANAGER_SOURCE, cdr.RunID)) qryCC, err = self.getCostFromRater(cdr) } diff --git a/engine/storage_sql.go b/engine/storage_sql.go index e7796e34f..d6457917a 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -35,7 +35,7 @@ import ( type SQLStorage struct { Db *sql.DB - db gorm.DB + db *gorm.DB } func (self *SQLStorage) Close() { diff --git a/glide.lock b/glide.lock index 06a89849e..18257fd92 100644 --- a/glide.lock +++ b/glide.lock @@ -1,12 +1,12 @@ hash: fde526f5fcd62d6b306dd6c4e30c6aa29ee3bfd625ffef3906c4d032eda7e9ec -updated: 2016-03-08T23:15:54.553132116+02:00 +updated: 2016-03-09T00:01:17.781232421+02:00 imports: - name: github.com/cenkalti/hub version: 57d753b5f4856e77b3cf8ecce78c97215a7d324d - name: github.com/cenkalti/rpc2 version: 2d1be381ce47537e9e076b2b76dc70933162e4e9 - name: github.com/cgrates/fsock - version: c3b1d274ae0e42742ba1bce2bf3a138d72fb82ee + version: a8ffdbdfc8440016df008ba91e6f05f806d7a69f - name: github.com/cgrates/kamevapi version: a376b1f937ba959857929fa3e111c0f3243278c0 - name: github.com/cgrates/osipsdagram @@ -18,7 +18,7 @@ imports: - name: github.com/DisposaBoy/JsonConfigReader version: 33a99fdf1d5ee1f79b5077e9c06f955ad356d5f4 - name: github.com/fiorix/go-diameter - version: 30569fd7282321ac213d8db9f39eac9321ab6bd5 + version: 1a8912836dee1f666f882b2e388e340a3a10ecdd subpackages: - diam - diam/avp @@ -28,40 +28,39 @@ imports: - diam/sm/smparser - diam/sm/smpeer - name: github.com/go-sql-driver/mysql - version: bb006fd699a123d3eb514561dbefc352e978949d + version: 0f2db9e6c9cff80a97ca5c2c5096242cc1554e16 - name: github.com/gorhill/cronexpr version: a557574d6c024ed6e36acc8b610f5f211c91568a - name: github.com/jinzhu/gorm - version: 2f7811c55f286c55cfc3a2aefb5c4049b9cd5214 + version: a0aa21aec5280d4394164fe1804629bc7507cb88 - name: github.com/jinzhu/inflection version: 3272df6c21d04180007eb3349844c89a3856bc25 - name: github.com/kr/pty version: f7ee69f31298ecbe5d2b349c711e2547a617d398 - name: github.com/lib/pq - version: 11fc39a580a008f1f39bb3d11d984fb34ed778d9 + version: 165a3529e799da61ab10faed1fabff3662d6193f subpackages: - - hstore - oid - name: github.com/mediocregopher/radix.v2 - version: 91435107718b55ff544323a2b0f25fdd8475d283 + version: 7bdaf7c45ec452ca691ab20535471e24460f0876 subpackages: - pool - redis - name: github.com/peterh/liner - version: 3f1c20449d1836aa4cbe38731b96f95cdf89634d + version: ad1edfd30321d8f006ccf05f1e0524adeb943060 - name: github.com/ugorji/go - version: 646ae4a518c1c3be0739df898118d9bccf993858 + version: 187fa0f8af224437e08ecb3f208c4d1a94859a61 subpackages: - codec - name: golang.org/x/net - version: 961116aeebe66bfb58bb4d51818c70d256acbbb8 + version: a4bbce9fcae005b22ae5443f6af064d80a6f5a55 subpackages: - websocket - context - name: gopkg.in/fsnotify.v1 - version: 508915b7500b6e42a87132e4afeb4729cebc7cbb + version: 875cf421b32f8f1b31bd43776297876d01542279 - name: gopkg.in/mgo.v2 - version: e30de8ac9ae3b30df7065f766c71f88bba7d4e49 + version: d90005c5262a3463800497ea5a89aed5fe22c886 subpackages: - bson - internal/sasl From 5c33108d332bea028f76c96f20565dad7c790ae8 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Mar 2016 00:09:57 +0200 Subject: [PATCH 155/199] glide cleanups --- glide.lock | 4 ++-- glide.yaml | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/glide.lock b/glide.lock index 18257fd92..58e26c53a 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: fde526f5fcd62d6b306dd6c4e30c6aa29ee3bfd625ffef3906c4d032eda7e9ec -updated: 2016-03-09T00:01:17.781232421+02:00 +hash: c4e3a1bdd7452ec3af195e09b8b3b1b9a61e36edfad557aeb01686706019c352 +updated: 2016-03-09T00:08:37.493018177+02:00 imports: - name: github.com/cenkalti/hub version: 57d753b5f4856e77b3cf8ecce78c97215a7d324d diff --git a/glide.yaml b/glide.yaml index d1d548281..7b78d485f 100644 --- a/glide.yaml +++ b/glide.yaml @@ -9,7 +9,7 @@ import: - package: github.com/cgrates/structmatcher - package: github.com/fiorix/go-diameter subpackages: - - /diam + - diam - diam/avp - diam/datatype - diam/dict @@ -17,20 +17,19 @@ import: - package: github.com/go-sql-driver/mysql - package: github.com/gorhill/cronexpr - package: github.com/jinzhu/gorm -- package: github.com/jinzhu/inflection - package: github.com/kr/pty - package: github.com/lib/pq - package: github.com/mediocregopher/radix.v2 subpackages: - - /pool + - pool - redis - package: github.com/peterh/liner - package: github.com/ugorji/go subpackages: - - /codec + - codec - package: golang.org/x/net subpackages: - - /websocket + - websocket - package: gopkg.in/fsnotify.v1 - package: gopkg.in/mgo.v2 subpackages: From 5324beb995987a1ffa559c0b506d70d17e6436eb Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 9 Mar 2016 10:01:44 +0100 Subject: [PATCH 156/199] Remove debug in calldesc --- engine/calldesc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/calldesc.go b/engine/calldesc.go index b5d2caffe..d45312e01 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -627,7 +627,7 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura } } } - utils.Logger.Debug(fmt.Sprintf("3_INIT DUR %v, TOTAL DUR: %v", initialDuration, totalDuration)) + //utils.Logger.Debug(fmt.Sprintf("3_INIT DUR %v, TOTAL DUR: %v", initialDuration, totalDuration)) return utils.MinDuration(initialDuration, totalDuration), nil } From be241c071af96a746b621f5cbbae0397c8144db9 Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 9 Mar 2016 10:42:17 +0100 Subject: [PATCH 157/199] Diameter fixes for LastUsed --- sessionmanager/smg_session.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 81c745a0a..cf5c03dae 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -79,7 +79,7 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) { func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.Duration, error) { self.lastUsage = dur // Reset the lastUsage for later reference lastUsedCorrection := time.Duration(0) // Used if lastUsed influences the debit - if self.cd.DurationIndex != 0 { + if self.cd.DurationIndex != 0 && lastUsed != 0 { if self.lastUsage > lastUsed { // We have debitted more than we have used, refund in the duration debitted lastUsedCorrection = -(self.lastUsage - lastUsed) } else { // We have debitted less than we have consumed, add the difference to duration debitted @@ -169,7 +169,6 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { return err } } - //utils.Logger.Debug(fmt.Sprintf("REFUND INCR: %s", utils.ToJSON(refundIncrements))) lastCC.Cost -= refundIncrements.GetTotalCost() lastCC.UpdateRatedUsage() lastCC.Timespans.Compress() From f982112ab7487394433ca598bccf7f3fba63f8ca Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 9 Mar 2016 11:48:36 +0100 Subject: [PATCH 158/199] Debug for lastUsed in diameter --- sessionmanager/smg_session.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index cf5c03dae..63fbc8115 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -77,6 +77,7 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) { // Attempts to debit a duration, returns maximum duration which can be debitted or error func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.Duration, error) { + utils.Logger.Debug(fmt.Sprintf("### SMGSession.debit durationIndex: %v, dur: %v, lastUsed: %v", self.cd.DurationIndex, dur, lastUsed)) self.lastUsage = dur // Reset the lastUsage for later reference lastUsedCorrection := time.Duration(0) // Used if lastUsed influences the debit if self.cd.DurationIndex != 0 && lastUsed != 0 { @@ -88,6 +89,7 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D } // apply the lastUsed correction dur += lastUsedCorrection + utils.Logger.Debug(fmt.Sprintf("### After lastUsedCorrection, durationIndex: %v, dur: %v, lastUsed: %v, lastUsedCorrection", self.cd.DurationIndex, dur, lastUsed, lastUsedCorrection)) // apply correction from previous run dur -= self.extraDuration self.extraDuration = 0 @@ -107,7 +109,11 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D if ccDuration != dur { self.extraDuration = ccDuration - dur } + utils.Logger.Debug(fmt.Sprintf("### ANSWER durationIndex: %v, dur: %v, extraDuration: %v, lastUsed: %v, lastUsedCorrection: %v", + self.cd.DurationIndex, dur, self.extraDuration, lastUsed, lastUsedCorrection)) dur -= lastUsedCorrection // Revert the correction to return the real duration reserved + utils.Logger.Debug(fmt.Sprintf("### ANSWER after lastUsedCorrection, durationIndex: %v, dur: %v, lastUsedCorrection: %v", + self.cd.DurationIndex, dur, lastUsedCorrection)) self.cd.DurationIndex -= dur self.cd.DurationIndex += ccDuration self.cd.MaxCostSoFar += cc.Cost From 84357ef26268857599d56dc9c88c268d05856cf1 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Mar 2016 11:51:14 +0200 Subject: [PATCH 159/199] skip action plans if missing on remove account --- apier/v1/accounts.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 6973cd84d..f20088a3e 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -257,6 +257,10 @@ func (self *ApierV1) RemoveAccount(attr utils.AttrRemoveAccount, reply *string) // remove it from all action plans _, err := engine.Guardian.Guard(func() (interface{}, error) { actionPlansMap, err := self.RatingDb.GetAllActionPlans() + if err == utils.ErrNotFound { + // no action plans + return 0, nil + } if err != nil { return 0, err } From 66451d0d0872ee5b234a7a3cd0c2dc3c98f25246 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Mar 2016 14:49:11 +0200 Subject: [PATCH 160/199] revert to older gorm version --- engine/cdrs.go | 4 ++-- engine/storage_sql.go | 2 +- glide.lock | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/engine/cdrs.go b/engine/cdrs.go index bcdd7c7c0..1911477b6 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -113,7 +113,7 @@ func (self *CdrServer) LogCallCost(ccl *CallCostLog) error { if ccl.CheckDuplicate { _, err := self.guard.Guard(func() (interface{}, error) { cc, err := self.cdrDb.GetCallCostLog(ccl.CgrId, ccl.RunId) - if err != nil && err != gorm.ErrRecordNotFound && err != mgov2.ErrNotFound { + if err != nil && err != gorm.RecordNotFound && err != mgov2.ErrNotFound { return nil, err } if cc != nil { @@ -326,7 +326,7 @@ func (self *CdrServer) rateCDR(cdr *CDR) error { } time.Sleep(delay()) } - if err != nil && (err == gorm.ErrRecordNotFound || err == mgov2.ErrNotFound) { //calculate CDR as for pseudoprepaid + if err != nil && (err == gorm.RecordNotFound || err == mgov2.ErrNotFound) { //calculate CDR as for pseudoprepaid utils.Logger.Warning(fmt.Sprintf(" WARNING: Could not find CallCostLog for cgrid: %s, source: %s, runid: %s, will recalculate", cdr.CGRID, utils.SESSION_MANAGER_SOURCE, cdr.RunID)) qryCC, err = self.getCostFromRater(cdr) } diff --git a/engine/storage_sql.go b/engine/storage_sql.go index d6457917a..e7796e34f 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -35,7 +35,7 @@ import ( type SQLStorage struct { Db *sql.DB - db *gorm.DB + db gorm.DB } func (self *SQLStorage) Close() { diff --git a/glide.lock b/glide.lock index 58e26c53a..0c3abf76f 100644 --- a/glide.lock +++ b/glide.lock @@ -32,7 +32,7 @@ imports: - name: github.com/gorhill/cronexpr version: a557574d6c024ed6e36acc8b610f5f211c91568a - name: github.com/jinzhu/gorm - version: a0aa21aec5280d4394164fe1804629bc7507cb88 + version: 2f7811c55f286c55cfc3a2aefb5c4049b9cd5214 - name: github.com/jinzhu/inflection version: 3272df6c21d04180007eb3349844c89a3856bc25 - name: github.com/kr/pty From e69e3aabe11c1c79423b9dd7153f8aee058f9b5c Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Mar 2016 15:03:33 +0200 Subject: [PATCH 161/199] fixes --- agents/dmtagent_it_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index b558049aa..b9aed75ac 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -297,7 +297,7 @@ func TestDmtAgentSendCCRUpdate(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.5008 + eAcntVal := 9.251800 if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { @@ -336,7 +336,7 @@ func TestDmtAgentSendCCRUpdate2(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 9.2016 + eAcntVal := 9.002800 if err := apierRpc.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if utils.Round(acnt.BalanceMap[utils.MONETARY].GetTotalValue(), 5, utils.ROUNDING_MIDDLE) != eAcntVal { From 8fa986d8a9f65c065de0b0f14feddb9d26a919ef Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Mar 2016 16:55:23 +0200 Subject: [PATCH 162/199] Balance values in callcost increments --- engine/account.go | 3 +- engine/account_test.go | 12 ++-- engine/balances.go | 16 ++++-- engine/calldesc.go | 20 +++---- engine/calldesc_test.go | 23 ++++---- engine/timespans.go | 16 ++++-- engine/timespans_test.go | 120 ++++++++++++++++++++++++++------------- 7 files changed, 132 insertions(+), 78 deletions(-) diff --git a/engine/account.go b/engine/account.go index 87c1f9e3c..046a95c57 100644 --- a/engine/account.go +++ b/engine/account.go @@ -487,7 +487,8 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo defaultBalance := ub.GetDefaultMoneyBalance() defaultBalance.SubstractValue(cost) increment.BalanceInfo.MoneyBalanceUuid = defaultBalance.Uuid - increment.BalanceInfo.AccountId = ub.ID + increment.BalanceInfo.MoneyBalanceValue = defaultBalance.Value + increment.BalanceInfo.AccountID = ub.ID increment.paid = true if count { ub.countUnits( diff --git a/engine/account_test.go b/engine/account_test.go index cb87a69af..0e6cecede 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -1187,12 +1187,12 @@ func TestDebitShared(t *testing.T) { t.Errorf("I%d: %+v (%+v)", index, incr, incr.BalanceInfo) } } - if cc.Timespans[0].Increments[0].BalanceInfo.AccountId != "groupie" || - cc.Timespans[0].Increments[1].BalanceInfo.AccountId != "groupie" || - cc.Timespans[0].Increments[2].BalanceInfo.AccountId != "groupie" || - cc.Timespans[0].Increments[3].BalanceInfo.AccountId != "groupie" || - cc.Timespans[0].Increments[4].BalanceInfo.AccountId != "groupie" || - cc.Timespans[0].Increments[5].BalanceInfo.AccountId != "groupie" { + if cc.Timespans[0].Increments[0].BalanceInfo.AccountID != "groupie" || + cc.Timespans[0].Increments[1].BalanceInfo.AccountID != "groupie" || + cc.Timespans[0].Increments[2].BalanceInfo.AccountID != "groupie" || + cc.Timespans[0].Increments[3].BalanceInfo.AccountID != "groupie" || + cc.Timespans[0].Increments[4].BalanceInfo.AccountID != "groupie" || + cc.Timespans[0].Increments[5].BalanceInfo.AccountID != "groupie" { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } } diff --git a/engine/balances.go b/engine/balances.go index f089c6253..c74855c51 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -356,7 +356,8 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala if b.GetValue() >= amount { b.SubstractValue(amount) inc.BalanceInfo.UnitBalanceUuid = b.Uuid - inc.BalanceInfo.AccountId = ub.ID + inc.BalanceInfo.UnitBalanceValue = b.Value + inc.BalanceInfo.AccountID = ub.ID inc.BalanceInfo.RateInterval = nil inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR} inc.Cost = 0 @@ -428,7 +429,8 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala if strategy == utils.MAX_COST_FREE && cd.MaxCostSoFar >= maxCost { cost, inc.Cost = 0.0, 0.0 inc.BalanceInfo.MoneyBalanceUuid = b.Uuid - inc.BalanceInfo.AccountId = ub.ID + inc.BalanceInfo.MoneyBalanceValue = b.Value + inc.BalanceInfo.AccountID = ub.ID inc.BalanceInfo.RateInterval = ts.RateInterval inc.paid = true if count { @@ -447,11 +449,13 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala if (cost == 0 || moneyBal != nil) && b.GetValue() >= amount { b.SubstractValue(amount) inc.BalanceInfo.UnitBalanceUuid = b.Uuid - inc.BalanceInfo.AccountId = ub.ID + inc.BalanceInfo.UnitBalanceValue = b.Value + inc.BalanceInfo.AccountID = ub.ID inc.BalanceInfo.RateInterval = nil inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR} if cost != 0 { inc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid + inc.BalanceInfo.MoneyBalanceValue = moneyBal.Value moneyBal.SubstractValue(cost) cd.MaxCostSoFar += cost } @@ -537,7 +541,8 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala if strategy == utils.MAX_COST_FREE && cd.MaxCostSoFar >= maxCost { amount, inc.Cost = 0.0, 0.0 inc.BalanceInfo.MoneyBalanceUuid = b.Uuid - inc.BalanceInfo.AccountId = ub.ID + inc.BalanceInfo.MoneyBalanceValue = b.Value + inc.BalanceInfo.AccountID = ub.ID if b.RatingSubject != "" { inc.BalanceInfo.RateInterval = ts.RateInterval } @@ -555,7 +560,8 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala b.SubstractValue(amount) cd.MaxCostSoFar += amount inc.BalanceInfo.MoneyBalanceUuid = b.Uuid - inc.BalanceInfo.AccountId = ub.ID + inc.BalanceInfo.MoneyBalanceValue = b.Value + inc.BalanceInfo.AccountID = ub.ID if b.RatingSubject != "" { inc.BalanceInfo.RateInterval = ts.RateInterval } diff --git a/engine/calldesc.go b/engine/calldesc.go index d45312e01..b82772bb5 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -783,23 +783,23 @@ func (cd *CallDescriptor) RefundIncrements() error { accMap := make(utils.StringMap) cd.Increments.Decompress() for _, increment := range cd.Increments { - accMap[increment.BalanceInfo.AccountId] = true + accMap[increment.BalanceInfo.AccountID] = true } // start increment refunding loop _, err := Guardian.Guard(func() (interface{}, error) { accountsCache := make(map[string]*Account) for _, increment := range cd.Increments { - account, found := accountsCache[increment.BalanceInfo.AccountId] + account, found := accountsCache[increment.BalanceInfo.AccountID] if !found { - if acc, err := accountingStorage.GetAccount(increment.BalanceInfo.AccountId); err == nil && acc != nil { + if acc, err := accountingStorage.GetAccount(increment.BalanceInfo.AccountID); err == nil && acc != nil { account = acc - accountsCache[increment.BalanceInfo.AccountId] = account + accountsCache[increment.BalanceInfo.AccountID] = account // will save the account only once at the end of the function defer accountingStorage.SetAccount(account) } } if account == nil { - utils.Logger.Warning(fmt.Sprintf("Could not get the account to be refunded: %s", increment.BalanceInfo.AccountId)) + utils.Logger.Warning(fmt.Sprintf("Could not get the account to be refunded: %s", increment.BalanceInfo.AccountID)) continue } //utils.Logger.Info(fmt.Sprintf("Refunding increment %+v", increment)) @@ -832,23 +832,23 @@ func (cd *CallDescriptor) RefundRounding() error { // all must be locked in order to use cache accMap := make(utils.StringMap) for _, inc := range cd.Increments { - accMap[inc.BalanceInfo.AccountId] = true + accMap[inc.BalanceInfo.AccountID] = true } // start increment refunding loop _, err := Guardian.Guard(func() (interface{}, error) { accountsCache := make(map[string]*Account) for _, increment := range cd.Increments { - account, found := accountsCache[increment.BalanceInfo.AccountId] + account, found := accountsCache[increment.BalanceInfo.AccountID] if !found { - if acc, err := accountingStorage.GetAccount(increment.BalanceInfo.AccountId); err == nil && acc != nil { + if acc, err := accountingStorage.GetAccount(increment.BalanceInfo.AccountID); err == nil && acc != nil { account = acc - accountsCache[increment.BalanceInfo.AccountId] = account + accountsCache[increment.BalanceInfo.AccountID] = account // will save the account only once at the end of the function defer accountingStorage.SetAccount(account) } } if account == nil { - utils.Logger.Warning(fmt.Sprintf("Could not get the account to be refunded: %s", increment.BalanceInfo.AccountId)) + utils.Logger.Warning(fmt.Sprintf("Could not get the account to be refunded: %s", increment.BalanceInfo.AccountID)) continue } cc := cd.CreateCallCost() diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 49b6cc26a..c6f92e450 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -1096,15 +1096,16 @@ func TestDebitAndMaxDebit(t *testing.T) { if err1 != nil || err2 != nil { t.Error("Error debiting and/or maxdebiting: ", err1, err2) } + if cc1.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue != 90 || + cc2.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue != 80 { + t.Error("Error setting the UnitBalanceValue: ", cc1.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue, cc2.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue) + } + // make UnitBalanceValues have the same value + cc1.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue = 0 + cc2.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue = 0 if !reflect.DeepEqual(cc1, cc2) { - t.Logf("CC1: %+v", cc1) - for _, ts := range cc1.Timespans { - t.Logf("TS: %+v", ts) - } - t.Logf("CC2: %+v", cc2) - for _, ts := range cc2.Timespans { - t.Logf("TS: %+v", ts) - } + t.Log("CC1: ", utils.ToIJSON(cc1)) + t.Log("CC2: ", utils.ToIJSON(cc2)) t.Error("Debit and MaxDebit differ") } } @@ -1454,9 +1455,9 @@ func TestCDRefundIncrements(t *testing.T) { } accountingStorage.SetAccount(ub) increments := Increments{ - &Increment{Cost: 2, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "", MoneyBalanceUuid: "moneya", AccountId: ub.ID}}, - &Increment{Cost: 2, Duration: 3 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minutea", MoneyBalanceUuid: "moneya", AccountId: ub.ID}}, - &Increment{Duration: 4 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minuteb", MoneyBalanceUuid: "", AccountId: ub.ID}}, + &Increment{Cost: 2, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "", MoneyBalanceUuid: "moneya", AccountID: ub.ID}}, + &Increment{Cost: 2, Duration: 3 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minutea", MoneyBalanceUuid: "moneya", AccountID: ub.ID}}, + &Increment{Duration: 4 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minuteb", MoneyBalanceUuid: "", AccountID: ub.ID}}, } cd := &CallDescriptor{TOR: utils.VOICE, Increments: increments} cd.RefundIncrements() diff --git a/engine/timespans.go b/engine/timespans.go index 34aec558b..c8532fe2e 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -67,17 +67,19 @@ func (mi *UnitInfo) Equal(other *UnitInfo) bool { // Holds information about the balance that made a specific payment type BalanceInfo struct { - UnitBalanceUuid string - MoneyBalanceUuid string - RateInterval *RateInterval - AccountId string // used when debited from shared balance + UnitBalanceUuid string + MoneyBalanceUuid string + UnitBalanceValue float64 + MoneyBalanceValue float64 + RateInterval *RateInterval + AccountID string // used when debited from shared balance } func (bi *BalanceInfo) Equal(other *BalanceInfo) bool { return bi.UnitBalanceUuid == other.UnitBalanceUuid && bi.MoneyBalanceUuid == other.MoneyBalanceUuid && reflect.DeepEqual(bi.RateInterval, other.RateInterval) && - bi.AccountId == other.AccountId + bi.AccountID == other.AccountID } type TimeSpans []*TimeSpan @@ -260,6 +262,10 @@ func (incs *Increments) Compress() { // must be pointer receiver cIncrs = append(cIncrs, incr) } else { cIncrs[len(cIncrs)-1].CompressFactor++ + if cIncrs[len(cIncrs)-1].BalanceInfo != nil && incr.BalanceInfo != nil { + cIncrs[len(cIncrs)-1].BalanceInfo.MoneyBalanceValue = incr.BalanceInfo.MoneyBalanceValue + cIncrs[len(cIncrs)-1].BalanceInfo.UnitBalanceValue = incr.BalanceInfo.UnitBalanceValue + } } } *incs = cIncrs diff --git a/engine/timespans_test.go b/engine/timespans_test.go index b797dff86..cd96c37fa 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -1510,34 +1510,54 @@ func TestTSIncrementsCompressDecompress(t *testing.T) { &TimeSpan{ Increments: Increments{ &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{ + UnitBalanceUuid: "1", + MoneyBalanceUuid: "2", + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + AccountID: "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{ + UnitBalanceUuid: "1", + MoneyBalanceUuid: "2", + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + AccountID: "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{ + UnitBalanceUuid: "1", + MoneyBalanceUuid: "2", + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + AccountID: "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{ + UnitBalanceUuid: "1", + MoneyBalanceUuid: "2", + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, + AccountID: "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{ + UnitBalanceUuid: "1", + MoneyBalanceUuid: "2", + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + AccountID: "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, }, }, @@ -1557,34 +1577,54 @@ func TestTSMultipleIncrementsCompressDecompress(t *testing.T) { &TimeSpan{ Increments: Increments{ &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{ + UnitBalanceUuid: "1", + MoneyBalanceUuid: "2", + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + AccountID: "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{ + UnitBalanceUuid: "1", + MoneyBalanceUuid: "2", + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + AccountID: "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{ + UnitBalanceUuid: "1", + MoneyBalanceUuid: "2", + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + AccountID: "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{ + UnitBalanceUuid: "1", + MoneyBalanceUuid: "2", + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, + AccountID: "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, &Increment{ - Duration: time.Minute, - Cost: 10.4, - BalanceInfo: &BalanceInfo{"1", "2", &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + Duration: time.Minute, + Cost: 10.4, + BalanceInfo: &BalanceInfo{ + UnitBalanceUuid: "1", + MoneyBalanceUuid: "2", + RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + AccountID: "3"}, + UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, }, }, }, From a8891cf1e0045dd6e6993acfd6796e41481230ad Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 9 Mar 2016 22:11:46 +0200 Subject: [PATCH 163/199] refactoring on balance info --- engine/account.go | 6 ++- engine/account_test.go | 40 ++++++++-------- engine/balances.go | 54 +++++++++++++-------- engine/callcost.go | 3 +- engine/calldesc.go | 14 +++--- engine/calldesc_test.go | 20 ++++---- engine/datacost.go | 12 ++--- engine/timespans.go | 89 +++++++++++++++++++++------------- engine/timespans_test.go | 100 ++++++++++++++++----------------------- 9 files changed, 178 insertions(+), 160 deletions(-) diff --git a/engine/account.go b/engine/account.go index 046a95c57..9cec58246 100644 --- a/engine/account.go +++ b/engine/account.go @@ -486,8 +486,10 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo cost := increment.Cost defaultBalance := ub.GetDefaultMoneyBalance() defaultBalance.SubstractValue(cost) - increment.BalanceInfo.MoneyBalanceUuid = defaultBalance.Uuid - increment.BalanceInfo.MoneyBalanceValue = defaultBalance.Value + increment.BalanceInfo.Monetary = &MonetaryInfo{ + UUID: defaultBalance.Uuid, + Value: defaultBalance.Value, + } increment.BalanceInfo.AccountID = ub.ID increment.paid = true if count { diff --git a/engine/account_test.go b/engine/account_test.go index 0e6cecede..90be903d2 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -194,7 +194,7 @@ func TestDebitCreditZeroSecond(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" { + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testb" { t.Logf("%+v", cc.Timespans[0]) t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } @@ -238,7 +238,7 @@ func TestDebitCreditZeroMinute(t *testing.T) { t.Error("Error debiting balance: ", err) } t.Logf("%+v", cc.Timespans) - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" || + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testb" || cc.Timespans[0].Increments[0].Duration != time.Minute { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } @@ -284,8 +284,8 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "tests" || - cc.Timespans[1].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" { + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "tests" || + cc.Timespans[1].Increments[0].BalanceInfo.Unit.UUID != "testm" { t.Error("Error setting balance id to increment: ", cc.Timespans) } if rifsBalance.BalanceMap[utils.VOICE][1].GetValue() != 0 || @@ -335,7 +335,7 @@ func TestDebitCreditNoCredit(t *testing.T) { if err == nil { t.Error("Showing no enough credit error ") } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" || + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testb" || cc.Timespans[0].Increments[0].Duration != time.Minute { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } @@ -387,7 +387,7 @@ func TestDebitCreditHasCredit(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" || + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testb" || cc.Timespans[0].Increments[0].Duration != time.Minute { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } @@ -435,7 +435,7 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" || + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testb" || cc.Timespans[0].Increments[0].Duration != 1*time.Second { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0].Duration) } @@ -487,7 +487,7 @@ func TestDebitCreditMoreTimespans(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" || + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testb" || cc.Timespans[0].Increments[0].Duration != time.Minute { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } @@ -536,7 +536,7 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" || + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testb" || cc.Timespans[0].Increments[0].Duration != time.Minute { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } @@ -630,7 +630,7 @@ func TestDebitCreditMoneyOnly(t *testing.T) { if err == nil { t.Error("Missing noy enough credit error ") } - if cc.Timespans[0].Increments[0].BalanceInfo.MoneyBalanceUuid != "money" || + if cc.Timespans[0].Increments[0].BalanceInfo.Monetary.UUID != "money" || cc.Timespans[0].Increments[0].Duration != 10*time.Second { t.Logf("%+v", cc.Timespans[0].Increments) t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0].BalanceInfo) @@ -684,8 +684,8 @@ func TestDebitCreditSubjectMinutes(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" || - cc.Timespans[0].Increments[0].BalanceInfo.MoneyBalanceUuid != "moneya" || + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testb" || + cc.Timespans[0].Increments[0].BalanceInfo.Monetary.UUID != "moneya" || cc.Timespans[0].Increments[0].Duration != 10*time.Second { t.Errorf("Error setting balance id to increment: %+v", cc.Timespans[0].Increments[0]) } @@ -738,7 +738,7 @@ func TestDebitCreditSubjectMoney(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.MoneyBalanceUuid != "moneya" || + if cc.Timespans[0].Increments[0].BalanceInfo.Monetary.UUID != "moneya" || cc.Timespans[0].Increments[0].Duration != 10*time.Second { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } @@ -789,8 +789,8 @@ func TestDebitCreditSubjectMoney(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testb" || - cc.Timespans[0].Increments[0].BalanceInfo.MoneyBalanceUuid != "moneya" || + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testb" || + cc.Timespans[0].Increments[0].BalanceInfo.Money.UUID != "moneya" || cc.Timespans[0].Increments[0].Duration != 10*time.Second { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } @@ -1306,7 +1306,7 @@ func TestDebitSMS(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" { + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testm" { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } if rifsBalance.BalanceMap[utils.SMS][0].GetValue() != 99 || @@ -1349,7 +1349,7 @@ func TestDebitGeneric(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" { + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testm" { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99 || @@ -1392,7 +1392,7 @@ func TestDebitGenericBalance(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" { + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testm" { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.49999 || @@ -1435,7 +1435,7 @@ func TestDebitGenericBalanceWithRatingSubject(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if cc.Timespans[0].Increments[0].BalanceInfo.UnitBalanceUuid != "testm" { + if cc.Timespans[0].Increments[0].BalanceInfo.Unit.UUID != "testm" { t.Error("Error setting balance id to increment: ", cc.Timespans[0]) } if rifsBalance.BalanceMap[utils.GENERIC][0].GetValue() != 99.49999 || @@ -1490,7 +1490,7 @@ func TestDebitDataUnits(t *testing.T) { if err != nil { t.Error("Error debiting balance: ", err) } - if ts.Increments[0].BalanceInfo.UnitBalanceUuid != "testm" { + if ts.Increments[0].BalanceInfo.Unit.UUID != "testm" { t.Error("Error setting balance id to increment: ", ts.Increments[0]) } if rifsBalance.BalanceMap[utils.DATA][0].GetValue() != 20 || diff --git a/engine/balances.go b/engine/balances.go index c74855c51..e51b7794f 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -355,11 +355,15 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala } if b.GetValue() >= amount { b.SubstractValue(amount) - inc.BalanceInfo.UnitBalanceUuid = b.Uuid - inc.BalanceInfo.UnitBalanceValue = b.Value + inc.BalanceInfo.Unit = &UnitInfo{ + UUID: b.Uuid, + Value: b.Value, + DestinationID: cc.Destination, + Consumed: amount, + TOR: cc.TOR, + RateInterval: nil, + } inc.BalanceInfo.AccountID = ub.ID - inc.BalanceInfo.RateInterval = nil - inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR} inc.Cost = 0 inc.paid = true if count { @@ -428,10 +432,12 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala } if strategy == utils.MAX_COST_FREE && cd.MaxCostSoFar >= maxCost { cost, inc.Cost = 0.0, 0.0 - inc.BalanceInfo.MoneyBalanceUuid = b.Uuid - inc.BalanceInfo.MoneyBalanceValue = b.Value + inc.BalanceInfo.Monetary = &MonetaryInfo{ + UUID: b.Uuid, + Value: b.Value, + RateInterval: ts.RateInterval, + } inc.BalanceInfo.AccountID = ub.ID - inc.BalanceInfo.RateInterval = ts.RateInterval inc.paid = true if count { ub.countUnits(cost, utils.MONETARY, cc, b) @@ -448,14 +454,20 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala } if (cost == 0 || moneyBal != nil) && b.GetValue() >= amount { b.SubstractValue(amount) - inc.BalanceInfo.UnitBalanceUuid = b.Uuid - inc.BalanceInfo.UnitBalanceValue = b.Value + inc.BalanceInfo.Unit = &UnitInfo{ + UUID: b.Uuid, + Value: b.Value, + DestinationID: cc.Destination, + Consumed: amount, + TOR: cc.TOR, + RateInterval: ts.RateInterval, + } inc.BalanceInfo.AccountID = ub.ID - inc.BalanceInfo.RateInterval = nil - inc.UnitInfo = &UnitInfo{cc.Destination, amount, cc.TOR} if cost != 0 { - inc.BalanceInfo.MoneyBalanceUuid = moneyBal.Uuid - inc.BalanceInfo.MoneyBalanceValue = moneyBal.Value + inc.BalanceInfo.Monetary = &MonetaryInfo{ + UUID: moneyBal.Uuid, + Value: moneyBal.Value, + } moneyBal.SubstractValue(cost) cd.MaxCostSoFar += cost } @@ -540,11 +552,13 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala } if strategy == utils.MAX_COST_FREE && cd.MaxCostSoFar >= maxCost { amount, inc.Cost = 0.0, 0.0 - inc.BalanceInfo.MoneyBalanceUuid = b.Uuid - inc.BalanceInfo.MoneyBalanceValue = b.Value + inc.BalanceInfo.Monetary = &MonetaryInfo{ + UUID: b.Uuid, + Value: b.Value, + } inc.BalanceInfo.AccountID = ub.ID if b.RatingSubject != "" { - inc.BalanceInfo.RateInterval = ts.RateInterval + inc.BalanceInfo.Monetary.RateInterval = ts.RateInterval } inc.paid = true if count { @@ -559,11 +573,13 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala if b.GetValue() >= amount { b.SubstractValue(amount) cd.MaxCostSoFar += amount - inc.BalanceInfo.MoneyBalanceUuid = b.Uuid - inc.BalanceInfo.MoneyBalanceValue = b.Value + inc.BalanceInfo.Monetary = &MonetaryInfo{ + UUID: b.Uuid, + Value: b.Value, + } inc.BalanceInfo.AccountID = ub.ID if b.RatingSubject != "" { - inc.BalanceInfo.RateInterval = ts.RateInterval + inc.BalanceInfo.Monetary.RateInterval = ts.RateInterval } inc.paid = true if count { diff --git a/engine/callcost.go b/engine/callcost.go index fcfd59006..71e8609f2 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -140,7 +140,6 @@ func (cc *CallCost) ToDataCost() (*DataCost, error) { Amount: incr.Duration.Seconds(), Cost: incr.Cost, BalanceInfo: incr.BalanceInfo, - UnitInfo: incr.UnitInfo, CompressFactor: incr.CompressFactor, paid: incr.paid, } @@ -192,7 +191,7 @@ func (cc *CallCost) Round() { continue // safe check } inc := ts.Increments[0] - if inc.BalanceInfo.MoneyBalanceUuid == "" || inc.Cost == 0 { + if inc.BalanceInfo.Monetary == nil || inc.Cost == 0 { // this is a unit payied timespan, nothing to round continue } diff --git a/engine/calldesc.go b/engine/calldesc.go index b82772bb5..13a49820e 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -608,7 +608,7 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura for _, incr := range ts.Increments { //utils.Logger.Debug("INCR: " + utils.ToJSON(incr)) totalCost += incr.Cost - if incr.BalanceInfo.MoneyBalanceUuid == defaultBalance.Uuid { + if incr.BalanceInfo.Monetary != nil && incr.BalanceInfo.Monetary.UUID == defaultBalance.Uuid { initialDefaultBalanceValue -= incr.Cost if initialDefaultBalanceValue < 0 { // this increment was payed with debt @@ -806,16 +806,16 @@ func (cd *CallDescriptor) RefundIncrements() error { var balance *Balance unitType := cd.TOR cc := cd.CreateCallCost() - if increment.BalanceInfo.UnitBalanceUuid != "" { - if balance = account.BalanceMap[unitType].GetBalance(increment.BalanceInfo.UnitBalanceUuid); balance == nil { + if increment.BalanceInfo.Unit != nil && increment.BalanceInfo.Unit.UUID != "" { + if balance = account.BalanceMap[unitType].GetBalance(increment.BalanceInfo.Unit.UUID); balance == nil { return 0, nil } balance.AddValue(increment.Duration.Seconds()) account.countUnits(-increment.Duration.Seconds(), unitType, cc, balance) } // check money too - if increment.BalanceInfo.MoneyBalanceUuid != "" { - if balance = account.BalanceMap[utils.MONETARY].GetBalance(increment.BalanceInfo.MoneyBalanceUuid); balance == nil { + if increment.BalanceInfo.Monetary != nil && increment.BalanceInfo.Monetary.UUID != "" { + if balance = account.BalanceMap[utils.MONETARY].GetBalance(increment.BalanceInfo.Monetary.UUID); balance == nil { return 0, nil } balance.AddValue(increment.Cost) @@ -852,9 +852,9 @@ func (cd *CallDescriptor) RefundRounding() error { continue } cc := cd.CreateCallCost() - if increment.BalanceInfo.MoneyBalanceUuid != "" { + if increment.BalanceInfo.Monetary != nil { var balance *Balance - if balance = account.BalanceMap[utils.MONETARY].GetBalance(increment.BalanceInfo.MoneyBalanceUuid); balance == nil { + if balance = account.BalanceMap[utils.MONETARY].GetBalance(increment.BalanceInfo.Monetary.UUID); balance == nil { return 0, nil } balance.AddValue(-increment.Cost) diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index c6f92e450..540bc8dad 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -1096,13 +1096,13 @@ func TestDebitAndMaxDebit(t *testing.T) { if err1 != nil || err2 != nil { t.Error("Error debiting and/or maxdebiting: ", err1, err2) } - if cc1.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue != 90 || - cc2.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue != 80 { - t.Error("Error setting the UnitBalanceValue: ", cc1.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue, cc2.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue) + if cc1.Timespans[0].Increments[0].BalanceInfo.Unit.Value != 90 || + cc2.Timespans[0].Increments[0].BalanceInfo.Unit.Value != 80 { + t.Error("Error setting the Unit.Value: ", cc1.Timespans[0].Increments[0].BalanceInfo.Unit.Value, cc2.Timespans[0].Increments[0].BalanceInfo.Unit.Value) } - // make UnitBalanceValues have the same value - cc1.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue = 0 - cc2.Timespans[0].Increments[0].BalanceInfo.UnitBalanceValue = 0 + // make Unit.Values have the same value + cc1.Timespans[0].Increments[0].BalanceInfo.Unit.Value = 0 + cc2.Timespans[0].Increments[0].BalanceInfo.Unit.Value = 0 if !reflect.DeepEqual(cc1, cc2) { t.Log("CC1: ", utils.ToIJSON(cc1)) t.Log("CC2: ", utils.ToIJSON(cc2)) @@ -1455,9 +1455,9 @@ func TestCDRefundIncrements(t *testing.T) { } accountingStorage.SetAccount(ub) increments := Increments{ - &Increment{Cost: 2, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "", MoneyBalanceUuid: "moneya", AccountID: ub.ID}}, - &Increment{Cost: 2, Duration: 3 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minutea", MoneyBalanceUuid: "moneya", AccountID: ub.ID}}, - &Increment{Duration: 4 * time.Second, BalanceInfo: &BalanceInfo{UnitBalanceUuid: "minuteb", MoneyBalanceUuid: "", AccountID: ub.ID}}, + &Increment{Cost: 2, BalanceInfo: &DebitInfo{Monetary: &MonetaryInfo{UUID: "moneya"}, AccountID: ub.ID}}, + &Increment{Cost: 2, Duration: 3 * time.Second, BalanceInfo: &DebitInfo{Unit: &UnitInfo{UUID: "minutea"}, Monetary: &MonetaryInfo{UUID: "moneya"}, AccountID: ub.ID}}, + &Increment{Duration: 4 * time.Second, BalanceInfo: &DebitInfo{Unit: &UnitInfo{UUID: "minuteb"}, AccountID: ub.ID}}, } cd := &CallDescriptor{TOR: utils.VOICE, Increments: increments} cd.RefundIncrements() @@ -1465,7 +1465,7 @@ func TestCDRefundIncrements(t *testing.T) { if ub.BalanceMap[utils.MONETARY][0].GetValue() != 104 || ub.BalanceMap[utils.VOICE][0].GetValue() != 13 || ub.BalanceMap[utils.VOICE][1].GetValue() != 14 { - t.Error("Error refunding money: ", ub.BalanceMap[utils.VOICE][1].GetValue()) + t.Error("Error refunding money: ", utils.ToIJSON(ub.BalanceMap)) } } diff --git a/engine/datacost.go b/engine/datacost.go index 36698295a..722efdaa2 100644 --- a/engine/datacost.go +++ b/engine/datacost.go @@ -35,11 +35,9 @@ type DataSpan struct { } type DataIncrement struct { - Amount float64 - Cost float64 - BalanceInfo *BalanceInfo // need more than one for units with cost - BalanceRateInterval *RateInterval - UnitInfo *UnitInfo - CompressFactor int - paid bool + Amount float64 + Cost float64 + BalanceInfo *DebitInfo // need more than one for units with cost + CompressFactor int + paid bool } diff --git a/engine/timespans.go b/engine/timespans.go index c8532fe2e..dfd8fb8b4 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -45,43 +45,64 @@ type TimeSpan struct { type Increment struct { Duration time.Duration Cost float64 - BalanceInfo *BalanceInfo // need more than one for units with cost - UnitInfo *UnitInfo + BalanceInfo *DebitInfo // need more than one for units with cost CompressFactor int paid bool } -// Holds the minute information related to a specified timespan -type UnitInfo struct { - DestinationId string - Quantity float64 - TOR string - //Price float64 -} - -func (mi *UnitInfo) Equal(other *UnitInfo) bool { - return mi.DestinationId == other.DestinationId && - mi.Quantity == other.Quantity && - mi.TOR == other.TOR -} - // Holds information about the balance that made a specific payment -type BalanceInfo struct { - UnitBalanceUuid string - MoneyBalanceUuid string - UnitBalanceValue float64 - MoneyBalanceValue float64 - RateInterval *RateInterval - AccountID string // used when debited from shared balance +type DebitInfo struct { + Unit *UnitInfo + Monetary *MonetaryInfo + AccountID string // used when debited from shared balance } -func (bi *BalanceInfo) Equal(other *BalanceInfo) bool { - return bi.UnitBalanceUuid == other.UnitBalanceUuid && - bi.MoneyBalanceUuid == other.MoneyBalanceUuid && - reflect.DeepEqual(bi.RateInterval, other.RateInterval) && +func (bi *DebitInfo) Equal(other *DebitInfo) bool { + return bi.Unit.Equal(other.Unit) && + bi.Monetary.Equal(other.Monetary) && bi.AccountID == other.AccountID } +type MonetaryInfo struct { + UUID string + Value float64 + RateInterval *RateInterval +} + +func (mi *MonetaryInfo) Equal(other *MonetaryInfo) bool { + if mi == nil && other == nil { + return true + } + if mi == nil || other == nil { + return false + } + return mi.UUID == other.UUID && + reflect.DeepEqual(mi.RateInterval, other.RateInterval) +} + +type UnitInfo struct { + UUID string + Value float64 + DestinationID string + Consumed float64 + TOR string + RateInterval *RateInterval +} + +func (ui *UnitInfo) Equal(other *UnitInfo) bool { + if ui == nil && other == nil { + return true + } + if ui == nil || other == nil { + return false + } + return ui.UUID == other.UUID && + ui.DestinationID == other.DestinationID && + ui.Consumed == other.Consumed && + ui.TOR == other.TOR && + reflect.DeepEqual(ui.RateInterval, other.RateInterval) +} + type TimeSpans []*TimeSpan // Will delete all timespans that are `under` the timespan at index @@ -219,7 +240,6 @@ func (incr *Increment) Clone() *Increment { nIncr := &Increment{ Duration: incr.Duration, Cost: incr.Cost, - UnitInfo: incr.UnitInfo, BalanceInfo: incr.BalanceInfo, } return nIncr @@ -228,8 +248,7 @@ func (incr *Increment) Clone() *Increment { func (incr *Increment) Equal(other *Increment) bool { return incr.Duration == other.Duration && incr.Cost == other.Cost && - ((incr.BalanceInfo == nil && other.BalanceInfo == nil) || incr.BalanceInfo.Equal(other.BalanceInfo)) && - ((incr.UnitInfo == nil && other.UnitInfo == nil) || incr.UnitInfo.Equal(other.UnitInfo)) + ((incr.BalanceInfo == nil && other.BalanceInfo == nil) || incr.BalanceInfo.Equal(other.BalanceInfo)) } func (incr *Increment) GetCompressFactor() int { @@ -263,8 +282,12 @@ func (incs *Increments) Compress() { // must be pointer receiver } else { cIncrs[len(cIncrs)-1].CompressFactor++ if cIncrs[len(cIncrs)-1].BalanceInfo != nil && incr.BalanceInfo != nil { - cIncrs[len(cIncrs)-1].BalanceInfo.MoneyBalanceValue = incr.BalanceInfo.MoneyBalanceValue - cIncrs[len(cIncrs)-1].BalanceInfo.UnitBalanceValue = incr.BalanceInfo.UnitBalanceValue + if cIncrs[len(cIncrs)-1].BalanceInfo.Monetary != nil && incr.BalanceInfo.Monetary != nil { + cIncrs[len(cIncrs)-1].BalanceInfo.Monetary.Value = incr.BalanceInfo.Monetary.Value + } + if cIncrs[len(cIncrs)-1].BalanceInfo.Unit != nil && incr.BalanceInfo.Unit != nil { + cIncrs[len(cIncrs)-1].BalanceInfo.Unit.Value = incr.BalanceInfo.Unit.Value + } } } } @@ -363,7 +386,7 @@ func (ts *TimeSpan) createIncrementsSlice() { inc := &Increment{ Duration: rateIncrement, Cost: incrementCost, - BalanceInfo: &BalanceInfo{}, + BalanceInfo: &DebitInfo{}, } ts.Increments = append(ts.Increments, inc) } diff --git a/engine/timespans_test.go b/engine/timespans_test.go index cd96c37fa..c89a078f0 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -1512,52 +1512,42 @@ func TestTSIncrementsCompressDecompress(t *testing.T) { &Increment{ Duration: time.Minute, Cost: 10.4, - BalanceInfo: &BalanceInfo{ - UnitBalanceUuid: "1", - MoneyBalanceUuid: "2", - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - AccountID: "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + BalanceInfo: &DebitInfo{ + Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2"}, + AccountID: "3"}, }, &Increment{ Duration: time.Minute, Cost: 10.4, - BalanceInfo: &BalanceInfo{ - UnitBalanceUuid: "1", - MoneyBalanceUuid: "2", - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - AccountID: "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + BalanceInfo: &DebitInfo{ + Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2"}, + AccountID: "3"}, }, &Increment{ Duration: time.Minute, Cost: 10.4, - BalanceInfo: &BalanceInfo{ - UnitBalanceUuid: "1", - MoneyBalanceUuid: "2", - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - AccountID: "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + BalanceInfo: &DebitInfo{ + Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2"}, + AccountID: "3"}, }, &Increment{ Duration: time.Minute, Cost: 10.4, - BalanceInfo: &BalanceInfo{ - UnitBalanceUuid: "1", - MoneyBalanceUuid: "2", - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, - AccountID: "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + BalanceInfo: &DebitInfo{ + Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2"}, + AccountID: "3"}, }, &Increment{ Duration: time.Minute, Cost: 10.4, - BalanceInfo: &BalanceInfo{ - UnitBalanceUuid: "1", - MoneyBalanceUuid: "2", - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - AccountID: "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + BalanceInfo: &DebitInfo{ + Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2"}, + AccountID: "3"}, }, }, }, @@ -1579,52 +1569,42 @@ func TestTSMultipleIncrementsCompressDecompress(t *testing.T) { &Increment{ Duration: time.Minute, Cost: 10.4, - BalanceInfo: &BalanceInfo{ - UnitBalanceUuid: "1", - MoneyBalanceUuid: "2", - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - AccountID: "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + BalanceInfo: &DebitInfo{ + Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2"}, + AccountID: "3"}, }, &Increment{ Duration: time.Minute, Cost: 10.4, - BalanceInfo: &BalanceInfo{ - UnitBalanceUuid: "1", - MoneyBalanceUuid: "2", - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - AccountID: "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + BalanceInfo: &DebitInfo{ + Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2"}, + AccountID: "3"}, }, &Increment{ Duration: time.Minute, Cost: 10.4, - BalanceInfo: &BalanceInfo{ - UnitBalanceUuid: "1", - MoneyBalanceUuid: "2", - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - AccountID: "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + BalanceInfo: &DebitInfo{ + Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2"}, + AccountID: "3"}, }, &Increment{ Duration: time.Minute, Cost: 10.4, - BalanceInfo: &BalanceInfo{ - UnitBalanceUuid: "1", - MoneyBalanceUuid: "2", - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}, - AccountID: "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + BalanceInfo: &DebitInfo{ + Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2"}, + AccountID: "3"}, }, &Increment{ Duration: time.Minute, Cost: 10.4, - BalanceInfo: &BalanceInfo{ - UnitBalanceUuid: "1", - MoneyBalanceUuid: "2", - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, - AccountID: "3"}, - UnitInfo: &UnitInfo{"1", 2.3, utils.VOICE}, + BalanceInfo: &DebitInfo{ + Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2"}, + AccountID: "3"}, }, }, }, From 31962c1a823fd4b9d2f8be5c61373b592bbd344d Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 10 Mar 2016 09:09:16 +0100 Subject: [PATCH 164/199] Diameter template - append_cca option --- config/config_defaults.go | 1 + config/config_json_test.go | 1 + config/daconfig.go | 4 ++++ config/libconfig_json.go | 1 + 4 files changed, 7 insertions(+) diff --git a/config/config_defaults.go b/config/config_defaults.go index af1fdd041..c1c413c14 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -285,6 +285,7 @@ const CGRATES_CFG_JSON = ` "request_filter": "Subscription-Id>Subscription-Id-Type(0)", // filter requests processed by this processor "flags": [], // flags to influence processing behavior "continue_on_success": false, // continue to the next template if executed + "append_cca": false, // when continuing will append cca fields to the previous ones "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true}, {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, diff --git a/config/config_json_test.go b/config/config_json_test.go index 462597e1f..654159925 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -443,6 +443,7 @@ func TestDiameterAgentJsonCfg(t *testing.T) { Request_filter: utils.StringPointer("Subscription-Id>Subscription-Id-Type(0)"), Flags: utils.StringSlicePointer([]string{}), Continue_on_success: utils.BoolPointer(false), + Append_cca: utils.BoolPointer(false), CCR_fields: &[]*CdrFieldJsonCfg{ &CdrFieldJsonCfg{Tag: utils.StringPointer("TOR"), Field_id: utils.StringPointer(utils.TOR), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer("^*voice"), Mandatory: utils.BoolPointer(true)}, diff --git a/config/daconfig.go b/config/daconfig.go index ae92905b5..6e465d6ae 100644 --- a/config/daconfig.go +++ b/config/daconfig.go @@ -110,6 +110,7 @@ type DARequestProcessor struct { RequestFilter utils.RSRFields Flags utils.StringMap // Various flags to influence behavior ContinueOnSuccess bool + AppendCCA bool CCRFields []*CfgCdrField CCAFields []*CfgCdrField } @@ -139,6 +140,9 @@ func (self *DARequestProcessor) loadFromJsonCfg(jsnCfg *DARequestProcessorJsnCfg if jsnCfg.Continue_on_success != nil { self.ContinueOnSuccess = *jsnCfg.Continue_on_success } + if jsnCfg.Append_cca != nil { + self.AppendCCA = *jsnCfg.Append_cca + } if jsnCfg.CCR_fields != nil { if self.CCRFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.CCR_fields); err != nil { return err diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 58f25c529..bc94bca2d 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -266,6 +266,7 @@ type DARequestProcessorJsnCfg struct { Request_filter *string Flags *[]string Continue_on_success *bool + Append_cca *bool CCR_fields *[]*CdrFieldJsonCfg CCA_fields *[]*CdrFieldJsonCfg } From fe822c2271d431a27634324e32fd54b5182de492 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 10 Mar 2016 20:29:05 +0100 Subject: [PATCH 165/199] DiameterAgent AppendCCA implementation --- agents/dmtagent.go | 33 ++++++++++++++++++++++++++------- agents/libdmt.go | 4 ++-- config/config_defaults.go | 2 +- config/config_json_test.go | 2 +- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 8d1751759..1e80363c2 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -69,7 +69,7 @@ func (self *DiameterAgent) handlers() diam.Handler { return dSM } -func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor) *CCA { +func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor, cca *CCA) *CCA { passesAllFilters := true for _, fldFilter := range reqProcessor.RequestFilter { if passes, _ := passesFieldFilter(ccr.diamMessage, fldFilter, nil); !passes { @@ -83,9 +83,12 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro utils.Logger.Info(fmt.Sprintf(" RequestProcessor: %s", reqProcessor.Id)) utils.Logger.Info(fmt.Sprintf(" CCR message: %s", ccr.diamMessage)) } - cca := NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) + if cca == nil || !reqProcessor.AppendCCA { + cca = NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) + } smgEv, err := ccr.AsSMGenericEvent(reqProcessor.CCRFields) if err != nil { + cca = NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA reply-code, error: %s", ccr.diamMessage, err)) @@ -100,13 +103,25 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro if reqProcessor.PublishEvent && self.pubsubs != nil { evt, err := smgEv.AsMapStringString() if err != nil { + cca = NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) + if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), + false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA reply-code, error: %s", ccr.diamMessage, err)) + return nil + } utils.Logger.Err(fmt.Sprintf(" Processing message: %+v failed converting SMGEvent to pubsub one, error: %s", ccr.diamMessage, err)) - return nil + return cca } var reply string if err := self.pubsubs.Call("PubSubV1.Publish", engine.CgrEvent(evt), &reply); err != nil { + cca = NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) + if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), + false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { + utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA reply-code, error: %s", ccr.diamMessage, err)) + return nil + } utils.Logger.Err(fmt.Sprintf(" Processing message: %+v failed publishing event, error: %s", ccr.diamMessage, err)) - return nil + return cca } } var maxUsage float64 @@ -168,6 +183,9 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro utils.Logger.Err(fmt.Sprintf(" CCA SetProcessorAVPs for message: %+v, error: %s", ccr.diamMessage, err)) return cca } + if reqProcessor.ContinueOnSuccess { + return nil + } return cca } @@ -177,10 +195,11 @@ func (self *DiameterAgent) handleCCR(c diam.Conn, m *diam.Message) { utils.Logger.Err(fmt.Sprintf(" Unmarshaling message: %s, error: %s", m, err)) return } - var cca *CCA // For now we simply overload in loop, maybe we will find some other use of this + var cca *CCA for _, reqProcessor := range self.cgrCfg.DiameterAgentCfg().RequestProcessors { - cca = self.processCCR(ccr, reqProcessor) - if cca != nil && !reqProcessor.ContinueOnSuccess { + ccaRcv := self.processCCR(ccr, reqProcessor, cca) + if ccaRcv != nil { // Received final answer, break processing + cca = ccaRcv break } } diff --git a/agents/libdmt.go b/agents/libdmt.go index 53c6bc084..0bd400963 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -59,6 +59,8 @@ const ( CGRResultCode = "CGRResultCode" ) +var ErrFilterNotPassing = errors.New("Filter not passing") + func loadDictionaries(dictsDir, componentId string) error { fi, err := os.Stat(dictsDir) if err != nil { @@ -365,8 +367,6 @@ func serializeAVPValueFromString(dictAVP *dict.AVP, valStr, timezone string) ([] } } -var ErrFilterNotPassing = errors.New("Filter not passing") - func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interface{}, processorVars map[string]string) (fmtValOut string, err error) { var outVal string passAtIndex := -1 diff --git a/config/config_defaults.go b/config/config_defaults.go index c1c413c14..f79ef663b 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -285,7 +285,7 @@ const CGRATES_CFG_JSON = ` "request_filter": "Subscription-Id>Subscription-Id-Type(0)", // filter requests processed by this processor "flags": [], // flags to influence processing behavior "continue_on_success": false, // continue to the next template if executed - "append_cca": false, // when continuing will append cca fields to the previous ones + "append_cca": true, // when continuing will append cca fields to the previous ones "ccr_fields":[ // import content_fields template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "^*voice", "mandatory": true}, {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "Session-Id", "mandatory": true}, diff --git a/config/config_json_test.go b/config/config_json_test.go index 654159925..9238aae39 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -443,7 +443,7 @@ func TestDiameterAgentJsonCfg(t *testing.T) { Request_filter: utils.StringPointer("Subscription-Id>Subscription-Id-Type(0)"), Flags: utils.StringSlicePointer([]string{}), Continue_on_success: utils.BoolPointer(false), - Append_cca: utils.BoolPointer(false), + Append_cca: utils.BoolPointer(true), CCR_fields: &[]*CdrFieldJsonCfg{ &CdrFieldJsonCfg{Tag: utils.StringPointer("TOR"), Field_id: utils.StringPointer(utils.TOR), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer("^*voice"), Mandatory: utils.BoolPointer(true)}, From 7794c454a71b59f9dd2f704d9b87140e4cac723e Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 10 Mar 2016 22:57:14 +0200 Subject: [PATCH 166/199] better increment descompress --- engine/timespans.go | 58 ++++++++++++++++++++++++++++++++-------- engine/timespans_test.go | 34 +++++++++++------------ 2 files changed, 64 insertions(+), 28 deletions(-) diff --git a/engine/timespans.go b/engine/timespans.go index dfd8fb8b4..57668a636 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -57,10 +57,23 @@ type DebitInfo struct { AccountID string // used when debited from shared balance } -func (bi *DebitInfo) Equal(other *DebitInfo) bool { - return bi.Unit.Equal(other.Unit) && - bi.Monetary.Equal(other.Monetary) && - bi.AccountID == other.AccountID +func (di *DebitInfo) Equal(other *DebitInfo) bool { + return di.Unit.Equal(other.Unit) && + di.Monetary.Equal(other.Monetary) && + di.AccountID == other.AccountID +} + +func (di *DebitInfo) Clone() *DebitInfo { + nDi := &DebitInfo{ + AccountID: di.AccountID, + } + if di.Unit != nil { + nDi.Unit = di.Unit.Clone() + } + if di.Monetary != nil { + nDi.Monetary = di.Monetary.Clone() + } + return nDi } type MonetaryInfo struct { @@ -69,6 +82,11 @@ type MonetaryInfo struct { RateInterval *RateInterval } +func (mi *MonetaryInfo) Clone() *MonetaryInfo { + newMi := *mi + return &newMi +} + func (mi *MonetaryInfo) Equal(other *MonetaryInfo) bool { if mi == nil && other == nil { return true @@ -89,6 +107,11 @@ type UnitInfo struct { RateInterval *RateInterval } +func (ui *UnitInfo) Clone() *UnitInfo { + newUi := *ui + return &newUi +} + func (ui *UnitInfo) Equal(other *UnitInfo) bool { if ui == nil && other == nil { return true @@ -237,12 +260,14 @@ func (tss *TimeSpans) Decompress() { // must be pointer receiver } func (incr *Increment) Clone() *Increment { - nIncr := &Increment{ - Duration: incr.Duration, - Cost: incr.Cost, - BalanceInfo: incr.BalanceInfo, + nInc := &Increment{ + Duration: incr.Duration, + Cost: incr.Cost, } - return nIncr + if incr.BalanceInfo != nil { + nInc.BalanceInfo = incr.BalanceInfo.Clone() + } + return nInc } func (incr *Increment) Equal(other *Increment) bool { @@ -297,8 +322,19 @@ func (incs *Increments) Compress() { // must be pointer receiver func (incs *Increments) Decompress() { // must be pointer receiver var cIncrs Increments for _, cIncr := range *incs { - for i := 0; i < cIncr.GetCompressFactor(); i++ { - cIncrs = append(cIncrs, cIncr.Clone()) + cf := cIncr.GetCompressFactor() + for i := 0; i < cf; i++ { + incr := cIncr.Clone() + // set right Values + if incr.BalanceInfo != nil { + if incr.BalanceInfo.Monetary != nil { + incr.BalanceInfo.Monetary.Value += (float64(cf-(i+1)) * incr.Cost) + } + if incr.BalanceInfo.Unit != nil { + incr.BalanceInfo.Unit.Value += (float64(cf-(i+1)) * incr.BalanceInfo.Unit.Consumed) + } + } + cIncrs = append(cIncrs, incr) } } *incs = cIncrs diff --git a/engine/timespans_test.go b/engine/timespans_test.go index c89a078f0..f8fb19dcd 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -1511,42 +1511,42 @@ func TestTSIncrementsCompressDecompress(t *testing.T) { Increments: Increments{ &Increment{ Duration: time.Minute, - Cost: 10.4, + Cost: 2, BalanceInfo: &DebitInfo{ - Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, - Monetary: &MonetaryInfo{UUID: "2"}, + Unit: &UnitInfo{UUID: "1", Value: 25, DestinationID: "1", Consumed: 1, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2", Value: 98}, AccountID: "3"}, }, &Increment{ Duration: time.Minute, - Cost: 10.4, + Cost: 2, BalanceInfo: &DebitInfo{ - Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, - Monetary: &MonetaryInfo{UUID: "2"}, + Unit: &UnitInfo{UUID: "1", Value: 24, DestinationID: "1", Consumed: 1, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2", Value: 96}, AccountID: "3"}, }, &Increment{ Duration: time.Minute, - Cost: 10.4, + Cost: 2, BalanceInfo: &DebitInfo{ - Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, - Monetary: &MonetaryInfo{UUID: "2"}, + Unit: &UnitInfo{UUID: "1", Value: 23, DestinationID: "1", Consumed: 1, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2", Value: 94}, AccountID: "3"}, }, &Increment{ Duration: time.Minute, - Cost: 10.4, + Cost: 2, BalanceInfo: &DebitInfo{ - Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}}, - Monetary: &MonetaryInfo{UUID: "2"}, + Unit: &UnitInfo{UUID: "1", Value: 22, DestinationID: "1", Consumed: 1, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1111 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2", Value: 92}, AccountID: "3"}, }, &Increment{ Duration: time.Minute, - Cost: 10.4, + Cost: 2, BalanceInfo: &DebitInfo{ - Unit: &UnitInfo{UUID: "1", DestinationID: "1", Consumed: 2.3, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, - Monetary: &MonetaryInfo{UUID: "2"}, + Unit: &UnitInfo{UUID: "1", Value: 21, DestinationID: "1", Consumed: 1, TOR: utils.VOICE, RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}}, + Monetary: &MonetaryInfo{UUID: "2", Value: 90}, AccountID: "3"}, }, }, @@ -1554,11 +1554,11 @@ func TestTSIncrementsCompressDecompress(t *testing.T) { } tss.Compress() if len(tss[0].Increments) != 3 { - t.Error("Error compressing timespan: ", tss[0].Increments) + t.Error("Error compressing timespan: ", utils.ToIJSON(tss[0])) } tss.Decompress() if len(tss[0].Increments) != 5 { - t.Error("Error decompressing timespans: ", tss[0].Increments) + t.Error("Error decompressing timespans: ", utils.ToIJSON(tss[0])) } } From 1916c7033043e87544b58382dec2503e0e601701 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 11 Mar 2016 11:02:11 +0200 Subject: [PATCH 167/199] moved structmatcher in cgrates and rsr operator --- engine/account.go | 2 +- engine/tp_reader.go | 2 +- glide.lock | 2 - glide.yaml | 1 - structmatcher/.travis.yml | 7 + structmatcher/README.md | 27 ++ structmatcher/structmatcher.go | 327 +++++++++++++++++++++ structmatcher/structmatcher_test.go | 429 ++++++++++++++++++++++++++++ 8 files changed, 792 insertions(+), 5 deletions(-) create mode 100644 structmatcher/.travis.yml create mode 100644 structmatcher/README.md create mode 100644 structmatcher/structmatcher.go create mode 100644 structmatcher/structmatcher_test.go diff --git a/engine/account.go b/engine/account.go index 9cec58246..136ffd296 100644 --- a/engine/account.go +++ b/engine/account.go @@ -25,8 +25,8 @@ import ( "time" "github.com/cgrates/cgrates/cache2go" + "github.com/cgrates/cgrates/structmatcher" "github.com/cgrates/cgrates/utils" - "github.com/cgrates/structmatcher" "strings" ) diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 82ac4d14f..bf662a335 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -8,8 +8,8 @@ import ( "strings" "time" + "github.com/cgrates/cgrates/structmatcher" "github.com/cgrates/cgrates/utils" - "github.com/cgrates/structmatcher" ) type TpReader struct { diff --git a/glide.lock b/glide.lock index 0c3abf76f..fa156bc51 100644 --- a/glide.lock +++ b/glide.lock @@ -13,8 +13,6 @@ imports: version: 3d6beed663452471dec3ca194137a30d379d9e8f - name: github.com/cgrates/rpcclient version: 79661b1e514823a9ac93b2b9e97e037ee190ba47 -- name: github.com/cgrates/structmatcher - version: 98feee0bab15ce165540fe5f0fa006db2e9f898c - name: github.com/DisposaBoy/JsonConfigReader version: 33a99fdf1d5ee1f79b5077e9c06f955ad356d5f4 - name: github.com/fiorix/go-diameter diff --git a/glide.yaml b/glide.yaml index 7b78d485f..fa8001b97 100644 --- a/glide.yaml +++ b/glide.yaml @@ -6,7 +6,6 @@ import: - package: github.com/cgrates/kamevapi - package: github.com/cgrates/osipsdagram - package: github.com/cgrates/rpcclient -- package: github.com/cgrates/structmatcher - package: github.com/fiorix/go-diameter subpackages: - diam diff --git a/structmatcher/.travis.yml b/structmatcher/.travis.yml new file mode 100644 index 000000000..d2e57b3a9 --- /dev/null +++ b/structmatcher/.travis.yml @@ -0,0 +1,7 @@ +language: go + +go: + - 1.5 + +branches: + only: master diff --git a/structmatcher/README.md b/structmatcher/README.md new file mode 100644 index 000000000..6220a301d --- /dev/null +++ b/structmatcher/README.md @@ -0,0 +1,27 @@ +# structmatcher +Query language for matching structures + +[![Build Status](https://secure.travis-ci.org/cgrates/structmatcher.png)](http://travis-ci.org/cgrates/structmatcher) + +The StructMatcher type will parse a condition string and match it against a given structure. + +The condition syntax is a json encoded string similar to mongodb query language. + +Examples: +- {"Weight":{"*gt":50}} checks for a balance with weight greater than 50 +- {"*or":[{"Value":{"*eq":0}},{"Value":{"*gte":100}}] checks for a balance with value equal to 0 or equal or highr than 100 + +Available operators: +- *eq: equal +- *gt: greater than +- *gte: greater or equal than +- *lt: less then +- *lte: less or equal than +- *exp: expired +- *or: logical or +- *and: logical and +- *has: receives a list of elements and checks that the elements are present in the specified field (StringMap type) + +Equal (*eq) and local and (*and) operators are implicit for shortcuts. In this way: + +{"*and":[{"Value":{"*eq":3}},{"Weight":{"*eq":10}}]} is equivalent to: {"Value":3, "Weight":10}. diff --git a/structmatcher/structmatcher.go b/structmatcher/structmatcher.go new file mode 100644 index 000000000..419e23095 --- /dev/null +++ b/structmatcher/structmatcher.go @@ -0,0 +1,327 @@ +package structmatcher + +/* +The condition syntax is a json encoded string similar to mongodb query language. + +Examples: +- {"Weight":{"*gt":50}} checks for a balance with weight greater than 50 +- {"*or":[{"Value":{"*eq":0}},{"Value":{"*gte":100}}] checks for a balance with value equal to 0 or equal or highr than 100 + +Available operators: +- *eq: equal +- *gt: greater than +- *gte: greater or equal than +- *lt: less then +- *lte: less or equal than +- *exp: expired +- *or: logical or +- *and: logical and +- *has: receives a list of elements and checks that the elements are present in the specified field (StringMap type) + +Equal (*eq) and local and (*and) operators are implicit for shortcuts. In this way: + +{"*and":[{"Value":{"*eq":3}},{"Weight":{"*eq":10}}]} is equivalent to: {"Value":3, "Weight":10}. +*/ + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "strings" + "time" + + "github.com/cgrates/cgrates/utils" +) + +const ( + CondEQ = "*eq" + CondGT = "*gt" + CondGTE = "*gte" + CondLT = "*lt" + CondLTE = "*lte" + CondEXP = "*exp" + CondOR = "*or" + CondAND = "*and" + CondHAS = "*has" + CondRSR = "*rsr" +) + +func NewErrInvalidArgument(arg interface{}) error { + return fmt.Errorf("INVALID_ARGUMENT: %v", arg) +} + +type StringMap map[string]bool + +var ( + ErrParserError = errors.New("PARSER_ERROR") + + operatorMap = map[string]func(field, value interface{}) (bool, error){ + CondEQ: func(field, value interface{}) (bool, error) { + return value == field, nil + }, + CondGT: func(field, value interface{}) (bool, error) { + var of, vf float64 + var ok bool + if of, ok = field.(float64); !ok { + return false, NewErrInvalidArgument(field) + } + if vf, ok = value.(float64); !ok { + return false, NewErrInvalidArgument(value) + } + return of > vf, nil + }, + CondGTE: func(field, value interface{}) (bool, error) { + var of, vf float64 + var ok bool + if of, ok = field.(float64); !ok { + return false, NewErrInvalidArgument(field) + } + if vf, ok = value.(float64); !ok { + return false, NewErrInvalidArgument(value) + } + return of >= vf, nil + }, + CondLT: func(field, value interface{}) (bool, error) { + var of, vf float64 + var ok bool + if of, ok = field.(float64); !ok { + return false, NewErrInvalidArgument(field) + } + if vf, ok = value.(float64); !ok { + return false, NewErrInvalidArgument(value) + } + return of < vf, nil + }, + CondLTE: func(field, value interface{}) (bool, error) { + var of, vf float64 + var ok bool + if of, ok = field.(float64); !ok { + return false, NewErrInvalidArgument(field) + } + if vf, ok = value.(float64); !ok { + return false, NewErrInvalidArgument(value) + } + return of <= vf, nil + }, + CondEXP: func(field, value interface{}) (bool, error) { + var expDate time.Time + var ok bool + if expDate, ok = field.(time.Time); !ok { + return false, NewErrInvalidArgument(field) + } + var expired bool + if expired, ok = value.(bool); !ok { + return false, NewErrInvalidArgument(value) + } + if expired { // check for expiration + return !expDate.IsZero() && expDate.Before(time.Now()), nil + } else { // check not expired + return expDate.IsZero() || expDate.After(time.Now()), nil + } + }, + CondHAS: func(field, value interface{}) (bool, error) { + var strMap StringMap + var ok bool + if strMap, ok = field.(StringMap); !ok { + return false, NewErrInvalidArgument(field) + } + var strSlice []interface{} + if strSlice, ok = value.([]interface{}); !ok { + return false, NewErrInvalidArgument(value) + } + for _, str := range strSlice { + if !strMap[str.(string)] { + return false, nil + } + } + return true, nil + }, + CondRSR: func(field, value interface{}) (bool, error) { + fltr, err := utils.NewRSRFilter(value.(string)) + if err != nil { + return false, err + } + return fltr.Pass(fmt.Sprintf("%v", field)), nil + }, + } +) + +type compositeElement interface { + element + addChild(element) error +} + +type element interface { + checkStruct(interface{}) (bool, error) +} + +type operatorSlice struct { + operator string + slice []element +} + +func (os *operatorSlice) addChild(ce element) error { + os.slice = append(os.slice, ce) + return nil +} +func (os *operatorSlice) checkStruct(o interface{}) (bool, error) { + switch os.operator { + case CondOR: + for _, cond := range os.slice { + check, err := cond.checkStruct(o) + if err != nil { + return false, err + } + if check { + return true, nil + } + } + case CondAND: + accumulator := true + for _, cond := range os.slice { + check, err := cond.checkStruct(o) + if err != nil { + return false, err + } + accumulator = accumulator && check + } + return accumulator, nil + } + return false, nil +} + +type keyStruct struct { + key string + elem element +} + +func (ks *keyStruct) addChild(ce element) error { + ks.elem = ce + return nil +} +func (ks *keyStruct) checkStruct(o interface{}) (bool, error) { + obj := reflect.ValueOf(o) + if obj.Kind() == reflect.Ptr { + obj = obj.Elem() + } + value := obj.FieldByName(ks.key) + if !value.IsValid() { + return false, NewErrInvalidArgument(ks.key) + } + return ks.elem.checkStruct(value.Interface()) +} + +type operatorValue struct { + operator string + value interface{} +} + +func (ov *operatorValue) checkStruct(o interface{}) (bool, error) { + if f, ok := operatorMap[ov.operator]; ok { + return f(o, ov.value) + } + return false, nil +} + +type keyValue struct { + key string + value interface{} +} + +func (kv *keyValue) checkStruct(o interface{}) (bool, error) { + obj := reflect.ValueOf(o) + if obj.Kind() == reflect.Ptr { + obj = obj.Elem() + } + value := obj.FieldByName(kv.key) + if !value.IsValid() { + return false, NewErrInvalidArgument(kv.key) + } + return value.Interface() == kv.value, nil +} + +type trueElement struct{} + +func (te *trueElement) checkStruct(o interface{}) (bool, error) { + return true, nil +} + +func isOperator(s string) bool { + return strings.HasPrefix(s, "*") +} + +func notEmpty(x interface{}) bool { + return !reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface()) +} + +type StructMatcher struct { + rootElement element +} + +func NewStructMatcher(q string) (sm *StructMatcher, err error) { + sm = &StructMatcher{} + err = sm.Parse(q) + return +} + +func (sm *StructMatcher) load(a map[string]interface{}, parentElement compositeElement) (element, error) { + for key, value := range a { + var currentElement element + switch t := value.(type) { + case []interface{}: + if key == CondHAS { + currentElement = &operatorValue{operator: key, value: t} + } else { + currentElement = &operatorSlice{operator: key} + for _, e := range t { + sm.load(e.(map[string]interface{}), currentElement.(compositeElement)) + } + } + case map[string]interface{}: + currentElement = &keyStruct{key: key} + //log.Print("map: ", t) + sm.load(t, currentElement.(compositeElement)) + case interface{}: + if isOperator(key) { + currentElement = &operatorValue{operator: key, value: t} + } else { + currentElement = &keyValue{key: key, value: t} + } + //log.Print("generic interface: ", t) + default: + return nil, ErrParserError + } + if parentElement != nil { // normal recurrent action + parentElement.addChild(currentElement) + } else { + if len(a) > 1 { // we have more keys in the map + parentElement = &operatorSlice{operator: CondAND} + parentElement.addChild(currentElement) + } else { // it was only one key value + return currentElement, nil + } + } + } + return parentElement, nil +} + +func (sm *StructMatcher) Parse(s string) (err error) { + a := make(map[string]interface{}) + if len(s) != 0 { + if err := json.Unmarshal([]byte([]byte(s)), &a); err != nil { + return err + } + sm.rootElement, err = sm.load(a, nil) + } else { + sm.rootElement = &trueElement{} + } + return +} + +func (sm *StructMatcher) Match(o interface{}) (bool, error) { + if sm.rootElement == nil { + return false, ErrParserError + } + return sm.rootElement.checkStruct(o) +} diff --git a/structmatcher/structmatcher_test.go b/structmatcher/structmatcher_test.go new file mode 100644 index 000000000..e221d1272 --- /dev/null +++ b/structmatcher/structmatcher_test.go @@ -0,0 +1,429 @@ +package structmatcher + +import ( + "encoding/json" + "strings" + "testing" + "time" +) + +func toJSON(v interface{}) string { + b, _ := json.MarshalIndent(v, "", " ") + return string(b) +} + +func TestStructMatcher(t *testing.T) { + cl := &StructMatcher{} + err := cl.Parse(`{"*or":[{"test":1},{"field":{"*gt":1}},{"best":"coco"}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + + err = cl.Parse(`{"*has":["NAT","RET","EUR"]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + err = cl.Parse(`{"Field":7, "Other":true}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + err = cl.Parse(``) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } +} + +func TestStructMatcherKeyValue(t *testing.T) { + o := struct { + Test string + Field float64 + Other bool + ExpDate time.Time + }{ + Test: "test", + Field: 6.0, + Other: true, + ExpDate: time.Date(2016, 1, 19, 20, 47, 0, 0, time.UTC), + } + cl := &StructMatcher{} + err := cl.Parse(`{"Test":"test"}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":6}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Other":true}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":6, "Other":true}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":7, "Other":true}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":6, "Other":false}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Other":true, "Field":{"*gt":5}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Other":true, "Field":{"*gt":7}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(``) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"ExpDate":{"*exp":true}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"ExpDate":{"*exp":false}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"*and":[{"Field":{"*gte":50}},{"Test":{"*eq":"test"}}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"WrongFieldName":{"*eq":1}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || err == nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } +} + +func TestStructMatcherKeyValuePointer(t *testing.T) { + o := &struct { + Test string + Field float64 + Other bool + }{ + Test: "test", + Field: 6.0, + Other: true, + } + cl := &StructMatcher{} + err := cl.Parse(`{"Test":"test"}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":6}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Other":true}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } +} + +func TestStructMatcherOperatorValue(t *testing.T) { + root := &operatorValue{operator: CondGT, value: 3.4} + if check, err := root.checkStruct(3.5); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(root)) + } + root = &operatorValue{operator: CondEQ, value: 3.4} + if check, err := root.checkStruct(3.5); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(root)) + } + root = &operatorValue{operator: CondEQ, value: 3.4} + if check, err := root.checkStruct(3.4); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(root)) + } + root = &operatorValue{operator: CondEQ, value: "zinc"} + if check, err := root.checkStruct("zinc"); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(root)) + } + root = &operatorValue{operator: CondHAS, value: []interface{}{"NAT", "RET", "EUR"}} + if check, err := root.checkStruct(StringMap{"WOR": true, "EUR": true, "NAT": true, "RET": true, "ROM": true}); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", !check, err, toJSON(root)) + } +} + +func TestStructMatcherKeyStruct(t *testing.T) { + o := struct { + Test string + Field float64 + Other bool + }{ + Test: "test", + Field: 6.0, + Other: true, + } + cl := &StructMatcher{} + err := cl.Parse(`{"Field":{"*gt": 5}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Test":{"*gt": 5}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || !strings.HasPrefix(err.Error(), "INVALID_ARGUMENT") { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*gte": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*lt": 7}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*lte": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*eq": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Test":{"*eq": "test"}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } +} + +func TestStructMatcherKeyStructPointer(t *testing.T) { + o := &struct { + Test string + Field float64 + Other bool + }{ + Test: "test", + Field: 6.0, + Other: true, + } + cl := &StructMatcher{} + err := cl.Parse(`{"Field":{"*gt": 5}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Test":{"*gt": 5}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || !strings.HasPrefix(err.Error(), "INVALID_ARGUMENT") { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*gte": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*lt": 7}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*lte": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Field":{"*eq": 6}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"Test":{"*eq": "test"}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } +} + +func TestStructMatcherOperatorSlice(t *testing.T) { + o := &struct { + Test string + Field float64 + Other bool + }{ + Test: "test", + Field: 6.0, + Other: true, + } + cl := &StructMatcher{} + err := cl.Parse(`{"*or":[{"Test":"test"},{"Field":{"*gt":5}},{"Other":true}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"*or":[{"Test":"test"},{"Field":{"*gt":7}},{"Other":false}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"*and":[{"Test":"test"},{"Field":{"*gt":5}},{"Other":true}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"*and":[{"Test":"test"},{"Field":{"*gt":7}},{"Other":false}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } +} + +func TestStructMatcherMixed(t *testing.T) { + o := &struct { + Test string + Field float64 + Categories StringMap + Other bool + }{ + Test: "test", + Field: 6.0, + Categories: StringMap{"call": true, "data": true, "voice": true}, + Other: true, + } + cl := &StructMatcher{} + err := cl.Parse(`{"*and":[{"Test":"test"},{"Field":{"*gt":5}},{"Other":true},{"Categories":{"*has":["data", "call"]}}]}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", check, err, toJSON(cl.rootElement)) + } +} + +func TestStructMatcherBalanceType(t *testing.T) { + type Balance struct { + Value float64 + } + + o := &struct { + BalanceType string + Balance + }{ + BalanceType: "*monetary", + Balance: Balance{Value: 10}, + } + cl := &StructMatcher{} + err := cl.Parse(`{"BalanceType":"*monetary","Value":10}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", !check, err, toJSON(cl.rootElement)) + } +} + +func TestStructMatcherRSR(t *testing.T) { + o := &struct { + BalanceType string + }{ + BalanceType: "*monetary", + } + cl := &StructMatcher{} + err := cl.Parse(`{"BalanceType":{"*rsr":"^*mon"}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); !check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", !check, err, toJSON(cl.rootElement)) + } + err = cl.Parse(`{"BalanceType":{"*rsr":"^*min"}}`) + if err != nil { + t.Errorf("Error loading structure: %+v (%v)", toJSON(cl.rootElement), err) + } + if check, err := cl.Match(o); check || err != nil { + t.Errorf("Error checking struct: %v %v (%v)", !check, err, toJSON(cl.rootElement)) + } +} From 79fa62a1c83e9d52d9b030088723f3de34f37688 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 11 Mar 2016 21:35:45 +0200 Subject: [PATCH 168/199] fixed missing cdrs_primary --- data/storage/postgres/create_cdrs_tables.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/storage/postgres/create_cdrs_tables.sql b/data/storage/postgres/create_cdrs_tables.sql index 3d386ec7e..d4425fb89 100644 --- a/data/storage/postgres/create_cdrs_tables.sql +++ b/data/storage/postgres/create_cdrs_tables.sql @@ -36,7 +36,7 @@ CREATE TABLE cdrs ( ); ; DROP INDEX IF EXISTS deleted_at_cp_idx; -CREATE INDEX deleted_at_cp_idx ON cdrs_primary (deleted_at); +CREATE INDEX deleted_at_cp_idx ON cdrs (deleted_at); DROP TABLE IF EXISTS sm_costs; From add958cc71b78ad877617da90254926825967eaf Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 11 Mar 2016 21:36:30 +0200 Subject: [PATCH 169/199] updated vagrant --- data/vagrant/Vagrantfile | 55 +++++++++++++++----------- data/vagrant/cgrates_devel.retry | 1 + data/vagrant/cgrates_devel.yml | 68 ++++++++++++++++---------------- 3 files changed, 66 insertions(+), 58 deletions(-) create mode 100644 data/vagrant/cgrates_devel.retry diff --git a/data/vagrant/Vagrantfile b/data/vagrant/Vagrantfile index 63bdddac2..680947427 100644 --- a/data/vagrant/Vagrantfile +++ b/data/vagrant/Vagrantfile @@ -1,37 +1,38 @@ # -*- mode: ruby -*- # vi: set ft=ruby : -# Vagrantfile API/syntax version. Don't touch unless you know what you're doing! -VAGRANTFILE_API_VERSION = "2" +# All Vagrant configuration is done below. The "2" in Vagrant.configure +# configures the configuration version (we support older styles for +# backwards compatibility). Please don't change it unless you know what +# you're doing. +Vagrant.configure(2) do |config| + # The most common configuration options are documented and commented below. + # For a complete reference, please see the online documentation at + # https://docs.vagrantup.com. -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - # All Vagrant configuration is done here. The most common configuration - # options are documented and commented below. For a complete reference, - # please see the online documentation at vagrantup.com. - - # Every Vagrant virtual environment requires a box to build off of. - config.vm.box = "debian74_64" - config.vm.box_url = "https://s3-eu-west-1.amazonaws.com/ffuenf-vagrant-boxes/debian/debian-7.4.0-amd64_virtualbox.box" - config.vm.network :public_network + # Every Vagrant development environment requires a box. You can search for + # boxes at https://atlas.hashicorp.com/search. + config.vm.box = "debian83_64" + config.vm.box_url = "https://s3.eu-central-1.amazonaws.com/ffuenf-vagrantboxes/debian/debian-8.3.0-amd64_virtualbox.box" + # Disable automatic box update checking. If you disable this, then + # boxes will only be checked for updates when the user runs + # `vagrant box outdated`. This is not recommended. + # config.vm.box_check_update = false # Create a forwarded port mapping which allows access to a specific port # within the machine from a port on the host machine. In the example below, # accessing "localhost:8080" will access port 80 on the guest machine. - # config.vm.network :forwarded_port, guest: 80, host: 8080 + # config.vm.network "forwarded_port", guest: 80, host: 8080 # Create a private network, which allows host-only access to the machine # using a specific IP. - # config.vm.network :private_network, ip: "192.168.33.10" + # config.vm.network "private_network", ip: "192.168.33.10" # Create a public network, which generally matched to bridged network. # Bridged networks make the machine appear as another physical device on # your network. - # config.vm.network :public_network - - # If true, then any SSH connections made will enable agent forwarding. - # Default value: false - # config.ssh.forward_agent = true + config.vm.network "public_network" # Share an additional folder to the guest VM. The first argument is # the path on the host to the actual folder. The second argument is @@ -43,16 +44,24 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # backing providers for Vagrant. These expose provider-specific options. # Example for VirtualBox: # - # config.vm.provider :virtualbox do |vb| - # # Don't boot with headless mode + # config.vm.provider "virtualbox" do |vb| + # # Display the VirtualBox GUI when booting the machine # vb.gui = true # - # # Use VBoxManage to customize the VM. For example to change memory: - # vb.customize ["modifyvm", :id, "--memory", "1024"] + # # Customize the amount of memory on the VM: + # vb.memory = "1024" # end # - # View the documentation for the provider you're using for more + # View the documentation for the provider you are using for more # information on available options. + + # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies + # such as FTP and Heroku are also available. See the documentation at + # https://docs.vagrantup.com/v2/push/atlas.html for more information. + # config.push.define "atlas" do |push| + # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" + # end + config.vm.provision "ansible" do |ansible| ansible.playbook = "cgrates_devel.yml" #ansible.verbose = "vvvv" diff --git a/data/vagrant/cgrates_devel.retry b/data/vagrant/cgrates_devel.retry new file mode 100644 index 000000000..4ad96d515 --- /dev/null +++ b/data/vagrant/cgrates_devel.retry @@ -0,0 +1 @@ +default diff --git a/data/vagrant/cgrates_devel.yml b/data/vagrant/cgrates_devel.yml index d567537b6..4002907c0 100644 --- a/data/vagrant/cgrates_devel.yml +++ b/data/vagrant/cgrates_devel.yml @@ -1,76 +1,74 @@ --- - hosts: all - user: vagrant - sudo: yes + become: true vars: root_db_password: CGRateS.org tasks: - - name: install dependency - apt: pkg={{ item }} state=latest - with_items: - - git - - bzr - - redis-server - - mysql-server - - python-pycurl - - python-mysqldb - - mercurial - - name: add cgrates user user: name=cgrates comment=CGRateS home=/var/run/cgrates shell=/bin/false system=yes - name: add freeswitch gpg key - command: gpg --keyserver pool.sks-keyservers.net --recv-key D76EDC7725E010CF - - - name: add freeswitch apt key - shell: gpg -a --export D76EDC7725E010CF | sudo apt-key add - + apt_key: url=https://files.freeswitch.org/repo/deb/debian/freeswitch_archive_g0.pub state=present - name: add freeswitch apt repo - apt_repository: repo='deb http://files.freeswitch.org/repo/deb/debian/ wheezy main' state=present - - - name: install freeswitch + apt_repository: repo='deb http://files.freeswitch.org/repo/deb/freeswitch-1.6/ jessie main' state=present + + - name: add mongo gpg key + apt_key: keyserver=keyserver.ubuntu.com id=EA312927 state=present + + - name: add mongo apt repo + apt_repository: repo='deb http://repo.mongodb.org/apt/debian wheezy/mongodb-org/3.2 main' state=present + + - name: install dependency apt: pkg={{ item }} update_cache=yes state=latest with_items: + - git + - bzr + - mercurial + - redis-server + - mysql-server + - postgresql-9.4 + - mongodb-org - freeswitch-meta-vanilla - freeswitch-mod-json-cdr + - libyuv-dev - name: update mysql root password for root account - mysql_user: name=root host=localhost password={{ root_db_password }} + mysql_user: name=cgrates host=localhost password={{ root_db_password }} - name: copy .my.cnf template: src=my.cnf dest=/root/.my.cnf mode=0600 -- hosts: all - user: vagrant +- hosts: all vars: root_db_password: CGRateS.org - go_version: 1.3 + go_version: 1.6 tasks: - name: get golang - get_url: url=https://storage.googleapis.com/golang/go{{ go_version }}.linux-amd64.tar.gz dest=~/go{{ go_version }}.linux-amd64.tar.gz - - - name: unpack go - command: chdir=~/ tar xvf go{{ go_version }}.linux-amd64.tar.gz - - - name: delete golang archive - file: path=~/go{{ go_version }}.linux-amd64.tar.gz state=absent + unarchive: src=https://storage.googleapis.com/golang/go{{ go_version }}.linux-amd64.tar.gz dest=~/go creates=~/go copy=no - name: add variables to variables /etc/profile copy: src=golang.sh dest=/etc/profile.d/golang.sh - sudo: yes + become: yes - name: get cgrates - shell: GOROOT=~/go GOPATH=~/code ~/go/bin/go get -u -v github.com/cgrates/cgrates + git: repo=https://github.com/cgrates/cgrates.git dest=/home/vagrant/code/src/github.com/cgrates/cgrates + + - name: get glide + shell: GOROOT=/home/vagrant/go GOPATH=/home/vagrant/code ~/go/bin/go get -u -v github.com/Masterminds/glide + + - name: install cgrates + shell: cd /home/vagrant/code/src/github.com/cgrates/cgrates; ~/code/bin/glide install - name: create cgr-engine link file: src=/home/vagrant/code/bin/cgr-engine dest=/usr/bin/cgr-engine state=link - sudo: yes + become: yes - name: create a link to data dir - sudo: yes + become: yes file: src=/home/vagrant/code/src/github.com/cgrates/cgrates/data dest=/usr/share/cgrates state=link - name: expand freeswitch json conf From b6c4b4842cd747e18a2687e8a85acdd6ad909f15 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Fri, 11 Mar 2016 21:49:57 +0200 Subject: [PATCH 170/199] started limiter component --- engine/models.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/engine/models.go b/engine/models.go index a37102094..85ce94362 100644 --- a/engine/models.go +++ b/engine/models.go @@ -343,6 +343,7 @@ type TpUser struct { AttributeName string `index:"3" re:""` AttributeValue string `index:"4" re:""` Weight float64 `index:"5" re:""` + CreatedAt time.Time } func (tu *TpUser) GetId() string { @@ -397,6 +398,20 @@ func (ta *TpAlias) GetId() string { return utils.ConcatenatedKey(ta.Direction, ta.Tenant, ta.Category, ta.Account, ta.Subject, ta.Context) } +type TpLimiter struct { + Id int64 + Tpid string + LimiterID string `index:"0" re:""` + ResourceID string `index:"1" re:""` + Filter string `index:"2" re:""` + TTL string `index:"3" re:""` + TimingIDs string `index:"4" re:""` + ActivationTime string `index:"5" re:""` + Limit float64 `index:"6" re:""` + ActionTriggers string `index:"7" re:""` + CreatedAt time.Time `index:"8" re:""` +} + type TBLCDRs struct { ID int64 Cgrid string From ad46d5517d0eda9648f3e20c805c450f7ccdbaf8 Mon Sep 17 00:00:00 2001 From: DanB Date: Sun, 13 Mar 2016 15:22:45 +0100 Subject: [PATCH 171/199] Diameter templates - append to last found AVP instead of first --- agents/libdmt.go | 8 ++++---- agents/libdmt_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/agents/libdmt.go b/agents/libdmt.go index 0bd400963..06970c739 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -431,7 +431,7 @@ func messageSetAVPsWithPath(m *diam.Message, path []interface{}, avpValStr strin } } if dictAVPs[len(path)-1].Data.Type == diam.GroupedAVPType { - return errors.New("Last AVP in path needs not to be GroupedAVP") + return errors.New("Last AVP in path cannot be GroupedAVP") } var msgAVP *diam.AVP // Keep a reference here towards last AVP lastAVPIdx := len(path) - 1 @@ -454,7 +454,7 @@ func messageSetAVPsWithPath(m *diam.Message, path []interface{}, avpValStr strin if i == lastAVPIdx-1 && !appnd { // last AVP needs to be appended in group avps, _ := m.FindAVPsWithPath(path[:lastAVPIdx], dict.UndefinedVendorID) if len(avps) != 0 { // Group AVP already in the message - prevGrpData := avps[0].Data.(*diam.GroupedAVP) + prevGrpData := avps[len(avps)-1].Data.(*diam.GroupedAVP) // Take the last avp found to append there prevGrpData.AVP = append(prevGrpData.AVP, msgAVP) m.Header.MessageLength += uint32(msgAVP.Len()) return nil @@ -465,8 +465,8 @@ func messageSetAVPsWithPath(m *diam.Message, path []interface{}, avpValStr strin if !appnd { // Not group AVP, replace the previous set one with this one avps, _ := m.FindAVPsWithPath(path, dict.UndefinedVendorID) if len(avps) != 0 { // Group AVP already in the message - m.Header.MessageLength -= uint32(avps[0].Len()) // decrease message length since we overwrite - *avps[0] = *msgAVP + m.Header.MessageLength -= uint32(avps[len(avps)-1].Len()) // decrease message length since we overwrite + *avps[len(avps)-1] = *msgAVP m.Header.MessageLength += uint32(msgAVP.Len()) return nil } diff --git a/agents/libdmt_test.go b/agents/libdmt_test.go index 0c99b4a93..ec6455013 100644 --- a/agents/libdmt_test.go +++ b/agents/libdmt_test.go @@ -21,6 +21,7 @@ package agents import ( "bytes" "encoding/binary" + "fmt" "reflect" "testing" "time" @@ -310,6 +311,44 @@ func TestMessageSetAVPsWithPath(t *testing.T) { } else if !reflect.DeepEqual(eMessage, m) { t.Errorf("Expecting: %+v, received: %+v", eMessage, m) } + // Multiple append + eMessage = diam.NewRequest(diam.CreditControl, 4, nil) + eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(431, avp.Mbit, 0, &diam.GroupedAVP{ // Granted-Service-Unit + AVP: []*diam.AVP{ + diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(3600)), + diam.NewAVP(421, avp.Mbit, 0, datatype.Unsigned64(153600)), // "CC-Total-Octets" + }, + }), + }, + }) + eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(431, avp.Mbit, 0, &diam.GroupedAVP{ // Granted-Service-Unit + AVP: []*diam.AVP{ + diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(2600)), + diam.NewAVP(421, avp.Mbit, 0, datatype.Unsigned64(143600)), // "CC-Total-Octets" + }, + }), + }, + }) + m = diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4, eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil) + if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Time"}, "3600", false, "UTC"); err != nil { + t.Error(err) + } + if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Total-Octets"}, "153600", false, "UTC"); err != nil { + t.Error(err) + } + if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Time"}, "2600", true, "UTC"); err != nil { + t.Error(err) + } + if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Total-Octets"}, "143600", false, "UTC"); err != nil { + t.Error(err) + } + if fmt.Sprintf("%q", eMessage) != fmt.Sprintf("%q", m) { // test with fmt since reflect.DeepEqual does not perform properly here + t.Errorf("Expecting: %+v, received: %+v", eMessage, m) + } } func TestCCASetProcessorAVPs(t *testing.T) { From e15cfac9e1afd58a03deffc1065ad40af12e3423 Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 14 Mar 2016 11:14:13 +0100 Subject: [PATCH 172/199] Debug on messageSetAvpWithPath --- agents/libdmt.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agents/libdmt.go b/agents/libdmt.go index 06970c739..47d047878 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -417,6 +417,7 @@ func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interfa // messageAddAVPsWithPath will dynamically add AVPs into the message // append: append to the message, on false overwrite if AVP is single or add to group if AVP is Grouped func messageSetAVPsWithPath(m *diam.Message, path []interface{}, avpValStr string, appnd bool, timezone string) error { + utils.Logger.Debug(fmt.Sprintf("### Start messageSetAVPsWithPath, message: %+v, path: %+v, avpValStr: %s, append: %v", m, path, avpValStr, appnd)) if len(path) == 0 { return errors.New("Empty path as AVP filter") } @@ -473,6 +474,7 @@ func messageSetAVPsWithPath(m *diam.Message, path []interface{}, avpValStr strin } m.AVP = append(m.AVP, msgAVP) m.Header.MessageLength += uint32(msgAVP.Len()) + utils.Logger.Debug(fmt.Sprintf("### Done messageSetAVPsWithPath, message: %+v", m)) return nil } From 6c4b0f0023e651b5526c1faaa51de53b276fdae6 Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 14 Mar 2016 11:51:04 +0100 Subject: [PATCH 173/199] More debug on diameter --- agents/dmtagent.go | 1 + agents/libdmt_test.go | 8 ++++++++ sessionmanager/smg_session.go | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 1e80363c2..26ab76fff 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -83,6 +83,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro utils.Logger.Info(fmt.Sprintf(" RequestProcessor: %s", reqProcessor.Id)) utils.Logger.Info(fmt.Sprintf(" CCR message: %s", ccr.diamMessage)) } + utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.processCCR: %+v, reqProcessor: %+v, cca: %+v", ccr, reqProcessor, cca)) if cca == nil || !reqProcessor.AppendCCA { cca = NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) } diff --git a/agents/libdmt_test.go b/agents/libdmt_test.go index ec6455013..7d6924c42 100644 --- a/agents/libdmt_test.go +++ b/agents/libdmt_test.go @@ -321,6 +321,7 @@ func TestMessageSetAVPsWithPath(t *testing.T) { diam.NewAVP(421, avp.Mbit, 0, datatype.Unsigned64(153600)), // "CC-Total-Octets" }, }), + diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(10)), }, }) eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{ @@ -331,6 +332,7 @@ func TestMessageSetAVPsWithPath(t *testing.T) { diam.NewAVP(421, avp.Mbit, 0, datatype.Unsigned64(143600)), // "CC-Total-Octets" }, }), + diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(11)), // Rating-Group }, }) m = diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4, eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil) @@ -340,12 +342,18 @@ func TestMessageSetAVPsWithPath(t *testing.T) { if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Total-Octets"}, "153600", false, "UTC"); err != nil { t.Error(err) } + if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Rating-Group"}, "10", false, "UTC"); err != nil { + t.Error(err) + } if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Time"}, "2600", true, "UTC"); err != nil { t.Error(err) } if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Granted-Service-Unit", "CC-Total-Octets"}, "143600", false, "UTC"); err != nil { t.Error(err) } + if err := messageSetAVPsWithPath(m, []interface{}{"Multiple-Services-Credit-Control", "Rating-Group"}, "11", false, "UTC"); err != nil { + t.Error(err) + } if fmt.Sprintf("%q", eMessage) != fmt.Sprintf("%q", m) { // test with fmt since reflect.DeepEqual does not perform properly here t.Errorf("Expecting: %+v, received: %+v", eMessage, m) } diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 63fbc8115..74d538e68 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -89,7 +89,7 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D } // apply the lastUsed correction dur += lastUsedCorrection - utils.Logger.Debug(fmt.Sprintf("### After lastUsedCorrection, durationIndex: %v, dur: %v, lastUsed: %v, lastUsedCorrection", self.cd.DurationIndex, dur, lastUsed, lastUsedCorrection)) + utils.Logger.Debug(fmt.Sprintf("### After lastUsedCorrection, durationIndex: %+v, dur: %+v, lastUsed: %+v, lastUsedCorrection: %+v", self.cd.DurationIndex, dur, lastUsed, lastUsedCorrection)) // apply correction from previous run dur -= self.extraDuration self.extraDuration = 0 From 04d786686923169edb247e9315f7adfab52ead44 Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 14 Mar 2016 12:10:40 +0100 Subject: [PATCH 174/199] More debug on diameter --- agents/dmtagent.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 26ab76fff..da7fe6ccf 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -83,7 +83,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro utils.Logger.Info(fmt.Sprintf(" RequestProcessor: %s", reqProcessor.Id)) utils.Logger.Info(fmt.Sprintf(" CCR message: %s", ccr.diamMessage)) } - utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.processCCR: %+v, reqProcessor: %+v, cca: %+v", ccr, reqProcessor, cca)) + utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.processCCR: %+v, reqProcessor: %+v, cca: %+v, cca message: %+v", ccr, reqProcessor, cca, cca.diamMessage)) if cca == nil || !reqProcessor.AppendCCA { cca = NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) } From 252b279225fb9439db8393f84b6f6d461d591e4e Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 14 Mar 2016 12:23:35 +0100 Subject: [PATCH 175/199] More debug on diameter --- agents/dmtagent.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index da7fe6ccf..ca5da2e73 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -83,7 +83,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro utils.Logger.Info(fmt.Sprintf(" RequestProcessor: %s", reqProcessor.Id)) utils.Logger.Info(fmt.Sprintf(" CCR message: %s", ccr.diamMessage)) } - utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.processCCR: %+v, reqProcessor: %+v, cca: %+v, cca message: %+v", ccr, reqProcessor, cca, cca.diamMessage)) + utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.processCCR: %+v, reqProcessor: %+v, cca: %+v", ccr, reqProcessor, cca)) if cca == nil || !reqProcessor.AppendCCA { cca = NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) } @@ -198,11 +198,14 @@ func (self *DiameterAgent) handleCCR(c diam.Conn, m *diam.Message) { } var cca *CCA for _, reqProcessor := range self.cgrCfg.DiameterAgentCfg().RequestProcessors { + utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.handleCCR before processCCR cca: %+v", cca)) ccaRcv := self.processCCR(ccr, reqProcessor, cca) if ccaRcv != nil { // Received final answer, break processing cca = ccaRcv + utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.handleCCR after processCCR returned cca: %+v", cca)) break } + utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.handleCCR after processCCR cca: %+v", cca)) } if cca == nil { utils.Logger.Err(fmt.Sprintf(" No request processor enabled for CCR: %s, ignoring request", ccr.diamMessage)) From 209d2610d310a35216b9be9fce1a68370837ed19 Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 14 Mar 2016 13:28:53 +0100 Subject: [PATCH 176/199] DiameterAgent.processCCR redesign to overload cca --- agents/dmtagent.go | 58 ++++++++++++++++++++-------------------------- agents/libdmt.go | 5 +++- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index ca5da2e73..f74c452ef 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -69,7 +69,7 @@ func (self *DiameterAgent) handlers() diam.Handler { return dSM } -func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor, cca *CCA) *CCA { +func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor, cca *CCA) (bool, error) { passesAllFilters := true for _, fldFilter := range reqProcessor.RequestFilter { if passes, _ := passesFieldFilter(ccr.diamMessage, fldFilter, nil); !passes { @@ -77,26 +77,25 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro } } if !passesAllFilters { // Not going with this processor further - return nil + return false, nil } if reqProcessor.DryRun { // DryRun should log the matching processor as well as the received CCR utils.Logger.Info(fmt.Sprintf(" RequestProcessor: %s", reqProcessor.Id)) utils.Logger.Info(fmt.Sprintf(" CCR message: %s", ccr.diamMessage)) } utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.processCCR: %+v, reqProcessor: %+v, cca: %+v", ccr, reqProcessor, cca)) - if cca == nil || !reqProcessor.AppendCCA { - cca = NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) + if !reqProcessor.AppendCCA { + *cca = *NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) } smgEv, err := ccr.AsSMGenericEvent(reqProcessor.CCRFields) if err != nil { - cca = NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) + *cca = *NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA reply-code, error: %s", ccr.diamMessage, err)) - return nil + return false, err } utils.Logger.Err(fmt.Sprintf(" Processing message: %+v AsSMGenericEvent, error: %s", ccr.diamMessage, err)) - return cca + return false, ErrDiameterRatingFailed } if len(reqProcessor.Flags) != 0 { smgEv[utils.CGRFlags] = reqProcessor.Flags.String() // Populate CGRFlags automatically @@ -104,25 +103,23 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro if reqProcessor.PublishEvent && self.pubsubs != nil { evt, err := smgEv.AsMapStringString() if err != nil { - cca = NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) + *cca = *NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA reply-code, error: %s", ccr.diamMessage, err)) - return nil + return false, err } utils.Logger.Err(fmt.Sprintf(" Processing message: %+v failed converting SMGEvent to pubsub one, error: %s", ccr.diamMessage, err)) - return cca + return false, ErrDiameterRatingFailed } var reply string if err := self.pubsubs.Call("PubSubV1.Publish", engine.CgrEvent(evt), &reply); err != nil { - cca = NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) + *cca = *NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA reply-code, error: %s", ccr.diamMessage, err)) - return nil + return false, err } utils.Logger.Err(fmt.Sprintf(" Processing message: %+v failed publishing event, error: %s", ccr.diamMessage, err)) - return cca + return false, ErrDiameterRatingFailed } } var maxUsage float64 @@ -172,22 +169,17 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro } if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, processorVars[CGRResultCode], false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) - return nil + return false, err } if err := cca.SetProcessorAVPs(reqProcessor, processorVars); err != nil { if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, strconv.Itoa(DiameterRatingFailed), false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { - utils.Logger.Err(fmt.Sprintf(" Processing message: %+v set CCA Reply-Code, error: %s", ccr.diamMessage, err)) - return nil + return false, err } utils.Logger.Err(fmt.Sprintf(" CCA SetProcessorAVPs for message: %+v, error: %s", ccr.diamMessage, err)) - return cca + return false, ErrDiameterRatingFailed } - if reqProcessor.ContinueOnSuccess { - return nil - } - return cca + return true, nil } func (self *DiameterAgent) handleCCR(c diam.Conn, m *diam.Message) { @@ -196,18 +188,18 @@ func (self *DiameterAgent) handleCCR(c diam.Conn, m *diam.Message) { utils.Logger.Err(fmt.Sprintf(" Unmarshaling message: %s, error: %s", m, err)) return } - var cca *CCA + cca := NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) + var processed bool for _, reqProcessor := range self.cgrCfg.DiameterAgentCfg().RequestProcessors { - utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.handleCCR before processCCR cca: %+v", cca)) - ccaRcv := self.processCCR(ccr, reqProcessor, cca) - if ccaRcv != nil { // Received final answer, break processing - cca = ccaRcv - utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.handleCCR after processCCR returned cca: %+v", cca)) + processed, err = self.processCCR(ccr, reqProcessor, cca) + if err != nil || (processed && !reqProcessor.ContinueOnSuccess) { break } - utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.handleCCR after processCCR cca: %+v", cca)) } - if cca == nil { + if err != nil && err != ErrDiameterRatingFailed { + utils.Logger.Err(fmt.Sprintf(" CCA SetProcessorAVPs for message: %+v, error: %s", ccr.diamMessage, err)) + return + } else if !processed { utils.Logger.Err(fmt.Sprintf(" No request processor enabled for CCR: %s, ignoring request", ccr.diamMessage)) return } diff --git a/agents/libdmt.go b/agents/libdmt.go index 47d047878..df7cf4d7c 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -59,7 +59,10 @@ const ( CGRResultCode = "CGRResultCode" ) -var ErrFilterNotPassing = errors.New("Filter not passing") +var ( + ErrFilterNotPassing = errors.New("Filter not passing") + ErrDiameterRatingFailed = errors.New("Diameter rating failed") +) func loadDictionaries(dictsDir, componentId string) error { fi, err := os.Stat(dictsDir) From cbf68b98ca1d00b421406ac966c4042476b3e39e Mon Sep 17 00:00:00 2001 From: DanB Date: Mon, 14 Mar 2016 14:53:15 +0100 Subject: [PATCH 177/199] Removing debug from diameter code --- agents/dmtagent.go | 1 - agents/libdmt.go | 2 -- 2 files changed, 3 deletions(-) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index f74c452ef..026fc0306 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -83,7 +83,6 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro utils.Logger.Info(fmt.Sprintf(" RequestProcessor: %s", reqProcessor.Id)) utils.Logger.Info(fmt.Sprintf(" CCR message: %s", ccr.diamMessage)) } - utils.Logger.Debug(fmt.Sprintf("### DiameterAgent.processCCR: %+v, reqProcessor: %+v, cca: %+v", ccr, reqProcessor, cca)) if !reqProcessor.AppendCCA { *cca = *NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) } diff --git a/agents/libdmt.go b/agents/libdmt.go index df7cf4d7c..bb60b4f75 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -420,7 +420,6 @@ func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interfa // messageAddAVPsWithPath will dynamically add AVPs into the message // append: append to the message, on false overwrite if AVP is single or add to group if AVP is Grouped func messageSetAVPsWithPath(m *diam.Message, path []interface{}, avpValStr string, appnd bool, timezone string) error { - utils.Logger.Debug(fmt.Sprintf("### Start messageSetAVPsWithPath, message: %+v, path: %+v, avpValStr: %s, append: %v", m, path, avpValStr, appnd)) if len(path) == 0 { return errors.New("Empty path as AVP filter") } @@ -477,7 +476,6 @@ func messageSetAVPsWithPath(m *diam.Message, path []interface{}, avpValStr strin } m.AVP = append(m.AVP, msgAVP) m.Header.MessageLength += uint32(msgAVP.Len()) - utils.Logger.Debug(fmt.Sprintf("### Done messageSetAVPsWithPath, message: %+v", m)) return nil } From 2d47552c520bf5d78a7d18ced9e48364bfed0094 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Mon, 14 Mar 2016 23:52:45 +0200 Subject: [PATCH 178/199] fix wrong NOT_FOUND error when removing account --- apier/v1/accounts.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index f20088a3e..6d006da25 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -157,6 +157,9 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error // clean previous action plans actionPlansMap, err := self.RatingDb.GetAllActionPlans() if err != nil { + if err == utils.ErrNotFound { // if no action plans just continue + return 0, nil + } return 0, err } var dirtyAps []string From 6553cff655b571c052a9aa68045996d413d89f19 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 15 Mar 2016 13:20:58 +0100 Subject: [PATCH 179/199] Better debug for SMG LastUsed --- sessionmanager/smg_session.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 74d538e68..7aba20e6d 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -77,7 +77,7 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) { // Attempts to debit a duration, returns maximum duration which can be debitted or error func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.Duration, error) { - utils.Logger.Debug(fmt.Sprintf("### SMGSession.debit durationIndex: %v, dur: %v, lastUsed: %v", self.cd.DurationIndex, dur, lastUsed)) + utils.Logger.Debug(fmt.Sprintf("### SMGSession.debit durationIndex: %v, dur: %v, lastUsed: %v, lastUsage: %v", self.cd.DurationIndex, dur, lastUsed, self.lastUsage)) self.lastUsage = dur // Reset the lastUsage for later reference lastUsedCorrection := time.Duration(0) // Used if lastUsed influences the debit if self.cd.DurationIndex != 0 && lastUsed != 0 { @@ -86,10 +86,11 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D } else { // We have debitted less than we have consumed, add the difference to duration debitted lastUsedCorrection = lastUsed - self.lastUsage } + utils.Logger.Debug(fmt.Sprintf("### Calculated lastUsedCorrection: %+v for lastUsage: %v, lastUsed: %v", lastUsedCorrection, self.lastUsage, lastUsed)) } // apply the lastUsed correction dur += lastUsedCorrection - utils.Logger.Debug(fmt.Sprintf("### After lastUsedCorrection, durationIndex: %+v, dur: %+v, lastUsed: %+v, lastUsedCorrection: %+v", self.cd.DurationIndex, dur, lastUsed, lastUsedCorrection)) + utils.Logger.Debug(fmt.Sprintf("### After lastUsedCorrection dur: %+v, lastUsedCorrection: %+v", dur, lastUsedCorrection)) // apply correction from previous run dur -= self.extraDuration self.extraDuration = 0 From 73e977059ce3dc09882730c0e51d361544c854fa Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 16 Mar 2016 09:50:07 +0100 Subject: [PATCH 180/199] Fix for LastUsage in SMGeneric --- sessionmanager/smg_it_test.go | 119 ++++++++++++++++++++++++++++++++++ sessionmanager/smg_session.go | 12 ++-- 2 files changed, 123 insertions(+), 8 deletions(-) diff --git a/sessionmanager/smg_it_test.go b/sessionmanager/smg_it_test.go index 80df57462..2bf5939e2 100644 --- a/sessionmanager/smg_it_test.go +++ b/sessionmanager/smg_it_test.go @@ -305,3 +305,122 @@ func TestSMGMixedRefund(t *testing.T) { t.Logf("After monetary: %f", acnt.BalanceMap[utils.MONETARY].GetTotalValue()) t.Logf("After voice: %f", acnt.BalanceMap[utils.VOICE].GetTotalValue()) } + +func TestSMGLastUsed(t *testing.T) { + if !*testIntegration { + return + } + smgEv := SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + 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: "2m", + } + var maxUsage float64 + if err := smgRPC.Call("SMGenericV1.SessionStart", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 120 { + t.Error("Bad max usage: ", maxUsage) + } + var acnt *engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + eAcntVal := 7.39002 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + 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: "2m", + utils.LastUsed: "1m30s", + } + if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 120 { + t.Error("Bad max usage: ", maxUsage) + } + attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + eAcntVal = 7.09005 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + 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: "2m", + utils.LastUsed: "2m30s", + } + if err := smgRPC.Call("SMGenericV1.SessionUpdate", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 120 { + t.Error("Bad max usage: ", maxUsage) + } + attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + eAcntVal = 6.5901 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "12349", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: "1006", + utils.CATEGORY: "call", + 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: "1m", + } + var rpl string + if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK { + t.Error(err) + } + eAcntVal = 7.09 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } +} diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 7aba20e6d..35447f2b0 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -77,7 +77,6 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) { // Attempts to debit a duration, returns maximum duration which can be debitted or error func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.Duration, error) { - utils.Logger.Debug(fmt.Sprintf("### SMGSession.debit durationIndex: %v, dur: %v, lastUsed: %v, lastUsage: %v", self.cd.DurationIndex, dur, lastUsed, self.lastUsage)) self.lastUsage = dur // Reset the lastUsage for later reference lastUsedCorrection := time.Duration(0) // Used if lastUsed influences the debit if self.cd.DurationIndex != 0 && lastUsed != 0 { @@ -86,11 +85,9 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D } else { // We have debitted less than we have consumed, add the difference to duration debitted lastUsedCorrection = lastUsed - self.lastUsage } - utils.Logger.Debug(fmt.Sprintf("### Calculated lastUsedCorrection: %+v for lastUsage: %v, lastUsed: %v", lastUsedCorrection, self.lastUsage, lastUsed)) } // apply the lastUsed correction dur += lastUsedCorrection - utils.Logger.Debug(fmt.Sprintf("### After lastUsedCorrection dur: %+v, lastUsedCorrection: %+v", dur, lastUsedCorrection)) // apply correction from previous run dur -= self.extraDuration self.extraDuration = 0 @@ -110,17 +107,16 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D if ccDuration != dur { self.extraDuration = ccDuration - dur } - utils.Logger.Debug(fmt.Sprintf("### ANSWER durationIndex: %v, dur: %v, extraDuration: %v, lastUsed: %v, lastUsedCorrection: %v", - self.cd.DurationIndex, dur, self.extraDuration, lastUsed, lastUsedCorrection)) - dur -= lastUsedCorrection // Revert the correction to return the real duration reserved - utils.Logger.Debug(fmt.Sprintf("### ANSWER after lastUsedCorrection, durationIndex: %v, dur: %v, lastUsedCorrection: %v", - self.cd.DurationIndex, dur, lastUsedCorrection)) self.cd.DurationIndex -= dur self.cd.DurationIndex += ccDuration self.cd.MaxCostSoFar += cc.Cost self.cd.LoopIndex += 1 self.sessionCds = append(self.sessionCds, self.cd.Clone()) self.callCosts = append(self.callCosts, cc) + ccDuration -= lastUsedCorrection + if ccDuration < 0 { // if correction has pushed ccDuration bellow 0 + ccDuration = 0 + } return ccDuration, nil } From bdabfd8633c590feda5a22bcebfa3e637b870e81 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 15 Mar 2016 17:03:33 +0200 Subject: [PATCH 181/199] Add error cecking in set account v2 too --- apier/v2/accounts.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index 5da921b9f..c3c495edd 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -115,6 +115,9 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { _, err := engine.Guardian.Guard(func() (interface{}, error) { actionPlansMap, err := self.RatingDb.GetAllActionPlans() if err != nil { + if err == utils.ErrNotFound { // if no action plans just continue + return 0, nil + } return 0, err } if attr.ActionPlansOverwrite { From 760f674132df3664b11f68688b8d729e4dd86310 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Tue, 15 Mar 2016 17:04:19 +0200 Subject: [PATCH 182/199] Add ID to increment balance information --- engine/account.go | 1 + engine/balances.go | 6 ++++++ engine/timespans.go | 2 ++ 3 files changed, 9 insertions(+) diff --git a/engine/account.go b/engine/account.go index 136ffd296..e231875a2 100644 --- a/engine/account.go +++ b/engine/account.go @@ -488,6 +488,7 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo defaultBalance.SubstractValue(cost) increment.BalanceInfo.Monetary = &MonetaryInfo{ UUID: defaultBalance.Uuid, + ID: defaultBalance.ID, Value: defaultBalance.Value, } increment.BalanceInfo.AccountID = ub.ID diff --git a/engine/balances.go b/engine/balances.go index e51b7794f..e44fa6033 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -357,6 +357,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala b.SubstractValue(amount) inc.BalanceInfo.Unit = &UnitInfo{ UUID: b.Uuid, + ID: b.ID, Value: b.Value, DestinationID: cc.Destination, Consumed: amount, @@ -434,6 +435,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala cost, inc.Cost = 0.0, 0.0 inc.BalanceInfo.Monetary = &MonetaryInfo{ UUID: b.Uuid, + ID: b.ID, Value: b.Value, RateInterval: ts.RateInterval, } @@ -456,6 +458,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala b.SubstractValue(amount) inc.BalanceInfo.Unit = &UnitInfo{ UUID: b.Uuid, + ID: b.ID, Value: b.Value, DestinationID: cc.Destination, Consumed: amount, @@ -466,6 +469,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala if cost != 0 { inc.BalanceInfo.Monetary = &MonetaryInfo{ UUID: moneyBal.Uuid, + ID: moneyBal.ID, Value: moneyBal.Value, } moneyBal.SubstractValue(cost) @@ -554,6 +558,7 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala amount, inc.Cost = 0.0, 0.0 inc.BalanceInfo.Monetary = &MonetaryInfo{ UUID: b.Uuid, + ID: b.ID, Value: b.Value, } inc.BalanceInfo.AccountID = ub.ID @@ -575,6 +580,7 @@ func (b *Balance) debitMoney(cd *CallDescriptor, ub *Account, moneyBalances Bala cd.MaxCostSoFar += amount inc.BalanceInfo.Monetary = &MonetaryInfo{ UUID: b.Uuid, + ID: b.ID, Value: b.Value, } inc.BalanceInfo.AccountID = ub.ID diff --git a/engine/timespans.go b/engine/timespans.go index 57668a636..8a716f307 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -78,6 +78,7 @@ func (di *DebitInfo) Clone() *DebitInfo { type MonetaryInfo struct { UUID string + ID string Value float64 RateInterval *RateInterval } @@ -100,6 +101,7 @@ func (mi *MonetaryInfo) Equal(other *MonetaryInfo) bool { type UnitInfo struct { UUID string + ID string Value float64 DestinationID string Consumed float64 From 758f7b4a4ab7723df3377bcb697b5e756a3fcad5 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 16 Mar 2016 11:42:27 +0100 Subject: [PATCH 183/199] apply aliases/users before derived charging --- engine/cdrs.go | 14 ++++++++++++++ engine/responder.go | 2 +- sessionmanager/smgeneric.go | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/engine/cdrs.go b/engine/cdrs.go index 1911477b6..b28fd9d2c 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -262,6 +262,20 @@ func (self *CdrServer) deriveCdrs(cdr *CDR) ([]*CDR, error) { if cdr.RunID != utils.MetaRaw { // Only derive *raw CDRs return cdrRuns, nil } + if err := LoadAlias(&AttrMatchingAlias{ + Destination: cdr.Destination, + Direction: cdr.Direction, + Tenant: cdr.Tenant, + Category: cdr.Category, + Account: cdr.Account, + Subject: cdr.Subject, + Context: utils.ALIAS_CONTEXT_RATING, + }, cdr, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { + return nil, err + } + if err := LoadUserProfile(cdr, utils.EXTRA_FIELDS); err != nil { + return nil, err + } attrsDC := &utils.AttrDerivedChargers{Tenant: cdr.Tenant, Category: cdr.Category, Direction: cdr.Direction, Account: cdr.Account, Subject: cdr.Subject, Destination: cdr.Destination} var dcs utils.DerivedChargers diff --git a/engine/responder.go b/engine/responder.go index 48f337ab2..64587311d 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -419,7 +419,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { return err } attrsDC := &utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT), - Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)} + Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT), Destination: ev.GetDestination(utils.META_DEFAULT)} dcs := &utils.DerivedChargers{} if err := rs.GetDerivedChargers(attrsDC, dcs); err != nil { rs.getCache().Cache(utils.GET_SESS_RUNS_CACHE_PREFIX+ev.CGRID, &cache2go.CacheItem{ diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index 8da95aa45..cf7d2a88e 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -95,6 +95,7 @@ func (self *SMGeneric) sessionStart(evStart SMGenericEvent, connId string) error s := &SMGSession{eventStart: evStart, connId: connId, runId: sessionRun.DerivedCharger.RunID, timezone: self.timezone, rater: self.rater, cdrsrv: self.cdrsrv, cd: sessionRun.CallDescriptor} self.indexSession(sessionId, s) + utils.Logger.Info(fmt.Sprintf(" Starting session: %s, runId: %s", sessionId, s.runId)) if self.cgrCfg.SmGenericConfig.DebitInterval != 0 { s.stopDebit = stopDebitChan go s.debitLoop(self.cgrCfg.SmGenericConfig.DebitInterval) @@ -116,6 +117,7 @@ func (self *SMGeneric) sessionEnd(sessionId string, usage time.Duration) error { return nil, nil // Did not find the session so no need to close it anymore } for idx, s := range ss { + utils.Logger.Info(fmt.Sprintf(" Ending session: %s, runId: %s", sessionId, s.runId)) if idx == 0 && s.stopDebit != nil { close(s.stopDebit) // Stop automatic debits } From 260fde99324a92644c7a76de21872176c7c6089d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 16 Mar 2016 12:03:37 +0100 Subject: [PATCH 184/199] more logging --- engine/responder.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/responder.go b/engine/responder.go index 64587311d..dccb11cfe 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -401,6 +401,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { if ev.Subject == "" { ev.Subject = ev.Account } + utils.Logger.Info(fmt.Sprintf("DC before: %+v", ev)) // replace aliases if err := LoadAlias( &AttrMatchingAlias{ @@ -418,8 +419,10 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { if err := LoadUserProfile(ev, utils.EXTRA_FIELDS); err != nil { return err } + utils.Logger.Info(fmt.Sprintf("DC after: %+v", ev)) attrsDC := &utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT), Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT), Destination: ev.GetDestination(utils.META_DEFAULT)} + utils.Logger.Info(fmt.Sprintf("Derived chargers for: %+v", attrsDC)) dcs := &utils.DerivedChargers{} if err := rs.GetDerivedChargers(attrsDC, dcs); err != nil { rs.getCache().Cache(utils.GET_SESS_RUNS_CACHE_PREFIX+ev.CGRID, &cache2go.CacheItem{ From 9d3b5cc9a2407791864df7227bf1febf0b64c9d1 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 16 Mar 2016 12:06:47 +0100 Subject: [PATCH 185/199] even more logging --- engine/responder.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/responder.go b/engine/responder.go index dccb11cfe..3a36e6150 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -21,6 +21,7 @@ package engine import ( "errors" "fmt" + "log" "net/rpc" "reflect" "runtime" @@ -431,6 +432,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { return err } dcs, _ = dcs.AppendDefaultRun() + log.Print("DCS: ", len(dcs)) sesRuns := make([]*SessionRun, 0) for _, dc := range dcs.Chargers { if !utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID}, ev.GetReqType(dc.RequestTypeField)) { @@ -472,6 +474,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { } sesRuns = append(sesRuns, &SessionRun{DerivedCharger: dc, CallDescriptor: cd}) } + log.Print("RUNS: ", len(sesRuns)) *sRuns = sesRuns rs.getCache().Cache(utils.GET_SESS_RUNS_CACHE_PREFIX+ev.CGRID, &cache2go.CacheItem{ Value: sRuns, From f19e25afae00b2a9a44d026a8fed307da45835c4 Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 16 Mar 2016 12:08:54 +0100 Subject: [PATCH 186/199] SMG.ChargeEvent fix for duration returned --- agents/dmtagent_it_test.go | 48 ++++++++++++++++++++++++------------- sessionmanager/smgeneric.go | 3 ++- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index b9aed75ac..b8fc04911 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -227,8 +227,8 @@ func TestDmtAgentSendCCRInit(t *testing.T) { if !*testIntegration { return } - cdr := &engine.CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, - OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", + cdr := &engine.CDR{CGRID: utils.Sha1("testccr1", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, + OriginID: "testccr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", Supplier: "SUPPL1", SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(0) * time.Second, PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, @@ -271,8 +271,8 @@ func TestDmtAgentSendCCRUpdate(t *testing.T) { if !*testIntegration { return } - cdr := &engine.CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, - OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", + cdr := &engine.CDR{CGRID: utils.Sha1("testccr1", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, + OriginID: "testccr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", Supplier: "SUPPL1", SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(300) * time.Second, PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, @@ -310,8 +310,8 @@ func TestDmtAgentSendCCRUpdate2(t *testing.T) { if !*testIntegration { return } - cdr := &engine.CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, - OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", + cdr := &engine.CDR{CGRID: utils.Sha1("testccr1", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, + OriginID: "testccr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", Supplier: "SUPPL1", SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(600) * time.Second, PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, @@ -348,8 +348,8 @@ func TestDmtAgentSendCCRTerminate(t *testing.T) { if !*testIntegration { return } - cdr := &engine.CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, - OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", + cdr := &engine.CDR{CGRID: utils.Sha1("testccr1", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, + OriginID: "testccr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", Supplier: "SUPPL1", SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(610) * time.Second, PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, @@ -390,7 +390,7 @@ func TestDmtAgentSendCCRSMS(t *testing.T) { return } ccr := diam.NewRequest(diam.CreditControl, 4, nil) - ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("cgrates;1451911932;00082")) + ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testccr2")) ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) @@ -425,13 +425,13 @@ func TestDmtAgentSendCCRSMS(t *testing.T) { AVP: []*diam.AVP{ diam.NewAVP(886, avp.Mbit, 10415, &diam.GroupedAVP{ // Originator-Address AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("49602200011")), // Address-Data + diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type + diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1003")), // Address-Data }}), diam.NewAVP(1201, avp.Mbit, 10415, &diam.GroupedAVP{ // Recipient-Address AVP: []*diam.AVP{ - diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type - diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("49780029555")), // Address-Data + diam.NewAVP(899, avp.Mbit, 10415, datatype.Enumerated(1)), // Address-Type + diam.NewAVP(897, avp.Mbit, 10415, datatype.UTF8String("1002")), // Address-Data }}), }, }), @@ -462,6 +462,20 @@ func TestDmtAgentSendCCRSMS(t *testing.T) { t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) } */ + var cdrs []*engine.ExternalCDR + req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, ToRs: []string{utils.SMS}} + if err := apierRpc.Call("ApierV2.GetCdrs", req, &cdrs); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(cdrs) != 1 { + t.Error("Unexpected number of CDRs returned: ", len(cdrs)) + } else { + if cdrs[0].Usage != "1" { + t.Errorf("Unexpected CDR Usage received, cdr: %+v ", cdrs[0]) + } + if cdrs[0].Cost != 0.6 { + t.Errorf("Unexpected CDR Cost received, cdr: %+v ", cdrs[0]) + } + } } func TestDmtAgentSendCCRSMSWrongAccount(t *testing.T) { @@ -469,7 +483,7 @@ func TestDmtAgentSendCCRSMSWrongAccount(t *testing.T) { return } ccr := diam.NewRequest(diam.CreditControl, 4, nil) - ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("cgrates;1451911932;00083")) + ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testccr3")) ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) @@ -537,8 +551,8 @@ func TestDmtAgentSendCCRInitWrongAccount(t *testing.T) { if !*testIntegration { return } - cdr := &engine.CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, - OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", + cdr := &engine.CDR{CGRID: utils.Sha1("testccr4", time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, + OriginID: "testccr4", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "non_existent", Subject: "non_existent", Destination: "1004", Supplier: "SUPPL1", SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(0) * time.Second, PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, @@ -571,7 +585,7 @@ func TestDmtAgentSendCCRSimpaEvent(t *testing.T) { return } ccr := diam.NewRequest(diam.CreditControl, 4, nil) - ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("cgrates;1451911932;00084")) + ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("testccr5")) ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) ccr.NewAVP(avp.OriginRealm, avp.Mbit, 0, datatype.DiameterIdentity("cgrates.org")) ccr.NewAVP(avp.DestinationRealm, avp.Mbit, 0, datatype.DiameterIdentity("routing1.huawei.com")) diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index 8da95aa45..421d88217 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -214,6 +214,7 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu } else if len(sessionRuns) == 0 { return nilDuration, nil } + var maxDurInit bool // Avoid differences between default 0 and received 0 for _, sR := range sessionRuns { cc := new(engine.CallCost) if err = self.rater.MaxDebit(sR.CallDescriptor, cc); err != nil { @@ -224,7 +225,7 @@ func (self *SMGeneric) ChargeEvent(gev SMGenericEvent, clnt *rpc2.Client) (maxDu if ccDur := cc.GetDuration(); ccDur == 0 { err = utils.ErrInsufficientCredit break - } else if ccDur < maxDur { + } else if !maxDurInit || ccDur < maxDur { maxDur = ccDur } } From f15aa4a5840eaeb2f9e04a13a2396cf048cb7702 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 16 Mar 2016 12:10:59 +0100 Subject: [PATCH 187/199] compilation fix --- engine/responder.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/engine/responder.go b/engine/responder.go index 3a36e6150..18350f8b5 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -21,7 +21,6 @@ package engine import ( "errors" "fmt" - "log" "net/rpc" "reflect" "runtime" @@ -432,7 +431,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { return err } dcs, _ = dcs.AppendDefaultRun() - log.Print("DCS: ", len(dcs)) + utils.Logger.Info(fmt.Sprintf("DCS: %v", len(dcs.Chargers))) sesRuns := make([]*SessionRun, 0) for _, dc := range dcs.Chargers { if !utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID}, ev.GetReqType(dc.RequestTypeField)) { @@ -474,7 +473,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { } sesRuns = append(sesRuns, &SessionRun{DerivedCharger: dc, CallDescriptor: cd}) } - log.Print("RUNS: ", len(sesRuns)) + utils.Logger.Info(fmt.Sprintf("RUNS: %v", len(sesRuns))) *sRuns = sesRuns rs.getCache().Cache(utils.GET_SESS_RUNS_CACHE_PREFIX+ev.CGRID, &cache2go.CacheItem{ Value: sRuns, From a8629fda07bb1d0b0c06a7321609ac71ae887d11 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Wed, 16 Mar 2016 12:31:19 +0100 Subject: [PATCH 188/199] sitch aliases - users order --- engine/cdrs.go | 22 +++++---- engine/responder.go | 91 ++++++++++++++++++++----------------- sessionmanager/smgeneric.go | 4 +- 3 files changed, 64 insertions(+), 53 deletions(-) diff --git a/engine/cdrs.go b/engine/cdrs.go index b28fd9d2c..27cd49dc6 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -133,6 +133,10 @@ func (self *CdrServer) RateCDRs(cdrFltr *utils.CDRsFilter, sendToStats bool) err return err } for _, cdr := range cdrs { + // replace user profile fields + if err := LoadUserProfile(cdr, utils.EXTRA_FIELDS); err != nil { + return err + } // replace aliases for cases they were loaded after CDR received if err := LoadAlias(&AttrMatchingAlias{ Destination: cdr.Destination, @@ -145,10 +149,6 @@ func (self *CdrServer) RateCDRs(cdrFltr *utils.CDRsFilter, sendToStats bool) err }, cdr, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } - // replace user profile fields - if err := LoadUserProfile(cdr, utils.EXTRA_FIELDS); err != nil { - return err - } if err := self.rateStoreStatsReplicate(cdr, sendToStats); err != nil { utils.Logger.Err(fmt.Sprintf(" Processing CDR %+v, got error: %s", cdr, err.Error())) } @@ -208,6 +208,9 @@ func (self *CdrServer) rateStoreStatsReplicate(cdr *CDR, sendToStats bool) error if cdr.RunID == utils.MetaRaw { // Overwrite *raw with *default for rating cdr.RunID = utils.META_DEFAULT } + if err := LoadUserProfile(cdr, utils.EXTRA_FIELDS); err != nil { + return err + } if err := LoadAlias(&AttrMatchingAlias{ Destination: cdr.Destination, Direction: cdr.Direction, @@ -219,9 +222,7 @@ func (self *CdrServer) rateStoreStatsReplicate(cdr *CDR, sendToStats bool) error }, cdr, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } - if err := LoadUserProfile(cdr, utils.EXTRA_FIELDS); err != nil { - return err - } + // Rate CDR if self.rater != nil && !cdr.Rated { if err := self.rateCDR(cdr); err != nil { @@ -262,6 +263,9 @@ func (self *CdrServer) deriveCdrs(cdr *CDR) ([]*CDR, error) { if cdr.RunID != utils.MetaRaw { // Only derive *raw CDRs return cdrRuns, nil } + if err := LoadUserProfile(cdr, utils.EXTRA_FIELDS); err != nil { + return nil, err + } if err := LoadAlias(&AttrMatchingAlias{ Destination: cdr.Destination, Direction: cdr.Direction, @@ -273,9 +277,7 @@ func (self *CdrServer) deriveCdrs(cdr *CDR) ([]*CDR, error) { }, cdr, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return nil, err } - if err := LoadUserProfile(cdr, utils.EXTRA_FIELDS); err != nil { - return nil, err - } + attrsDC := &utils.AttrDerivedChargers{Tenant: cdr.Tenant, Category: cdr.Category, Direction: cdr.Direction, Account: cdr.Account, Subject: cdr.Subject, Destination: cdr.Destination} var dcs utils.DerivedChargers diff --git a/engine/responder.go b/engine/responder.go index 18350f8b5..b6d12c545 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -81,6 +81,10 @@ func (rs *Responder) GetCost(arg *CallDescriptor, reply *CallCost) (err error) { if arg.Subject == "" { arg.Subject = arg.Account } + // replace user profile fields + if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { + return err + } // replace aliases if err := LoadAlias( &AttrMatchingAlias{ @@ -94,10 +98,7 @@ func (rs *Responder) GetCost(arg *CallDescriptor, reply *CallCost) (err error) { }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } - // replace user profile fields - if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { - return err - } + if rs.Bal != nil { r, e := rs.getCallCost(arg, "Responder.GetCost") *reply, err = *r, e @@ -119,6 +120,10 @@ func (rs *Responder) Debit(arg *CallDescriptor, reply *CallCost) (err error) { if arg.Subject == "" { arg.Subject = arg.Account } + // replace user profile fields + if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { + return err + } // replace aliases if err := LoadAlias( &AttrMatchingAlias{ @@ -132,10 +137,7 @@ func (rs *Responder) Debit(arg *CallDescriptor, reply *CallCost) (err error) { }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } - // replace user profile fields - if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { - return err - } + if rs.Bal != nil { r, e := rs.getCallCost(arg, "Responder.Debit") *reply, err = *r, e @@ -158,6 +160,10 @@ func (rs *Responder) MaxDebit(arg *CallDescriptor, reply *CallCost) (err error) if arg.Subject == "" { arg.Subject = arg.Account } + // replace user profile fields + if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { + return err + } // replace aliases if err := LoadAlias( &AttrMatchingAlias{ @@ -171,10 +177,7 @@ func (rs *Responder) MaxDebit(arg *CallDescriptor, reply *CallCost) (err error) }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } - // replace user profile fields - if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { - return err - } + if rs.Bal != nil { r, e := rs.getCallCost(arg, "Responder.MaxDebit") *reply, err = *r, e @@ -204,6 +207,10 @@ func (rs *Responder) RefundIncrements(arg *CallDescriptor, reply *float64) (err if arg.Subject == "" { arg.Subject = arg.Account } + // replace user profile fields + if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { + return err + } // replace aliases if err := LoadAlias( &AttrMatchingAlias{ @@ -217,10 +224,7 @@ func (rs *Responder) RefundIncrements(arg *CallDescriptor, reply *float64) (err }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } - // replace user profile fields - if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { - return err - } + if rs.Bal != nil { *reply, err = rs.callMethod(arg, "Responder.RefundIncrements") } else { @@ -241,6 +245,10 @@ func (rs *Responder) RefundRounding(arg *CallDescriptor, reply *float64) (err er if arg.Subject == "" { arg.Subject = arg.Account } + // replace user profile fields + if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { + return err + } // replace aliases if err := LoadAlias( &AttrMatchingAlias{ @@ -254,10 +262,7 @@ func (rs *Responder) RefundRounding(arg *CallDescriptor, reply *float64) (err er }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } - // replace user profile fields - if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { - return err - } + if rs.Bal != nil { *reply, err = rs.callMethod(arg, "Responder.RefundRounding") } else { @@ -274,6 +279,10 @@ func (rs *Responder) GetMaxSessionTime(arg *CallDescriptor, reply *float64) (err if arg.Subject == "" { arg.Subject = arg.Account } + // replace user profile fields + if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { + return err + } // replace aliases if err := LoadAlias( &AttrMatchingAlias{ @@ -287,10 +296,7 @@ func (rs *Responder) GetMaxSessionTime(arg *CallDescriptor, reply *float64) (err }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } - // replace user profile fields - if err := LoadUserProfile(arg, utils.EXTRA_FIELDS); err != nil { - return err - } + if rs.Bal != nil { *reply, err = rs.callMethod(arg, "Responder.GetMaxSessionTime") } else { @@ -308,6 +314,10 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *CDR, reply *float64) error { if ev.Subject == "" { ev.Subject = ev.Account } + // replace user profile fields + if err := LoadUserProfile(ev, utils.EXTRA_FIELDS); err != nil { + return err + } // replace aliases if err := LoadAlias( &AttrMatchingAlias{ @@ -321,10 +331,7 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *CDR, reply *float64) error { }, ev, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } - // replace user profile fields - if err := LoadUserProfile(ev, utils.EXTRA_FIELDS); err != nil { - return err - } + maxCallDuration := -1.0 attrsDC := &utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT), Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)} @@ -401,7 +408,11 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { if ev.Subject == "" { ev.Subject = ev.Account } - utils.Logger.Info(fmt.Sprintf("DC before: %+v", ev)) + //utils.Logger.Info(fmt.Sprintf("DC before: %+v", ev)) + // replace user profile fields + if err := LoadUserProfile(ev, utils.EXTRA_FIELDS); err != nil { + return err + } // replace aliases if err := LoadAlias( &AttrMatchingAlias{ @@ -415,14 +426,11 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { }, ev, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } - // replace user profile fields - if err := LoadUserProfile(ev, utils.EXTRA_FIELDS); err != nil { - return err - } - utils.Logger.Info(fmt.Sprintf("DC after: %+v", ev)) + + //utils.Logger.Info(fmt.Sprintf("DC after: %+v", ev)) attrsDC := &utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT), Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT), Destination: ev.GetDestination(utils.META_DEFAULT)} - utils.Logger.Info(fmt.Sprintf("Derived chargers for: %+v", attrsDC)) + //utils.Logger.Info(fmt.Sprintf("Derived chargers for: %+v", attrsDC)) dcs := &utils.DerivedChargers{} if err := rs.GetDerivedChargers(attrsDC, dcs); err != nil { rs.getCache().Cache(utils.GET_SESS_RUNS_CACHE_PREFIX+ev.CGRID, &cache2go.CacheItem{ @@ -431,7 +439,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { return err } dcs, _ = dcs.AppendDefaultRun() - utils.Logger.Info(fmt.Sprintf("DCS: %v", len(dcs.Chargers))) + //utils.Logger.Info(fmt.Sprintf("DCS: %v", len(dcs.Chargers))) sesRuns := make([]*SessionRun, 0) for _, dc := range dcs.Chargers { if !utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID}, ev.GetReqType(dc.RequestTypeField)) { @@ -473,7 +481,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { } sesRuns = append(sesRuns, &SessionRun{DerivedCharger: dc, CallDescriptor: cd}) } - utils.Logger.Info(fmt.Sprintf("RUNS: %v", len(sesRuns))) + //utils.Logger.Info(fmt.Sprintf("RUNS: %v", len(sesRuns))) *sRuns = sesRuns rs.getCache().Cache(utils.GET_SESS_RUNS_CACHE_PREFIX+ev.CGRID, &cache2go.CacheItem{ Value: sRuns, @@ -533,6 +541,10 @@ func (rs *Responder) GetLCR(attrs *AttrGetLcr, reply *LCRCost) error { if attrs.CallDescriptor.Subject == "" { attrs.CallDescriptor.Subject = attrs.CallDescriptor.Account } + // replace user profile fields + if err := LoadUserProfile(attrs.CallDescriptor, utils.EXTRA_FIELDS); err != nil { + return err + } // replace aliases cd := attrs.CallDescriptor if err := LoadAlias( @@ -547,10 +559,7 @@ func (rs *Responder) GetLCR(attrs *AttrGetLcr, reply *LCRCost) error { }, cd, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } - // replace user profile fields - if err := LoadUserProfile(attrs.CallDescriptor, utils.EXTRA_FIELDS); err != nil { - return err - } + lcrCost, err := attrs.CallDescriptor.GetLCR(rs.Stats, attrs.Paginator) if err != nil { return err diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index c547c8305..1da986e1b 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -95,7 +95,7 @@ func (self *SMGeneric) sessionStart(evStart SMGenericEvent, connId string) error s := &SMGSession{eventStart: evStart, connId: connId, runId: sessionRun.DerivedCharger.RunID, timezone: self.timezone, rater: self.rater, cdrsrv: self.cdrsrv, cd: sessionRun.CallDescriptor} self.indexSession(sessionId, s) - utils.Logger.Info(fmt.Sprintf(" Starting session: %s, runId: %s", sessionId, s.runId)) + //utils.Logger.Info(fmt.Sprintf(" Starting session: %s, runId: %s", sessionId, s.runId)) if self.cgrCfg.SmGenericConfig.DebitInterval != 0 { s.stopDebit = stopDebitChan go s.debitLoop(self.cgrCfg.SmGenericConfig.DebitInterval) @@ -117,7 +117,7 @@ func (self *SMGeneric) sessionEnd(sessionId string, usage time.Duration) error { return nil, nil // Did not find the session so no need to close it anymore } for idx, s := range ss { - utils.Logger.Info(fmt.Sprintf(" Ending session: %s, runId: %s", sessionId, s.runId)) + //utils.Logger.Info(fmt.Sprintf(" Ending session: %s, runId: %s", sessionId, s.runId)) if idx == 0 && s.stopDebit != nil { close(s.stopDebit) // Stop automatic debits } From 09dd7a61198cc915aaa1a6a44e690d6235efc4ff Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 16 Mar 2016 17:38:34 +0100 Subject: [PATCH 189/199] Remove some info in the smg_tests --- sessionmanager/smg_it_test.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sessionmanager/smg_it_test.go b/sessionmanager/smg_it_test.go index 2bf5939e2..7beabc218 100644 --- a/sessionmanager/smg_it_test.go +++ b/sessionmanager/smg_it_test.go @@ -351,8 +351,6 @@ func TestSMGLastUsed(t *testing.T) { utils.CATEGORY: "call", 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: "2m", utils.LastUsed: "1m30s", } @@ -380,8 +378,6 @@ func TestSMGLastUsed(t *testing.T) { utils.CATEGORY: "call", 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: "2m", utils.LastUsed: "2m30s", } @@ -409,8 +405,6 @@ func TestSMGLastUsed(t *testing.T) { utils.CATEGORY: "call", 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: "1m", } var rpl string From 377b0d57492c320012b91dd792cc632b8d64a4cf Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 16 Mar 2016 18:34:50 +0100 Subject: [PATCH 190/199] SMG.debit - store lazy the lastUsed --- sessionmanager/smg_session.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 35447f2b0..47f7e3132 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -77,7 +77,6 @@ func (self *SMGSession) debitLoop(debitInterval time.Duration) { // Attempts to debit a duration, returns maximum duration which can be debitted or error func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.Duration, error) { - self.lastUsage = dur // Reset the lastUsage for later reference lastUsedCorrection := time.Duration(0) // Used if lastUsed influences the debit if self.cd.DurationIndex != 0 && lastUsed != 0 { if self.lastUsage > lastUsed { // We have debitted more than we have used, refund in the duration debitted @@ -98,6 +97,7 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D self.cd.DurationIndex += dur cc := &engine.CallCost{} if err := self.rater.MaxDebit(self.cd, cc); err != nil { + self.lastUsage = 0 return 0, err } // cd corrections @@ -117,6 +117,7 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D if ccDuration < 0 { // if correction has pushed ccDuration bellow 0 ccDuration = 0 } + self.lastUsage = ccDuration // Reset the lastUsage for later reference return ccDuration, nil } From c39d114bd774b9dfe7fce12b448d9f7fe051fd52 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 17 Mar 2016 09:32:16 +0100 Subject: [PATCH 191/199] small cosmetic changes --- engine/calldesc.go | 1 + engine/ratingprofile.go | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/engine/calldesc.go b/engine/calldesc.go index 13a49820e..5f378dfdf 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -554,6 +554,7 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura if account.AllowNegative { return -1, nil } + // for zero duration index if origCD.DurationIndex < origCD.TimeEnd.Sub(origCD.TimeStart) { origCD.DurationIndex = origCD.TimeEnd.Sub(origCD.TimeStart) } diff --git a/engine/ratingprofile.go b/engine/ratingprofile.go index 9ef1cfd27..f0ebf0ac3 100644 --- a/engine/ratingprofile.go +++ b/engine/ratingprofile.go @@ -145,9 +145,9 @@ func (ris RatingInfos) String() string { return string(b) } -func (rp *RatingProfile) GetRatingPlansForPrefix(cd *CallDescriptor) (err error) { +func (rpf *RatingProfile) GetRatingPlansForPrefix(cd *CallDescriptor) (err error) { var ris RatingInfos - for index, rpa := range rp.RatingPlanActivations.GetActiveForCall(cd) { + for index, rpa := range rpf.RatingPlanActivations.GetActiveForCall(cd) { rpl, err := ratingStorage.GetRatingPlan(rpa.RatingPlanId, false) if err != nil || rpl == nil { utils.Logger.Err(fmt.Sprintf("Error checking destination: %v", err)) @@ -202,7 +202,7 @@ func (rp *RatingProfile) GetRatingPlansForPrefix(cd *CallDescriptor) (err error) } if len(prefix) > 0 { ris = append(ris, &RatingInfo{ - MatchedSubject: rp.Id, + MatchedSubject: rpf.Id, RatingPlanId: rpl.Id, MatchedPrefix: prefix, MatchedDestId: destinationId, From 148e283ad64cf94f0aea275e03197aa2aeb04d5d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 17 Mar 2016 10:17:42 +0100 Subject: [PATCH 192/199] return ACCOUNT_DISABLED error from debit functions --- engine/calldesc.go | 9 +++++++++ utils/consts.go | 1 + 2 files changed, 10 insertions(+) diff --git a/engine/calldesc.go b/engine/calldesc.go index 5f378dfdf..d5f3c155f 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -639,6 +639,9 @@ func (cd *CallDescriptor) GetMaxSessionDuration() (duration time.Duration, err e utils.Logger.Err(fmt.Sprintf("Account: %s, not found", cd.GetAccountKey())) return 0, utils.ErrAccountNotFound } else { + if account.Disabled { + return 0, utils.ErrAccountDisabled + } if memberIds, err := account.GetUniqueSharedGroupMembers(cd); err == nil { if _, err := Guardian.Guard(func() (interface{}, error) { duration, err = cd.getMaxSessionDuration(account) @@ -706,6 +709,9 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) { utils.Logger.Err(fmt.Sprintf("Account: %s, not found", cd.GetAccountKey())) return nil, utils.ErrAccountNotFound } else { + if account.Disabled { + return nil, utils.ErrAccountDisabled + } if memberIds, sgerr := account.GetUniqueSharedGroupMembers(cd); sgerr == nil { _, err = Guardian.Guard(func() (interface{}, error) { cc, err = cd.debit(account, cd.DryRun, true) @@ -728,6 +734,9 @@ func (cd *CallDescriptor) MaxDebit() (cc *CallCost, err error) { utils.Logger.Err(fmt.Sprintf("Account: %s, not found", cd.GetAccountKey())) return nil, utils.ErrAccountNotFound } else { + if account.Disabled { + return nil, utils.ErrAccountDisabled + } //log.Printf("ACC: %+v", account) if memberIDs, err := account.GetUniqueSharedGroupMembers(cd); err == nil { _, err = Guardian.Guard(func() (interface{}, error) { diff --git a/utils/consts.go b/utils/consts.go index e197a5083..72c15b145 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -28,6 +28,7 @@ var ( ErrUnauthorizedDestination = errors.New("UNAUTHORIZED_DESTINATION") ErrRatingPlanNotFound = errors.New("RATING_PLAN_NOT_FOUND") ErrAccountNotFound = errors.New("ACCOUNT_NOT_FOUND") + ErrAccountDisabled = errors.New("ACCOUNT_DISABLED") ErrUserNotFound = errors.New("USER_NOT_FOUND") ErrInsufficientCredit = errors.New("INSUFFICENT_CREDIT") ) From 853a303ef98d8b8d306362ea5a912a44aefa3c3c Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 17 Mar 2016 12:40:09 +0100 Subject: [PATCH 193/199] DiameterAgent pretify ACCOUNT_DISABLED error --- agents/dmtagent.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 026fc0306..2b5466bac 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -159,6 +159,8 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro processorVars[CGRError] = utils.ErrUserNotFound.Error() case strings.HasSuffix(err.Error(), utils.ErrInsufficientCredit.Error()): processorVars[CGRError] = utils.ErrInsufficientCredit.Error() + case strings.HasSuffix(err.Error(), utils.ErrAccountDisabled.Error()): + processorVars[CGRError] = utils.ErrAccountDisabled.Error() default: // Unknown error processorVars[CGRError] = err.Error() processorVars[CGRResultCode] = strconv.Itoa(DiameterRatingFailed) From 3ec470060a1fce1c0f6713b96dc29e87620c1309 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 17 Mar 2016 12:47:17 +0100 Subject: [PATCH 194/199] refund can go past the last update --- sessionmanager/smg_it_test.go | 14 +++++++++----- sessionmanager/smg_session.go | 12 ++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/sessionmanager/smg_it_test.go b/sessionmanager/smg_it_test.go index 7beabc218..e508d1567 100644 --- a/sessionmanager/smg_it_test.go +++ b/sessionmanager/smg_it_test.go @@ -310,6 +310,14 @@ func TestSMGLastUsed(t *testing.T) { if !*testIntegration { return } + var acnt *engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + eAcntVal := 8.790000 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.MONETARY].GetTotalValue()) + } smgEv := SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.VOICE, @@ -332,9 +340,7 @@ func TestSMGLastUsed(t *testing.T) { if maxUsage != 120 { t.Error("Bad max usage: ", maxUsage) } - var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 7.39002 + eAcntVal = 7.39002 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { @@ -360,7 +366,6 @@ func TestSMGLastUsed(t *testing.T) { if maxUsage != 120 { t.Error("Bad max usage: ", maxUsage) } - attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} eAcntVal = 7.09005 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) @@ -387,7 +392,6 @@ func TestSMGLastUsed(t *testing.T) { if maxUsage != 120 { t.Error("Bad max usage: ", maxUsage) } - attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} eAcntVal = 6.5901 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 47f7e3132..18e443c59 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -181,9 +181,12 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { // Session has ended, check debits and refund the extra charged duration func (self *SMGSession) close(endTime time.Time) error { + firstCC := self.callCosts[0] + for _, cc := range self.callCosts[1:] { + firstCC.Merge(cc) + } if len(self.callCosts) != 0 { // We have had at least one cost calculation - lastCC := self.callCosts[len(self.callCosts)-1] - end := lastCC.GetEndTime() + end := firstCC.GetEndTime() refundDuration := end.Sub(endTime) self.refund(refundDuration) } @@ -214,10 +217,7 @@ func (self *SMGSession) saveOperations() error { if len(self.callCosts) == 0 { return nil // There are no costs to save, ignore the operation } - firstCC := self.callCosts[0] - for _, cc := range self.callCosts[1:] { - firstCC.Merge(cc) - } + firstCC := self.callCosts[0] // was merged in close methos firstCC.Timespans.Compress() firstCC.Round() roundIncrements := firstCC.GetRoundIncrements() From dd5e8bb510a35077f348e6ae03bcb722df3eac4f Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 17 Mar 2016 12:50:27 +0100 Subject: [PATCH 195/199] make refund use merged callcost --- sessionmanager/smg_session.go | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 18e443c59..efe31dd87 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -124,11 +124,11 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D // Attempts to refund a duration, error on failure func (self *SMGSession) refund(refundDuration time.Duration) error { initialRefundDuration := refundDuration - lastCC := self.callCosts[len(self.callCosts)-1] - lastCC.Timespans.Decompress() + firstCC := self.callCosts[0] // use merged cc (from close function) + firstCC.Timespans.Decompress() var refundIncrements engine.Increments - for i := len(lastCC.Timespans) - 1; i >= 0; i-- { - ts := lastCC.Timespans[i] + for i := len(firstCC.Timespans) - 1; i >= 0; i-- { + ts := firstCC.Timespans[i] tsDuration := ts.GetDuration() if refundDuration <= tsDuration { @@ -144,8 +144,8 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { } } if lastRefundedIncrementIndex == 0 { - lastCC.Timespans[i] = nil - lastCC.Timespans = lastCC.Timespans[:i] + firstCC.Timespans[i] = nil + firstCC.Timespans = firstCC.Timespans[:i] } else { ts.SplitByIncrement(lastRefundedIncrementIndex) ts.Cost = ts.CalculateCost() @@ -154,8 +154,8 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { } else { refundIncrements = append(refundIncrements, ts.Increments...) // remove the timespan entirely - lastCC.Timespans[i] = nil - lastCC.Timespans = lastCC.Timespans[:i] + firstCC.Timespans[i] = nil + firstCC.Timespans = firstCC.Timespans[:i] // continue to the next timespan with what is left to refund refundDuration -= tsDuration } @@ -163,7 +163,7 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { // show only what was actualy refunded (stopped in timespan) // utils.Logger.Info(fmt.Sprintf("Refund duration: %v", initialRefundDuration-refundDuration)) if len(refundIncrements) > 0 { - cd := lastCC.CreateCallDescriptor() + cd := firstCC.CreateCallDescriptor() cd.Increments = refundIncrements cd.Increments.Compress() utils.Logger.Info(fmt.Sprintf("Refunding duration %v with cd: %s", initialRefundDuration, utils.ToJSON(cd))) @@ -173,9 +173,9 @@ func (self *SMGSession) refund(refundDuration time.Duration) error { return err } } - lastCC.Cost -= refundIncrements.GetTotalCost() - lastCC.UpdateRatedUsage() - lastCC.Timespans.Compress() + firstCC.Cost -= refundIncrements.GetTotalCost() + firstCC.UpdateRatedUsage() + firstCC.Timespans.Compress() return nil } From 529a642c87b6699f66caead99623626c66963d3d Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 17 Mar 2016 17:20:49 +0100 Subject: [PATCH 196/199] updated dockerfile --- data/docker/devel/Dockerfile | 6 +++--- data/docker/devel/start.sh | 3 +-- sessionmanager/smg_it_test.go | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/data/docker/devel/Dockerfile b/data/docker/devel/Dockerfile index 915076ebb..47e251317 100644 --- a/data/docker/devel/Dockerfile +++ b/data/docker/devel/Dockerfile @@ -11,10 +11,10 @@ RUN gpg --keyserver pool.sks-keyservers.net --recv-key D76EDC7725E010CF && gpg - RUN echo 'deb http://files.freeswitch.org/repo/deb/debian/ jessie main' > /etc/apt/sources.list.d/freeswitch.list # add mongo repo keys -RUN apt-key adv --keyserver 'keyserver.ubuntu.com' --recv '7F0CEB10' +RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv EA312927 # add mongo repo -RUN echo 'deb http://repo.mongodb.org/apt/debian wheezy/mongodb-org/3.0 main' | tee '/etc/apt/sources.list.d/mongodb-org-3.0.list' +RUN echo 'deb http://repo.mongodb.org/apt/debian wheezy/mongodb-org/3.2 main' | tee '/etc/apt/sources.list.d/mongodb-org-3.2.list' # install dependencies RUN apt-get -y update && apt-get -y install git bzr mercurial redis-server mysql-server python-pycurl python-mysqldb postgresql postgresql-client sudo wget freeswitch-meta-vanilla vim zsh mongodb-org @@ -26,7 +26,7 @@ COPY mongod.conf /etc/mongod.conf RUN useradd -c CGRateS -d /var/run/cgrates -s /bin/false -r cgrates # install golang -RUN wget -qO- https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz | tar xzf - -C /root/ +RUN wget -qO- https://storage.googleapis.com/golang/go1.6.linux-amd64.tar.gz | tar xzf - -C /root/ #install glide RUN GOROOT=/root/go GOPATH=/root/code /root/go/bin/go get github.com/Masterminds/glide diff --git a/data/docker/devel/start.sh b/data/docker/devel/start.sh index 75df8c736..fbacacd13 100755 --- a/data/docker/devel/start.sh +++ b/data/docker/devel/start.sh @@ -24,11 +24,10 @@ mongo --eval 'db.createUser({"user":"cgrates", "pwd":"CGRateS.org", "roles":[{ro #env vars export GOROOT=/root/go; export GOPATH=/root/code; export PATH=$GOROOT/bin:$GOPATH/bin:$PATH -export GO15VENDOREXPERIMENT=1 # build and install cgrates cd /root/cgr -#glide -y devel.yaml up +#glide -y devel.yaml install ./build.sh # create cgr-engine link diff --git a/sessionmanager/smg_it_test.go b/sessionmanager/smg_it_test.go index e508d1567..cd246596e 100644 --- a/sessionmanager/smg_it_test.go +++ b/sessionmanager/smg_it_test.go @@ -415,7 +415,7 @@ func TestSMGLastUsed(t *testing.T) { if err = smgRPC.Call("SMGenericV1.SessionEnd", smgEv, &rpl); err != nil || rpl != utils.OK { t.Error(err) } - eAcntVal = 7.09 + eAcntVal = 7.59 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.MONETARY].GetTotalValue() != eAcntVal { From 0f38ebabdd054b02eb9e82d859fe69e63b8d5b53 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 17 Mar 2016 17:45:24 +0100 Subject: [PATCH 197/199] Adding LastUsed to SMG.sessionEnd --- sessionmanager/smg_event.go | 8 ++++++-- sessionmanager/smg_session.go | 6 ++++++ sessionmanager/smgeneric.go | 17 ++++++++++++++++- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/sessionmanager/smg_event.go b/sessionmanager/smg_event.go index 0e710418c..643c8693f 100644 --- a/sessionmanager/smg_event.go +++ b/sessionmanager/smg_event.go @@ -157,7 +157,11 @@ func (self SMGenericEvent) GetUsage(fieldName string) (time.Duration, error) { if fieldName == utils.META_DEFAULT { fieldName = utils.USAGE } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + valIf, hasVal := self[fieldName] + if !hasVal { + return nilDuration, utils.ErrNotFound + } + result, _ := utils.ConvertIfaceToString(valIf) return utils.ParseDurationWithSecs(result) } @@ -167,7 +171,7 @@ func (self SMGenericEvent) GetLastUsed(fieldName string) (time.Duration, error) } valStr, hasVal := self[fieldName] if !hasVal { - return nilDuration, nil + return nilDuration, utils.ErrNotFound } result, _ := utils.ConvertIfaceToString(valStr) return utils.ParseDurationWithSecs(result) diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index 47f7e3132..7e5d13849 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -42,6 +42,7 @@ type SMGSession struct { callCosts []*engine.CallCost extraDuration time.Duration // keeps the current duration debited on top of what heas been asked lastUsage time.Duration // Keep record of the last debit for LastUsed functionality + totalUsage time.Duration } // Called in case of automatic debits @@ -87,6 +88,7 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed time.Duration) (time.D } // apply the lastUsed correction dur += lastUsedCorrection + self.totalUsage += dur // Should reflect the total usage so far // apply correction from previous run dur -= self.extraDuration self.extraDuration = 0 @@ -250,3 +252,7 @@ func (self *SMGSession) saveOperations() error { } return nil } + +func (self *SMGSession) TotalUsage() time.Duration { + return self.totalUsage +} diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index 1da986e1b..ac4285df3 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -200,7 +200,22 @@ func (self *SMGeneric) SessionStart(gev SMGenericEvent, clnt *rpc2.Client) (time func (self *SMGeneric) SessionEnd(gev SMGenericEvent, clnt *rpc2.Client) error { usage, err := gev.GetUsage(utils.META_DEFAULT) if err != nil { - return err + if err != utils.ErrNotFound { + return err + + } + lastUsed, err := gev.GetLastUsed(utils.META_DEFAULT) + if err != nil { + return err + } + var s *SMGSession + for _, s = range self.getSession(gev.GetUUID()) { + break + } + if s == nil { + return nil + } + usage = s.TotalUsage() + lastUsed } if err := self.sessionEnd(gev.GetUUID(), usage); err != nil { return err From f8269bcccff21ca7d3e595ebf7a8f0418f8e2b55 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 17 Mar 2016 19:11:48 +0100 Subject: [PATCH 198/199] SMGeneric error handling fixes --- agents/dmtagent_it_test.go | 2 +- sessionmanager/smgeneric.go | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index b8fc04911..eff5e81ee 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -231,7 +231,7 @@ func TestDmtAgentSendCCRInit(t *testing.T) { OriginID: "testccr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", Supplier: "SUPPL1", SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(0) * time.Second, PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, + Usage: time.Duration(0), PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, } ccr := storedCdrToCCR(cdr, "UNIT_TEST", daCfg.DiameterAgentCfg().OriginRealm, daCfg.DiameterAgentCfg().VendorId, daCfg.DiameterAgentCfg().ProductName, utils.DIAMETER_FIRMWARE_REVISION, daCfg.DiameterAgentCfg().DebitInterval, false) diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index ac4285df3..f18036b44 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -170,11 +170,14 @@ func (self *SMGeneric) GetLcrSuppliers(gev SMGenericEvent, clnt *rpc2.Client) ([ // Execute debits for usage/maxUsage func (self *SMGeneric) SessionUpdate(gev SMGenericEvent, clnt *rpc2.Client) (time.Duration, error) { evLastUsed, err := gev.GetLastUsed(utils.META_DEFAULT) - if err != nil { + if err != nil && err != utils.ErrNotFound { return nilDuration, err } evMaxUsage, err := gev.GetMaxUsage(utils.META_DEFAULT, self.cgrCfg.MaxCallDuration) if err != nil { + if err == utils.ErrNotFound { + err = utils.ErrMandatoryIeMissing + } return nilDuration, err } evUuid := gev.GetUUID() @@ -206,6 +209,9 @@ func (self *SMGeneric) SessionEnd(gev SMGenericEvent, clnt *rpc2.Client) error { } lastUsed, err := gev.GetLastUsed(utils.META_DEFAULT) if err != nil { + if err == utils.ErrNotFound { + err = utils.ErrMandatoryIeMissing + } return err } var s *SMGSession From 3097315aa95a7d92f24edb6f2fc6db5e42b7b231 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 17 Mar 2016 20:45:14 +0100 Subject: [PATCH 199/199] SMGenericV1.ActiveSessions implementation --- apier/v1/smgenericv1.go | 39 +++++++++++++++++++++++++++++++++++ sessionmanager/smg_session.go | 37 +++++++++++++++++++++++++++++++++ sessionmanager/smgeneric.go | 9 ++++++++ utils/apitpdata.go | 13 ++++++++++++ 4 files changed, 98 insertions(+) diff --git a/apier/v1/smgenericv1.go b/apier/v1/smgenericv1.go index d56c1d431..eed4310fb 100644 --- a/apier/v1/smgenericv1.go +++ b/apier/v1/smgenericv1.go @@ -89,6 +89,45 @@ func (self *SMGenericV1) ProcessCdr(ev sessionmanager.SMGenericEvent, reply *str return nil } +func (self *SMGenericV1) ActiveSessions(attrs utils.AttrSMGGetActiveSessions, reply *[]*sessionmanager.ActiveSession) error { + aSessions := make([]*sessionmanager.ActiveSession, 0) + for _, sGrp := range self.sm.Sessions() { // Add sessions to return with filter + for _, s := range sGrp { + as := s.AsActiveSession(self.sm.Timezone()) + if attrs.ToR != nil && *attrs.ToR != as.TOR { + continue + } + if attrs.RunID != nil && *attrs.RunID != as.RunId { + continue + } + if attrs.RequestType != nil && *attrs.RequestType != as.ReqType { + continue + } + if attrs.Tenant != nil && *attrs.Tenant != as.Tenant { + continue + } + if attrs.Category != nil && *attrs.Category != as.Category { + continue + } + if attrs.Account != nil && *attrs.Account != as.Account { + continue + } + if attrs.Subject != nil && *attrs.Subject != as.Subject { + continue + } + if attrs.Destination != nil && *attrs.Destination != as.Destination { + continue + } + if attrs.Supplier != nil && *attrs.Supplier != as.Supplier { + continue + } + aSessions = append(aSessions, as) + } + } + *reply = aSessions + return nil +} + // rpcclient.RpcClientConnection interface func (self *SMGenericV1) Call(serviceMethod string, args interface{}, reply interface{}) error { switch serviceMethod { diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index bd449d266..815daa4e3 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -256,3 +256,40 @@ func (self *SMGSession) saveOperations() error { func (self *SMGSession) TotalUsage() time.Duration { return self.totalUsage } + +func (self *SMGSession) AsActiveSession(timezone string) *ActiveSession { + sTime, _ := self.eventStart.GetSetupTime(utils.META_DEFAULT, timezone) + aTime, _ := self.eventStart.GetAnswerTime(utils.META_DEFAULT, timezone) + usage, _ := self.eventStart.GetUsage(utils.META_DEFAULT) + pdd, _ := self.eventStart.GetPdd(utils.META_DEFAULT) + aSession := &ActiveSession{ + CgrId: self.eventStart.GetCgrId(timezone), + TOR: utils.VOICE, + RunId: self.runId, + AccId: self.eventStart.GetUUID(), + CdrHost: self.eventStart.GetOriginatorIP(utils.META_DEFAULT), + CdrSource: self.eventStart.GetCdrSource(), + ReqType: self.eventStart.GetReqType(utils.META_DEFAULT), + Direction: self.eventStart.GetDirection(utils.META_DEFAULT), + Tenant: self.eventStart.GetTenant(utils.META_DEFAULT), + Category: self.eventStart.GetCategory(utils.META_DEFAULT), + Account: self.eventStart.GetAccount(utils.META_DEFAULT), + Subject: self.eventStart.GetSubject(utils.META_DEFAULT), + Destination: self.eventStart.GetDestination(utils.META_DEFAULT), + SetupTime: sTime, + AnswerTime: aTime, + Usage: usage, + Pdd: pdd, + ExtraFields: self.eventStart.GetExtraFields(), + Supplier: self.eventStart.GetSupplier(utils.META_DEFAULT), + SMId: "CGR-DA", + } + if self.cd != nil { + aSession.LoopIndex = self.cd.LoopIndex + aSession.DurationIndex = self.cd.DurationIndex + aSession.MaxRate = self.cd.MaxRate + aSession.MaxRateUnit = self.cd.MaxRateUnit + aSession.MaxCostSoFar = self.cd.MaxCostSoFar + } + return aSession +} diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index f18036b44..e80417211 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -344,6 +344,15 @@ func (self *SMGeneric) Connect() error { return nil } +// Used by APIer to retrieve sessions +func (self *SMGeneric) Sessions() map[string][]*SMGSession { + return self.getSessions() +} + +func (self *SMGeneric) Timezone() string { + return self.timezone +} + // System shutdown func (self *SMGeneric) Shutdown() error { for ssId := range self.getSessions() { // Force sessions shutdown diff --git a/utils/apitpdata.go b/utils/apitpdata.go index cc4a47a53..56dcd006f 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -1156,3 +1156,16 @@ type AliasValue struct { Alias string Weight float64 } + +// AttrSMGGetActiveSessions will filter returned sessions by SMGenericV1 +type AttrSMGGetActiveSessions struct { + ToR *string + RunID *string + RequestType *string + Tenant *string + Category *string + Account *string + Subject *string + Destination *string + Supplier *string +}