diff --git a/agents/dmtagent.go b/agents/dmtagent.go index 57eea014f..40f224c0e 100644 --- a/agents/dmtagent.go +++ b/agents/dmtagent.go @@ -24,6 +24,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" @@ -76,7 +77,8 @@ func (self *DiameterAgent) handlers() diam.Handler { return dSM } -func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor, processorVars map[string]string, cca *CCA) (bool, error) { +func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestProcessor, + processorVars map[string]string, cca *CCA) (bool, error) { passesAllFilters := true for _, fldFilter := range reqProcessor.RequestFilter { if passes, _ := passesFieldFilter(ccr.diamMessage, fldFilter, nil); !passes { @@ -129,7 +131,7 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro return false, ErrDiameterRatingFailed } } - var maxUsage float64 + var maxUsage time.Duration processorVars[CGRResultCode] = strconv.Itoa(diam.Success) processorVars[CGRError] = "" if reqProcessor.DryRun { // DryRun does not send over network @@ -138,15 +140,15 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro } else { // Find out maxUsage over APIs switch ccr.CCRequestType { case 1: - err = self.smg.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage) + err = self.smg.Call("SMGenericV2.InitiateSession", smgEv, &maxUsage) case 2: - err = self.smg.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage) + err = self.smg.Call("SMGenericV2.UpdateSession", smgEv, &maxUsage) case 3, 4: // Handle them together since we generate CDR for them var rpl string if ccr.CCRequestType == 3 { err = self.smg.Call("SMGenericV1.TerminateSession", smgEv, &rpl) } else if ccr.CCRequestType == 4 { - err = self.smg.Call("SMGenericV1.ChargeEvent", smgEv.Clone(), &maxUsage) + err = self.smg.Call("SMGenericV2.ChargeEvent", smgEv.Clone(), &maxUsage) if maxUsage == 0 { smgEv[utils.USAGE] = 0 // For CDR not to debit } @@ -182,12 +184,12 @@ func (self DiameterAgent) processCCR(ccr *CCR, reqProcessor *config.DARequestPro maxUsage = 0 } if prevMaxUsageStr, hasKey := processorVars[CGRMaxUsage]; hasKey { - prevMaxUsage, _ := strconv.ParseFloat(prevMaxUsageStr, 64) + prevMaxUsage, _ := utils.ParseDurationWithNanosecs(prevMaxUsageStr) if prevMaxUsage < maxUsage { maxUsage = prevMaxUsage } } - processorVars[CGRMaxUsage] = strconv.FormatFloat(maxUsage, 'f', -1, 64) + processorVars[CGRMaxUsage] = strconv.FormatInt(maxUsage.Nanoseconds(), 10) } if err := messageSetAVPsWithPath(cca.diamMessage, []interface{}{"Result-Code"}, processorVars[CGRResultCode], false, self.cgrCfg.DiameterAgentCfg().Timezone); err != nil { diff --git a/agents/dmtagent_it_test.go b/agents/dmtagent_it_test.go index 23515985c..1c9db8688 100644 --- a/agents/dmtagent_it_test.go +++ b/agents/dmtagent_it_test.go @@ -64,27 +64,6 @@ func TestDmtAgentInitCfg(t *testing.T) { rplyTimeout, _ = utils.ParseDurationWithSecs(*replyTimeout) } -// Remove data in both rating and accounting db -func TestDmtAgentResetDataDb(t *testing.T) { - if err := engine.InitDataDb(daCfg); err != nil { - t.Fatal(err) - } -} - -// Wipe out the cdr database -func TestDmtAgentResetStorDb(t *testing.T) { - if err := engine.InitStorDb(daCfg); err != nil { - t.Fatal(err) - } -} - -// Start CGR Engine -func TestDmtAgentStartEngine(t *testing.T) { - if _, err := engine.StopStartEngine(daCfgPath, 4000); err != nil { - t.Fatal(err) - } -} - func TestDmtAgentCCRAsSMGenericEvent(t *testing.T) { cfgDefaults, _ := config.NewDefaultCGRConfig() loadDictionaries(cfgDefaults.DiameterAgentCfg().DictionariesDir, "UNIT_TEST") @@ -133,10 +112,15 @@ func TestDmtAgentCCRAsSMGenericEvent(t *testing.T) { if ccr.diamMessage, err = ccr.AsDiameterMessage(); err != nil { t.Error(err) } - eSMGE := sessionmanager.SMGenericEvent{"EventName": DIAMETER_CCR, "OriginID": "routinga;1442095190;1476802709", - "Account": "*users", "AnswerTime": "2015-11-23 12:22:24 +0000 UTC", "Category": "call", - "Destination": "4986517174964", "Direction": "*out", "RequestType": "*users", "SetupTime": "2015-11-23 12:22:24 +0000 UTC", - "Subject": "*users", "SubscriberId": "4986517174963", "ToR": "*voice", "Tenant": "*users", "Usage": "300"} + eSMGE := sessionmanager.SMGenericEvent{"EventName": DIAMETER_CCR, + "OriginID": "routinga;1442095190;1476802709", + "Account": "*users", "Category": "call", + "AnswerTime": "2015-11-23 12:22:24 +0000 UTC", + "Destination": "4986517174964", "Direction": "*out", + "RequestType": "*users", + "SetupTime": "2015-11-23 12:22:24 +0000 UTC", + "Subject": "*users", "SubscriberId": "4986517174963", + "ToR": "*voice", "Tenant": "*users", "Usage": "5m0s"} ccrFields := []*config.CfgCdrField{ &config.CfgCdrField{Tag: "TOR", FieldId: "ToR", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("^*voice", utils.INFIELD_SEP), Mandatory: true}, @@ -172,25 +156,35 @@ func TestDmtAgentCCRAsSMGenericEvent(t *testing.T) { func TestDmtAgentPopulateCCTotalOctets(t *testing.T) { 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}, + &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 { + 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 { + 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 { + 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") @@ -199,6 +193,27 @@ func TestDmtAgentPopulateCCTotalOctets(t *testing.T) { } } +// Remove data in both rating and accounting db +func TestDmtAgentResetDataDb(t *testing.T) { + if err := engine.InitDataDb(daCfg); err != nil { + t.Fatal(err) + } +} + +// Wipe out the cdr database +func TestDmtAgentResetStorDb(t *testing.T) { + if err := engine.InitStorDb(daCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func TestDmtAgentStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(daCfgPath, 4000); err != nil { + t.Fatal(err) + } +} + // Connect rpc client to rater func TestDmtAgentApierRpcConn(t *testing.T) { var err error @@ -228,14 +243,22 @@ func TestDmtAgentConnectDiameterClient(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 TestDmtAgentSendCCRInit(t *testing.T) { - 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), PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, + 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, + Tenant: "cgrates.org", Category: "call", Account: "1001", + Subject: "1001", Destination: "1004", + SetupTime: time.Date(2015, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2015, 11, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(0), RunID: utils.DEFAULT_RUNID, + 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) + 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) @@ -245,7 +268,8 @@ func TestDmtAgentSendCCRInit(t *testing.T) { } time.Sleep(time.Duration(*waitRater) * time.Millisecond) msg := dmtClient.ReceivedMessage(rplyTimeout) - if avps, err := msg.FindAVPsWithPath([]interface{}{"Granted-Service-Unit", "CC-Time"}, dict.UndefinedVendorID); err != nil { + if avps, err := msg.FindAVPsWithPath([]interface{}{"Granted-Service-Unit", "CC-Time"}, + dict.UndefinedVendorID); err != nil { t.Error(err) } else if len(avps) == 0 { t.Error("Granted-Service-Unit not found") @@ -270,10 +294,10 @@ func TestDmtAgentSendCCRInit(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:52:26Z"' func TestDmtAgentSendCCRUpdate(t *testing.T) { 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", + OriginID: "testccr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", 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"}, + Usage: time.Duration(300) * 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) @@ -306,10 +330,10 @@ func TestDmtAgentSendCCRUpdate(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:57:26Z"' func TestDmtAgentSendCCRUpdate2(t *testing.T) { 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", + OriginID: "testccr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", 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"}, + Usage: time.Duration(600) * 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) @@ -341,10 +365,10 @@ func TestDmtAgentSendCCRUpdate2(t *testing.T) { func TestDmtAgentSendCCRTerminate(t *testing.T) { 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", + OriginID: "testccr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1004", 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"}, + Usage: time.Duration(610) * 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, true) @@ -535,10 +559,10 @@ 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) { 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", + OriginID: "testccr4", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "non_existent", Subject: "non_existent", Destination: "1004", 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) * 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) @@ -643,7 +667,7 @@ func TestDmtAgentCdrs(t *testing.T) { } else if len(cdrs) != 1 { t.Error("Unexpected number of CDRs returned: ", len(cdrs)) } else { - if cdrs[0].Usage != "610" { + if cdrs[0].Usage != "10m10s" { t.Errorf("Unexpected CDR Usage received, cdr: %+v ", cdrs[0]) } if cdrs[0].Cost != 0.7565 { diff --git a/agents/libdmt.go b/agents/libdmt.go index ed9b73c59..2273e6076 100644 --- a/agents/libdmt.go +++ b/agents/libdmt.go @@ -126,16 +126,15 @@ func disectUsageForCCR(usage time.Duration, debitInterval time.Duration, callEnd return } -func usageFromCCR(reqType, reqNr, reqCCTime, usedCCTime int, debitIterval time.Duration) time.Duration { - dISecs := debitIterval.Seconds() - var ccTime int +func usageFromCCR(reqType int, reqNr, usedCCTime int64, debitIterval time.Duration) (usage time.Duration) { + //dISecs := debitIterval.Nano() + //var ccTime int + usage = debitIterval if reqType == 3 { reqNr -= 1 // decrease request number to reach the real number - ccTime = usedCCTime + (int(dISecs) * reqNr) - } else { - ccTime = int(dISecs) + usage = (time.Duration(usedCCTime) * time.Second) + time.Duration(debitIterval.Nanoseconds()*reqNr) } - return time.Duration(ccTime) * time.Second + return } // Utility function to convert from StoredCdr to CCR struct @@ -180,12 +179,13 @@ func avpValAsString(a *diam.AVP) string { } // Handler for meta functions -func metaHandler(m *diam.Message, tag, arg string, dur time.Duration) (string, error) { +func metaHandler(m *diam.Message, processorVars map[string]string, + tag, arg string, dur time.Duration) (string, error) { switch tag { case META_CCR_USAGE: var ok bool var reqType datatype.Enumerated - var reqNr, reqUnit, usedUnit datatype.Unsigned32 + var reqNr, usedUnit datatype.Unsigned32 if ccReqTypeAvp, err := m.FindAVP("CC-Request-Type", 0); err != nil { return "", err } else if ccReqTypeAvp == nil { @@ -206,7 +206,7 @@ func metaHandler(m *diam.Message, tag, arg string, dur time.Duration) (string, e return "", err } else if len(reqUnitAVPs) == 0 { return "", errors.New("Requested-Service-Unit>CC-Time not found") - } else if reqUnit, ok = reqUnitAVPs[0].Data.(datatype.Unsigned32); !ok { + } else if usedUnit, ok = reqUnitAVPs[0].Data.(datatype.Unsigned32); !ok { return "", fmt.Errorf("Requested-Service-Unit>CC-Time must be Unsigned32 and not %v", reqUnitAVPs[0].Data.Type()) } case datatype.Enumerated(3), datatype.Enumerated(4): @@ -218,16 +218,16 @@ func metaHandler(m *diam.Message, tag, arg string, dur time.Duration) (string, e } } } - usage := usageFromCCR(int(reqType), int(reqNr), int(reqUnit), int(usedUnit), dur) - return strconv.FormatFloat(usage.Seconds(), 'f', -1, 64), nil + return usageFromCCR(int(reqType), int64(reqNr), int64(usedUnit), dur).String(), nil } 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, nil) +func metaValueExponent(m *diam.Message, processorVars map[string]string, + argsTpl utils.RSRFields, roundingDecimals int) (string, error) { + valStr := composedFieldvalue(m, argsTpl, 0, processorVars) handlerArgs := strings.Split(valStr, utils.HandlerArgSep) if len(handlerArgs) != 2 { return "", errors.New("Unexpected number of arguments") @@ -244,8 +244,9 @@ 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, passAtIndex, roundingDecimals int) (string, error) { - valStr := composedFieldvalue(m, argsTpl, passAtIndex, nil) +func metaSum(m *diam.Message, processorVars map[string]string, + argsTpl utils.RSRFields, passAtIndex, roundingDecimals int) (string, error) { + valStr := composedFieldvalue(m, argsTpl, passAtIndex, processorVars) handlerArgs := strings.Split(valStr, utils.HandlerArgSep) var summed float64 for _, arg := range handlerArgs { @@ -270,7 +271,8 @@ func splitIntoInterface(content, sep string) []interface{} { // avpsWithPath is used to find AVPs by specifying RSRField as filter func avpsWithPath(m *diam.Message, rsrFld *utils.RSRField) ([]*diam.AVP, error) { - return m.FindAVPsWithPath(splitIntoInterface(rsrFld.Id, utils.HIERARCHY_SEP), dict.UndefinedVendorID) + return m.FindAVPsWithPath( + splitIntoInterface(rsrFld.Id, utils.HIERARCHY_SEP), dict.UndefinedVendorID) } func passesFieldFilter(m *diam.Message, fieldFilter *utils.RSRField, processorVars map[string]string) (bool, int) { @@ -377,7 +379,8 @@ func serializeAVPValueFromString(dictAVP *dict.AVP, valStr, timezone string) ([] } } -func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interface{}, processorVars map[string]string) (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 @@ -403,11 +406,11 @@ func fieldOutVal(m *diam.Message, cfgFld *config.CfgCdrField, extraParam interfa case utils.META_HANDLER: switch cfgFld.HandlerId { case META_VALUE_EXPONENT: - outVal, err = metaValueExponent(m, cfgFld.Value, 10) // FixMe: add here configured number of decimals + outVal, err = metaValueExponent(m, processorVars, cfgFld.Value, 10) // FixMe: add here configured number of decimals case META_SUM: - outVal, err = metaSum(m, cfgFld.Value, passAtIndex, 10) + outVal, err = metaSum(m, processorVars, cfgFld.Value, passAtIndex, 10) default: - outVal, err = metaHandler(m, cfgFld.HandlerId, cfgFld.Layout, extraParam.(time.Duration)) + outVal, err = metaHandler(m, processorVars, 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 743af1805..eb42abf45 100644 --- a/agents/libdmt_test.go +++ b/agents/libdmt_test.go @@ -59,25 +59,25 @@ func TestDisectUsageForCCR(t *testing.T) { } func TestUsageFromCCR(t *testing.T) { - if usage := usageFromCCR(1, 0, 300, 0, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second { + if usage := usageFromCCR(1, 0, 0, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second { t.Error(usage) } - if usage := usageFromCCR(2, 0, 300, 300, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second { + if usage := usageFromCCR(2, 0, 300, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second { t.Error(usage) } - if usage := usageFromCCR(2, 3, 300, 300, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second { + if usage := usageFromCCR(2, 3, 300, time.Duration(300)*time.Second); usage != time.Duration(300)*time.Second { t.Error(usage.Seconds()) } - if usage := usageFromCCR(3, 3, 0, 10, time.Duration(300)*time.Second); usage != time.Duration(610)*time.Second { + if usage := usageFromCCR(3, 3, 10, time.Duration(300)*time.Second); usage != time.Duration(610)*time.Second { t.Error(usage) } - if usage := usageFromCCR(3, 4, 0, 35, time.Duration(300)*time.Second); usage != time.Duration(935)*time.Second { + if usage := usageFromCCR(3, 4, 35, time.Duration(300)*time.Second); usage != time.Duration(935)*time.Second { t.Error(usage) } - if usage := usageFromCCR(3, 1, 0, 35, time.Duration(300)*time.Second); usage != time.Duration(35)*time.Second { + if usage := usageFromCCR(3, 1, 35, time.Duration(300)*time.Second); usage != time.Duration(35)*time.Second { t.Error(usage) } - if usage := usageFromCCR(1, 0, 360, 0, time.Duration(360)*time.Second); usage != time.Duration(360)*time.Second { + if usage := usageFromCCR(1, 0, 0, time.Duration(360)*time.Second); usage != time.Duration(360)*time.Second { t.Error(usage) } } @@ -108,12 +108,17 @@ 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 { + if val, err := metaValueExponent(m, nil, 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 != "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 { + if _, err = metaValueExponent(m, nil, + 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 } } @@ -136,12 +141,17 @@ func TestMetaSum(t *testing.T) { }), }, }) - 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), 0, 10); err != nil { + if val, err := metaSum(m, nil, + utils.ParseRSRFieldsMustCompile( + "Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;^|;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", + utils.INFIELD_SEP), 0, 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), 0, 10); err == nil { + if _, err = metaSum(m, nil, utils.ParseRSRFieldsMustCompile( + "Requested-Service-Unit>CC-Money>Unit-Value>Value-Digits;Requested-Service-Unit>CC-Money>Unit-Value>Exponent", + utils.INFIELD_SEP), 0, 10); err == nil { t.Error("Should have received error") // Insufficient number arguments } } diff --git a/agents/radagent_it_test.go b/agents/radagent_it_test.go index 53d30fbc9..eae9797f0 100644 --- a/agents/radagent_it_test.go +++ b/agents/radagent_it_test.go @@ -272,7 +272,7 @@ func TestRAitAcctStop(t *testing.T) { } else if len(cdrs) != 1 { t.Error("Unexpected number of CDRs returned: ", len(cdrs)) } else { - if cdrs[0].Usage != "4" { + if cdrs[0].Usage != "4s" { t.Errorf("Unexpected CDR Usage received, cdr: %v %+v ", cdrs[0].Usage, cdrs[0]) } if cdrs[0].CostSource != utils.SESSION_MANAGER_SOURCE { diff --git a/apier/v1/apier_it_test.go b/apier/v1/apier_it_test.go index a492833af..136729664 100644 --- a/apier/v1/apier_it_test.go +++ b/apier/v1/apier_it_test.go @@ -1354,11 +1354,11 @@ func TestApierMaxDebitInexistentAcnt(t *testing.T) { func TestApierCdrServer(t *testing.T) { httpClient := new(http.Client) - cdrForm1 := url.Values{utils.ACCID: []string{"dsafdsaf"}, utils.CDRHOST: []string{"192.168.1.1"}, utils.REQTYPE: []string{utils.META_RATED}, utils.DIRECTION: []string{"*out"}, + cdrForm1 := url.Values{utils.ACCID: []string{"dsafdsaf"}, utils.CDRHOST: []string{"192.168.1.1"}, utils.REQTYPE: []string{utils.META_RATED}, utils.TENANT: []string{"cgrates.org"}, utils.CATEGORY: []string{"call"}, utils.ACCOUNT: []string{"1001"}, utils.SUBJECT: []string{"1001"}, utils.DESTINATION: []string{"1002"}, utils.SETUP_TIME: []string{"2013-11-07T08:42:22Z"}, utils.ANSWER_TIME: []string{"2013-11-07T08:42:26Z"}, utils.USAGE: []string{"10"}, "field_extr1": []string{"val_extr1"}, "fieldextr2": []string{"valextr2"}} - cdrForm2 := url.Values{utils.ACCID: []string{"adsafdsaf"}, utils.CDRHOST: []string{"192.168.1.1"}, utils.REQTYPE: []string{utils.META_RATED}, utils.DIRECTION: []string{"*out"}, + cdrForm2 := url.Values{utils.ACCID: []string{"adsafdsaf"}, utils.CDRHOST: []string{"192.168.1.1"}, utils.REQTYPE: []string{utils.META_RATED}, utils.TENANT: []string{"cgrates.org"}, utils.CATEGORY: []string{"call"}, utils.ACCOUNT: []string{"1001"}, utils.SUBJECT: []string{"1001"}, utils.DESTINATION: []string{"1002"}, utils.SETUP_TIME: []string{"2013-11-07T08:42:23Z"}, utils.ANSWER_TIME: []string{"2013-11-07T08:42:26Z"}, utils.USAGE: []string{"10"}, "field_extr1": []string{"val_extr1"}, "fieldextr2": []string{"valextr2"}} @@ -1384,7 +1384,7 @@ func TestApierITGetCdrs(t *testing.T) { func TestApierITProcessCdr(t *testing.T) { var reply string cdr := engine.CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "test", RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", + OriginHost: "192.168.1.1", Source: "test", RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, diff --git a/apier/v1/auth.go b/apier/v1/auth.go index 1d2b4ac85..80a3556f4 100644 --- a/apier/v1/auth.go +++ b/apier/v1/auth.go @@ -38,9 +38,6 @@ func (self *ApierV1) GetMaxUsage(usageRecord engine.UsageRecord, maxUsage *float if usageRecord.RequestType == "" { usageRecord.RequestType = self.Config.DefaultReqType } - if usageRecord.Direction == "" { - usageRecord.Direction = utils.OUT - } if usageRecord.Tenant == "" { usageRecord.Tenant = self.Config.DefaultTenant } diff --git a/apier/v1/cdrstatsv1_it_test.go b/apier/v1/cdrstatsv1_it_test.go index 322087d27..929511d23 100644 --- a/apier/v1/cdrstatsv1_it_test.go +++ b/apier/v1/cdrstatsv1_it_test.go @@ -98,28 +98,28 @@ func TestCDRStatsitPostCdrs(t *testing.T) { storedCdrs := []*engine.CDR{ &engine.CDR{CGRID: utils.Sha1("dsafdsafa", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsafa", OriginHost: "192.168.1.1", Source: "test", - RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", + RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(), AnswerTime: time.Now(), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, }, &engine.CDR{CGRID: utils.Sha1("dsafdsafb", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsafb", OriginHost: "192.168.1.1", Source: "test", - RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", + RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(), AnswerTime: time.Now(), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(5) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, }, &engine.CDR{CGRID: utils.Sha1("dsafdsafc", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsafc", OriginHost: "192.168.1.1", Source: "test", - RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", + RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(), AnswerTime: time.Now(), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(30) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, }, &engine.CDR{CGRID: utils.Sha1("dsafdsafd", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsafd", OriginHost: "192.168.1.1", Source: "test", - RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", + RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "+4986517174963", SetupTime: time.Now(), AnswerTime: time.Time{}, RunID: utils.DEFAULT_RUNID, Usage: time.Duration(0) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, diff --git a/apier/v1/costs.go b/apier/v1/costs.go index b006e94e1..37e93c4c0 100644 --- a/apier/v1/costs.go +++ b/apier/v1/costs.go @@ -34,7 +34,7 @@ type AttrGetDataCost struct { } func (apier *ApierV1) GetDataCost(attrs AttrGetDataCost, reply *engine.DataCost) error { - usageAsDuration := time.Duration(attrs.Usage) * time.Second // Convert to seconds to match the loaded rates + usageAsDuration := time.Duration(attrs.Usage) * time.Nanosecond // Convert to seconds to match the loaded rates cd := &engine.CallDescriptor{ Direction: attrs.Direction, Category: attrs.Category, diff --git a/apier/v1/debit.go b/apier/v1/debit.go index a80d7fcdf..82f454e48 100644 --- a/apier/v1/debit.go +++ b/apier/v1/debit.go @@ -59,9 +59,6 @@ func (apier *ApierV1) DebitUsageWithOptions(args AttrDebitUsageWithOptions, repl if usageRecord.RequestType == "" { usageRecord.RequestType = apier.Config.DefaultReqType } - if usageRecord.Direction == "" { - usageRecord.Direction = utils.OUT - } if usageRecord.Tenant == "" { usageRecord.Tenant = apier.Config.DefaultTenant } diff --git a/apier/v1/debit_test.go b/apier/v1/debit_test.go index f5f8ad6dc..460d13751 100644 --- a/apier/v1/debit_test.go +++ b/apier/v1/debit_test.go @@ -37,7 +37,7 @@ var ( func init() { apierDebitStorage, _ = engine.NewMapStorage() cfg, _ := config.NewDefaultCGRConfig() - responder := new(engine.Responder) + responder := &engine.Responder{MaxComputedUsage: cfg.RALsMaxComputedUsage} dm = engine.NewDataManager(apierDebitStorage) engine.SetDataStorage(dm) apierDebit = &ApierV1{ @@ -125,7 +125,6 @@ func TestDebitUsageWithOptions(t *testing.T) { Usage: "1", ToR: utils.MONETARY, Category: "call", - Direction: "*out", SetupTime: time.Date(2013, 11, 7, 7, 42, 20, 0, time.UTC).String(), AnswerTime: time.Date(2013, 11, 7, 7, 42, 20, 0, time.UTC).String(), } diff --git a/apier/v1/smgenericv1_it_test.go b/apier/v1/smgenericv1_it_test.go index 9bcf76f1e..4c53fda0b 100644 --- a/apier/v1/smgenericv1_it_test.go +++ b/apier/v1/smgenericv1_it_test.go @@ -100,7 +100,7 @@ func TestSMGV1CacheStats(t *testing.T) { t.Error(reply) } var rcvStats *utils.CacheStats - expectedStats := &utils.CacheStats{Destinations: 5, ReverseDestinations: 7, RatingPlans: 4, RatingProfiles: 9, + expectedStats := &utils.CacheStats{Destinations: 5, ReverseDestinations: 7, RatingPlans: 4, RatingProfiles: 10, Actions: 9, ActionPlans: 4, AccountActionPlans: 5, SharedGroups: 1, DerivedChargers: 1, LcrProfiles: 5, CdrStats: 6, Users: 3, Aliases: 1, ReverseAliases: 2, ResourceProfiles: 3, Resources: 3, StatQueues: 1, StatQueueProfiles: 1, Thresholds: 7, ThresholdProfiles: 7, Filters: 16, LCRProfiles: 1} diff --git a/apier/v1/triggers.go b/apier/v1/triggers.go index 7ceb17094..ad4c74460 100644 --- a/apier/v1/triggers.go +++ b/apier/v1/triggers.go @@ -254,7 +254,7 @@ func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, at.Executed = *attr.Executed } if attr.MinSleep != nil { - minSleep, err := utils.ParseDurationWithSecs(*attr.MinSleep) + minSleep, err := utils.ParseDurationWithNanosecs(*attr.MinSleep) if err != nil { return 0, err } @@ -443,7 +443,7 @@ func (self *ApierV1) SetActionTrigger(attr AttrSetActionTrigger, reply *string) newAtr.Recurrent = *attr.Recurrent } if attr.MinSleep != nil { - minSleep, err := utils.ParseDurationWithSecs(*attr.MinSleep) + minSleep, err := utils.ParseDurationWithNanosecs(*attr.MinSleep) if err != nil { *reply = err.Error() return err diff --git a/apier/v2/apier.go b/apier/v2/apier.go index 047d3daf0..63a48df0d 100644 --- a/apier/v2/apier.go +++ b/apier/v2/apier.go @@ -338,7 +338,7 @@ func (self *ApierV2) SetActions(attrs utils.AttrSetActions, reply *string) error for idx, apiAct := range attrs.Actions { var vf *utils.ValueFormula if apiAct.Units != "" { - if x, err := utils.ParseBalanceFilterValue(apiAct.Units); err == nil { + if x, err := utils.ParseBalanceFilterValue(apiAct.BalanceType, apiAct.Units); err == nil { vf = x } else { return err diff --git a/apier/v2/cdrs_it_test.go b/apier/v2/cdrs_it_test.go index 3f604cebb..a75fcece9 100644 --- a/apier/v2/cdrs_it_test.go +++ b/apier/v2/cdrs_it_test.go @@ -117,7 +117,7 @@ func testV2CDRsInjectUnratedCdr(t *testing.T) { } strCdr1 := &engine.CDR{CGRID: utils.Sha1("bbb1", time.Date(2015, 11, 21, 10, 47, 24, 0, time.UTC).String()), RunID: utils.MetaRaw, ToR: utils.VOICE, OriginID: "bbb1", OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoInjectUnratedCdr", RequestType: utils.META_RATED, - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2015, 11, 21, 10, 47, 24, 0, time.UTC), AnswerTime: time.Date(2015, 11, 21, 10, 47, 26, 0, time.UTC), Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: -1} @@ -144,7 +144,7 @@ func testV2CDRsProcessCdrRated(t *testing.T) { cdr := &engine.CDR{ CGRID: utils.Sha1("dsafdsaf", time.Date(2015, 12, 13, 18, 15, 26, 0, time.UTC).String()), RunID: utils.DEFAULT_RUNID, OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessCdrRated", RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", + OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessCdrRated", RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2015, 12, 13, 18, 15, 26, 0, time.UTC), AnswerTime: time.Date(2015, 12, 13, 18, 15, 26, 0, time.UTC), Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, @@ -162,7 +162,7 @@ func testV2CDRsProcessCdrRaw(t *testing.T) { cdr := &engine.CDR{ CGRID: utils.Sha1("abcdeftg", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, RunID: utils.MetaRaw, ToR: utils.VOICE, OriginID: "abcdeftg", - OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessCdrRaw", RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", + OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessCdrRaw", RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", Account: "1002", Subject: "1002", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, @@ -229,19 +229,19 @@ func testV2CDRsProcessPrepaidCdr(t *testing.T) { var reply string cdrs := []*engine.CDR{ &engine.CDR{CGRID: utils.Sha1("dsafdsaf2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessPrepaidCdr1", RequestType: utils.META_PREPAID, Direction: "*out", Tenant: "cgrates.org", + OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessPrepaidCdr1", RequestType: utils.META_PREPAID, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, Rated: true, }, &engine.CDR{CGRID: utils.Sha1("abcdeftg2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessPrepaidCdr2", RequestType: utils.META_PREPAID, Direction: "*out", Tenant: "cgrates.org", + OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessPrepaidCdr2", RequestType: utils.META_PREPAID, Tenant: "cgrates.org", Category: "call", Account: "1002", Subject: "1002", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, }, &engine.CDR{CGRID: utils.Sha1("aererfddf2", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessPrepaidCdr3", RequestType: utils.META_PREPAID, Direction: "*out", Tenant: "cgrates.org", + OriginHost: "192.168.1.1", Source: "TestV2CdrsMongoProcessPrepaidCdr3", RequestType: utils.META_PREPAID, Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, diff --git a/apier/v2/smgeneric.go b/apier/v2/smgeneric.go index eacb4d891..6360c4ea7 100644 --- a/apier/v2/smgeneric.go +++ b/apier/v2/smgeneric.go @@ -43,3 +43,8 @@ func (smgv2 *SMGenericV2) InitiateSession(ev sessionmanager.SMGenericEvent, maxU func (smgv2 *SMGenericV2) UpdateSession(ev sessionmanager.SMGenericEvent, maxUsage *time.Duration) error { return smgv2.SMG.BiRPCV2UpdateSession(nil, ev, maxUsage) } + +// Called on individual Events (eg SMS) +func (smgv2 *SMGenericV2) ChargeEvent(ev sessionmanager.SMGenericEvent, maxUsage *time.Duration) error { + return smgv2.SMG.BiRPCV2ChargeEvent(nil, ev, maxUsage) +} diff --git a/apier/v2/triggers.go b/apier/v2/triggers.go index 9edb7a6e0..5a93919de 100644 --- a/apier/v2/triggers.go +++ b/apier/v2/triggers.go @@ -88,7 +88,7 @@ func (attr *AttrSetAccountActionTriggers) UpdateActionTrigger(at *engine.ActionT at.Executed = *attr.Executed } if attr.MinSleep != nil { - if at.MinSleep, err = utils.ParseDurationWithSecs(*attr.MinSleep); err != nil { + if at.MinSleep, err = utils.ParseDurationWithNanosecs(*attr.MinSleep); err != nil { return } } diff --git a/cache/cache.go b/cache/cache.go index 521fc80ee..9fb0a5de7 100755 --- a/cache/cache.go +++ b/cache/cache.go @@ -56,7 +56,7 @@ type transactionItem struct { func init() { // ToDo: revert to nil config as soon as we handle cacheInstances properly dfCfg, _ := config.NewDefaultCGRConfig() - NewCache(dfCfg.CacheConfig) + NewCache(dfCfg.CacheCfg()) } func NewCache(cacheCfg config.CacheConfig) { diff --git a/cdrc/csv_test.go b/cdrc/csv_test.go index 83e80f28d..36fa36340 100644 --- a/cdrc/csv_test.go +++ b/cdrc/csv_test.go @@ -33,10 +33,6 @@ func TestCsvRecordToCDR(t *testing.T) { cdrcConfig.CdrSourceId = "TEST_CDRC" cdrcConfig.ContentFields = append(cdrcConfig.ContentFields, &config.CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, FieldId: utils.MEDI_RUNID, Value: utils.ParseRSRFieldsMustCompile("^*default", utils.INFIELD_SEP)}) - cdrcConfig.ContentFields = append(cdrcConfig.ContentFields, &config.CfgCdrField{Tag: "SupplierTest", Type: utils.META_COMPOSED, - FieldId: utils.SUPPLIER, Value: []*utils.RSRField{&utils.RSRField{Id: "14"}}}) - cdrcConfig.ContentFields = append(cdrcConfig.ContentFields, &config.CfgCdrField{Tag: "DisconnectCauseTest", Type: utils.META_COMPOSED, - FieldId: utils.DISCONNECT_CAUSE, Value: []*utils.RSRField{&utils.RSRField{Id: "16"}}}) csvProcessor := &CsvRecordsProcessor{dfltCdrcCfg: cdrcConfig, cdrcCfgs: []*config.CdrcConfig{cdrcConfig}} cdrRow := []string{"firstField", "secondField"} _, err := csvProcessor.recordToStoredCdr(cdrRow, cdrcConfig) @@ -44,32 +40,29 @@ func TestCsvRecordToCDR(t *testing.T) { t.Error("Failed to corectly detect missing fields from record") } cdrRow = []string{"ignored", "ignored", utils.VOICE, "acc1", utils.META_PREPAID, "*out", "cgrates.org", "call", "1001", "1001", "+4986517174963", - "2013-02-03 19:50:00", "2013-02-03 19:54:00", "62", "supplier1", "172.16.1.1", "NORMAL_DISCONNECT"} + "2013-02-03 19:50:00", "2013-02-03 19:54:00", "62s", "supplier1", "172.16.1.1", "NORMAL_DISCONNECT"} rtCdr, err := csvProcessor.recordToStoredCdr(cdrRow, cdrcConfig) if err != nil { t.Error("Failed to parse CDR in rated cdr", err) } expectedCdr := &engine.CDR{ - CGRID: utils.Sha1(cdrRow[3], time.Date(2013, 2, 3, 19, 50, 0, 0, time.UTC).String()), - RunID: "*default", - ToR: cdrRow[2], - OriginID: cdrRow[3], - OriginHost: "0.0.0.0", // Got it over internal interface - Source: "TEST_CDRC", - RequestType: cdrRow[4], - Direction: cdrRow[5], - Tenant: cdrRow[6], - Category: cdrRow[7], - Account: cdrRow[8], - Subject: cdrRow[9], - Destination: cdrRow[10], - SetupTime: time.Date(2013, 2, 3, 19, 50, 0, 0, time.UTC), - AnswerTime: time.Date(2013, 2, 3, 19, 54, 0, 0, time.UTC), - Usage: time.Duration(62) * time.Second, - Supplier: "supplier1", - DisconnectCause: "NORMAL_DISCONNECT", - ExtraFields: map[string]string{}, - Cost: -1, + CGRID: utils.Sha1(cdrRow[3], time.Date(2013, 2, 3, 19, 50, 0, 0, time.UTC).String()), + RunID: "*default", + ToR: cdrRow[2], + OriginID: cdrRow[3], + OriginHost: "0.0.0.0", // Got it over internal interface + Source: "TEST_CDRC", + RequestType: cdrRow[4], + Tenant: cdrRow[6], + Category: cdrRow[7], + Account: cdrRow[8], + Subject: cdrRow[9], + Destination: cdrRow[10], + SetupTime: time.Date(2013, 2, 3, 19, 50, 0, 0, time.UTC), + AnswerTime: time.Date(2013, 2, 3, 19, 54, 0, 0, time.UTC), + Usage: time.Duration(62) * time.Second, + ExtraFields: map[string]string{}, + Cost: -1, } if !reflect.DeepEqual(expectedCdr, rtCdr) { t.Errorf("Expected: \n%v, \nreceived: \n%v", expectedCdr, rtCdr) @@ -95,7 +88,7 @@ func TestCsvDataMultiplyFactor(t *testing.T) { ToR: cdrRow[0], OriginHost: "0.0.0.0", Source: "TEST_CDRC", - Usage: time.Duration(1) * time.Second, + Usage: time.Duration(1), ExtraFields: map[string]string{}, Cost: -1, } @@ -108,14 +101,15 @@ func TestCsvDataMultiplyFactor(t *testing.T) { ToR: cdrRow[0], OriginHost: "0.0.0.0", Source: "TEST_CDRC", - Usage: time.Duration(1024) * time.Second, + Usage: time.Duration(1024), ExtraFields: map[string]string{}, Cost: -1, } - if rtCdr, _ := csvProcessor.recordToStoredCdr(cdrRow, cdrcConfig); !reflect.DeepEqual(expectedCdr, rtCdr) { + if rtCdr, _ := csvProcessor.recordToStoredCdr(cdrRow, + cdrcConfig); !reflect.DeepEqual(expectedCdr, rtCdr) { t.Errorf("Expected: \n%v, \nreceived: \n%v", expectedCdr, rtCdr) } - cdrRow = []string{"*voice", "1"} + cdrRow = []string{"*voice", "1s"} expectedCdr = &engine.CDR{ CGRID: utils.Sha1("", sTime.String()), ToR: cdrRow[0], @@ -125,17 +119,25 @@ func TestCsvDataMultiplyFactor(t *testing.T) { ExtraFields: map[string]string{}, Cost: -1, } - if rtCdr, _ := csvProcessor.recordToStoredCdr(cdrRow, cdrcConfig); !reflect.DeepEqual(expectedCdr, rtCdr) { + if rtCdr, _ := csvProcessor.recordToStoredCdr(cdrRow, + cdrcConfig); !reflect.DeepEqual(expectedCdr, rtCdr) { t.Errorf("Expected: \n%v, \nreceived: \n%v", expectedCdr, rtCdr) } } func TestCsvPairToRecord(t *testing.T) { - eRecord := []string{"INVITE", "2daec40c", "548625ac", "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", "1436454408", "*prepaid", "1001", "1002", "", "3401:2069362475", "2"} - invPr := &UnpairedRecord{Method: "INVITE", Timestamp: time.Date(2015, 7, 9, 15, 6, 48, 0, time.UTC), - Values: []string{"INVITE", "2daec40c", "548625ac", "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", "1436454408", "*prepaid", "1001", "1002", "", "3401:2069362475"}} + eRecord := []string{"INVITE", "2daec40c", "548625ac", + "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", "1436454408", + "*prepaid", "1001", "1002", "", "3401:2069362475", "2"} + invPr := &UnpairedRecord{Method: "INVITE", + Timestamp: time.Date(2015, 7, 9, 15, 6, 48, 0, time.UTC), + Values: []string{"INVITE", "2daec40c", "548625ac", + "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", + "1436454408", "*prepaid", "1001", "1002", "", "3401:2069362475"}} byePr := &UnpairedRecord{Method: "BYE", Timestamp: time.Date(2015, 7, 9, 15, 6, 50, 0, time.UTC), - Values: []string{"BYE", "2daec40c", "548625ac", "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", "1436454410", "", "", "", "", "3401:2069362475"}} + Values: []string{"BYE", "2daec40c", "548625ac", + "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", + "1436454410", "", "", "", "", "3401:2069362475"}} if rec, err := pairToRecord(invPr, byePr); err != nil { t.Error(err) } else if !reflect.DeepEqual(eRecord, rec) { @@ -152,7 +154,9 @@ func TestCsvPairToRecord(t *testing.T) { if _, err := pairToRecord(invPr, invPr); err == nil || err.Error() != "MISSING_BYE" { t.Error(err) } - byePr.Values = []string{"BYE", "2daec40c", "548625ac", "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", "1436454410", "", "", "", "3401:2069362475"} // Took one value out + byePr.Values = []string{"BYE", "2daec40c", "548625ac", + "dd0c4c617a9919d29a6175cdff223a9e@0:0:0:0:0:0:0:0", "200", "OK", + "1436454410", "", "", "", "3401:2069362475"} // Took one value out if _, err := pairToRecord(invPr, byePr); err == nil || err.Error() != "INCONSISTENT_VALUES_LENGTH" { t.Error(err) } diff --git a/cdrc/partial_cdr_test.go b/cdrc/partial_cdr_test.go index a8582a0d4..e6d7b8b01 100644 --- a/cdrc/partial_cdr_test.go +++ b/cdrc/partial_cdr_test.go @@ -39,8 +39,10 @@ func TestPartialCDRRecordSort(t *testing.T) { func TestPartialCDRRecordMergeCDRs(t *testing.T) { cdr1 := &engine.CDR{OrderID: 1, ToR: utils.VOICE, - OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: "TestPartialCDRRecordMergeCDRs", RequestType: utils.META_RATED, Direction: "*out", - Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", Supplier: "SUPPL1", + OriginID: "dsafdsaf", OriginHost: "192.168.1.1", + Source: "TestPartialCDRRecordMergeCDRs", RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", + Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), Partial: true, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } @@ -55,9 +57,12 @@ func TestPartialCDRRecordMergeCDRs(t *testing.T) { } pCdr := &PartialCDRRecord{cdrs: []*engine.CDR{cdr1, cdr2, cdr3}} eCDR := &engine.CDR{OrderID: 3, ToR: utils.VOICE, - OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: "TestPartialCDRRecordMergeCDRs", RequestType: utils.META_RATED, Direction: "*out", - Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", Supplier: "SUPPL1", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 43, 0, 0, time.UTC), Partial: false, + OriginID: "dsafdsaf", OriginHost: "192.168.1.1", + Source: "TestPartialCDRRecordMergeCDRs", RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", + Subject: "1001", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 43, 0, 0, time.UTC), Partial: false, Usage: time.Duration(62 * time.Second), ExtraFields: map[string]string{"field_extr1": "val_extr11", "fieldextr2": "valextr2", "disconnect_direction": "upstream"}, } diff --git a/cdrc/xml.go b/cdrc/xml.go index 3ffbbbeda..31380dc23 100644 --- a/cdrc/xml.go +++ b/cdrc/xml.go @@ -91,7 +91,8 @@ func handlerSubstractUsage(xmlElmnt tree.Res, argsTpl utils.RSRFields, cdrPath u return tEnd.Sub(tStart), nil } -func NewXMLRecordsProcessor(recordsReader io.Reader, cdrPath utils.HierarchyPath, timezone string, httpSkipTlsCheck bool, cdrcCfgs []*config.CdrcConfig) (*XMLRecordsProcessor, error) { +func NewXMLRecordsProcessor(recordsReader io.Reader, cdrPath utils.HierarchyPath, timezone string, + httpSkipTlsCheck bool, cdrcCfgs []*config.CdrcConfig) (*XMLRecordsProcessor, error) { xp, err := goxpath.Parse(cdrPath.AsString("/", true)) if err != nil { return nil, err @@ -103,7 +104,8 @@ func NewXMLRecordsProcessor(recordsReader io.Reader, cdrPath utils.HierarchyPath if err != nil { return nil, err } - xmlProc := &XMLRecordsProcessor{cdrPath: cdrPath, timezone: timezone, httpSkipTlsCheck: httpSkipTlsCheck, cdrcCfgs: cdrcCfgs} + xmlProc := &XMLRecordsProcessor{cdrPath: cdrPath, timezone: timezone, + httpSkipTlsCheck: httpSkipTlsCheck, cdrcCfgs: cdrcCfgs} xmlProc.cdrXmlElmts = goxpath.MustExec(xp, xmlNode, nil) return xmlProc, nil } @@ -161,19 +163,19 @@ func (xmlProc *XMLRecordsProcessor) recordToCDR(xmlEntity tree.Res, cdrcCfg *con cdr := &engine.CDR{OriginHost: "0.0.0.0", Source: cdrcCfg.CdrSourceId, ExtraFields: make(map[string]string), Cost: -1} var lazyHttpFields []*config.CfgCdrField var err error + fldVals := make(map[string]string) for _, cdrFldCfg := range cdrcCfg.ContentFields { - var fieldVal string if cdrFldCfg.Type == utils.META_COMPOSED { for _, cfgFieldRSR := range cdrFldCfg.Value { if cfgFieldRSR.IsStatic() { - fieldVal += cfgFieldRSR.ParseValue("") + fldVals[cdrFldCfg.FieldId] += cfgFieldRSR.ParseValue("") } else { // Dynamic value extracted using path absolutePath := utils.ParseHierarchyPath(cfgFieldRSR.Id, "") relPath := utils.HierarchyPath(absolutePath[len(xmlProc.cdrPath)-1:]) // Need relative path to the xmlElmnt if elmntText, err := elementText(xmlEntity, relPath.AsString("/", true)); err != nil && err != utils.ErrNotFound { return nil, fmt.Errorf("Ignoring record: %v - cannot extract field %s, err: %s", xmlEntity, cdrFldCfg.Tag, err.Error()) } else { - fieldVal += cfgFieldRSR.ParseValue(elmntText) + fldVals[cdrFldCfg.FieldId] += cfgFieldRSR.ParseValue(elmntText) } } } @@ -184,11 +186,11 @@ func (xmlProc *XMLRecordsProcessor) recordToCDR(xmlEntity tree.Res, cdrcCfg *con if err != nil { return nil, fmt.Errorf("Ignoring record: %v - cannot extract field %s, err: %s", xmlEntity, cdrFldCfg.Tag, err.Error()) } - fieldVal += strconv.FormatFloat(usage.Seconds(), 'f', -1, 64) + fldVals[cdrFldCfg.FieldId] += strconv.FormatFloat(usage.Seconds(), 'f', -1, 64) } else { return nil, fmt.Errorf("Unsupported field type: %s", cdrFldCfg.Type) } - if err := cdr.ParseFieldValue(cdrFldCfg.FieldId, fieldVal, xmlProc.timezone); err != nil { + if err := cdr.ParseFieldValue(cdrFldCfg.FieldId, fldVals[cdrFldCfg.FieldId], xmlProc.timezone); err != nil { return nil, err } } diff --git a/cdrc/xml_test.go b/cdrc/xml_test.go index 57f1e4f43..da245fb69 100644 --- a/cdrc/xml_test.go +++ b/cdrc/xml_test.go @@ -221,8 +221,6 @@ func TestXMLRPProcess(t *testing.T) { Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>localCallId", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("^*rated", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, FieldId: utils.DIRECTION, - Value: utils.ParseRSRFieldsMustCompile("^*out", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("~broadWorksCDR>cdrData>basicModule>userId:s/.*@(.*)/${1}/", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, @@ -235,8 +233,12 @@ func TestXMLRPProcess(t *testing.T) { Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>startTime", utils.INFIELD_SEP), Mandatory: true}, &config.CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>answerTime", utils.INFIELD_SEP), Mandatory: true}, - &config.CfgCdrField{Tag: "Usage", Type: utils.META_HANDLER, FieldId: utils.USAGE, HandlerId: utils.HandlerSubstractUsage, - Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>releaseTime;^|;broadWorksCDR>cdrData>basicModule>answerTime", utils.INFIELD_SEP), Mandatory: true}, + &config.CfgCdrField{Tag: "Usage", Type: utils.META_HANDLER, + FieldId: utils.USAGE, HandlerId: utils.HandlerSubstractUsage, + Value: utils.ParseRSRFieldsMustCompile("broadWorksCDR>cdrData>basicModule>releaseTime;^|;broadWorksCDR>cdrData>basicModule>answerTime", + utils.INFIELD_SEP), Mandatory: true}, + &config.CfgCdrField{Tag: "UsageSeconds", Type: utils.META_COMPOSED, FieldId: utils.USAGE, + Value: utils.ParseRSRFieldsMustCompile("^s", utils.INFIELD_SEP), Mandatory: true}, }, }, } @@ -255,9 +257,13 @@ func TestXMLRPProcess(t *testing.T) { t.Error(err) } expectedCDRs := []*engine.CDR{ - &engine.CDR{CGRID: "1f045359a0784d15e051d7e41ae30132b139d714", OriginHost: "0.0.0.0", Source: "TestXML", OriginID: "25160047719:0", - ToR: "*voice", RequestType: "*rated", Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Destination: "+4986517174963", - SetupTime: time.Date(2016, 4, 19, 21, 0, 5, 247000000, time.UTC), AnswerTime: time.Date(2016, 4, 19, 21, 0, 6, 813000000, time.UTC), Usage: time.Duration(13483000000), + &engine.CDR{CGRID: "1f045359a0784d15e051d7e41ae30132b139d714", + OriginHost: "0.0.0.0", Source: "TestXML", OriginID: "25160047719:0", + ToR: "*voice", RequestType: "*rated", Tenant: "cgrates.org", + Category: "call", Account: "1001", Destination: "+4986517174963", + SetupTime: time.Date(2016, 4, 19, 21, 0, 5, 247000000, time.UTC), + AnswerTime: time.Date(2016, 4, 19, 21, 0, 6, 813000000, time.UTC), + Usage: time.Duration(13483000000), ExtraFields: map[string]string{}, Cost: -1}, } if !reflect.DeepEqual(expectedCDRs, cdrs) { diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index 163404d36..0a198cb95 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -61,6 +61,7 @@ var ( version = flag.Bool("version", false, "Prints the application version.") pidFile = flag.String("pid", "", "Write pid file") cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") + memprofile = flag.String("memprofile", "", "write memory profile to file") scheduledShutdown = flag.String("scheduled_shutdown", "", "shutdown the engine after this duration") singlecpu = flag.Bool("singlecpu", false, "Run on single CPU core") syslogger = flag.String("logger", "", "logger <*syslog|*stdout>") @@ -726,7 +727,7 @@ func main() { defer pprof.StopCPUProfile() } if *scheduledShutdown != "" { - shutdownDur, err := utils.ParseDurationWithSecs(*scheduledShutdown) + shutdownDur, err := utils.ParseDurationWithNanosecs(*scheduledShutdown) if err != nil { log.Fatal(err) } @@ -742,7 +743,8 @@ func main() { log.Fatalf("Could not parse config: <%s>", err.Error()) return } - config.SetCgrConfig(cfg) // Share the config object + config.SetCgrConfig(cfg) // Share the config object + cache.NewCache(cfg.CacheCfg()) // init cache // init syslog if err = initLogger(cfg); err != nil { log.Fatalf("Could not initialize syslog connection, err: <%s>", err.Error()) @@ -754,16 +756,14 @@ func main() { } utils.Logger.SetLogLevel(lgLevel) - // Init cache - cache.NewCache(cfg.CacheConfig) - var loadDb engine.LoadStorage var cdrDb engine.CdrStorage var dm *engine.DataManager - if cfg.RALsEnabled || cfg.CDRStatsEnabled || cfg.PubSubServerEnabled || cfg.AliasesServerEnabled || cfg.UserServerEnabled || cfg.SchedulerEnabled { + if cfg.RALsEnabled || cfg.CDRStatsEnabled || cfg.PubSubServerEnabled || + cfg.AliasesServerEnabled || cfg.UserServerEnabled || cfg.SchedulerEnabled { dm, err = engine.ConfigureDataStorage(cfg.DataDbType, cfg.DataDbHost, cfg.DataDbPort, - cfg.DataDbName, cfg.DataDbUser, cfg.DataDbPass, cfg.DBDataEncoding, cfg.CacheConfig, cfg.LoadHistorySize) + cfg.DataDbName, cfg.DataDbUser, cfg.DataDbPass, cfg.DBDataEncoding, cfg.CacheCfg(), cfg.LoadHistorySize) if err != nil { // Cannot configure getter database, show stopper utils.Logger.Crit(fmt.Sprintf("Could not configure dataDb: %s exiting!", err)) return @@ -777,7 +777,8 @@ func main() { } if cfg.RALsEnabled || cfg.CDRSEnabled || cfg.SchedulerEnabled { // Only connect to storDb if necessary storDb, err := engine.ConfigureStorStorage(cfg.StorDBType, cfg.StorDBHost, cfg.StorDBPort, - cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass, cfg.DBDataEncoding, cfg.StorDBMaxOpenConns, cfg.StorDBMaxIdleConns, cfg.StorDBConnMaxLifetime, cfg.StorDBCDRSIndexes) + cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass, cfg.DBDataEncoding, cfg.StorDBMaxOpenConns, + cfg.StorDBMaxIdleConns, cfg.StorDBConnMaxLifetime, cfg.StorDBCDRSIndexes) if err != nil { // Cannot configure logger database, show stopper utils.Logger.Crit(fmt.Sprintf("Could not configure logger database: %s exiting!", err)) return @@ -930,6 +931,18 @@ func main() { internalPubSubSChan, internalUserSChan, internalAliaseSChan, internalRsChan, internalStatSChan, internalSMGChan) <-exitChan + if *memprofile != "" { + f, err := os.Create(*memprofile) + if err != nil { + log.Fatal("could not create memory profile file: ", err) + } + defer f.Close() + runtime.GC() // get up-to-date statistics + if err := pprof.WriteHeapProfile(f); err != nil { + log.Fatal("could not write memory profile: ", err) + } + } + if *pidFile != "" { if err := os.Remove(*pidFile); err != nil { utils.Logger.Warning("Could not remove pid file: " + err.Error()) diff --git a/cmd/cgr-engine/rater.go b/cmd/cgr-engine/rater.go index 3a0c398a3..a2a9720a5 100755 --- a/cmd/cgr-engine/rater.go +++ b/cmd/cgr-engine/rater.go @@ -37,74 +37,74 @@ func startRater(internalRaterChan chan rpcclient.RpcClientConnection, cacheDoneC serviceManager *servmanager.ServiceManager, server *utils.Server, dm *engine.DataManager, loadDb engine.LoadStorage, cdrDb engine.CdrStorage, stopHandled *bool, exitChan chan bool) { var waitTasks []chan struct{} - + cacheCfg := cfg.CacheCfg() //Cache load cacheTaskChan := make(chan struct{}) waitTasks = append(waitTasks, cacheTaskChan) go func() { defer close(cacheTaskChan) var dstIDs, rvDstIDs, rplIDs, rpfIDs, actIDs, aplIDs, aapIDs, atrgIDs, sgIDs, lcrIDs, dcIDs, alsIDs, rvAlsIDs, rspIDs, resIDs, stqIDs, stqpIDs, thIDs, thpIDs, fltrIDs, lcrPrfIDs []string - if cCfg, has := cfg.CacheConfig[utils.CacheDestinations]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheDestinations]; !has || !cCfg.Precache { dstIDs = make([]string, 0) // Don't cache any } - if cCfg, has := cfg.CacheConfig[utils.CacheReverseDestinations]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheReverseDestinations]; !has || !cCfg.Precache { rvDstIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheRatingPlans]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheRatingPlans]; !has || !cCfg.Precache { rplIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheRatingProfiles]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheRatingProfiles]; !has || !cCfg.Precache { rpfIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheActions]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheActions]; !has || !cCfg.Precache { actIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheActionPlans]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheActionPlans]; !has || !cCfg.Precache { aplIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheAccountActionPlans]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheAccountActionPlans]; !has || !cCfg.Precache { aapIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheActionTriggers]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheActionTriggers]; !has || !cCfg.Precache { atrgIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheSharedGroups]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheSharedGroups]; !has || !cCfg.Precache { sgIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheLCRRules]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheLCRRules]; !has || !cCfg.Precache { lcrIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheDerivedChargers]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheDerivedChargers]; !has || !cCfg.Precache { dcIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheAliases]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheAliases]; !has || !cCfg.Precache { alsIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheReverseAliases]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheReverseAliases]; !has || !cCfg.Precache { rvAlsIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheResourceProfiles]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheResourceProfiles]; !has || !cCfg.Precache { rspIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheResources]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheResources]; !has || !cCfg.Precache { resIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheStatQueues]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheStatQueues]; !has || !cCfg.Precache { stqIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheStatQueueProfiles]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheStatQueueProfiles]; !has || !cCfg.Precache { stqpIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheThresholds]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheThresholds]; !has || !cCfg.Precache { thIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheThresholdProfiles]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheThresholdProfiles]; !has || !cCfg.Precache { thpIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheFilters]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheFilters]; !has || !cCfg.Precache { fltrIDs = make([]string, 0) } - if cCfg, has := cfg.CacheConfig[utils.CacheLCRProfiles]; !has || !cCfg.Precache { + if cCfg, has := cacheCfg[utils.CacheLCRProfiles]; !has || !cCfg.Precache { lcrPrfIDs = make([]string, 0) } @@ -232,7 +232,7 @@ func startRater(internalRaterChan chan rpcclient.RpcClientConnection, cacheDoneC for _, chn := range waitTasks { <-chn } - responder := &engine.Responder{ExitChan: exitChan} + responder := &engine.Responder{ExitChan: exitChan, MaxComputedUsage: cfg.RALsMaxComputedUsage} responder.SetTimeToLive(cfg.ResponseCacheTTL, nil) apierRpcV1 := &v1.ApierV1{StorDb: loadDb, DataManager: dm, CdrDb: cdrDb, Config: cfg, Responder: responder, ServManager: serviceManager, diff --git a/cmd/cgr-loader/cgr-loader.go b/cmd/cgr-loader/cgr-loader.go index 69325d674..d450c30d8 100755 --- a/cmd/cgr-loader/cgr-loader.go +++ b/cmd/cgr-loader/cgr-loader.go @@ -81,7 +81,8 @@ func main() { var rater, cdrstats, users rpcclient.RpcClientConnection var loader engine.LoadReader - dm, errDataDB = engine.ConfigureDataStorage(*datadb_type, *datadb_host, *datadb_port, *datadb_name, *datadb_user, *datadb_pass, *dbdata_encoding, config.CgrConfig().CacheConfig, *loadHistorySize) + dm, errDataDB = engine.ConfigureDataStorage(*datadb_type, *datadb_host, *datadb_port, *datadb_name, + *datadb_user, *datadb_pass, *dbdata_encoding, config.CgrConfig().CacheCfg(), *loadHistorySize) if *fromStorDb || *toStorDb { storDb, errStorDb = engine.ConfigureLoadStorage(*stor_db_type, *stor_db_host, *stor_db_port, *stor_db_name, *stor_db_user, *stor_db_pass, *dbdata_encoding, config.CgrConfig().StorDBMaxOpenConns, config.CgrConfig().StorDBMaxIdleConns, config.CgrConfig().StorDBConnMaxLifetime, config.CgrConfig().StorDBCDRSIndexes) diff --git a/cmd/cgr-migrator/cgr-migrator.go b/cmd/cgr-migrator/cgr-migrator.go index 07f97a76d..6b99f6a9b 100755 --- a/cmd/cgr-migrator/cgr-migrator.go +++ b/cmd/cgr-migrator/cgr-migrator.go @@ -66,7 +66,7 @@ var ( inStorDBUser = flag.String("stordb_user", config.CgrConfig().StorDBUser, "The storDb user to sign in as.") inStorDBPass = flag.String("stordb_passwd", config.CgrConfig().StorDBPass, "The storDb user's password.") - loadHistorySize = flag.Int("load_history_size", config.CgrConfig().LoadHistorySize, "Limit the number of records in the load history") + loadHistorySize = flag.Int("load_history_size", config.CgrConfig().LoadHistorySize, "Limit the number of records in the load history") dbDataEncoding = flag.String("dbData_encoding", config.CgrConfig().DBDataEncoding, "The encoding used to store object Data in strings") inDBDataEncoding = flag.String("in_dbData_encoding", "", "The encoding used to store object Data in strings") @@ -86,8 +86,9 @@ func main() { log.Print("Initializing DataDB:", *outDataDBType) log.Print("Initializing storDB:", *outStorDBType) } + var dmOUT *engine.DataManager - dmOUT, _ = engine.ConfigureDataStorage(*outDataDBType, *outDataDBHost, *outDataDBPort, *outDataDBName, *outDataDBUser, *outDataDBPass, *dbDataEncoding, config.CgrConfig().CacheConfig, *loadHistorySize) + dmOUT, _ = engine.ConfigureDataStorage(*outDataDBType, *outDataDBHost, *outDataDBPort, *outDataDBName, *outDataDBUser, *outDataDBPass, *dbDataEncoding, config.CgrConfig().CacheCfg(), *loadHistorySize) storDB, err := engine.ConfigureStorStorage(*outStorDBType, *outStorDBHost, *outStorDBPort, *outStorDBName, *outStorDBUser, *outStorDBPass, *dbDataEncoding, config.CgrConfig().StorDBMaxOpenConns, config.CgrConfig().StorDBMaxIdleConns, config.CgrConfig().StorDBConnMaxLifetime, config.CgrConfig().StorDBCDRSIndexes) if err != nil { @@ -105,7 +106,7 @@ func main() { log.Print("Initializing inDataDB:", *inDataDBType) } var dmIN *engine.DataManager - dmIN, _ = engine.ConfigureDataStorage(*inDataDBType, *inDataDBHost, *inDataDBPort, *inDataDBName, *inDataDBUser, *inDataDBPass, *dbDataEncoding, config.CgrConfig().CacheConfig, *loadHistorySize) + dmIN, _ = engine.ConfigureDataStorage(*inDataDBType, *inDataDBHost, *inDataDBPort, *inDataDBName, *inDataDBUser, *inDataDBPass, *dbDataEncoding, config.CgrConfig().CacheCfg(), *loadHistorySize) inDataDB, err := migrator.ConfigureV1DataStorage(*inDataDBType, *inDataDBHost, *inDataDBPort, *inDataDBName, *inDataDBUser, *inDataDBPass, *dbDataEncoding) if err != nil { log.Fatal(err) diff --git a/cmd/cgr-tester/cdr_repl/process_cdr.go b/cmd/cgr-tester/cdr_repl/process_cdr.go index 299e90ad8..36a45997e 100644 --- a/cmd/cgr-tester/cdr_repl/process_cdr.go +++ b/cmd/cgr-tester/cdr_repl/process_cdr.go @@ -52,7 +52,7 @@ func main() { for i := 0; i < 10000; i++ { cdr := &engine.CDR{OriginID: fmt.Sprintf("httpjsonrpc_%d", i), ToR: utils.VOICE, OriginHost: "192.168.1.1", Source: "UNKNOWN", RequestType: utils.META_PSEUDOPREPAID, - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}} cdrs = append(cdrs, cdr) diff --git a/cmd/cgr-tester/cgr-tester.go b/cmd/cgr-tester/cgr-tester.go index 82ab90eac..37cb96da0 100644 --- a/cmd/cgr-tester/cgr-tester.go +++ b/cmd/cgr-tester/cgr-tester.go @@ -61,7 +61,8 @@ var ( ) func durInternalRater(cd *engine.CallDescriptor) (time.Duration, error) { - dm, err := engine.ConfigureDataStorage(*datadb_type, *datadb_host, *datadb_port, *datadb_name, *datadb_user, *datadb_pass, *dbdata_encoding, cgrConfig.CacheConfig, *loadHistorySize) + dm, err := engine.ConfigureDataStorage(*datadb_type, *datadb_host, *datadb_port, + *datadb_name, *datadb_user, *datadb_pass, *dbdata_encoding, cgrConfig.CacheCfg(), *loadHistorySize) if err != nil { return nilDuration, fmt.Errorf("Could not connect to data database: %s", err.Error()) } @@ -156,7 +157,7 @@ func main() { var timeparsed time.Duration var err error tstart := time.Now().Local() - timeparsed, err = utils.ParseDurationWithSecs(*usage) + timeparsed, err = utils.ParseDurationWithNanosecs(*usage) tend := tstart.Add(timeparsed) cd := &engine.CallDescriptor{ TimeStart: tstart, diff --git a/config/cacheconfig.go b/config/cacheconfig.go index bbc8bc4e4..d31366a2c 100755 --- a/config/cacheconfig.go +++ b/config/cacheconfig.go @@ -40,7 +40,7 @@ func (self *CacheParamConfig) loadFromJsonCfg(jsnCfg *CacheParamJsonCfg) error { self.Limit = *jsnCfg.Limit } if jsnCfg.Ttl != nil { - if self.TTL, err = utils.ParseDurationWithSecs(*jsnCfg.Ttl); err != nil { + if self.TTL, err = utils.ParseDurationWithNanosecs(*jsnCfg.Ttl); err != nil { return err } } diff --git a/config/cdrcconfig.go b/config/cdrcconfig.go index bfb5db4f7..f9d8326fb 100644 --- a/config/cdrcconfig.go +++ b/config/cdrcconfig.go @@ -114,7 +114,7 @@ func (self *CdrcConfig) loadFromJsonCfg(jsnCfg *CdrcJsonCfg) error { self.ContinueOnSuccess = *jsnCfg.Continue_on_success } if jsnCfg.Partial_record_cache != nil { - if self.PartialRecordCache, err = utils.ParseDurationWithSecs(*jsnCfg.Partial_record_cache); err != nil { + if self.PartialRecordCache, err = utils.ParseDurationWithNanosecs(*jsnCfg.Partial_record_cache); err != nil { return err } } diff --git a/config/config.go b/config/config.go index 4038d6b33..59622dfa2 100755 --- a/config/config.go +++ b/config/config.go @@ -65,10 +65,11 @@ func SetCgrConfig(cfg *CGRConfig) { func NewDefaultCGRConfig() (*CGRConfig, error) { cfg := new(CGRConfig) + cfg.RALsMaxComputedUsage = make(map[string]time.Duration) cfg.InstanceID = utils.GenUUID() cfg.DataFolderPath = "/usr/share/cgrates/" cfg.SmGenericConfig = new(SmGenericConfig) - cfg.CacheConfig = make(CacheConfig) + cfg.cacheConfig = make(CacheConfig) cfg.SmFsConfig = new(SmFsConfig) cfg.SmKamConfig = new(SmKamConfig) cfg.SmOsipsConfig = new(SmOsipsConfig) @@ -201,7 +202,7 @@ type CGRConfig struct { StorDBConnMaxLifetime int StorDBCDRSIndexes []string DBDataEncoding string // The encoding used to store object data in strings: - CacheConfig CacheConfig + cacheConfig CacheConfig RPCJSONListen string // RPC JSON listening address RPCGOBListen string // RPC GOB listening address HTTPListen string // HTTP listening address @@ -238,6 +239,7 @@ type CGRConfig struct { RALsAliasSConns []*HaPoolConfig RpSubjectPrefixMatching bool // enables prefix matching for the rating profile subject LcrSubjectPrefixMatching bool // enables prefix matching for the lcr subject + RALsMaxComputedUsage map[string]time.Duration SchedulerEnabled bool CDRSEnabled bool // Enable CDR Server service CDRSExtraFields []*utils.RSRField // Extra fields to store in CDRs @@ -772,7 +774,7 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { self.ConnectAttempts = *jsnGeneralCfg.Connect_attempts } if jsnGeneralCfg.Response_cache_ttl != nil { - if self.ResponseCacheTTL, err = utils.ParseDurationWithSecs(*jsnGeneralCfg.Response_cache_ttl); err != nil { + if self.ResponseCacheTTL, err = utils.ParseDurationWithNanosecs(*jsnGeneralCfg.Response_cache_ttl); err != nil { return err } } @@ -780,12 +782,12 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { self.Reconnects = *jsnGeneralCfg.Reconnects } if jsnGeneralCfg.Connect_timeout != nil { - if self.ConnectTimeout, err = utils.ParseDurationWithSecs(*jsnGeneralCfg.Connect_timeout); err != nil { + if self.ConnectTimeout, err = utils.ParseDurationWithNanosecs(*jsnGeneralCfg.Connect_timeout); err != nil { return err } } if jsnGeneralCfg.Reply_timeout != nil { - if self.ReplyTimeout, err = utils.ParseDurationWithSecs(*jsnGeneralCfg.Reply_timeout); err != nil { + if self.ReplyTimeout, err = utils.ParseDurationWithNanosecs(*jsnGeneralCfg.Reply_timeout); err != nil { return err } } @@ -808,19 +810,19 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { self.DefaultTimezone = *jsnGeneralCfg.Default_timezone } if jsnGeneralCfg.Internal_ttl != nil { - if self.InternalTtl, err = utils.ParseDurationWithSecs(*jsnGeneralCfg.Internal_ttl); err != nil { + if self.InternalTtl, err = utils.ParseDurationWithNanosecs(*jsnGeneralCfg.Internal_ttl); err != nil { return err } } if jsnGeneralCfg.Locking_timeout != nil { - if self.LockingTimeout, err = utils.ParseDurationWithSecs(*jsnGeneralCfg.Locking_timeout); err != nil { + if self.LockingTimeout, err = utils.ParseDurationWithNanosecs(*jsnGeneralCfg.Locking_timeout); err != nil { return err } } } if jsnCacheCfg != nil { - if err := self.CacheConfig.loadFromJsonCfg(jsnCacheCfg); err != nil { + if err := self.cacheConfig.loadFromJsonCfg(jsnCacheCfg); err != nil { return err } } @@ -917,6 +919,13 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { if jsnRALsCfg.Lcr_subject_prefix_matching != nil { self.LcrSubjectPrefixMatching = *jsnRALsCfg.Lcr_subject_prefix_matching } + if jsnRALsCfg.Max_computed_usage != nil { + for k, v := range *jsnRALsCfg.Max_computed_usage { + if self.RALsMaxComputedUsage[k], err = utils.ParseDurationWithNanosecs(v); err != nil { + return + } + } + } } if jsnSchedCfg != nil && jsnSchedCfg.Enabled != nil { self.SchedulerEnabled = *jsnSchedCfg.Enabled @@ -933,9 +942,6 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { if jsnCdrsCfg.Store_cdrs != nil { self.CDRSStoreCdrs = *jsnCdrsCfg.Store_cdrs } - if jsnCdrsCfg.Cdr_account_summary != nil { - self.CDRScdrAccountSummary = *jsnCdrsCfg.Cdr_account_summary - } if jsnCdrsCfg.Sm_cost_retries != nil { self.CDRSSMCostRetries = *jsnCdrsCfg.Sm_cost_retries } @@ -999,7 +1005,7 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { if jsnCdrstatsCfg.Enabled != nil { self.CDRStatsEnabled = *jsnCdrstatsCfg.Enabled if jsnCdrstatsCfg.Save_Interval != nil { - if self.CDRStatsSaveInterval, err = utils.ParseDurationWithSecs(*jsnCdrstatsCfg.Save_Interval); err != nil { + if self.CDRStatsSaveInterval, err = utils.ParseDurationWithNanosecs(*jsnCdrstatsCfg.Save_Interval); err != nil { return err } } @@ -1116,7 +1122,7 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) (err error) { self.HistoryDir = *jsnHistServCfg.History_dir } if jsnHistServCfg.Save_interval != nil { - if self.HistorySaveInterval, err = utils.ParseDurationWithSecs(*jsnHistServCfg.Save_interval); err != nil { + if self.HistorySaveInterval, err = utils.ParseDurationWithNanosecs(*jsnHistServCfg.Save_interval); err != nil { return err } } @@ -1238,3 +1244,7 @@ func (self *CGRConfig) SMAsteriskCfg() *SMAsteriskCfg { func (cfg *CGRConfig) FilterSCfg() *FilterSCfg { return cfg.filterSCfg } + +func (cfg *CGRConfig) CacheCfg() CacheConfig { + return cfg.cacheConfig +} diff --git a/config/config_defaults.go b/config/config_defaults.go index 66e9eabbd..26f5474c2 100755 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -139,7 +139,13 @@ const CGRATES_CFG_JSON = ` "users_conns": [], // address where to reach the user service, empty to disable user profile functionality: <""|*internal|x.y.z.y:1234> "aliases_conns": [], // 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 - "lcr_subject_prefix_matching": false // enables prefix matching for the lcr subject + "lcr_subject_prefix_matching": false, // enables prefix matching for the lcr subject + "max_computed_usage": { // do not compute usage higher than this, prevents memory overload + "*any": "189h", + "*voice": "72h", + "*data": "107374182400", + "*sms": "10000" + }, }, @@ -147,7 +153,6 @@ const CGRATES_CFG_JSON = ` "enabled": false, // start the CDR Server service: "extra_fields": [], // extra fields to store in CDRs for non-generic CDRs "store_cdrs": true, // store cdrs in storDb - "cdr_account_summary": false, // add account information from dataDB "sm_cost_retries": 5, // number of queries to sm_costs before recalculating CDR "rals_conns": [ {"address": "*internal"} // address where to reach the Rater for cost calculation, empty to disable functionality: <""|*internal|x.y.z.y:1234> @@ -181,7 +186,6 @@ const CGRATES_CFG_JSON = ` {"tag":"TOR", "type": "*composed", "value": "ToR"}, {"tag":"OriginID", "type": "*composed", "value": "OriginID"}, {"tag":"RequestType", "type": "*composed", "value": "RequestType"}, - {"tag":"Direction", "type": "*composed", "value": "Direction"}, {"tag":"Tenant", "type": "*composed", "value": "Tenant"}, {"tag":"Category", "type": "*composed", "value": "Category"}, {"tag":"Account", "type": "*composed", "value": "Account"}, @@ -231,7 +235,6 @@ const CGRATES_CFG_JSON = ` {"tag": "TOR", "field_id": "ToR", "type": "*composed", "value": "2", "mandatory": true}, {"tag": "OriginID", "field_id": "OriginID", "type": "*composed", "value": "3", "mandatory": true}, {"tag": "RequestType", "field_id": "RequestType", "type": "*composed", "value": "4", "mandatory": true}, - {"tag": "Direction", "field_id": "Direction", "type": "*composed", "value": "5", "mandatory": true}, {"tag": "Tenant", "field_id": "Tenant", "type": "*composed", "value": "6", "mandatory": true}, {"tag": "Category", "field_id": "Category", "type": "*composed", "value": "7", "mandatory": true}, {"tag": "Account", "field_id": "Account", "type": "*composed", "value": "8", "mandatory": true}, @@ -248,7 +251,6 @@ const CGRATES_CFG_JSON = ` {"tag": "TOR", "type": "*composed", "value": "ToR"}, {"tag": "OriginID", "type": "*composed", "value": "OriginID"}, {"tag": "RequestType", "type": "*composed", "value": "RequestType"}, - {"tag": "Direction", "type": "*composed", "value": "Direction"}, {"tag": "Tenant", "type": "*composed", "value": "Tenant"}, {"tag": "Category", "type": "*composed", "value": "Category"}, {"tag": "Account", "type": "*composed", "value": "Account"}, diff --git a/config/config_json_test.go b/config/config_json_test.go index 5851078a6..76b58aaa2 100755 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -198,11 +198,23 @@ func TestDfStorDBJsonCfg(t *testing.T) { } func TestDfRalsJsonCfg(t *testing.T) { - eCfg := &RalsJsonCfg{Enabled: utils.BoolPointer(false), Thresholds_conns: &[]*HaPoolJsonCfg{}, - Cdrstats_conns: &[]*HaPoolJsonCfg{}, Stats_conns: &[]*HaPoolJsonCfg{}, - Historys_conns: &[]*HaPoolJsonCfg{}, Pubsubs_conns: &[]*HaPoolJsonCfg{}, - Users_conns: &[]*HaPoolJsonCfg{}, Aliases_conns: &[]*HaPoolJsonCfg{}, - Rp_subject_prefix_matching: utils.BoolPointer(false), Lcr_subject_prefix_matching: utils.BoolPointer(false)} + eCfg := &RalsJsonCfg{ + Enabled: utils.BoolPointer(false), + Thresholds_conns: &[]*HaPoolJsonCfg{}, + Cdrstats_conns: &[]*HaPoolJsonCfg{}, + Stats_conns: &[]*HaPoolJsonCfg{}, + Historys_conns: &[]*HaPoolJsonCfg{}, + Pubsubs_conns: &[]*HaPoolJsonCfg{}, + Users_conns: &[]*HaPoolJsonCfg{}, + Aliases_conns: &[]*HaPoolJsonCfg{}, + Rp_subject_prefix_matching: utils.BoolPointer(false), + Lcr_subject_prefix_matching: utils.BoolPointer(false), + Max_computed_usage: &map[string]string{ + utils.ANY: "189h", + utils.VOICE: "72h", + utils.DATA: "107374182400", + utils.SMS: "10000"}, + } if cfg, err := dfCgrJsonCfg.RalsJsonCfg(); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCfg, cfg) { @@ -221,11 +233,10 @@ func TestDfSchedulerJsonCfg(t *testing.T) { func TestDfCdrsJsonCfg(t *testing.T) { eCfg := &CdrsJsonCfg{ - Enabled: utils.BoolPointer(false), - Extra_fields: utils.StringSlicePointer([]string{}), - Store_cdrs: utils.BoolPointer(true), - Cdr_account_summary: utils.BoolPointer(false), - Sm_cost_retries: utils.IntPointer(5), + Enabled: utils.BoolPointer(false), + Extra_fields: utils.StringSlicePointer([]string{}), + Store_cdrs: utils.BoolPointer(true), + Sm_cost_retries: utils.IntPointer(5), Rals_conns: &[]*HaPoolJsonCfg{ &HaPoolJsonCfg{ Address: utils.StringPointer("*internal"), @@ -275,9 +286,6 @@ func TestDfCdreJsonCfgs(t *testing.T) { &CdrFieldJsonCfg{Tag: utils.StringPointer("RequestType"), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer(utils.REQTYPE)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Direction"), - Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.DIRECTION)}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Tenant"), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer(utils.TENANT)}, @@ -342,8 +350,6 @@ func TestDfCdrcJsonCfg(t *testing.T) { Value: utils.StringPointer("3"), Mandatory: utils.BoolPointer(true)}, &CdrFieldJsonCfg{Tag: utils.StringPointer("RequestType"), Field_id: utils.StringPointer(utils.REQTYPE), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer("4"), Mandatory: utils.BoolPointer(true)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Direction"), Field_id: utils.StringPointer(utils.DIRECTION), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer("5"), Mandatory: utils.BoolPointer(true)}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Tenant"), Field_id: utils.StringPointer(utils.TENANT), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer("6"), Mandatory: utils.BoolPointer(true)}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Category"), Field_id: utils.StringPointer(utils.CATEGORY), Type: utils.StringPointer(utils.META_COMPOSED), @@ -377,9 +383,6 @@ func TestDfCdrcJsonCfg(t *testing.T) { &CdrFieldJsonCfg{Tag: utils.StringPointer("RequestType"), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer(utils.REQTYPE)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Direction"), - Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.DIRECTION)}, &CdrFieldJsonCfg{Tag: utils.StringPointer("Tenant"), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer(utils.TENANT)}, diff --git a/config/config_test.go b/config/config_test.go index 13dc02d60..6860e8a59 100755 --- a/config/config_test.go +++ b/config/config_test.go @@ -103,27 +103,45 @@ func TestCgrCfgCDRC(t *testing.T) { PartialCacheExpiryAction: "*dump_to_file", HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ - &CfgCdrField{FieldId: "ToR", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("~7:s/^(voice|data|sms|mms|generic)$/*$1/", utils.INFIELD_SEP)}, - &CfgCdrField{FieldId: "AnswerTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("1", utils.INFIELD_SEP)}, - &CfgCdrField{FieldId: "Usage", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("~9:s/^(\\d+)$/${1}s/", utils.INFIELD_SEP)}, + &CfgCdrField{FieldId: "ToR", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile("~7:s/^(voice|data|sms|mms|generic)$/*$1/", utils.INFIELD_SEP)}, + &CfgCdrField{FieldId: "AnswerTime", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile("1", utils.INFIELD_SEP)}, + &CfgCdrField{FieldId: "Usage", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile("~9:s/^(\\d+)$/${1}s/", utils.INFIELD_SEP)}, }, TrailerFields: make([]*CfgCdrField, 0), CacheDumpFields: []*CfgCdrField{ - &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.MEDI_RUNID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TOR, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.REQTYPE, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DIRECTION, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CATEGORY, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SUBJECT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DESTINATION, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SETUP_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ANSWER_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.USAGE, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.MEDI_RUNID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.TOR, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ACCID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.REQTYPE, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.CATEGORY, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.SUBJECT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.DESTINATION, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.SETUP_TIME, utils.INFIELD_SEP), + Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ANSWER_TIME, utils.INFIELD_SEP), + Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.USAGE, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, }, }, } @@ -300,6 +318,15 @@ func TestCgrCfgJSONDefaultsRALs(t *testing.T) { if cgrCfg.LcrSubjectPrefixMatching != false { t.Error(cgrCfg.LcrSubjectPrefixMatching) } + eMaxCU := map[string]time.Duration{ + utils.ANY: time.Duration(189 * time.Hour), + utils.VOICE: time.Duration(72 * time.Hour), + utils.DATA: time.Duration(107374182400), + utils.SMS: time.Duration(10000), + } + if !reflect.DeepEqual(eMaxCU, cgrCfg.RALsMaxComputedUsage) { + t.Errorf("Expecting: %+v, received: %+v", eMaxCU, cgrCfg.RALsMaxComputedUsage) + } } func TestCgrCfgJSONDefaultsScheduler(t *testing.T) { @@ -364,21 +391,37 @@ func TestCgrCfgJSONDefaultsCDRStats(t *testing.T) { func TestCgrCfgJSONDefaultsCdreProfiles(t *testing.T) { eFields := []*CfgCdrField{} eContentFlds := []*CfgCdrField{ - &CfgCdrField{Tag: "CGRID", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("CGRID", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RunID", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("RunID", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "TOR", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("ToR", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "OriginID", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("OriginID", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RequestType", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("RequestType", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Direction", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Direction", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Tenant", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Tenant", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Category", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Category", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Account", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Account", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Subject", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Subject", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Destination", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Destination", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "SetupTime", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("SetupTime", utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "AnswerTime", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("AnswerTime", utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "Usage", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Usage", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Cost", Type: "*composed", Value: utils.ParseRSRFieldsMustCompile("Cost", utils.INFIELD_SEP), RoundingDecimals: 4}, + &CfgCdrField{Tag: "CGRID", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("CGRID", utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RunID", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("RunID", utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "TOR", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("ToR", utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "OriginID", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("OriginID", utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RequestType", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("RequestType", utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Tenant", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("Tenant", utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Category", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("Category", utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Account", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("Account", utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Subject", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("Subject", utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Destination", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("Destination", utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "SetupTime", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("SetupTime", utils.INFIELD_SEP), + Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "AnswerTime", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("AnswerTime", utils.INFIELD_SEP), + Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "Usage", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("Usage", utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Cost", Type: "*composed", + Value: utils.ParseRSRFieldsMustCompile("Cost", utils.INFIELD_SEP), + RoundingDecimals: 4}, } eCdreCfg := map[string]*CdreConfig{ "*default": { @@ -468,8 +511,9 @@ func TestCgrCfgJSONDefaultsCacheCFG(t *testing.T) { TTL: time.Duration(0), StaticTTL: false, Precache: false}, utils.CacheLCRProfiles: &CacheParamConfig{Limit: -1, TTL: time.Duration(0), StaticTTL: false, Precache: false}} - if !reflect.DeepEqual(eCacheCfg, cgrCfg.CacheConfig) { - t.Errorf("received: %s, \nexpecting: %s", utils.ToJSON(eCacheCfg), utils.ToJSON(cgrCfg.CacheConfig)) + if !reflect.DeepEqual(eCacheCfg, cgrCfg.CacheCfg()) { + t.Errorf("received: %s, \nexpecting: %s", + utils.ToJSON(eCacheCfg), utils.ToJSON(cgrCfg.CacheCfg())) } } diff --git a/config/configcdrc_test.go b/config/configcdrc_test.go index caf7e5a52..ad69e2655 100644 --- a/config/configcdrc_test.go +++ b/config/configcdrc_test.go @@ -53,48 +53,81 @@ func TestLoadCdrcConfigMultipleFiles(t *testing.T) { PartialCacheExpiryAction: utils.MetaDumpToFile, HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ - &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, FieldId: utils.ACCID, Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, FieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, FieldId: utils.TOR, + Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, FieldId: utils.ACCID, + Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, + Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, + Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, + Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, + Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, + Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, + Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, + Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, + Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, + Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, }, TrailerFields: make([]*CfgCdrField, 0), CacheDumpFields: []*CfgCdrField{ - &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.MEDI_RUNID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TOR, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.REQTYPE, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DIRECTION, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CATEGORY, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SUBJECT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DESTINATION, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SETUP_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ANSWER_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.USAGE, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.MEDI_RUNID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.TOR, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ACCID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.REQTYPE, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.CATEGORY, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.SUBJECT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.DESTINATION, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.SETUP_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ANSWER_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.USAGE, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, }, }, } @@ -117,48 +150,81 @@ func TestLoadCdrcConfigMultipleFiles(t *testing.T) { PartialCacheExpiryAction: utils.MetaDumpToFile, HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ - &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, FieldId: utils.ACCID, Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, FieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, FieldId: utils.TOR, + Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, FieldId: utils.ACCID, + Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, + Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, + Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, + Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, + Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, + Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, + Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, + Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, + Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, + Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, }, TrailerFields: make([]*CfgCdrField, 0), CacheDumpFields: []*CfgCdrField{ - &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.MEDI_RUNID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TOR, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.REQTYPE, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DIRECTION, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CATEGORY, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SUBJECT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DESTINATION, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SETUP_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ANSWER_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.USAGE, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.MEDI_RUNID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.TOR, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ACCID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.REQTYPE, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.CATEGORY, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.SUBJECT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.DESTINATION, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.SETUP_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ANSWER_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.USAGE, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, }, }, } @@ -181,30 +247,51 @@ func TestLoadCdrcConfigMultipleFiles(t *testing.T) { PartialCacheExpiryAction: utils.MetaDumpToFile, HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ - &CfgCdrField{FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("~7:s/^(voice|data|sms|mms|generic)$/*$1/", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false}, - &CfgCdrField{Tag: "", Type: "", FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("1", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false}, - &CfgCdrField{FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("~9:s/^(\\d+)$/${1}s/", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false}, + &CfgCdrField{FieldId: utils.TOR, + Value: utils.ParseRSRFieldsMustCompile("~7:s/^(voice|data|sms|mms|generic)$/*$1/", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false}, + &CfgCdrField{Tag: "", Type: "", FieldId: utils.ANSWER_TIME, + Value: utils.ParseRSRFieldsMustCompile("1", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false}, + &CfgCdrField{FieldId: utils.USAGE, + Value: utils.ParseRSRFieldsMustCompile("~9:s/^(\\d+)$/${1}s/", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: false}, }, TrailerFields: make([]*CfgCdrField, 0), CacheDumpFields: []*CfgCdrField{ - &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.MEDI_RUNID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TOR, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.REQTYPE, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DIRECTION, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CATEGORY, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SUBJECT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DESTINATION, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SETUP_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ANSWER_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.USAGE, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.MEDI_RUNID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.TOR, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ACCID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.REQTYPE, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.CATEGORY, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.SUBJECT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.DESTINATION, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.SETUP_TIME, utils.INFIELD_SEP), + Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ANSWER_TIME, utils.INFIELD_SEP), + Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.USAGE, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, }, }, } @@ -227,52 +314,88 @@ func TestLoadCdrcConfigMultipleFiles(t *testing.T) { PartialCacheExpiryAction: utils.MetaDumpToFile, HeaderFields: make([]*CfgCdrField, 0), ContentFields: []*CfgCdrField{ - &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, FieldId: utils.TOR, Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, FieldId: utils.ACCID, Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, FieldId: utils.DIRECTION, Value: utils.ParseRSRFieldsMustCompile("5", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, - &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), - FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, FieldId: utils.TOR, + Value: utils.ParseRSRFieldsMustCompile("2", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, FieldId: utils.ACCID, + Value: utils.ParseRSRFieldsMustCompile("3", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, FieldId: utils.REQTYPE, + Value: utils.ParseRSRFieldsMustCompile("4", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, FieldId: utils.TENANT, + Value: utils.ParseRSRFieldsMustCompile("6", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, FieldId: utils.CATEGORY, + Value: utils.ParseRSRFieldsMustCompile("7", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, FieldId: utils.ACCOUNT, + Value: utils.ParseRSRFieldsMustCompile("8", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, FieldId: utils.SUBJECT, + Value: utils.ParseRSRFieldsMustCompile("9", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, FieldId: utils.DESTINATION, + Value: utils.ParseRSRFieldsMustCompile("10", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, FieldId: utils.SETUP_TIME, + Value: utils.ParseRSRFieldsMustCompile("11", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, FieldId: utils.ANSWER_TIME, + Value: utils.ParseRSRFieldsMustCompile("12", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, + &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, FieldId: utils.USAGE, + Value: utils.ParseRSRFieldsMustCompile("13", utils.INFIELD_SEP), + FieldFilter: utils.ParseRSRFieldsMustCompile("", utils.INFIELD_SEP), + Width: 0, Strip: "", Padding: "", Layout: "", Mandatory: true}, }, TrailerFields: make([]*CfgCdrField, 0), CacheDumpFields: []*CfgCdrField{ - &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.MEDI_RUNID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TOR, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.REQTYPE, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Direction", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DIRECTION, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CATEGORY, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SUBJECT, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.DESTINATION, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.SETUP_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.ANSWER_TIME, utils.INFIELD_SEP), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.USAGE, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.MEDI_RUNID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.TOR, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ACCID, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.REQTYPE, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.CATEGORY, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.SUBJECT, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.DESTINATION, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.SETUP_TIME, utils.INFIELD_SEP), + Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.ANSWER_TIME, utils.INFIELD_SEP), + Layout: "2006-01-02T15:04:05Z07:00"}, + &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.USAGE, utils.INFIELD_SEP)}, + &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, + Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, }, }, } if !reflect.DeepEqual(eCgrCfg.CdrcProfiles, cgrCfg.CdrcProfiles) { - t.Errorf("Expected: \n%s\n, received: \n%s\n", utils.ToJSON(eCgrCfg.CdrcProfiles), utils.ToJSON(cgrCfg.CdrcProfiles)) + t.Errorf("Expected: \n%s\n, received: \n%s\n", + utils.ToJSON(eCgrCfg.CdrcProfiles), utils.ToJSON(cgrCfg.CdrcProfiles)) } } diff --git a/config/daconfig.go b/config/daconfig.go index 18a1e765a..92b8d2e88 100644 --- a/config/daconfig.go +++ b/config/daconfig.go @@ -76,7 +76,7 @@ func (self *DiameterAgentCfg) loadFromJsonCfg(jsnCfg *DiameterAgentJsonCfg) erro } if jsnCfg.Debit_interval != nil { var err error - if self.DebitInterval, err = utils.ParseDurationWithSecs(*jsnCfg.Debit_interval); err != nil { + if self.DebitInterval, err = utils.ParseDurationWithNanosecs(*jsnCfg.Debit_interval); err != nil { return err } } diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 5e464ecfc..ec9046d5a 100755 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -89,6 +89,7 @@ type RalsJsonCfg struct { Users_conns *[]*HaPoolJsonCfg Rp_subject_prefix_matching *bool Lcr_subject_prefix_matching *bool + Max_computed_usage *map[string]string } // Scheduler config section @@ -98,19 +99,18 @@ type SchedulerJsonCfg struct { // Cdrs config section type CdrsJsonCfg struct { - Enabled *bool - Extra_fields *[]string - Store_cdrs *bool - Cdr_account_summary *bool - Sm_cost_retries *int - Rals_conns *[]*HaPoolJsonCfg - Pubsubs_conns *[]*HaPoolJsonCfg - Users_conns *[]*HaPoolJsonCfg - Aliases_conns *[]*HaPoolJsonCfg - Cdrstats_conns *[]*HaPoolJsonCfg - Thresholds_conns *[]*HaPoolJsonCfg - Stats_conns *[]*HaPoolJsonCfg - Online_cdr_exports *[]string + Enabled *bool + Extra_fields *[]string + Store_cdrs *bool + Sm_cost_retries *int + Rals_conns *[]*HaPoolJsonCfg + Pubsubs_conns *[]*HaPoolJsonCfg + Users_conns *[]*HaPoolJsonCfg + Aliases_conns *[]*HaPoolJsonCfg + Cdrstats_conns *[]*HaPoolJsonCfg + Thresholds_conns *[]*HaPoolJsonCfg + Stats_conns *[]*HaPoolJsonCfg + Online_cdr_exports *[]string } type CdrReplicationJsonCfg struct { diff --git a/config/reslimitercfg.go b/config/reslimitercfg.go index 09b988903..3ed767136 100644 --- a/config/reslimitercfg.go +++ b/config/reslimitercfg.go @@ -46,7 +46,7 @@ func (rlcfg *ResourceSConfig) loadFromJsonCfg(jsnCfg *ResourceSJsonCfg) (err err } } if jsnCfg.Store_interval != nil { - if rlcfg.StoreInterval, err = utils.ParseDurationWithSecs(*jsnCfg.Store_interval); err != nil { + if rlcfg.StoreInterval, err = utils.ParseDurationWithNanosecs(*jsnCfg.Store_interval); err != nil { return } } diff --git a/config/smconfig.go b/config/smconfig.go index a3ab87d76..af7d29527 100644 --- a/config/smconfig.go +++ b/config/smconfig.go @@ -137,34 +137,34 @@ func (self *SmGenericConfig) loadFromJsonCfg(jsnCfg *SmGenericJsonCfg) error { } } if jsnCfg.Debit_interval != nil { - if self.DebitInterval, err = utils.ParseDurationWithSecs(*jsnCfg.Debit_interval); err != nil { + if self.DebitInterval, err = utils.ParseDurationWithNanosecs(*jsnCfg.Debit_interval); err != nil { return err } } if jsnCfg.Min_call_duration != nil { - if self.MinCallDuration, err = utils.ParseDurationWithSecs(*jsnCfg.Min_call_duration); err != nil { + if self.MinCallDuration, err = utils.ParseDurationWithNanosecs(*jsnCfg.Min_call_duration); err != nil { return err } } if jsnCfg.Max_call_duration != nil { - if self.MaxCallDuration, err = utils.ParseDurationWithSecs(*jsnCfg.Max_call_duration); err != nil { + if self.MaxCallDuration, err = utils.ParseDurationWithNanosecs(*jsnCfg.Max_call_duration); err != nil { return err } } if jsnCfg.Session_ttl != nil { - if self.SessionTTL, err = utils.ParseDurationWithSecs(*jsnCfg.Session_ttl); err != nil { + if self.SessionTTL, err = utils.ParseDurationWithNanosecs(*jsnCfg.Session_ttl); err != nil { return err } } if jsnCfg.Session_ttl_max_delay != nil { - if maxTTLDelay, err := utils.ParseDurationWithSecs(*jsnCfg.Session_ttl_max_delay); err != nil { + if maxTTLDelay, err := utils.ParseDurationWithNanosecs(*jsnCfg.Session_ttl_max_delay); err != nil { return err } else { self.SessionTTLMaxDelay = &maxTTLDelay } } if jsnCfg.Session_ttl_last_used != nil { - if sessionTTLLastUsed, err := utils.ParseDurationWithSecs(*jsnCfg.Session_ttl_last_used); err != nil { + if sessionTTLLastUsed, err := utils.ParseDurationWithNanosecs(*jsnCfg.Session_ttl_last_used); err != nil { return err } else { self.SessionTTLLastUsed = &sessionTTLLastUsed @@ -234,22 +234,22 @@ func (self *SmFsConfig) loadFromJsonCfg(jsnCfg *SmFsJsonCfg) error { } } if jsnCfg.Debit_interval != nil { - if self.DebitInterval, err = utils.ParseDurationWithSecs(*jsnCfg.Debit_interval); err != nil { + if self.DebitInterval, err = utils.ParseDurationWithNanosecs(*jsnCfg.Debit_interval); err != nil { return err } } if jsnCfg.Min_call_duration != nil { - if self.MinCallDuration, err = utils.ParseDurationWithSecs(*jsnCfg.Min_call_duration); err != nil { + if self.MinCallDuration, err = utils.ParseDurationWithNanosecs(*jsnCfg.Min_call_duration); err != nil { return err } } if jsnCfg.Max_call_duration != nil { - if self.MaxCallDuration, err = utils.ParseDurationWithSecs(*jsnCfg.Max_call_duration); err != nil { + if self.MaxCallDuration, err = utils.ParseDurationWithNanosecs(*jsnCfg.Max_call_duration); err != nil { return err } } if jsnCfg.Min_dur_low_balance != nil { - if self.MinDurLowBalance, err = utils.ParseDurationWithSecs(*jsnCfg.Min_dur_low_balance); err != nil { + if self.MinDurLowBalance, err = utils.ParseDurationWithNanosecs(*jsnCfg.Min_dur_low_balance); err != nil { return err } } @@ -266,12 +266,12 @@ func (self *SmFsConfig) loadFromJsonCfg(jsnCfg *SmFsJsonCfg) error { self.SubscribePark = *jsnCfg.Subscribe_park } if jsnCfg.Channel_sync_interval != nil { - if self.ChannelSyncInterval, err = utils.ParseDurationWithSecs(*jsnCfg.Channel_sync_interval); err != nil { + if self.ChannelSyncInterval, err = utils.ParseDurationWithNanosecs(*jsnCfg.Channel_sync_interval); err != nil { return err } } if jsnCfg.Max_wait_connection != nil { - if self.MaxWaitConnection, err = utils.ParseDurationWithSecs(*jsnCfg.Max_wait_connection); err != nil { + if self.MaxWaitConnection, err = utils.ParseDurationWithNanosecs(*jsnCfg.Max_wait_connection); err != nil { return err } } @@ -359,17 +359,17 @@ func (self *SmKamConfig) loadFromJsonCfg(jsnCfg *SmKamJsonCfg) error { self.CreateCdr = *jsnCfg.Create_cdr } if jsnCfg.Debit_interval != nil { - if self.DebitInterval, err = utils.ParseDurationWithSecs(*jsnCfg.Debit_interval); err != nil { + if self.DebitInterval, err = utils.ParseDurationWithNanosecs(*jsnCfg.Debit_interval); err != nil { return err } } if jsnCfg.Min_call_duration != nil { - if self.MinCallDuration, err = utils.ParseDurationWithSecs(*jsnCfg.Min_call_duration); err != nil { + if self.MinCallDuration, err = utils.ParseDurationWithNanosecs(*jsnCfg.Min_call_duration); err != nil { return err } } if jsnCfg.Max_call_duration != nil { - if self.MaxCallDuration, err = utils.ParseDurationWithSecs(*jsnCfg.Max_call_duration); err != nil { + if self.MaxCallDuration, err = utils.ParseDurationWithNanosecs(*jsnCfg.Max_call_duration); err != nil { return err } } @@ -439,22 +439,22 @@ func (self *SmOsipsConfig) loadFromJsonCfg(jsnCfg *SmOsipsJsonCfg) error { self.CreateCdr = *jsnCfg.Create_cdr } if jsnCfg.Debit_interval != nil { - if self.DebitInterval, err = utils.ParseDurationWithSecs(*jsnCfg.Debit_interval); err != nil { + if self.DebitInterval, err = utils.ParseDurationWithNanosecs(*jsnCfg.Debit_interval); err != nil { return err } } if jsnCfg.Min_call_duration != nil { - if self.MinCallDuration, err = utils.ParseDurationWithSecs(*jsnCfg.Min_call_duration); err != nil { + if self.MinCallDuration, err = utils.ParseDurationWithNanosecs(*jsnCfg.Min_call_duration); err != nil { return err } } if jsnCfg.Max_call_duration != nil { - if self.MaxCallDuration, err = utils.ParseDurationWithSecs(*jsnCfg.Max_call_duration); err != nil { + if self.MaxCallDuration, err = utils.ParseDurationWithNanosecs(*jsnCfg.Max_call_duration); err != nil { return err } } if jsnCfg.Events_subscribe_interval != nil { - if self.EventsSubscribeInterval, err = utils.ParseDurationWithSecs(*jsnCfg.Events_subscribe_interval); err != nil { + if self.EventsSubscribeInterval, err = utils.ParseDurationWithNanosecs(*jsnCfg.Events_subscribe_interval); err != nil { return err } } diff --git a/config/statscfg.go b/config/statscfg.go index 4c078188b..2863d0967 100644 --- a/config/statscfg.go +++ b/config/statscfg.go @@ -39,7 +39,7 @@ func (st *StatSCfg) loadFromJsonCfg(jsnCfg *StatServJsonCfg) (err error) { st.Enabled = *jsnCfg.Enabled } if jsnCfg.Store_interval != nil { - if st.StoreInterval, err = utils.ParseDurationWithSecs(*jsnCfg.Store_interval); err != nil { + if st.StoreInterval, err = utils.ParseDurationWithNanosecs(*jsnCfg.Store_interval); err != nil { return err } } diff --git a/config/thresholdscfg.go b/config/thresholdscfg.go index 625dd2bcc..257d7b9e0 100644 --- a/config/thresholdscfg.go +++ b/config/thresholdscfg.go @@ -38,7 +38,7 @@ func (t *ThresholdSCfg) loadFromJsonCfg(jsnCfg *ThresholdSJsonCfg) (err error) { t.Enabled = *jsnCfg.Enabled } if jsnCfg.Store_interval != nil { - if t.StoreInterval, err = utils.ParseDurationWithSecs(*jsnCfg.Store_interval); err != nil { + if t.StoreInterval, err = utils.ParseDurationWithNanosecs(*jsnCfg.Store_interval); err != nil { return err } } diff --git a/console/maxusage.go b/console/maxusage.go index ea0d5051b..f49858d6d 100644 --- a/console/maxusage.go +++ b/console/maxusage.go @@ -48,7 +48,7 @@ func (self *CmdGetMaxUsage) RpcMethod() string { func (self *CmdGetMaxUsage) RpcParams(reset bool) interface{} { if reset || self.rpcParams == nil { - self.rpcParams = &engine.UsageRecord{Direction: "*out"} + self.rpcParams = &engine.UsageRecord{} } return self.rpcParams } diff --git a/data/conf/cgrates/cgrates.json b/data/conf/cgrates/cgrates.json index 671e5f08e..0621035a3 100644 --- a/data/conf/cgrates/cgrates.json +++ b/data/conf/cgrates/cgrates.json @@ -111,7 +111,6 @@ // "enabled": false, // start the CDR Server service: // "extra_fields": [], // extra fields to store in CDRs for non-generic CDRs // "store_cdrs": true, // store cdrs in storDb -// "cdr_account_summary": false, // add account information from dataDB // "sm_cost_retries": 5, // number of queries to sm_costs before recalculating CDR // "rals_conns": [ // {"address": "*internal"} // address where to reach the Rater for cost calculation, empty to disable functionality: <""|*internal|x.y.z.y:1234> diff --git a/data/conf/samples/dmtagent/cgrates.json b/data/conf/samples/dmtagent/cgrates.json index a01d42a04..97da4c4db 100644 --- a/data/conf/samples/dmtagent/cgrates.json +++ b/data/conf/samples/dmtagent/cgrates.json @@ -4,6 +4,10 @@ // Used for cgradmin // Starts rater, scheduler +"general": { + "log_level": 7, +}, + "listen": { "rpc_json": ":2012", // RPC JSON listening address "rpc_gob": ":2013", // RPC GOB listening address diff --git a/data/conf/samples/dmtagent/voice.json b/data/conf/samples/dmtagent/voice.json index a1b369ea4..5d67b2bbf 100644 --- a/data/conf/samples/dmtagent/voice.json +++ b/data/conf/samples/dmtagent/voice.json @@ -17,7 +17,8 @@ {"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": "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}, @@ -29,7 +30,9 @@ {"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}, + "field_id": "Granted-Service-Unit>CC-Time", + "type": "*handler", "handler_id": "*value_exponent", + "value": "CGRMaxUsage;^|-9", "mandatory": true}, ], }, { @@ -54,7 +57,9 @@ ], "cca_fields":[ // fields returned in CCA {"tag": "GrantedUnits", "field_filter":"CGRError(^$)", - "field_id": "Granted-Service-Unit>CC-Time", "type": "*composed", "value": "CGRMaxUsage", "mandatory": true}, + "field_id": "Granted-Service-Unit>CC-Time", + "type": "*handler", "handler_id": "*value_exponent", + "value": "CGRMaxUsage;^|-9", "mandatory": true}, ], }, { @@ -79,7 +84,9 @@ ], "cca_fields":[ // fields returned in CCA {"tag": "GrantedUnits", "field_filter":"CGRError(^$)", - "field_id": "Granted-Service-Unit>CC-Time", "type": "*composed", "value": "CGRMaxUsage", "mandatory": true}, + "field_id": "Granted-Service-Unit>CC-Time", + "type": "*handler", "handler_id": "*value_exponent", + "value": "CGRMaxUsage;^|-9", "mandatory": true}, ], }, ], diff --git a/data/conf/samples/smg/cgrates.json b/data/conf/samples/smg/cgrates.json index 0c310c98b..1d3ab8913 100644 --- a/data/conf/samples/smg/cgrates.json +++ b/data/conf/samples/smg/cgrates.json @@ -20,6 +20,12 @@ "rals": { "enabled": true, // enable Rater service: + "max_computed_usage": { // do not compute usage higher than this, prevents memory overload + "*any": "189h", + "*voice": "72h", + "*data": "102400", + "*sms": "10000" + }, }, "scheduler": { @@ -33,6 +39,9 @@ "sm_generic": { "enabled": true, "session_ttl": "50ms", + "rals_conns": [ + {"address": "127.0.0.1:2012", "transport": "*json"} + ] }, } diff --git a/data/storage/migrator/usage_float_to_int.sql b/data/storage/migrator/usage_float_to_int.sql index ef5b1b03c..eb68915ea 100644 --- a/data/storage/migrator/usage_float_to_int.sql +++ b/data/storage/migrator/usage_float_to_int.sql @@ -1,3 +1,4 @@ + ALTER TABLE cdrs CHANGE COLUMN `usage` `usage_old` DECIMAL(30,9); ALTER TABLE cdrs ADD `usage` BIGINT; UPDATE cdrs SET `usage` = `usage_old` * 1000000000 WHERE usage_old IS NOT NULL; diff --git a/data/storage/mysql/create_cdrs_tables.sql b/data/storage/mysql/create_cdrs_tables.sql index 40598db44..c424c9e94 100644 --- a/data/storage/mysql/create_cdrs_tables.sql +++ b/data/storage/mysql/create_cdrs_tables.sql @@ -5,30 +5,25 @@ DROP TABLE IF EXISTS cdrs; CREATE TABLE cdrs ( id int(11) NOT NULL AUTO_INCREMENT, - cgrid char(40) NOT NULL, + cgrid varchar(40) NOT NULL, run_id varchar(64) NOT NULL, origin_host varchar(64) NOT NULL, source varchar(64) NOT NULL, origin_id varchar(64) NOT NULL, tor varchar(16) NOT NULL, request_type varchar(24) NOT NULL, - direction varchar(8) NOT NULL, tenant varchar(64) NOT NULL, category varchar(32) NOT NULL, account varchar(128) NOT NULL, subject varchar(128) NOT NULL, destination varchar(128) NOT NULL, setup_time datetime NOT NULL, - pdd DECIMAL(12,9) NOT NULL, answer_time datetime NOT NULL, - `usage` DECIMAL(30,9) NOT NULL, - supplier varchar(128) NOT NULL, - disconnect_cause varchar(64) NOT NULL, + `usage` BIGINT NOT NULL, extra_fields text NOT NULL, cost_source varchar(64) NOT NULL, cost DECIMAL(20,4) NOT NULL, cost_details text, - account_summary text, extra_info text, created_at TIMESTAMP NULL, updated_at TIMESTAMP NULL, @@ -40,12 +35,12 @@ CREATE TABLE cdrs ( DROP TABLE IF EXISTS sm_costs; CREATE TABLE sm_costs ( id int(11) NOT NULL AUTO_INCREMENT, - cgrid char(40) NOT NULL, + cgrid varchar(40) NOT NULL, run_id varchar(64) NOT NULL, origin_host varchar(64) NOT NULL, origin_id varchar(64) NOT NULL, cost_source varchar(64) NOT NULL, - `usage` DECIMAL(30,9) NOT NULL, + `usage` BIGINT NOT NULL, cost_details text, created_at TIMESTAMP NULL, deleted_at TIMESTAMP NULL, diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 5aa70cac2..8b4a1f8c9 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -491,6 +491,7 @@ CREATE TABLE tp_filters ( -- Table structure for table `tp_lcr` -- + DROP TABLE IF EXISTS tp_lcr; CREATE TABLE tp_lcr ( `pk` int(11) NOT NULL AUTO_INCREMENT, diff --git a/data/storage/postgres/create_cdrs_tables.sql b/data/storage/postgres/create_cdrs_tables.sql index 1cb0cff64..ad565c068 100644 --- a/data/storage/postgres/create_cdrs_tables.sql +++ b/data/storage/postgres/create_cdrs_tables.sql @@ -5,30 +5,25 @@ DROP TABLE IF EXISTS cdrs; CREATE TABLE cdrs ( id SERIAL PRIMARY KEY, - cgrid CHAR(40) NOT NULL, + cgrid VARCHAR(40) NOT NULL, run_id VARCHAR(64) NOT NULL, origin_host VARCHAR(64) NOT NULL, source VARCHAR(64) NOT NULL, origin_id VARCHAR(64) NOT NULL, tor VARCHAR(16) NOT NULL, request_type VARCHAR(24) NOT NULL, - direction VARCHAR(8) NOT NULL, tenant VARCHAR(64) NOT NULL, category VARCHAR(32) NOT NULL, account VARCHAR(128) NOT NULL, subject VARCHAR(128) NOT NULL, destination VARCHAR(128) NOT NULL, setup_time TIMESTAMP WITH TIME ZONE NOT NULL, - pdd NUMERIC(12,9) NOT NULL, answer_time TIMESTAMP WITH TIME ZONE NOT NULL, - usage NUMERIC(30,9) NOT NULL, - supplier VARCHAR(128) NOT NULL, - disconnect_cause VARCHAR(64) NOT NULL, + usage BIGINT NOT NULL, extra_fields jsonb NOT NULL, cost_source VARCHAR(64) NOT NULL, cost NUMERIC(20,4) DEFAULT NULL, cost_details jsonb, - account_summary jsonb, extra_info text, created_at TIMESTAMP WITH TIME ZONE, updated_at TIMESTAMP WITH TIME ZONE NULL, @@ -43,12 +38,12 @@ CREATE INDEX deleted_at_cp_idx ON cdrs (deleted_at); DROP TABLE IF EXISTS sm_costs; CREATE TABLE sm_costs ( id SERIAL PRIMARY KEY, - cgrid CHAR(40) NOT NULL, + cgrid VARCHAR(40) NOT NULL, run_id VARCHAR(64) NOT NULL, origin_host VARCHAR(64) NOT NULL, origin_id VARCHAR(64) NOT NULL, cost_source VARCHAR(64) NOT NULL, - usage NUMERIC(30,9) NOT NULL, + usage BIGINT NOT NULL, cost_details jsonb, created_at TIMESTAMP WITH TIME ZONE, deleted_at TIMESTAMP WITH TIME ZONE NULL, @@ -62,4 +57,3 @@ DROP INDEX IF EXISTS run_origin_smcost_idx; CREATE INDEX run_origin_smcost_idx ON sm_costs (run_id, origin_id); DROP INDEX IF EXISTS deleted_at_smcost_idx; CREATE INDEX deleted_at_smcost_idx ON sm_costs (deleted_at); - diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index efcd3a7fe..a8a75247d 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -480,7 +480,9 @@ CREATE TABLE tp_filters ( CREATE INDEX tp_filters_idx ON tp_filters (tpid); CREATE INDEX tp_filters_unique ON tp_filters ("tpid","tenant", "id", "filter_type", "filter_field_name"); + -- + -- Table structure for table `tp_lcr` -- @@ -500,8 +502,9 @@ CREATE TABLE tp_filters ( "weight" decimal(8,2) NOT NULL, "created_at" TIMESTAMP WITH TIME ZONE ); - CREATE INDEX tp_lcr_idx ON tp_lcr (tpid); - CREATE INDEX tp_lcr_unique ON tp_lcr ("tpid", "tenant", "id", "filter_ids"); + CREATE INDEX tp_lcr_idx ON tp_lcr (tpid); + CREATE INDEX tp_lcr_unique ON tp_lcr ("tpid", "tenant", "id", "filter_ids"); + -- -- Table structure for table `versions` diff --git a/data/tariffplans/test/a1/ActionPlans.csv b/data/tariffplans/test/a1/ActionPlans.csv index 3c5d35121..a852fa4de 100644 --- a/data/tariffplans/test/a1/ActionPlans.csv +++ b/data/tariffplans/test/a1/ActionPlans.csv @@ -1,3 +1,4 @@ +#Id,ActionsId,TimingId,Weight PACKAGE_1,LOG,*asap,10 PACKAGE_1,LOG,FIRST_OF_YEAR_2020,10 -PACKAGE_2,LOG,FIRST_OF_YEAR_2020,10 \ No newline at end of file +PACKAGE_2,LOG,FIRST_OF_YEAR_2020,10 diff --git a/data/tariffplans/test/a1/Actions.csv b/data/tariffplans/test/a1/Actions.csv index 04864b6ef..6b10e6e73 100644 --- a/data/tariffplans/test/a1/Actions.csv +++ b/data/tariffplans/test/a1/Actions.csv @@ -1 +1,2 @@ -LOG,*log,,,,,,,,,,,,,,false,false,10 \ No newline at end of file +#ActionsId[0],Action[1],ExtraParameters[2],Filter[3],BalanceId[4],BalanceType[5],Directions[6],Categories[7],DestinationIds[8],RatingSubject[9],SharedGroup[10],ExpiryTime[11],TimingIds[12],Units[13],BalanceWeight[14],BalanceBlocker[15],BalanceDisabled[16],Weight[17] +LOG,*log,,,,,,,,,,,,,,false,false,10 diff --git a/data/tariffplans/test/a1/DestinationRates.csv b/data/tariffplans/test/a1/DestinationRates.csv index f3cf703cc..1e64ecac3 100644 --- a/data/tariffplans/test/a1/DestinationRates.csv +++ b/data/tariffplans/test/a1/DestinationRates.csv @@ -1 +1,2 @@ +#Id,DestinationId,RatesTag,RoundingMethod,RoundingDecimals,MaxCost,MaxCostStrategy DR_DATA1,*any,RT_DATA1,*up,5,, diff --git a/data/tariffplans/test/a1/Rates.csv b/data/tariffplans/test/a1/Rates.csv index aa04963f7..c4fbab57e 100644 --- a/data/tariffplans/test/a1/Rates.csv +++ b/data/tariffplans/test/a1/Rates.csv @@ -1 +1,2 @@ +#Id,ConnectFee,Rate,RateUnit,RateIncrement,GroupIntervalStart RT_DATA1,0,0.0,1048576,10240,0 diff --git a/data/tariffplans/test/a1/RatingPlans.csv b/data/tariffplans/test/a1/RatingPlans.csv index a013a71da..34f413c1f 100644 --- a/data/tariffplans/test/a1/RatingPlans.csv +++ b/data/tariffplans/test/a1/RatingPlans.csv @@ -1 +1,2 @@ +#Id,DestinationRatesId,TimingTag,Weight RP_DATA1,DR_DATA1,*any,10 diff --git a/data/tariffplans/test/a1/RatingProfiles.csv b/data/tariffplans/test/a1/RatingProfiles.csv index 3ec883fe0..c72e83a99 100644 --- a/data/tariffplans/test/a1/RatingProfiles.csv +++ b/data/tariffplans/test/a1/RatingProfiles.csv @@ -1 +1,2 @@ +#Direction,Tenant,Category,Subject,ActivationTime,RatingPlanId,RatesFallbackSubject,CdrStatQueueIds *out,cgrates.org,data1,rpdata1,2015-01-01T00:00:00Z,RP_DATA1,, diff --git a/data/tariffplans/test/a1/Timings.csv b/data/tariffplans/test/a1/Timings.csv index 3a0c95d45..745a8cb9d 100644 --- a/data/tariffplans/test/a1/Timings.csv +++ b/data/tariffplans/test/a1/Timings.csv @@ -1 +1,2 @@ -FIRST_OF_YEAR_2020,2020,1,1,*any,00:00:00 \ No newline at end of file +#Tag,Years,Months,MonthDays,WeekDays,Time +FIRST_OF_YEAR_2020,2020,1,1,*any,00:00:00 diff --git a/data/tariffplans/testtp/Rates.csv b/data/tariffplans/testtp/Rates.csv index b8860104b..49f04432a 100644 --- a/data/tariffplans/testtp/Rates.csv +++ b/data/tariffplans/testtp/Rates.csv @@ -3,4 +3,4 @@ RT_1CENT,0,1,1s,1s,0s RT_DATA_2c,0,0.002,10,10,0 RT_SMS_5c,0,0.005,1,1,0 RT_DATA_r,0,0.1,1048576,10240,0 -RT_ZERO,0,0,1,1,0 +RT_ZERO,0,0,1s,1s,0s diff --git a/data/tariffplans/tutorial/Actions.csv b/data/tariffplans/tutorial/Actions.csv index accbdefb3..f1ee20f9f 100644 --- a/data/tariffplans/tutorial/Actions.csv +++ b/data/tariffplans/tutorial/Actions.csv @@ -1,8 +1,8 @@ #ActionsId[0],Action[1],ExtraParameters[2],Filter[3],BalanceId[4],BalanceType[5],Directions[6],Categories[7],DestinationIds[8],RatingSubject[9],SharedGroup[10],ExpiryTime[11],TimingIds[12],Units[13],BalanceWeight[14],BalanceBlocker[15],BalanceDisabled[16],Weight[17] TOPUP_RST_10,*topup_reset,,,,*monetary,*out,,*any,,,*unlimited,,10,10,false,false,10 TOPUP_RST_5,*topup_reset,,,,*monetary,*out,,*any,,,*unlimited,,5,20,false,false,10 -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_5,*topup_reset,,,,*voice,*out,,DST_1002,SPECIAL_1002,,*unlimited,,90s,20,false,false,10 +TOPUP_120_DST1003,*topup_reset,,,,*voice,*out,,DST_1003,,,*unlimited,,120s,20,false,false,10 TOPUP_RST_SHARED_5,*topup,,,,*monetary,*out,,*any,,SHARED_A,*unlimited,,5,10,false,false,10 SHARED_A_0,*topup_reset,,,,*monetary,*out,,*any,,SHARED_A,*unlimited,,0,10,false,false,10 TOPUP_RST_DATA_100,*topup_reset,,,,*data,*out,,*any,,,*unlimited,,102400,10,false,false,10 diff --git a/data/tariffplans/tutorial/RatingProfiles.csv b/data/tariffplans/tutorial/RatingProfiles.csv index 0d1d56064..2375cff20 100644 --- a/data/tariffplans/tutorial/RatingProfiles.csv +++ b/data/tariffplans/tutorial/RatingProfiles.csv @@ -2,9 +2,10 @@ *out,cgrates.org,call,*any,2014-01-14T00:00:00Z,RP_RETAIL1,, *out,cgrates.org,call,1001,2014-01-14T00:00:00Z,RP_RETAIL2,, *out,cgrates.org,call,SPECIAL_1002,2014-01-14T00:00:00Z,RP_SPECIAL_1002,, +*out,cgrates.org,generic,*any,2014-01-14T00:00:00Z,RP_GENERIC,, +*out,cgrates.org,data,*any,2014-01-14T00:00:00Z,RP_GENERIC,, *out,cgrates.org,lcr_profile1,suppl1,2014-01-14T00:00:00Z,RP_RETAIL1,,STATS_SUPPL1 *out,cgrates.org,lcr_profile1,suppl2,2014-01-14T00:00:00Z,RP_RETAIL2,,STATS_SUPPL2 *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,, diff --git a/engine/account.go b/engine/account.go index 1c0ede2bd..bcaeca80b 100644 --- a/engine/account.go +++ b/engine/account.go @@ -393,12 +393,13 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo for _, balance := range usefulUnitBalances { //utils.Logger.Info(fmt.Sprintf("Unit balance: %+v", balance)) //utils.Logger.Info(fmt.Sprintf("CD BEFORE UNIT: %+v", cd)) - - partCC, debitErr := balance.debitUnits(cd, balance.account, usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0) + partCC, debitErr := balance.debitUnits(cd, balance.account, + usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0) if debitErr != nil { return nil, debitErr } - if balance.RatingSubject != "" && !strings.HasPrefix(balance.RatingSubject, utils.ZERO_RATING_SUBJECT_PREFIX) { + if balance.RatingSubject != "" && + !strings.HasPrefix(balance.RatingSubject, utils.ZERO_RATING_SUBJECT_PREFIX) { hadBalanceSubj = true } //utils.Logger.Info(fmt.Sprintf("CD AFTER UNIT: %+v", cd)) @@ -439,7 +440,8 @@ func (ub *Account) debitCreditBalance(cd *CallDescriptor, count bool, dryRun boo for _, balance := range usefulMoneyBalances { //utils.Logger.Info(fmt.Sprintf("Money balance: %+v", balance)) //utils.Logger.Info(fmt.Sprintf("CD BEFORE MONEY: %+v", cd)) - partCC, debitErr := balance.debitMoney(cd, balance.account, usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0) + partCC, debitErr := balance.debitMoney(cd, balance.account, + usefulMoneyBalances, count, dryRun, len(cc.Timespans) == 0) if debitErr != nil { return nil, debitErr } diff --git a/engine/account_test.go b/engine/account_test.go index eda9563e2..a97e04c98 100644 --- a/engine/account_test.go +++ b/engine/account_test.go @@ -31,7 +31,8 @@ var ( ) func TestBalanceStoreRestore(t *testing.T) { - b := &Balance{Value: 14, Weight: 1, Uuid: "test", ExpirationDate: time.Date(2013, time.July, 15, 17, 48, 0, 0, time.UTC)} + b := &Balance{Value: 14, Weight: 1, Uuid: "test", + ExpirationDate: time.Date(2013, time.July, 15, 17, 48, 0, 0, time.UTC)} marsh := NewCodecMsgpackMarshaler() output, err := marsh.Marshal(b) if err != nil { @@ -154,7 +155,10 @@ 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]Balances{utils.VOICE: Balances{b1, b2}, utils.MONETARY: Balances{&Balance{Value: 21}}}} + rifsBalance := &Account{ID: "other", + BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1, b2}, + utils.MONETARY: Balances{&Balance{Value: 21}}}} dm.DataDB().SetAccount(rifsBalance) result, err := dm.DataDB().GetAccount(rifsBalance.ID) if err != nil || rifsBalance.ID != result.ID || @@ -167,7 +171,10 @@ 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 * float64(time.Second), Weight: 10, + DestinationIDs: utils.StringMap{"NAT": true}, + RatingSubject: "*zero1s"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -289,7 +296,9 @@ func TestDebitFreeEmpty(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 * float64(time.Second), + Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, + RatingSubject: "*zero1m"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -298,7 +307,11 @@ func TestDebitCreditZeroMinute(t *testing.T) { TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC), DurationIndex: 0, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + RateInterval: &RateInterval{ + Rating: &RIRate{Rates: RateGroups{ + &Rate{GroupIntervalStart: 0, Value: 100, + RateIncrement: 10 * time.Second, + RateUnit: time.Second}}}}, }, }, TOR: utils.VOICE, @@ -312,10 +325,12 @@ func TestDebitCreditZeroMinute(t *testing.T) { TOR: utils.VOICE, testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ - utils.VOICE: Balances{b1}, - utils.MONETARY: Balances{&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) if err != nil { @@ -324,18 +339,23 @@ func TestDebitCreditZeroMinute(t *testing.T) { //t.Logf("%+v", cc.Timespans) 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]) + t.Errorf("Error setting balance id to increment: %s", + utils.ToJSON(cc)) } - if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 10 || + if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 10*float64(time.Second) || rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 21 { - t.Error("Error extracting minutes from balance: ", - rifsBalance.BalanceMap[utils.VOICE][0]) + t.Errorf("Error extracting minutes from balance: %s", + utils.ToJSON(rifsBalance.BalanceMap[utils.VOICE][0])) } } 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 * float64(time.Second), + DestinationIDs: utils.StringMap{"NAT": true}, + RatingSubject: "*zero1m", Weight: 5} + b2 := &Balance{Uuid: "tests", Value: 10 * float64(time.Second), Weight: 10, + DestinationIDs: utils.StringMap{"NAT": true}, RatingSubject: "*zero1s"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -373,7 +393,7 @@ func TestDebitCreditZeroMixedMinute(t *testing.T) { t.Error("Error setting balance id to increment: ", cc.Timespans) } if rifsBalance.BalanceMap[utils.VOICE][1].GetValue() != 0 || - rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 10 || + rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 10*float64(time.Second) || rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 21 { t.Logf("TS0: %+v", cc.Timespans[0]) t.Logf("TS1: %+v", cc.Timespans[1]) @@ -382,7 +402,9 @@ 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 * float64(time.Second), + DestinationIDs: utils.StringMap{"NAT": true}, + RatingSubject: "*zero1m", Weight: 10} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -391,13 +413,23 @@ func TestDebitCreditNoCredit(t *testing.T) { TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC), DurationIndex: 0, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + RateInterval: &RateInterval{ + Rating: &RIRate{ + Rates: RateGroups{ + &Rate{GroupIntervalStart: 0, Value: 100, + RateIncrement: 10 * time.Second, + RateUnit: time.Second}}}}, }, &TimeSpan{ TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC), TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC), DurationIndex: 10 * time.Second, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + RateInterval: &RateInterval{ + Rating: &RIRate{ + Rates: RateGroups{ + &Rate{GroupIntervalStart: 0, Value: 100, + RateIncrement: 10 * time.Second, + RateUnit: time.Second}}}}, }, }, TOR: utils.VOICE, @@ -411,9 +443,10 @@ func TestDebitCreditNoCredit(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - rifsBalance := &Account{ID: "other", BalanceMap: map[string]Balances{ - utils.VOICE: Balances{b1}, - }} + rifsBalance := &Account{ID: "other", + BalanceMap: map[string]Balances{ + utils.VOICE: Balances{b1}, + }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) if err == nil { @@ -423,7 +456,7 @@ func TestDebitCreditNoCredit(t *testing.T) { cc.Timespans[0].Increments[0].Duration != time.Minute { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } - if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 10 { + if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 10*float64(time.Second) { t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[utils.VOICE][0]) } @@ -433,7 +466,9 @@ 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 * float64(time.Second), + DestinationIDs: utils.StringMap{"NAT": true}, + Weight: 10, RatingSubject: "*zero1m"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -475,10 +510,11 @@ func TestDebitCreditHasCredit(t *testing.T) { cc.Timespans[0].Increments[0].Duration != time.Minute { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } - if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 10 || + if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 10*float64(time.Second) || rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 30 { t.Errorf("Error extracting minutes from balance: %+v, %+v", - rifsBalance.BalanceMap[utils.VOICE][0].GetValue(), rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) + rifsBalance.BalanceMap[utils.VOICE][0].GetValue(), + rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) } if len(cc.Timespans) != 3 || cc.Timespans[0].GetDuration() != time.Minute { t.Error("Error truncating extra timespans: ", cc.Timespans) @@ -486,7 +522,9 @@ 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 * float64(time.Second), + DestinationIDs: utils.StringMap{"NAT": true}, + Weight: 10, RatingSubject: "*zero1s"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -496,7 +534,12 @@ func TestDebitCreditSplitMinutesMoney(t *testing.T) { TimeEnd: time.Date(2013, 9, 24, 10, 48, 20, 0, time.UTC), DurationIndex: 0, ratingInfo: &RatingInfo{}, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 1, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + RateInterval: &RateInterval{ + Rating: &RIRate{ + Rates: RateGroups{ + &Rate{GroupIntervalStart: 0, Value: 1, + RateIncrement: 10 * time.Second, + RateUnit: time.Second}}}}, }, }, TOR: utils.VOICE, @@ -534,7 +577,9 @@ 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 * float64(time.Second), + DestinationIDs: utils.StringMap{"NAT": true}, + Weight: 10, RatingSubject: "*zero1m"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -543,13 +588,22 @@ func TestDebitCreditMoreTimespans(t *testing.T) { TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), TimeEnd: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC), DurationIndex: 0, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + RateInterval: &RateInterval{ + Rating: &RIRate{ + Rates: RateGroups{ + &Rate{GroupIntervalStart: 0, Value: 100, + RateIncrement: 10 * time.Second, + RateUnit: time.Second}}}}, }, &TimeSpan{ TimeStart: time.Date(2013, 9, 24, 10, 48, 10, 0, time.UTC), TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC), DurationIndex: 10 * time.Second, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 10 * time.Second, RateUnit: time.Second}}}}, + RateInterval: &RateInterval{ + Rating: &RIRate{Rates: RateGroups{ + &Rate{GroupIntervalStart: 0, Value: 100, + RateIncrement: 10 * time.Second, + RateUnit: time.Second}}}}, }, }, TOR: utils.VOICE, @@ -575,15 +629,19 @@ func TestDebitCreditMoreTimespans(t *testing.T) { cc.Timespans[0].Increments[0].Duration != time.Minute { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } - if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 30 { + if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 30*float64(time.Second) { t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[utils.VOICE][0]) } } 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 * float64(time.Second), + DestinationIDs: utils.StringMap{"NAT": true}, + Weight: 10, RatingSubject: "*zero1m"} + b2 := &Balance{Uuid: "testa", Value: 150 * float64(time.Second), + DestinationIDs: utils.StringMap{"NAT": true}, + Weight: 5, RatingSubject: "*zero1s"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -624,15 +682,17 @@ func TestDebitCreditMoreTimespansMixed(t *testing.T) { cc.Timespans[0].Increments[0].Duration != time.Minute { t.Error("Error setting balance id to increment: ", cc.Timespans[0].Increments[0]) } - if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 10 || - rifsBalance.BalanceMap[utils.VOICE][1].GetValue() != 130 { + if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 10*float64(time.Second) || + rifsBalance.BalanceMap[utils.VOICE][1].GetValue() != 130*float64(time.Second) { t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[utils.VOICE][1], cc.Timespans[1]) } } 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 * float64(time.Second), + DestinationIDs: utils.StringMap{"NAT": true}, + Weight: 10, RatingSubject: "*zero1m"} cc := &CallCost{ Direction: utils.OUT, Destination: "0723045326", @@ -731,7 +791,11 @@ 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 * float64(time.Second), + Weight: 10, DestinationIDs: utils.StringMap{"NAT": true}, + RatingSubject: "minu"} cc := &CallCost{ Tenant: "vdf", Category: "0", @@ -773,10 +837,11 @@ func TestDebitCreditSubjectMinutes(t *testing.T) { cc.Timespans[0].Increments[0].Duration != 10*time.Second { t.Errorf("Error setting balance id to increment: %+v", cc.Timespans[0].Increments[0]) } - if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 180 || + if rifsBalance.BalanceMap[utils.VOICE][0].GetValue() != 180*float64(time.Second) || rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 280 { t.Errorf("Error extracting minutes from balance: %+v, %+v", - rifsBalance.BalanceMap[utils.VOICE][0].GetValue(), rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) + rifsBalance.BalanceMap[utils.VOICE][0].GetValue(), + rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) } if len(cc.Timespans) != 1 || cc.Timespans[0].GetDuration() != 70*time.Second { for _, ts := range cc.Timespans { @@ -1004,53 +1069,128 @@ func TestAccountAddMinutBucketEmpty(t *testing.T) { func TestAccountExecuteTriggeredActions(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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"}}, + ID: "TEST_UB", + BalanceMap: map[string]Balances{ + utils.MONETARY: Balances{ + &Balance{Directions: utils.NewStringMap(utils.OUT), Value: 100}}, + utils.VOICE: Balances{ + &Balance{Value: 10 * float64(time.Second), + 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"}}, } 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 { - t.Error("Error executing triggered actions", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue()) + if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || + ub.BalanceMap[utils.VOICE][0].GetValue() != 20*float64(time.Second) { + t.Error("Error executing triggered actions", + ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue()) } // are set to executed ub.countUnits(1, utils.MONETARY, nil, nil) - if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { - t.Error("Error executing triggered actions", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue()) + if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || + ub.BalanceMap[utils.VOICE][0].GetValue() != 20*float64(time.Second) { + t.Error("Error executing triggered actions", + ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue()) } // we can reset them ub.ResetActionTriggers(nil) ub.countUnits(10, utils.MONETARY, nil, nil) - if ub.BalanceMap[utils.MONETARY][0].GetValue() != 120 || ub.BalanceMap[utils.VOICE][0].GetValue() != 30 { - t.Error("Error executing triggered actions", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue()) + if ub.BalanceMap[utils.MONETARY][0].GetValue() != 120 || + ub.BalanceMap[utils.VOICE][0].GetValue() != 30*float64(time.Second) { + t.Error("Error executing triggered actions", + ub.BalanceMap[utils.MONETARY][0].GetValue(), + ub.BalanceMap[utils.VOICE][0].GetValue()) } } func TestAccountExecuteTriggeredActionsBalance(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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"}}, + ID: "TEST_UB", + 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 * float64(time.Second), + 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"}}, } ub.countUnits(1, utils.MONETARY, nil, nil) - if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || ub.BalanceMap[utils.VOICE][0].GetValue() != 20 { - t.Error("Error executing triggered actions", ub.BalanceMap[utils.MONETARY][0].GetValue(), ub.BalanceMap[utils.VOICE][0].GetValue(), len(ub.BalanceMap[utils.MONETARY])) + if ub.BalanceMap[utils.MONETARY][0].GetValue() != 110 || + ub.BalanceMap[utils.VOICE][0].GetValue() != 20*float64(time.Second) { + t.Error("Error executing triggered actions", + ub.BalanceMap[utils.MONETARY][0].GetValue(), + ub.BalanceMap[utils.VOICE][0].GetValue(), + len(ub.BalanceMap[utils.MONETARY])) } } func TestAccountExecuteTriggeredActionsOrder(t *testing.T) { ub := &Account{ - ID: "TEST_UB_OREDER", - 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"}}, + ID: "TEST_UB_OREDER", + 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"}}, } ub.countUnits(1, utils.MONETARY, &CallCost{Direction: utils.OUT}, nil) - if len(ub.BalanceMap[utils.MONETARY]) != 1 || ub.BalanceMap[utils.MONETARY][0].GetValue() != 10 { + if len(ub.BalanceMap[utils.MONETARY]) != 1 || + ub.BalanceMap[utils.MONETARY][0].GetValue() != 10 { - t.Errorf("Error executing triggered actions in order %v", ub.BalanceMap[utils.MONETARY][0].GetValue()) + t.Errorf("Error executing triggered actions in order %v", + ub.BalanceMap[utils.MONETARY][0].GetValue()) } } @@ -1084,19 +1224,35 @@ func TestAccountExecuteTriggeredDayWeek(t *testing.T) { func TestAccountExpActionTrigger(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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}}}}, + ID: "TEST_UB", + 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 * float64(time.Second), Weight: 20, + DestinationIDs: utils.StringMap{"NAT": true}, + Directions: utils.StringMap{utils.OUT: true}}, + &Balance{Weight: 10 * float64(time.Second), + 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) if ub.BalanceMap[utils.MONETARY][0].IsExpired() || ub.BalanceMap[utils.MONETARY][0].GetValue() != 10 || // expired was cleaned - ub.BalanceMap[utils.VOICE][0].GetValue() != 20 || + ub.BalanceMap[utils.VOICE][0].GetValue() != 20*float64(time.Second) || ub.ActionTriggers[0].Executed != true { 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])) + t.Error("Error executing triggered actions", + ub.BalanceMap[utils.MONETARY][0].GetValue(), + ub.BalanceMap[utils.VOICE][0].GetValue(), + len(ub.BalanceMap[utils.MONETARY])) } } @@ -1168,13 +1324,17 @@ func TestCleanExpired(t *testing.T) { } func TestAccountUnitCounting(t *testing.T) { - ub := &Account{UnitCounters: UnitCounters{utils.MONETARY: []*UnitCounter{&UnitCounter{Counters: CounterFilters{&CounterFilter{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[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 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[utils.MONETARY]) != 1 || ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 20 { + if len(ub.UnitCounters[utils.MONETARY]) != 1 || + ub.UnitCounters[utils.MONETARY][0].Counters[0].Value != 20 { t.Error("Error counting units") } } @@ -1362,10 +1522,16 @@ func TestDebitSMS(t *testing.T) { Timespans: []*TimeSpan{ &TimeSpan{ TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), - TimeEnd: time.Date(2013, 9, 24, 10, 48, 1, 0, time.UTC), + TimeEnd: time.Date(2013, 9, 24, 10, 48, 0, 1, time.UTC), ratingInfo: &RatingInfo{}, DurationIndex: 0, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1 * time.Second, RateUnit: time.Second}}}}, + RateInterval: &RateInterval{ + Rating: &RIRate{ + Rates: RateGroups{ + &Rate{GroupIntervalStart: 0, + Value: 100, + RateIncrement: 1, + RateUnit: time.Nanosecond}}}}, }, }, TOR: utils.SMS, @@ -1379,10 +1545,15 @@ func TestDebitSMS(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - 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}}, - }} + 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) if err != nil { @@ -1394,7 +1565,9 @@ func TestDebitSMS(t *testing.T) { if rifsBalance.BalanceMap[utils.SMS][0].GetValue() != 99 || rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 21 { t.Log(cc.Timespans[0].Increments) - t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[utils.SMS][0].GetValue(), rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) + t.Error("Error extracting minutes from balance: ", + rifsBalance.BalanceMap[utils.SMS][0].GetValue(), + rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) } } @@ -1405,10 +1578,15 @@ func TestDebitGeneric(t *testing.T) { Timespans: []*TimeSpan{ &TimeSpan{ TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), - TimeEnd: time.Date(2013, 9, 24, 10, 48, 1, 0, time.UTC), + TimeEnd: time.Date(2013, 9, 24, 10, 48, 0, 1, time.UTC), ratingInfo: &RatingInfo{}, DurationIndex: 0, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1 * time.Second, RateUnit: time.Second}}}}, + RateInterval: &RateInterval{ + Rating: &RIRate{ + Rates: RateGroups{ + &Rate{GroupIntervalStart: 0, Value: 100, + RateIncrement: 1, + RateUnit: time.Nanosecond}}}}, }, }, TOR: utils.GENERIC, @@ -1422,10 +1600,13 @@ func TestDebitGeneric(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - 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}}, - }} + 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) if err != nil { @@ -1451,7 +1632,13 @@ func TestDebitGenericBalance(t *testing.T) { TimeEnd: time.Date(2013, 9, 24, 10, 48, 30, 0, time.UTC), ratingInfo: &RatingInfo{}, DurationIndex: 0, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 100, RateIncrement: 1 * time.Second, RateUnit: time.Second}}}}, + RateInterval: &RateInterval{ + Rating: &RIRate{ + Rates: RateGroups{ + &Rate{GroupIntervalStart: 0, + Value: 100, + RateIncrement: 1 * time.Second, + RateUnit: time.Second}}}}, }, }, TOR: utils.VOICE, @@ -1465,10 +1652,14 @@ func TestDebitGenericBalance(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - 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}}, - }} + 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 * float64(time.Second)}}}, + utils.MONETARY: Balances{&Balance{Value: 21}}, + }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) if err != nil { @@ -1480,7 +1671,9 @@ func TestDebitGenericBalance(t *testing.T) { 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()) + t.Error("Error extracting minutes from balance: ", + rifsBalance.BalanceMap[utils.GENERIC][0].GetValue(), + rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) } } @@ -1494,7 +1687,12 @@ func TestDebitGenericBalanceWithRatingSubject(t *testing.T) { TimeEnd: time.Date(2013, 9, 24, 10, 48, 30, 0, time.UTC), ratingInfo: &RatingInfo{}, DurationIndex: 0, - RateInterval: &RateInterval{Rating: &RIRate{Rates: RateGroups{&Rate{GroupIntervalStart: 0, Value: 0, RateIncrement: time.Second, RateUnit: time.Second}}}}, + RateInterval: &RateInterval{ + Rating: &RIRate{ + Rates: RateGroups{ + &Rate{GroupIntervalStart: 0, Value: 0, + RateIncrement: time.Second, + RateUnit: time.Second}}}}, }, }, TOR: utils.VOICE, @@ -1508,10 +1706,15 @@ func TestDebitGenericBalanceWithRatingSubject(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - 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}}, - }} + 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 * float64(time.Second)}, + RatingSubject: "free"}}, + utils.MONETARY: Balances{&Balance{Value: 21}}, + }} var err error cc, err = rifsBalance.debitCreditBalance(cd, false, false, true) if err != nil { @@ -1523,7 +1726,9 @@ func TestDebitGenericBalanceWithRatingSubject(t *testing.T) { 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()) + t.Error("Error extracting minutes from balance: ", + rifsBalance.BalanceMap[utils.GENERIC][0].GetValue(), + rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) } } @@ -1534,14 +1739,19 @@ func TestDebitDataUnits(t *testing.T) { Timespans: []*TimeSpan{ &TimeSpan{ TimeStart: time.Date(2013, 9, 24, 10, 48, 0, 0, time.UTC), - TimeEnd: time.Date(2013, 9, 24, 10, 49, 20, 0, time.UTC), + TimeEnd: time.Date(2013, 9, 24, 10, 48, 0, 80, time.UTC), ratingInfo: &RatingInfo{}, DurationIndex: 0, RateInterval: &RateInterval{ Rating: &RIRate{ Rates: RateGroups{ - &Rate{GroupIntervalStart: 0, Value: 2, RateIncrement: 1 * time.Second, RateUnit: time.Minute}, - &Rate{GroupIntervalStart: 60, Value: 1, RateIncrement: 1 * time.Second, RateUnit: time.Second}, + &Rate{GroupIntervalStart: 0, + Value: 2, RateIncrement: 1, + RateUnit: 1}, + &Rate{GroupIntervalStart: 60, + Value: 1, + RateIncrement: 1, + RateUnit: 1}, }, }, }, @@ -1558,15 +1768,20 @@ func TestDebitDataUnits(t *testing.T) { DurationIndex: cc.GetDuration(), testCallcost: cc, } - 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}}, - }} + 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) // test rating information ts := cc.Timespans[0] - if ts.MatchedSubject != "testm" || ts.MatchedPrefix != "0723" || ts.MatchedDestId != "NAT" || ts.RatingPlanId != utils.META_NONE { + if ts.MatchedSubject != "testm" || ts.MatchedPrefix != "0723" || + ts.MatchedDestId != "NAT" || ts.RatingPlanId != utils.META_NONE { t.Errorf("Error setting rating info: %+v", ts.ratingInfo) } if err != nil { @@ -1578,7 +1793,9 @@ func TestDebitDataUnits(t *testing.T) { if rifsBalance.BalanceMap[utils.DATA][0].GetValue() != 20 || rifsBalance.BalanceMap[utils.MONETARY][0].GetValue() != 21 { t.Log(ts.Increments) - t.Error("Error extracting minutes from balance: ", rifsBalance.BalanceMap[utils.DATA][0].GetValue(), rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) + t.Error("Error extracting minutes from balance: ", + rifsBalance.BalanceMap[utils.DATA][0].GetValue(), + rifsBalance.BalanceMap[utils.MONETARY][0].GetValue()) } } diff --git a/engine/action.go b/engine/action.go index fe5627eb0..2aaddaa8d 100644 --- a/engine/action.go +++ b/engine/action.go @@ -187,14 +187,13 @@ func parseTemplateValue(rsrFlds utils.RSRFields, acnt *Account, action *Action) func cdrLogAction(acc *Account, sq *CDRStatsQueueTriggered, a *Action, acs Actions) (err error) { defaultTemplate := map[string]utils.RSRFields{ - utils.TOR: utils.ParseRSRFieldsMustCompile("BalanceType", utils.INFIELD_SEP), - utils.CDRHOST: utils.ParseRSRFieldsMustCompile("^127.0.0.1", utils.INFIELD_SEP), - utils.DIRECTION: utils.ParseRSRFieldsMustCompile("Directions", utils.INFIELD_SEP), - utils.REQTYPE: utils.ParseRSRFieldsMustCompile("^"+utils.META_PREPAID, utils.INFIELD_SEP), - utils.TENANT: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP), - utils.ACCOUNT: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP), - utils.SUBJECT: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP), - utils.COST: utils.ParseRSRFieldsMustCompile("ActionValue", utils.INFIELD_SEP), + utils.TOR: utils.ParseRSRFieldsMustCompile("BalanceType", utils.INFIELD_SEP), + utils.CDRHOST: utils.ParseRSRFieldsMustCompile("^127.0.0.1", utils.INFIELD_SEP), + utils.REQTYPE: utils.ParseRSRFieldsMustCompile("^"+utils.META_PREPAID, utils.INFIELD_SEP), + utils.TENANT: utils.ParseRSRFieldsMustCompile(utils.TENANT, utils.INFIELD_SEP), + utils.ACCOUNT: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP), + utils.SUBJECT: utils.ParseRSRFieldsMustCompile(utils.ACCOUNT, utils.INFIELD_SEP), + utils.COST: utils.ParseRSRFieldsMustCompile("ActionValue", utils.INFIELD_SEP), } template := make(map[string]string) @@ -214,12 +213,15 @@ func cdrLogAction(acc *Account, sq *CDRStatsQueueTriggered, a *Action, acs Actio // set stored cdr values var cdrs []*CDR for _, action := range acs { - if !utils.IsSliceMember([]string{DEBIT, DEBIT_RESET, TOPUP, TOPUP_RESET}, action.ActionType) || action.Balance == nil { + if !utils.IsSliceMember([]string{DEBIT, DEBIT_RESET, TOPUP, TOPUP_RESET}, action.ActionType) || + action.Balance == nil { continue // Only log specific actions } - cdr := &CDR{RunID: action.ActionType, Source: CDRLOG, SetupTime: time.Now(), AnswerTime: time.Now(), OriginID: utils.GenUUID(), ExtraFields: make(map[string]string)} + cdr := &CDR{RunID: action.ActionType, Source: CDRLOG, + SetupTime: time.Now(), AnswerTime: time.Now(), OriginID: utils.GenUUID(), + ExtraFields: make(map[string]string)} cdr.CGRID = utils.Sha1(cdr.OriginID, cdr.SetupTime.String()) - cdr.Usage = time.Duration(1) * time.Second + cdr.Usage = time.Duration(1) elem := reflect.ValueOf(cdr).Elem() for key, rsrFlds := range defaultTemplate { parsedValue := parseTemplateValue(rsrFlds, acc, action) diff --git a/engine/actions_it_test.go b/engine/actions_it_test.go index e06d0abd1..0a44997ae 100644 --- a/engine/actions_it_test.go +++ b/engine/actions_it_test.go @@ -101,7 +101,8 @@ func TestActionsitSetCdrlogDebit(t *testing.T) { t.Errorf("Calling ApierV1.ExecuteAction received: %s", reply) } var rcvedCdrs []*ExternalCDR - if err := actsLclRpc.Call("ApierV2.GetCdrs", utils.RPCCDRsFilter{Sources: []string{CDRLOG}, Accounts: []string{attrsSetAccount.Account}}, &rcvedCdrs); err != nil { + if err := actsLclRpc.Call("ApierV2.GetCdrs", utils.RPCCDRsFilter{Sources: []string{CDRLOG}, + Accounts: []string{attrsSetAccount.Account}}, &rcvedCdrs); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(rcvedCdrs) != 1 { t.Error("Unexpected number of CDRs returned: ", len(rcvedCdrs)) diff --git a/engine/actions_test.go b/engine/actions_test.go index 59e4b0e92..28767e61b 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -113,14 +113,16 @@ func TestActionPlanOnlyWeekdays(t *testing.T) { } func TestActionPlanHourWeekdays(t *testing.T) { - at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{WeekDays: []time.Weekday{time.Monday}, StartTime: "10:01:00"}}} + at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{ + WeekDays: []time.Weekday{time.Monday}, StartTime: "10:01:00"}}} st := at.GetNextStartTime(referenceDate) y, m, d := now.Date() e := time.Date(y, m, d, 10, 1, 0, 0, time.Local) day := e.Day() for i := 0; i < 8; i++ { - e = time.Date(e.Year(), e.Month(), day, e.Hour(), e.Minute(), e.Second(), e.Nanosecond(), e.Location()) + e = time.Date(e.Year(), e.Month(), day, e.Hour(), + e.Minute(), e.Second(), e.Nanosecond(), e.Location()) n := e.AddDate(0, 0, i) if n.Weekday() == time.Monday && (n.Equal(now) || n.After(now)) { e = n @@ -136,7 +138,8 @@ func TestActionPlanOnlyMonthdays(t *testing.T) { y, m, d := now.Date() tomorrow := time.Date(y, m, d, 0, 0, 0, 0, time.Local).AddDate(0, 0, 1) - at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{MonthDays: utils.MonthDays{1, 25, 2, tomorrow.Day()}}}} + at := &ActionTiming{Timing: &RateInterval{ + Timing: &RITiming{MonthDays: utils.MonthDays{1, 25, 2, tomorrow.Day()}}}} st := at.GetNextStartTime(referenceDate) expected := tomorrow if !st.Equal(expected) { @@ -152,7 +155,8 @@ func TestActionPlanHourMonthdays(t *testing.T) { if now.After(testTime) { y, m, d = tomorrow.Date() } - at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{MonthDays: utils.MonthDays{now.Day(), tomorrow.Day()}, StartTime: "10:01:00"}}} + at := &ActionTiming{Timing: &RateInterval{ + Timing: &RITiming{MonthDays: utils.MonthDays{now.Day(), tomorrow.Day()}, StartTime: "10:01:00"}}} st := at.GetNextStartTime(referenceDate) expected := time.Date(y, m, d, 10, 1, 0, 0, time.Local) if !st.Equal(expected) { @@ -164,7 +168,8 @@ func TestActionPlanOnlyMonths(t *testing.T) { y, m, _ := now.Date() nextMonth := time.Date(y, m, 1, 0, 0, 0, 0, time.Local).AddDate(0, 1, 0) - at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{Months: utils.Months{time.February, time.May, nextMonth.Month()}}}} + at := &ActionTiming{Timing: &RateInterval{ + Timing: &RITiming{Months: utils.Months{time.February, time.May, nextMonth.Month()}}}} st := at.GetNextStartTime(referenceDate) expected := time.Date(nextMonth.Year(), nextMonth.Month(), 1, 0, 0, 0, 0, time.Local) if !st.Equal(expected) { @@ -250,7 +255,8 @@ func TestActionPlanFirstOfTheMonth(t *testing.T) { func TestActionPlanOnlyYears(t *testing.T) { y, _, _ := referenceDate.Date() nextYear := time.Date(y, 1, 1, 0, 0, 0, 0, time.Local).AddDate(1, 0, 0) - at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{Years: utils.Years{now.Year(), nextYear.Year()}}}} + at := &ActionTiming{Timing: &RateInterval{ + Timing: &RITiming{Years: utils.Years{now.Year(), nextYear.Year()}}}} st := at.GetNextStartTime(referenceDate) expected := nextYear if !st.Equal(expected) { @@ -268,7 +274,8 @@ func TestActionPlanPast(t *testing.T) { } func TestActionPlanHourYears(t *testing.T) { - at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{Years: utils.Years{referenceDate.Year(), referenceDate.Year() + 1}, StartTime: "10:01:00"}}} + at := &ActionTiming{Timing: &RateInterval{Timing: &RITiming{ + Years: utils.Years{referenceDate.Year(), referenceDate.Year() + 1}, StartTime: "10:01:00"}}} st := at.GetNextStartTime(referenceDate) expected := time.Date(referenceDate.Year(), 1, 1, 10, 1, 0, 0, time.Local) if referenceDate.After(expected) { @@ -447,8 +454,10 @@ func TestActionPlanFunctionNotAvailable(t *testing.T) { func TestActionTimingPriorityListSortByWeight(t *testing.T) { at1 := &ActionTiming{Timing: &RateInterval{ Timing: &RITiming{ - Years: utils.Years{2020}, - Months: utils.Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, + Years: utils.Years{2020}, + Months: utils.Months{time.January, time.February, time.March, + time.April, time.May, time.June, time.July, time.August, time.September, + time.October, time.November, time.December}, MonthDays: utils.MonthDays{1}, StartTime: "00:00:00", }, @@ -456,8 +465,10 @@ func TestActionTimingPriorityListSortByWeight(t *testing.T) { }} at2 := &ActionTiming{Timing: &RateInterval{ Timing: &RITiming{ - Years: utils.Years{2020}, - Months: utils.Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, + Years: utils.Years{2020}, + Months: utils.Months{time.January, time.February, time.March, + time.April, time.May, time.June, time.July, time.August, time.September, + time.October, time.November, time.December}, MonthDays: utils.MonthDays{2}, StartTime: "00:00:00", }, @@ -475,7 +486,9 @@ func TestActionTimingPriorityListWeight(t *testing.T) { at1 := &ActionTiming{ Timing: &RateInterval{ Timing: &RITiming{ - Months: utils.Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, + Months: utils.Months{time.January, time.February, time.March, + time.April, time.May, time.June, time.July, time.August, time.September, + time.October, time.November, time.December}, MonthDays: utils.MonthDays{1}, StartTime: "00:00:00", }, @@ -485,7 +498,9 @@ func TestActionTimingPriorityListWeight(t *testing.T) { at2 := &ActionTiming{ Timing: &RateInterval{ Timing: &RITiming{ - Months: utils.Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, + Months: utils.Months{time.January, time.February, time.March, + time.April, time.May, time.June, time.July, time.August, time.September, + time.October, time.November, time.December}, MonthDays: utils.MonthDays{1}, StartTime: "00:00:00", }, @@ -550,26 +565,33 @@ func TestActionPlansRemoveMember(t *testing.T) { }, } - if err := dm.DataDB().SetActionPlan(ap1.Id, ap1, true, utils.NonTransactional); err != nil { + if err := dm.DataDB().SetActionPlan(ap1.Id, ap1, true, + utils.NonTransactional); err != nil { t.Error(err) } - if err = dm.DataDB().SetActionPlan(ap2.Id, ap2, true, utils.NonTransactional); err != nil { + if err = dm.DataDB().SetActionPlan(ap2.Id, ap2, true, + utils.NonTransactional); err != nil { t.Error(err) } - if err = dm.CacheDataFromDB(utils.ACTION_PLAN_PREFIX, []string{ap1.Id, ap2.Id}, true); err != nil { + if err = dm.CacheDataFromDB(utils.ACTION_PLAN_PREFIX, + []string{ap1.Id, ap2.Id}, true); err != nil { t.Error(err) } - if err = dm.DataDB().SetAccountActionPlans(account1.ID, []string{ap1.Id}, false); err != nil { + if err = dm.DataDB().SetAccountActionPlans(account1.ID, + []string{ap1.Id}, false); err != nil { t.Error(err) } - if err = dm.CacheDataFromDB(utils.AccountActionPlansPrefix, []string{account1.ID}, true); err != nil { + if err = dm.CacheDataFromDB(utils.AccountActionPlansPrefix, + []string{account1.ID}, true); err != nil { t.Error(err) } dm.DataDB().GetAccountActionPlans(account1.ID, true, utils.NonTransactional) // FixMe: remove here after finishing testing of map - if err = dm.DataDB().SetAccountActionPlans(account2.ID, []string{ap2.Id}, false); err != nil { + if err = dm.DataDB().SetAccountActionPlans(account2.ID, + []string{ap2.Id}, false); err != nil { t.Error(err) } - if err = dm.CacheDataFromDB(utils.AccountActionPlansPrefix, []string{account2.ID}, false); err != nil { + if err = dm.CacheDataFromDB(utils.AccountActionPlansPrefix, + []string{account2.ID}, false); err != nil { t.Error(err) } @@ -643,7 +665,8 @@ func TestActionTriggerMatchMinuteBucketBlank(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalanceFilter{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) } @@ -658,7 +681,8 @@ func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, utils.TRIGGER_MAX_BALANCE, 2)} + a := &Action{ExtraParameters: fmt.Sprintf(`{"ThresholdType":"%v", "ThresholdValue": %v}`, + utils.TRIGGER_MAX_BALANCE, 2)} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -673,7 +697,9 @@ func TestActionTriggerMatchAllFull(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, 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) } @@ -688,7 +714,9 @@ 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":"%s"}`, utils.TRIGGER_MAX_BALANCE_COUNTER)} + 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) } @@ -703,7 +731,9 @@ func TestActionTriggerMatcBalanceFalse(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - 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)} + 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) } @@ -718,7 +748,8 @@ func TestActionTriggerMatcAllFalse(t *testing.T) { ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY)}, ExtraParameters: fmt.Sprintf(`{"UniqueID":"ZIP", "GroupID":"%s", "ThresholdType":"%s"}`, "TEST", utils.TRIGGER_MAX_BALANCE)} + 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) } @@ -767,10 +798,15 @@ func TestActionTriggers(t *testing.T) { func TestActionResetTriggres(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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}}, + ID: "TEST_UB", + 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}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { @@ -780,10 +816,12 @@ func TestActionResetTriggres(t *testing.T) { func TestActionResetTriggresExecutesThem(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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}}, + ID: "TEST_UB", + 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}}, } resetTriggersAction(ub, nil, nil, nil) if ub.ActionTriggers[0].Executed == true || ub.BalanceMap[utils.MONETARY][0].GetValue() == 12 { @@ -793,10 +831,18 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { func TestActionResetTriggresActionFilter(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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}}, + ID: "TEST_UB", + 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}}, } resetTriggersAction(ub, nil, &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.SMS)}}, nil) if ub.ActionTriggers[0].Executed == false || ub.ActionTriggers[1].Executed == false { @@ -806,10 +852,19 @@ func TestActionResetTriggresActionFilter(t *testing.T) { func TestActionSetPostpaid(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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}}, + ID: "TEST_UB", + 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}}, } allowNegativeAction(ub, nil, nil, nil) if !ub.AllowNegative { @@ -819,11 +874,20 @@ func TestActionSetPostpaid(t *testing.T) { func TestActionSetPrepaid(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - AllowNegative: 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")}}}, - 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}}, + ID: "TEST_UB", + AllowNegative: 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")}}}, + 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) if ub.AllowNegative { @@ -833,11 +897,20 @@ func TestActionSetPrepaid(t *testing.T) { func TestActionResetPrepaid(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - AllowNegative: 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")}}}, - 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}}, + ID: "TEST_UB", + AllowNegative: 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")}}}, + 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) if !ub.AllowNegative || @@ -852,10 +925,19 @@ func TestActionResetPrepaid(t *testing.T) { func TestActionResetPostpaid(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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}}, + ID: "TEST_UB", + 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}}, } resetAccountAction(ub, nil, nil, nil) if ub.BalanceMap[utils.MONETARY].GetTotalValue() != 0 || @@ -868,12 +950,26 @@ func TestActionResetPostpaid(t *testing.T) { func TestActionTopupResetCredit(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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}}, + ID: "TEST_UB", + 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}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), + Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 10 || @@ -913,7 +1009,8 @@ func TestActionTopupResetCreditId(t *testing.T) { }, }, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), ID: utils.StringPointer("TEST_B"), Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), ID: utils.StringPointer("TEST_B"), + Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -932,7 +1029,8 @@ func TestActionTopupResetCreditNoId(t *testing.T) { }, }, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), + Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupResetAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 20 || @@ -946,11 +1044,22 @@ func TestActionTopupResetMinutes(t *testing.T) { ID: "TEST_UB", 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}}, + 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}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &utils.ValueFormula{Static: 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.ValueFormula{Static: 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 || @@ -964,12 +1073,27 @@ func TestActionTopupResetMinutes(t *testing.T) { func TestActionTopupCredit(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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}}, + ID: "TEST_UB", + 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}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), + Value: &utils.ValueFormula{Static: 10}, + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} topupAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 110 || @@ -982,12 +1106,23 @@ func TestActionTopupCredit(t *testing.T) { func TestActionTopupMinutes(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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}}, + ID: "TEST_UB", + 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}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &utils.ValueFormula{Static: 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.ValueFormula{Static: 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 || @@ -1001,12 +1136,26 @@ func TestActionTopupMinutes(t *testing.T) { func TestActionDebitCredit(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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}}, + ID: "TEST_UB", + 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}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} + a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), + Value: &utils.ValueFormula{Static: 10}, + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT))}} debitAction(ub, nil, a, nil) if ub.AllowNegative || ub.BalanceMap[utils.MONETARY].GetTotalValue() != 90 || @@ -1019,12 +1168,25 @@ func TestActionDebitCredit(t *testing.T) { func TestActionDebitMinutes(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - 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}}, + ID: "TEST_UB", + 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}}, } - a := &Action{Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Value: &utils.ValueFormula{Static: 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.ValueFormula{Static: 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 || @@ -1043,10 +1205,16 @@ func TestActionResetAllCounters(t *testing.T) { 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)}}}, + &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) @@ -1056,13 +1224,15 @@ func TestActionResetAllCounters(t *testing.T) { 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[utils.MONETARY][0], ub.UnitCounters[utils.MONETARY][0].Counters[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() } c := ub.UnitCounters[utils.MONETARY][0].Counters[0] - if c.Filter.GetWeight() != 20 || c.Value != 0 || c.Filter.GetDestinationIDs()["NAT"] == false { + if c.Filter.GetWeight() != 20 || c.Value != 0 || + c.Filter.GetDestinationIDs()["NAT"] == false { t.Errorf("Balance cloned incorrectly: %+v", c) } } @@ -1073,8 +1243,13 @@ func TestActionResetCounterOnlyDefault(t *testing.T) { AllowNegative: 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}}, + 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() @@ -1101,11 +1276,23 @@ func TestActionResetCounterOnlyDefault(t *testing.T) { func TestActionResetCounterCredit(t *testing.T) { ub := &Account{ - ID: "TEST_UB", - AllowNegative: 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")}}}, - 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}}, + ID: "TEST_UB", + AllowNegative: 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")}}}, + 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)}} resetCountersAction(ub, nil, a, nil) @@ -1159,8 +1346,10 @@ func TestActionTriggerLogging(t *testing.T) { func TestActionPlanLogging(t *testing.T) { i := &RateInterval{ Timing: &RITiming{ - Months: utils.Months{time.January, time.February, time.March, time.April, time.May, time.June, time.July, time.August, time.September, time.October, time.November, time.December}, - MonthDays: utils.MonthDays{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, + Months: utils.Months{time.January, time.February, time.March, time.April, time.May, time.June, + time.July, time.August, time.September, time.October, time.November, time.December}, + MonthDays: utils.MonthDays{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}, WeekDays: utils.WeekDays{time.Monday, time.Tuesday, time.Wednesday, time.Thursday, time.Friday}, StartTime: "18:00:00", EndTime: "00:00:00", @@ -1236,7 +1425,9 @@ func TestTopupAction(t *testing.T) { initialUb, _ := dm.DataDB().GetAccount("vdf:minu") a := &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 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.ValueFormula{Static: 25}, + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1257,7 +1448,9 @@ func TestTopupActionLoaded(t *testing.T) { initialUb, _ := dm.DataDB().GetAccount("vdf:minitsboy") a := &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 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.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), + Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20)}, } at := &ActionTiming{ @@ -1284,7 +1477,8 @@ func TestActionCdrlogEmpty(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1306,11 +1500,13 @@ func TestActionCdrlogWithParams(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1333,11 +1529,13 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) { err := cdrLogAction(acnt, nil, cdrlog, Actions{ &Action{ ActionType: DEBIT, - Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, &Action{ ActionType: DEBIT_RESET, - Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, + Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 25}, + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(20)}, }, }) if err != nil { @@ -1358,7 +1556,8 @@ func TestActionCdrLogParamsWithOverload(t *testing.T) { } func TestActionSetDDestination(t *testing.T) { - acc := &Account{BalanceMap: map[string]Balances{utils.MONETARY: Balances{&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"}} dm.DataDB().SetDestination(origD, utils.NonTransactional) dm.DataDB().SetReverseDestination(origD, utils.NonTransactional) @@ -1425,11 +1624,13 @@ func TestActionTransactionFuncType(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.MONETARY)}, + Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, + Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ ActionType: "VALID_FUNCTION_TYPE", - Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, Type: utils.StringPointer("test")}, + Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, + Type: utils.StringPointer("test")}, }, }, } @@ -1461,7 +1662,8 @@ func TestActionTransactionBalanceType(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.MONETARY)}, + Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, + Type: utils.StringPointer(utils.MONETARY)}, }, &Action{ ActionType: TOPUP, @@ -1497,7 +1699,8 @@ func TestActionTransactionBalanceNotType(t *testing.T) { actions: []*Action{ &Action{ ActionType: TOPUP, - Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, Type: utils.StringPointer(utils.VOICE)}, + Balance: &BalanceFilter{Value: &utils.ValueFormula{Static: 1.1}, + Type: utils.StringPointer(utils.VOICE)}, }, &Action{ ActionType: TOPUP, diff --git a/engine/balances.go b/engine/balances.go index e752c4863..84a1c35f5 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -234,7 +234,7 @@ func (b *Balance) GetMinutesForCredit(origCD *CallDescriptor, initialCredit floa ts.createIncrementsSlice() if cd.MaxRate > 0 && cd.MaxRateUnit > 0 { rate, _, rateUnit := ts.RateInterval.GetRateParameters(ts.GetGroupStart()) - if rate/rateUnit.Seconds() > cd.MaxRate/cd.MaxRateUnit.Seconds() { + if rate/float64(rateUnit.Nanoseconds()) > cd.MaxRate/float64(cd.MaxRateUnit.Nanoseconds()) { return } } @@ -309,14 +309,13 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala if !b.IsActiveAt(cd.TimeStart) || b.GetValue() <= 0 { return } - if duration, err := utils.ParseZeroRatingSubject(b.RatingSubject); err == nil { + if duration, err := utils.ParseZeroRatingSubject(cd.TOR, b.RatingSubject); err == nil { // we have *zero based units cc = cd.CreateCallCost() cc.Timespans = append(cc.Timespans, &TimeSpan{ TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd, }) - ts := cc.Timespans[0] ts.RoundToDuration(duration) ts.RateInterval = &RateInterval{ @@ -349,9 +348,10 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala for incIndex, inc := range ts.Increments { //log.Printf("INCREMENET: %+v", inc) - amount := inc.Duration.Seconds() + amount := float64(inc.Duration.Nanoseconds()) if b.Factor != nil { - amount = utils.Round(amount/b.Factor.GetValue(cd.TOR), globalRoundingDecimals, utils.ROUNDING_UP) + amount = utils.Round(amount/b.Factor.GetValue(cd.TOR), + globalRoundingDecimals, utils.ROUNDING_UP) } if b.GetValue() >= amount { b.SubstractValue(amount) @@ -440,7 +440,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala } // debit minutes and money - amount := inc.Duration.Seconds() + amount := float64(inc.Duration.Nanoseconds()) if b.Factor != nil { amount = utils.Round(amount/b.Factor.GetValue(cd.TOR), globalRoundingDecimals, utils.ROUNDING_UP) } @@ -517,7 +517,7 @@ func (b *Balance) debitUnits(cd *CallDescriptor, ub *Account, moneyBalances Bala } } else { inc.paid = false - // delete the rest of the unpiad increments/timespans + // delete the rest of the unpaid increments/timespans if incIndex == 0 { // cut the entire current timespan cc.Timespans = cc.Timespans[:tsIndex] diff --git a/engine/caches.go b/engine/caches.go new file mode 100644 index 000000000..8dbadbf8b --- /dev/null +++ b/engine/caches.go @@ -0,0 +1,44 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package engine + +import ( + "sync" + + "github.com/cgrates/cgrates/cache" + "github.com/cgrates/cgrates/config" +) + +func NewCacheS(cfg *config.CGRConfig, dm *DataManager) (c *CacheS) { + c = &CacheS{cfg: cfg, dm: dm, + cItems: make(map[string]chan struct{})} + for k := range cfg.CacheCfg() { + c.cItems[k] = make(chan struct{}) + } + cache.NewCache(cfg.CacheCfg()) // init cache + return +} + +// CacheS deals with cache preload and other cache related tasks +type CacheS struct { + cfg *config.CGRConfig + dm *DataManager + cItems map[string]chan struct{} // signal receiving of the + lMux sync.RWMutex +} diff --git a/engine/callcost.go b/engine/callcost.go index b66f6e27c..42def32c4 100644 --- a/engine/callcost.go +++ b/engine/callcost.go @@ -69,7 +69,7 @@ func (cc *CallCost) UpdateRatedUsage() time.Duration { return 0 } totalDuration := cc.GetDuration() - cc.RatedUsage = totalDuration.Seconds() + cc.RatedUsage = float64(totalDuration.Nanoseconds()) return totalDuration } @@ -121,15 +121,15 @@ func (cc *CallCost) ToDataCost() (*DataCost, error) { } dc.DataSpans = make([]*DataSpan, len(cc.Timespans)) for i, ts := range cc.Timespans { - length := ts.TimeEnd.Sub(ts.TimeStart).Seconds() - callDuration := ts.DurationIndex.Seconds() + length := ts.TimeEnd.Sub(ts.TimeStart).Nanoseconds() + callDuration := ts.DurationIndex.Nanoseconds() dc.DataSpans[i] = &DataSpan{ - DataStart: callDuration - length, - DataEnd: callDuration, + DataStart: float64(callDuration - length), + DataEnd: float64(callDuration), Cost: ts.Cost, ratingInfo: ts.ratingInfo, RateInterval: ts.RateInterval, - DataIndex: callDuration, + DataIndex: float64(callDuration), MatchedSubject: ts.MatchedSubject, MatchedPrefix: ts.MatchedPrefix, MatchedDestId: ts.MatchedDestId, @@ -138,7 +138,7 @@ 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(), + Amount: float64(incr.Duration.Nanoseconds()), Cost: incr.Cost, BalanceInfo: incr.BalanceInfo, CompressFactor: incr.CompressFactor, diff --git a/engine/callcost_test.go b/engine/callcost_test.go index 4f87cd25a..6ec384f94 100644 --- a/engine/callcost_test.go +++ b/engine/callcost_test.go @@ -27,7 +27,9 @@ import ( func TestSingleResultMerge(t *testing.T) { t1 := time.Date(2012, time.February, 2, 17, 0, 0, 0, time.UTC) t2 := time.Date(2012, time.February, 2, 17, 1, 0, 0, time.UTC) - cd := &CallDescriptor{Direction: utils.OUT, Category: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2} + cd := &CallDescriptor{Direction: utils.OUT, Category: "0", + Tenant: "vdf", Subject: "rif", + Destination: "0256", TimeStart: t1, TimeEnd: t2} cc1, _ := cd.getCost() if cc1.Cost != 61 { t.Errorf("expected 61 was %v", cc1.Cost) @@ -50,7 +52,7 @@ func TestSingleResultMerge(t *testing.T) { t.Errorf("Exdpected 120 was %v", cc1.Cost) } d := cc1.UpdateRatedUsage() - if d != 2*time.Minute || cc1.RatedUsage != 120.0 { + if d != 2*time.Minute || cc1.RatedUsage != 120.0*float64(time.Second) { t.Errorf("error updating rating usage: %v, %v", d, cc1.RatedUsage) } } diff --git a/engine/calldesc.go b/engine/calldesc.go index 1b3a03744..1fddce57a 100755 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -599,7 +599,7 @@ func (origCD *CallDescriptor) getMaxSessionDuration(origAcc *Account) (time.Dura for _, ts := range cc.Timespans { if cd.MaxRate > 0 && cd.MaxRateUnit > 0 { rate, _, rateUnit := ts.RateInterval.GetRateParameters(ts.GetGroupStart()) - if rate/rateUnit.Seconds() > cd.MaxRate/cd.MaxRateUnit.Seconds() { + if rate/float64(rateUnit.Nanoseconds()) > cd.MaxRate/float64(cd.MaxRateUnit.Nanoseconds()) { return utils.MinDuration(initialDuration, totalDuration), nil } } @@ -824,8 +824,8 @@ func (cd *CallDescriptor) refundIncrements() (err error) { if balance = account.BalanceMap[unitType].GetBalance(increment.BalanceInfo.Unit.UUID); balance == nil { return } - balance.AddValue(increment.Duration.Seconds()) - account.countUnits(-increment.Duration.Seconds(), unitType, cc, balance) + balance.AddValue(float64(increment.Duration.Nanoseconds())) + account.countUnits(-float64(increment.Duration.Nanoseconds()), unitType, cc, balance) } // check money too if increment.BalanceInfo.Monetary != nil && increment.BalanceInfo.Monetary.UUID != "" { diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 5068eadfa..df0cafe9e 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -39,12 +39,21 @@ func init() { func populateDB() { ats := []*Action{ - &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}}}, - &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), Weight: utils.Float64Pointer(20), Value: &utils.ValueFormula{Static: 10}, DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, + &Action{ActionType: "*topup", + Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), + Value: &utils.ValueFormula{Static: 10}}}, + &Action{ActionType: "*topup", + Balance: &BalanceFilter{Type: utils.StringPointer(utils.VOICE), + Weight: utils.Float64Pointer(20), + Value: &utils.ValueFormula{Static: 10 * float64(time.Second)}, + DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, } ats1 := []*Action{ - &Action{ActionType: "*topup", Balance: &BalanceFilter{Type: utils.StringPointer(utils.MONETARY), Value: &utils.ValueFormula{Static: 10}}, Weight: 10}, + &Action{ActionType: "*topup", + Balance: &BalanceFilter{ + Type: utils.StringPointer(utils.MONETARY), + Value: &utils.ValueFormula{Static: 10}}, Weight: 10}, &Action{ActionType: "*reset_account", Weight: 20}, } @@ -53,16 +62,21 @@ func populateDB() { 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}, + &Balance{Value: 200 * float64(time.Second), + DestinationIDs: utils.NewStringMap("NAT"), Weight: 10}, + &Balance{Value: 100 * float64(time.Second), + DestinationIDs: utils.NewStringMap("RET"), Weight: 20}, }}, } broker := &Account{ ID: "vdf:broker", 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}, + &Balance{Value: 20 * float64(time.Second), + DestinationIDs: utils.NewStringMap("NAT"), + Weight: 10, RatingSubject: "rif"}, + &Balance{Value: 100 * float64(time.Second), + DestinationIDs: utils.NewStringMap("RET"), Weight: 20}, }}, } luna := &Account{ @@ -77,8 +91,11 @@ func populateDB() { ID: "vdf:minitsboy", 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}, + &Balance{Value: 20 * float64(time.Second), + DestinationIDs: utils.NewStringMap("NAT"), + Weight: 10, RatingSubject: "rif"}, + &Balance{Value: 100 * float64(time.Second), + DestinationIDs: utils.NewStringMap("RET"), Weight: 20}, }, utils.MONETARY: Balances{ &Balance{Value: 100, Weight: 10}, @@ -116,7 +133,9 @@ func populateDB() { func debitTest(t *testing.T, wg *sync.WaitGroup) { t1 := time.Date(2017, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2017, time.February, 2, 17, 30, 59, 0, time.UTC) - cd := &CallDescriptor{Direction: "*out", Category: "call", Tenant: "cgrates.org", Account: "moneyp", Subject: "nt", Destination: "49", TimeStart: t1, TimeEnd: t2, LoopIndex: 0} + cd := &CallDescriptor{Direction: "*out", Category: "call", + Tenant: "cgrates.org", Account: "moneyp", Subject: "nt", + Destination: "49", TimeStart: t1, TimeEnd: t2, LoopIndex: 0} if _, err := cd.Debit(); err != nil { t.Errorf("Error debiting balance: %s", err) } @@ -144,7 +163,9 @@ func TestSerialDebit(t *testing.T) { wg.Wait() t1 := time.Date(2017, time.February, 2, 17, 30, 0, 0, time.UTC) t2 := time.Date(2017, time.February, 2, 17, 30, 59, 0, time.UTC) - cd := &CallDescriptor{Direction: "*out", Category: "call", Tenant: "cgrates.org", Account: "moneyp", Subject: "nt", Destination: "49", TimeStart: t1, TimeEnd: t2, LoopIndex: 0} + cd := &CallDescriptor{Direction: "*out", Category: "call", + Tenant: "cgrates.org", Account: "moneyp", Subject: "nt", + Destination: "49", TimeStart: t1, TimeEnd: t2, LoopIndex: 0} acc, err := cd.getAccount() if err != nil { @@ -152,14 +173,9 @@ func TestSerialDebit(t *testing.T) { } expBalance := initialBalance - float64(debitsToDo*60) if acc.BalanceMap[utils.MONETARY][0].GetValue() != expBalance { - t.Errorf("Balance does not match: %f, expected %f", acc.BalanceMap[utils.MONETARY][0].GetValue(), expBalance) + t.Errorf("Balance does not match: %f, expected %f", + acc.BalanceMap[utils.MONETARY][0].GetValue(), expBalance) } - /* - out, err := json.Marshal(acc) - if err == nil { - t.Log("Account: %s", string(out)) - } - */ } @@ -1308,9 +1324,11 @@ 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.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) + if cc1.Timespans[0].Increments[0].BalanceInfo.Unit.Value != 90*float64(time.Second) || + cc2.Timespans[0].Increments[0].BalanceInfo.Unit.Value != 80*float64(time.Second) { + t.Error("Error setting the Unit.Value: ", + cc1.Timespans[0].Increments[0].BalanceInfo.Unit.Value, + cc2.Timespans[0].Increments[0].BalanceInfo.Unit.Value) } // make Unit.Values have the same value cc1.Timespans[0].Increments[0].BalanceInfo.Unit.Value = 0 @@ -1595,8 +1613,9 @@ func TestMaxDebitConsumesMinutes(t *testing.T) { LoopIndex: 0, DurationIndex: 0} cd1.MaxDebit() - if cd1.account.BalanceMap[utils.VOICE][0].GetValue() != 20 { - t.Error("Error using minutes: ", cd1.account.BalanceMap[utils.VOICE][0].GetValue()) + if cd1.account.BalanceMap[utils.VOICE][0].GetValue() != 20*float64(time.Second) { + t.Error("Error using minutes: ", + cd1.account.BalanceMap[utils.VOICE][0].GetValue()) } } @@ -1662,23 +1681,32 @@ func TestCDRefundIncrements(t *testing.T) { &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}}, + &Balance{Uuid: "minutea", + Value: 10 * float64(time.Second), + Weight: 20, + DestinationIDs: utils.StringMap{"NAT": true}}, + &Balance{Uuid: "minuteb", + Value: 10 * float64(time.Second), + DestinationIDs: utils.StringMap{"RET": true}}, }, }, } dm.DataDB().SetAccount(ub) increments := Increments{ - &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}}, + &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() ub, _ = dm.DataDB().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 { + ub.BalanceMap[utils.VOICE][0].GetValue() != 13*float64(time.Second) || + ub.BalanceMap[utils.VOICE][1].GetValue() != 14*float64(time.Second) { t.Error("Error refunding money: ", utils.ToIJSON(ub.BalanceMap)) } } @@ -1717,7 +1745,8 @@ func TestCDDebitBalanceSubjectWithFallback(t *testing.T) { ID: "TCDDBSWF:account1", BalanceMap: map[string]Balances{ utils.VOICE: Balances{ - &Balance{ID: "voice1", Value: 60, RatingSubject: "SubjTCDDBSWF"}, + &Balance{ID: "voice1", Value: 60 * float64(time.Second), + RatingSubject: "SubjTCDDBSWF"}, }}, } dm.DataDB().SetAccount(acnt) diff --git a/engine/cdr.go b/engine/cdr.go index 89b11668b..6c33eef33 100644 --- a/engine/cdr.go +++ b/engine/cdr.go @@ -32,10 +32,11 @@ import ( func NewCDRFromExternalCDR(extCdr *ExternalCDR, timezone string) (*CDR, error) { var err error - cdr := &CDR{CGRID: extCdr.CGRID, RunID: extCdr.RunID, OrderID: extCdr.OrderID, ToR: extCdr.ToR, OriginID: extCdr.OriginID, OriginHost: extCdr.OriginHost, - Source: extCdr.Source, RequestType: extCdr.RequestType, Direction: extCdr.Direction, Tenant: extCdr.Tenant, Category: extCdr.Category, - Account: extCdr.Account, Subject: extCdr.Subject, Destination: extCdr.Destination, Supplier: extCdr.Supplier, - DisconnectCause: extCdr.DisconnectCause, CostSource: extCdr.CostSource, Cost: extCdr.Cost, Rated: extCdr.Rated} + cdr := &CDR{CGRID: extCdr.CGRID, RunID: extCdr.RunID, OrderID: extCdr.OrderID, ToR: extCdr.ToR, + OriginID: extCdr.OriginID, OriginHost: extCdr.OriginHost, + Source: extCdr.Source, RequestType: extCdr.RequestType, Tenant: extCdr.Tenant, Category: extCdr.Category, + Account: extCdr.Account, Subject: extCdr.Subject, Destination: extCdr.Destination, + CostSource: extCdr.CostSource, Cost: extCdr.Cost, Rated: extCdr.Rated} if extCdr.SetupTime != "" { if cdr.SetupTime, err = utils.ParseTimeDetectLayout(extCdr.SetupTime, timezone); err != nil { return nil, err @@ -50,12 +51,7 @@ func NewCDRFromExternalCDR(extCdr *ExternalCDR, timezone string) (*CDR, error) { } } if extCdr.Usage != "" { - if cdr.Usage, err = utils.ParseDurationWithSecs(extCdr.Usage); err != nil { - return nil, err - } - } - if extCdr.PDD != "" { - if cdr.PDD, err = utils.ParseDurationWithSecs(extCdr.PDD); err != nil { + if cdr.Usage, err = utils.ParseDurationWithNanosecs(extCdr.Usage); err != nil { return nil, err } } @@ -74,39 +70,35 @@ func NewCDRFromExternalCDR(extCdr *ExternalCDR, timezone string) (*CDR, error) { } func NewCDRWithDefaults(cfg *config.CGRConfig) *CDR { - return &CDR{ToR: utils.VOICE, RequestType: cfg.DefaultReqType, Direction: utils.OUT, Tenant: cfg.DefaultTenant, Category: cfg.DefaultCategory, + return &CDR{ToR: utils.VOICE, RequestType: cfg.DefaultReqType, + Tenant: cfg.DefaultTenant, Category: cfg.DefaultCategory, ExtraFields: make(map[string]string), Cost: -1} } type CDR struct { - CGRID string - RunID string - OrderID int64 // Stor order id used as export order id - OriginHost string // represents the IP address of the host generating the CDR (automatically populated by the server) - Source string // formally identifies the source of the CDR (free form field) - OriginID string // represents the unique accounting id given by the telecom switch generating the CDR - ToR string // type of record, meta-field, should map to one of the TORs hardcoded inside the server <*voice|*data|*sms|*generic> - RequestType string // matching the supported request types by the **CGRateS**, accepted values are hardcoded in the server . - Direction string // matching the supported direction identifiers of the CGRateS <*out> - Tenant string // tenant whom this record belongs - Category string // free-form filter for this record, matching the category defined in rating profiles. - Account string // account id (accounting subsystem) the record should be attached to - Subject string // rating subject (rating subsystem) this record should be attached to - Destination string // destination to be charged - SetupTime time.Time // set-up time of the event. Supported formats: datetime RFC3339 compatible, SQL datetime (eg: MySQL), unix timestamp. - PDD time.Duration // PDD value - AnswerTime time.Time // answer time of the event. Supported formats: datetime RFC3339 compatible, SQL datetime (eg: MySQL), unix timestamp. - Usage time.Duration // event usage information (eg: in case of tor=*voice this will represent the total duration of a call) - Supplier string // Supplier information when available - DisconnectCause string // Disconnect cause of the event - ExtraFields map[string]string // Extra fields to be stored in CDR - CostSource string // The source of this cost - Cost float64 - CostDetails *CallCost // Attach the cost details to CDR when possible - AccountSummary *AccountSummary // Store AccountSummary information - ExtraInfo string // Container for extra information related to this CDR, eg: populated with error reason in case of error on calculation - Rated bool // Mark the CDR as rated so we do not process it during rating - Partial bool // Used for partial record processing by CDRC + CGRID string + RunID string + OrderID int64 // Stor order id used as export order id + OriginHost string // represents the IP address of the host generating the CDR (automatically populated by the server) + Source string // formally identifies the source of the CDR (free form field) + OriginID string // represents the unique accounting id given by the telecom switch generating the CDR + ToR string // type of record, meta-field, should map to one of the TORs hardcoded inside the server <*voice|*data|*sms|*generic> + RequestType string // matching the supported request types by the **CGRateS**, accepted values are hardcoded in the server . + Tenant string // tenant whom this record belongs + Category string // free-form filter for this record, matching the category defined in rating profiles. + Account string // account id (accounting subsystem) the record should be attached to + Subject string // rating subject (rating subsystem) this record should be attached to + Destination string // destination to be charged + SetupTime time.Time // set-up time of the event. Supported formats: datetime RFC3339 compatible, SQL datetime (eg: MySQL), unix timestamp. + AnswerTime time.Time // answer time of the event. Supported formats: datetime RFC3339 compatible, SQL datetime (eg: MySQL), unix timestamp. + Usage time.Duration // event usage information (eg: in case of tor=*voice this will represent the total duration of a call) + ExtraFields map[string]string // Extra fields to be stored in CDR + ExtraInfo string // Container for extra information related to this CDR, eg: populated with error reason in case of error on calculation + Partial bool // Used for partial record processing by CDRC + Rated bool // Mark the CDR as rated so we do not process it during rating + CostSource string // The source of this cost + Cost float64 + CostDetails *CallCost // Attach the cost details to CDR when possible } func (cdr *CDR) CostDetailsJson() string { @@ -114,11 +106,6 @@ func (cdr *CDR) CostDetailsJson() string { return string(mrshled) } -func (cdr *CDR) AccountSummaryJson() string { - mrshled, _ := json.Marshal(cdr.AccountSummary) - return string(mrshled) -} - func (cdr *CDR) ComputeCGRID() { cdr.CGRID = utils.Sha1(cdr.OriginID, cdr.SetupTime.UTC().String()) } @@ -173,8 +160,6 @@ func (cdr *CDR) FieldAsString(rsrFld *utils.RSRField) string { return rsrFld.ParseValue(cdr.Source) case utils.REQTYPE: return rsrFld.ParseValue(cdr.RequestType) - case utils.DIRECTION: - return rsrFld.ParseValue(cdr.Direction) case utils.TENANT: return rsrFld.ParseValue(cdr.Tenant) case utils.CATEGORY: @@ -187,16 +172,10 @@ func (cdr *CDR) FieldAsString(rsrFld *utils.RSRField) string { return rsrFld.ParseValue(cdr.Destination) case utils.SETUP_TIME: return rsrFld.ParseValue(cdr.SetupTime.Format(time.RFC3339)) - case utils.PDD: - return strconv.FormatFloat(cdr.PDD.Seconds(), 'f', -1, 64) case utils.ANSWER_TIME: return rsrFld.ParseValue(cdr.AnswerTime.Format(time.RFC3339)) case utils.USAGE: - return strconv.FormatFloat(cdr.Usage.Seconds(), 'f', -1, 64) - case utils.SUPPLIER: - return rsrFld.ParseValue(cdr.Supplier) - case utils.DISCONNECT_CAUSE: - return rsrFld.ParseValue(cdr.DisconnectCause) + return cdr.Usage.String() case utils.MEDI_RUNID: return rsrFld.ParseValue(cdr.RunID) case utils.RATED_FLD: @@ -205,8 +184,6 @@ func (cdr *CDR) FieldAsString(rsrFld *utils.RSRField) string { return rsrFld.ParseValue(strconv.FormatFloat(cdr.Cost, 'f', -1, 64)) // Recommended to use FormatCost case utils.COST_DETAILS: return rsrFld.ParseValue(cdr.CostDetailsJson()) - case utils.ACCOUNT_SUMMARY: - return rsrFld.ParseValue(cdr.AccountSummaryJson()) case utils.PartialField: return rsrFld.ParseValue(strconv.FormatBool(cdr.Partial)) default: @@ -230,8 +207,6 @@ func (cdr *CDR) ParseFieldValue(fieldId, fieldVal, timezone string) error { cdr.OriginID += fieldVal case utils.REQTYPE: cdr.RequestType += fieldVal - case utils.DIRECTION: - cdr.Direction += fieldVal case utils.TENANT: cdr.Tenant += fieldVal case utils.CATEGORY: @@ -248,22 +223,14 @@ func (cdr *CDR) ParseFieldValue(fieldId, fieldVal, timezone string) error { if cdr.SetupTime, err = utils.ParseTimeDetectLayout(fieldVal, timezone); err != nil { return fmt.Errorf("Cannot parse answer time field with value: %s, err: %s", fieldVal, err.Error()) } - case utils.PDD: - if cdr.PDD, err = utils.ParseDurationWithSecs(fieldVal); err != nil { - return fmt.Errorf("Cannot parse answer time field with value: %s, err: %s", fieldVal, err.Error()) - } case utils.ANSWER_TIME: if cdr.AnswerTime, err = utils.ParseTimeDetectLayout(fieldVal, timezone); err != nil { return fmt.Errorf("Cannot parse answer time field with value: %s, err: %s", fieldVal, err.Error()) } case utils.USAGE: - if cdr.Usage, err = utils.ParseDurationWithSecs(fieldVal); err != nil { + if cdr.Usage, err = utils.ParseDurationWithNanosecs(fieldVal); err != nil { return fmt.Errorf("Cannot parse duration field with value: %s, err: %s", fieldVal, err.Error()) } - case utils.SUPPLIER: - cdr.Supplier += fieldVal - case utils.DISCONNECT_CAUSE: - cdr.DisconnectCause += fieldVal case utils.COST: if cdr.Cost, err = strconv.ParseFloat(fieldVal, 64); err != nil { return fmt.Errorf("Cannot parse cost field with value: %s, err: %s", fieldVal, err.Error()) @@ -296,8 +263,8 @@ func (cdr *CDR) Clone() *CDR { } // Used in mediation, primaryMandatory marks whether missing field out of request represents error or can be ignored -func (cdr *CDR) ForkCdr(runId string, RequestTypeFld, directionFld, tenantFld, categFld, accountFld, subjectFld, destFld, setupTimeFld, PDDFld, - answerTimeFld, durationFld, supplierFld, disconnectCauseFld, ratedFld, costFld *utils.RSRField, +func (cdr *CDR) ForkCdr(runId string, RequestTypeFld, tenantFld, categFld, accountFld, subjectFld, destFld, setupTimeFld, + answerTimeFld, durationFld, ratedFld, costFld *utils.RSRField, extraFlds []*utils.RSRField, primaryMandatory bool, timezone string) (*CDR, error) { if RequestTypeFld == nil { RequestTypeFld, _ = utils.NewRSRField(utils.META_DEFAULT) @@ -305,12 +272,6 @@ func (cdr *CDR) ForkCdr(runId string, RequestTypeFld, directionFld, tenantFld, c if RequestTypeFld.Id == utils.META_DEFAULT { RequestTypeFld.Id = utils.REQTYPE } - if directionFld == nil { - directionFld, _ = utils.NewRSRField(utils.META_DEFAULT) - } - if directionFld.Id == utils.META_DEFAULT { - directionFld.Id = utils.DIRECTION - } if tenantFld == nil { tenantFld, _ = utils.NewRSRField(utils.META_DEFAULT) } @@ -359,24 +320,6 @@ func (cdr *CDR) ForkCdr(runId string, RequestTypeFld, directionFld, tenantFld, c if durationFld.Id == utils.META_DEFAULT { durationFld.Id = utils.USAGE } - if PDDFld == nil { - PDDFld, _ = utils.NewRSRField(utils.META_DEFAULT) - } - if PDDFld.Id == utils.META_DEFAULT { - PDDFld.Id = utils.PDD - } - if supplierFld == nil { - supplierFld, _ = utils.NewRSRField(utils.META_DEFAULT) - } - if supplierFld.Id == utils.META_DEFAULT { - supplierFld.Id = utils.SUPPLIER - } - if disconnectCauseFld == nil { - disconnectCauseFld, _ = utils.NewRSRField(utils.META_DEFAULT) - } - if disconnectCauseFld.Id == utils.META_DEFAULT { - disconnectCauseFld.Id = utils.DISCONNECT_CAUSE - } if ratedFld == nil { ratedFld, _ = utils.NewRSRField(utils.META_DEFAULT) } @@ -402,10 +345,6 @@ func (cdr *CDR) ForkCdr(runId string, RequestTypeFld, directionFld, tenantFld, c if primaryMandatory && len(frkStorCdr.RequestType) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.REQTYPE, RequestTypeFld.Id) } - frkStorCdr.Direction = cdr.FieldAsString(directionFld) - if primaryMandatory && len(frkStorCdr.Direction) == 0 { - return nil, utils.NewErrMandatoryIeMissing(utils.DIRECTION, directionFld.Id) - } frkStorCdr.Tenant = cdr.FieldAsString(tenantFld) if primaryMandatory && len(frkStorCdr.Tenant) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.TENANT, tenantFld.Id) @@ -441,17 +380,9 @@ func (cdr *CDR) ForkCdr(runId string, RequestTypeFld, directionFld, tenantFld, c durStr := cdr.FieldAsString(durationFld) if primaryMandatory && len(durStr) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.USAGE, durationFld.Id) - } else if frkStorCdr.Usage, err = utils.ParseDurationWithSecs(durStr); err != nil { + } else if frkStorCdr.Usage, err = utils.ParseDurationWithNanosecs(durStr); err != nil { return nil, err } - PDDStr := cdr.FieldAsString(PDDFld) - if primaryMandatory && len(PDDStr) == 0 { - return nil, utils.NewErrMandatoryIeMissing(utils.PDD, PDDFld.Id) - } else if frkStorCdr.PDD, err = utils.ParseDurationWithSecs(PDDStr); err != nil { - return nil, err - } - frkStorCdr.Supplier = cdr.FieldAsString(supplierFld) - frkStorCdr.DisconnectCause = cdr.FieldAsString(disconnectCauseFld) ratedStr := cdr.FieldAsString(ratedFld) if primaryMandatory && len(ratedStr) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.RATED_FLD, ratedFld.Id) @@ -472,32 +403,35 @@ func (cdr *CDR) ForkCdr(runId string, RequestTypeFld, directionFld, tenantFld, c } func (cdr *CDR) AsExternalCDR() *ExternalCDR { + var usageStr string + switch cdr.ToR { + case utils.VOICE: // usage as time + usageStr = cdr.Usage.String() + default: // usage as units + usageStr = strconv.FormatInt(cdr.Usage.Nanoseconds(), 10) + } return &ExternalCDR{CGRID: cdr.CGRID, - RunID: cdr.RunID, - OrderID: cdr.OrderID, - OriginHost: cdr.OriginHost, - Source: cdr.Source, - OriginID: cdr.OriginID, - ToR: cdr.ToR, - RequestType: cdr.RequestType, - Direction: cdr.Direction, - Tenant: cdr.Tenant, - Category: cdr.Category, - Account: cdr.Account, - Subject: cdr.Subject, - Destination: cdr.Destination, - SetupTime: cdr.SetupTime.Format(time.RFC3339), - PDD: cdr.FieldAsString(&utils.RSRField{Id: utils.PDD}), - AnswerTime: cdr.AnswerTime.Format(time.RFC3339), - Usage: cdr.FormatUsage(utils.SECONDS), - Supplier: cdr.Supplier, - DisconnectCause: cdr.DisconnectCause, - ExtraFields: cdr.ExtraFields, - CostSource: cdr.CostSource, - Cost: cdr.Cost, - CostDetails: cdr.CostDetailsJson(), - ExtraInfo: cdr.ExtraInfo, - Rated: cdr.Rated, + RunID: cdr.RunID, + OrderID: cdr.OrderID, + OriginHost: cdr.OriginHost, + Source: cdr.Source, + OriginID: cdr.OriginID, + ToR: cdr.ToR, + RequestType: cdr.RequestType, + Tenant: cdr.Tenant, + Category: cdr.Category, + Account: cdr.Account, + Subject: cdr.Subject, + Destination: cdr.Destination, + SetupTime: cdr.SetupTime.Format(time.RFC3339), + AnswerTime: cdr.AnswerTime.Format(time.RFC3339), + Usage: usageStr, + ExtraFields: cdr.ExtraFields, + CostSource: cdr.CostSource, + Cost: cdr.Cost, + CostDetails: cdr.CostDetailsJson(), + ExtraInfo: cdr.ExtraInfo, + Rated: cdr.Rated, } } @@ -520,15 +454,6 @@ func (cdr *CDR) GetUUID() string { func (cdr *CDR) GetSessionIds() []string { return []string{cdr.GetUUID()} } -func (cdr *CDR) GetDirection(fieldName string) string { - if utils.IsSliceMember([]string{utils.DIRECTION, utils.META_DEFAULT, ""}, fieldName) { - return cdr.Direction - } - if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value - return fieldName[len(utils.STATIC_VALUE_PREFIX):] - } - return cdr.FieldAsString(&utils.RSRField{Id: fieldName}) -} func (cdr *CDR) GetSubject(fieldName string) string { if utils.IsSliceMember([]string{utils.SUBJECT, utils.META_DEFAULT, ""}, fieldName) { return cdr.Subject @@ -629,31 +554,7 @@ func (cdr *CDR) GetDuration(fieldName string) (time.Duration, error) { } else { durVal = cdr.FieldAsString(&utils.RSRField{Id: fieldName}) } - return utils.ParseDurationWithSecs(durVal) -} -func (cdr *CDR) GetPdd(fieldName string) (time.Duration, error) { - if utils.IsSliceMember([]string{utils.PDD, utils.META_DEFAULT, ""}, fieldName) { - return cdr.PDD, nil - } - var PDDVal string - if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value - PDDVal = fieldName[len(utils.STATIC_VALUE_PREFIX):] - } else { - PDDVal = cdr.FieldAsString(&utils.RSRField{Id: fieldName}) - } - return utils.ParseDurationWithSecs(PDDVal) -} -func (cdr *CDR) GetSupplier(fieldName string) string { - if utils.IsSliceMember([]string{utils.SUPPLIER, utils.META_DEFAULT, ""}, fieldName) { - return cdr.Supplier - } - return cdr.FieldAsString(&utils.RSRField{Id: fieldName}) -} -func (cdr *CDR) GetDisconnectCause(fieldName string) string { - if utils.IsSliceMember([]string{utils.DISCONNECT_CAUSE, utils.META_DEFAULT, ""}, fieldName) { - return cdr.DisconnectCause - } - return cdr.FieldAsString(&utils.RSRField{Id: fieldName}) + return utils.ParseDurationWithNanosecs(durVal) } func (cdr *CDR) GetOriginatorIP(fieldName string) string { if utils.IsSliceMember([]string{utils.CDRHOST, utils.META_DEFAULT, ""}, fieldName) { @@ -807,18 +708,14 @@ func (cdr *CDR) AsMapStringIface() (mp map[string]interface{}, err error) { mp[utils.ACCID] = cdr.OriginID mp[utils.TOR] = cdr.ToR mp[utils.REQTYPE] = cdr.RequestType - mp[utils.DIRECTION] = cdr.Direction mp[utils.TENANT] = cdr.Tenant mp[utils.CATEGORY] = cdr.Category mp[utils.ACCOUNT] = cdr.Account mp[utils.SUBJECT] = cdr.Subject mp[utils.DESTINATION] = cdr.Destination mp[utils.SETUP_TIME] = cdr.SetupTime - mp[utils.PDD] = cdr.PDD mp[utils.ANSWER_TIME] = cdr.AnswerTime mp[utils.USAGE] = cdr.Usage - mp[utils.SUPPLIER] = cdr.Supplier - mp[utils.DISCONNECT_CAUSE] = cdr.DisconnectCause mp[utils.CostSource] = cdr.CostSource mp[utils.COST] = cdr.Cost mp[utils.COST_DETAILS] = cdr.CostDetails @@ -868,40 +765,97 @@ func (cdr *CDR) AsExportMap(exportFields []*config.CfgCdrField, httpSkipTlsCheck return } +// AsCDRsTBL converts the CDR into the format used for SQL storage +func (cdr *CDR) AsCDRsql() (cdrSql *CDRsql) { + cdrSql = new(CDRsql) + cdrSql.Cgrid = cdr.CGRID + cdrSql.RunID = cdr.RunID + cdrSql.OriginHost = cdr.OriginHost + cdrSql.Source = cdr.Source + cdrSql.OriginID = cdr.OriginID + cdrSql.TOR = cdr.ToR + cdrSql.RequestType = cdr.RequestType + cdrSql.Tenant = cdr.Tenant + cdrSql.Category = cdr.Category + cdrSql.Account = cdr.Account + cdrSql.Subject = cdr.Subject + cdrSql.Destination = cdr.Destination + cdrSql.SetupTime = cdr.SetupTime + cdrSql.AnswerTime = cdr.AnswerTime + cdrSql.Usage = cdr.Usage.Nanoseconds() + cdrSql.ExtraFields = utils.ToJSON(cdr.ExtraFields) + cdrSql.CostSource = cdr.CostSource + cdrSql.Cost = cdr.Cost + cdrSql.CostDetails = utils.ToJSON(cdr.CostDetails) + cdrSql.ExtraInfo = cdr.ExtraInfo + cdrSql.CreatedAt = time.Now() + return +} + +// NewCDRFromSQL converts the CDRsql into CDR +func NewCDRFromSQL(cdrSql *CDRsql) (cdr *CDR, err error) { + cdr = new(CDR) + cdr.CGRID = cdrSql.Cgrid + cdr.RunID = cdrSql.RunID + cdr.OriginHost = cdrSql.OriginHost + cdr.Source = cdrSql.Source + cdr.OriginID = cdrSql.OriginID + cdr.OrderID = cdrSql.ID + cdr.ToR = cdrSql.TOR + cdr.RequestType = cdrSql.RequestType + cdr.Tenant = cdrSql.Tenant + cdr.Category = cdrSql.Category + cdr.Account = cdrSql.Account + cdr.Subject = cdrSql.Subject + cdr.Destination = cdrSql.Destination + cdr.SetupTime = cdrSql.SetupTime + cdr.AnswerTime = cdrSql.AnswerTime + cdr.Usage = time.Duration(cdrSql.Usage) + cdr.CostSource = cdrSql.CostSource + cdr.Cost = cdrSql.Cost + cdr.ExtraInfo = cdrSql.ExtraInfo + if cdrSql.ExtraFields != "" { + if err = json.Unmarshal([]byte(cdrSql.ExtraFields), &cdr.ExtraFields); err != nil { + return nil, err + } + } + if cdrSql.CostDetails != "" { + if err = json.Unmarshal([]byte(cdrSql.CostDetails), &cdr.CostDetails); err != nil { + return nil, err + } + } + return +} + type ExternalCDR struct { - CGRID string - RunID string - OrderID int64 - OriginHost string - Source string - OriginID string - ToR string - RequestType string - Direction string - Tenant string - Category string - Account string - Subject string - Destination string - SetupTime string - PDD string - AnswerTime string - Usage string - Supplier string - DisconnectCause string - ExtraFields map[string]string - CostSource string - Cost float64 - CostDetails string - ExtraInfo string - Rated bool // Mark the CDR as rated so we do not process it during mediation + CGRID string + RunID string + OrderID int64 + OriginHost string + Source string + OriginID string + ToR string + RequestType string + Tenant string + Category string + Account string + Subject string + Destination string + SetupTime string + AnswerTime string + Usage string + ExtraFields map[string]string + CostSource string + Cost float64 + CostDetails string + ExtraInfo string + Rated bool // Mark the CDR as rated so we do not process it during mediation } // Used when authorizing requests from outside, eg ApierV1.GetMaxUsage type UsageRecord struct { ToR string RequestType string - Direction string Tenant string Category string Account string @@ -915,15 +869,15 @@ type UsageRecord struct { func (self *UsageRecord) AsCDR(timezone string) (*CDR, error) { var err error - cdr := &CDR{CGRID: self.GetId(), ToR: self.ToR, RequestType: self.RequestType, Direction: self.Direction, - Tenant: self.Tenant, Category: self.Category, Account: self.Account, Subject: self.Subject, Destination: self.Destination} + cdr := &CDR{CGRID: self.GetId(), ToR: self.ToR, RequestType: self.RequestType, Tenant: self.Tenant, + Category: self.Category, Account: self.Account, Subject: self.Subject, Destination: self.Destination} if cdr.SetupTime, err = utils.ParseTimeDetectLayout(self.SetupTime, timezone); err != nil { return nil, err } if cdr.AnswerTime, err = utils.ParseTimeDetectLayout(self.AnswerTime, timezone); err != nil { return nil, err } - if cdr.Usage, err = utils.ParseDurationWithSecs(self.Usage); err != nil { + if cdr.Usage, err = utils.ParseDurationWithNanosecs(self.Usage); err != nil { return nil, err } if self.ExtraFields != nil { @@ -940,7 +894,7 @@ func (self *UsageRecord) AsCallDescriptor(timezone string, denyNegative bool) (* cd := &CallDescriptor{ CgrID: self.GetId(), TOR: self.ToR, - Direction: self.Direction, + Direction: utils.OUT, Tenant: self.Tenant, Category: self.Category, Subject: self.Subject, @@ -955,7 +909,7 @@ func (self *UsageRecord) AsCallDescriptor(timezone string, denyNegative bool) (* if cd.TimeStart, err = utils.ParseTimeDetectLayout(timeStr, timezone); err != nil { return nil, err } - if usage, err := utils.ParseDurationWithSecs(self.Usage); err != nil { + if usage, err := utils.ParseDurationWithNanosecs(self.Usage); err != nil { return nil, err } else { cd.TimeEnd = cd.TimeStart.Add(usage) @@ -970,5 +924,5 @@ func (self *UsageRecord) AsCallDescriptor(timezone string, denyNegative bool) (* } func (self *UsageRecord) GetId() string { - return utils.Sha1(self.ToR, self.RequestType, self.Direction, self.Tenant, self.Category, self.Account, self.Subject, self.Destination, self.SetupTime, self.AnswerTime, self.Usage) + return utils.Sha1(self.ToR, self.RequestType, self.Tenant, self.Category, self.Account, self.Subject, self.Destination, self.SetupTime, self.AnswerTime, self.Usage) } diff --git a/engine/cdr_it_test.go b/engine/cdr_it_test.go index f1989e99b..6975abe7f 100644 --- a/engine/cdr_it_test.go +++ b/engine/cdr_it_test.go @@ -36,7 +36,7 @@ func TestHttpJsonPost(t *testing.T) { cdrOut := &ExternalCDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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", + Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", Account: "account1", Subject: "tgooiscs0014", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String(), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String(), RunID: utils.DEFAULT_RUNID, diff --git a/engine/cdr_test.go b/engine/cdr_test.go index c016609f0..94a4b2451 100644 --- a/engine/cdr_test.go +++ b/engine/cdr_test.go @@ -33,17 +33,24 @@ func TestCDRInterfaces(t *testing.T) { } func TestNewCDRFromExternalCDR(t *testing.T) { - extCdr := &ExternalCDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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: "1002", Supplier: "SUPPL1", + extCdr := &ExternalCDR{ + CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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, + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: "2013-11-07T08:42:20Z", AnswerTime: "2013-11-07T08:42:26Z", RunID: utils.DEFAULT_RUNID, - Usage: "0.00000001", PDD: "7.0", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, Rated: true, + Usage: "10", Cost: 1.01, Rated: true, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } - eStorCdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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: "1002", Supplier: "SUPPL1", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10), PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, Rated: true, + eStorCdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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, RunID: utils.DEFAULT_RUNID, + Tenant: "cgrates.org", Category: "call", Account: "1001", + Subject: "1001", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10), Cost: 1.01, Rated: true, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } if CDR, err := NewCDRFromExternalCDR(extCdr, ""); err != nil { t.Error(err) @@ -53,11 +60,15 @@ func TestNewCDRFromExternalCDR(t *testing.T) { } func TestCDRClone(t *testing.T) { - storCdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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: "1002", Supplier: "SUPPL1", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10), PDD: time.Duration(7) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, Rated: true, + storCdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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, Tenant: "cgrates.org", + Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10), + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Cost: 1.01, Rated: true, } if clnStorCdr := storCdr.Clone(); !reflect.DeepEqual(storCdr, clnStorCdr) { t.Errorf("Expecting: %+v, received: %+v", storCdr, clnStorCdr) @@ -65,13 +76,14 @@ func TestCDRClone(t *testing.T) { } func TestFieldAsString(t *testing.T) { - cdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "test", RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", - Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + cdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), + OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", Source: "test", RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", + Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, PDD: time.Duration(5) * time.Second, Supplier: "SUPPL1", + Usage: time.Duration(10) * time.Second, Cost: 1.01, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, - Cost: 1.01, } if cdr.FieldAsString(&utils.RSRField{Id: utils.CGRID}) != cdr.CGRID || cdr.FieldAsString(&utils.RSRField{Id: utils.ORDERID}) != "123" || @@ -80,16 +92,13 @@ func TestFieldAsString(t *testing.T) { cdr.FieldAsString(&utils.RSRField{Id: utils.CDRHOST}) != cdr.OriginHost || cdr.FieldAsString(&utils.RSRField{Id: utils.CDRSOURCE}) != cdr.Source || cdr.FieldAsString(&utils.RSRField{Id: utils.REQTYPE}) != cdr.RequestType || - cdr.FieldAsString(&utils.RSRField{Id: utils.DIRECTION}) != cdr.Direction || cdr.FieldAsString(&utils.RSRField{Id: utils.CATEGORY}) != cdr.Category || cdr.FieldAsString(&utils.RSRField{Id: utils.ACCOUNT}) != cdr.Account || cdr.FieldAsString(&utils.RSRField{Id: utils.SUBJECT}) != cdr.Subject || cdr.FieldAsString(&utils.RSRField{Id: utils.DESTINATION}) != cdr.Destination || cdr.FieldAsString(&utils.RSRField{Id: utils.SETUP_TIME}) != cdr.SetupTime.Format(time.RFC3339) || cdr.FieldAsString(&utils.RSRField{Id: utils.ANSWER_TIME}) != cdr.AnswerTime.Format(time.RFC3339) || - cdr.FieldAsString(&utils.RSRField{Id: utils.USAGE}) != "10" || - cdr.FieldAsString(&utils.RSRField{Id: utils.PDD}) != "5" || - cdr.FieldAsString(&utils.RSRField{Id: utils.SUPPLIER}) != cdr.Supplier || + cdr.FieldAsString(&utils.RSRField{Id: utils.USAGE}) != "10s" || cdr.FieldAsString(&utils.RSRField{Id: utils.MEDI_RUNID}) != cdr.RunID || cdr.FieldAsString(&utils.RSRField{Id: utils.COST}) != "1.01" || cdr.FieldAsString(&utils.RSRField{Id: "field_extr1"}) != cdr.ExtraFields["field_extr1"] || @@ -103,16 +112,13 @@ func TestFieldAsString(t *testing.T) { cdr.FieldAsString(&utils.RSRField{Id: utils.CDRHOST}) != cdr.OriginHost, cdr.FieldAsString(&utils.RSRField{Id: utils.CDRSOURCE}) != cdr.Source, cdr.FieldAsString(&utils.RSRField{Id: utils.REQTYPE}) != cdr.RequestType, - cdr.FieldAsString(&utils.RSRField{Id: utils.DIRECTION}) != cdr.Direction, cdr.FieldAsString(&utils.RSRField{Id: utils.CATEGORY}) != cdr.Category, cdr.FieldAsString(&utils.RSRField{Id: utils.ACCOUNT}) != cdr.Account, cdr.FieldAsString(&utils.RSRField{Id: utils.SUBJECT}) != cdr.Subject, cdr.FieldAsString(&utils.RSRField{Id: utils.DESTINATION}) != cdr.Destination, cdr.FieldAsString(&utils.RSRField{Id: utils.SETUP_TIME}) != cdr.SetupTime.Format(time.RFC3339), cdr.FieldAsString(&utils.RSRField{Id: utils.ANSWER_TIME}) != cdr.AnswerTime.Format(time.RFC3339), - cdr.FieldAsString(&utils.RSRField{Id: utils.USAGE}) != "10", - cdr.FieldAsString(&utils.RSRField{Id: utils.PDD}) != "5", - cdr.FieldAsString(&utils.RSRField{Id: utils.SUPPLIER}) != cdr.Supplier, + cdr.FieldAsString(&utils.RSRField{Id: utils.USAGE}) != "10s", cdr.FieldAsString(&utils.RSRField{Id: utils.MEDI_RUNID}) != cdr.RunID, cdr.FieldAsString(&utils.RSRField{Id: utils.COST}) != "1.01", cdr.FieldAsString(&utils.RSRField{Id: "field_extr1"}) != cdr.ExtraFields["field_extr1"], @@ -122,15 +128,18 @@ func TestFieldAsString(t *testing.T) { } func TestFieldsAsString(t *testing.T) { - cdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "test", RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", - Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, PDD: time.Duration(5) * time.Second, Supplier: "SUPPL1", + cdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), + OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: "test", + RequestType: utils.META_RATED, Tenant: "cgrates.org", + Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, } eVal := "call_from_1001" - if val := cdr.FieldsAsString(utils.ParseRSRFieldsMustCompile("Category;^_from_;Account", utils.INFIELD_SEP)); val != eVal { + if val := cdr.FieldsAsString( + utils.ParseRSRFieldsMustCompile("Category;^_from_;Account", utils.INFIELD_SEP)); val != eVal { t.Errorf("Expecting : %s, received: %s", eVal, val) } } @@ -265,40 +274,47 @@ func TestCDRAsHttpForm(t *testing.T) { */ func TestCDRForkCdr(t *testing.T) { - storCdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 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: "1002", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), PDD: time.Duration(200) * time.Millisecond, - AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, Supplier: "suppl1", - ExtraFields: map[string]string{"field_extr1": "val_extr1", "field_extr2": "valextr2"}, Cost: 1.01} - rtSampleCdrOut, err := storCdr.ForkCdr("sample_run1", &utils.RSRField{Id: utils.REQTYPE}, &utils.RSRField{Id: utils.DIRECTION}, &utils.RSRField{Id: utils.TENANT}, + storCdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), + OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, + RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", Account: "1001", + Subject: "1001", Destination: "1002", RunID: utils.DEFAULT_RUNID, + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, Cost: 1.01, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "field_extr2": "valextr2"}} + rtSampleCdrOut, err := storCdr.ForkCdr("sample_run1", &utils.RSRField{Id: utils.REQTYPE}, &utils.RSRField{Id: utils.TENANT}, &utils.RSRField{Id: utils.CATEGORY}, &utils.RSRField{Id: utils.ACCOUNT}, &utils.RSRField{Id: utils.SUBJECT}, &utils.RSRField{Id: utils.DESTINATION}, - &utils.RSRField{Id: utils.SETUP_TIME}, &utils.RSRField{Id: utils.PDD}, &utils.RSRField{Id: utils.ANSWER_TIME}, &utils.RSRField{Id: utils.USAGE}, - &utils.RSRField{Id: utils.SUPPLIER}, &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, &utils.RSRField{Id: utils.RATED_FLD}, &utils.RSRField{Id: utils.COST}, + &utils.RSRField{Id: utils.SETUP_TIME}, &utils.RSRField{Id: utils.ANSWER_TIME}, &utils.RSRField{Id: utils.USAGE}, + &utils.RSRField{Id: utils.RATED_FLD}, &utils.RSRField{Id: utils.COST}, []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "field_extr2"}}, true, "") if err != nil { t.Error("Unexpected error received", err) } - expctSplRatedCdr := &CDR{CGRID: storCdr.CGRID, 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: "1002", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), PDD: time.Duration(200) * time.Millisecond, AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(10) * time.Second, Supplier: "suppl1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "field_extr2": "valextr2"}, - RunID: "sample_run1", Rated: false, Cost: 1.01} + expctSplRatedCdr := &CDR{CGRID: storCdr.CGRID, ToR: utils.VOICE, OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "field_extr2": "valextr2"}, + RunID: "sample_run1", Rated: false, Cost: 1.01} if !reflect.DeepEqual(expctSplRatedCdr, rtSampleCdrOut) { t.Errorf("Expected: %v, received: %v", expctSplRatedCdr, rtSampleCdrOut) } } func TestCDRForkCdrStaticVals(t *testing.T) { - storCdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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", + storCdr := CDR{ + CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, + Usage: time.Duration(10) * time.Second, Cost: 1.01, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } rsrStPostpaid, _ := utils.NewRSRField("^" + utils.META_POSTPAID) - rsrStIn, _ := utils.NewRSRField("^*in") rsrStCgr, _ := utils.NewRSRField("^cgrates.com") rsrStPC, _ := utils.NewRSRField("^premium_call") rsrStFA, _ := utils.NewRSRField("^first_account") @@ -306,52 +322,55 @@ func TestCDRForkCdrStaticVals(t *testing.T) { rsrStST, _ := utils.NewRSRField("^2013-12-07T08:42:24Z") rsrStAT, _ := utils.NewRSRField("^2013-12-07T08:42:26Z") rsrStDur, _ := utils.NewRSRField("^12s") - rsrStSuppl, _ := utils.NewRSRField("^supplier1") - rsrStDCause, _ := utils.NewRSRField("^HANGUP_COMPLETE") - rsrPDD, _ := utils.NewRSRField("^3") rsrStRated, _ := utils.NewRSRField("^true") rsrStCost, _ := utils.NewRSRField("^1.2") - rtCdrOut2, err := storCdr.ForkCdr("wholesale_run", rsrStPostpaid, rsrStIn, rsrStCgr, rsrStPC, rsrStFA, rsrStFS, &utils.RSRField{Id: utils.DESTINATION}, - rsrStST, rsrPDD, rsrStAT, rsrStDur, rsrStSuppl, rsrStDCause, rsrStRated, rsrStCost, []*utils.RSRField{}, true, "") + rtCdrOut2, err := storCdr.ForkCdr("wholesale_run", rsrStPostpaid, rsrStCgr, rsrStPC, rsrStFA, rsrStFS, &utils.RSRField{Id: utils.DESTINATION}, + rsrStST, rsrStAT, rsrStDur, rsrStRated, rsrStCost, []*utils.RSRField{}, true, "") if err != nil { t.Error("Unexpected error received", err) } - expctRatedCdr2 := &CDR{CGRID: storCdr.CGRID, ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_POSTPAID, - Direction: "*in", Tenant: "cgrates.com", Category: "premium_call", Account: "first_account", Subject: "first_subject", Destination: "1002", + expctRatedCdr2 := &CDR{CGRID: storCdr.CGRID, ToR: utils.VOICE, OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_POSTPAID, + Tenant: "cgrates.com", Category: "premium_call", Account: "first_account", + Subject: "first_subject", Destination: "1002", SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), - AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(12) * time.Second, PDD: time.Duration(3) * time.Second, - Supplier: "supplier1", DisconnectCause: "HANGUP_COMPLETE", Rated: true, Cost: 1.2, + AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(12) * time.Second, Rated: true, Cost: 1.2, ExtraFields: map[string]string{}, RunID: "wholesale_run"} if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) { t.Errorf("Received: %v, expected: %v", rtCdrOut2, expctRatedCdr2) } - _, err = storCdr.ForkCdr("wholesale_run", &utils.RSRField{Id: "dummy_header"}, &utils.RSRField{Id: utils.DIRECTION}, &utils.RSRField{Id: utils.TENANT}, - &utils.RSRField{Id: utils.TOR}, &utils.RSRField{Id: utils.ACCOUNT}, &utils.RSRField{Id: utils.SUBJECT}, &utils.RSRField{Id: utils.DESTINATION}, - &utils.RSRField{Id: utils.SETUP_TIME}, &utils.RSRField{Id: utils.PDD}, &utils.RSRField{Id: utils.ANSWER_TIME}, &utils.RSRField{Id: utils.USAGE}, - &utils.RSRField{Id: utils.SUPPLIER}, - &utils.RSRField{Id: utils.DISCONNECT_CAUSE}, &utils.RSRField{Id: utils.RATED_FLD}, &utils.RSRField{Id: utils.COST}, []*utils.RSRField{}, true, "") + _, err = storCdr.ForkCdr("wholesale_run", &utils.RSRField{Id: "dummy_header"}, + &utils.RSRField{Id: utils.TENANT}, &utils.RSRField{Id: utils.TOR}, &utils.RSRField{Id: utils.ACCOUNT}, + &utils.RSRField{Id: utils.SUBJECT}, &utils.RSRField{Id: utils.DESTINATION}, + &utils.RSRField{Id: utils.SETUP_TIME}, &utils.RSRField{Id: utils.ANSWER_TIME}, &utils.RSRField{Id: utils.USAGE}, + &utils.RSRField{Id: utils.RATED_FLD}, &utils.RSRField{Id: utils.COST}, []*utils.RSRField{}, true, "") if err == nil { t.Error("Failed to detect missing header") } } func TestCDRForkCdrFromMetaDefaults(t *testing.T) { - storCdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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: "1002", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, PDD: time.Duration(4) * time.Second, Supplier: "SUPPL3", - ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, + storCdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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, Tenant: "cgrates.org", Category: "call", + Account: "1001", Subject: "1001", Destination: "1002", RunID: utils.DEFAULT_RUNID, + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, Cost: 1.01, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } - expctCdr := &CDR{CGRID: storCdr.CGRID, 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: "1002", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(10) * time.Second, PDD: time.Duration(4) * time.Second, Supplier: "SUPPL3", Cost: 1.01, - ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, RunID: "wholesale_run"} - cdrOut, err := storCdr.ForkCdr("wholesale_run", &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, - &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, - &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, - &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, + expctCdr := &CDR{CGRID: storCdr.CGRID, ToR: utils.VOICE, OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, Cost: 1.01, RunID: "wholesale_run", + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}} + cdrOut, err := storCdr.ForkCdr("wholesale_run", &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, + &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, + &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, + &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, &utils.RSRField{Id: utils.META_DEFAULT}, []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true, "") if err != nil { t.Fatal("Unexpected error received", err) @@ -361,7 +380,7 @@ func TestCDRForkCdrFromMetaDefaults(t *testing.T) { t.Errorf("Expected: %v, received: %v", expctCdr, cdrOut) } // Should also accept nil as defaults - if cdrOut, err := storCdr.ForkCdr("wholesale_run", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, + if cdrOut, err := storCdr.ForkCdr("wholesale_run", nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, []*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true, ""); err != nil { t.Fatal("Unexpected error received", err) } else if !reflect.DeepEqual(expctCdr, cdrOut) { @@ -370,28 +389,35 @@ func TestCDRForkCdrFromMetaDefaults(t *testing.T) { } func TestCDRAsExternalCDR(t *testing.T) { - storCdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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", + storCdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10), PDD: time.Duration(7) * time.Second, Supplier: "SUPPL1", - ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01} - expectOutCdr := &ExternalCDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10 * time.Second), Cost: 1.01, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}} + expectOutCdr := &ExternalCDR{ + CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 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, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: "2013-11-07T08:42:20Z", AnswerTime: "2013-11-07T08:42:26Z", RunID: utils.DEFAULT_RUNID, - Usage: "0.00000001", PDD: "7", Supplier: "SUPPL1", - ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, CostDetails: "null"} + Usage: "10s", Cost: 1.01, CostDetails: "null", + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}} if cdrOut := storCdr.AsExternalCDR(); !reflect.DeepEqual(expectOutCdr, cdrOut) { t.Errorf("Expected: %+v, received: %+v", expectOutCdr, cdrOut) } } func TestCDREventFields(t *testing.T) { - cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "test", RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "dan", Subject: "dans", - Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 27, 0, time.UTC), - RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, Supplier: "suppl1", + cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), + OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", + Source: "test", RequestType: utils.META_RATED, Tenant: "cgrates.org", + Category: "call", Account: "dan", Subject: "dans", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 27, 0, time.UTC), + RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01} if ev := cdr.AsEvent(""); ev != Event(cdr) { @@ -406,9 +432,6 @@ func TestCDREventFields(t *testing.T) { if res := cdr.GetUUID(); res != "dsafdsaf" { t.Error("Received: ", res) } - if res := cdr.GetDirection(utils.META_DEFAULT); res != "*out" { - t.Error("Received: ", res) - } if res := cdr.GetSubject(utils.META_DEFAULT); res != "dans" { t.Error("Received: ", res) } @@ -442,9 +465,6 @@ func TestCDREventFields(t *testing.T) { if dur, _ := cdr.GetDuration(utils.META_DEFAULT); dur != cdr.Usage { t.Error("Received: ", dur) } - if suppl := cdr.GetSupplier(utils.META_DEFAULT); suppl != cdr.Supplier { - t.Error("Received: ", suppl) - } if res := cdr.GetOriginatorIP(utils.META_DEFAULT); res != cdr.OriginHost { t.Error("Received: ", res) } @@ -454,13 +474,17 @@ func TestCDREventFields(t *testing.T) { } func TesUsageReqAsCDR(t *testing.T) { - setupReq := &UsageRecord{ToR: utils.VOICE, RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", + setupReq := &UsageRecord{ToR: utils.VOICE, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", - SetupTime: "2013-11-07T08:42:20Z", AnswerTime: "2013-11-07T08:42:26Z", Usage: "0.00000001", - } - eStorCdr := &CDR{ToR: utils.VOICE, RequestType: utils.META_RATED, Direction: "*out", - Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(10)} + SetupTime: "2013-11-07T08:42:20Z", AnswerTime: "2013-11-07T08:42:26Z", + Usage: "0.00000001"} + eStorCdr := &CDR{ToR: utils.VOICE, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", + Subject: "1001", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10)} if CDR, err := setupReq.AsCDR(""); err != nil { t.Error(err) } else if !reflect.DeepEqual(eStorCdr, CDR) { @@ -469,13 +493,19 @@ func TesUsageReqAsCDR(t *testing.T) { } func TestUsageReqAsCD(t *testing.T) { - req := &UsageRecord{ToR: utils.VOICE, RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", Category: "call", + req := &UsageRecord{ToR: utils.VOICE, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", - SetupTime: "2013-11-07T08:42:20Z", AnswerTime: "2013-11-07T08:42:26Z", Usage: "0.00000001", + SetupTime: "2013-11-07T08:42:20Z", AnswerTime: "2013-11-07T08:42:26Z", + Usage: "10", } - eCD := &CallDescriptor{CgrID: "9473e7b2e075d168b9da10ae957ee68fe5a217e4", TOR: req.ToR, Direction: req.Direction, Tenant: req.Tenant, - Category: req.Category, Account: req.Account, Subject: req.Subject, Destination: req.Destination, - TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), TimeEnd: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).Add(time.Duration(10)), DenyNegativeAccount: true} + eCD := &CallDescriptor{CgrID: "c4630df20b2a0c5b11311e4b5a8c3178cf314344", TOR: req.ToR, + Direction: utils.OUT, Tenant: req.Tenant, + Category: req.Category, Account: req.Account, + Subject: req.Subject, Destination: req.Destination, + TimeStart: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + TimeEnd: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).Add(time.Duration(10)), + DenyNegativeAccount: true} if cd, err := req.AsCallDescriptor("", true); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCD, cd) { @@ -511,7 +541,6 @@ func TestCDRAsMapStringIface(t *testing.T) { OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1002", @@ -521,40 +550,35 @@ func TestCDRAsMapStringIface(t *testing.T) { AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, - Supplier: "SUPPL1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, } mp := map[string]interface{}{ - "field_extr1": "val_extr1", - "fieldextr2": "valextr2", - utils.CGRID: cdr.CGRID, - utils.MEDI_RUNID: utils.DEFAULT_RUNID, - utils.ORDERID: cdr.OrderID, - utils.CDRHOST: "192.168.1.1", - utils.CDRSOURCE: utils.UNIT_TEST, - utils.ACCID: "dsafdsaf", - utils.TOR: utils.VOICE, - utils.REQTYPE: utils.META_RATED, - utils.DIRECTION: "*out", - utils.TENANT: "cgrates.org", - utils.CATEGORY: "call", - utils.ACCOUNT: "1002", - utils.SUBJECT: "1001", - utils.DESTINATION: "+4986517174963", - utils.SETUP_TIME: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), - utils.PDD: time.Duration(0) * time.Second, - utils.ANSWER_TIME: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - utils.USAGE: time.Duration(10) * time.Second, - utils.SUPPLIER: "SUPPL1", - utils.DISCONNECT_CAUSE: cdr.DisconnectCause, - utils.CostSource: cdr.CostSource, - utils.COST: 1.01, - utils.COST_DETAILS: cdr.CostDetails, - utils.RATED: false, - utils.PartialField: false, - utils.ExtraInfo: cdr.ExtraInfo, + "field_extr1": "val_extr1", + "fieldextr2": "valextr2", + utils.CGRID: cdr.CGRID, + utils.MEDI_RUNID: utils.DEFAULT_RUNID, + utils.ORDERID: cdr.OrderID, + utils.CDRHOST: "192.168.1.1", + utils.CDRSOURCE: utils.UNIT_TEST, + utils.ACCID: "dsafdsaf", + utils.TOR: utils.VOICE, + utils.REQTYPE: utils.META_RATED, + utils.TENANT: "cgrates.org", + utils.CATEGORY: "call", + utils.ACCOUNT: "1002", + utils.SUBJECT: "1001", + utils.DESTINATION: "+4986517174963", + utils.SETUP_TIME: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + utils.ANSWER_TIME: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + utils.USAGE: time.Duration(10) * time.Second, + utils.CostSource: cdr.CostSource, + utils.COST: 1.01, + utils.COST_DETAILS: cdr.CostDetails, + utils.RATED: false, + utils.PartialField: false, + utils.ExtraInfo: cdr.ExtraInfo, } if cdrMp, err := cdr.AsMapStringIface(); err != nil { t.Error(err) @@ -571,7 +595,7 @@ func TestCDRAsExportRecord(t *testing.T) { ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", RequestType: utils.META_RATED, - Direction: "*out", Tenant: "cgrates.org", + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", @@ -635,12 +659,15 @@ func TestCDRAsExportRecord(t *testing.T) { } func TestCDRAsExportMap(t *testing.T) { - cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 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: "+4986517174963", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(10) * time.Second, Supplier: "SUPPL1", - ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, + cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), + OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", + Subject: "1001", Destination: "+4986517174963", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, + Usage: time.Duration(10) * time.Second, Cost: 1.01, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } eCDRMp := map[string]string{ utils.CGRID: cdr.CGRID, @@ -658,3 +685,102 @@ func TestCDRAsExportMap(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", eCDRMp, cdrMp) } } + +func TestCDRAsCDRsql(t *testing.T) { + cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), + OrderID: 123, + ToR: utils.VOICE, + OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", + Source: utils.UNIT_TEST, + RequestType: utils.META_RATED, + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "1001", + Destination: "+4986517174963", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).Local(), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).Local(), + RunID: utils.DEFAULT_RUNID, + Usage: time.Duration(10) * time.Second, + Cost: 1.01, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + } + eCDR := cdr.AsCDRsql() + eCDRSql := &CDRsql{ + Cgrid: cdr.CGRID, + RunID: cdr.RunID, + OriginID: "dsafdsaf", + TOR: utils.VOICE, + Source: utils.UNIT_TEST, + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "1001", + Destination: "+4986517174963", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).Local(), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).Local(), + Usage: cdr.Usage.Nanoseconds(), + Cost: cdr.Cost, + ExtraFields: utils.ToJSON(cdr.ExtraFields), + RequestType: cdr.RequestType, + OriginHost: cdr.OriginHost, + CostDetails: utils.ToJSON(cdr.CostDetails), + CreatedAt: eCDR.CreatedAt, + } + if !reflect.DeepEqual(eCDR, eCDRSql) { + t.Errorf("Expecting: %+v, received: %+v", eCDR, eCDRSql) + } + +} + +func TestCDRNewCDRFromSQL(t *testing.T) { + extraFields := map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"} + cdrSql := &CDRsql{ + ID: 123, + Cgrid: "abecd993d06672714c4218a6dcf8278e0589a171", + RunID: "*default", + OriginID: "dsafdsaf", + TOR: utils.VOICE, + Source: utils.UNIT_TEST, + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "1001", + Destination: "+4986517174963", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).Local(), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).Local(), + Usage: 10000000000, + Cost: 1.01, + RequestType: utils.META_RATED, + OriginHost: "192.168.1.1", + ExtraFields: utils.ToJSON(extraFields), + } + + cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), + OrderID: 123, + ToR: utils.VOICE, + OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", + Source: utils.UNIT_TEST, + RequestType: utils.META_RATED, + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "1001", + Destination: "+4986517174963", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).Local(), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).Local(), + RunID: utils.DEFAULT_RUNID, + Usage: time.Duration(10) * time.Second, + Cost: 1.01, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + } + + if eCDR, err := NewCDRFromSQL(cdrSql); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(cdr, eCDR) { + t.Errorf("Expecting: %+v, received: %+v", cdr, eCDR) + } + +} diff --git a/engine/cdrecsv_test.go b/engine/cdrecsv_test.go index 2b1ec84ba..1158a7dcf 100644 --- a/engine/cdrecsv_test.go +++ b/engine/cdrecsv_test.go @@ -32,11 +32,14 @@ func TestCsvCdrWriter(t *testing.T) { writer := &bytes.Buffer{} cfg, _ := config.NewDefaultCGRConfig() storedCdr1 := &CDR{ - CGRID: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", - RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", - Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(), - Usage: time.Duration(10) * time.Second, RunID: utils.DEFAULT_RUNID, - ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01, + CGRID: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), + ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", + RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", + Account: "1001", Subject: "1001", Destination: "1002", + SetupTime: time.Unix(1383813745, 0).UTC(), + AnswerTime: time.Unix(1383813746, 0).UTC(), + Usage: time.Duration(10) * time.Second, RunID: utils.DEFAULT_RUNID, Cost: 1.01, + ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, } cdre, err := NewCDRExporter([]*CDR{storedCdr1}, cfg.CdreProfiles["*default"], utils.MetaFileCSV, "", "", "firstexport", true, 1, ',', map[string]float64{}, 0.0, cfg.RoundingDecimals, cfg.HttpSkipTlsVerify, nil) @@ -50,7 +53,7 @@ func TestCsvCdrWriter(t *testing.T) { if err := cdre.writeCsv(csvWriter); err != nil { t.Error("Unexpected error: ", err) } - expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6,*default,*voice,dsafdsaf,*rated,*out,cgrates.org,call,1001,1001,1002,2013-11-07T08:42:25Z,2013-11-07T08:42:26Z,10,1.01000` + expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6,*default,*voice,dsafdsaf,*rated,cgrates.org,call,1001,1001,1002,2013-11-07T08:42:25Z,2013-11-07T08:42:26Z,10,1.01000` result := strings.TrimSpace(writer.String()) if result != expected { t.Errorf("Expected: \n%s received: \n%s.", expected, result) @@ -63,11 +66,15 @@ func TestCsvCdrWriter(t *testing.T) { func TestAlternativeFieldSeparator(t *testing.T) { writer := &bytes.Buffer{} cfg, _ := config.NewDefaultCGRConfig() - storedCdr1 := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", - RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", - Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813745, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(), - Usage: time.Duration(10) * time.Second, RunID: utils.DEFAULT_RUNID, - ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, Cost: 1.01, + storedCdr1 := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Unix(1383813745, 0).UTC().String()), + ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", + RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", + Account: "1001", Subject: "1001", Destination: "1002", + SetupTime: time.Unix(1383813745, 0).UTC(), + AnswerTime: time.Unix(1383813746, 0).UTC(), + Usage: time.Duration(10) * time.Second, + RunID: utils.DEFAULT_RUNID, Cost: 1.01, + ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, } cdre, err := NewCDRExporter([]*CDR{storedCdr1}, cfg.CdreProfiles["*default"], utils.MetaFileCSV, "", "", "firstexport", true, 1, '|', map[string]float64{}, 0.0, cfg.RoundingDecimals, cfg.HttpSkipTlsVerify, nil) @@ -81,7 +88,7 @@ func TestAlternativeFieldSeparator(t *testing.T) { if err := cdre.writeCsv(csvWriter); err != nil { t.Error("Unexpected error: ", err) } - expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6|*default|*voice|dsafdsaf|*rated|*out|cgrates.org|call|1001|1001|1002|2013-11-07T08:42:25Z|2013-11-07T08:42:26Z|10|1.01000` + expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6|*default|*voice|dsafdsaf|*rated|cgrates.org|call|1001|1001|1002|2013-11-07T08:42:25Z|2013-11-07T08:42:26Z|10|1.01000` result := strings.TrimSpace(writer.String()) if result != expected { t.Errorf("Expected: \n%s received: \n%s.", expected, result) diff --git a/engine/cdrefwv_test.go b/engine/cdrefwv_test.go index 9a52e878b..4c29f3065 100644 --- a/engine/cdrefwv_test.go +++ b/engine/cdrefwv_test.go @@ -28,28 +28,45 @@ import ( ) var hdrJsnCfgFlds = []*config.CdrFieldJsonCfg{ - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("TypeOfRecord"), Type: utils.StringPointer(utils.META_CONSTANT), Value: utils.StringPointer("10"), Width: utils.IntPointer(2)}, - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler1"), Type: utils.StringPointer(utils.META_FILLER), Width: utils.IntPointer(3)}, - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("DistributorCode"), Type: utils.StringPointer(utils.META_CONSTANT), Value: utils.StringPointer("VOI"), Width: utils.IntPointer(3)}, - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileSeqNr"), Type: utils.StringPointer(utils.META_HANDLER), Value: utils.StringPointer(META_EXPORTID), - Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("zeroleft")}, - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("LastCdr"), Type: utils.StringPointer(utils.META_HANDLER), Value: utils.StringPointer(META_LASTCDRATIME), + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("TypeOfRecord"), + Type: utils.StringPointer(utils.META_CONSTANT), Value: utils.StringPointer("10"), + Width: utils.IntPointer(2)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler1"), + Type: utils.StringPointer(utils.META_FILLER), Width: utils.IntPointer(3)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("DistributorCode"), + Type: utils.StringPointer(utils.META_CONSTANT), Value: utils.StringPointer("VOI"), + Width: utils.IntPointer(3)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileSeqNr"), + Type: utils.StringPointer(utils.META_HANDLER), Value: utils.StringPointer(META_EXPORTID), + Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), + Padding: utils.StringPointer("zeroleft")}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("LastCdr"), + Type: utils.StringPointer(utils.META_HANDLER), Value: utils.StringPointer(META_LASTCDRATIME), Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")}, - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileCreationfTime"), Type: utils.StringPointer(utils.META_HANDLER), Value: utils.StringPointer(META_TIMENOW), + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileCreationfTime"), + Type: utils.StringPointer(utils.META_HANDLER), Value: utils.StringPointer(META_TIMENOW), Width: utils.IntPointer(12), Layout: utils.StringPointer("020106150400")}, - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileVersion"), Type: utils.StringPointer(utils.META_CONSTANT), Value: utils.StringPointer("01"), Width: utils.IntPointer(2)}, - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler2"), Type: utils.StringPointer(utils.META_FILLER), Width: utils.IntPointer(105)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("FileVersion"), + Type: utils.StringPointer(utils.META_CONSTANT), Value: utils.StringPointer("01"), + Width: utils.IntPointer(2)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Filler2"), + Type: utils.StringPointer(utils.META_FILLER), Width: utils.IntPointer(105)}, } var contentJsnCfgFlds = []*config.CdrFieldJsonCfg{ - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("TypeOfRecord"), Type: utils.StringPointer(utils.META_CONSTANT), Value: utils.StringPointer("20"), Width: utils.IntPointer(2)}, - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Account"), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer(utils.ACCOUNT), Width: utils.IntPointer(12), + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("TypeOfRecord"), + Type: utils.StringPointer(utils.META_CONSTANT), Value: utils.StringPointer("20"), Width: utils.IntPointer(2)}, + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Account"), Type: utils.StringPointer(utils.META_COMPOSED), + Value: utils.StringPointer(utils.ACCOUNT), Width: utils.IntPointer(12), Strip: utils.StringPointer("left"), Padding: utils.StringPointer("right")}, - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Subject"), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer(utils.SUBJECT), Width: utils.IntPointer(5), + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Subject"), Type: utils.StringPointer(utils.META_COMPOSED), + Value: utils.StringPointer(utils.SUBJECT), Width: utils.IntPointer(5), Strip: utils.StringPointer("right"), Padding: utils.StringPointer("right")}, - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("CLI"), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer("cli"), Width: utils.IntPointer(15), + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("CLI"), Type: utils.StringPointer(utils.META_COMPOSED), + Value: utils.StringPointer("cli"), Width: utils.IntPointer(15), Strip: utils.StringPointer("xright"), Padding: utils.StringPointer("right")}, - &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Destination"), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer(utils.DESTINATION), Width: utils.IntPointer(24), + &config.CdrFieldJsonCfg{Tag: utils.StringPointer("Destination"), Type: utils.StringPointer(utils.META_COMPOSED), + Value: utils.StringPointer(utils.DESTINATION), Width: utils.IntPointer(24), Strip: utils.StringPointer("xright"), Padding: utils.StringPointer("right")}, &config.CdrFieldJsonCfg{Tag: utils.StringPointer("TOR"), Type: utils.StringPointer(utils.META_CONSTANT), Value: utils.StringPointer("02"), Width: utils.IntPointer(2)}, &config.CdrFieldJsonCfg{Tag: utils.StringPointer("SubtypeTOR"), Type: utils.StringPointer(utils.META_CONSTANT), Value: utils.StringPointer("11"), Width: utils.IntPointer(4), @@ -117,11 +134,12 @@ func TestWriteCdr(t *testing.T) { } cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), ToR: utils.VOICE, OrderID: 1, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", - RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", - Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", + Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(10) * time.Second, RunID: utils.DEFAULT_RUNID, Cost: 2.34567, + Usage: time.Duration(10) * time.Second, + RunID: utils.DEFAULT_RUNID, Cost: 2.34567, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } @@ -178,7 +196,8 @@ func TestWriteCdrs(t *testing.T) { TrailerFields: trailerCfgFlds, } cdr1 := &CDR{CGRID: utils.Sha1("aaa1", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), - ToR: utils.VOICE, OrderID: 2, OriginID: "aaa1", OriginHost: "192.168.1.1", RequestType: utils.META_RATED, Direction: "*out", Tenant: "cgrates.org", + ToR: utils.VOICE, OrderID: 2, OriginID: "aaa1", OriginHost: "192.168.1.1", + RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1010", SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), @@ -186,20 +205,24 @@ func TestWriteCdrs(t *testing.T) { ExtraFields: map[string]string{"productnumber": "12341", "fieldextr2": "valextr2"}, } cdr2 := &CDR{CGRID: utils.Sha1("aaa2", time.Date(2013, 11, 7, 7, 42, 20, 0, time.UTC).String()), - ToR: utils.VOICE, OrderID: 4, OriginID: "aaa2", OriginHost: "192.168.1.2", RequestType: utils.META_PREPAID, Direction: "*out", Tenant: "cgrates.org", + ToR: utils.VOICE, OrderID: 4, OriginID: "aaa2", OriginHost: "192.168.1.2", + RequestType: utils.META_PREPAID, Tenant: "cgrates.org", Category: "call", Account: "1002", Subject: "1002", Destination: "1011", SetupTime: time.Date(2013, 11, 7, 7, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 7, 42, 26, 0, time.UTC), - Usage: time.Duration(5) * time.Minute, RunID: utils.DEFAULT_RUNID, Cost: 1.40001, + Usage: time.Duration(5) * time.Minute, + RunID: utils.DEFAULT_RUNID, Cost: 1.40001, ExtraFields: map[string]string{"productnumber": "12342", "fieldextr2": "valextr2"}, } cdr3 := &CDR{} cdr4 := &CDR{CGRID: utils.Sha1("aaa3", time.Date(2013, 11, 7, 9, 42, 18, 0, time.UTC).String()), - ToR: utils.VOICE, OrderID: 3, OriginID: "aaa4", OriginHost: "192.168.1.4", RequestType: utils.META_POSTPAID, Direction: "*out", Tenant: "cgrates.org", + ToR: utils.VOICE, OrderID: 3, OriginID: "aaa4", OriginHost: "192.168.1.4", + RequestType: utils.META_POSTPAID, Tenant: "cgrates.org", Category: "call", Account: "1004", Subject: "1004", Destination: "1013", SetupTime: time.Date(2013, 11, 7, 9, 42, 18, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 9, 42, 26, 0, time.UTC), - Usage: time.Duration(20) * time.Second, RunID: utils.DEFAULT_RUNID, Cost: 2.34567, + Usage: time.Duration(20) * time.Second, + RunID: utils.DEFAULT_RUNID, Cost: 2.34567, ExtraFields: map[string]string{"productnumber": "12344", "fieldextr2": "valextr2"}, } cfg, _ := config.NewDefaultCGRConfig() diff --git a/engine/cdrs.go b/engine/cdrs.go index 4d01ef3e3..33c2189c6 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -166,9 +166,6 @@ func (self *CdrServer) storeSMCost(smCost *SMCost, checkDuplicate bool) error { // Returns error if not able to properly store the CDR, mediation is async since we can always recover offline func (self *CdrServer) processCdr(cdr *CDR) (err error) { - if cdr.Direction == "" { - cdr.Direction = utils.OUT - } if cdr.RequestType == "" { cdr.RequestType = self.cgrCfg.DefaultReqType } @@ -248,7 +245,7 @@ func (self *CdrServer) deriveRateStoreStatsReplicate(cdr *CDR, store, cdrstats, } if err := LoadAlias(&AttrMatchingAlias{ Destination: cdrRun.Destination, - Direction: cdrRun.Direction, + Direction: utils.OUT, Tenant: cdrRun.Tenant, Category: cdrRun.Category, Account: cdrRun.Account, @@ -275,21 +272,6 @@ func (self *CdrServer) deriveRateStoreStatsReplicate(cdr *CDR, store, cdrstats, } } } - // Store AccountSummary if requested - if self.cgrCfg.CDRScdrAccountSummary { - for _, ratedCDR := range ratedCDRs { - if utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID, utils.META_PSEUDOPREPAID, utils.PSEUDOPREPAID, - utils.META_POSTPAID, utils.POSTPAID}, ratedCDR.RequestType) { - acntID := utils.ConcatenatedKey(ratedCDR.Tenant, ratedCDR.Account) - acnt, err := self.dm.DataDB().GetAccount(acntID) - if err != nil { - utils.Logger.Err(fmt.Sprintf(" Querying AccountDigest for account: %s got error: %s", acntID, err.Error())) - } else if acnt.ID != "" { - ratedCDR.AccountSummary = acnt.AsAccountSummary() - } - } - } - } // Store rated CDRs if store { for _, ratedCDR := range ratedCDRs { @@ -340,7 +322,7 @@ func (self *CdrServer) deriveCdrs(cdr *CDR) ([]*CDR, error) { } if err := LoadAlias(&AttrMatchingAlias{ Destination: cdr.Destination, - Direction: cdr.Direction, + Direction: utils.OUT, Tenant: cdr.Tenant, Category: cdr.Category, Account: cdr.Account, @@ -349,7 +331,7 @@ func (self *CdrServer) deriveCdrs(cdr *CDR) ([]*CDR, error) { }, cdr, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return nil, err } - attrsDC := &utils.AttrDerivedChargers{Tenant: cdr.Tenant, Category: cdr.Category, Direction: cdr.Direction, + attrsDC := &utils.AttrDerivedChargers{Tenant: cdr.Tenant, Category: cdr.Category, Direction: utils.OUT, Account: cdr.Account, Subject: cdr.Subject, Destination: cdr.Destination} var dcs utils.DerivedChargers if err := self.rals.Call("Responder.GetDerivedChargers", attrsDC, &dcs); err != nil { @@ -369,18 +351,14 @@ func (self *CdrServer) deriveCdrs(cdr *CDR) ([]*CDR, error) { continue } dcRequestTypeFld, _ := utils.NewRSRField(dc.RequestTypeField) - dcDirFld, _ := utils.NewRSRField(dc.DirectionField) dcTenantFld, _ := utils.NewRSRField(dc.TenantField) dcCategoryFld, _ := utils.NewRSRField(dc.CategoryField) dcAcntFld, _ := utils.NewRSRField(dc.AccountField) dcSubjFld, _ := utils.NewRSRField(dc.SubjectField) dcDstFld, _ := utils.NewRSRField(dc.DestinationField) dcSTimeFld, _ := utils.NewRSRField(dc.SetupTimeField) - dcPddFld, _ := utils.NewRSRField(dc.PDDField) dcATimeFld, _ := utils.NewRSRField(dc.AnswerTimeField) dcDurFld, _ := utils.NewRSRField(dc.UsageField) - dcSupplFld, _ := utils.NewRSRField(dc.SupplierField) - dcDCauseFld, _ := utils.NewRSRField(dc.DisconnectCauseField) dcRatedFld, _ := utils.NewRSRField(dc.RatedField) dcCostFld, _ := utils.NewRSRField(dc.CostField) @@ -389,8 +367,8 @@ func (self *CdrServer) deriveCdrs(cdr *CDR) ([]*CDR, error) { dcExtraFields = append(dcExtraFields, &utils.RSRField{Id: key}) } - forkedCdr, err := cdr.ForkCdr(dc.RunID, dcRequestTypeFld, dcDirFld, dcTenantFld, dcCategoryFld, dcAcntFld, dcSubjFld, dcDstFld, - dcSTimeFld, dcPddFld, dcATimeFld, dcDurFld, dcSupplFld, dcDCauseFld, dcRatedFld, dcCostFld, dcExtraFields, true, self.cgrCfg.DefaultTimezone) + forkedCdr, err := cdr.ForkCdr(dc.RunID, dcRequestTypeFld, dcTenantFld, dcCategoryFld, dcAcntFld, dcSubjFld, dcDstFld, + dcSTimeFld, dcATimeFld, dcDurFld, dcRatedFld, dcCostFld, dcExtraFields, true, self.cgrCfg.DefaultTimezone) if err != nil { utils.Logger.Err(fmt.Sprintf("Could not fork CGR with cgrid %s, run: %s, error: %s", cdr.CGRID, dc.RunID, err.Error())) continue // do not add it to the forked CDR list @@ -414,7 +392,8 @@ func (self *CdrServer) rateCDR(cdr *CDR) ([]*CDR, error) { cdr.ExtraInfo = "" // Clean previous ExtraInfo, useful when re-rating var cdrsRated []*CDR _, hasLastUsed := cdr.ExtraFields[utils.LastUsed] - if utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID}, cdr.RequestType) && (cdr.Usage != 0 || hasLastUsed) { // ToDo: Get rid of PREPAID as soon as we don't want to support it backwards + if utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID}, cdr.RequestType) && + (cdr.Usage != 0 || hasLastUsed) { // ToDo: Get rid of PREPAID as soon as we don't want to support it backwards // Should be previously calculated and stored in DB fib := utils.Fib() var smCosts []*SMCost @@ -423,7 +402,8 @@ func (self *CdrServer) rateCDR(cdr *CDR) ([]*CDR, error) { cgrID = "" // for queries involving originIDPrefix we ignore CGRID } for i := 0; i < self.cgrCfg.CDRSSMCostRetries; i++ { - smCosts, err = self.cdrDb.GetSMCosts(cgrID, cdr.RunID, cdr.OriginHost, cdr.ExtraFields[utils.OriginIDPrefix]) + smCosts, err = self.cdrDb.GetSMCosts(cgrID, cdr.RunID, cdr.OriginHost, + cdr.ExtraFields[utils.OriginIDPrefix]) if err == nil && len(smCosts) != 0 { break } @@ -436,7 +416,7 @@ func (self *CdrServer) rateCDR(cdr *CDR) ([]*CDR, error) { cdrClone := cdr.Clone() cdrClone.OriginID = smCost.OriginID if cdr.Usage == 0 { - cdrClone.Usage = time.Duration(smCost.Usage * utils.NANO_MULTIPLIER) // Usage is float as seconds, convert back to duration + cdrClone.Usage = smCost.Usage } cdrClone.Cost = smCost.CostDetails.Cost cdrClone.CostDetails = smCost.CostDetails @@ -470,7 +450,7 @@ func (self *CdrServer) getCostFromRater(cdr *CDR) (*CallCost, error) { } cd := &CallDescriptor{ TOR: cdr.ToR, - Direction: cdr.Direction, + Direction: utils.OUT, Tenant: cdr.Tenant, Category: cdr.Category, Subject: cdr.Subject, diff --git a/engine/cdrstats.go b/engine/cdrstats.go index 842ed2a48..ffaf57236 100644 --- a/engine/cdrstats.go +++ b/engine/cdrstats.go @@ -105,9 +105,6 @@ func (cs *CdrStats) AcceptCdr(cdr *CDR) bool { if len(cs.ReqType) > 0 && !utils.IsSliceMember(cs.ReqType, cdr.RequestType) { return false } - if len(cs.Direction) > 0 && !utils.IsSliceMember(cs.Direction, cdr.Direction) { - return false - } if len(cs.Tenant) > 0 && !utils.IsSliceMember(cs.Tenant, cdr.Tenant) { return false } @@ -150,20 +147,6 @@ func (cs *CdrStats) AcceptCdr(cdr *CDR) bool { return false } } - if len(cs.PddInterval) > 0 { - if cdr.PDD < cs.PddInterval[0] { - return false - } - if len(cs.PddInterval) > 1 && cdr.PDD >= cs.PddInterval[1] { - return false - } - } - if len(cs.Supplier) > 0 && !utils.IsSliceMember(cs.Supplier, cdr.Supplier) { - return false - } - if len(cs.DisconnectCause) > 0 && !utils.IsSliceMember(cs.DisconnectCause, cdr.DisconnectCause) { - return false - } if len(cs.MediationRunIds) > 0 && !utils.IsSliceMember(cs.MediationRunIds, cdr.RunID) { return false } diff --git a/engine/cdrstats_queue.go b/engine/cdrstats_queue.go index ea5213a2d..b20c56621 100644 --- a/engine/cdrstats_queue.go +++ b/engine/cdrstats_queue.go @@ -201,7 +201,6 @@ func (sq *CDRStatsQueue) simplifyCdr(cdr *CDR) *QCdr { return &QCdr{ SetupTime: cdr.SetupTime, AnswerTime: cdr.AnswerTime, - Pdd: cdr.PDD, Usage: cdr.Usage, Cost: cdr.Cost, Dest: cdr.Destination, diff --git a/engine/cgrcdr.go b/engine/cgrcdr.go index 41eabd4da..e59b9df49 100644 --- a/engine/cgrcdr.go +++ b/engine/cgrcdr.go @@ -66,18 +66,14 @@ func (cgrCdr CgrCdr) AsCDR(timezone string) *CDR { storCdr.OriginHost = cgrCdr[utils.CDRHOST] storCdr.Source = cgrCdr[utils.CDRSOURCE] storCdr.RequestType = cgrCdr[utils.REQTYPE] - storCdr.Direction = utils.OUT storCdr.Tenant = cgrCdr[utils.TENANT] storCdr.Category = cgrCdr[utils.CATEGORY] storCdr.Account = cgrCdr[utils.ACCOUNT] storCdr.Subject = cgrCdr[utils.SUBJECT] storCdr.Destination = cgrCdr[utils.DESTINATION] storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.SETUP_TIME], timezone) // Not interested to process errors, should do them if necessary in a previous step - storCdr.PDD, _ = utils.ParseDurationWithSecs(cgrCdr[utils.PDD]) storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.ANSWER_TIME], timezone) - storCdr.Usage, _ = utils.ParseDurationWithSecs(cgrCdr[utils.USAGE]) - storCdr.Supplier = cgrCdr[utils.SUPPLIER] - storCdr.DisconnectCause = cgrCdr[utils.DISCONNECT_CAUSE] + storCdr.Usage, _ = utils.ParseDurationWithNanosecs(cgrCdr[utils.USAGE]) storCdr.ExtraFields = cgrCdr.getExtraFields() storCdr.Cost = -1 if costStr, hasIt := cgrCdr[utils.COST]; hasIt { diff --git a/engine/cgrcdr_test.go b/engine/cgrcdr_test.go index 1d9e94cff..71ad57a00 100644 --- a/engine/cgrcdr_test.go +++ b/engine/cgrcdr_test.go @@ -33,19 +33,25 @@ func TestCgrCdrInterfaces(t *testing.T) { } func TestCgrCdrAsCDR(t *testing.T) { - cgrCdr := CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "dsafdsaf", utils.CDRHOST: "192.168.1.1", utils.CDRSOURCE: "internal_test", utils.REQTYPE: utils.META_RATED, - utils.DIRECTION: utils.OUT, - utils.TENANT: "cgrates.org", utils.CATEGORY: "call", - utils.ACCOUNT: "1001", utils.SUBJECT: "1001", utils.DESTINATION: "1002", utils.SETUP_TIME: "2013-11-07T08:42:20Z", utils.ANSWER_TIME: "2013-11-07T08:42:26Z", - utils.USAGE: "10", utils.SUPPLIER: "SUPPL1", "field_extr1": "val_extr1", "fieldextr2": "valextr2"} + cgrCdr := CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "dsafdsaf", utils.CDRHOST: "192.168.1.1", + utils.CDRSOURCE: "internal_test", utils.REQTYPE: utils.META_RATED, + utils.TENANT: "cgrates.org", utils.CATEGORY: "call", + utils.ACCOUNT: "1001", utils.SUBJECT: "1001", utils.DESTINATION: "1002", + utils.SETUP_TIME: "2013-11-07T08:42:20Z", utils.ANSWER_TIME: "2013-11-07T08:42:26Z", + utils.USAGE: "10s", "field_extr1": "val_extr1", "fieldextr2": "valextr2"} setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr[utils.SETUP_TIME], "") - expctRtCdr := &CDR{CGRID: utils.Sha1(cgrCdr[utils.ACCID], setupTime.String()), ToR: utils.VOICE, OriginID: cgrCdr[utils.ACCID], OriginHost: cgrCdr[utils.CDRHOST], + expctRtCdr := &CDR{CGRID: utils.Sha1(cgrCdr[utils.ACCID], setupTime.String()), + ToR: utils.VOICE, OriginID: cgrCdr[utils.ACCID], + OriginHost: cgrCdr[utils.CDRHOST], Source: cgrCdr[utils.CDRSOURCE], RequestType: cgrCdr[utils.REQTYPE], - Direction: cgrCdr[utils.DIRECTION], Tenant: cgrCdr[utils.TENANT], Category: cgrCdr[utils.CATEGORY], Account: cgrCdr[utils.ACCOUNT], Subject: cgrCdr[utils.SUBJECT], - Destination: cgrCdr[utils.DESTINATION], SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(10) * time.Second, Supplier: "SUPPL1", - ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: -1} + Tenant: cgrCdr[utils.TENANT], Category: cgrCdr[utils.CATEGORY], + Account: cgrCdr[utils.ACCOUNT], Subject: cgrCdr[utils.SUBJECT], + Destination: cgrCdr[utils.DESTINATION], + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, Cost: -1, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}} if CDR := cgrCdr.AsCDR(""); !reflect.DeepEqual(expctRtCdr, CDR) { t.Errorf("Expecting %v, received: %v", expctRtCdr, CDR) } @@ -57,28 +63,23 @@ func TestReplicatedCgrCdrAsCDR(t *testing.T) { utils.CDRSOURCE: "internal_test", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: utils.OUT, utils.TENANT: "cgrates.org", utils.CATEGORY: "call", utils.ACCOUNT: "1001", utils.SUBJECT: "1001", utils.DESTINATION: "1002", utils.SETUP_TIME: "2013-11-07T08:42:20Z", utils.PDD: "0.200", utils.ANSWER_TIME: "2013-11-07T08:42:26Z", - utils.USAGE: "10", utils.SUPPLIER: "SUPPL1", utils.DISCONNECT_CAUSE: "NORMAL_CLEARING", utils.COST: "0.12", utils.RATED: "true", "field_extr1": "val_extr1", "fieldextr2": "valextr2"} + utils.USAGE: "10s", utils.SUPPLIER: "SUPPL1", utils.DISCONNECT_CAUSE: "NORMAL_CLEARING", utils.COST: "0.12", utils.RATED: "true", "field_extr1": "val_extr1", "fieldextr2": "valextr2"} expctRtCdr := &CDR{CGRID: cgrCdr[utils.CGRID], - ToR: cgrCdr[utils.TOR], - OriginID: cgrCdr[utils.ACCID], - OriginHost: cgrCdr[utils.CDRHOST], - Source: cgrCdr[utils.CDRSOURCE], - RequestType: cgrCdr[utils.REQTYPE], - Direction: cgrCdr[utils.DIRECTION], - Tenant: cgrCdr[utils.TENANT], - Category: cgrCdr[utils.CATEGORY], - Account: cgrCdr[utils.ACCOUNT], - Subject: cgrCdr[utils.SUBJECT], - Destination: cgrCdr[utils.DESTINATION], - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), - PDD: time.Duration(200) * time.Millisecond, - AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(10) * time.Second, - Supplier: cgrCdr[utils.SUPPLIER], - DisconnectCause: cgrCdr[utils.DISCONNECT_CAUSE], - ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, - Cost: 0.12, - Rated: true, + ToR: cgrCdr[utils.TOR], + OriginID: cgrCdr[utils.ACCID], + OriginHost: cgrCdr[utils.CDRHOST], + Source: cgrCdr[utils.CDRSOURCE], + RequestType: cgrCdr[utils.REQTYPE], + Tenant: cgrCdr[utils.TENANT], + Category: cgrCdr[utils.CATEGORY], + Account: cgrCdr[utils.ACCOUNT], + Subject: cgrCdr[utils.SUBJECT], + Destination: cgrCdr[utils.DESTINATION], + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Cost: 0.12, Rated: true, } if CDR := cgrCdr.AsCDR(""); !reflect.DeepEqual(expctRtCdr, CDR) { t.Errorf("Expecting %v, received: %v", expctRtCdr, CDR) diff --git a/engine/datamanager.go b/engine/datamanager.go index d5e826632..715f34d48 100644 --- a/engine/datamanager.go +++ b/engine/datamanager.go @@ -23,7 +23,7 @@ import ( ) func NewDataManager(dataDB DataDB) *DataManager { - return &DataManager{dataDB: dataDB, cacheCfg: config.CgrConfig().CacheConfig} + return &DataManager{dataDB: dataDB, cacheCfg: config.CgrConfig().CacheCfg()} } // DataManager is the data storage manager for CGRateS diff --git a/engine/datamanager_it_test.go b/engine/datamanager_it_test.go index 22d286ce8..07c06e19b 100644 --- a/engine/datamanager_it_test.go +++ b/engine/datamanager_it_test.go @@ -58,8 +58,9 @@ func TestDMitMongo(t *testing.T) { if err != nil { t.Fatal(err) } - dataDB, err := NewMongoStorage(mgoITCfg.StorDBHost, mgoITCfg.StorDBPort, mgoITCfg.StorDBName, mgoITCfg.StorDBUser, mgoITCfg.StorDBPass, - utils.StorDB, nil, mgoITCfg.CacheConfig, mgoITCfg.LoadHistorySize) + dataDB, err := NewMongoStorage(mgoITCfg.StorDBHost, mgoITCfg.StorDBPort, + mgoITCfg.StorDBName, mgoITCfg.StorDBUser, mgoITCfg.StorDBPass, + utils.StorDB, nil, mgoITCfg.CacheCfg(), mgoITCfg.LoadHistorySize) if err != nil { t.Fatal("Could not connect to Redis", err.Error()) } diff --git a/engine/event.go b/engine/event.go index d4558c410..bc0a7855e 100644 --- a/engine/event.go +++ b/engine/event.go @@ -28,7 +28,6 @@ type Event interface { GetCgrId(timezone string) string GetUUID() string GetSessionIds() []string // Returns identifiers needed to control a session (eg disconnect) - GetDirection(string) string GetSubject(string) string GetAccount(string) string GetDestination(string) string @@ -41,9 +40,6 @@ type Event interface { GetAnswerTime(string, string) (time.Time, error) GetEndTime(string, string) (time.Time, error) GetDuration(string) (time.Duration, error) - GetPdd(string) (time.Duration, error) - GetSupplier(string) string - GetDisconnectCause(string) string GetExtraFields() map[string]string MissingParameter(string) bool ParseEventValue(*utils.RSRField, string) string diff --git a/engine/eventcost.go b/engine/eventcost.go index ad1ab0e52..55325274c 100644 --- a/engine/eventcost.go +++ b/engine/eventcost.go @@ -252,7 +252,7 @@ func (ec *EventCost) ComputeEventCostUsageIndexes() { func (ec *EventCost) AsCallCost() *CallCost { cc := &CallCost{ - Cost: ec.GetCost(), RatedUsage: ec.GetUsage().Seconds(), + Cost: ec.GetCost(), RatedUsage: float64(ec.GetUsage().Nanoseconds()), AccountSummary: ec.AccountSummary} cc.Timespans = make(TimeSpans, len(ec.Charges)) for i, cIl := range ec.Charges { @@ -265,7 +265,6 @@ func (ec *EventCost) AsCallCost() *CallCost { ts.TimeEnd = ts.TimeStart.Add( time.Duration(cIl.Usage().Nanoseconds() * int64(cIl.CompressFactor))) if cIl.RatingID != "" { - //fmt.Printf("Checking RatingID: <%s>\n", cIl.RatingID) if ec.Rating[cIl.RatingID].RatingFiltersID != "" { rfs := ec.RatingFilters[ec.Rating[cIl.RatingID].RatingFiltersID] ts.MatchedSubject = rfs["Subject"].(string) diff --git a/engine/eventcost_test.go b/engine/eventcost_test.go index 5a51b1ce8..24b0c6a8d 100644 --- a/engine/eventcost_test.go +++ b/engine/eventcost_test.go @@ -840,7 +840,7 @@ func TestECAsCallCost(t *testing.T) { } eCC := &CallCost{ Cost: 0.85, - RatedUsage: 120.0, + RatedUsage: 120000000000, AccountSummary: acntSummary, Timespans: TimeSpans{ &TimeSpan{ diff --git a/engine/fscdr.go b/engine/fscdr.go index 1181eb7b2..66d9bb1cc 100644 --- a/engine/fscdr.go +++ b/engine/fscdr.go @@ -24,7 +24,6 @@ import ( "reflect" "strconv" "strings" - "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" @@ -131,20 +130,14 @@ func (fsCdr FSCdr) AsCDR(timezone string) *CDR { storCdr.OriginHost = fsCdr.vars[FS_IP] storCdr.Source = FS_CDR_SOURCE storCdr.RequestType = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_REQTYPE], fsCdr.cgrCfg.DefaultReqType) - storCdr.Direction = utils.OUT storCdr.Tenant = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_TENANT], fsCdr.cgrCfg.DefaultTenant) storCdr.Category = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_CATEGORY], fsCdr.cgrCfg.DefaultCategory) storCdr.Account = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_ACCOUNT], fsCdr.vars[FS_USERNAME]) storCdr.Subject = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_SUBJECT], fsCdr.vars[utils.CGR_ACCOUNT], fsCdr.vars[FS_USERNAME]) storCdr.Destination = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_DESTINATION], fsCdr.vars[FS_CALL_DEST_NR], fsCdr.vars[FS_SIP_REQUSER]) storCdr.SetupTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_SETUP_TIME], timezone) // Not interested to process errors, should do them if necessary in a previous step - pddStr := utils.FirstNonEmpty(fsCdr.vars[FS_PROGRESS_MEDIAMSEC], fsCdr.vars[FS_PROGRESSMS]) - pddStr += "ms" - storCdr.PDD, _ = time.ParseDuration(pddStr) storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(fsCdr.vars[FS_ANSWER_TIME], timezone) storCdr.Usage, _ = utils.ParseDurationWithSecs(fsCdr.vars[FS_DURATION]) - storCdr.Supplier = fsCdr.vars[utils.CGR_SUPPLIER] - storCdr.DisconnectCause = utils.FirstNonEmpty(fsCdr.vars[utils.CGR_DISCONNECT_CAUSE], fsCdr.vars["hangup_cause"]) storCdr.ExtraFields = fsCdr.getExtraFields() storCdr.Cost = -1 return storCdr diff --git a/engine/fscdr_test.go b/engine/fscdr_test.go index 7e7db0801..568eb2824 100644 --- a/engine/fscdr_test.go +++ b/engine/fscdr_test.go @@ -53,10 +53,15 @@ func TestCDRFields(t *testing.T) { } setupTime, _ := utils.ParseTimeDetectLayout("1436280728", "") answerTime, _ := utils.ParseTimeDetectLayout("1436280728", "") - expctCDR := &CDR{CGRID: "164b0422fdc6a5117031b427439482c6a4f90e41", ToR: utils.VOICE, OriginID: "e3133bf7-dcde-4daf-9663-9a79ffcef5ad", - OriginHost: "127.0.0.1", Source: "freeswitch_json", Direction: utils.OUT, Category: "call", RequestType: utils.META_PREPAID, Tenant: "cgrates.org", Account: "1001", Subject: "1001", - Destination: "1003", SetupTime: setupTime, PDD: time.Duration(28) * time.Millisecond, AnswerTime: answerTime, Usage: time.Duration(66) * time.Second, Supplier: "supplier1", - DisconnectCause: "NORMAL_CLEARING", ExtraFields: map[string]string{"sip_user_agent": "PJSUA v2.3 Linux-3.2.0.4/x86_64/glibc-2.13"}, Cost: -1} + expctCDR := &CDR{CGRID: "164b0422fdc6a5117031b427439482c6a4f90e41", + ToR: utils.VOICE, OriginID: "e3133bf7-dcde-4daf-9663-9a79ffcef5ad", + OriginHost: "127.0.0.1", Source: "freeswitch_json", Category: "call", + RequestType: utils.META_PREPAID, Tenant: "cgrates.org", + Account: "1001", Subject: "1001", + Destination: "1003", SetupTime: setupTime, + AnswerTime: answerTime, Usage: time.Duration(66) * time.Second, + Cost: -1, + ExtraFields: map[string]string{"sip_user_agent": "PJSUA v2.3 Linux-3.2.0.4/x86_64/glibc-2.13"}} if CDR := fsCdr.AsCDR(""); !reflect.DeepEqual(expctCDR, CDR) { t.Errorf("Expecting: %v, received: %v", expctCDR, CDR) } diff --git a/engine/lcr.go b/engine/lcr.go index 2556ef083..fbfa8838a 100644 --- a/engine/lcr.go +++ b/engine/lcr.go @@ -93,7 +93,7 @@ func (self *LcrRequest) AsCallDescriptor(timezone string) (*CallDescriptor, erro var callDur time.Duration if len(self.Duration) == 0 { callDur = time.Duration(1) * time.Minute - } else if callDur, err = utils.ParseDurationWithSecs(self.Duration); err != nil { + } else if callDur, err = utils.ParseDurationWithNanosecs(self.Duration); err != nil { return nil, err } cd := &CallDescriptor{ @@ -198,22 +198,22 @@ func (le *LCREntry) GetQOSLimits() (minASR, maxASR float64, minPDD, maxPDD, minA if maxASR, err = strconv.ParseFloat(params[1], 64); err != nil { maxASR = -1 } - if minPDD, err = utils.ParseDurationWithSecs(params[2]); err != nil || params[2] == "" { + if minPDD, err = utils.ParseDurationWithNanosecs(params[2]); err != nil || params[2] == "" { minPDD = -1 } - if maxPDD, err = utils.ParseDurationWithSecs(params[3]); err != nil || params[3] == "" { + if maxPDD, err = utils.ParseDurationWithNanosecs(params[3]); err != nil || params[3] == "" { maxPDD = -1 } - if minACD, err = utils.ParseDurationWithSecs(params[4]); err != nil || params[4] == "" { + if minACD, err = utils.ParseDurationWithNanosecs(params[4]); err != nil || params[4] == "" { minACD = -1 } - if maxACD, err = utils.ParseDurationWithSecs(params[5]); err != nil || params[5] == "" { + if maxACD, err = utils.ParseDurationWithNanosecs(params[5]); err != nil || params[5] == "" { maxACD = -1 } - if minTCD, err = utils.ParseDurationWithSecs(params[6]); err != nil || params[6] == "" { + if minTCD, err = utils.ParseDurationWithNanosecs(params[6]); err != nil || params[6] == "" { minTCD = -1 } - if maxTCD, err = utils.ParseDurationWithSecs(params[7]); err != nil || params[7] == "" { + if maxTCD, err = utils.ParseDurationWithNanosecs(params[7]); err != nil || params[7] == "" { maxTCD = -1 } if minACC, err = strconv.ParseFloat(params[8], 64); err != nil { diff --git a/engine/lcr_test.go b/engine/lcr_test.go index b28f99752..6ea9916b7 100644 --- a/engine/lcr_test.go +++ b/engine/lcr_test.go @@ -93,7 +93,7 @@ func TestLcrQOSSorterOACD(t *testing.T) { func TestLcrGetQosLimitsAll(t *testing.T) { le := &LCREntry{ - StrategyParams: "1.2;2.3;4;7;45s;67m;16s;17m;8.9;10.11;12.13;14.15;2;3", + StrategyParams: "1.2;2.3;4s;7s;45s;67m;16s;17m;8.9;10.11;12.13;14.15;2;3", } minAsr, maxAsr, minPdd, maxPdd, minAcd, maxAcd, minTcd, maxTcd, minAcc, maxAcc, minTcc, maxTcc, minDdc, maxDdc := le.GetQOSLimits() if minAsr != 1.2 || maxAsr != 2.3 || @@ -109,7 +109,7 @@ func TestLcrGetQosLimitsAll(t *testing.T) { func TestLcrGetQosLimitsSome(t *testing.T) { le := &LCREntry{ - StrategyParams: "1.2;;3;;;67m;;30m;1;;3;;;2", + StrategyParams: "1.2;;3s;;;67m;;30m;1;;3;;;2", } minAsr, maxAsr, minPdd, maxPdd, minAcd, maxAcd, minTcd, maxTcd, minAcc, maxAcc, minTcc, maxTcc, minDdc, maxDdc := le.GetQOSLimits() if minAsr != 1.2 || maxAsr != -1 || diff --git a/engine/libstats.go b/engine/libstats.go index 8a94e5b9a..d7478eca2 100755 --- a/engine/libstats.go +++ b/engine/libstats.go @@ -91,7 +91,7 @@ func (se StatEvent) Usage() (at time.Duration, err error) { if !canCast { return at, errors.New("cannot cast to string") } - return utils.ParseDurationWithSecs(usStr) + return utils.ParseDurationWithNanosecs(usStr) } // Cost returns the Cost of StatEvent @@ -126,7 +126,7 @@ func (se StatEvent) Pdd() (pdd time.Duration, err error) { if !canCast { return pdd, errors.New("cannot cast to string") } - return utils.ParseDurationWithSecs(pddStr) + return utils.ParseDurationWithNanosecs(pddStr) } // Destination returns the Destination of StatEvent diff --git a/engine/libtest.go b/engine/libtest.go index 3810f0078..9664aedc9 100644 --- a/engine/libtest.go +++ b/engine/libtest.go @@ -34,7 +34,8 @@ import ( ) func InitDataDb(cfg *config.CGRConfig) error { - dm, err := ConfigureDataStorage(cfg.DataDbType, cfg.DataDbHost, cfg.DataDbPort, cfg.DataDbName, cfg.DataDbUser, cfg.DataDbPass, cfg.DBDataEncoding, cfg.CacheConfig, cfg.LoadHistorySize) + dm, err := ConfigureDataStorage(cfg.DataDbType, cfg.DataDbHost, cfg.DataDbPort, cfg.DataDbName, + cfg.DataDbUser, cfg.DataDbPass, cfg.DBDataEncoding, cfg.CacheCfg(), cfg.LoadHistorySize) if err != nil { return err } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 4b78f0764..0ce863c12 100755 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -58,20 +58,20 @@ WEEKENDS,*any,*any,*any,6;7,00:00:00 ONE_TIME_RUN,2012,,,,*asap ` rates = ` -R1,0,0.2,60,1,0 -R2,0,0.1,60s,1s,0 -R3,0,0.05,60s,1s,0 -R4,1,1,1s,1s,0 -R5,0,0.5,1s,1s,0 -LANDLINE_OFFPEAK,0,1,1,60,0 -LANDLINE_OFFPEAK,0,1,1,1,60 +R1,0,0.2,60s,1s,0s +R2,0,0.1,60s,1s,0s +R3,0,0.05,60s,1s,0s +R4,1,1,1s,1s,0s +R5,0,0.5,1s,1s,0s +LANDLINE_OFFPEAK,0,1,1s,60s,0s +LANDLINE_OFFPEAK,0,1,1s,1s,60s GBP_71,0.000000,5.55555,1s,1s,0s GBP_72,0.000000,7.77777,1s,1s,0s -GBP_70,0.000000,1,1,1,0 +GBP_70,0.000000,1,1s,1s,0s RT_UK_Mobile_BIG5_PKG,0.01,0,20s,20s,0s RT_UK_Mobile_BIG5,0.01,0.10,1s,1s,0s -R_URG,0,0,1,1,0 -MX,0,1,1s,1s,0 +R_URG,0,0,1s,1s,0s +MX,0,1,1s,1s,0s DY,0.15,0.05,60s,1s,0s CF,1.12,0,1s,1s,0s ` @@ -163,10 +163,10 @@ SG3,*any,*lowest, ` actions = ` MINI,*topup_reset,,,,*monetary,*out,,,,,*unlimited,,10,10,false,false,10 -MINI,*topup,,,,*voice,*out,,NAT,test,,*unlimited,,100,10,false,false,10 +MINI,*topup,,,,*voice,*out,,NAT,test,,*unlimited,,100s,10,false,false,10 SHARED,*topup,,,,*monetary,*out,,,,SG1,*unlimited,,100,10,false,false,10 TOPUP10_AC,*topup_reset,,,,*monetary,*out,,*any,,,*unlimited,,1,10,false,false,10 -TOPUP10_AC1,*topup_reset,,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40,10,false,false,10 +TOPUP10_AC1,*topup_reset,,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40s,10,false,false,10 SE0,*topup_reset,,,,*monetary,*out,,,,SG2,*unlimited,,0,10,false,false,10 SE10,*topup_reset,,,,*monetary,*out,,,,SG2,*unlimited,,10,5,false,false,10 SE10,*topup,,,,*monetary,*out,,,,,*unlimited,,10,10,false,false,10 @@ -179,8 +179,8 @@ BLOCK,*topup,,,bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 BLOCK_EMPTY,*topup,,,bblocker,*monetary,*out,,NAT,,,*unlimited,,0,20,true,false,20 BLOCK_EMPTY,*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 +EXP,*topup,,,,*voice,*out,,,,,*monthly,*any,300s,10,false,false,10 +NOEXP,*topup,,,,*voice,*out,,,,,*unlimited,*any,50s,10,false,false,10 VF,*debit,,,,*monetary,*out,,,,,*unlimited,*any,"{""Method"":""*incremental"",""Params"":{""Units"":10, ""Interval"":""month"", ""Increment"":""day""}}",10,false,false,10 TOPUP_RST_GNR_1000,*topup_reset,"{""*voice"": 60.0,""*data"":1024.0,""*sms"":1.0}",,,*generic,*out,,*any,,,*unlimited,,1000,20,false,false,10 ` @@ -495,7 +495,7 @@ func TestLoadRates(t *testing.T) { t.Error("Failed to load rates: ", len(csvr.rates)) } rate := csvr.rates["R1"].RateSlots[0] - expctRs, err := utils.NewRateSlot(0, 0.2, "60", "1", "0") + expctRs, err := utils.NewRateSlot(0, 0.2, "60s", "1s", "0s") if err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || @@ -505,16 +505,16 @@ func TestLoadRates(t *testing.T) { t.Error("Error loading rate: ", rate, expctRs) } rate = csvr.rates["R2"].RateSlots[0] - if expctRs, err = utils.NewRateSlot(0, 0.1, "60s", "1s", "0"); err != nil { + if expctRs, err = utils.NewRateSlot(0, 0.1, "60s", "1s", "0s"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || rate.RateIncrementDuration() != expctRs.RateIncrementDuration() || rate.GroupIntervalStartDuration() != expctRs.GroupIntervalStartDuration() { - t.Error("Error loading rate: ", rate) + t.Errorf("Expecting: %+v, received: %+v", expctRs, rate) } rate = csvr.rates["R3"].RateSlots[0] - if expctRs, err = utils.NewRateSlot(0, 0.05, "60s", "1s", "0"); err != nil { + if expctRs, err = utils.NewRateSlot(0, 0.05, "60s", "1s", "0s"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || @@ -523,7 +523,7 @@ func TestLoadRates(t *testing.T) { t.Error("Error loading rate: ", rate) } rate = csvr.rates["R4"].RateSlots[0] - if expctRs, err = utils.NewRateSlot(1, 1.0, "1s", "1s", "0"); err != nil { + if expctRs, err = utils.NewRateSlot(1, 1.0, "1s", "1s", "0s"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || @@ -532,7 +532,7 @@ func TestLoadRates(t *testing.T) { t.Error("Error loading rate: ", rate) } rate = csvr.rates["R5"].RateSlots[0] - if expctRs, err = utils.NewRateSlot(0, 0.5, "1s", "1s", "0"); err != nil { + if expctRs, err = utils.NewRateSlot(0, 0.5, "1s", "1s", "0s"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || @@ -541,7 +541,7 @@ func TestLoadRates(t *testing.T) { t.Error("Error loading rate: ", rate) } rate = csvr.rates["LANDLINE_OFFPEAK"].RateSlots[0] - if expctRs, err = utils.NewRateSlot(0, 1, "1", "60", "0"); err != nil { + if expctRs, err = utils.NewRateSlot(0, 1, "1s", "60s", "0s"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || @@ -550,7 +550,7 @@ func TestLoadRates(t *testing.T) { t.Error("Error loading rate: ", rate) } rate = csvr.rates["LANDLINE_OFFPEAK"].RateSlots[1] - if expctRs, err = utils.NewRateSlot(0, 1, "1", "1", "60"); err != nil { + if expctRs, err = utils.NewRateSlot(0, 1, "1s", "1s", "60s"); err != nil { t.Error("Error loading rate: ", rate, err.Error()) } else if !reflect.DeepEqual(rate, expctRs) || rate.RateUnitDuration() != expctRs.RateUnitDuration() || @@ -941,7 +941,7 @@ func TestLoadActions(t *testing.T) { Type: utils.StringPointer(utils.VOICE), Uuid: as1[1].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), - Value: &utils.ValueFormula{Static: 100}, + Value: &utils.ValueFormula{Static: 100 * float64(time.Second)}, Weight: utils.Float64Pointer(10), RatingSubject: utils.StringPointer("test"), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 42a73ced2..2f18e777f 100755 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -1925,7 +1925,7 @@ func APItoResource(tpRL *utils.TPResource, timezone string) (rp *ResourceProfile AllocationMessage: tpRL.AllocationMessage, } if tpRL.UsageTTL != "" { - if rp.UsageTTL, err = utils.ParseDurationWithSecs(tpRL.UsageTTL); err != nil { + if rp.UsageTTL, err = utils.ParseDurationWithNanosecs(tpRL.UsageTTL); err != nil { return nil, err } } @@ -2077,7 +2077,7 @@ func APItoStats(tpST *utils.TPStats, timezone string) (st *StatQueueProfile, err MinItems: tpST.MinItems, } if tpST.TTL != "" { - if st.TTL, err = utils.ParseDurationWithSecs(tpST.TTL); err != nil { + if st.TTL, err = utils.ParseDurationWithNanosecs(tpST.TTL); err != nil { return nil, err } } @@ -2282,7 +2282,7 @@ func APItoThresholdProfile(tpTH *utils.TPThreshold, timezone string) (th *Thresh Async: tpTH.Async, } if tpTH.MinSleep != "" { - if th.MinSleep, err = utils.ParseDurationWithSecs(tpTH.MinSleep); err != nil { + if th.MinSleep, err = utils.ParseDurationWithNanosecs(tpTH.MinSleep); err != nil { return nil, err } } diff --git a/engine/model_helpers_test.go b/engine/model_helpers_test.go index 20e8dbc84..db2709966 100755 --- a/engine/model_helpers_test.go +++ b/engine/model_helpers_test.go @@ -876,7 +876,7 @@ func TestAPItoTPStats(t *testing.T) { Weight: 20.0, MinItems: tps.MinItems, } - if eTPs.TTL, err = utils.ParseDurationWithSecs(tps.TTL); err != nil { + if eTPs.TTL, err = utils.ParseDurationWithNanosecs(tps.TTL); err != nil { t.Errorf("Got error: %+v", err) } at, _ := utils.ParseTimeDetectLayout("2014-07-29T15:00:00Z", "UTC") @@ -949,7 +949,7 @@ func TestAPItoTPThreshold(t *testing.T) { FilterIDs: tps.FilterIDs, ActionIDs: []string{"WARN3"}, } - if eTPs.MinSleep, err = utils.ParseDurationWithSecs(tps.MinSleep); err != nil { + if eTPs.MinSleep, err = utils.ParseDurationWithNanosecs(tps.MinSleep); err != nil { t.Errorf("Got error: %+v", err) } at, _ := utils.ParseTimeDetectLayout("2014-07-29T15:00:00Z", "UTC") diff --git a/engine/models.go b/engine/models.go index 2a035f367..2f1379da6 100755 --- a/engine/models.go +++ b/engine/models.go @@ -398,59 +398,6 @@ func (ta *TpAlias) GetId() string { return utils.ConcatenatedKey(ta.Direction, ta.Tenant, ta.Category, ta.Account, ta.Subject, ta.Context) } -type TBLCDRs struct { - ID int64 - Cgrid string - RunID string - OriginHost string - Source string - OriginID string - Tor string - RequestType string - Direction string - Tenant string - Category string - Account string - Subject string - Destination string - SetupTime time.Time - Pdd float64 - AnswerTime time.Time - Usage float64 - Supplier string - DisconnectCause string - ExtraFields string - Cost float64 - CostDetails string - CostSource string - AccountSummary string - ExtraInfo string - CreatedAt time.Time - UpdatedAt time.Time - DeletedAt *time.Time -} - -func (t TBLCDRs) TableName() string { - return utils.TBLCDRs -} - -type TBLSMCosts struct { - ID int64 - Cgrid string - RunID string - OriginHost string - OriginID string - CostSource string - Usage float64 - CostDetails string - CreatedAt time.Time - DeletedAt *time.Time -} - -func (t TBLSMCosts) TableName() string { - return utils.TBLSMCosts -} - type TpResource struct { PK uint `gorm:"primary_key"` Tpid string @@ -468,16 +415,6 @@ type TpResource struct { CreatedAt time.Time } -type TBLVersion struct { - ID uint - Item string - Version int64 -} - -func (t TBLVersion) TableName() string { - return utils.TBLVersions -} - type TpStats struct { PK uint `gorm:"primary_key"` Tpid string @@ -525,6 +462,64 @@ type TpFilter struct { CreatedAt time.Time } +type CDRsql struct { + ID int64 + Cgrid string + RunID string + OriginHost string + Source string + OriginID string + TOR string + RequestType string + Tenant string + Category string + Account string + Subject string + Destination string + SetupTime time.Time + AnswerTime time.Time + Usage int64 + ExtraFields string + CostSource string + Cost float64 + CostDetails string + ExtraInfo string + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt *time.Time +} + +func (t CDRsql) TableName() string { + return utils.CDRsTBL +} + +type SMCostSQL struct { + ID int64 + Cgrid string + RunID string + OriginHost string + OriginID string + CostSource string + Usage int64 + CostDetails string + CreatedAt time.Time + DeletedAt *time.Time +} + +func (t SMCostSQL) TableName() string { + return utils.SMCostsTBL +} + +type TBLVersion struct { + ID uint + Item string + Version int64 +} + +func (t TBLVersion) TableName() string { + return utils.TBLVersions +} + type TpLCR struct { PK uint `gorm:"primary_key"` Tpid string diff --git a/engine/onstor_it_test.go b/engine/onstor_it_test.go index 81e65cef4..95c01317b 100644 --- a/engine/onstor_it_test.go +++ b/engine/onstor_it_test.go @@ -106,7 +106,8 @@ var sTestsOnStorIT = []func(t *testing.T){ func TestOnStorITRedisConnect(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() - rdsITdb, err = NewRedisStorage(fmt.Sprintf("%s:%s", cfg.DataDbHost, cfg.DataDbPort), 4, cfg.DataDbPass, cfg.DBDataEncoding, utils.REDIS_MAX_CONNS, nil, 1) + rdsITdb, err = NewRedisStorage(fmt.Sprintf("%s:%s", cfg.DataDbHost, cfg.DataDbPort), 4, + cfg.DataDbPass, cfg.DBDataEncoding, utils.REDIS_MAX_CONNS, nil, 1) if err != nil { t.Fatal("Could not connect to Redis", err.Error()) } @@ -126,8 +127,9 @@ func TestOnStorITMongoConnect(t *testing.T) { if err != nil { t.Fatal(err) } - if mgoITdb, err = NewMongoStorage(mgoITCfg.StorDBHost, mgoITCfg.StorDBPort, mgoITCfg.StorDBName, mgoITCfg.StorDBUser, mgoITCfg.StorDBPass, - utils.StorDB, nil, mgoITCfg.CacheConfig, mgoITCfg.LoadHistorySize); err != nil { + if mgoITdb, err = NewMongoStorage(mgoITCfg.StorDBHost, mgoITCfg.StorDBPort, + mgoITCfg.StorDBName, mgoITCfg.StorDBUser, mgoITCfg.StorDBPass, + utils.StorDB, nil, mgoITCfg.CacheCfg(), mgoITCfg.LoadHistorySize); err != nil { t.Fatal(err) } onStorCfg = mgoITCfg.StorDBName @@ -1094,6 +1096,7 @@ func testOnStorITCacheFilter(t *testing.T) { } else if rcv := itm.(*Filter); !reflect.DeepEqual(filter, rcv) { t.Errorf("Expecting: %+v, received: %+v", filter, rcv) } + } func testOnStorITCacheLCRProfile(t *testing.T) { diff --git a/engine/rateinterval.go b/engine/rateinterval.go index 665905c90..c84a586c8 100644 --- a/engine/rateinterval.go +++ b/engine/rateinterval.go @@ -348,11 +348,10 @@ func (i *RateInterval) Equal(o *RateInterval) bool { } func (i *RateInterval) GetCost(duration, startSecond time.Duration) float64 { - price, _, rateUnit := i. - GetRateParameters(startSecond) - price /= rateUnit.Seconds() - d := duration.Seconds() - return d * price + price, _, rateUnit := i.GetRateParameters(startSecond) + price /= float64(rateUnit.Nanoseconds()) + d := float64(duration.Nanoseconds()) + return utils.Round(d*price, globalRoundingDecimals, utils.ROUNDING_MIDDLE) } // Gets the price for a the provided start second diff --git a/engine/ratingplan.go b/engine/ratingplan.go index fabe6e5b1..46a93084f 100644 --- a/engine/ratingplan.go +++ b/engine/ratingplan.go @@ -152,7 +152,8 @@ func (rp *RatingPlan) getFirstUnsaneRating() string { if nextRate.GroupIntervalStart <= rate.GroupIntervalStart { return rating.tag } - if math.Mod(nextRate.GroupIntervalStart.Seconds(), rate.RateIncrement.Seconds()) != 0 { + if math.Mod(float64(nextRate.GroupIntervalStart.Nanoseconds()), + float64(rate.RateIncrement.Nanoseconds())) != 0 { return rating.tag } if rate.RateUnit == 0 || rate.RateIncrement == 0 { diff --git a/engine/responder.go b/engine/responder.go index a8014a128..1057460c1 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -46,12 +46,13 @@ type AttrGetLcr struct { } type Responder struct { - ExitChan chan bool - Stats rpcclient.RpcClientConnection - Timeout time.Duration - Timezone string - cnt int64 - responseCache *cache.ResponseCache + ExitChan chan bool + Stats rpcclient.RpcClientConnection + Timeout time.Duration + Timezone string + MaxComputedUsage map[string]time.Duration + cnt int64 + responseCache *cache.ResponseCache } func (rs *Responder) SetTimeToLive(timeToLive time.Duration, out *int) error { @@ -66,6 +67,18 @@ func (rs *Responder) getCache() *cache.ResponseCache { return rs.responseCache } +// usageAllowed checks requested usage against configured MaxComputedUsage +func (rs *Responder) usageAllowed(tor string, reqUsage time.Duration) (allowed bool) { + mcu, has := rs.MaxComputedUsage[tor] + if !has { + mcu = rs.MaxComputedUsage[utils.ANY] + } + if reqUsage <= mcu { + allowed = true + } + return +} + /* RPC method thet provides the external RPC interface for getting the rating information. */ @@ -91,6 +104,9 @@ func (rs *Responder) GetCost(arg *CallDescriptor, reply *CallCost) (err error) { }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } r, e := guardian.Guardian.Guard(func() (interface{}, error) { return arg.GetCost() }, 0, arg.GetAccountKey()) @@ -124,6 +140,9 @@ func (rs *Responder) Debit(arg *CallDescriptor, reply *CallCost) (err error) { }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } r, e := arg.Debit() if e != nil { return e @@ -161,6 +180,9 @@ func (rs *Responder) MaxDebit(arg *CallDescriptor, reply *CallCost) (err error) }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } r, e := arg.MaxDebit() if e != nil { rs.getCache().Cache(cacheKey, &cache.CacheItem{ @@ -208,6 +230,9 @@ func (rs *Responder) RefundIncrements(arg *CallDescriptor, reply *float64) (err }) return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } err = arg.RefundIncrements() rs.getCache().Cache(cacheKey, &cache.CacheItem{ Value: reply, @@ -247,6 +272,9 @@ func (rs *Responder) RefundRounding(arg *CallDescriptor, reply *float64) (err er }) return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } err = arg.RefundRounding() rs.getCache().Cache(cacheKey, &cache.CacheItem{ Value: reply, @@ -276,6 +304,9 @@ func (rs *Responder) GetMaxSessionTime(arg *CallDescriptor, reply *float64) (err }, arg, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { return err } + if !rs.usageAllowed(arg.TOR, arg.GetDuration()) { + return utils.ErrMaxUsageExceeded + } r, e := arg.GetMaxSessionDuration() *reply, err = float64(r), e return @@ -302,7 +333,7 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *CDR, reply *float64) error { if err := LoadAlias( &AttrMatchingAlias{ Destination: ev.Destination, - Direction: ev.Direction, + Direction: utils.OUT, Tenant: ev.Tenant, Category: ev.Category, Account: ev.Account, @@ -312,9 +343,12 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *CDR, reply *float64) error { rs.getCache().Cache(cacheKey, &cache.CacheItem{Err: err}) return err } - + if !rs.usageAllowed(ev.ToR, ev.Usage) { + return utils.ErrMaxUsageExceeded + } maxCallDuration := -1.0 - attrsDC := &utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Direction: ev.GetDirection(utils.META_DEFAULT), + attrsDC := &utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), + Category: ev.GetCategory(utils.META_DEFAULT), Direction: utils.OUT, Account: ev.GetAccount(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT)} dcs := &utils.DerivedChargers{} if err := rs.GetDerivedChargers(attrsDC, dcs); err != nil { @@ -354,7 +388,7 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *CDR, reply *float64) error { CgrID: ev.GetCgrId(rs.Timezone), RunID: dc.RunID, TOR: ev.ToR, - Direction: ev.GetDirection(dc.DirectionField), + Direction: utils.OUT, Tenant: ev.GetTenant(dc.TenantField), Category: ev.GetCategory(dc.CategoryField), Subject: ev.GetSubject(dc.SubjectField), @@ -407,7 +441,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { if err := LoadAlias( &AttrMatchingAlias{ Destination: ev.Destination, - Direction: ev.Direction, + Direction: utils.OUT, Tenant: ev.Tenant, Category: ev.Category, Account: ev.Account, @@ -418,8 +452,10 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { } //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)} + attrsDC := &utils.AttrDerivedChargers{Tenant: ev.GetTenant(utils.META_DEFAULT), + Category: ev.GetCategory(utils.META_DEFAULT), Direction: utils.OUT, + 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 { @@ -453,7 +489,7 @@ func (rs *Responder) GetSessionRuns(ev *CDR, sRuns *[]*SessionRun) error { CgrID: ev.GetCgrId(rs.Timezone), RunID: dc.RunID, TOR: ev.ToR, - Direction: ev.GetDirection(dc.DirectionField), + Direction: utils.OUT, Tenant: ev.GetTenant(dc.TenantField), Category: ev.GetCategory(dc.CategoryField), Subject: ev.GetSubject(dc.SubjectField), @@ -515,6 +551,9 @@ func (rs *Responder) GetLCR(attrs *AttrGetLcr, reply *LCRCost) error { rs.getCache().Cache(cacheKey, &cache.CacheItem{Err: err}) return err } + if !rs.usageAllowed(cd.TOR, cd.GetDuration()) { + return utils.ErrMaxUsageExceeded + } lcrCost, err := attrs.CallDescriptor.GetLCR(rs.Stats, attrs.LCRFilter, attrs.Paginator) if err != nil { rs.getCache().Cache(cacheKey, &cache.CacheItem{Err: err}) @@ -532,7 +571,7 @@ func (rs *Responder) GetLCR(attrs *AttrGetLcr, reply *LCRCost) error { func (rs *Responder) Status(arg string, reply *map[string]interface{}) (err error) { if arg != "" { // Introduce delay in answer, used in some automated tests - if delay, err := utils.ParseDurationWithSecs(arg); err == nil { + if delay, err := utils.ParseDurationWithNanosecs(arg); err == nil { time.Sleep(delay) } } diff --git a/engine/responder_test.go b/engine/responder_test.go index b865a20b8..56f66754d 100644 --- a/engine/responder_test.go +++ b/engine/responder_test.go @@ -33,6 +33,7 @@ var rsponder *Responder func init() { cfg, _ := config.NewDefaultCGRConfig() config.SetCgrConfig(cfg) + rsponder = &Responder{MaxComputedUsage: cfg.RALsMaxComputedUsage} } // Test internal abilites of GetDerivedChargers @@ -40,7 +41,6 @@ func TestResponderGetDerivedChargers(t *testing.T) { cfgedDC := &utils.DerivedChargers{DestinationIDs: utils.StringMap{}, Chargers: []*utils.DerivedCharger{&utils.DerivedCharger{RunID: "responder1", RequestTypeField: utils.META_DEFAULT, DirectionField: "test", TenantField: "test", CategoryField: "test", AccountField: "test", SubjectField: "test", DestinationField: "test", SetupTimeField: "test", AnswerTimeField: "test", UsageField: "test"}}} - rsponder = &Responder{} attrs := &utils.AttrDerivedChargers{Tenant: "cgrates.org", Category: "call", Direction: "*out", Account: "responder_test", Subject: "responder_test"} if err := dm.DataDB().SetDerivedChargers(utils.DerivedChargersKey(utils.OUT, utils.ANY, utils.ANY, utils.ANY, utils.ANY), cfgedDC, utils.NonTransactional); err != nil { t.Error(err) @@ -55,28 +55,37 @@ func TestResponderGetDerivedChargers(t *testing.T) { func TestResponderGetDerivedMaxSessionTime(t *testing.T) { testTenant := "vdf" - cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "test", RequestType: utils.META_RATED, Direction: "*out", Tenant: testTenant, Category: "call", Account: "dan", Subject: "dan", - Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), - RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, - Cost: 1.01} + cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), + OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", OriginHost: "192.168.1.1", Source: "test", + RequestType: utils.META_RATED, Tenant: testTenant, Category: "call", Account: "dan", Subject: "dan", + Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Cost: 1.01} var maxSessionTime float64 if err := rsponder.GetDerivedMaxSessionTime(cdr, &maxSessionTime); err != nil { t.Error(err) } else if maxSessionTime != -1 { t.Error("Unexpected maxSessionTime received: ", maxSessionTime) } - deTMobile := &Destination{Id: "DE_TMOBILE", Prefixes: []string{"+49151", "+49160", "+49170", "+49171", "+49175"}} + deTMobile := &Destination{Id: "DE_TMOBILE", + Prefixes: []string{"+49151", "+49160", "+49170", "+49171", "+49175"}} if err := dm.DataDB().SetDestination(deTMobile, utils.NonTransactional); err != nil { t.Error(err) } if err := dm.DataDB().SetReverseDestination(deTMobile, utils.NonTransactional); 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")} - 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}}} + b10 := &Balance{Value: 10 * float64(time.Second), + Weight: 10, DestinationIDs: utils.NewStringMap("DE_TMOBILE")} + b20 := &Balance{Value: 20 * float64(time.Second), + Weight: 10, DestinationIDs: utils.NewStringMap("DE_TMOBILE")} + 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 := dm.DataDB().SetAccount(rifsAccount); err != nil { t.Error(err) } @@ -123,11 +132,13 @@ func TestResponderGetDerivedMaxSessionTime(t *testing.T) { func TestResponderGetSessionRuns(t *testing.T) { testTenant := "vdf" - cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", - OriginHost: "192.168.1.1", Source: "test", RequestType: utils.META_PREPAID, Direction: "*out", Tenant: testTenant, Category: "call", Account: "dan2", Subject: "dan2", - Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), PDD: 3 * time.Second, - AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), Supplier: "suppl1", - RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, + cdr := &CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), + OrderID: 123, ToR: utils.VOICE, OriginID: "dsafdsaf", + OriginHost: "192.168.1.1", Source: "test", RequestType: utils.META_PREPAID, + Tenant: testTenant, Category: "call", Account: "dan2", Subject: "dan2", + Destination: "1002", SetupTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + RunID: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01} keyCharger1 := utils.ConcatenatedKey("*out", testTenant, "call", "dan2", "dan2") dfDC := &utils.DerivedCharger{RunID: utils.DEFAULT_RUNID, RequestTypeField: utils.META_DEFAULT, DirectionField: utils.META_DEFAULT, TenantField: utils.META_DEFAULT, @@ -431,10 +442,18 @@ 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)} - 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} + bRif12 := &Balance{Value: 40 * float64(time.Second), + Weight: 10, DestinationIDs: utils.NewStringMap(dstDe.Id)} + bIvo12 := &Balance{Value: 60 * float64(time.Second), + Weight: 10, DestinationIDs: utils.NewStringMap(dstDe.Id)} + 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 := dm.DataDB().SetAccount(acnt); err != nil { t.Error(err) @@ -454,87 +473,101 @@ func TestResponderGetLCR(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", eLcLcr.Entry, lcrLc.Entry) } else if !reflect.DeepEqual(eLcLcr.SupplierCosts, lcrLc.SupplierCosts) { - t.Errorf("Expecting: %+v, received: %+v", eLcLcr.SupplierCosts, lcrLc.SupplierCosts) + t.Errorf("Expecting: %s\n, received: %+v", utils.ToJSON(eLcLcr.SupplierCosts), utils.ToJSON(lcrLc.SupplierCosts)) } + /* + // Test *qos_threshold strategy here, + // FixMe with newly stats + cdQosThreshold := &CallDescriptor{ + TimeStart: time.Date(2015, 04, 06, 17, 40, 0, 0, time.UTC), + TimeEnd: time.Date(2015, 04, 06, 17, 41, 0, 0, time.UTC), + Tenant: "tenant12", + Direction: utils.OUT, + Category: "call_qos_threshold", + Destination: "+4986517174963", + Account: "dan", + Subject: "dan", + } + eQTLcr := &LCRCost{ + Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", + Strategy: LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "35;;;;4m;;;;;;;;;", Weight: 10.0}, + SupplierCosts: []*LCRSupplierCost{ + &LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, + QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}}, + &LCRSupplierCost{Supplier: "*out:tenant12:call:rif12", Cost: 0.4, Duration: 60 * time.Second, + QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}}, + &LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, + QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}}, + }, + } + var lcrQT LCRCost + if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdQosThreshold}, &lcrQT); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eQTLcr.Entry, lcrQT.Entry) { + t.Errorf("Expecting: %+v, received: %+v", eQTLcr.Entry, lcrQT.Entry) - // Test *qos_threshold strategy here - cdQosThreshold := &CallDescriptor{ - TimeStart: time.Date(2015, 04, 06, 17, 40, 0, 0, time.UTC), - TimeEnd: time.Date(2015, 04, 06, 17, 41, 0, 0, time.UTC), - Tenant: "tenant12", - Direction: utils.OUT, - Category: "call_qos_threshold", - Destination: "+4986517174963", - Account: "dan", - Subject: "dan", - } - eQTLcr := &LCRCost{ - Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "35;;;;4m;;;;;;;;;", Weight: 10.0}, - SupplierCosts: []*LCRSupplierCost{ - &LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}}, - &LCRSupplierCost{Supplier: "*out:tenant12:call:rif12", Cost: 0.4, Duration: 60 * time.Second, QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}}, - &LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, PDD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}}, - }, - } - var lcrQT LCRCost - if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdQosThreshold}, &lcrQT); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eQTLcr.Entry, lcrQT.Entry) { - t.Errorf("Expecting: %+v, received: %+v", eQTLcr.Entry, lcrQT.Entry) + } else if !reflect.DeepEqual(eQTLcr.SupplierCosts, lcrQT.SupplierCosts) { + t.Errorf("Expecting: %+v, received: %+v", eQTLcr.SupplierCosts, lcrQT.SupplierCosts) + } - } else if !reflect.DeepEqual(eQTLcr.SupplierCosts, lcrQT.SupplierCosts) { - t.Errorf("Expecting: %+v, received: %+v", eQTLcr.SupplierCosts, lcrQT.SupplierCosts) - } + cdr := &CDR{AnswerTime: time.Now(), Usage: 3 * time.Minute, Cost: 1} + rsponder.Stats.Call("CDRStatsV1.AppendCDR", cdr, &r) + cdr = &CDR{AnswerTime: time.Now(), Usage: 5 * time.Minute, Cost: 2} + rsponder.Stats.Call("CDRStatsV1.AppendCDR", cdr, &r) - cdr := &CDR{Supplier: "rif12", AnswerTime: time.Now(), Usage: 3 * time.Minute, Cost: 1} - rsponder.Stats.Call("CDRStatsV1.AppendCDR", cdr, &r) - cdr = &CDR{Supplier: "dan12", AnswerTime: time.Now(), Usage: 5 * time.Minute, Cost: 2} - rsponder.Stats.Call("CDRStatsV1.AppendCDR", cdr, &r) + eQTLcr = &LCRCost{ + Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", + Strategy: LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "35;;;;4m;;;;;;;;;", Weight: 10.0}, + SupplierCosts: []*LCRSupplierCost{ + &LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, + QOS: map[string]float64{PDD: -1, TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}}, + &LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, + QOS: map[string]float64{PDD: -1, ACD: 300, TCD: 300, ASR: 100, ACC: 2, TCC: 2, DDC: 2}, qosSortParams: []string{"35", "4m"}}, + }, + } + if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdQosThreshold}, &lcrQT); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eQTLcr.Entry, lcrQT.Entry) { + t.Errorf("Expecting: %+v, received: %+v", eQTLcr.Entry, lcrQT.Entry) + } else if !reflect.DeepEqual(eQTLcr.SupplierCosts, lcrQT.SupplierCosts) { + t.Errorf("Expecting: %s, received: %s", + utils.ToJSON(eQTLcr.SupplierCosts), utils.ToJSON(lcrQT.SupplierCosts)) + } - eQTLcr = &LCRCost{ - Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_QOS_THRESHOLD, StrategyParams: "35;;;;4m;;;;;;;;;", Weight: 10.0}, - SupplierCosts: []*LCRSupplierCost{ - &LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, QOS: map[string]float64{PDD: -1, TCD: -1, ACC: -1, TCC: -1, ASR: -1, ACD: -1, DDC: -1}, qosSortParams: []string{"35", "4m"}}, - &LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{PDD: -1, ACD: 300, TCD: 300, ASR: 100, ACC: 2, TCC: 2, DDC: 2}, qosSortParams: []string{"35", "4m"}}, - }, - } - if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdQosThreshold}, &lcrQT); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eQTLcr.Entry, lcrQT.Entry) { - t.Errorf("Expecting: %+v, received: %+v", eQTLcr.Entry, lcrQT.Entry) - } else if !reflect.DeepEqual(eQTLcr.SupplierCosts, lcrQT.SupplierCosts) { - t.Errorf("Expecting: %+v, received: %+v", eQTLcr.SupplierCosts, lcrQT.SupplierCosts) - } - // Test *qos strategy here - cdQos := &CallDescriptor{ - TimeStart: time.Date(2015, 04, 06, 17, 40, 0, 0, time.UTC), - TimeEnd: time.Date(2015, 04, 06, 17, 41, 0, 0, time.UTC), - Tenant: "tenant12", - Direction: utils.OUT, - Category: "call_qos", - Destination: "+4986517174963", - Account: "dan", - Subject: "dan", - } - eQosLcr := &LCRCost{ - Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_QOS, Weight: 10.0}, - SupplierCosts: []*LCRSupplierCost{ - &LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, QOS: map[string]float64{ACD: -1, PDD: -1, TCD: -1, ASR: -1, ACC: -1, TCC: -1, DDC: -1}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}}, - &LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 300, PDD: -1, TCD: 300, ASR: 100, ACC: 2, TCC: 2, DDC: 2}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}}, - &LCRSupplierCost{Supplier: "*out:tenant12:call:rif12", Cost: 0.4, Duration: 60 * time.Second, QOS: map[string]float64{ACD: 180, PDD: -1, TCD: 180, ASR: 100, ACC: 1, TCC: 1, DDC: 1}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}}, - }, - } - var lcrQ LCRCost - if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdQos}, &lcrQ); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(eQosLcr.Entry, lcrQ.Entry) { - t.Errorf("Expecting: %+v, received: %+v", eQosLcr.Entry, lcrQ.Entry) + // Test *qos strategy here + cdQos := &CallDescriptor{ + TimeStart: time.Date(2015, 04, 06, 17, 40, 0, 0, time.UTC), + TimeEnd: time.Date(2015, 04, 06, 17, 41, 0, 0, time.UTC), + Tenant: "tenant12", + Direction: utils.OUT, + Category: "call_qos", + Destination: "+4986517174963", + Account: "dan", + Subject: "dan", + } + eQosLcr := &LCRCost{ + Entry: &LCREntry{DestinationId: utils.ANY, RPCategory: "call", Strategy: LCR_STRATEGY_QOS, Weight: 10.0}, + SupplierCosts: []*LCRSupplierCost{ + &LCRSupplierCost{Supplier: "*out:tenant12:call:ivo12", Cost: 0, Duration: 60 * time.Second, + QOS: map[string]float64{ACD: -1, PDD: -1, TCD: -1, ASR: -1, ACC: -1, TCC: -1, DDC: -1}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}}, + &LCRSupplierCost{Supplier: "*out:tenant12:call:dan12", Cost: 0.6, Duration: 60 * time.Second, + QOS: map[string]float64{ACD: 300, PDD: -1, TCD: 300, ASR: 100, ACC: 2, TCC: 2, DDC: 2}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}}, + &LCRSupplierCost{Supplier: "*out:tenant12:call:rif12", Cost: 0.4, Duration: 60 * time.Second, + QOS: map[string]float64{ACD: 180, PDD: -1, TCD: 180, ASR: 100, ACC: 1, TCC: 1, DDC: 1}, qosSortParams: []string{ASR, PDD, ACD, TCD, ACC, TCC, DDC}}, + }, + } + var lcrQ LCRCost + if err := rsponder.GetLCR(&AttrGetLcr{CallDescriptor: cdQos}, &lcrQ); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eQosLcr.Entry, lcrQ.Entry) { + t.Errorf("Expecting: %+v, received: %+v", eQosLcr.Entry, lcrQ.Entry) - } else if !reflect.DeepEqual(eQosLcr.SupplierCosts, lcrQ.SupplierCosts) { - t.Errorf("Expecting: %+v, received: %+v", eQosLcr.SupplierCosts, lcrQ.SupplierCosts) - } + } else if !reflect.DeepEqual(eQosLcr.SupplierCosts, lcrQ.SupplierCosts) { + t.Errorf("Expecting: %+v, received: %+v", eQosLcr.SupplierCosts, lcrQ.SupplierCosts) + } + */ } func TestResponderGobSMCost(t *testing.T) { diff --git a/engine/statscdrs_test.go b/engine/statscdrs_test.go deleted file mode 100644 index 5df153147..000000000 --- a/engine/statscdrs_test.go +++ /dev/null @@ -1,555 +0,0 @@ -/* -Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments -Copyright (C) ITsysCOM GmbH - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ -package engine - -import ( - "testing" - "time" - - "github.com/cgrates/cgrates/utils" -) - -func TestCDRStatsQueueInit(t *testing.T) { - sq := NewCDRStatsQueue(&CdrStats{Metrics: []string{ASR, ACC}}) - if len(sq.metrics) != 2 { - t.Error("Expected 2 metrics got ", len(sq.metrics)) - } -} - -func TestStatsValue(t *testing.T) { - sq := NewCDRStatsQueue(&CdrStats{Metrics: []string{ASR, ACD, TCD, ACC, TCC}}) - cdr := &CDR{ - SetupTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - AnswerTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - Usage: 10 * time.Second, - Cost: 1, - } - sq.AppendCDR(cdr) - cdr.Cost = 2 - sq.AppendCDR(cdr) - cdr.Cost = 3 - sq.AppendCDR(cdr) - s := sq.GetStats() - if s[ASR] != 100 || - s[ACD] != 10 || - s[TCD] != 30 || - s[ACC] != 2 || - s[TCC] != 6 { - t.Errorf("Error getting stats: %+v", s) - } -} - -func TestStatsSimplifyCDR(t *testing.T) { - cdr := &CDR{ - ToR: "tor", - OriginID: "accid", - OriginHost: "cdrhost", - Source: "cdrsource", - RequestType: "reqtype", - Direction: "direction", - Tenant: "tenant", - Category: "category", - Account: "account", - Subject: "subject", - Destination: "12345678", - SetupTime: time.Date(2014, 7, 3, 13, 43, 0, 0, time.UTC), - Usage: 10 * time.Second, - RunID: "mri", - Cost: 10, - } - sq := &CDRStatsQueue{} - qcdr := sq.simplifyCdr(cdr) - if cdr.SetupTime != qcdr.SetupTime || - cdr.AnswerTime != qcdr.AnswerTime || - cdr.Usage != qcdr.Usage || - cdr.Cost != qcdr.Cost { - t.Errorf("Failed to simplify cdr: %+v", qcdr) - } -} - -func TestAcceptCdr(t *testing.T) { - sq := NewCDRStatsQueue(nil) - cdr := &CDR{ - ToR: "tor", - OriginID: "accid", - OriginHost: "cdrhost", - Source: "cdrsource", - RequestType: "reqtype", - Direction: "direction", - Tenant: "tenant", - Category: "category", - Account: "account", - Subject: "subject", - Destination: "0723045326", - SetupTime: time.Date(2014, 7, 3, 13, 43, 0, 0, time.UTC), - Usage: 10 * time.Second, - PDD: 7 * time.Second, - Supplier: "supplier1", - DisconnectCause: "normal", - RunID: "mri", - Cost: 10, - } - sq.conf = &CdrStats{} - if sq.conf.AcceptCdr(cdr) != true { - t.Errorf("Should have accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{TOR: []string{"test"}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{CdrHost: []string{"test"}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{CdrSource: []string{"test"}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{Direction: []string{"test"}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{Tenant: []string{"test"}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{Category: []string{"test"}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{Account: []string{"test"}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{Subject: []string{"test"}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{Supplier: []string{"test"}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{DisconnectCause: []string{"test"}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{DestinationIds: []string{"test"}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{DestinationIds: []string{"NAT", "RET"}} - if sq.conf.AcceptCdr(cdr) != true { - t.Errorf("Should have accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{SetupInterval: []time.Time{time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{SetupInterval: []time.Time{time.Date(2014, 7, 3, 13, 42, 0, 0, time.UTC), time.Date(2014, 7, 3, 13, 43, 0, 0, time.UTC)}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{SetupInterval: []time.Time{time.Date(2014, 7, 3, 13, 42, 0, 0, time.UTC)}} - if sq.conf.AcceptCdr(cdr) != true { - t.Errorf("Should have accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{SetupInterval: []time.Time{time.Date(2014, 7, 3, 13, 42, 0, 0, time.UTC), time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC)}} - if sq.conf.AcceptCdr(cdr) != true { - t.Errorf("Should have accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{UsageInterval: []time.Duration{11 * time.Second}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{UsageInterval: []time.Duration{1 * time.Second, 10 * time.Second}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{PddInterval: []time.Duration{8 * time.Second}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{PddInterval: []time.Duration{3 * time.Second, 7 * time.Second}} - if sq.conf.AcceptCdr(cdr) == true { - t.Errorf("Should have NOT accepted this CDR: %+v", cdr) - } - sq.conf = &CdrStats{PddInterval: []time.Duration{3 * time.Second, 8 * time.Second}} - if sq.conf.AcceptCdr(cdr) != true { - t.Errorf("Should have accepted this CDR: %+v", cdr) - } -} - -func TestCDRStatsQueueIds(t *testing.T) { - cdrStats := NewStats(dm, 0) - ids := []string{} - if err := cdrStats.GetQueueIds(0, &ids); err != nil { - t.Error("Errorf getting queue ids: ", err) - } - result := len(ids) - expected := 5 - if result != expected { - t.Errorf("Errorf loading stats queues. Expected %v was %v (%v)", expected, result, ids) - } -} - -func TestStatsAppendCdr(t *testing.T) { - cdrStats := NewStats(dm, 0) - cdr := &CDR{ - Tenant: "cgrates.org", - Category: "call", - AnswerTime: time.Now(), - SetupTime: time.Now(), - Usage: 10 * time.Second, - Cost: 10, - Supplier: "suppl1", - DisconnectCause: "NORMAL_CLEARNING", - } - err := cdrStats.AppendCDR(cdr, nil) - if err != nil { - t.Error("Error appending cdr to stats: ", err) - } - t.Log(cdrStats.queues) - if len(cdrStats.queues) != 5 || - len(cdrStats.queues["CDRST1"].Cdrs) != 0 || - len(cdrStats.queues["CDRST2"].Cdrs) != 1 { - t.Error("Error appending cdr to queue: ", utils.ToIJSON(cdrStats.queues)) - } -} - -func TestStatsGetValues(t *testing.T) { - cdrStats := NewStats(dm, 0) - cdr := &CDR{ - Tenant: "cgrates.org", - Category: "call", - AnswerTime: time.Now(), - SetupTime: time.Now(), - Usage: 10 * time.Second, - Cost: 10, - } - cdrStats.AppendCDR(cdr, nil) - cdr = &CDR{ - Tenant: "cgrates.org", - Category: "call", - AnswerTime: time.Now(), - SetupTime: time.Now(), - Usage: 2 * time.Second, - Cost: 4, - } - cdrStats.AppendCDR(cdr, nil) - valMap := make(map[string]float64) - if err := cdrStats.GetValues("CDRST2", &valMap); err != nil { - t.Error("Error getting metric values: ", err) - } - if len(valMap) != 2 || valMap["ACD"] != 6 || valMap["ASR"] != 100 { - t.Error("Error on metric map: ", valMap) - } -} - -func TestStatsReloadQueues(t *testing.T) { - cdrStats := NewStats(dm, 0) - cdr := &CDR{ - Tenant: "cgrates.org", - Category: "call", - AnswerTime: time.Now(), - SetupTime: time.Now(), - Usage: 10 * time.Second, - Cost: 10, - } - cdrStats.AppendCDR(cdr, nil) - if err := cdrStats.ReloadQueues(nil, nil); err != nil { - t.Error("Error reloading queues: ", err) - } - ids := []string{} - if err := cdrStats.GetQueueIds(0, &ids); err != nil { - t.Error("Error getting queue ids: ", err) - } - result := len(ids) - expected := 5 - if result != expected { - t.Errorf("Error loading stats queues. Expected %v was %v: %v", expected, result, ids) - } - valMap := make(map[string]float64) - if err := cdrStats.GetValues("CDRST2", &valMap); err != nil { - t.Error("Error getting metric values: ", err) - } - if len(valMap) != 2 || valMap["ACD"] != 10 || valMap["ASR"] != 100 { - t.Error("Error on metric map: ", valMap) - } -} - -func TestStatsReloadQueuesWithDefault(t *testing.T) { - cdrStats := NewStats(dm, 0) - cdrStats.AddQueue(&CdrStats{ - Id: utils.META_DEFAULT, - }, nil) - cdr := &CDR{ - Tenant: "cgrates.org", - Category: "call", - AnswerTime: time.Now(), - SetupTime: time.Now(), - Usage: 10 * time.Second, - Cost: 10, - } - cdrStats.AppendCDR(cdr, nil) - - if err := cdrStats.ReloadQueues(nil, nil); err != nil { - t.Error("Error reloading queues: ", err) - } - ids := []string{} - if err := cdrStats.GetQueueIds(0, &ids); err != nil { - t.Error("Error getting queue ids: ", err) - } - result := len(ids) - expected := 6 - if result != expected { - t.Errorf("Error loading stats queues. Expected %v was %v", expected, result) - } - valMap := make(map[string]float64) - if err := cdrStats.GetValues("CDRST2", &valMap); err != nil { - t.Error("Error getting metric values: ", err) - } - if len(valMap) != 2 || valMap["ACD"] != 10 || valMap["ASR"] != 100 { - t.Error("Error on metric map: ", valMap) - } -} - -func TestStatsReloadQueuesWithIds(t *testing.T) { - cdrStats := NewStats(dm, 0) - cdr := &CDR{ - Tenant: "cgrates.org", - Category: "call", - AnswerTime: time.Now(), - SetupTime: time.Now(), - Usage: 10 * time.Second, - Cost: 10, - } - cdrStats.AppendCDR(cdr, nil) - if err := cdrStats.ReloadQueues([]string{"CDRST1"}, nil); err != nil { - t.Error("Error reloading queues: ", err) - } - ids := []string{} - if err := cdrStats.GetQueueIds(0, &ids); err != nil { - t.Error("Error getting queue ids: ", err) - } - result := len(ids) - expected := 6 - if result != expected { - t.Errorf("Error loading stats queues. Expected %v was %v", expected, result) - } - valMap := make(map[string]float64) - if err := cdrStats.GetValues("CDRST2", &valMap); err != nil { - t.Error("Error getting metric values: ", err) - } - if len(valMap) != 2 || valMap["ACD"] != 10 || valMap["ASR"] != 100 { - t.Error("Error on metric map: ", valMap) - } -} - -func TestStatsSaveQueues(t *testing.T) { - cdrStats := NewStats(dm, 0) - cdr := &CDR{ - Tenant: "cgrates.org", - Category: "call", - AnswerTime: time.Now(), - SetupTime: time.Now(), - Usage: 10 * time.Second, - Cost: 10, - } - cdrStats.AppendCDR(cdr, nil) - ids := []string{} - cdrStats.GetQueueIds(0, &ids) - if _, found := cdrStats.queueSavers["CDRST1"]; !found { - t.Error("Error creating queue savers: ", cdrStats.queueSavers) - } -} - -func TestStatsResetQueues(t *testing.T) { - cdrStats := NewStats(dm, 0) - cdr := &CDR{ - Tenant: "cgrates.org", - Category: "call", - AnswerTime: time.Now(), - SetupTime: time.Now(), - Usage: 10 * time.Second, - Cost: 10, - } - cdrStats.AppendCDR(cdr, nil) - if err := cdrStats.ResetQueues(nil, nil); err != nil { - t.Error("Error reloading queues: ", err) - } - ids := []string{} - if err := cdrStats.GetQueueIds(0, &ids); err != nil { - t.Error("Error getting queue ids: ", err) - } - result := len(ids) - expected := 6 - if result != expected { - t.Errorf("Error loading stats queues. Expected %v was %v", expected, result) - } - valMap := make(map[string]float64) - if err := cdrStats.GetValues("CDRST2", &valMap); err != nil { - t.Error("Error getting metric values: ", err) - } - if len(valMap) != 2 || valMap["ACD"] != STATS_NA || valMap["ASR"] != STATS_NA { - t.Error("Error on metric map: ", valMap) - } -} - -func TestStatsResetQueuesWithIds(t *testing.T) { - cdrStats := NewStats(dm, 0) - cdr := &CDR{ - Tenant: "cgrates.org", - Category: "call", - AnswerTime: time.Now(), - SetupTime: time.Now(), - Usage: 10 * time.Second, - Cost: 10, - } - cdrStats.AppendCDR(cdr, nil) - if err := cdrStats.ResetQueues([]string{"CDRST1"}, nil); err != nil { - t.Error("Error reloading queues: ", err) - } - ids := []string{} - if err := cdrStats.GetQueueIds(0, &ids); err != nil { - t.Error("Error getting queue ids: ", err) - } - result := len(ids) - expected := 6 - if result != expected { - t.Errorf("Error loading stats queues. Expected %v was %v", expected, result) - } - valMap := make(map[string]float64) - if err := cdrStats.GetValues("CDRST2", &valMap); err != nil { - t.Error("Error getting metric values: ", err) - } - if len(valMap) != 2 || valMap["ACD"] != 10 || valMap["ASR"] != 100 { - t.Error("Error on metric map: ", valMap) - } -} - -func TestStatsSaveRestoreQeue(t *testing.T) { - sq := &CDRStatsQueue{ - conf: &CdrStats{Id: "TTT"}, - Cdrs: []*QCdr{&QCdr{Cost: 9.0}}, - } - if err := dm.SetCdrStatsQueue(sq); err != nil { - t.Error("Error saving metric: ", err) - } - recovered, err := dm.GetCdrStatsQueue(sq.GetId()) - if err != nil { - t.Error("Error loading metric: ", err) - } - if len(recovered.Cdrs) != 1 || recovered.Cdrs[0].Cost != sq.Cdrs[0].Cost { - t.Errorf("Expecting %+v got: %+v", sq.Cdrs[0], recovered.Cdrs[0]) - } -} - -func TestStatsPurgeTimeOne(t *testing.T) { - sq := NewCDRStatsQueue(&CdrStats{Metrics: []string{ASR, ACD, TCD, ACC, TCC}, TimeWindow: 30 * time.Minute}) - cdr := &CDR{ - SetupTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - AnswerTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - Usage: 10 * time.Second, - Cost: 1, - } - qcdr := sq.AppendCDR(cdr) - qcdr.EventTime = qcdr.SetupTime - s := sq.GetStats() - if s[ASR] != -1 || - s[ACD] != -1 || - s[TCD] != -1 || - s[ACC] != -1 || - s[TCC] != -1 { - t.Errorf("Error getting stats: %+v", s) - } -} - -func TestStatsPurgeTime(t *testing.T) { - sq := NewCDRStatsQueue(&CdrStats{Metrics: []string{ASR, ACD, TCD, ACC, TCC}, TimeWindow: 30 * time.Minute}) - cdr := &CDR{ - SetupTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - AnswerTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - Usage: 10 * time.Second, - Cost: 1, - } - qcdr := sq.AppendCDR(cdr) - qcdr.EventTime = qcdr.SetupTime - cdr.Cost = 2 - qcdr = sq.AppendCDR(cdr) - qcdr.EventTime = qcdr.SetupTime - cdr.Cost = 3 - qcdr = sq.AppendCDR(cdr) - qcdr.EventTime = qcdr.SetupTime - s := sq.GetStats() - if s[ASR] != -1 || - s[ACD] != -1 || - s[TCD] != -1 || - s[ACC] != -1 || - s[TCC] != -1 { - t.Errorf("Error getting stats: %+v", s) - } -} - -func TestStatsPurgeTimeFirst(t *testing.T) { - sq := NewCDRStatsQueue(&CdrStats{Metrics: []string{ASR, ACD, TCD, ACC, TCC}, TimeWindow: 30 * time.Minute}) - cdr := &CDR{ - SetupTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - AnswerTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - Usage: 10 * time.Second, - Cost: 1, - } - qcdr := sq.AppendCDR(cdr) - cdr.Cost = 2 - cdr.SetupTime = time.Date(2024, 7, 14, 14, 25, 0, 0, time.UTC) - cdr.AnswerTime = time.Date(2024, 7, 14, 14, 25, 0, 0, time.UTC) - qcdr.EventTime = qcdr.SetupTime - sq.AppendCDR(cdr) - cdr.Cost = 3 - sq.AppendCDR(cdr) - s := sq.GetStats() - if s[ASR] != 100 || - s[ACD] != 10 || - s[TCD] != 20 || - s[ACC] != 2.5 || - s[TCC] != 5 { - t.Errorf("Error getting stats: %+v", s) - } -} - -func TestStatsPurgeLength(t *testing.T) { - sq := NewCDRStatsQueue(&CdrStats{Metrics: []string{ASR, ACD, TCD, ACC, TCC}, QueueLength: 1}) - cdr := &CDR{ - SetupTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - AnswerTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - Usage: 10 * time.Second, - Cost: 1, - } - sq.AppendCDR(cdr) - cdr.Cost = 2 - sq.AppendCDR(cdr) - cdr.Cost = 3 - sq.AppendCDR(cdr) - s := sq.GetStats() - if s[ASR] != 100 || - s[ACD] != 10 || - s[TCD] != 10 || - s[ACC] != 3 || - s[TCC] != 3 { - t.Errorf("Error getting stats: %+v", s) - } -} diff --git a/engine/storage_cdrs_it_test.go b/engine/storage_cdrs_it_test.go index 4483b8eae..46b1fe5ff 100644 --- a/engine/storage_cdrs_it_test.go +++ b/engine/storage_cdrs_it_test.go @@ -90,28 +90,24 @@ func testSetCDR(cfg *config.CGRConfig) error { return err } rawCDR := &CDR{ - CGRID: utils.Sha1("testevent1", time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC).String()), - RunID: utils.MetaRaw, - OrderID: time.Now().UnixNano(), - OriginHost: "127.0.0.1", - Source: "testSetCDRs", - OriginID: "testevent1", - ToR: utils.VOICE, - RequestType: utils.META_PREPAID, - Direction: utils.OUT, - Tenant: "cgrates.org", - Category: "call", - Account: "1004", - Subject: "1004", - Destination: "1007", - SetupTime: time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC), - PDD: time.Duration(20) * time.Millisecond, - AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), - Usage: time.Duration(35) * time.Second, - Supplier: "SUPPLIER1", - DisconnectCause: "NORMAL_DISCONNECT", - ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, - Cost: -1, + CGRID: utils.Sha1("testevent1", time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC).String()), + RunID: utils.MetaRaw, + OrderID: time.Now().UnixNano(), + OriginHost: "127.0.0.1", + Source: "testSetCDRs", + OriginID: "testevent1", + ToR: utils.VOICE, + RequestType: utils.META_PREPAID, + Tenant: "cgrates.org", + Category: "call", + Account: "1004", + Subject: "1004", + Destination: "1007", + SetupTime: time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), + Usage: time.Duration(35) * time.Second, + ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, + Cost: -1, } if err := cdrStorage.SetCDR(rawCDR, false); err != nil { return fmt.Errorf("rawCDR: %+v, SetCDR err: %s", rawCDR, err.Error()) @@ -122,28 +118,24 @@ func testSetCDR(cfg *config.CGRConfig) error { return fmt.Errorf("rawCDR %+v, Unexpected number of CDRs returned: %d", rawCDR, len(cdrs)) } ratedCDR := &CDR{ - CGRID: utils.Sha1("testevent1", time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC).String()), - RunID: utils.META_DEFAULT, - OriginHost: "127.0.0.1", - Source: "testSetCDRs", - OriginID: "testevent1", - ToR: utils.VOICE, - RequestType: utils.META_PREPAID, - Direction: utils.OUT, - Tenant: "cgrates.org", - Category: "call", - Account: "1004", - Subject: "1004", - Destination: "1007", - SetupTime: time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC), - PDD: time.Duration(20) * time.Millisecond, - AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), - Usage: time.Duration(35) * time.Second, - Supplier: "SUPPLIER1", - DisconnectCause: "NORMAL_DISCONNECT", - ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, - CostSource: "testSetCDRs", - Cost: 0.17, + CGRID: utils.Sha1("testevent1", time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC).String()), + RunID: utils.META_DEFAULT, + OriginHost: "127.0.0.1", + Source: "testSetCDRs", + OriginID: "testevent1", + ToR: utils.VOICE, + RequestType: utils.META_PREPAID, + Tenant: "cgrates.org", + Category: "call", + Account: "1004", + Subject: "1004", + Destination: "1007", + SetupTime: time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), + Usage: time.Duration(35) * time.Second, + ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, + CostSource: "testSetCDRs", + Cost: 0.17, } if err := cdrStorage.SetCDR(ratedCDR, false); err != nil { return fmt.Errorf("ratedCDR: %+v, SetCDR err: %s", ratedCDR, err.Error()) @@ -253,245 +245,205 @@ func testGetCDRs(cfg *config.CGRConfig) error { } cdrs := []*CDR{ &CDR{ - CGRID: utils.Sha1("testevent1", time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC).String()), - RunID: utils.MetaRaw, - OriginHost: "127.0.0.1", - Source: "testGetCDRs", - OriginID: "testevent1", - ToR: utils.VOICE, - RequestType: utils.META_PREPAID, - Direction: utils.OUT, - Tenant: "cgrates.org", - Category: "call", - Account: "1001", - Subject: "1001", - Destination: "1002", - SetupTime: time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC), - PDD: time.Duration(20) * time.Millisecond, - AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), - Usage: time.Duration(35) * time.Second, - Supplier: "SUPPLIER1", - DisconnectCause: "NORMAL_DISCONNECT", - ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, - CostSource: "", - Cost: -1, + CGRID: utils.Sha1("testevent1", time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC).String()), + RunID: utils.MetaRaw, + OriginHost: "127.0.0.1", + Source: "testGetCDRs", + OriginID: "testevent1", + ToR: utils.VOICE, + RequestType: utils.META_PREPAID, + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "1001", + Destination: "1002", + SetupTime: time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), + Usage: time.Duration(35) * time.Second, + ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, + CostSource: "", + Cost: -1, }, &CDR{ - CGRID: utils.Sha1("testevent1", time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC).String()), - RunID: utils.META_DEFAULT, - OriginHost: "127.0.0.1", - Source: "testGetCDRs", - OriginID: "testevent1", - ToR: utils.VOICE, - RequestType: utils.META_PREPAID, - Direction: utils.OUT, - Tenant: "cgrates.org", - Category: "call", - Account: "1001", - Subject: "1001", - Destination: "1002", - SetupTime: time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC), - PDD: time.Duration(20) * time.Millisecond, - AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), - Usage: time.Duration(35) * time.Second, - Supplier: "SUPPLIER1", - DisconnectCause: "NORMAL_DISCONNECT", - ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, - CostSource: "testGetCDRs", - Cost: 0.17, + CGRID: utils.Sha1("testevent1", time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC).String()), + RunID: utils.META_DEFAULT, + OriginHost: "127.0.0.1", + Source: "testGetCDRs", + OriginID: "testevent1", + ToR: utils.VOICE, + RequestType: utils.META_PREPAID, + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "1001", + Destination: "1002", + SetupTime: time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), + Usage: time.Duration(35) * time.Second, + ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, + CostSource: "testGetCDRs", + Cost: 0.17, }, &CDR{ - CGRID: utils.Sha1("testevent1", time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC).String()), - RunID: "run2", - OriginHost: "127.0.0.1", - Source: "testGetCDRs", - OriginID: "testevent1", - ToR: utils.VOICE, - RequestType: utils.META_RATED, - Direction: utils.OUT, - Tenant: "cgrates.org", - Category: "call_derived", - Account: "1001", - Subject: "1002", - Destination: "1002", - SetupTime: time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC), - PDD: time.Duration(20) * time.Millisecond, - AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), - Usage: time.Duration(35) * time.Second, - Supplier: "SUPPLIER1", - DisconnectCause: "NORMAL_DISCONNECT", - ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, - CostSource: "testGetCDRs", - Cost: 0.17, + CGRID: utils.Sha1("testevent1", time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC).String()), + RunID: "run2", + OriginHost: "127.0.0.1", + Source: "testGetCDRs", + OriginID: "testevent1", + ToR: utils.VOICE, + RequestType: utils.META_RATED, + Tenant: "cgrates.org", + Category: "call_derived", + Account: "1001", + Subject: "1002", + Destination: "1002", + SetupTime: time.Date(2015, 12, 12, 14, 52, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), + Usage: time.Duration(35) * time.Second, + ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, + CostSource: "testGetCDRs", + Cost: 0.17, }, &CDR{ - CGRID: utils.Sha1("testevent2", time.Date(2015, 12, 29, 12, 58, 0, 0, time.UTC).String()), - RunID: utils.META_DEFAULT, - OriginHost: "192.168.1.12", - Source: "testGetCDRs", - OriginID: "testevent2", - ToR: utils.VOICE, - RequestType: utils.META_POSTPAID, - Direction: utils.OUT, - Tenant: "itsyscom.com", - Category: "call", - Account: "1004", - Subject: "1004", - Destination: "1007", - SetupTime: time.Date(2015, 12, 29, 12, 58, 0, 0, time.UTC), - PDD: time.Duration(10) * time.Millisecond, - AnswerTime: time.Date(2015, 12, 29, 12, 59, 0, 0, time.UTC), - Usage: time.Duration(0) * time.Second, - Supplier: "SUPPLIER1", - DisconnectCause: "NO_ANSWER", - ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, - CostSource: "rater1", - Cost: 0, + CGRID: utils.Sha1("testevent2", time.Date(2015, 12, 29, 12, 58, 0, 0, time.UTC).String()), + RunID: utils.META_DEFAULT, + OriginHost: "192.168.1.12", + Source: "testGetCDRs", + OriginID: "testevent2", + ToR: utils.VOICE, + RequestType: utils.META_POSTPAID, + Tenant: "itsyscom.com", + Category: "call", + Account: "1004", + Subject: "1004", + Destination: "1007", + SetupTime: time.Date(2015, 12, 29, 12, 58, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 29, 12, 59, 0, 0, time.UTC), + Usage: time.Duration(0) * time.Second, + ExtraFields: map[string]string{"ExtraHeader1": "ExtraVal1", "ExtraHeader2": "ExtraVal2"}, + CostSource: "rater1", + Cost: 0, }, &CDR{ - CGRID: utils.Sha1("testevent3", time.Date(2015, 12, 28, 12, 58, 0, 0, time.UTC).String()), - RunID: utils.MetaRaw, - OriginHost: "192.168.1.13", - Source: "testGetCDRs3", - OriginID: "testevent3", - ToR: utils.VOICE, - RequestType: utils.META_PSEUDOPREPAID, - Direction: utils.OUT, - Tenant: "cgrates.org", - Category: "call", - Account: "1002", - Subject: "1002", - Destination: "1003", - SetupTime: time.Date(2015, 12, 28, 12, 58, 0, 0, time.UTC), - PDD: time.Duration(20) * time.Millisecond, - AnswerTime: time.Date(2015, 12, 28, 12, 58, 30, 0, time.UTC), - Usage: time.Duration(125) * time.Second, - Supplier: "SUPPLIER2", - DisconnectCause: "NORMAL_DISCONNECT", - ExtraFields: map[string]string{}, - CostSource: "", - Cost: -1, + CGRID: utils.Sha1("testevent3", time.Date(2015, 12, 28, 12, 58, 0, 0, time.UTC).String()), + RunID: utils.MetaRaw, + OriginHost: "192.168.1.13", + Source: "testGetCDRs3", + OriginID: "testevent3", + ToR: utils.VOICE, + RequestType: utils.META_PSEUDOPREPAID, + Tenant: "cgrates.org", + Category: "call", + Account: "1002", + Subject: "1002", + Destination: "1003", + SetupTime: time.Date(2015, 12, 28, 12, 58, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 28, 12, 58, 30, 0, time.UTC), + Usage: time.Duration(125) * time.Second, + ExtraFields: map[string]string{}, + CostSource: "", + Cost: -1, }, &CDR{ - CGRID: utils.Sha1("testevent3", time.Date(2015, 12, 28, 12, 58, 0, 0, time.UTC).String()), - RunID: utils.META_DEFAULT, - OriginHost: "192.168.1.13", - Source: "testGetCDRs3", - OriginID: "testevent3", - ToR: utils.VOICE, - RequestType: utils.META_RATED, - Direction: utils.OUT, - Tenant: "cgrates.org", - Category: "call", - Account: "1002", - Subject: "1002", - Destination: "1003", - SetupTime: time.Date(2015, 12, 28, 12, 58, 0, 0, time.UTC), - PDD: time.Duration(20) * time.Millisecond, - AnswerTime: time.Date(2015, 12, 28, 12, 58, 30, 0, time.UTC), - Usage: time.Duration(125) * time.Second, - Supplier: "SUPPLIER2", - DisconnectCause: "NORMAL_DISCONNECT", - ExtraFields: map[string]string{}, - CostSource: "testSetCDRs", - Cost: -1, - ExtraInfo: "AccountNotFound", + CGRID: utils.Sha1("testevent3", time.Date(2015, 12, 28, 12, 58, 0, 0, time.UTC).String()), + RunID: utils.META_DEFAULT, + OriginHost: "192.168.1.13", + Source: "testGetCDRs3", + OriginID: "testevent3", + ToR: utils.VOICE, + RequestType: utils.META_RATED, + Tenant: "cgrates.org", + Category: "call", + Account: "1002", + Subject: "1002", + Destination: "1003", + SetupTime: time.Date(2015, 12, 28, 12, 58, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 28, 12, 58, 30, 0, time.UTC), + Usage: time.Duration(125) * time.Second, + ExtraFields: map[string]string{}, + CostSource: "testSetCDRs", + Cost: -1, + ExtraInfo: "AccountNotFound", }, &CDR{ - CGRID: utils.Sha1("testevent4", time.Date(2015, 12, 14, 14, 52, 0, 0, time.UTC).String()), - RunID: utils.MetaRaw, - OriginHost: "192.168.1.14", - Source: "testGetCDRs", - OriginID: "testevent4", - ToR: utils.VOICE, - RequestType: utils.META_PSEUDOPREPAID, - Direction: utils.OUT, - Tenant: "itsyscom.com", - Category: "call", - Account: "1003", - Subject: "1003", - Destination: "1007", - SetupTime: time.Date(2015, 12, 14, 14, 52, 0, 0, time.UTC), - PDD: time.Duration(2) * time.Second, - AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), - Usage: time.Duration(64) * time.Second, - Supplier: "SUPPLIER1", - DisconnectCause: "NORMAL_DISCONNECT", - ExtraFields: map[string]string{"ExtraHeader3": "ExtraVal3"}, - CostSource: "", - Cost: -1, + CGRID: utils.Sha1("testevent4", time.Date(2015, 12, 14, 14, 52, 0, 0, time.UTC).String()), + RunID: utils.MetaRaw, + OriginHost: "192.168.1.14", + Source: "testGetCDRs", + OriginID: "testevent4", + ToR: utils.VOICE, + RequestType: utils.META_PSEUDOPREPAID, + Tenant: "itsyscom.com", + Category: "call", + Account: "1003", + Subject: "1003", + Destination: "1007", + SetupTime: time.Date(2015, 12, 14, 14, 52, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), + Usage: time.Duration(64) * time.Second, + ExtraFields: map[string]string{"ExtraHeader3": "ExtraVal3"}, + CostSource: "", + Cost: -1, }, &CDR{ - CGRID: utils.Sha1("testevent4", time.Date(2015, 12, 14, 14, 52, 0, 0, time.UTC).String()), - RunID: utils.META_DEFAULT, - OriginHost: "192.168.1.14", - Source: "testGetCDRs", - OriginID: "testevent4", - ToR: utils.VOICE, - RequestType: utils.META_RATED, - Direction: utils.OUT, - Tenant: "itsyscom.com", - Category: "call", - Account: "1003", - Subject: "1003", - Destination: "1007", - SetupTime: time.Date(2015, 12, 14, 14, 52, 0, 0, time.UTC), - PDD: time.Duration(2) * time.Second, - AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), - Usage: time.Duration(64) * time.Second, - Supplier: "SUPPLIER1", - DisconnectCause: "NORMAL_DISCONNECT", - ExtraFields: map[string]string{"ExtraHeader3": "ExtraVal3"}, - CostSource: "testSetCDRs", - Cost: 1.205, + CGRID: utils.Sha1("testevent4", time.Date(2015, 12, 14, 14, 52, 0, 0, time.UTC).String()), + RunID: utils.META_DEFAULT, + OriginHost: "192.168.1.14", + Source: "testGetCDRs", + OriginID: "testevent4", + ToR: utils.VOICE, + RequestType: utils.META_RATED, + Tenant: "itsyscom.com", + Category: "call", + Account: "1003", + Subject: "1003", + Destination: "1007", + SetupTime: time.Date(2015, 12, 14, 14, 52, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 12, 14, 52, 20, 0, time.UTC), + Usage: time.Duration(64) * time.Second, + ExtraFields: map[string]string{"ExtraHeader3": "ExtraVal3"}, + CostSource: "testSetCDRs", + Cost: 1.205, }, &CDR{ - CGRID: utils.Sha1("testevent5", time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC).String()), - RunID: utils.MetaRaw, - OriginHost: "127.0.0.1", - Source: "testGetCDRs5", - OriginID: "testevent5", - ToR: utils.SMS, - RequestType: utils.META_PREPAID, - Direction: utils.OUT, - Tenant: "cgrates.org", - Category: "sms", - Account: "1001", - Subject: "1001", - Destination: "1002", - SetupTime: time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC), - PDD: time.Duration(0), - AnswerTime: time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC), - Usage: time.Duration(1) * time.Second, - Supplier: "SUPPLIER3", - DisconnectCause: "SENT_OK", - ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, - CostSource: "", - Cost: -1, + CGRID: utils.Sha1("testevent5", time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC).String()), + RunID: utils.MetaRaw, + OriginHost: "127.0.0.1", + Source: "testGetCDRs5", + OriginID: "testevent5", + ToR: utils.SMS, + RequestType: utils.META_PREPAID, + Tenant: "cgrates.org", + Category: "sms", + Account: "1001", + Subject: "1001", + Destination: "1002", + SetupTime: time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC), + Usage: time.Duration(1) * time.Second, + ExtraFields: map[string]string{"Service-Context-Id": "voice@huawei.com"}, + CostSource: "", + Cost: -1, }, &CDR{ - CGRID: utils.Sha1("testevent5", time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC).String()), - RunID: utils.META_DEFAULT, - OriginHost: "127.0.0.1", - Source: "testGetCDRs5", - OriginID: "testevent5", - ToR: utils.SMS, - RequestType: utils.META_PREPAID, - Direction: utils.OUT, - Tenant: "cgrates.org", - Category: "sms", - Account: "1001", - Subject: "1001", - Destination: "1002", - SetupTime: time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC), - PDD: time.Duration(0), - AnswerTime: time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC), - Usage: time.Duration(1) * time.Second, - Supplier: "SUPPLIER3", - DisconnectCause: "SENT_OK", - ExtraFields: map[string]string{"Service-Context-Id": "voice2@huawei.com"}, - CostSource: "rater", - Cost: 0.15, + CGRID: utils.Sha1("testevent5", time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC).String()), + RunID: utils.META_DEFAULT, + OriginHost: "127.0.0.1", + Source: "testGetCDRs5", + OriginID: "testevent5", + ToR: utils.SMS, + RequestType: utils.META_PREPAID, + Tenant: "cgrates.org", + Category: "sms", + Account: "1001", + Subject: "1001", + Destination: "1002", + SetupTime: time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC), + AnswerTime: time.Date(2015, 12, 15, 18, 22, 0, 0, time.UTC), + Usage: time.Duration(1) * time.Second, + ExtraFields: map[string]string{"Service-Context-Id": "voice2@huawei.com"}, + CostSource: "rater", + Cost: 0.15, }, } // Store all CDRs @@ -622,13 +574,6 @@ func testGetCDRs(cfg *config.CGRConfig) error { } else if len(CDRs) != 6 { return fmt.Errorf("testGetCDRs #35, unexpected number of CDRs returned: %+v", CDRs) } - - // Filter on direction - if CDRs, _, err := cdrStorage.GetCDRs(&utils.CDRsFilter{Directions: []string{utils.OUT}}, false); err != nil { - return fmt.Errorf("testGetCDRs #36 err: %v", err) - } else if len(CDRs) != 10 { - return fmt.Errorf("testGetCDRs #37, unexpected number of CDRs returned: %+v", CDRs) - } // Filter on Tenant if CDRs, _, err := cdrStorage.GetCDRs(&utils.CDRsFilter{Tenants: []string{"itsyscom.com"}}, false); err != nil { return fmt.Errorf("testGetCDRs #38 err: %v", err) @@ -709,7 +654,7 @@ func testGetCDRs(cfg *config.CGRConfig) error { return fmt.Errorf("testGetCDRs #63, unexpected number of CDRs returned: %+v", CDRs) } // Filter on MinUsage - if CDRs, _, err := cdrStorage.GetCDRs(&utils.CDRsFilter{MinUsage: "125"}, false); err != nil { + if CDRs, _, err := cdrStorage.GetCDRs(&utils.CDRsFilter{MinUsage: "125s"}, false); err != nil { return fmt.Errorf("testGetCDRs #64, err: %v", err) } else if len(CDRs) != 2 { return fmt.Errorf("testGetCDRs #65, unexpected number of CDRs returned: %d", len(CDRs)) @@ -766,24 +711,6 @@ func testGetCDRs(cfg *config.CGRConfig) error { } else if len(CDRs) != 2 { return fmt.Errorf("testGetCDRs #77, unexpected number of CDRs returned: %+v", CDRs) } - // Filter on MinPDD - if CDRs, _, err := cdrStorage.GetCDRs(&utils.CDRsFilter{MinPDD: "20ms"}, false); err != nil { - return fmt.Errorf("testGetCDRs #78, err: %v", err) - } else if len(CDRs) != 7 { - return fmt.Errorf("testGetCDRs #79, unexpected number of CDRs returned: %+v", CDRs) - } - // Filter on maxPdd - if CDRs, _, err := cdrStorage.GetCDRs(&utils.CDRsFilter{MaxPDD: "1s"}, false); err != nil { - return fmt.Errorf("testGetCDRs #80, err: %v", err) - } else if len(CDRs) != 8 { - return fmt.Errorf("testGetCDRs #81, unexpected number of CDRs returned: %+v", CDRs) - } - // Filter on minPdd, maxPdd - if CDRs, _, err := cdrStorage.GetCDRs(&utils.CDRsFilter{MinPDD: "10ms", MaxPDD: "1s"}, false); err != nil { - return fmt.Errorf("testGetCDRs #82, err: %v", err) - } else if len(CDRs) != 6 { - return fmt.Errorf("testGetCDRs #83, unexpected number of CDRs returned: %+v", CDRs) - } // Combined filter if CDRs, _, err := cdrStorage.GetCDRs(&utils.CDRsFilter{RequestTypes: []string{utils.META_RATED}, AnswerTimeStart: &timeStart, AnswerTimeEnd: &timeEnd}, false); err != nil { return fmt.Errorf("testGetCDRs #84, err: %v", err) diff --git a/engine/storage_csv.go b/engine/storage_csv.go index 5999ae89c..31284616f 100755 --- a/engine/storage_csv.go +++ b/engine/storage_csv.go @@ -706,6 +706,7 @@ func (csvs *CSVStorage) GetTPFilters(tpid, id string) ([]*utils.TPFilter, error) } func (csvs *CSVStorage) GetTPLCRProfiles(tpid, id string) ([]*utils.TPLCR, error) { + csvReader, fp, err := csvs.readerFunc(csvs.lcrProfilesFn, csvs.sep, getColumnCount(TpLCR{})) if err != nil { //log.Print("Could not load lcr profiles file: ", err) diff --git a/engine/storage_map.go b/engine/storage_map.go index 0254f1ede..f611d6571 100755 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -72,7 +72,7 @@ func (s storage) smembers(key string, ms Marshaler) (idMap utils.StringMap, ok b func NewMapStorage() (*MapStorage, error) { return &MapStorage{dict: make(map[string][]byte), ms: NewCodecMsgpackMarshaler(), - cacheCfg: config.CgrConfig().CacheConfig}, nil + cacheCfg: config.CgrConfig().CacheCfg()}, nil } func NewMapStorageJson() (mpStorage *MapStorage, err error) { diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index ac7590df1..4a7231c49 100755 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -257,7 +257,7 @@ func (ms *MongoStorage) EnsureIndexes() (err error) { Background: false, Sparse: false, } - if err = db.C(utils.TBLCDRs).EnsureIndex(idx); err != nil { + if err = db.C(utils.CDRsTBL).EnsureIndex(idx); err != nil { return } for _, idxKey := range ms.cdrsIndexes { @@ -268,7 +268,7 @@ func (ms *MongoStorage) EnsureIndexes() (err error) { Background: false, Sparse: false, } - if err = db.C(utils.TBLCDRs).EnsureIndex(idx); err != nil { + if err = db.C(utils.CDRsTBL).EnsureIndex(idx); err != nil { return } } @@ -279,7 +279,7 @@ func (ms *MongoStorage) EnsureIndexes() (err error) { Background: false, Sparse: false, } - if err = db.C(utils.TBLSMCosts).EnsureIndex(idx); err != nil { + if err = db.C(utils.SMCostsTBL).EnsureIndex(idx); err != nil { return } idx = mgo.Index{ @@ -289,7 +289,7 @@ func (ms *MongoStorage) EnsureIndexes() (err error) { Background: false, Sparse: false, } - if err = db.C(utils.TBLSMCosts).EnsureIndex(idx); err != nil { + if err = db.C(utils.SMCostsTBL).EnsureIndex(idx); err != nil { return } idx = mgo.Index{ @@ -299,7 +299,7 @@ func (ms *MongoStorage) EnsureIndexes() (err error) { Background: false, Sparse: false, } - if err = db.C(utils.TBLSMCosts).EnsureIndex(idx); err != nil { + if err = db.C(utils.SMCostsTBL).EnsureIndex(idx); err != nil { return } } diff --git a/engine/storage_mongo_stordb.go b/engine/storage_mongo_stordb.go index 81eeafad9..515a537eb 100755 --- a/engine/storage_mongo_stordb.go +++ b/engine/storage_mongo_stordb.go @@ -892,13 +892,13 @@ func (ms *MongoStorage) SetSMCost(smc *SMCost) error { if smc.CostDetails == nil { return nil } - session, col := ms.conn(utils.TBLSMCosts) + session, col := ms.conn(utils.SMCostsTBL) defer session.Close() return col.Insert(smc) } func (ms *MongoStorage) RemoveSMCost(smc *SMCost) error { - session, col := ms.conn(utils.TBLSMCosts) + session, col := ms.conn(utils.SMCostsTBL) defer session.Close() tx := col.Bulk() tx.Remove(bson.M{"cgrid": smc.CGRID, "runid": smc.RunID}, smc) @@ -921,7 +921,7 @@ func (ms *MongoStorage) GetSMCosts(cgrid, runid, originHost, originIDPrefix stri filter[OriginIDLow] = bson.M{"$regex": bson.RegEx{Pattern: fmt.Sprintf("^%s", originIDPrefix)}} } // Execute query - session, col := ms.conn(utils.TBLSMCosts) + session, col := ms.conn(utils.SMCostsTBL) defer session.Close() iter := col.Find(filter).Iter() var smCost SMCost @@ -942,7 +942,7 @@ func (ms *MongoStorage) SetCDR(cdr *CDR, allowUpdate bool) (err error) { if cdr.OrderID == 0 { cdr.OrderID = ms.cnter.Next() } - session, col := ms.conn(utils.TBLCDRs) + session, col := ms.conn(utils.CDRsTBL) defer session.Close() if allowUpdate { _, err = col.Upsert(bson.M{CGRIDLow: cdr.CGRID, RunIDLow: cdr.RunID}, cdr) @@ -984,58 +984,40 @@ func (ms *MongoStorage) cleanEmptyFilters(filters bson.M) { } } -// _, err := col(utils.TBLCDRs).UpdateAll(bson.M{CGRIDLow: bson.M{"$in": cgrIds}}, bson.M{"$set": bson.M{"deleted_at": time.Now()}}) +// _, err := col(utils.CDRsTBL).UpdateAll(bson.M{CGRIDLow: bson.M{"$in": cgrIds}}, bson.M{"$set": bson.M{"deleted_at": time.Now()}}) func (ms *MongoStorage) GetCDRs(qryFltr *utils.CDRsFilter, remove bool) ([]*CDR, int64, error) { - var minPDD, maxPDD, minUsage, maxUsage *time.Duration - if len(qryFltr.MinPDD) != 0 { - if parsed, err := utils.ParseDurationWithSecs(qryFltr.MinPDD); err != nil { - return nil, 0, err - } else { - minPDD = &parsed - } - } - if len(qryFltr.MaxPDD) != 0 { - if parsed, err := utils.ParseDurationWithSecs(qryFltr.MaxPDD); err != nil { - return nil, 0, err - } else { - maxPDD = &parsed - } - } + var minUsage, maxUsage *time.Duration if len(qryFltr.MinUsage) != 0 { - if parsed, err := utils.ParseDurationWithSecs(qryFltr.MinUsage); err != nil { + if parsed, err := utils.ParseDurationWithNanosecs(qryFltr.MinUsage); err != nil { return nil, 0, err } else { minUsage = &parsed } } if len(qryFltr.MaxUsage) != 0 { - if parsed, err := utils.ParseDurationWithSecs(qryFltr.MaxUsage); err != nil { + if parsed, err := utils.ParseDurationWithNanosecs(qryFltr.MaxUsage); err != nil { return nil, 0, err } else { maxUsage = &parsed } } filters := bson.M{ - CGRIDLow: bson.M{"$in": qryFltr.CGRIDs, "$nin": qryFltr.NotCGRIDs}, - RunIDLow: bson.M{"$in": qryFltr.RunIDs, "$nin": qryFltr.NotRunIDs}, - OrderIDLow: bson.M{"$gte": qryFltr.OrderIDStart, "$lt": qryFltr.OrderIDEnd}, - ToRLow: bson.M{"$in": qryFltr.ToRs, "$nin": qryFltr.NotToRs}, - CDRHostLow: bson.M{"$in": qryFltr.OriginHosts, "$nin": qryFltr.NotOriginHosts}, - CDRSourceLow: bson.M{"$in": qryFltr.Sources, "$nin": qryFltr.NotSources}, - RequestTypeLow: bson.M{"$in": qryFltr.RequestTypes, "$nin": qryFltr.NotRequestTypes}, - DirectionLow: bson.M{"$in": qryFltr.Directions, "$nin": qryFltr.NotDirections}, - TenantLow: bson.M{"$in": qryFltr.Tenants, "$nin": qryFltr.NotTenants}, - CategoryLow: bson.M{"$in": qryFltr.Categories, "$nin": qryFltr.NotCategories}, - AccountLow: bson.M{"$in": qryFltr.Accounts, "$nin": qryFltr.NotAccounts}, - SubjectLow: bson.M{"$in": qryFltr.Subjects, "$nin": qryFltr.NotSubjects}, - SupplierLow: bson.M{"$in": qryFltr.Suppliers, "$nin": qryFltr.NotSuppliers}, - DisconnectCauseLow: bson.M{"$in": qryFltr.DisconnectCauses, "$nin": qryFltr.NotDisconnectCauses}, - SetupTimeLow: bson.M{"$gte": qryFltr.SetupTimeStart, "$lt": qryFltr.SetupTimeEnd}, - AnswerTimeLow: bson.M{"$gte": qryFltr.AnswerTimeStart, "$lt": qryFltr.AnswerTimeEnd}, - CreatedAtLow: bson.M{"$gte": qryFltr.CreatedAtStart, "$lt": qryFltr.CreatedAtEnd}, - UpdatedAtLow: bson.M{"$gte": qryFltr.UpdatedAtStart, "$lt": qryFltr.UpdatedAtEnd}, - UsageLow: bson.M{"$gte": minUsage, "$lt": maxUsage}, - PDDLow: bson.M{"$gte": minPDD, "$lt": maxPDD}, + CGRIDLow: bson.M{"$in": qryFltr.CGRIDs, "$nin": qryFltr.NotCGRIDs}, + RunIDLow: bson.M{"$in": qryFltr.RunIDs, "$nin": qryFltr.NotRunIDs}, + OrderIDLow: bson.M{"$gte": qryFltr.OrderIDStart, "$lt": qryFltr.OrderIDEnd}, + ToRLow: bson.M{"$in": qryFltr.ToRs, "$nin": qryFltr.NotToRs}, + CDRHostLow: bson.M{"$in": qryFltr.OriginHosts, "$nin": qryFltr.NotOriginHosts}, + CDRSourceLow: bson.M{"$in": qryFltr.Sources, "$nin": qryFltr.NotSources}, + RequestTypeLow: bson.M{"$in": qryFltr.RequestTypes, "$nin": qryFltr.NotRequestTypes}, + TenantLow: bson.M{"$in": qryFltr.Tenants, "$nin": qryFltr.NotTenants}, + CategoryLow: bson.M{"$in": qryFltr.Categories, "$nin": qryFltr.NotCategories}, + AccountLow: bson.M{"$in": qryFltr.Accounts, "$nin": qryFltr.NotAccounts}, + SubjectLow: bson.M{"$in": qryFltr.Subjects, "$nin": qryFltr.NotSubjects}, + SetupTimeLow: bson.M{"$gte": qryFltr.SetupTimeStart, "$lt": qryFltr.SetupTimeEnd}, + AnswerTimeLow: bson.M{"$gte": qryFltr.AnswerTimeStart, "$lt": qryFltr.AnswerTimeEnd}, + CreatedAtLow: bson.M{"$gte": qryFltr.CreatedAtStart, "$lt": qryFltr.CreatedAtEnd}, + UpdatedAtLow: bson.M{"$gte": qryFltr.UpdatedAtStart, "$lt": qryFltr.UpdatedAtEnd}, + UsageLow: bson.M{"$gte": minUsage, "$lt": maxUsage}, //CostDetailsLow + "." + AccountLow: bson.M{"$in": qryFltr.RatedAccounts, "$nin": qryFltr.NotRatedAccounts}, //CostDetailsLow + "." + SubjectLow: bson.M{"$in": qryFltr.RatedSubjects, "$nin": qryFltr.NotRatedSubjects}, } @@ -1115,7 +1097,7 @@ func (ms *MongoStorage) GetCDRs(qryFltr *utils.CDRsFilter, remove bool) ([]*CDR, } //file.WriteString(fmt.Sprintf("AFTER: %v\n", utils.ToIJSON(filters))) //file.Close() - session, col := ms.conn(utils.TBLCDRs) + session, col := ms.conn(utils.CDRsTBL) defer session.Close() if remove { if chgd, err := col.RemoveAll(filters); err != nil { diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 936377ebc..7f0ed1258 100755 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -57,7 +57,7 @@ func (self *SQLStorage) Flush(scriptsPath string) (err error) { return err } } - if _, err := self.Db.Query(fmt.Sprintf("SELECT 1 FROM %s", utils.TBLCDRs)); err != nil { + if _, err := self.Db.Query(fmt.Sprintf("SELECT 1 FROM %s", utils.CDRsTBL)); err != nil { return err } if err := SetDBVersions(self); err != nil { @@ -107,7 +107,7 @@ func (self *SQLStorage) IsDBEmpty() (resp bool, err error) { utils.TBLTPSharedGroups, utils.TBLTPCdrStats, utils.TBLTPLcrs, utils.TBLTPActions, utils.TBLTPActionTriggers, utils.TBLTPAccountActions, utils.TBLTPDerivedChargers, utils.TBLTPUsers, utils.TBLTPAliases, utils.TBLTPResources, utils.TBLTPStats, utils.TBLTPThresholds, - utils.TBLTPFilters, utils.TBLSMCosts, utils.TBLCDRs, utils.TBLTPActionPlans, + utils.TBLTPFilters, utils.SMCostsTBL, utils.CDRsTBL, utils.TBLTPActionPlans, utils.TBLVersions, utils.TBLTPLcr, } for _, tbl := range tbls { @@ -717,20 +717,15 @@ func (self *SQLStorage) SetSMCost(smc *SMCost) error { if smc.CostDetails == nil { return nil } - tss, err := json.Marshal(smc.CostDetails) - if err != nil { - utils.Logger.Err(fmt.Sprintf("Error marshalling timespans to json: %v", err)) - return err - } tx := self.db.Begin() - cd := &TBLSMCosts{ + cd := &SMCostSQL{ Cgrid: smc.CGRID, RunID: smc.RunID, OriginHost: smc.OriginHost, OriginID: smc.OriginID, CostSource: smc.CostSource, - CostDetails: string(tss), - Usage: smc.Usage, + CostDetails: smc.CostDetails.AsJSON(), + Usage: smc.Usage.Nanoseconds(), CreatedAt: time.Now(), } if tx.Save(cd).Error != nil { // Check further since error does not properly reflect duplicates here (sql: no rows in result set) @@ -744,7 +739,7 @@ func (self *SQLStorage) SetSMCost(smc *SMCost) error { func (self *SQLStorage) RemoveSMCost(smc *SMCost) error { tx := self.db.Begin() - if err := tx.Where(&TBLSMCosts{Cgrid: smc.CGRID, RunID: smc.RunID}).Delete(SMCost{}).Error; err != nil { + if err := tx.Where(&SMCostSQL{Cgrid: smc.CGRID, RunID: smc.RunID}).Delete(SMCost{}).Error; err != nil { tx.Rollback() return err } @@ -755,7 +750,7 @@ func (self *SQLStorage) RemoveSMCost(smc *SMCost) error { // GetSMCosts is used to retrieve one or multiple SMCosts based on filter func (self *SQLStorage) GetSMCosts(cgrid, runid, originHost, originIDPrefix string) ([]*SMCost, error) { var smCosts []*SMCost - filter := &TBLSMCosts{} + filter := &SMCostSQL{} if cgrid != "" { filter.Cgrid = cgrid } @@ -769,7 +764,7 @@ func (self *SQLStorage) GetSMCosts(cgrid, runid, originHost, originIDPrefix stri if originIDPrefix != "" { q = self.db.Where(filter).Where(fmt.Sprintf("origin_id LIKE '%s%%'", originIDPrefix)) } - results := make([]*TBLSMCosts, 0) + results := make([]*SMCostSQL, 0) if err := q.Find(&results).Error; err != nil { return nil, err } @@ -783,7 +778,7 @@ func (self *SQLStorage) GetSMCosts(cgrid, runid, originHost, originIDPrefix stri OriginHost: result.OriginHost, OriginID: result.OriginID, CostSource: result.CostSource, - Usage: result.Usage, + Usage: time.Duration(result.Usage), CostDetails: &CallCost{}, } if err := json.Unmarshal([]byte(result.CostDetails), smc.CostDetails); err != nil { @@ -805,73 +800,19 @@ func (self *SQLStorage) LogActionTiming(source string, at *ActionTiming, as Acti } func (self *SQLStorage) SetCDR(cdr *CDR, allowUpdate bool) error { - extraFields, err := json.Marshal(cdr.ExtraFields) - if err != nil { - return err - } tx := self.db.Begin() - saved := tx.Save(&TBLCDRs{ - Cgrid: cdr.CGRID, - RunID: cdr.RunID, - OriginHost: cdr.OriginHost, - Source: cdr.Source, - OriginID: cdr.OriginID, - Tor: cdr.ToR, - RequestType: cdr.RequestType, - Direction: cdr.Direction, - Tenant: cdr.Tenant, - Category: cdr.Category, - Account: cdr.Account, - Subject: cdr.Subject, - Destination: cdr.Destination, - SetupTime: cdr.SetupTime, - Pdd: cdr.PDD.Seconds(), - AnswerTime: cdr.AnswerTime, - Usage: cdr.Usage.Seconds(), - Supplier: cdr.Supplier, - DisconnectCause: cdr.DisconnectCause, - ExtraFields: string(extraFields), - CostSource: cdr.CostSource, - Cost: cdr.Cost, - CostDetails: cdr.CostDetailsJson(), - AccountSummary: utils.ToJSON(cdr.AccountSummary), - ExtraInfo: cdr.ExtraInfo, - CreatedAt: time.Now(), - }) + cdrSql := cdr.AsCDRsql() + cdrSql.CreatedAt = time.Now() + saved := tx.Save(cdrSql) if saved.Error != nil { tx.Rollback() if !allowUpdate { return saved.Error } tx = self.db.Begin() - updated := tx.Model(&TBLCDRs{}).Where(&TBLCDRs{Cgrid: cdr.CGRID, RunID: cdr.RunID, OriginID: cdr.OriginID}).Updates( - TBLCDRs{ - OriginHost: cdr.OriginHost, - Source: cdr.Source, - OriginID: cdr.OriginID, - Tor: cdr.ToR, - RequestType: cdr.RequestType, - Direction: cdr.Direction, - Tenant: cdr.Tenant, - Category: cdr.Category, - Account: cdr.Account, - Subject: cdr.Subject, - Destination: cdr.Destination, - SetupTime: cdr.SetupTime, - Pdd: cdr.PDD.Seconds(), - AnswerTime: cdr.AnswerTime, - Usage: cdr.Usage.Seconds(), - Supplier: cdr.Supplier, - DisconnectCause: cdr.DisconnectCause, - ExtraFields: string(extraFields), - CostSource: cdr.CostSource, - Cost: cdr.Cost, - CostDetails: cdr.CostDetailsJson(), - AccountSummary: utils.ToJSON(cdr.AccountSummary), - ExtraInfo: cdr.ExtraInfo, - UpdatedAt: time.Now(), - }, - ) + cdrSql.UpdatedAt = time.Now() + updated := tx.Model(&CDRsql{}).Where( + &CDRsql{Cgrid: cdr.CGRID, RunID: cdr.RunID, OriginID: cdr.OriginID}).Updates(cdrSql) if updated.Error != nil { tx.Rollback() return updated.Error @@ -885,7 +826,7 @@ func (self *SQLStorage) SetCDR(cdr *CDR, allowUpdate bool) error { // qryFltr.Unscoped will ignore soft deletes or delete records permanently func (self *SQLStorage) GetCDRs(qryFltr *utils.CDRsFilter, remove bool) ([]*CDR, int64, error) { var cdrs []*CDR - q := self.db.Table(utils.TBLCDRs).Select("*") + q := self.db.Table(utils.CDRsTBL).Select("*") if qryFltr.Unscoped { q = q.Unscoped() } @@ -926,12 +867,6 @@ func (self *SQLStorage) GetCDRs(qryFltr *utils.CDRsFilter, remove bool) ([]*CDR, if len(qryFltr.NotRequestTypes) != 0 { q = q.Where("request_type not in (?)", qryFltr.NotRequestTypes) } - if len(qryFltr.Directions) != 0 { - q = q.Where("direction in (?)", qryFltr.Directions) - } - if len(qryFltr.NotDirections) != 0 { - q = q.Where("direction not in (?)", qryFltr.NotDirections) - } if len(qryFltr.Tenants) != 0 { q = q.Where("tenant in (?)", qryFltr.Tenants) } @@ -978,23 +913,11 @@ func (self *SQLStorage) GetCDRs(qryFltr *utils.CDRsFilter, remove bool) ([]*CDR, qIds.WriteString(" )") q = q.Where(qIds.String()) } - if len(qryFltr.Suppliers) != 0 { - q = q.Where("supplier in (?)", qryFltr.Subjects) - } - if len(qryFltr.NotSuppliers) != 0 { - q = q.Where("supplier not in (?)", qryFltr.NotSubjects) - } - if len(qryFltr.DisconnectCauses) != 0 { - q = q.Where("disconnect_cause in (?)", qryFltr.DisconnectCauses) - } - if len(qryFltr.NotDisconnectCauses) != 0 { - q = q.Where("disconnect_cause not in (?)", qryFltr.NotDisconnectCauses) - } if len(qryFltr.Costs) != 0 { - q = q.Where(utils.TBLCDRs+".cost in (?)", qryFltr.Costs) + q = q.Where(utils.CDRsTBL+".cost in (?)", qryFltr.Costs) } if len(qryFltr.NotCosts) != 0 { - q = q.Where(utils.TBLCDRs+".cost not in (?)", qryFltr.NotCosts) + q = q.Where(utils.CDRsTBL+".cost not in (?)", qryFltr.NotCosts) } if len(qryFltr.ExtraFields) != 0 { // Extra fields searches, implemented as contains in extra field qIds := bytes.NewBufferString("(") @@ -1031,10 +954,10 @@ func (self *SQLStorage) GetCDRs(qryFltr *utils.CDRsFilter, remove bool) ([]*CDR, q = q.Where(qIds.String()) } if qryFltr.OrderIDStart != nil { // Keep backwards compatible by testing 0 value - q = q.Where(utils.TBLCDRs+".id >= ?", *qryFltr.OrderIDStart) + q = q.Where(utils.CDRsTBL+".id >= ?", *qryFltr.OrderIDStart) } if qryFltr.OrderIDEnd != nil { - q = q.Where(utils.TBLCDRs+".id < ?", *qryFltr.OrderIDEnd) + q = q.Where(utils.CDRsTBL+".id < ?", *qryFltr.OrderIDEnd) } if qryFltr.SetupTimeStart != nil { q = q.Where("setup_time >= ?", qryFltr.SetupTimeStart) @@ -1061,44 +984,28 @@ func (self *SQLStorage) GetCDRs(qryFltr *utils.CDRsFilter, remove bool) ([]*CDR, q = q.Where("updated_at < ?", qryFltr.UpdatedAtEnd) } if len(qryFltr.MinUsage) != 0 { - if minUsage, err := utils.ParseDurationWithSecs(qryFltr.MinUsage); err != nil { + minUsage, err := utils.ParseDurationWithNanosecs(qryFltr.MinUsage) + if err != nil { return nil, 0, err + } + if self.db.Dialect().GetName() == utils.MYSQL { // MySQL needs escaping for usage + q = q.Where("`usage` >= ?", minUsage.Nanoseconds()) } else { - if self.db.Dialect().GetName() == utils.MYSQL { // MySQL needs escaping for usage - q = q.Where("`usage` >= ?", minUsage.Seconds()) - } else { - q = q.Where("usage >= ?", minUsage.Seconds()) - } + q = q.Where("usage >= ?", minUsage.Nanoseconds()) } } if len(qryFltr.MaxUsage) != 0 { - if maxUsage, err := utils.ParseDurationWithSecs(qryFltr.MaxUsage); err != nil { + maxUsage, err := utils.ParseDurationWithNanosecs(qryFltr.MaxUsage) + if err != nil { return nil, 0, err + } + if self.db.Dialect().GetName() == utils.MYSQL { // MySQL needs escaping for usage + q = q.Where("`usage` < ?", maxUsage.Nanoseconds()) } else { - if self.db.Dialect().GetName() == utils.MYSQL { // MySQL needs escaping for usage - q = q.Where("`usage` < ?", maxUsage.Seconds()) - } else { - q = q.Where("usage < ?", maxUsage.Seconds()) - } + q = q.Where("usage < ?", maxUsage.Nanoseconds()) } } - if len(qryFltr.MinPDD) != 0 { - if minPDD, err := utils.ParseDurationWithSecs(qryFltr.MinPDD); err != nil { - return nil, 0, err - } else { - q = q.Where("pdd >= ?", minPDD.Seconds()) - } - - } - if len(qryFltr.MaxPDD) != 0 { - if maxPDD, err := utils.ParseDurationWithSecs(qryFltr.MaxPDD); err != nil { - return nil, 0, err - } else { - q = q.Where("pdd < ?", maxPDD.Seconds()) - } - } - if qryFltr.MinCost != nil { if qryFltr.MaxCost == nil { q = q.Where("cost >= ?", *qryFltr.MinCost) @@ -1136,58 +1043,16 @@ func (self *SQLStorage) GetCDRs(qryFltr *utils.CDRsFilter, remove bool) ([]*CDR, return nil, cnt, nil } // Execute query - results := make([]*TBLCDRs, 0) + results := make([]*CDRsql, 0) if err := q.Find(&results).Error; err != nil { return nil, 0, err } for _, result := range results { - extraFieldsMp := make(map[string]string) - if result.ExtraFields != "" { - if err := json.Unmarshal([]byte(result.ExtraFields), &extraFieldsMp); err != nil { - return nil, 0, fmt.Errorf("JSON unmarshal error for cgrid: %s, runid: %v, error: %s", result.Cgrid, result.RunID, err.Error()) - } + if cdr, err := NewCDRFromSQL(result); err != nil { + return nil, 0, err + } else { + cdrs = append(cdrs, cdr) } - var callCost CallCost - if result.CostDetails != "" { - if err := json.Unmarshal([]byte(result.CostDetails), &callCost); err != nil { - return nil, 0, fmt.Errorf("JSON unmarshal callcost error for cgrid: %s, runid: %v, error: %s", result.Cgrid, result.RunID, err.Error()) - } - } - acntSummary, err := NewAccountSummaryFromJSON(result.AccountSummary) - if err != nil { - return nil, 0, fmt.Errorf("JSON unmarshal account summary error for cgrid: %s, runid: %v, error: %s", result.Cgrid, result.RunID, err.Error()) - } - usageDur := time.Duration(result.Usage * utils.NANO_MULTIPLIER) - pddDur := time.Duration(result.Pdd * utils.NANO_MULTIPLIER) - storCdr := &CDR{ - CGRID: result.Cgrid, - RunID: result.RunID, - OrderID: result.ID, - OriginHost: result.OriginHost, - Source: result.Source, - OriginID: result.OriginID, - ToR: result.Tor, - RequestType: result.RequestType, - Direction: result.Direction, - Tenant: result.Tenant, - Category: result.Category, - Account: result.Account, - Subject: result.Subject, - Destination: result.Destination, - SetupTime: result.SetupTime, - PDD: pddDur, - AnswerTime: result.AnswerTime, - Usage: usageDur, - Supplier: result.Supplier, - DisconnectCause: result.DisconnectCause, - ExtraFields: extraFieldsMp, - CostSource: result.CostSource, - Cost: result.Cost, - CostDetails: &callCost, - AccountSummary: acntSummary, - ExtraInfo: result.ExtraInfo, - } - cdrs = append(cdrs, storCdr) } if len(cdrs) == 0 && !remove { return cdrs, 0, utils.ErrNotFound diff --git a/engine/storage_utils.go b/engine/storage_utils.go index a287c8472..ef411d85d 100755 --- a/engine/storage_utils.go +++ b/engine/storage_utils.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "strconv" + "time" "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" @@ -121,7 +122,7 @@ type SMCost struct { OriginHost string OriginID string CostSource string - Usage float64 + Usage time.Duration CostDetails *CallCost } @@ -141,6 +142,6 @@ type V2SMCost struct { OriginHost string OriginID string CostSource string - Usage float64 + Usage time.Duration CostDetails *EventCost } diff --git a/engine/stordb_it_test.go b/engine/stordb_it_test.go index 82fada23d..3d600a3bc 100755 --- a/engine/stordb_it_test.go +++ b/engine/stordb_it_test.go @@ -1677,9 +1677,6 @@ func testStorDBitCRUDCDRs(t *testing.T) { if !(reflect.DeepEqual(snd[0].RequestType, rcv[0].RequestType) || reflect.DeepEqual(snd[0].RequestType, rcv[1].RequestType)) { t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].RequestType, rcv[0].RequestType, rcv[1].RequestType) } - if !(reflect.DeepEqual(snd[0].Direction, rcv[0].Direction) || reflect.DeepEqual(snd[0].Direction, rcv[1].Direction)) { - t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].Direction, rcv[0].Direction, rcv[1].Direction) - } if !(reflect.DeepEqual(snd[0].Tenant, rcv[0].Tenant) || reflect.DeepEqual(snd[0].Tenant, rcv[1].Tenant)) { t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].Tenant, rcv[0].Tenant, rcv[1].Tenant) } @@ -1698,21 +1695,12 @@ func testStorDBitCRUDCDRs(t *testing.T) { if !(snd[0].SetupTime.Equal(rcv[0].SetupTime) || snd[0].SetupTime.Equal(rcv[1].SetupTime)) { t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].SetupTime, rcv[0].SetupTime, rcv[1].SetupTime) } - if !(reflect.DeepEqual(snd[0].PDD, rcv[0].PDD) || reflect.DeepEqual(snd[0].PDD, rcv[1].PDD)) { - t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].PDD, rcv[0].PDD, rcv[1].PDD) - } if !(snd[0].AnswerTime.Equal(rcv[0].AnswerTime) || snd[0].AnswerTime.Equal(rcv[1].AnswerTime)) { t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].AnswerTime, rcv[0].AnswerTime, rcv[1].AnswerTime) } if !(reflect.DeepEqual(snd[0].Usage, rcv[0].Usage) || reflect.DeepEqual(snd[0].Usage, rcv[1].Usage)) { t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].Usage, rcv[0].Usage, rcv[1].Usage) } - if !(reflect.DeepEqual(snd[0].Supplier, rcv[0].Supplier) || reflect.DeepEqual(snd[0].Supplier, rcv[1].Supplier)) { - t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].Supplier, rcv[0].Supplier, rcv[1].Supplier) - } - if !(reflect.DeepEqual(snd[0].DisconnectCause, rcv[0].DisconnectCause) || reflect.DeepEqual(snd[0].DisconnectCause, rcv[1].DisconnectCause)) { - t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].DisconnectCause, rcv[0].DisconnectCause, rcv[1].DisconnectCause) - } if !(reflect.DeepEqual(snd[0].ExtraFields, rcv[0].ExtraFields) || reflect.DeepEqual(snd[0].ExtraFields, rcv[1].ExtraFields)) { t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].ExtraFields, rcv[0].ExtraFields, rcv[1].ExtraFields) } @@ -1725,9 +1713,6 @@ func testStorDBitCRUDCDRs(t *testing.T) { if !(reflect.DeepEqual(snd[0].CostDetails, rcv[0].CostDetails) || reflect.DeepEqual(snd[0].CostDetails, rcv[1].CostDetails)) { t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].CostDetails, rcv[0].CostDetails, rcv[1].CostDetails) } - if !(reflect.DeepEqual(snd[0].AccountSummary, rcv[0].AccountSummary) || reflect.DeepEqual(snd[0].AccountSummary, rcv[1].AccountSummary)) { - t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].AccountSummary, rcv[0].AccountSummary, rcv[1].AccountSummary) - } if !(reflect.DeepEqual(snd[0].ExtraInfo, rcv[0].ExtraInfo) || reflect.DeepEqual(snd[0].ExtraInfo, rcv[1].ExtraInfo)) { t.Errorf("Expecting: %+v, received: %+v || %+v", snd[0].ExtraInfo, rcv[0].ExtraInfo, rcv[1].ExtraInfo) } diff --git a/engine/suretax_test.go b/engine/suretax_test.go index 63c1cb404..16d554ed5 100644 --- a/engine/suretax_test.go +++ b/engine/suretax_test.go @@ -30,11 +30,16 @@ import ( func TestNewSureTaxRequest(t *testing.T) { CGRID := utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()) cdr := &CDR{CGRID: CGRID, 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: "1002", Supplier: "SUPPL1", - SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), RunID: utils.DEFAULT_RUNID, - Usage: time.Duration(12) * time.Second, PDD: time.Duration(7) * time.Second, - ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, Rated: true, + OriginID: "dsafdsaf", OriginHost: "192.168.1.1", + Source: utils.UNIT_TEST, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", + Subject: "1001", Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + RunID: utils.DEFAULT_RUNID, + Usage: time.Duration(12) * time.Second, + ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Cost: 1.01, Rated: true, } cfg, _ := config.NewDefaultCGRConfig() stCfg := cfg.SureTaxCfg() diff --git a/engine/tp_reader.go b/engine/tp_reader.go index 7f0f2d559..b97bf4069 100755 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -599,7 +599,7 @@ func (tpr *TpReader) LoadActions() (err error) { } if tpact.Units != "" && tpact.Units != utils.ANY { - vf, err := utils.ParseBalanceFilterValue(tpact.Units) + vf, err := utils.ParseBalanceFilterValue(tpact.BalanceType, tpact.Units) if err != nil { return err } @@ -738,7 +738,7 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { if err != nil { return err } - minSleep, err := utils.ParseDurationWithSecs(atr.MinSleep) + minSleep, err := utils.ParseDurationWithNanosecs(atr.MinSleep) if err != nil { return err } @@ -937,7 +937,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions) for key, atrsLst := range atrs { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, atr := range atrsLst { - minSleep, _ := utils.ParseDurationWithSecs(atr.MinSleep) + minSleep, _ := utils.ParseDurationWithNanosecs(atr.MinSleep) expTime, _ := utils.ParseTimeDetectLayout(atr.ExpirationDate, tpr.timezone) actTime, _ := utils.ParseTimeDetectLayout(atr.ActivationDate, tpr.timezone) if atr.UniqueID == "" { @@ -1061,7 +1061,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions) } if tpact.Units != "" && tpact.Units != utils.ANY { - vf, err := utils.ParseBalanceFilterValue(tpact.Units) + vf, err := utils.ParseBalanceFilterValue(tpact.BalanceType, tpact.Units) if err != nil { return err } @@ -1271,7 +1271,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { for _, atrsLst := range atrsM { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, atr := range atrsLst { - minSleep, _ := utils.ParseDurationWithSecs(atr.MinSleep) + minSleep, _ := utils.ParseDurationWithNanosecs(atr.MinSleep) expTime, _ := utils.ParseTimeDetectLayout(atr.ExpirationDate, tpr.timezone) actTime, _ := utils.ParseTimeDetectLayout(atr.ActivationDate, tpr.timezone) if atr.UniqueID == "" { @@ -1404,7 +1404,7 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { } if tpact.Units != "" && tpact.Units != utils.ANY { - vf, err := utils.ParseBalanceFilterValue(tpact.Units) + vf, err := utils.ParseBalanceFilterValue(tpact.BalanceType, tpact.Units) if err != nil { return err } diff --git a/engine/users_test.go b/engine/users_test.go index 6983bb7a0..ca51f5261 100644 --- a/engine/users_test.go +++ b/engine/users_test.go @@ -610,7 +610,6 @@ func TestUsersUsageRecordGetLoadUserProfile(t *testing.T) { ur := &UsageRecord{ ToR: utils.USERS, RequestType: utils.USERS, - Direction: "*out", Tenant: "", Category: "call", Account: utils.USERS, @@ -628,7 +627,6 @@ func TestUsersUsageRecordGetLoadUserProfile(t *testing.T) { expected := &UsageRecord{ ToR: "04", RequestType: "4", - Direction: "*out", Tenant: "", Category: "call", Account: "rif", @@ -657,7 +655,6 @@ func TestUsersExternalCDRGetLoadUserProfileExtraFields(t *testing.T) { ur := &ExternalCDR{ ToR: utils.USERS, RequestType: utils.USERS, - Direction: "*out", Tenant: "", Category: "call", Account: utils.USERS, @@ -678,7 +675,6 @@ func TestUsersExternalCDRGetLoadUserProfileExtraFields(t *testing.T) { expected := &ExternalCDR{ ToR: "04", RequestType: "4", - Direction: "*out", Tenant: "", Category: "call", Account: "rif", @@ -710,7 +706,6 @@ func TestUsersExternalCDRGetLoadUserProfileExtraFieldsNotFound(t *testing.T) { ur := &ExternalCDR{ ToR: utils.USERS, RequestType: utils.USERS, - Direction: "*out", Tenant: "", Category: "call", Account: utils.USERS, @@ -744,7 +739,6 @@ func TestUsersExternalCDRGetLoadUserProfileExtraFieldsSet(t *testing.T) { ur := &ExternalCDR{ ToR: utils.USERS, RequestType: utils.USERS, - Direction: "*out", Tenant: "", Category: "call", Account: utils.USERS, @@ -766,7 +760,6 @@ func TestUsersExternalCDRGetLoadUserProfileExtraFieldsSet(t *testing.T) { expected := &ExternalCDR{ ToR: "04", RequestType: "4", - Direction: "*out", Tenant: "", Category: "call", Account: "rif", diff --git a/engine/version.go b/engine/version.go index b17a29228..b0959e32c 100644 --- a/engine/version.go +++ b/engine/version.go @@ -24,6 +24,9 @@ import ( "github.com/cgrates/cgrates/utils" ) +// Versions will keep trac of various item versions +type Versions map[string]int64 // map[item]versionNr + func CheckVersions(storage Storage) error { // get current db version storType := storage.GetStorageType() @@ -104,29 +107,6 @@ func (vers Versions) Compare(curent Versions, storType string) string { return "" } -func CurrentDBVersions(storType string) Versions { - dataDbVersions := CurrentDataDBVersions() - storDbVersions := CurrentStorDBVersions() - - allVersions := make(Versions) - for k, v := range dataDbVersions { - allVersions[k] = v - } - for k, v := range storDbVersions { - allVersions[k] = v - } - - switch storType { - case utils.MONGO, utils.MAPSTOR: - return allVersions - case utils.POSTGRES, utils.MYSQL: - return storDbVersions - case utils.REDIS: - return dataDbVersions - } - return nil -} - func CurrentDataDBVersions() Versions { return Versions{ utils.StatS: 2, @@ -184,5 +164,25 @@ func CurrentStorDBVersions() Versions { } } -// Versions will keep trac of various item versions -type Versions map[string]int64 // map[item]versionNr +func CurrentDBVersions(storType string) Versions { + dataDbVersions := CurrentDataDBVersions() + storDbVersions := CurrentStorDBVersions() + + allVersions := make(Versions) + for k, v := range dataDbVersions { + allVersions[k] = v + } + for k, v := range storDbVersions { + allVersions[k] = v + } + + switch storType { + case utils.MONGO, utils.MAPSTOR: + return allVersions + case utils.POSTGRES, utils.MYSQL: + return storDbVersions + case utils.REDIS: + return dataDbVersions + } + return nil +} diff --git a/engine/versions_it_test.go b/engine/versions_it_test.go index 8141aa64d..2d1eeecb5 100644 --- a/engine/versions_it_test.go +++ b/engine/versions_it_test.go @@ -46,12 +46,15 @@ func TestVersionsITMongo(t *testing.T) { if cfg, err = config.NewCGRConfigFromFolder(path.Join(*dataDir, "conf", "samples", "tutmongo")); err != nil { t.Fatal(err) } - if dm3, err = ConfigureDataStorage(cfg.DataDbType, cfg.DataDbHost, cfg.DataDbPort, cfg.DataDbName, cfg.DataDbUser, cfg.DataDbPass, - cfg.DBDataEncoding, cfg.CacheConfig, *loadHistorySize); err != nil { + if dm3, err = ConfigureDataStorage(cfg.DataDbType, cfg.DataDbHost, + cfg.DataDbPort, cfg.DataDbName, cfg.DataDbUser, cfg.DataDbPass, + cfg.DBDataEncoding, cfg.CacheCfg(), *loadHistorySize); err != nil { log.Fatal(err) } - storageDb, err = ConfigureStorStorage(cfg.StorDBType, cfg.StorDBHost, cfg.StorDBPort, cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass, cfg.DBDataEncoding, - config.CgrConfig().StorDBMaxOpenConns, config.CgrConfig().StorDBMaxIdleConns, config.CgrConfig().StorDBConnMaxLifetime, config.CgrConfig().StorDBCDRSIndexes) + storageDb, err = ConfigureStorStorage(cfg.StorDBType, cfg.StorDBHost, + cfg.StorDBPort, cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass, cfg.DBDataEncoding, + config.CgrConfig().StorDBMaxOpenConns, config.CgrConfig().StorDBMaxIdleConns, + config.CgrConfig().StorDBConnMaxLifetime, config.CgrConfig().StorDBCDRSIndexes) if err != nil { log.Fatal(err) } @@ -66,13 +69,16 @@ func TestVersionsITRedisMYSQL(t *testing.T) { if cfg, err = config.NewCGRConfigFromFolder(path.Join(*dataDir, "conf", "samples", "tutmysql")); err != nil { t.Fatal(err) } - dm3, err = ConfigureDataStorage(cfg.DataDbType, cfg.DataDbHost, cfg.DataDbPort, cfg.DataDbName, cfg.DataDbUser, cfg.DataDbPass, cfg.DBDataEncoding, cfg.CacheConfig, *loadHistorySize) + dm3, err = ConfigureDataStorage(cfg.DataDbType, cfg.DataDbHost, cfg.DataDbPort, + cfg.DataDbName, cfg.DataDbUser, cfg.DataDbPass, cfg.DBDataEncoding, cfg.CacheCfg(), *loadHistorySize) if err != nil { log.Fatal(err) } - storageDb, err = ConfigureStorStorage(cfg.StorDBType, cfg.StorDBHost, cfg.StorDBPort, cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass, cfg.DBDataEncoding, - config.CgrConfig().StorDBMaxOpenConns, config.CgrConfig().StorDBMaxIdleConns, config.CgrConfig().StorDBConnMaxLifetime, config.CgrConfig().StorDBCDRSIndexes) + storageDb, err = ConfigureStorStorage(cfg.StorDBType, cfg.StorDBHost, cfg.StorDBPort, + cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass, cfg.DBDataEncoding, + config.CgrConfig().StorDBMaxOpenConns, config.CgrConfig().StorDBMaxIdleConns, + config.CgrConfig().StorDBConnMaxLifetime, config.CgrConfig().StorDBCDRSIndexes) if err != nil { log.Fatal(err) } @@ -87,12 +93,15 @@ func TestVersionsITRedisPostgres(t *testing.T) { if cfg, err = config.NewCGRConfigFromFolder(path.Join(*dataDir, "conf", "samples", "storage", "postgres")); err != nil { t.Fatal(err) } - dm3, err = ConfigureDataStorage(cfg.DataDbType, cfg.DataDbHost, cfg.DataDbPort, cfg.DataDbName, cfg.DataDbUser, cfg.DataDbPass, cfg.DBDataEncoding, cfg.CacheConfig, *loadHistorySize) + dm3, err = ConfigureDataStorage(cfg.DataDbType, cfg.DataDbHost, cfg.DataDbPort, + cfg.DataDbName, cfg.DataDbUser, cfg.DataDbPass, cfg.DBDataEncoding, cfg.CacheCfg(), *loadHistorySize) if err != nil { log.Fatal(err) } - storageDb, err = ConfigureStorStorage(cfg.StorDBType, cfg.StorDBHost, cfg.StorDBPort, cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass, cfg.DBDataEncoding, - config.CgrConfig().StorDBMaxOpenConns, config.CgrConfig().StorDBMaxIdleConns, config.CgrConfig().StorDBConnMaxLifetime, config.CgrConfig().StorDBCDRSIndexes) + storageDb, err = ConfigureStorStorage(cfg.StorDBType, cfg.StorDBHost, + cfg.StorDBPort, cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass, cfg.DBDataEncoding, + config.CgrConfig().StorDBMaxOpenConns, config.CgrConfig().StorDBMaxIdleConns, + config.CgrConfig().StorDBConnMaxLifetime, config.CgrConfig().StorDBCDRSIndexes) if err != nil { log.Fatal(err) } @@ -132,7 +141,6 @@ func testVersion(t *testing.T) { switch storType { case utils.MONGO, utils.MAPSTOR: currentVersion = allVersions - testVersion = allVersions testVersion[utils.Accounts] = 1 test = "Migration needed: please backup cgr data and run : " diff --git a/general_tests/a1_it_test.go b/general_tests/a1_it_test.go index b81a89827..4f9960e73 100644 --- a/general_tests/a1_it_test.go +++ b/general_tests/a1_it_test.go @@ -86,8 +86,8 @@ func TestA1itLoadTPFromFolder(t *testing.T) { t.Error(reply) } time.Sleep(time.Duration(100 * time.Millisecond)) - tStart, _ := utils.ParseDate("2017-03-03T10:39:33Z") - tEnd, _ := utils.ParseDate("2017-03-03T12:30:13Z") // Equivalent of 10240 which is a chunk of data charged + tStart := time.Date(2017, 3, 3, 10, 39, 33, 0, time.UTC) + tEnd := time.Date(2017, 3, 3, 10, 39, 33, 10240, time.UTC) cd := engine.CallDescriptor{ Direction: "*out", Category: "data1", @@ -100,7 +100,7 @@ func TestA1itLoadTPFromFolder(t *testing.T) { var cc engine.CallCost if err := a1rpc.Call("Responder.GetCost", cd, &cc); err != nil { t.Error("Got error on Responder.GetCost: ", err.Error()) - } else if cc.Cost != 0.0 || cc.RatedUsage != 10240 { + } else if cc.Cost != 0.0 { t.Errorf("Calling Responder.GetCost got callcost: %v", cc) } } @@ -146,7 +146,7 @@ func TestA1itDataSession1(t *testing.T) { utils.SessionTTLUsage: "0s", } var maxUsage float64 - if err := a1rpc.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + if err := a1rpc.Call(utils.SMGenericV2InitiateSession, smgEv, &maxUsage); err != nil { t.Error(err) } else if maxUsage != 10240 { t.Error("Received: ", maxUsage) @@ -171,7 +171,8 @@ func TestA1itDataSession1(t *testing.T) { utils.ANSWER_TIME: "2017-03-03 11:39:32 +0100 CET", utils.USAGE: "2097152", } - if err := a1rpc.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := a1rpc.Call(utils.SMGenericV2UpdateSession, + smgEv, &maxUsage); err != nil { t.Error(err) } else if maxUsage != 2097152 { t.Error("Bad max usage: ", maxUsage) @@ -217,7 +218,7 @@ func TestA1itDataSession1(t *testing.T) { t.Error(err) } if len(cc.Timespans) != 3 { - t.Errorf("Unexpected number of timespans: %+v", cc.Timespans) + t.Errorf("Unexpected number of timespans: %+v", len(cc.Timespans)) } if cc.RatedUsage != 2202800 { t.Errorf("RatingUsage expected: %f received %f, callcost: %+v ", 2202800.0, cc.RatedUsage, cc) diff --git a/general_tests/acntacts_test.go b/general_tests/acntacts_test.go index 6b45644d5..eaa6ce72f 100644 --- a/general_tests/acntacts_test.go +++ b/general_tests/acntacts_test.go @@ -43,7 +43,7 @@ func TestAcntActsLoadCsv(t *testing.T) { ratingProfiles := `` sharedGroups := `` lcrs := `` - actions := `TOPUP10_AC,*topup_reset,,,,*voice,*out,,*any,,,*unlimited,,10,10,false,false,10 + actions := `TOPUP10_AC,*topup_reset,,,,*voice,*out,,*any,,,*unlimited,,10s,10,false,false,10 DISABLE_ACNT,*disable_account,,,,,,,,,,,,,,false,false,10 ENABLE_ACNT,*enable_account,,,,,,,,,,,,,,false,false,10` actionPlans := `TOPUP10_AT,TOPUP10_AC,ASAP,10` diff --git a/general_tests/auth_test.go b/general_tests/auth_test.go index ee0f14524..c73de55f7 100644 --- a/general_tests/auth_test.go +++ b/general_tests/auth_test.go @@ -31,11 +31,11 @@ var dbAuth *engine.DataManager var rsponder *engine.Responder func TestAuthSetStorage(t *testing.T) { - config.CgrConfig().CacheConfig[utils.CacheRatingPlans].Precache = true // precache rating plan + config.CgrConfig().CacheCfg()[utils.CacheRatingPlans].Precache = true // precache rating plan data, _ := engine.NewMapStorageJson() dbAuth = engine.NewDataManager(data) engine.SetDataStorage(dbAuth) - rsponder = new(engine.Responder) + rsponder = &engine.Responder{MaxComputedUsage: config.CgrConfig().RALsMaxComputedUsage} } @@ -95,18 +95,19 @@ RP_ANY,DR_ANY_1CNT,*any,10` } func TestAuthPostpaidNoAcnt(t *testing.T) { - cdr := &engine.CDR{ToR: utils.VOICE, RequestType: utils.META_POSTPAID, Direction: "*out", Tenant: "cgrates.org", + cdr := &engine.CDR{ToR: utils.VOICE, RequestType: utils.META_POSTPAID, Tenant: "cgrates.org", Category: "call", Account: "nonexistent", Subject: "testauthpostpaid1", Destination: "4986517174963", SetupTime: time.Date(2015, 8, 27, 11, 26, 0, 0, time.UTC)} var maxSessionTime float64 - if err := rsponder.GetDerivedMaxSessionTime(cdr, &maxSessionTime); err == nil || err != utils.ErrAccountNotFound { + if err := rsponder.GetDerivedMaxSessionTime(cdr, &maxSessionTime); err == nil || + err != utils.ErrAccountNotFound { t.Error(err) } } func TestAuthPostpaidNoDestination(t *testing.T) { // Test subject which does not have destination attached - cdr := &engine.CDR{ToR: utils.VOICE, RequestType: utils.META_POSTPAID, Direction: "*out", Tenant: "cgrates.org", + cdr := &engine.CDR{ToR: utils.VOICE, RequestType: utils.META_POSTPAID, Tenant: "cgrates.org", Category: "call", Account: "testauthpostpaid1", Subject: "testauthpostpaid1", Destination: "441231234", SetupTime: time.Date(2015, 8, 27, 11, 26, 0, 0, time.UTC)} var maxSessionTime float64 @@ -117,7 +118,7 @@ func TestAuthPostpaidNoDestination(t *testing.T) { func TestAuthPostpaidFallbackDest(t *testing.T) { // Test subject which has fallback for destination - cdr := &engine.CDR{ToR: utils.VOICE, RequestType: utils.META_POSTPAID, Direction: "*out", Tenant: "cgrates.org", + cdr := &engine.CDR{ToR: utils.VOICE, RequestType: utils.META_POSTPAID, Tenant: "cgrates.org", Category: "call", Account: "testauthpostpaid1", Subject: "testauthpostpaid2", Destination: "441231234", SetupTime: time.Date(2015, 8, 27, 11, 26, 0, 0, time.UTC)} var maxSessionTime float64 @@ -130,7 +131,7 @@ func TestAuthPostpaidFallbackDest(t *testing.T) { func TestAuthPostpaidWithDestination(t *testing.T) { // Test subject which does not have destination attached - cdr := &engine.CDR{ToR: utils.VOICE, RequestType: utils.META_POSTPAID, Direction: "*out", Tenant: "cgrates.org", + cdr := &engine.CDR{ToR: utils.VOICE, RequestType: utils.META_POSTPAID, Tenant: "cgrates.org", Category: "call", Account: "testauthpostpaid1", Subject: "testauthpostpaid1", Destination: "4986517174963", SetupTime: time.Date(2015, 8, 27, 11, 26, 0, 0, time.UTC)} var maxSessionTime float64 diff --git a/general_tests/cdrs_onlexp_it_test.go b/general_tests/cdrs_onlexp_it_test.go index 9c674cc6b..3e98febc4 100644 --- a/general_tests/cdrs_onlexp_it_test.go +++ b/general_tests/cdrs_onlexp_it_test.go @@ -93,7 +93,7 @@ func TestCDRsOnExpHttpCdrReplication(t *testing.T) { } testCdr1 := &engine.CDR{CGRID: utils.Sha1("httpjsonrpc1", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), ToR: utils.VOICE, OriginID: "httpjsonrpc1", OriginHost: "192.168.1.1", Source: "UNKNOWN", RequestType: utils.META_PSEUDOPREPAID, - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, RunID: utils.DEFAULT_RUNID, Cost: 1.201, Rated: true} @@ -125,7 +125,6 @@ func TestCDRsOnExpHttpCdrReplication(t *testing.T) { rcvedCdrs[0].ToR != testCdr1.ToR || rcvedCdrs[0].OriginHost != testCdr1.OriginHost || rcvedCdrs[0].RequestType != testCdr1.RequestType || - rcvedCdrs[0].Direction != testCdr1.Direction || rcvedCdrs[0].Tenant != testCdr1.Tenant || rcvedCdrs[0].Category != testCdr1.Category || rcvedCdrs[0].Account != testCdr1.Account || @@ -182,7 +181,7 @@ func TestCDRsOnExpAMQPReplication(t *testing.T) { time.Sleep(time.Duration(5 * time.Second)) testCdr := &engine.CDR{CGRID: utils.Sha1("amqpreconnect", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), ToR: utils.VOICE, OriginID: "amqpreconnect", OriginHost: "192.168.1.1", Source: "UNKNOWN", RequestType: utils.META_PSEUDOPREPAID, - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2013, 12, 7, 8, 42, 26, 0, time.UTC), Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, RunID: utils.DEFAULT_RUNID, Cost: 1.201, Rated: true} diff --git a/general_tests/datachrg1_test.go b/general_tests/datachrg1_test.go index e42489457..1761bc983 100644 --- a/general_tests/datachrg1_test.go +++ b/general_tests/datachrg1_test.go @@ -35,7 +35,7 @@ func TestSetStorageDtChrg1(t *testing.T) { func TestLoadCsvTpDtChrg1(t *testing.T) { timings := `TM1,*any,*any,*any,*any,00:00:00 TM2,*any,*any,*any,*any,01:00:00` - rates := `RT_DATA_2c,0,0.002,10,10,0 + rates := `RT_DATA_2c,0,0.002,10s,10s,0 RT_DATA_1c,0,0.001,10,10,0` destinationRates := `DR_DATA_1,*any,RT_DATA_2c,*up,4,0, DR_DATA_2,*any,RT_DATA_1c,*up,4,0,` @@ -95,7 +95,7 @@ func TestGetDataCostDtChrg1(t *testing.T) { func TestGetDataCostSecondIntDtChrg1(t *testing.T) { usedData := 20 - usageDur := time.Duration(usedData) * time.Second + usageDur := time.Duration(usedData) timeStart := time.Date(2014, 3, 4, 1, 0, 0, 0, time.Local) cd := &engine.CallDescriptor{ Direction: "*out", diff --git a/general_tests/ddazmbl1_test.go b/general_tests/ddazmbl1_test.go index fae9da700..b2bc263c9 100644 --- a/general_tests/ddazmbl1_test.go +++ b/general_tests/ddazmbl1_test.go @@ -29,13 +29,13 @@ import ( var dataDB *engine.DataManager -func TestSetStorage(t *testing.T) { +func TestDZ1SetStorage(t *testing.T) { data, _ := engine.NewMapStorageJson() dataDB = engine.NewDataManager(data) engine.SetDataStorage(dataDB) } -func TestLoadCsvTp(t *testing.T) { +func TestDZ1LoadCsvTp(t *testing.T) { timings := `ALWAYS,*any,*any,*any,*any,00:00:00 ASAP,*any,*any,*any,*any,*asap` destinations := `DST_UK_Mobile_BIG5,447596 @@ -51,7 +51,7 @@ RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10` sharedGroups := `` lcrs := `` actions := `TOPUP10_AC,*topup_reset,,,,*monetary,*out,,*any,,,*unlimited,,10,10,false,false,10 -TOPUP10_AC1,*topup_reset,,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40,10,false,false,10` +TOPUP10_AC1,*topup_reset,,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40000000000,10,false,false,10` actionPlans := `TOPUP10_AT,TOPUP10_AC,ASAP,10 TOPUP10_AT,TOPUP10_AC1,ASAP,10` actionTriggers := `` @@ -65,8 +65,12 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` thresholds := `` filters := `` lcrprofiles := `` - csvr := engine.NewTpReader(dataDB.DataDB(), engine.NewStringCSVStorage(',', destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, - sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, derivedCharges, cdrStats, users, aliases, resLimits, stats, thresholds, filters, lcrprofiles), "", "") + csvr := engine.NewTpReader(dataDB.DataDB(), + engine.NewStringCSVStorage(',', destinations, timings, rates, + destinationRates, ratingPlans, ratingProfiles, + sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, + derivedCharges, cdrStats, users, aliases, resLimits, stats, + thresholds, filters, lcrprofiles), "", "") if err := csvr.LoadDestinations(); err != nil { t.Fatal(err) } @@ -113,6 +117,7 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` t.Error("No account saved") } cache.Flush() + dataDB.LoadDataDBCache(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil) if cachedDests := cache.CountEntries(utils.DESTINATION_PREFIX); cachedDests != 0 { @@ -129,21 +134,23 @@ TOPUP10_AT,TOPUP10_AC1,ASAP,10` } } -func TestExecuteActions(t *testing.T) { +func TestDZ1ExecuteActions(t *testing.T) { scheduler.NewScheduler(dataDB).Reload() time.Sleep(10 * time.Millisecond) // Give time to scheduler to topup the account if acnt, err := dataDB.DataDB().GetAccount("cgrates.org:12344"); err != nil { t.Error(err) } else if len(acnt.BalanceMap) != 2 { t.Error("Account does not have enough balances: ", acnt.BalanceMap) - } else if acnt.BalanceMap[utils.VOICE][0].Value != 40 { - t.Error("Account does not have enough minutes in balance", acnt.BalanceMap[utils.VOICE][0].Value) + } else if acnt.BalanceMap[utils.VOICE][0].Value != 40000000000 { + t.Error("Account does not have enough minutes in balance", + acnt.BalanceMap[utils.VOICE][0].Value) } else if acnt.BalanceMap[utils.MONETARY][0].Value != 10 { - t.Error("Account does not have enough monetary balance", acnt.BalanceMap[utils.MONETARY][0].Value) + t.Error("Account does not have enough monetary balance", + acnt.BalanceMap[utils.MONETARY][0].Value) } } -func TestDebit(t *testing.T) { +func TestDZ1Debit(t *testing.T) { cd := &engine.CallDescriptor{ Direction: "*out", Category: "call", @@ -163,10 +170,12 @@ func TestDebit(t *testing.T) { if err != nil { t.Error(err) } - if acnt.BalanceMap[utils.VOICE][0].Value != 20 { - t.Error("Account does not have expected minutes in balance", acnt.BalanceMap[utils.VOICE][0].Value) + if acnt.BalanceMap[utils.VOICE][0].Value != 20000000000 { + t.Error("Account does not have expected *voice units in balance", + acnt.BalanceMap[utils.VOICE][0].Value) } if acnt.BalanceMap[utils.MONETARY][0].Value != 9.99 { - t.Error("Account does not have expected monetary balance", acnt.BalanceMap[utils.MONETARY][0].Value) + t.Error("Account does not have expected *monetary units in balance", + acnt.BalanceMap[utils.MONETARY][0].Value) } } diff --git a/general_tests/ddazmbl2_test.go b/general_tests/ddazmbl2_test.go index 654843f8e..5362da005 100644 --- a/general_tests/ddazmbl2_test.go +++ b/general_tests/ddazmbl2_test.go @@ -51,7 +51,7 @@ RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10` sharedGroups := `` lcrs := `` actions := `TOPUP10_AC,*topup_reset,,,,*monetary,*out,,*any,,,*unlimited,,0,10,false,false,10 -TOPUP10_AC1,*topup_reset,,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40,10,false,false,10` +TOPUP10_AC1,*topup_reset,,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40s,10,false,false,10` actionPlans := `TOPUP10_AT,TOPUP10_AC,ASAP,10 TOPUP10_AT,TOPUP10_AC1,ASAP,10` actionTriggers := `` @@ -136,7 +136,7 @@ func TestExecuteActions2(t *testing.T) { t.Error(err) } else if len(acnt.BalanceMap) != 2 { t.Error("Account does not have enough balances: ", acnt.BalanceMap) - } else if acnt.BalanceMap[utils.VOICE][0].Value != 40 { + } else if acnt.BalanceMap[utils.VOICE][0].Value != 40*float64(time.Second) { t.Error("Account does not have enough minutes in balance", acnt.BalanceMap[utils.VOICE][0].Value) } else if acnt.BalanceMap[utils.MONETARY][0].Value != 0 { t.Error("Account does not have enough monetary balance", acnt.BalanceMap[utils.MONETARY][0].Value) @@ -166,7 +166,7 @@ func TestDebit2(t *testing.T) { if len(acnt.BalanceMap) != 2 { t.Error("Wrong number of user balances found", acnt.BalanceMap) } - if acnt.BalanceMap[utils.VOICE][0].Value != 20 { + if acnt.BalanceMap[utils.VOICE][0].Value != 20*float64(time.Second) { t.Error("Account does not have expected minutes in balance", acnt.BalanceMap[utils.VOICE][0].Value) } for _, blnc := range acnt.BalanceMap[utils.MONETARY] { // Test negative balance for default one diff --git a/general_tests/ddazmbl3_test.go b/general_tests/ddazmbl3_test.go index 4d938f800..f4aae38b7 100644 --- a/general_tests/ddazmbl3_test.go +++ b/general_tests/ddazmbl3_test.go @@ -50,7 +50,7 @@ RP_UK,DR_UK_Mobile_BIG5,ALWAYS,10` *out,cgrates.org,call,discounted_minutes,2013-01-06T00:00:00Z,RP_UK_Mobile_BIG5_PKG,,` sharedGroups := `` lcrs := `` - actions := `TOPUP10_AC1,*topup_reset,,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40,10,false,false,10` + actions := `TOPUP10_AC1,*topup_reset,,,,*voice,*out,,DST_UK_Mobile_BIG5,discounted_minutes,,*unlimited,,40s,10,false,false,10` actionPlans := `TOPUP10_AT,TOPUP10_AC1,ASAP,10` actionTriggers := `` accountActions := `cgrates.org,12346,TOPUP10_AT,,,` @@ -134,7 +134,7 @@ func TestExecuteActions3(t *testing.T) { t.Error(err) } else if len(acnt.BalanceMap) != 1 { t.Error("Account does not have enough balances: ", acnt.BalanceMap) - } else if acnt.BalanceMap[utils.VOICE][0].Value != 40 { + } else if acnt.BalanceMap[utils.VOICE][0].Value != 40*float64(time.Second) { t.Error("Account does not have enough minutes in balance", acnt.BalanceMap[utils.VOICE][0].Value) } } @@ -162,7 +162,7 @@ func TestDebit3(t *testing.T) { if len(acnt.BalanceMap) != 2 { t.Error("Wrong number of user balances found", acnt.BalanceMap) } - if acnt.BalanceMap[utils.VOICE][0].Value != 20 { + if acnt.BalanceMap[utils.VOICE][0].Value != 20*float64(time.Second) { t.Error("Account does not have expected minutes in balance", acnt.BalanceMap[utils.VOICE][0].Value) } if acnt.BalanceMap[utils.MONETARY][0].Value != -0.01 { diff --git a/general_tests/fsevcorelate_test.go b/general_tests/fsevcorelate_test.go index 915dbeee1..fd125ddad 100644 --- a/general_tests/fsevcorelate_test.go +++ b/general_tests/fsevcorelate_test.go @@ -569,9 +569,6 @@ func TestEvCdrCorelate(t *testing.T) { if evStoredCdr.RequestType != jsnStoredCdr.RequestType { t.Errorf("evStoredCdr.RequestType: %s, jsnStoredCdr.RequestType: %s", evStoredCdr.RequestType, jsnStoredCdr.RequestType) } - if evStoredCdr.Direction != jsnStoredCdr.Direction { - t.Errorf("evStoredCdr.Direction: %s, jsnStoredCdr.Direction: %s", evStoredCdr.Direction, jsnStoredCdr.Direction) - } if evStoredCdr.Tenant != jsnStoredCdr.Tenant { t.Errorf("evStoredCdr.Tenant: %s, jsnStoredCdr.Tenant: %s", evStoredCdr.Tenant, jsnStoredCdr.Tenant) } @@ -590,19 +587,10 @@ func TestEvCdrCorelate(t *testing.T) { if evStoredCdr.SetupTime != jsnStoredCdr.SetupTime { t.Errorf("evStoredCdr.SetupTime: %v, jsnStoredCdr.SetupTime: %v", evStoredCdr.SetupTime, jsnStoredCdr.SetupTime) } - if evStoredCdr.PDD != jsnStoredCdr.PDD { - t.Errorf("evStoredCdr.PDD: %v, jsnStoredCdr.PDD: %v", evStoredCdr.PDD, jsnStoredCdr.PDD) - } if evStoredCdr.AnswerTime != jsnStoredCdr.AnswerTime { t.Errorf("evStoredCdr.AnswerTime: %v, jsnStoredCdr.AnswerTime: %v", evStoredCdr.AnswerTime, jsnStoredCdr.AnswerTime) } if evStoredCdr.Usage != jsnStoredCdr.Usage { t.Errorf("evStoredCdr.Usage: %v, jsnStoredCdr.Usage: %v", evStoredCdr.Usage, jsnStoredCdr.Usage) } - if evStoredCdr.Supplier != jsnStoredCdr.Supplier { - t.Errorf("evStoredCdr.Supplier: %s, jsnStoredCdr.Supplier: %s", evStoredCdr.Supplier, jsnStoredCdr.Supplier) - } - if evStoredCdr.DisconnectCause != jsnStoredCdr.DisconnectCause { - t.Errorf("evStoredCdr.DisconnectCause: %s, jsnStoredCdr.DisconnectCause: %s", evStoredCdr.DisconnectCause, jsnStoredCdr.DisconnectCause) - } } diff --git a/general_tests/smschrg1_test.go b/general_tests/smschrg1_test.go index 8fa89a76f..c5e479109 100644 --- a/general_tests/smschrg1_test.go +++ b/general_tests/smschrg1_test.go @@ -28,7 +28,7 @@ import ( ) func TestSMSSetStorageSmsChrg1(t *testing.T) { - config.CgrConfig().CacheConfig[utils.CacheRatingPlans].Precache = true // precache rating plan + config.CgrConfig().CacheCfg()[utils.CacheRatingPlans].Precache = true // precache rating plan data, _ := engine.NewMapStorageJson() dataDB = engine.NewDataManager(data) engine.SetDataStorage(dataDB) @@ -70,7 +70,7 @@ func TestSMSLoadCsvTpSmsChrg1(t *testing.T) { } func TestSMSGetDataCostSmsChrg1(t *testing.T) { - usageDur := time.Second + usageDur := time.Duration(1) timeStart := time.Date(2014, 3, 4, 0, 0, 0, 0, time.Local) cd := &engine.CallDescriptor{ Direction: "*out", diff --git a/general_tests/tut_smgeneric_it_test.go b/general_tests/tut_smgeneric_it_test.go index 2d3d6277c..f9385bf28 100644 --- a/general_tests/tut_smgeneric_it_test.go +++ b/general_tests/tut_smgeneric_it_test.go @@ -98,7 +98,7 @@ func TestTutSMGCacheStats(t *testing.T) { t.Error(reply) } var rcvStats *utils.CacheStats - expectedStats := &utils.CacheStats{Destinations: 5, ReverseDestinations: 7, RatingPlans: 4, RatingProfiles: 9, + expectedStats := &utils.CacheStats{Destinations: 5, ReverseDestinations: 7, RatingPlans: 4, RatingProfiles: 10, Actions: 9, ActionPlans: 4, AccountActionPlans: 5, SharedGroups: 1, DerivedChargers: 1, LcrProfiles: 5, CdrStats: 6, Users: 3, Aliases: 1, ReverseAliases: 2, ResourceProfiles: 3, Resources: 3, StatQueues: 1, StatQueueProfiles: 1, Thresholds: 7, ThresholdProfiles: 7, Filters: 16, LCRProfiles: 1} diff --git a/general_tests/tutorial_it_test.go b/general_tests/tutorial_it_test.go index 3a507f6ca..be1c1fc4f 100644 --- a/general_tests/tutorial_it_test.go +++ b/general_tests/tutorial_it_test.go @@ -102,7 +102,7 @@ func TestTutITCacheStats(t *testing.T) { t.Error(reply) } var rcvStats *utils.CacheStats - expectedStats := &utils.CacheStats{Destinations: 5, ReverseDestinations: 7, RatingPlans: 4, RatingProfiles: 9, + expectedStats := &utils.CacheStats{Destinations: 5, ReverseDestinations: 7, RatingPlans: 4, RatingProfiles: 10, Actions: 9, ActionPlans: 4, AccountActionPlans: 5, SharedGroups: 1, DerivedChargers: 1, LcrProfiles: 5, CdrStats: 6, Users: 3, Aliases: 1, ReverseAliases: 2, ResourceProfiles: 3, Resources: 3, StatQueues: 1, StatQueueProfiles: 1, Thresholds: 7, ThresholdProfiles: 7, Filters: 16, LCRProfiles: 1} @@ -498,7 +498,6 @@ func TestTutITDerivedMaxSessionTime(t *testing.T) { OriginID: "testevent1", OriginHost: "127.0.0.1", RequestType: utils.META_PREPAID, - Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "1004", @@ -507,7 +506,6 @@ func TestTutITDerivedMaxSessionTime(t *testing.T) { SetupTime: tStart, AnswerTime: tStart, Usage: time.Duration(120) * time.Second, - Supplier: "suppl1", Cost: -1, } var maxTime float64 @@ -520,9 +518,10 @@ func TestTutITDerivedMaxSessionTime(t *testing.T) { // Check MaxUsage func TestTutITMaxUsage(t *testing.T) { - setupReq := &engine.UsageRecord{ToR: utils.VOICE, RequestType: utils.META_PREPAID, Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", + setupReq := &engine.UsageRecord{ToR: utils.VOICE, + RequestType: utils.META_PREPAID, Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1001", - SetupTime: "2014-08-04T13:00:00Z", Usage: "1", + SetupTime: "2014-08-04T13:00:00Z", Usage: "1s", } var maxTime float64 if err := tutLocalRpc.Call("ApierV2.GetMaxUsage", setupReq, &maxTime); err != nil { @@ -530,7 +529,7 @@ func TestTutITMaxUsage(t *testing.T) { } else if maxTime != 1 { t.Errorf("Calling ApierV2.MaxUsage got maxTime: %f", maxTime) } - setupReq = &engine.UsageRecord{ToR: utils.VOICE, RequestType: utils.META_RATED, Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", + setupReq = &engine.UsageRecord{ToR: utils.VOICE, RequestType: utils.META_RATED, Tenant: "cgrates.org", Category: "call", Account: "test_max_usage", Destination: "1001", SetupTime: "2014-08-04T13:00:00Z", } @@ -543,7 +542,7 @@ func TestTutITMaxUsage(t *testing.T) { // Check DebitUsage func TestTutITDebitUsage(t *testing.T) { - setupReq := &engine.UsageRecord{ToR: utils.VOICE, RequestType: utils.META_PREPAID, Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", + setupReq := &engine.UsageRecord{ToR: utils.VOICE, RequestType: utils.META_PREPAID, Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1001", AnswerTime: "2014-08-04T13:00:00Z", Usage: "1", } @@ -558,10 +557,10 @@ func TestTutITDebitUsage(t *testing.T) { // Test CDR from external sources func TestTutITProcessExternalCdr(t *testing.T) { cdr := &engine.ExternalCDR{ToR: utils.VOICE, - OriginID: "testextcdr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: utils.OUT, - Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1001", Supplier: "SUPPL1", + OriginID: "testextcdr1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1001", SetupTime: "2014-08-04T13:00:00Z", AnswerTime: "2014-08-04T13:00:07Z", - Usage: "1", PDD: "7.0", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Usage: "1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } var reply string if err := tutLocalRpc.Call("CdrsV1.ProcessExternalCdr", cdr, &reply); err != nil { @@ -574,10 +573,9 @@ func TestTutITProcessExternalCdr(t *testing.T) { // Test CDR involving UserProfile func TestTutITProcessExternalCdrUP(t *testing.T) { cdr := &engine.ExternalCDR{ToR: utils.VOICE, - OriginID: "testextcdr2", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, Direction: utils.OUT, - RequestType: utils.USERS, Tenant: utils.USERS, Account: utils.USERS, Destination: "1001", Supplier: "SUPPL1", - SetupTime: "2014-08-04T13:00:00Z", AnswerTime: "2014-08-04T13:00:07Z", - Usage: "2", PDD: "0.2", DisconnectCause: "NORMAL_DISCONNECT", + OriginID: "testextcdr2", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, + RequestType: utils.USERS, Tenant: utils.USERS, Account: utils.USERS, Destination: "1001", + SetupTime: "2014-08-04T13:00:00Z", AnswerTime: "2014-08-04T13:00:07Z", Usage: "2s", ExtraFields: map[string]string{"Cli": "+4986517174964", "fieldextr2": "valextr2", "SysUserName": utils.USERS}, } var reply string @@ -589,14 +587,15 @@ func TestTutITProcessExternalCdrUP(t *testing.T) { time.Sleep(time.Duration(*waitRater) * time.Millisecond) eCdr := &engine.ExternalCDR{CGRID: "63a8d2bfeca2cfb790826c3ec461696d6574cfde", OrderID: 2, ToR: utils.VOICE, - OriginID: "testextcdr2", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: utils.OUT, - Tenant: "cgrates.org", Category: "call", Account: "1004", Subject: "1004", Destination: "1001", Supplier: "SUPPL1", - SetupTime: time.Date(2014, 8, 4, 13, 0, 0, 0, time.UTC).Local().Format(time.RFC3339), AnswerTime: time.Date(2014, 8, 4, 13, 0, 7, 0, time.UTC).Local().Format(time.RFC3339), - Usage: "2", PDD: "0.2", DisconnectCause: "NORMAL_DISCONNECT", + OriginID: "testextcdr2", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1004", Subject: "1004", Destination: "1001", + SetupTime: time.Date(2014, 8, 4, 13, 0, 0, 0, time.UTC).Local().Format(time.RFC3339), + AnswerTime: time.Date(2014, 8, 4, 13, 0, 7, 0, time.UTC).Local().Format(time.RFC3339), Usage: "2s", ExtraFields: map[string]string{"Cli": "+4986517174964", "fieldextr2": "valextr2", "SysUserName": "danb4"}, RunID: utils.DEFAULT_RUNID, Cost: 1} var cdrs []*engine.ExternalCDR - req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1004"}, DestinationPrefixes: []string{"1001"}} + req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, + Accounts: []string{"1004"}, DestinationPrefixes: []string{"1001"}} if err := tutLocalRpc.Call("ApierV2.GetCdrs", req, &cdrs); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(cdrs) != 1 { @@ -629,33 +628,24 @@ func TestTutITProcessExternalCdrUP(t *testing.T) { if cdrs[0].Destination != eCdr.Destination { t.Errorf("Unexpected Destination for CDR: %+v", cdrs[0]) } - if cdrs[0].Supplier != eCdr.Supplier { - t.Errorf("Unexpected Supplier for CDR: %+v", cdrs[0]) - } if cdrs[0].SetupTime != eCdr.SetupTime { t.Errorf("Unexpected SetupTime for CDR: %+v", cdrs[0]) } - if cdrs[0].PDD != eCdr.PDD { - t.Errorf("Unexpected PDD for CDR: %+v", cdrs[0]) - } if cdrs[0].AnswerTime != eCdr.AnswerTime { t.Errorf("Unexpected AnswerTime for CDR: %+v", cdrs[0]) } if cdrs[0].Usage != eCdr.Usage { t.Errorf("Unexpected Usage for CDR: %+v", cdrs[0]) } - if cdrs[0].DisconnectCause != eCdr.DisconnectCause { - t.Errorf("Unexpected DisconnectCause for CDR: %+v", cdrs[0]) - } } } func TestTutITCostErrors(t *testing.T) { cdr := &engine.ExternalCDR{ToR: utils.VOICE, - OriginID: "TestTutIT_1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, Direction: utils.OUT, - Tenant: "cgrates.org", Category: "fake", Account: "2001", Subject: "2001", Destination: "1001", Supplier: "SUPPL1", + OriginID: "TestTutIT_1", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "fake", Account: "2001", Subject: "2001", Destination: "1001", SetupTime: "2014-08-04T13:00:00Z", AnswerTime: "2014-08-04T13:00:07Z", - Usage: "1", PDD: "7.0", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Usage: "1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } var reply string if err := tutLocalRpc.Call("CdrsV1.ProcessExternalCdr", cdr, &reply); err != nil { @@ -679,10 +669,10 @@ func TestTutITCostErrors(t *testing.T) { } } cdr2 := &engine.ExternalCDR{ToR: utils.VOICE, - OriginID: "TestTutIT_2", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_POSTPAID, Direction: utils.OUT, - Tenant: "cgrates.org", Category: "fake", Account: "2002", Subject: "2002", Destination: "1001", Supplier: "SUPPL1", + OriginID: "TestTutIT_2", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_POSTPAID, + Tenant: "cgrates.org", Category: "fake", Account: "2002", Subject: "2002", Destination: "1001", SetupTime: "2014-08-04T13:00:00Z", AnswerTime: "2014-08-04T13:00:07Z", - Usage: "1", PDD: "7.0", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Usage: "1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } if err := tutLocalRpc.Call("CdrsV1.ProcessExternalCdr", cdr2, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) @@ -704,10 +694,10 @@ func TestTutITCostErrors(t *testing.T) { } } cdr3 := &engine.ExternalCDR{ToR: utils.VOICE, - OriginID: "TestTutIT_3", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_POSTPAID, Direction: utils.OUT, - Tenant: "cgrates.org", Category: "fake", Account: "1001", Subject: "1001", Destination: "2002", Supplier: "SUPPL1", + OriginID: "TestTutIT_3", OriginHost: "192.168.1.1", Source: utils.UNIT_TEST, RequestType: utils.META_POSTPAID, + Tenant: "cgrates.org", Category: "fake", Account: "1001", Subject: "1001", Destination: "2002", SetupTime: "2014-08-04T13:00:00Z", AnswerTime: "2014-08-04T13:00:07Z", - Usage: "1", PDD: "7.0", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, + Usage: "1", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } if err := tutLocalRpc.Call("CdrsV1.ProcessExternalCdr", cdr3, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) @@ -928,46 +918,69 @@ func TestTutITLcrQos(t *testing.T) { TimeEnd: tEnd, } eStLcr := &engine.LCRCost{ - Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS, StrategyParams: "", Weight: 10.0}, + Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", + Strategy: engine.LCR_STRATEGY_QOS, StrategyParams: "", Weight: 10.0}, SupplierCosts: []*engine.LCRSupplierCost{ - &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second, - QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}}, - &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 1.2, Duration: 60 * time.Second, - QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}}, - }, - } - eStLcr2 := &engine.LCRCost{ - Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS, StrategyParams: "", Weight: 10.0}, - SupplierCosts: []*engine.LCRSupplierCost{ - &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 1.2, Duration: 60 * time.Second, - QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}}, - &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second, - QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}}, + &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", + Cost: 1.2, Duration: 60 * time.Second, + QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, + engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}}, + &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", + Cost: 1.2, Duration: 60 * time.Second, + QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, + engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}}, }, } + /* + eStLcr2 := &engine.LCRCost{ + Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", + Strategy: engine.LCR_STRATEGY_QOS, StrategyParams: "", Weight: 10.0}, + SupplierCosts: []*engine.LCRSupplierCost{ + &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", + Cost: 1.2, Duration: 60 * time.Second, + QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, + engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}}, + &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", + Cost: 1.2, Duration: 60 * time.Second, + QOS: map[string]float64{engine.TCD: -1, engine.ACC: -1, engine.TCC: -1, + engine.ASR: -1, engine.ACD: -1, engine.DDC: -1}}, + }, + } + */ var lcr engine.LCRCost // Since there is no real quality difference, the suppliers will come in random order here 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) { - t.Errorf("Expecting: %+v, received: %+v", eStLcr.Entry, lcr.Entry) - } else if !reflect.DeepEqual(eStLcr.SupplierCosts, lcr.SupplierCosts) && !reflect.DeepEqual(eStLcr2.SupplierCosts, lcr.SupplierCosts) { - t.Errorf("Expecting: %+v, received: %+v", eStLcr.SupplierCosts[0], lcr.SupplierCosts[0]) - } + /* + if err := tutLocalRpc.Call("Responder.GetLCR", cd, &lcr); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eStLcr.Entry, lcr.Entry) { + t.Errorf("Expecting: %+v, received: %+v", eStLcr.Entry, lcr.Entry) + } else if !reflect.DeepEqual(eStLcr.SupplierCosts, lcr.SupplierCosts) && + !reflect.DeepEqual(eStLcr2.SupplierCosts, lcr.SupplierCosts) { + t.Errorf("Expecting: %+v, received: %+v", eStLcr.SupplierCosts[0], lcr.SupplierCosts[0]) + } + */ // Post some CDRs to influence stats - testCdr1 := &engine.CDR{CGRID: utils.Sha1("testcdr1", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), - ToR: utils.VOICE, OriginID: "testcdr1", OriginHost: "192.168.1.1", Source: "TEST_QOS_LCR", RequestType: utils.META_RATED, - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002", - SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(2) * time.Minute, Supplier: "suppl1", + testCdr1 := &engine.CDR{CGRID: utils.Sha1("testcdr1", + time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), + ToR: utils.VOICE, OriginID: "testcdr1", OriginHost: "192.168.1.1", + Source: "TEST_QOS_LCR", RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1001", + Subject: "1001", Destination: "1002", + SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), + AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(2) * time.Minute, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}} - testCdr2 := &engine.CDR{CGRID: utils.Sha1("testcdr2", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), - ToR: utils.VOICE, OriginID: "testcdr2", OriginHost: "192.168.1.1", Source: "TEST_QOS_LCR", RequestType: utils.META_RATED, - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1002", Subject: "1002", Destination: "1003", - SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(90) * time.Second, Supplier: "suppl2", + testCdr2 := &engine.CDR{CGRID: utils.Sha1("testcdr2", + time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), + ToR: utils.VOICE, OriginID: "testcdr2", OriginHost: "192.168.1.1", + Source: "TEST_QOS_LCR", RequestType: utils.META_RATED, + Tenant: "cgrates.org", Category: "call", Account: "1002", + Subject: "1002", Destination: "1003", + SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), + AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(90) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}} var reply string for _, cdr := range []*engine.CDR{testCdr1, testCdr2} { @@ -979,12 +992,20 @@ func TestTutITLcrQos(t *testing.T) { } // Based on stats, supplier1 should always be better since he has a higer ACD eStLcr = &engine.LCRCost{ - Entry: &engine.LCREntry{DestinationId: utils.ANY, RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS, StrategyParams: "", Weight: 10.0}, + Entry: &engine.LCREntry{DestinationId: utils.ANY, + RPCategory: "lcr_profile1", Strategy: engine.LCR_STRATEGY_QOS, + StrategyParams: "", Weight: 10.0}, SupplierCosts: []*engine.LCRSupplierCost{ - &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl1", Cost: 1.2, Duration: 60 * time.Second, - QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}}, - &engine.LCRSupplierCost{Supplier: "*out:cgrates.org:lcr_profile1:suppl2", Cost: 1.2, Duration: 60 * time.Second, - QOS: map[string]float64{engine.TCD: 90, engine.ACC: 0.325, engine.TCC: 0.325, engine.ASR: 100, engine.ACD: 90}}, + &engine.LCRSupplierCost{ + Supplier: "*out:cgrates.org:lcr_profile1:suppl1", + Cost: 1.2, Duration: 60 * time.Second, + QOS: map[string]float64{engine.TCD: 240, engine.ACC: 0.35, + engine.TCC: 0.7, engine.ASR: 100, engine.ACD: 120}}, + &engine.LCRSupplierCost{ + Supplier: "*out:cgrates.org:lcr_profile1:suppl2", + Cost: 1.2, Duration: 60 * time.Second, + QOS: map[string]float64{engine.TCD: 90, engine.ACC: 0.325, + engine.TCC: 0.325, engine.ASR: 100, engine.ACD: 90}}, }, } cd.CgrID = "4" @@ -998,9 +1019,9 @@ func TestTutITLcrQos(t *testing.T) { } testCdr3 := &engine.CDR{CGRID: utils.Sha1("testcdr3", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), ToR: utils.VOICE, OriginID: "testcdr3", OriginHost: "192.168.1.1", Source: "TEST_QOS_LCR", RequestType: utils.META_RATED, - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1004", + Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1004", SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(180) * time.Second, Supplier: "suppl2"} + Usage: time.Duration(180) * time.Second} if err := tutLocalRpc.Call("CdrsV1.ProcessCdr", testCdr3, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if reply != utils.OK { @@ -1061,9 +1082,9 @@ func TestTutITLcrQosThreshold(t *testing.T) { } testCdr4 := &engine.CDR{CGRID: utils.Sha1("testcdr4", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), ToR: utils.VOICE, OriginID: "testcdr4", OriginHost: "192.168.1.1", Source: "TEST_QOS_LCR", RequestType: utils.META_RATED, - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1004", + Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1004", SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(60) * time.Second, Supplier: "suppl2"} + Usage: time.Duration(60) * time.Second} var reply string if err := tutLocalRpc.Call("CdrsV1.ProcessCdr", testCdr4, &reply); err != nil { // Should drop ACD under the 2m required by threshold, removing suppl2 from lcr t.Error("Unexpected error: ", err.Error()) @@ -1126,9 +1147,9 @@ func TestTutITLcrQosThreshold(t *testing.T) { } testCdr5 := &engine.CDR{CGRID: utils.Sha1("testcdr5", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), ToR: utils.VOICE, OriginID: "testcdr5", OriginHost: "192.168.1.1", Source: "TEST_QOS_LCR", RequestType: utils.META_RATED, - Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1004", + Tenant: "cgrates.org", Category: "call", Account: "1003", Subject: "1003", Destination: "1004", SetupTime: time.Date(2014, 12, 7, 8, 42, 24, 0, time.UTC), AnswerTime: time.Date(2014, 12, 7, 8, 42, 26, 0, time.UTC), - Usage: time.Duration(1) * time.Second, Supplier: "suppl2"} + Usage: time.Duration(1) * time.Second} if err := tutLocalRpc.Call("CdrsV1.ProcessCdr", testCdr5, &reply); err != nil { // Should drop ACD under the 1m required by threshold, removing suppl2 from lcr t.Error("Unexpected error: ", err.Error()) } else if reply != utils.OK { @@ -1302,16 +1323,16 @@ func TestTutITCdrStatsAfter(t *testing.T) { func TestTutITPrepaidCDRWithSMCost(t *testing.T) { cdr := &engine.CDR{CGRID: utils.Sha1("testprepaid1", time.Date(2016, 4, 6, 13, 29, 24, 0, time.UTC).String()), ToR: utils.VOICE, OriginID: "testprepaid1", OriginHost: "192.168.1.1", Source: "TEST_PREPAID_CDR_SMCOST1", RequestType: utils.META_PREPAID, - Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1003", + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1003", SetupTime: time.Date(2016, 4, 6, 13, 29, 24, 0, time.UTC), AnswerTime: time.Date(2016, 4, 6, 13, 30, 0, 0, time.UTC), - Usage: time.Duration(90) * time.Second, Supplier: "suppl1", + Usage: time.Duration(90) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}} smCost := &engine.SMCost{CGRID: cdr.CGRID, RunID: utils.META_DEFAULT, OriginHost: cdr.OriginHost, OriginID: cdr.OriginID, CostSource: "TestTutITPrepaidCDRWithSMCost", - Usage: cdr.Usage.Seconds(), + Usage: cdr.Usage, CostDetails: &engine.CallCost{ Direction: utils.OUT, Destination: "1003", @@ -1359,9 +1380,9 @@ func TestTutITPrepaidCDRWithSMCost(t *testing.T) { func TestTutITPrepaidCDRWithoutSMCost(t *testing.T) { cdr := &engine.CDR{CGRID: utils.Sha1("testprepaid2", time.Date(2016, 4, 6, 13, 29, 24, 0, time.UTC).String()), ToR: utils.VOICE, OriginID: "testprepaid2", OriginHost: "192.168.1.1", Source: "TEST_PREPAID_CDR_NO_SMCOST1", RequestType: utils.META_PREPAID, - Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1003", + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1003", SetupTime: time.Date(2016, 4, 6, 13, 29, 24, 0, time.UTC), AnswerTime: time.Date(2016, 4, 6, 13, 30, 0, 0, time.UTC), - Usage: time.Duration(90) * time.Second, Supplier: "suppl1", + Usage: time.Duration(90) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}} var reply string if err := tutLocalRpc.Call("CdrsV1.ProcessCdr", cdr, &reply); err != nil { @@ -1390,9 +1411,9 @@ func TestTutITPrepaidCDRWithoutSMCost(t *testing.T) { func TestTutITExportCDR(t *testing.T) { cdr := &engine.CDR{ToR: utils.VOICE, OriginID: "testexportcdr1", OriginHost: "192.168.1.1", Source: "TestTutITExportCDR", RequestType: utils.META_RATED, - Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1003", + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1003", SetupTime: time.Date(2016, 11, 30, 17, 5, 24, 0, time.UTC), AnswerTime: time.Date(2016, 11, 30, 17, 6, 4, 0, time.UTC), - Usage: time.Duration(98) * time.Second, Supplier: "suppl1", + Usage: time.Duration(98) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}} cdr.ComputeCGRID() var reply string diff --git a/sessionmanager/data_it_test.go b/sessionmanager/data_it_test.go index d14166d2f..41c8ebe76 100644 --- a/sessionmanager/data_it_test.go +++ b/sessionmanager/data_it_test.go @@ -75,7 +75,7 @@ func TestSMGDataApierRpcConn(t *testing.T) { // Load the tariff plan, creating accounts and their balances func TestSMGDataTPFromFolder(t *testing.T) { - attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "testtp")} + attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")} var loadInst utils.LoadInstance if err := smgRPC.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil { t.Error(err) @@ -85,36 +85,84 @@ func TestSMGDataTPFromFolder(t *testing.T) { func TestSMGDataLastUsedData(t *testing.T) { var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"} - eAcntVal := 50000000000.000000 + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} + eAcntVal := 102400.0 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + t.Errorf("Expected: %f, received: %f", + eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } + tStart, _ := utils.ParseDate("2016-01-05T18:31:05Z") + cd := engine.CallDescriptor{ + Direction: "*out", + Category: "data", + Tenant: "cgrates.org", + Subject: "1001", + Account: "1001", + Destination: utils.DATA, + TimeStart: tStart, + TimeEnd: tStart.Add(time.Duration(1024)), + } + var cc engine.CallCost + // Make sure the cost is what we expect to be for 1MB of data + if err := smgRPC.Call("Responder.GetCost", cd, &cc); err != nil { + t.Error("Got error on Responder.GetCost: ", err.Error()) + } else if cc.Cost != 1024 { + t.Errorf("Calling Responder.GetCost got callcost: %v", cc.Cost) } smgEv := SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.DATA, utils.ACCID: "123491", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", utils.TENANT: "cgrates.org", utils.REQTYPE: utils.META_PREPAID, utils.SETUP_TIME: "2016-01-05 18:30:59", utils.ANSWER_TIME: "2016-01-05 18:31:05", - utils.USAGE: "1048576", + utils.USAGE: "5120", // 5MB } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage int64 + if err := smgRPC.Call("SMGenericV2.InitiateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 5120 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49998945280.000000 //1054720 + eAcntVal = 97280.0 // 100 -5 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", + eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } + smgEv = SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.DATA, + utils.ACCID: "123491", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: utils.DATA, + utils.CATEGORY: "data", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.SETUP_TIME: "2016-01-05 18:30:59", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.USAGE: "5120", + utils.LastUsed: "4096", + } + if err := smgRPC.Call("SMGenericV2.UpdateSession", smgEv, &maxUsage); err != nil { + t.Error(err) + } + if maxUsage != 5120 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 93184.0 // 100-9 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { @@ -125,37 +173,9 @@ func TestSMGDataLastUsedData(t *testing.T) { utils.TOR: utils.DATA, utils.ACCID: "123491", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", - utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", - utils.REQTYPE: utils.META_PREPAID, - utils.SETUP_TIME: "2016-01-05 18:30:59", - utils.ANSWER_TIME: "2016-01-05 18:31:05", - utils.USAGE: "1048576", - utils.LastUsed: "20000", - } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { - t.Error(err) - } - if maxUsage != 1.048576e+06 { - t.Error("Bad max usage: ", maxUsage) - } - eAcntVal = 49998924800.000000 //20480 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) - } - smgEv = SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.DATA, - utils.ACCID: "123491", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: "1001", + utils.SUBJECT: "1001", + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", utils.TENANT: "cgrates.org", utils.REQTYPE: utils.META_PREPAID, @@ -164,58 +184,73 @@ func TestSMGDataLastUsedData(t *testing.T) { utils.LastUsed: "0", } var rpl string - if err = smgRPC.Call("SMGenericV1.TerminateSession", smgEv, &rpl); err != nil || rpl != utils.OK { + if err = smgRPC.Call("SMGenericV1.TerminateSession", + smgEv, &rpl); err != nil || rpl != utils.OK { t.Error(err) } - eAcntVal = 49999979520.000000 //20480 + eAcntVal = 98304.0 //100-4 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + t.Errorf("Expected: %f, received: %f", + eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) } } -func TestSMGDataLastUsedMultipleData(t *testing.T) { +func TestSMGDataLastUsedMultipleUpdates(t *testing.T) { var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"} - eAcntVal := 49999979520.000000 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + acntAttrs := &utils.AttrGetAccount{Tenant: "cgrates.org", + Account: "TestSMGDataLastUsedMultipleData"} + eAcntVal := 102400.0 + attrSetBalance := utils.AttrSetBalance{ + Tenant: acntAttrs.Tenant, Account: acntAttrs.Account, + BalanceType: utils.DATA, + BalanceID: utils.StringPointer("TestSMGDataLastUsedMultipleData"), + Value: utils.Float64Pointer(eAcntVal)} + var reply string + if err := smgRPC.Call("ApierV2.SetBalance", attrSetBalance, &reply); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if reply != utils.OK { + t.Errorf("Received: %s", reply) + } + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { + t.Error(err) + } else if totalVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); totalVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", totalVal) } smgEv := SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.DATA, utils.ACCID: "123492", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", + utils.TENANT: acntAttrs.Tenant, utils.REQTYPE: utils.META_PREPAID, utils.SETUP_TIME: "2016-01-05 18:30:50", utils.ANSWER_TIME: "2016-01-05 18:31:05", - utils.USAGE: "1048576", + utils.USAGE: "6144", // 6 MB } var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call("SMGenericV2.InitiateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 6144 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49998924800.000000 // 1054720 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 96256 // 100-6 + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if totalVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); totalVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", totalVal) } aSessions := make([]*ActiveSession, 0) if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 { + } else if len(aSessions) != 1 || + aSessions[0].Usage != time.Duration(6144) { t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) } smgEv = SMGenericEvent{ @@ -223,710 +258,529 @@ func TestSMGDataLastUsedMultipleData(t *testing.T) { utils.TOR: utils.DATA, utils.ACCID: "123492", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", + utils.TENANT: acntAttrs.Tenant, utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "20000", + utils.SETUP_TIME: "2016-01-05 18:30:50", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.USAGE: "8192", // 8 MB + utils.LastUsed: "7168", } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call("SMGenericV2.UpdateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 8192 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49998904320.000000 // 20480 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 87040.000000 // 15MB used + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if totalVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); totalVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", totalVal) } if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1068576 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) + } else if len(aSessions) != 1 || + aSessions[0].Usage != time.Duration(15360) { + t.Errorf("wrong active sessions: %f", aSessions[0].Usage) } smgEv = SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.DATA, utils.ACCID: "123492", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", + utils.TENANT: acntAttrs.Tenant, utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "20000", + utils.SETUP_TIME: "2016-01-05 18:30:50", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.USAGE: "1024", // 8 MB + utils.LastUsed: "5120", // 5 MB } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call("SMGenericV2.UpdateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 1024 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49998883840.000000 // 20480 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 87040.000000 // the amount is not modified and there will be 1024 extra left in SMG + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if totalVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); totalVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", totalVal) } if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1088576 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) + } else if len(aSessions) != 1 || + aSessions[0].Usage != time.Duration(13312) { // 14MB in used, 2MB extra reserved + t.Errorf("wrong active sessions: %+v", aSessions[0].Usage) } smgEv = SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.DATA, utils.ACCID: "123492", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", + utils.TENANT: acntAttrs.Tenant, utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "20000", + utils.SETUP_TIME: "2016-01-05 18:30:50", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.USAGE: "1024", } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call("SMGenericV2.UpdateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 1024 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49998863360.000000 // 20480 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 87040.000000 + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if totalVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); totalVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", totalVal) } if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1108576 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) + } else if len(aSessions) != 1 || + aSessions[0].Usage != time.Duration(14336) { // 14MB in use + t.Errorf("wrong active sessions: %f", aSessions[0].Usage) } smgEv = SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.DATA, utils.ACCID: "123492", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", + utils.TENANT: acntAttrs.Tenant, utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "20000", - } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { - t.Error(err) - } - if maxUsage != 1.048576e+06 { - t.Error("Bad max usage: ", maxUsage) - } - eAcntVal = 49998842880.000000 // 20480 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) - } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { - t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1128576 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) - } - smgEv = SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.DATA, - utils.ACCID: "123492", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", - utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", - utils.REQTYPE: utils.META_PREPAID, - utils.LastUsed: "0", + utils.SETUP_TIME: "2016-01-05 18:30:50", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.LastUsed: "0", // refund 1024 (extra used) + 1024 (extra reserved) } var rpl string if err = smgRPC.Call("SMGenericV1.TerminateSession", smgEv, &rpl); err != nil || rpl != utils.OK { t.Error(err) } - eAcntVal = 49999897600.000000 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 89088.000000 + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if totalVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); totalVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", totalVal) } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { + if err := smgRPC.Call("SMGenericV1.GetActiveSessions", + nil, &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err, aSessions) } -} - -func TestSMGDataDerivedChargingNoCredit(t *testing.T) { - var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1011"} - eAcntVal := 50000.0 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + if err := smgRPC.Call("SMGenericV1.ProcessCDR", smgEv, &reply); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) + } else if reply != utils.OK { + t.Errorf("Received reply: %s", reply) } - smgEv := SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.VOICE, - utils.ACCID: "1234967", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1011", - utils.SUBJECT: "1011", - utils.DESTINATION: "+49", - 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: "100", - } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { - t.Error(err) - } - // the second derived charging run has no credit - - if maxUsage != 0 { - t.Error("Bad max usage: ", maxUsage) - } - eAcntVal = 50000.0 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) + time.Sleep(time.Duration(20) * time.Millisecond) + var cdrs []*engine.ExternalCDR + req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, + Accounts: []string{acntAttrs.Account}} + if err := smgRPC.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 != "13312" { + t.Errorf("Unexpected CDR Usage received, cdr: %v %+v ", cdrs[0].Usage, cdrs[0]) + } } } func TestSMGDataTTLExpired(t *testing.T) { var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"} - eAcntVal := 49999897600.000000 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + acntAttrs := &utils.AttrGetAccount{Tenant: "cgrates.org", + Account: "TestSMGDataTTLExpired"} + eAcntVal := 102400.0 + attrSetBalance := utils.AttrSetBalance{ + Tenant: acntAttrs.Tenant, Account: acntAttrs.Account, + BalanceType: utils.DATA, + BalanceID: utils.StringPointer("TestSMGDataTTLExpired"), + Value: utils.Float64Pointer(eAcntVal)} + var reply string + if err := smgRPC.Call("ApierV2.SetBalance", attrSetBalance, &reply); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if reply != utils.OK { + t.Errorf("Received: %s", reply) + } + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { + t.Error(err) + } else if totalVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); totalVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", totalVal) } smgEv := SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.DATA, - utils.ACCID: "123494", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", - utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", - utils.REQTYPE: utils.META_PREPAID, - utils.SETUP_TIME: "2016-01-05 18:30:52", - utils.ANSWER_TIME: "2016-01-05 18:31:05", - utils.USAGE: "1048576", + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.DATA, + utils.ACCID: "TestSMGDataTTLExpired", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, + utils.CATEGORY: "data", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.SETUP_TIME: "2016-01-05 18:30:52", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.USAGE: "1024", + utils.SessionTTLUsage: "2048", // will be charged on TTL } var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call("SMGenericV2.InitiateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 1024 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49998842880.000000 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 101376.000000 + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if dataVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); dataVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, dataVal) } - time.Sleep(50 * time.Millisecond) - eAcntVal = 49998842880.000000 //1054720 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + time.Sleep(70 * time.Millisecond) + eAcntVal = 99328.000000 + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if dataVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); dataVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, dataVal) } } -func TestSMGDataTTLExpiredMultiUpdates(t *testing.T) { +func TestSMGDataTTLExpMultiUpdates(t *testing.T) { var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"} - eAcntVal := 49998842880.000000 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + acntAttrs := &utils.AttrGetAccount{Tenant: "cgrates.org", + Account: "TestSMGDataTTLExpMultiUpdates"} + eAcntVal := 102400.0 + attrSetBalance := utils.AttrSetBalance{ + Tenant: acntAttrs.Tenant, Account: acntAttrs.Account, + BalanceType: utils.DATA, + BalanceID: utils.StringPointer("TestSMGDataTTLExpMultiUpdates"), + Value: utils.Float64Pointer(eAcntVal)} + var reply string + if err := smgRPC.Call("ApierV2.SetBalance", attrSetBalance, &reply); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if reply != utils.OK { + t.Errorf("Received: %s", reply) + } + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { + t.Error(err) + } else if totalVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); totalVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", totalVal) } smgEv := SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.DATA, utils.ACCID: "123495", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", utils.TENANT: "cgrates.org", utils.REQTYPE: utils.META_PREPAID, utils.SETUP_TIME: "2016-01-05 18:30:53", utils.ANSWER_TIME: "2016-01-05 18:31:05", - utils.USAGE: "1048576", + utils.USAGE: "4096", // 3MB } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage int64 + if err := smgRPC.Call("SMGenericV2.InitiateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 4096 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49997788160.000000 //1054720 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 98304.000000 //96MB + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if dataVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); dataVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, dataVal) } aSessions := make([]*ActiveSession, 0) if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) + } else if len(aSessions) != 1 || + int64(aSessions[0].Usage) != 4096 { + t.Errorf("wrong active sessions: %d", int64(aSessions[0].Usage)) } - smgEv = SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.DATA, - utils.ACCID: "123495", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", - utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", - utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "20000", - } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.DATA, + utils.ACCID: "123495", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, + utils.CATEGORY: "data", + utils.TENANT: "cgrates.org", + utils.REQTYPE: utils.META_PREPAID, + utils.SETUP_TIME: "2016-01-05 18:30:53", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.LastUsed: "1024", + utils.USAGE: "4096", + utils.SessionTTLUsage: "2048", // will be charged on TTL + utils.SessionTTLLastUsed: "1024"} // will force last usage on timeout + if err := smgRPC.Call("SMGenericV2.UpdateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 4096 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49997767680.000000 // 20480 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 97280.000000 // 20480 + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) } - - time.Sleep(50 * time.Millisecond) - eAcntVal = 49997767680.000000 //0 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + time.Sleep(60 * time.Millisecond) // TTL will kick in + eAcntVal = 98304.000000 // 1MB is returned + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if dataVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); dataVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, dataVal) } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { + if err := smgRPC.Call("SMGenericV1.GetActiveSessions", + nil, &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err, aSessions) } } func TestSMGDataMultipleDataNoUsage(t *testing.T) { var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"} - eAcntVal := 49997767680.000000 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + acntAttrs := &utils.AttrGetAccount{Tenant: "cgrates.org", + Account: "TestSMGDataTTLExpMultiUpdates"} + eAcntVal := 102400.0 + attrSetBalance := utils.AttrSetBalance{ + Tenant: acntAttrs.Tenant, Account: acntAttrs.Account, + BalanceType: utils.DATA, + BalanceID: utils.StringPointer("TestSMGDataTTLExpMultiUpdates"), + Value: utils.Float64Pointer(eAcntVal)} + var reply string + if err := smgRPC.Call("ApierV2.SetBalance", attrSetBalance, &reply); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if reply != utils.OK { + t.Errorf("Received: %s", reply) + } + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { + t.Error(err) + } else if totalVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); totalVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", totalVal) } smgEv := SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.DATA, - utils.ACCID: "123496", + utils.ACCID: "123495", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", utils.TENANT: "cgrates.org", utils.REQTYPE: utils.META_PREPAID, - utils.SETUP_TIME: "2016-01-05 18:30:54", + utils.SETUP_TIME: "2016-01-05 18:30:53", utils.ANSWER_TIME: "2016-01-05 18:31:05", - utils.USAGE: "1048576", + utils.USAGE: "2048", } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage int64 + if err := smgRPC.Call("SMGenericV2.InitiateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 2048 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49996712960.000000 // 1054720 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 100352.000000 // 1054720 + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if dataVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); dataVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, dataVal) } aSessions := make([]*ActiveSession, 0) if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) + } else if len(aSessions) != 1 || + int64(aSessions[0].Usage) != 2048 { + t.Errorf("wrong active sessions usage: %d", int64(aSessions[0].Usage)) } smgEv = SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.DATA, - utils.ACCID: "123496", + utils.ACCID: "123495", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", utils.TENANT: "cgrates.org", utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "0", + utils.SETUP_TIME: "2016-01-05 18:30:53", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.SessionTTL: "1h", // cancel timeout since usage 0 will not update it + utils.USAGE: "1024", + utils.LastUsed: "1024", } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call("SMGenericV2.UpdateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 1024 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49996712960.000000 // 0 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 100352.000000 // 1054720 + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if dataVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); dataVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, dataVal) } + aSessions = make([]*ActiveSession, 0) if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) + } else if len(aSessions) != 1 || + int64(aSessions[0].Usage) != 2048 { + t.Errorf("wrong active sessions usage: %d", int64(aSessions[0].Usage)) } + smgEv = SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.DATA, - utils.ACCID: "123496", + utils.ACCID: "123495", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", utils.TENANT: "cgrates.org", utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", + utils.SETUP_TIME: "2016-01-05 18:30:53", + utils.ANSWER_TIME: "2016-01-05 18:31:05", + utils.SessionTTL: "1h", // cancel timeout since usage 0 will not update it + utils.USAGE: "0", utils.LastUsed: "0", } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call("SMGenericV2.UpdateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 0 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49996712960.000000 // 0 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 100352.000000 // 1054720 + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if dataVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); dataVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, dataVal) } + aSessions = make([]*ActiveSession, 0) if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) + } else if len(aSessions) != 1 || + int64(aSessions[0].Usage) != 1024 { + t.Errorf("wrong active sessions usage: %d", int64(aSessions[0].Usage)) } smgEv = SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.DATA, - utils.ACCID: "123496", + utils.ACCID: "123495", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", - utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", - utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "0", - } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { - t.Error(err) - } - if maxUsage != 1.048576e+06 { - t.Error("Bad max usage: ", maxUsage) - } - eAcntVal = 49996712960.000000 // 0 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) - } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { - t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) - } - smgEv = SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.DATA, - utils.ACCID: "123496", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", - utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", - utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "0", - } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { - t.Error(err) - } - if maxUsage != 1.048576e+06 { - t.Error("Bad max usage: ", maxUsage) - } - eAcntVal = 49996712960.000000 // 0 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) - } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { - t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) - } - smgEv = SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.DATA, - utils.ACCID: "123496", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", utils.TENANT: "cgrates.org", utils.REQTYPE: utils.META_PREPAID, + utils.SETUP_TIME: "2016-01-05 18:30:53", + utils.ANSWER_TIME: "2016-01-05 18:31:05", utils.LastUsed: "0", } var rpl string if err = smgRPC.Call("SMGenericV1.TerminateSession", smgEv, &rpl); err != nil || rpl != utils.OK { t.Error(err) } - eAcntVal = 49997767680.000000 // refunded - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 101376.000000 // refunded last 1MB reserved and unused + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { + if err := smgRPC.Call("SMGenericV1.GetActiveSessions", + nil, &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err, aSessions) } } -func TestSMGDataMultipleDataConstantUsage(t *testing.T) { +// TestSMGDataTTLUsageProtection makes sure that original TTL (50ms) +// limits the additional debit without overloading memory +func TestSMGDataTTLUsageProtection(t *testing.T) { var acnt *engine.Account - attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1010"} - eAcntVal := 49997767680.000000 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + acntAttrs := &utils.AttrGetAccount{Tenant: "cgrates.org", + Account: "TestSMGDataTTLUsageProtection"} + eAcntVal := 102400.0 + attrSetBalance := utils.AttrSetBalance{ + Tenant: acntAttrs.Tenant, Account: acntAttrs.Account, + BalanceType: utils.DATA, + BalanceID: utils.StringPointer("TestSMGDataTTLUsageProtection"), + Value: utils.Float64Pointer(eAcntVal)} + var reply string + if err := smgRPC.Call("ApierV2.SetBalance", attrSetBalance, &reply); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if reply != utils.OK { + t.Errorf("Received: %s", reply) + } + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { + t.Error(err) + } else if totalVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); totalVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", totalVal) } smgEv := SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", utils.TOR: utils.DATA, - utils.ACCID: "123497", + utils.ACCID: "123495", utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", + utils.ACCOUNT: acntAttrs.Account, + utils.SUBJECT: acntAttrs.Account, + utils.DESTINATION: utils.DATA, utils.CATEGORY: "data", utils.TENANT: "cgrates.org", utils.REQTYPE: utils.META_PREPAID, - utils.SETUP_TIME: "2016-01-05 18:30:55", + utils.SETUP_TIME: "2016-01-05 18:30:53", utils.ANSWER_TIME: "2016-01-05 18:31:05", - utils.USAGE: "1048576", + utils.USAGE: "2048", } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage int64 + if err := smgRPC.Call("SMGenericV2.InitiateSession", smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 1.048576e+06 { + if maxUsage != 2048 { t.Error("Bad max usage: ", maxUsage) } - eAcntVal = 49996712960.000000 // 1054720 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + eAcntVal = 100352.000000 // 1054720 + if err := smgRPC.Call("ApierV2.GetAccount", acntAttrs, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) + } else if dataVal := acnt.BalanceMap[utils.DATA].GetTotalValue(); dataVal != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, dataVal) } aSessions := make([]*ActiveSession, 0) if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1048576 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) + } else if len(aSessions) != 1 || + int64(aSessions[0].Usage) != 2048 { + t.Errorf("wrong active sessions usage: %d", int64(aSessions[0].Usage)) } - - smgEv = SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.DATA, - utils.ACCID: "123497", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", - utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", - utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "600", - } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { - t.Error(err) - } - if maxUsage != 1.048576e+06 { - t.Error("Bad max usage: ", maxUsage) - } - eAcntVal = 49996712960.000000 // 0 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) - } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { - t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1049176 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) - } - smgEv = SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.DATA, - utils.ACCID: "123497", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", - utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", - utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "600", - } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { - t.Error(err) - } - if maxUsage != 1.048576e+06 { - t.Error("Bad max usage: ", maxUsage) - } - eAcntVal = 49996712960.000000 // 0 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) - } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { - t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1049776 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) - } - smgEv = SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.DATA, - utils.ACCID: "123497", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", - utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", - utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "600", - } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { - t.Error(err) - } - if maxUsage != 1.048576e+06 { - t.Error("Bad max usage: ", maxUsage) - } - eAcntVal = 49996712960.000000 // 0 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) - } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { - t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1050376 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) - } - smgEv = SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.DATA, - utils.ACCID: "123497", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", - utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", - utils.REQTYPE: utils.META_PREPAID, - utils.USAGE: "1048576", - utils.LastUsed: "600", - } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { - t.Error(err) - } - if maxUsage != 1.048576e+06 { - t.Error("Bad max usage: ", maxUsage) - } - eAcntVal = 49996712960.000000 // 0 - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) - } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err != nil { - t.Error(err) - } else if len(aSessions) != 1 || aSessions[0].Usage.Seconds() != 1050976 { - t.Errorf("wrong active sessions: %f", aSessions[0].Usage.Seconds()) - } - smgEv = SMGenericEvent{ - utils.EVENT_NAME: "TEST_EVENT", - utils.TOR: utils.DATA, - utils.ACCID: "123497", - utils.DIRECTION: utils.OUT, - utils.ACCOUNT: "1010", - utils.SUBJECT: "1010", - utils.DESTINATION: "222", - utils.CATEGORY: "data", - utils.TENANT: "cgrates.org", - utils.REQTYPE: utils.META_PREPAID, - utils.LastUsed: "0", - } - var rpl string - if err = smgRPC.Call("SMGenericV1.TerminateSession", smgEv, &rpl); err != nil || rpl != utils.OK { - t.Error(err) - } - eAcntVal = 49997757440.000000 // 10240 (from the start) - if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { - t.Error(err) - } else if acnt.BalanceMap[utils.DATA].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.DATA].GetTotalValue()) - } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", nil, &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { - t.Error(err) + time.Sleep(60 * time.Millisecond) + if err := smgRPC.Call("SMGenericV1.GetActiveSessions", + nil, &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err, aSessions) } } diff --git a/sessionmanager/fsevent.go b/sessionmanager/fsevent.go index 389fec573..dfdca569f 100644 --- a/sessionmanager/fsevent.go +++ b/sessionmanager/fsevent.go @@ -347,7 +347,6 @@ func (fsev FSEvent) AsCDR(timezone string) *engine.CDR { storCdr.OriginHost = fsev.GetOriginatorIP(utils.META_DEFAULT) storCdr.Source = "FS_" + fsev.GetName() storCdr.RequestType = fsev.GetReqType(utils.META_DEFAULT) - storCdr.Direction = fsev.GetDirection(utils.META_DEFAULT) storCdr.Tenant = fsev.GetTenant(utils.META_DEFAULT) storCdr.Category = fsev.GetCategory(utils.META_DEFAULT) storCdr.Account = fsev.GetAccount(utils.META_DEFAULT) @@ -356,11 +355,8 @@ func (fsev FSEvent) AsCDR(timezone string) *engine.CDR { storCdr.SetupTime, _ = fsev.GetSetupTime(utils.META_DEFAULT, timezone) storCdr.AnswerTime, _ = fsev.GetAnswerTime(utils.META_DEFAULT, timezone) storCdr.Usage, _ = fsev.GetDuration(utils.META_DEFAULT) - storCdr.PDD, _ = fsev.GetPdd(utils.META_DEFAULT) storCdr.ExtraFields = fsev.GetExtraFields() storCdr.Cost = -1 - storCdr.Supplier = fsev.GetSupplier(utils.META_DEFAULT) - storCdr.DisconnectCause = fsev.GetDisconnectCause(utils.META_DEFAULT) return storCdr } diff --git a/sessionmanager/fsevent_test.go b/sessionmanager/fsevent_test.go index 1ef75c074..3dbdc4acf 100644 --- a/sessionmanager/fsevent_test.go +++ b/sessionmanager/fsevent_test.go @@ -371,7 +371,6 @@ func TestEventParseStatic(t *testing.T) { answerTime, _ := ev.GetAnswerTime("^2013-12-07 08:42:24", "") dur, _ := ev.GetDuration("^60s") if ev.GetReqType("^test") != "test" || - ev.GetDirection("^test") != "test" || ev.GetTenant("^test") != "test" || ev.GetCategory("^test") != "test" || ev.GetAccount("^test") != "test" || @@ -379,12 +378,9 @@ func TestEventParseStatic(t *testing.T) { ev.GetDestination("^test") != "test" || setupTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC) || answerTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC) || - dur != time.Duration(60)*time.Second || - ev.GetSupplier("^test") != "test" || - ev.GetDisconnectCause("^test") != "test" { + dur != time.Duration(60)*time.Second { t.Error("Values out of static not matching", ev.GetReqType("^test") != "test", - ev.GetDirection("^test") != "test", ev.GetTenant("^test") != "test", ev.GetCategory("^test") != "test", ev.GetAccount("^test") != "test", @@ -392,9 +388,7 @@ func TestEventParseStatic(t *testing.T) { ev.GetDestination("^test") != "test", setupTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), answerTime != time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC), - dur != time.Duration(60)*time.Second, - ev.GetSupplier("^test") != "test", - ev.GetDisconnectCause("^test") != "test") + dur != time.Duration(60)*time.Second) } } @@ -424,7 +418,6 @@ Task-Runtime: 1349437318` answerTime, _ := ev.GetAnswerTime("Event-Date-Local", "") dur, _ := ev.GetDuration("Event-Calling-Line-Number") if ev.GetReqType("FreeSWITCH-Hostname") != "h1.ip-switch.net" || - ev.GetDirection("FreeSWITCH-Hostname") != "*out" || ev.GetTenant("FreeSWITCH-Hostname") != "h1.ip-switch.net" || ev.GetCategory("FreeSWITCH-Hostname") != "h1.ip-switch.net" || ev.GetAccount("FreeSWITCH-Hostname") != "h1.ip-switch.net" || @@ -432,12 +425,9 @@ Task-Runtime: 1349437318` ev.GetDestination("FreeSWITCH-Hostname") != "h1.ip-switch.net" || setupTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC) || answerTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC) || - dur != time.Duration(65)*time.Second || - ev.GetSupplier("FreeSWITCH-Hostname") != "h1.ip-switch.net" || - ev.GetDisconnectCause("FreeSWITCH-Hostname") != "h1.ip-switch.net" { + dur != time.Duration(65)*time.Second { t.Error("Values out of static not matching", ev.GetReqType("FreeSWITCH-Hostname") != "h1.ip-switch.net", - ev.GetDirection("FreeSWITCH-Hostname") != "*out", ev.GetTenant("FreeSWITCH-Hostname") != "h1.ip-switch.net", ev.GetCategory("FreeSWITCH-Hostname") != "h1.ip-switch.net", ev.GetAccount("FreeSWITCH-Hostname") != "h1.ip-switch.net", @@ -445,9 +435,7 @@ Task-Runtime: 1349437318` ev.GetDestination("FreeSWITCH-Hostname") != "h1.ip-switch.net", setupTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC), answerTime != time.Date(2012, 10, 5, 13, 41, 38, 0, time.UTC), - dur != time.Duration(65)*time.Second, - ev.GetSupplier("FreeSWITCH-Hostname") != "h1.ip-switch.net", - ev.GetDisconnectCause("FreeSWITCH-Hostname") != "h1.ip-switch.net") + dur != time.Duration(65)*time.Second) } } @@ -482,7 +470,6 @@ func TestParseFsHangup(t *testing.T) { answerTime, _ := ev.GetAnswerTime(utils.META_DEFAULT, "") dur, _ := ev.GetDuration(utils.META_DEFAULT) if ev.GetReqType(utils.META_DEFAULT) != utils.META_PREPAID || - ev.GetDirection(utils.META_DEFAULT) != "*out" || ev.GetTenant(utils.META_DEFAULT) != "cgrates.org" || ev.GetCategory(utils.META_DEFAULT) != "call" || ev.GetAccount(utils.META_DEFAULT) != "1001" || @@ -490,12 +477,9 @@ func TestParseFsHangup(t *testing.T) { ev.GetDestination(utils.META_DEFAULT) != "1003" || setupTime.UTC() != time.Date(2015, 7, 7, 14, 52, 8, 0, time.UTC) || answerTime.UTC() != time.Date(2015, 7, 7, 14, 52, 8, 0, time.UTC) || - dur != time.Duration(66)*time.Second || - ev.GetSupplier(utils.META_DEFAULT) != "supplier1" || - ev.GetDisconnectCause(utils.META_DEFAULT) != "NORMAL_CLEARING" { + dur != time.Duration(66)*time.Second { t.Error("Default values not matching", ev.GetReqType(utils.META_DEFAULT) != utils.META_PREPAID, - ev.GetDirection(utils.META_DEFAULT) != "*out", ev.GetTenant(utils.META_DEFAULT) != "cgrates.org", ev.GetCategory(utils.META_DEFAULT) != "call", ev.GetAccount(utils.META_DEFAULT) != "1001", @@ -503,9 +487,7 @@ func TestParseFsHangup(t *testing.T) { ev.GetDestination(utils.META_DEFAULT) != "1003", setupTime.UTC() != time.Date(2015, 7, 7, 14, 52, 8, 0, time.UTC), answerTime.UTC() != time.Date(2015, 7, 7, 14, 52, 8, 0, time.UTC), - dur != time.Duration(66)*time.Second, - ev.GetSupplier(utils.META_DEFAULT) != "supplier1", - ev.GetDisconnectCause(utils.META_DEFAULT) != "NORMAL_CLEARING") + dur != time.Duration(66)*time.Second) } } @@ -635,11 +617,12 @@ func TestFsEvAsCDR(t *testing.T) { setupTime, _ := utils.ParseTimeDetectLayout("1436280728", "") aTime, _ := utils.ParseTimeDetectLayout("1436280728", "") eStoredCdr := &engine.CDR{CGRID: "164b0422fdc6a5117031b427439482c6a4f90e41", - ToR: utils.VOICE, OriginID: "e3133bf7-dcde-4daf-9663-9a79ffcef5ad", OriginHost: "10.0.3.15", Source: "FS_CHANNEL_HANGUP_COMPLETE", RequestType: utils.META_PREPAID, - Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", + ToR: utils.VOICE, OriginID: "e3133bf7-dcde-4daf-9663-9a79ffcef5ad", + OriginHost: "10.0.3.15", Source: "FS_CHANNEL_HANGUP_COMPLETE", RequestType: utils.META_PREPAID, + Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1003", SetupTime: setupTime, AnswerTime: aTime, - Usage: time.Duration(66) * time.Second, PDD: time.Duration(28) * time.Millisecond, Supplier: "supplier1", - DisconnectCause: "NORMAL_CLEARING", ExtraFields: make(map[string]string), Cost: -1} + Usage: time.Duration(66) * time.Second, + ExtraFields: make(map[string]string), Cost: -1} if storedCdr := ev.AsCDR(""); !reflect.DeepEqual(eStoredCdr, storedCdr) { t.Errorf("Expecting: %+v, received: %+v", eStoredCdr, storedCdr) } diff --git a/sessionmanager/fssessionmanager.go b/sessionmanager/fssessionmanager.go index 25fa269db..246095e2e 100644 --- a/sessionmanager/fssessionmanager.go +++ b/sessionmanager/fssessionmanager.go @@ -128,7 +128,7 @@ func (sm *FSSessionManager) setCgrLcr(ev engine.Event, connId string) error { } cd := &engine.CallDescriptor{ CgrID: ev.GetCgrId(sm.Timezone()), - Direction: ev.GetDirection(utils.META_DEFAULT), + Direction: utils.OUT, Tenant: ev.GetTenant(utils.META_DEFAULT), Category: ev.GetCategory(utils.META_DEFAULT), Subject: ev.GetSubject(utils.META_DEFAULT), diff --git a/sessionmanager/kamevent.go b/sessionmanager/kamevent.go index 47ebd22d7..6f9a40a5a 100644 --- a/sessionmanager/kamevent.go +++ b/sessionmanager/kamevent.go @@ -328,7 +328,6 @@ func (kev KamEvent) AsCDR(timezone string) *engine.CDR { storCdr.OriginHost = kev.GetOriginatorIP(utils.META_DEFAULT) storCdr.Source = kev.GetCdrSource() storCdr.RequestType = kev.GetReqType(utils.META_DEFAULT) - storCdr.Direction = kev.GetDirection(utils.META_DEFAULT) storCdr.Tenant = kev.GetTenant(utils.META_DEFAULT) storCdr.Category = kev.GetCategory(utils.META_DEFAULT) storCdr.Account = kev.GetAccount(utils.META_DEFAULT) @@ -337,9 +336,6 @@ func (kev KamEvent) AsCDR(timezone string) *engine.CDR { storCdr.SetupTime, _ = kev.GetSetupTime(utils.META_DEFAULT, timezone) storCdr.AnswerTime, _ = kev.GetAnswerTime(utils.META_DEFAULT, timezone) storCdr.Usage, _ = kev.GetDuration(utils.META_DEFAULT) - storCdr.PDD, _ = kev.GetPdd(utils.META_DEFAULT) - storCdr.Supplier = kev.GetSupplier(utils.META_DEFAULT) - storCdr.DisconnectCause = kev.GetDisconnectCause(utils.META_DEFAULT) storCdr.ExtraFields = kev.GetExtraFields() storCdr.Cost = -1 diff --git a/sessionmanager/osipsevent.go b/sessionmanager/osipsevent.go index 61e1d3552..d246d2ee3 100644 --- a/sessionmanager/osipsevent.go +++ b/sessionmanager/osipsevent.go @@ -272,7 +272,6 @@ func (osipsEv *OsipsEvent) AsCDR(timezone string) *engine.CDR { storCdr.OriginHost = osipsEv.GetOriginatorIP(utils.META_DEFAULT) storCdr.Source = "OSIPS_" + osipsEv.GetName() storCdr.RequestType = osipsEv.GetReqType(utils.META_DEFAULT) - storCdr.Direction = osipsEv.GetDirection(utils.META_DEFAULT) storCdr.Tenant = osipsEv.GetTenant(utils.META_DEFAULT) storCdr.Category = osipsEv.GetCategory(utils.META_DEFAULT) storCdr.Account = osipsEv.GetAccount(utils.META_DEFAULT) @@ -281,9 +280,6 @@ func (osipsEv *OsipsEvent) AsCDR(timezone string) *engine.CDR { storCdr.SetupTime, _ = osipsEv.GetSetupTime(utils.META_DEFAULT, timezone) storCdr.AnswerTime, _ = osipsEv.GetAnswerTime(utils.META_DEFAULT, timezone) storCdr.Usage, _ = osipsEv.GetDuration(utils.META_DEFAULT) - storCdr.PDD, _ = osipsEv.GetPdd(utils.META_DEFAULT) - storCdr.Supplier = osipsEv.GetSupplier(utils.META_DEFAULT) - storCdr.DisconnectCause = osipsEv.GetDisconnectCause(utils.META_DEFAULT) storCdr.ExtraFields = osipsEv.GetExtraFields() storCdr.Cost = -1 return storCdr diff --git a/sessionmanager/osipsevent_test.go b/sessionmanager/osipsevent_test.go index fc990d5c0..35964e5a7 100644 --- a/sessionmanager/osipsevent_test.go +++ b/sessionmanager/osipsevent_test.go @@ -142,12 +142,15 @@ func TestOsipsEventMissingParameter(t *testing.T) { func TestOsipsEventAsCDR(t *testing.T) { setupTime, _ := utils.ParseTimeDetectLayout("1406370492", "") answerTime, _ := utils.ParseTimeDetectLayout("1406370499", "") - eStoredCdr := &engine.CDR{CGRID: utils.Sha1("ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", setupTime.UTC().String()), - ToR: utils.VOICE, OriginID: "ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", OriginHost: "172.16.254.77", Source: "OSIPS_E_ACC_CDR", + eStoredCdr := &engine.CDR{ + CGRID: utils.Sha1("ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", setupTime.UTC().String()), + ToR: utils.VOICE, OriginID: "ODVkMDI2Mzc2MDY5N2EzODhjNTAzNTdlODhiZjRlYWQ", + OriginHost: "172.16.254.77", Source: "OSIPS_E_ACC_CDR", RequestType: utils.META_PREPAID, - Direction: utils.OUT, Tenant: "itsyscom.com", Category: "call", Account: "dan", Subject: "dan", + Tenant: "itsyscom.com", Category: "call", Account: "dan", Subject: "dan", Destination: "+4986517174963", SetupTime: setupTime, AnswerTime: answerTime, - Usage: time.Duration(20) * time.Second, PDD: time.Duration(3) * time.Second, Supplier: "supplier3", DisconnectCause: "200", ExtraFields: map[string]string{"extra1": "val1", "extra2": "val2"}, Cost: -1} + Usage: time.Duration(20) * time.Second, + ExtraFields: map[string]string{"extra1": "val1", "extra2": "val2"}, Cost: -1} if storedCdr := osipsEv.AsCDR(""); !reflect.DeepEqual(eStoredCdr, storedCdr) { t.Errorf("Expecting: %+v, received: %+v", eStoredCdr, storedCdr) } @@ -163,8 +166,8 @@ func TestOsipsAccMissedToStoredCdr(t *testing.T) { }} eStoredCdr := &engine.CDR{CGRID: utils.Sha1("27b1e6679ad0109b5d756e42bb4c9c28@0:0:0:0:0:0:0:0", setupTime.UTC().String()), ToR: utils.VOICE, OriginID: "27b1e6679ad0109b5d756e42bb4c9c28@0:0:0:0:0:0:0:0", OriginHost: "172.16.254.77", Source: "OSIPS_E_ACC_MISSED_EVENT", - RequestType: utils.META_PSEUDOPREPAID, Direction: utils.OUT, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Supplier: "supplier1", - DisconnectCause: "404", Destination: "1002", SetupTime: setupTime, AnswerTime: setupTime, + RequestType: utils.META_PSEUDOPREPAID, Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", + Destination: "1002", SetupTime: setupTime, AnswerTime: setupTime, Usage: time.Duration(0), ExtraFields: map[string]string{"extra1": "val1", "extra2": "val2"}, Cost: -1} if storedCdr := osipsEv.AsCDR(""); !reflect.DeepEqual(eStoredCdr, storedCdr) { t.Errorf("Expecting: %+v, received: %+v", eStoredCdr, storedCdr) diff --git a/sessionmanager/session.go b/sessionmanager/session.go index e4dbbbe34..14cadcbde 100644 --- a/sessionmanager/session.go +++ b/sessionmanager/session.go @@ -272,7 +272,6 @@ func (s *Session) AsActiveSessions() []*ActiveSession { sTime, _ := s.eventStart.GetSetupTime(utils.META_DEFAULT, s.sessionManager.Timezone()) aTime, _ := s.eventStart.GetAnswerTime(utils.META_DEFAULT, s.sessionManager.Timezone()) usage, _ := s.eventStart.GetDuration(utils.META_DEFAULT) - pdd, _ := s.eventStart.GetPdd(utils.META_DEFAULT) for _, sessionRun := range s.sessionRuns { aSession := &ActiveSession{ CGRID: s.eventStart.GetCgrId(s.sessionManager.Timezone()), @@ -281,7 +280,6 @@ func (s *Session) AsActiveSessions() []*ActiveSession { CdrHost: s.eventStart.GetOriginatorIP(utils.META_DEFAULT), CdrSource: "FS_" + s.eventStart.GetName(), ReqType: s.eventStart.GetReqType(utils.META_DEFAULT), - Direction: s.eventStart.GetDirection(utils.META_DEFAULT), Tenant: s.eventStart.GetTenant(utils.META_DEFAULT), Category: s.eventStart.GetCategory(utils.META_DEFAULT), Account: s.eventStart.GetAccount(utils.META_DEFAULT), @@ -290,9 +288,7 @@ func (s *Session) AsActiveSessions() []*ActiveSession { SetupTime: sTime, AnswerTime: aTime, Usage: usage, - Pdd: pdd, ExtraFields: s.eventStart.GetExtraFields(), - Supplier: s.eventStart.GetSupplier(utils.META_DEFAULT), SMId: "UNKNOWN", } if sessionRun.DerivedCharger != nil { @@ -326,18 +322,15 @@ type ActiveSession struct { OriginID string // represents the unique accounting id given by the telecom switch generating the CDR CdrHost string // represents the IP address of the host generating the CDR (automatically populated by the server) CdrSource string // formally identifies the source of the CDR (free form field) - ReqType string // matching the supported request types by the **CGRateS**, accepted values are hardcoded in the server . - Direction string // matching the supported direction identifiers of the CGRateS <*out> + ReqType string // matching the supported request types by the **CGRateS**, accepted values are hardcoded in the server Tenant string // tenant whom this record belongs Category string // free-form filter for this record, matching the category defined in rating profiles. Account string // account id (accounting subsystem) the record should be attached to Subject string // rating subject (rating subsystem) this record should be attached to Destination string // destination to be charged SetupTime time.Time // set-up time of the event. Supported formats: datetime RFC3339 compatible, SQL datetime (eg: MySQL), unix timestamp. - Pdd time.Duration // PDD value AnswerTime time.Time // answer time of the event. Supported formats: datetime RFC3339 compatible, SQL datetime (eg: MySQL), unix timestamp. Usage time.Duration // event usage information (eg: in case of tor=*voice this will represent the total duration of a call) - Supplier string // Supplier information when available ExtraFields map[string]string // Extra fields to be stored in CDR SMId string SMConnId string diff --git a/sessionmanager/smasterisk.go b/sessionmanager/smasterisk.go index f4edd0280..f96c2d2d8 100644 --- a/sessionmanager/smasterisk.go +++ b/sessionmanager/smasterisk.go @@ -25,6 +25,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/cgrates/aringo" "github.com/cgrates/cgrates/config" @@ -180,8 +181,8 @@ func (sma *SMAsterisk) handleChannelStateChange(ev *SMAsteriskEvent) { } return } - var maxUsage float64 - if err := sma.smg.Call("SMGenericV1.InitiateSession", *smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := sma.smg.Call(utils.SMGenericV2InitiateSession, *smgEv, &maxUsage); err != nil { utils.Logger.Err(fmt.Sprintf(" Error: %s when attempting to initiate session for channelID: %s", err.Error(), ev.ChannelID())) if err := sma.hangupChannel(ev.ChannelID()); err != nil { utils.Logger.Err(fmt.Sprintf(" Error: %s when attempting to disconnect channelID: %s", err.Error(), ev.ChannelID())) diff --git a/sessionmanager/smg_event.go b/sessionmanager/smg_event.go index 44a65af6a..009928976 100644 --- a/sessionmanager/smg_event.go +++ b/sessionmanager/smg_event.go @@ -43,7 +43,7 @@ func (ev SMGenericEvent) HasField(fieldName string) (hasField bool) { } func (self SMGenericEvent) GetName() string { - result, _ := utils.ConvertIfaceToString(self[utils.EVENT_NAME]) + result, _ := utils.CastFieldIfToString(self[utils.EVENT_NAME]) return result } @@ -51,7 +51,7 @@ func (self SMGenericEvent) GetTOR(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.TOR } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -65,7 +65,7 @@ func (self SMGenericEvent) GetOriginID(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.ACCID } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -77,7 +77,7 @@ func (self SMGenericEvent) GetDirection(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.DIRECTION } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -85,7 +85,7 @@ func (self SMGenericEvent) GetAccount(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.ACCOUNT } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -93,7 +93,7 @@ func (self SMGenericEvent) GetSubject(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.SUBJECT } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -101,7 +101,7 @@ func (self SMGenericEvent) GetDestination(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.DESTINATION } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -113,7 +113,7 @@ func (self SMGenericEvent) GetCategory(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.CATEGORY } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -121,7 +121,7 @@ func (self SMGenericEvent) GetTenant(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.TENANT } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -129,7 +129,7 @@ func (self SMGenericEvent) GetReqType(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.REQTYPE } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -137,7 +137,7 @@ func (self SMGenericEvent) GetSetupTime(fieldName, timezone string) (time.Time, if fieldName == utils.META_DEFAULT { fieldName = utils.SETUP_TIME } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return utils.ParseTimeDetectLayout(result, timezone) } @@ -145,7 +145,7 @@ func (self SMGenericEvent) GetAnswerTime(fieldName, timezone string) (time.Time, if fieldName == utils.META_DEFAULT { fieldName = utils.ANSWER_TIME } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return utils.ParseTimeDetectLayout(result, timezone) } @@ -170,8 +170,8 @@ func (self SMGenericEvent) GetUsage(fieldName string) (time.Duration, error) { if !hasVal { return nilDuration, utils.ErrNotFound } - result, _ := utils.ConvertIfaceToString(valIf) - return utils.ParseDurationWithSecs(result) + result, _ := utils.CastFieldIfToString(valIf) + return utils.ParseDurationWithNanosecs(result) } func (self SMGenericEvent) GetLastUsed(fieldName string) (time.Duration, error) { @@ -182,24 +182,27 @@ func (self SMGenericEvent) GetLastUsed(fieldName string) (time.Duration, error) if !hasVal { return nilDuration, utils.ErrNotFound } - result, _ := utils.ConvertIfaceToString(valStr) - return utils.ParseDurationWithSecs(result) + result, _ := utils.CastFieldIfToString(valStr) + return utils.ParseDurationWithNanosecs(result) } // GetSessionTTL retrieves SessionTTL setting out of SMGenericEvent -func (self SMGenericEvent) GetSessionTTL(sesTTL time.Duration, cfgSessionTTLMaxDelay *time.Duration) time.Duration { +func (self SMGenericEvent) GetSessionTTL(sesTTL time.Duration, + cfgSessionTTLMaxDelay *time.Duration) time.Duration { valIf, hasVal := self[utils.SessionTTL] if hasVal { - ttlStr, converted := utils.ConvertIfaceToString(valIf) + ttlStr, converted := utils.CastFieldIfToString(valIf) if !converted { - utils.Logger.Warning(fmt.Sprintf("SMGenericEvent, cannot convert SessionTTL, disabling functionality for event: <%s>", - self.GetCGRID(utils.META_DEFAULT))) + utils.Logger.Warning( + fmt.Sprintf("SMGenericEvent, cannot convert SessionTTL, disabling functionality for event: <%s>", + self.GetCGRID(utils.META_DEFAULT))) return time.Duration(0) } var err error - if sesTTL, err = utils.ParseDurationWithSecs(ttlStr); err != nil { - utils.Logger.Warning(fmt.Sprintf("SMGenericEvent, cannot parse SessionTTL, disabling functionality for event: <%s>", - self.GetCGRID(utils.META_DEFAULT))) + if sesTTL, err = utils.ParseDurationWithNanosecs(ttlStr); err != nil { + utils.Logger.Warning( + fmt.Sprintf("SMGenericEvent, cannot parse SessionTTL, disabling functionality for event: <%s>", + self.GetCGRID(utils.META_DEFAULT))) return time.Duration(0) } } @@ -209,13 +212,13 @@ func (self SMGenericEvent) GetSessionTTL(sesTTL time.Duration, cfgSessionTTLMaxD sessionTTLMaxDelay = cfgSessionTTLMaxDelay.Nanoseconds() / 1000000 // Milliseconds precision } if sesTTLMaxDelayIf, hasVal := self[utils.SessionTTLMaxDelay]; hasVal { - maxTTLDelaxStr, converted := utils.ConvertIfaceToString(sesTTLMaxDelayIf) + maxTTLDelaxStr, converted := utils.CastFieldIfToString(sesTTLMaxDelayIf) if !converted { utils.Logger.Warning(fmt.Sprintf("SMGenericEvent, cannot convert SessionTTLMaxDelay, disabling functionality for event: <%s>", self.GetCGRID(utils.META_DEFAULT))) return time.Duration(0) } - if maxTTLDelay, err := utils.ParseDurationWithSecs(maxTTLDelaxStr); err != nil { + if maxTTLDelay, err := utils.ParseDurationWithNanosecs(maxTTLDelaxStr); err != nil { utils.Logger.Warning(fmt.Sprintf("SMGenericEvent, cannot parse SessionTTLMaxDelay, disabling functionality for event: <%s>", self.GetCGRID(utils.META_DEFAULT))) return time.Duration(0) @@ -236,11 +239,11 @@ func (self SMGenericEvent) GetSessionTTLLastUsed() *time.Duration { if !hasVal { return nil } - ttlStr, converted := utils.ConvertIfaceToString(valIf) + ttlStr, converted := utils.CastFieldIfToString(valIf) if !converted { return nil } - if ttl, err := utils.ParseDurationWithSecs(ttlStr); err != nil { + if ttl, err := utils.ParseDurationWithNanosecs(ttlStr); err != nil { return nil } else { return &ttl @@ -253,11 +256,11 @@ func (self SMGenericEvent) GetSessionTTLUsage() *time.Duration { if !hasVal { return nil } - ttlStr, converted := utils.ConvertIfaceToString(valIf) + ttlStr, converted := utils.CastFieldIfToString(valIf) if !converted { return nil } - if ttl, err := utils.ParseDurationWithSecs(ttlStr); err != nil { + if ttl, err := utils.ParseDurationWithNanosecs(ttlStr); err != nil { return nil } else { return &ttl @@ -272,23 +275,23 @@ func (self SMGenericEvent) GetMaxUsage(fieldName string, cfgMaxUsage time.Durati if !hasIt { return cfgMaxUsage, nil } - result, _ := utils.ConvertIfaceToString(maxUsageStr) - return utils.ParseDurationWithSecs(result) + result, _ := utils.CastFieldIfToString(maxUsageStr) + return utils.ParseDurationWithNanosecs(result) } func (self SMGenericEvent) GetPdd(fieldName string) (time.Duration, error) { if fieldName == utils.META_DEFAULT { fieldName = utils.PDD } - result, _ := utils.ConvertIfaceToString(self[fieldName]) - return utils.ParseDurationWithSecs(result) + result, _ := utils.CastFieldIfToString(self[fieldName]) + return utils.ParseDurationWithNanosecs(result) } func (self SMGenericEvent) GetSupplier(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.SUPPLIER } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -296,7 +299,7 @@ func (self SMGenericEvent) GetDisconnectCause(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.DISCONNECT_CAUSE } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -304,7 +307,7 @@ func (self SMGenericEvent) GetOriginatorIP(fieldName string) string { if fieldName == utils.META_DEFAULT { fieldName = utils.CDRHOST } - result, _ := utils.ConvertIfaceToString(self[fieldName]) + result, _ := utils.CastFieldIfToString(self[fieldName]) return result } @@ -319,7 +322,7 @@ func (self SMGenericEvent) GetExtraFields() map[string]string { if utils.IsSliceMember(primaryFields, key) { continue } - result, _ := utils.ConvertIfaceToString(val) + result, _ := utils.CastFieldIfToString(val) extraFields[key] = result } return extraFields @@ -330,7 +333,7 @@ func (self SMGenericEvent) GetFieldAsString(fieldName string) (string, error) { if !hasVal { return "", utils.ErrNotFound } - result, converted := utils.ConvertIfaceToString(valIf) + result, converted := utils.CastFieldIfToString(valIf) if !converted { return "", utils.ErrNotConvertible } @@ -405,7 +408,7 @@ func (self SMGenericEvent) ParseEventValue(rsrFld *utils.RSRField, timezone stri case utils.COST: return rsrFld.ParseValue(strconv.FormatFloat(-1, 'f', -1, 64)) // Recommended to use FormatCost default: - strVal, _ := utils.ConvertIfaceToString(self[rsrFld.Id]) + strVal, _ := utils.CastFieldIfToString(self[rsrFld.Id]) val := rsrFld.ParseValue(strVal) return val } @@ -424,7 +427,6 @@ func (self SMGenericEvent) AsCDR(cfg *config.CGRConfig, timezone string) *engine storCdr.OriginHost = self.GetOriginatorIP(utils.META_DEFAULT) storCdr.Source = self.GetCdrSource() storCdr.RequestType = utils.FirstNonEmpty(self.GetReqType(utils.META_DEFAULT), storCdr.RequestType) - storCdr.Direction = utils.FirstNonEmpty(self.GetDirection(utils.META_DEFAULT), storCdr.Direction) storCdr.Tenant = utils.FirstNonEmpty(self.GetTenant(utils.META_DEFAULT), storCdr.Tenant) storCdr.Category = utils.FirstNonEmpty(self.GetCategory(utils.META_DEFAULT), storCdr.Category) storCdr.Account = self.GetAccount(utils.META_DEFAULT) @@ -433,9 +435,6 @@ func (self SMGenericEvent) AsCDR(cfg *config.CGRConfig, timezone string) *engine storCdr.SetupTime, _ = self.GetSetupTime(utils.META_DEFAULT, timezone) storCdr.AnswerTime, _ = self.GetAnswerTime(utils.META_DEFAULT, timezone) storCdr.Usage, _ = self.GetUsage(utils.META_DEFAULT) - storCdr.PDD, _ = self.GetPdd(utils.META_DEFAULT) - storCdr.Supplier = self.GetSupplier(utils.META_DEFAULT) - storCdr.DisconnectCause = self.GetDisconnectCause(utils.META_DEFAULT) storCdr.ExtraFields = self.GetExtraFields() storCdr.Cost = -1 return storCdr @@ -452,8 +451,8 @@ func (self SMGenericEvent) ComputeLcr() bool { } func (self SMGenericEvent) AsLcrRequest() *engine.LcrRequest { - setupTimeStr, _ := utils.ConvertIfaceToString(self[utils.SETUP_TIME]) - usageStr, _ := utils.ConvertIfaceToString(self[utils.USAGE]) + setupTimeStr, _ := utils.CastFieldIfToString(self[utils.SETUP_TIME]) + usageStr, _ := utils.CastFieldIfToString(self[utils.USAGE]) return &engine.LcrRequest{ Direction: self.GetDirection(utils.META_DEFAULT), Tenant: self.GetTenant(utils.META_DEFAULT), diff --git a/sessionmanager/smg_event_test.go b/sessionmanager/smg_event_test.go index 9b17c07c7..b164922aa 100644 --- a/sessionmanager/smg_event_test.go +++ b/sessionmanager/smg_event_test.go @@ -172,10 +172,12 @@ func TestSMGenericEventAsCDR(t *testing.T) { smGev["Extra1"] = "Value1" smGev["Extra2"] = 5 eStoredCdr := &engine.CDR{CGRID: "70c4d16dce41d1f2777b4e8442cff39cf87f5f19", - 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), - Usage: time.Duration(83) * time.Second, PDD: time.Duration(300) * time.Millisecond, Supplier: "supplier1", DisconnectCause: "NORMAL_DISCONNECT", + ToR: utils.SMS, OriginID: "12345", OriginHost: "10.0.3.15", Source: "SMG_TEST_EVENT", + RequestType: utils.META_PREPAID, + 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), + Usage: time.Duration(83) * time.Second, ExtraFields: map[string]string{"Extra1": "Value1", "Extra2": "5"}, Cost: -1} if storedCdr := smGev.AsCDR(cfg, "UTC"); !reflect.DeepEqual(eStoredCdr, storedCdr) { t.Errorf("Expecting: %+v, received: %+v", eStoredCdr, storedCdr) diff --git a/sessionmanager/smg_it_test.go b/sessionmanager/smg_it_test.go index f0f967061..af39cc12e 100644 --- a/sessionmanager/smg_it_test.go +++ b/sessionmanager/smg_it_test.go @@ -107,11 +107,12 @@ func TestSMGVoiceMonetaryRefund(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", utils.USAGE: "1m30s", } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := smgRPC.Call(utils.SMGenericV2InitiateSession, + smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 90 { + if maxUsage != time.Duration(90*time.Second) { t.Error("Bad max usage: ", maxUsage) } var acnt *engine.Account @@ -165,20 +166,22 @@ func TestSMGVoiceVoiceRefund(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", utils.USAGE: "1m30s", } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := smgRPC.Call(utils.SMGenericV2InitiateSession, + smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 90 { + if maxUsage != time.Duration(90*time.Second) { t.Error("Received: ", maxUsage) } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eAcntVal := 120.0 + eAcntVal := 120.0 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { - t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) + t.Errorf("Expected: %f, received: %f", + eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) } smgEv = SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", @@ -199,7 +202,7 @@ func TestSMGVoiceVoiceRefund(t *testing.T) { if err = smgRPC.Call("SMGenericV1.TerminateSession", smgEv, &rpl); err != nil || rpl != utils.OK { t.Error(err) } - eAcntVal = 150.0 + eAcntVal = 150.0 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { @@ -230,16 +233,16 @@ func TestSMGVoiceMixedRefund(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", utils.USAGE: "1m30s", } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := smgRPC.Call(utils.SMGenericV2InitiateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 90 { + if maxUsage != time.Duration(90*time.Second) { t.Error("Bad max usage: ", maxUsage) } //var acnt *engine.Account //attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"} - eVoiceVal := 90.0 + eVoiceVal := 90.0 * float64(time.Second) eMoneyVal := 8.7399 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) @@ -266,7 +269,7 @@ func TestSMGVoiceMixedRefund(t *testing.T) { if err = smgRPC.Call("SMGenericV1.TerminateSession", smgEv, &rpl); err != nil || rpl != utils.OK { t.Error(err) } - eVoiceVal = 90.0 + eVoiceVal = 90.0 * float64(time.Second) eMoneyVal = 8.79 if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) @@ -302,11 +305,11 @@ func TestSMGVoiceLastUsed(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", utils.USAGE: "2m", } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := smgRPC.Call(utils.SMGenericV2InitiateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } eAcntVal = 7.39002 @@ -329,10 +332,10 @@ func TestSMGVoiceLastUsed(t *testing.T) { utils.USAGE: "2m", utils.LastUsed: "1m30s", } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call(utils.SMGenericV2UpdateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } eAcntVal = 7.09005 @@ -355,10 +358,10 @@ func TestSMGVoiceLastUsed(t *testing.T) { utils.USAGE: "2m", utils.LastUsed: "2m30s", } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call(utils.SMGenericV2UpdateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } eAcntVal = 6.590100 @@ -416,11 +419,11 @@ func TestSMGVoiceLastUsedEnd(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", utils.USAGE: "2m", } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := smgRPC.Call(utils.SMGenericV2InitiateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } eAcntVal = 6.190020 @@ -443,10 +446,10 @@ func TestSMGVoiceLastUsedEnd(t *testing.T) { utils.USAGE: "2m", utils.LastUsed: "30s", } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call(utils.SMGenericV2UpdateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } eAcntVal = 6.090030 @@ -504,11 +507,11 @@ func TestSMGVoiceLastUsedNotFixed(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", utils.USAGE: "2m", } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := smgRPC.Call(utils.SMGenericV2InitiateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } eAcntVal = 5.190020 @@ -531,10 +534,10 @@ func TestSMGVoiceLastUsedNotFixed(t *testing.T) { utils.USAGE: "2m", utils.LastUsed: "13s", } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call(utils.SMGenericV2UpdateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } eAcntVal = 5.123360 @@ -592,16 +595,17 @@ func TestSMGVoiceSessionTTL(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", utils.USAGE: "2m", } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := smgRPC.Call(utils.SMGenericV2InitiateSession, smgEv, &maxUsage); err != nil { t.Error(err) } time.Sleep(time.Duration(30 * time.Millisecond)) - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } var aSessions []*ActiveSession - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", map[string]string{utils.MEDI_RUNID: utils.META_DEFAULT, utils.ACCID: "12360"}, &aSessions); err != nil { + if err := smgRPC.Call("SMGenericV1.GetActiveSessions", + map[string]string{utils.MEDI_RUNID: utils.META_DEFAULT, utils.ACCID: "12360"}, &aSessions); err != nil { t.Error(err) } else if len(aSessions) != 1 { t.Errorf("Unexpected number of sessions received: %+v", aSessions) @@ -628,11 +632,11 @@ func TestSMGVoiceSessionTTL(t *testing.T) { utils.USAGE: "2m", utils.LastUsed: "30s", } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call(utils.SMGenericV2UpdateSession, smgEv, &maxUsage); err != nil { t.Error(err) } time.Sleep(time.Duration(10 * time.Millisecond)) - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } if err := smgRPC.Call("SMGenericV1.GetActiveSessions", map[string]string{utils.MEDI_RUNID: utils.META_DEFAULT, utils.ACCID: "12360"}, &aSessions); err != nil { @@ -662,7 +666,7 @@ func TestSMGVoiceSessionTTL(t *testing.T) { } else if len(cdrs) != 1 { t.Error("Unexpected number of CDRs returned: ", len(cdrs)) } else { - if cdrs[0].Usage != "150.05" { + if cdrs[0].Usage != "2m30.05s" { t.Errorf("Unexpected CDR Usage received, cdr: %v %+v ", cdrs[0].Usage, cdrs[0]) } if cdrs[0].Cost != 1.5333 { @@ -673,7 +677,7 @@ func TestSMGVoiceSessionTTL(t *testing.T) { func TestSMGVoiceSessionTTLWithRelocate(t *testing.T) { attrSetBalance := utils.AttrSetBalance{Tenant: "cgrates.org", Account: "TestTTLWithRelocate", BalanceType: utils.VOICE, BalanceID: utils.StringPointer("TestTTLWithRelocate"), - Value: utils.Float64Pointer(300), RatingSubject: utils.StringPointer("*zero50ms")} + Value: utils.Float64Pointer(300 * float64(time.Second)), RatingSubject: utils.StringPointer("*zero50ms")} var reply string if err := smgRPC.Call("ApierV2.SetBalance", attrSetBalance, &reply); err != nil { t.Error(err) @@ -682,7 +686,7 @@ func TestSMGVoiceSessionTTLWithRelocate(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: attrSetBalance.Tenant, Account: attrSetBalance.Account} - eAcntVal := 300.0 + eAcntVal := 300.0 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { @@ -703,12 +707,12 @@ func TestSMGVoiceSessionTTLWithRelocate(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", utils.USAGE: "2m", } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := smgRPC.Call(utils.SMGenericV2InitiateSession, smgEv, &maxUsage); err != nil { t.Error(err) } time.Sleep(time.Duration(10) * time.Millisecond) - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } var aSessions []*ActiveSession @@ -720,7 +724,7 @@ func TestSMGVoiceSessionTTLWithRelocate(t *testing.T) { } else if aSessions[0].Usage != time.Duration(120)*time.Second { t.Errorf("Expecting 2m, received usage: %v", aSessions[0].Usage) } - eAcntVal = 180.0 + eAcntVal = 180.0 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { @@ -741,47 +745,54 @@ func TestSMGVoiceSessionTTLWithRelocate(t *testing.T) { utils.USAGE: "2m", utils.LastUsed: "30s", } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call(utils.SMGenericV2UpdateSession, + smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } time.Sleep(time.Duration(20) * time.Millisecond) - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", map[string]string{utils.MEDI_RUNID: utils.META_DEFAULT, - utils.ACCID: smgEv.GetOriginID(utils.META_DEFAULT)}, &aSessions); err != nil { + if err := smgRPC.Call("SMGenericV1.GetActiveSessions", + map[string]string{utils.MEDI_RUNID: utils.META_DEFAULT, + utils.ACCID: smgEv.GetOriginID(utils.META_DEFAULT)}, &aSessions); err != nil { t.Error(err) } else if len(aSessions) != 1 { t.Errorf("Unexpected number of sessions received: %+v", aSessions) } else if aSessions[0].Usage != time.Duration(150)*time.Second { t.Errorf("Expecting 2m30s, received usage: %v", aSessions[0].Usage) } - eAcntVal = 150.0 + eAcntVal = 150.0 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { - t.Errorf("Expecting: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) + t.Errorf("Expecting: %f, received: %f", + eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) } time.Sleep(100 * time.Millisecond) - eAcntVal = 149.95 + eAcntVal = 149.95 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { - t.Errorf("Expecting: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) + t.Errorf("Expecting: %f, received: %f", + eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) } - if err := smgRPC.Call("SMGenericV1.GetActiveSessions", map[string]string{utils.MEDI_RUNID: utils.META_DEFAULT, - utils.ACCID: smgEv.GetOriginID(utils.META_DEFAULT)}, &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { + if err := smgRPC.Call("SMGenericV1.GetActiveSessions", + map[string]string{utils.MEDI_RUNID: utils.META_DEFAULT, + utils.ACCID: smgEv.GetOriginID(utils.META_DEFAULT)}, + &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err, aSessions) } var cdrs []*engine.ExternalCDR - req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, DestinationPrefixes: []string{smgEv.GetDestination(utils.META_DEFAULT)}} + req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, + DestinationPrefixes: []string{smgEv.GetDestination(utils.META_DEFAULT)}} if err := smgRPC.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 != "150.05" { + if cdrs[0].Usage != "2m30.05s" { t.Errorf("Unexpected CDR Usage received, cdr: %v %+v ", cdrs[0].Usage, cdrs[0]) } } @@ -791,7 +802,7 @@ func TestSMGVoiceSessionTTLWithRelocate(t *testing.T) { func TestSMGVoiceRelocateWithOriginIDPrefix(t *testing.T) { attrSetBalance := utils.AttrSetBalance{Tenant: "cgrates.org", Account: "TestRelocateWithOriginIDPrefix", BalanceType: utils.VOICE, BalanceID: utils.StringPointer("TestRelocateWithOriginIDPrefix"), - Value: utils.Float64Pointer(300), RatingSubject: utils.StringPointer("*zero1s")} + Value: utils.Float64Pointer(300 * float64(time.Second)), RatingSubject: utils.StringPointer("*zero1s")} var reply string if err := smgRPC.Call("ApierV2.SetBalance", attrSetBalance, &reply); err != nil { t.Error(err) @@ -800,7 +811,7 @@ func TestSMGVoiceRelocateWithOriginIDPrefix(t *testing.T) { } var acnt *engine.Account attrs := &utils.AttrGetAccount{Tenant: attrSetBalance.Tenant, Account: attrSetBalance.Account} - eAcntVal := 300.0 + eAcntVal := 300.0 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { @@ -821,11 +832,11 @@ func TestSMGVoiceRelocateWithOriginIDPrefix(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", utils.USAGE: "2m", } - var maxUsage float64 - if err := smgRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := smgRPC.Call(utils.SMGenericV2InitiateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } time.Sleep(time.Duration(20) * time.Millisecond) @@ -838,7 +849,7 @@ func TestSMGVoiceRelocateWithOriginIDPrefix(t *testing.T) { } else if aSessions[0].Usage != time.Duration(120)*time.Second { t.Errorf("Expecting 2m, received usage: %v", aSessions[0].Usage) } - eAcntVal = 180.0 + eAcntVal = 180.0 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { @@ -859,10 +870,10 @@ func TestSMGVoiceRelocateWithOriginIDPrefix(t *testing.T) { utils.USAGE: "2m", utils.LastUsed: "30s", } - if err := smgRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + if err := smgRPC.Call(utils.SMGenericV2UpdateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 120 { + if maxUsage != time.Duration(120*time.Second) { t.Error("Bad max usage: ", maxUsage) } time.Sleep(time.Duration(20) * time.Millisecond) @@ -874,7 +885,7 @@ func TestSMGVoiceRelocateWithOriginIDPrefix(t *testing.T) { } else if aSessions[0].Usage != time.Duration(150)*time.Second { t.Errorf("Expecting 2m30s, received usage: %v", aSessions[0].Usage) } - eAcntVal = 150.0 + eAcntVal = 150.0 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { @@ -902,7 +913,7 @@ func TestSMGVoiceRelocateWithOriginIDPrefix(t *testing.T) { utils.ACCID: "12372-1"}, &aSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err, aSessions) } - eAcntVal = 240 + eAcntVal = 240 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { @@ -915,19 +926,62 @@ func TestSMGVoiceRelocateWithOriginIDPrefix(t *testing.T) { } time.Sleep(time.Duration(20) * time.Millisecond) var cdrs []*engine.ExternalCDR - req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, DestinationPrefixes: []string{smgEv.GetDestination(utils.META_DEFAULT)}} + req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, + DestinationPrefixes: []string{smgEv.GetDestination(utils.META_DEFAULT)}} if err := smgRPC.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 != "60" { + if cdrs[0].Usage != "1m0s" { t.Errorf("Unexpected CDR Usage received, cdr: %v %+v ", cdrs[0].Usage, cdrs[0]) } } - } +/* +func TestSMGDataDerivedChargingNoCredit(t *testing.T) { + var acnt *engine.Account + attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1011"} + eAcntVal := 50000.0 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) + } + smgEv := SMGenericEvent{ + utils.EVENT_NAME: "TEST_EVENT", + utils.TOR: utils.VOICE, + utils.ACCID: "1234967", + utils.DIRECTION: utils.OUT, + utils.ACCOUNT: "1011", + utils.SUBJECT: "1011", + utils.DESTINATION: "+49", + 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: "100", + } + var maxUsage float64 + if err := smgRPC.Call("SMGenericV2.InitiateSession", smgEv, &maxUsage); err != nil { + t.Error(err) + } + // the second derived charging run has no credit + + if maxUsage != 0 { + t.Error("Bad max usage: ", maxUsage) + } + eAcntVal = 50000.0 + if err := smgRPC.Call("ApierV2.GetAccount", attrs, &acnt); err != nil { + t.Error(err) + } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { + t.Errorf("Expected: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) + } +} +*/ + // ToDo: Add test for ChargeEvent with derived charging, one with debit possible and second not so we see refund and error.CreditInsufficient showing up. func TestSMGVoiceSessionStopCgrEngine(t *testing.T) { diff --git a/sessionmanager/smg_session.go b/sessionmanager/smg_session.go index fe5e0f49c..a4b74683b 100644 --- a/sessionmanager/smg_session.go +++ b/sessionmanager/smg_session.go @@ -22,7 +22,6 @@ import ( "errors" "fmt" "reflect" - "strconv" "sync" "time" @@ -153,7 +152,7 @@ func (self *SMGSession) debit(dur time.Duration, lastUsed *time.Duration) (time. // Send disconnect order to remote connection func (self *SMGSession) disconnectSession(reason string) error { - self.EventStart[utils.USAGE] = strconv.FormatFloat(self.TotalUsage.Seconds(), 'f', -1, 64) // Set the usage to total one debitted + self.EventStart[utils.USAGE] = self.TotalUsage.Nanoseconds() // Set the usage to total one debitted if self.clntConn == nil || reflect.ValueOf(self.clntConn).IsNil() { return errors.New("Calling SMGClientV1.DisconnectSession requires bidirectional JSON connection") } @@ -241,7 +240,7 @@ func (self *SMGSession) storeSMCost() error { RunID: self.RunID, OriginHost: self.EventStart.GetOriginatorIP(utils.META_DEFAULT), OriginID: self.EventStart.GetOriginID(utils.META_DEFAULT), - Usage: self.TotalUsage.Seconds(), + Usage: self.TotalUsage, CostDetails: self.EventCost, } var reply string @@ -261,7 +260,6 @@ func (self *SMGSession) AsActiveSession(timezone string) *ActiveSession { defer self.mux.RUnlock() sTime, _ := self.EventStart.GetSetupTime(utils.META_DEFAULT, timezone) aTime, _ := self.EventStart.GetAnswerTime(utils.META_DEFAULT, timezone) - pdd, _ := self.EventStart.GetPdd(utils.META_DEFAULT) aSession := &ActiveSession{ CGRID: self.CGRID, TOR: self.EventStart.GetTOR(utils.META_DEFAULT), @@ -270,7 +268,6 @@ func (self *SMGSession) AsActiveSession(timezone string) *ActiveSession { 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), @@ -279,9 +276,7 @@ func (self *SMGSession) AsActiveSession(timezone string) *ActiveSession { SetupTime: sTime, AnswerTime: aTime, Usage: self.TotalUsage, - Pdd: pdd, ExtraFields: self.EventStart.GetExtraFields(), - Supplier: self.EventStart.GetSupplier(utils.META_DEFAULT), SMId: "CGR-DA", } if self.CD != nil { diff --git a/sessionmanager/smgbirpc_it_test.go b/sessionmanager/smgbirpc_it_test.go index 160edc254..7e5a40854 100644 --- a/sessionmanager/smgbirpc_it_test.go +++ b/sessionmanager/smgbirpc_it_test.go @@ -39,7 +39,8 @@ var ( disconnectEvChan = make(chan *utils.AttrDisconnectSession) ) -func handleDisconnectSession(clnt *rpc2.Client, args *utils.AttrDisconnectSession, reply *string) error { +func handleDisconnectSession(clnt *rpc2.Client, + args *utils.AttrDisconnectSession, reply *string) error { disconnectEvChan <- args *reply = utils.OK return nil @@ -79,11 +80,14 @@ func TestSMGBiRPCStartEngine(t *testing.T) { // Connect rpc client to rater func TestSMGBiRPCApierRpcConn(t *testing.T) { + time.Sleep(time.Duration(1 * time.Second)) clntHandlers := map[string]interface{}{"SMGClientV1.DisconnectSession": handleDisconnectSession} - if _, err = utils.NewBiJSONrpcClient(smgBiRPCCfg.SmGenericConfig.ListenBijson, clntHandlers); err != nil { // First attempt is to make sure multiple clients are supported + if _, err = utils.NewBiJSONrpcClient(smgBiRPCCfg.SmGenericConfig.ListenBijson, + clntHandlers); err != nil { // First attempt is to make sure multiple clients are supported t.Fatal(err) } - if smgBiRPC, err = utils.NewBiJSONrpcClient(smgBiRPCCfg.SmGenericConfig.ListenBijson, clntHandlers); err != nil { + if smgBiRPC, err = utils.NewBiJSONrpcClient(smgBiRPCCfg.SmGenericConfig.ListenBijson, + clntHandlers); err != nil { t.Fatal(err) } if smgRPC, err = jsonrpc.Dial("tcp", smgBiRPCCfg.RPCJSONListen); err != nil { // Connect also simple RPC so we can check accounts and such @@ -103,8 +107,12 @@ func TestSMGBiRPCTPFromFolder(t *testing.T) { func TestSMGBiRPCSessionAutomaticDisconnects(t *testing.T) { // Create a balance with 1 second inside and rating increments of 1ms (to be compatible with debit interval) - attrSetBalance := utils.AttrSetBalance{Tenant: "cgrates.org", Account: "TestSMGBiRPCSessionAutomaticDisconnects", BalanceType: utils.VOICE, BalanceID: utils.StringPointer("TestSMGBiRPCSessionAutomaticDisconnects"), - Value: utils.Float64Pointer(0.01), RatingSubject: utils.StringPointer("*zero1ms")} + attrSetBalance := utils.AttrSetBalance{Tenant: "cgrates.org", + Account: "TestSMGBiRPCSessionAutomaticDisconnects", + BalanceType: utils.VOICE, + BalanceID: utils.StringPointer("TestSMGBiRPCSessionAutomaticDisconnects"), + Value: utils.Float64Pointer(0.01 * float64(time.Second)), + RatingSubject: utils.StringPointer("*zero1ms")} var reply string if err := smgRPC.Call("ApierV2.SetBalance", attrSetBalance, &reply); err != nil { t.Error(err) @@ -112,12 +120,14 @@ func TestSMGBiRPCSessionAutomaticDisconnects(t *testing.T) { t.Errorf("Received: %s", reply) } var acnt *engine.Account - attrGetAcnt := &utils.AttrGetAccount{Tenant: attrSetBalance.Tenant, Account: attrSetBalance.Account} - eAcntVal := 0.01 + attrGetAcnt := &utils.AttrGetAccount{Tenant: attrSetBalance.Tenant, + Account: attrSetBalance.Account} + eAcntVal := 0.01 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrGetAcnt, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { - t.Errorf("Expecting: %f, received: %f", eAcntVal, acnt.BalanceMap[utils.VOICE].GetTotalValue()) + t.Errorf("Expecting: %f, received: %f", eAcntVal, + acnt.BalanceMap[utils.VOICE].GetTotalValue()) } smgEv := SMGenericEvent{ utils.EVENT_NAME: "TEST_EVENT", @@ -134,7 +144,8 @@ func TestSMGBiRPCSessionAutomaticDisconnects(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", } var maxUsage float64 - if err := smgBiRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + if err := smgBiRPC.Call(utils.SMGenericV1InitiateSession, + smgEv, &maxUsage); err != nil { t.Error(err) } if maxUsage != -1 { @@ -165,15 +176,16 @@ func TestSMGBiRPCSessionAutomaticDisconnects(t *testing.T) { } else if reply != utils.OK { t.Errorf("Received reply: %s", reply) } - time.Sleep(time.Duration(10) * time.Millisecond) + time.Sleep(time.Duration(20) * time.Millisecond) var cdrs []*engine.ExternalCDR - req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, DestinationPrefixes: []string{smgEv.GetDestination(utils.META_DEFAULT)}} + req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, + DestinationPrefixes: []string{smgEv.GetDestination(utils.META_DEFAULT)}} if err := smgRPC.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 != "0.01" { + if cdrs[0].Usage != "10ms" { t.Errorf("Unexpected CDR Usage received, cdr: %v %+v ", cdrs[0].Usage, cdrs[0]) } else if cdrs[0].CostSource != utils.SESSION_MANAGER_SOURCE { t.Errorf("Unexpected CDR CostSource received, cdr: %v %+v ", cdrs[0].CostSource, cdrs[0]) @@ -183,7 +195,7 @@ func TestSMGBiRPCSessionAutomaticDisconnects(t *testing.T) { func TestSMGBiRPCSessionOriginatorTerminate(t *testing.T) { attrSetBalance := utils.AttrSetBalance{Tenant: "cgrates.org", Account: "TestSMGBiRPCSessionOriginatorTerminate", BalanceType: utils.VOICE, BalanceID: utils.StringPointer("TestSMGBiRPCSessionOriginatorTerminate"), - Value: utils.Float64Pointer(1), RatingSubject: utils.StringPointer("*zero1ms")} + Value: utils.Float64Pointer(1 * float64(time.Second)), RatingSubject: utils.StringPointer("*zero1ms")} var reply string if err := smgRPC.Call("ApierV2.SetBalance", attrSetBalance, &reply); err != nil { t.Error(err) @@ -192,7 +204,7 @@ func TestSMGBiRPCSessionOriginatorTerminate(t *testing.T) { } var acnt *engine.Account attrGetAcnt := &utils.AttrGetAccount{Tenant: attrSetBalance.Tenant, Account: attrSetBalance.Account} - eAcntVal := 1.0 + eAcntVal := 1.0 * float64(time.Second) if err := smgRPC.Call("ApierV2.GetAccount", attrGetAcnt, &acnt); err != nil { t.Error(err) } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() != eAcntVal { @@ -213,7 +225,8 @@ func TestSMGBiRPCSessionOriginatorTerminate(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", } var maxUsage float64 - if err := smgBiRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + if err := smgBiRPC.Call(utils.SMGenericV1InitiateSession, + smgEv, &maxUsage); err != nil { t.Error(err) } if maxUsage != -1 { @@ -222,13 +235,14 @@ func TestSMGBiRPCSessionOriginatorTerminate(t *testing.T) { time.Sleep(time.Duration(10 * time.Millisecond)) // Give time for debits to occur smgEv[utils.USAGE] = "7ms" var rpl string - if err = smgBiRPC.Call("SMGenericV1.TerminateSession", smgEv, &rpl); err != nil || rpl != utils.OK { + if err = smgBiRPC.Call("SMGenericV1.TerminateSession", + smgEv, &rpl); err != nil || rpl != utils.OK { t.Error(err) } time.Sleep(time.Duration(50 * time.Millisecond)) // Give time for debits to occur if err := smgRPC.Call("ApierV2.GetAccount", attrGetAcnt, &acnt); err != nil { t.Error(err) - } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() > 0.995 { // FixMe: should be not 0.93? + } else if acnt.BalanceMap[utils.VOICE].GetTotalValue() > 0.995*float64(time.Second) { // FixMe: should be not 0.93? t.Errorf("Balance value: %f", acnt.BalanceMap[utils.VOICE].GetTotalValue()) } if err := smgRPC.Call("SMGenericV1.ProcessCDR", smgEv, &reply); err != nil { @@ -244,7 +258,7 @@ func TestSMGBiRPCSessionOriginatorTerminate(t *testing.T) { } else if len(cdrs) != 1 { t.Error("Unexpected number of CDRs returned: ", len(cdrs)) } else { - if cdrs[0].Usage != "0.007" { + if cdrs[0].Usage != "7ms" { t.Errorf("Unexpected CDR Usage received, cdr: %v %+v ", cdrs[0].Usage, cdrs[0]) } else if cdrs[0].CostSource != utils.SESSION_MANAGER_SOURCE { t.Errorf("Unexpected CDR CostSource received, cdr: %v %+v ", cdrs[0].CostSource, cdrs[0]) diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index 911fbfb49..2f53e511e 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -361,22 +361,26 @@ func (smg *SMGeneric) getSessionIDsForPrefix(prefix string, passiveSessions bool } // sessionStart will handle a new session, pass the connectionId so we can communicate on disconnect request -func (smg *SMGeneric) sessionStart(evStart SMGenericEvent, clntConn rpcclient.RpcClientConnection) (err error) { +func (smg *SMGeneric) sessionStart(evStart SMGenericEvent, + clntConn rpcclient.RpcClientConnection) (err error) { cgrID := evStart.GetCGRID(utils.META_DEFAULT) _, err = guardian.Guardian.Guard(func() (interface{}, error) { // Lock it on CGRID level if pSS := smg.passiveToActive(cgrID); len(pSS) != 0 { return nil, nil // ToDo: handle here also debits } var sessionRuns []*engine.SessionRun - if err := smg.rals.Call("Responder.GetSessionRuns", evStart.AsCDR(smg.cgrCfg, smg.Timezone), &sessionRuns); err != nil { + if err := smg.rals.Call("Responder.GetSessionRuns", + evStart.AsCDR(smg.cgrCfg, smg.Timezone), &sessionRuns); err != nil { return nil, err } else if len(sessionRuns) == 0 { return nil, nil } stopDebitChan := make(chan struct{}) for _, sessionRun := range sessionRuns { - s := &SMGSession{CGRID: cgrID, EventStart: evStart, RunID: sessionRun.DerivedCharger.RunID, Timezone: smg.Timezone, - rals: smg.rals, cdrsrv: smg.cdrsrv, CD: sessionRun.CallDescriptor, clntConn: clntConn} + s := &SMGSession{CGRID: cgrID, EventStart: evStart, + RunID: sessionRun.DerivedCharger.RunID, Timezone: smg.Timezone, + rals: smg.rals, cdrsrv: smg.cdrsrv, + CD: sessionRun.CallDescriptor, clntConn: clntConn} smg.recordASession(s) //utils.Logger.Info(fmt.Sprintf(" Starting session: %s, runId: %s", sessionId, s.runId)) if smg.cgrCfg.SmGenericConfig.DebitInterval != 0 { @@ -530,7 +534,8 @@ func (smg *SMGeneric) setPassiveSessions(cgrID string, ss []*SMGSession) (err er if len(ss) == 0 { return } - for _, cacheKey := range []string{"InitiateSession" + cgrID, "UpdateSession" + cgrID, "TerminateSession" + cgrID} { + for _, cacheKey := range []string{"InitiateSession" + cgrID, + "UpdateSession" + cgrID, "TerminateSession" + cgrID} { if _, err := smg.responseCache.Get(cacheKey); err == nil { // Stop processing passive when there has been an update over active RPC smg.deletePassiveSessions(cgrID) return ErrActiveSession @@ -742,7 +747,9 @@ func (smg *SMGeneric) UpdateSession(gev SMGenericEvent, clnt rpcclient.RpcClient smg.replicateSessionsWithID(initialCGRID, false, smg.smgReplConns) } smg.resetTerminatorTimer(cgrID, - gev.GetSessionTTL(smg.cgrCfg.SmGenericConfig.SessionTTL, smg.cgrCfg.SmGenericConfig.SessionTTLMaxDelay), + gev.GetSessionTTL( + smg.cgrCfg.SmGenericConfig.SessionTTL, + smg.cgrCfg.SmGenericConfig.SessionTTLMaxDelay), gev.GetSessionTTLLastUsed(), gev.GetSessionTTLUsage()) var lastUsed *time.Duration var evLastUsed time.Duration @@ -751,7 +758,8 @@ func (smg *SMGeneric) UpdateSession(gev SMGenericEvent, clnt rpcclient.RpcClient } else if err != utils.ErrNotFound { return } - if maxUsage, err = gev.GetMaxUsage(utils.META_DEFAULT, smg.cgrCfg.SmGenericConfig.MaxCallDuration); err != nil { + if maxUsage, err = gev.GetMaxUsage(utils.META_DEFAULT, + smg.cgrCfg.SmGenericConfig.MaxCallDuration); err != nil { if err == utils.ErrNotFound { err = utils.ErrMandatoryIeMissing } @@ -961,7 +969,8 @@ func (smg *SMGeneric) ProcessCDR(gev SMGenericEvent) (err error) { } defer smg.responseCache.Cache(cacheKey, &cache.CacheItem{Err: err}) var reply string - if err = smg.cdrsrv.Call("CdrsV1.ProcessCDR", gev.AsCDR(smg.cgrCfg, smg.Timezone), &reply); err != nil { + if err = smg.cdrsrv.Call("CdrsV1.ProcessCDR", + gev.AsCDR(smg.cgrCfg, smg.Timezone), &reply); err != nil { return } return @@ -985,24 +994,28 @@ func (smg *SMGeneric) Call(serviceMethod string, args interface{}, reply interfa } // Part of utils.BiRPCServer to help internal connections do calls over rpcclient.RpcClientConnection interface -func (smg *SMGeneric) CallBiRPC(clnt rpcclient.RpcClientConnection, serviceMethod string, args interface{}, reply interface{}) error { +func (smg *SMGeneric) CallBiRPC(clnt rpcclient.RpcClientConnection, + serviceMethod string, args interface{}, reply interface{}) error { parts := strings.Split(serviceMethod, ".") if len(parts) != 2 { return rpcclient.ErrUnsupporteServiceMethod } // get method BiRPCV1.Method - method := reflect.ValueOf(smg).MethodByName("BiRPC" + parts[0][len(parts[0])-2:] + parts[1]) // Inherit the version V1 in the method name and add prefix + method := reflect.ValueOf(smg).MethodByName( + "BiRPC" + parts[0][len(parts[0])-2:] + parts[1]) // Inherit the version V1 in the method name and add prefix if !method.IsValid() { return rpcclient.ErrUnsupporteServiceMethod } // construct the params var clntVal reflect.Value if clnt == nil { - clntVal = reflect.New(reflect.TypeOf(new(utils.BiRPCInternalClient))).Elem() // Kinda cheat since we make up a type here + clntVal = reflect.New( + reflect.TypeOf(new(utils.BiRPCInternalClient))).Elem() // Kinda cheat since we make up a type here } else { clntVal = reflect.ValueOf(clnt) } - params := []reflect.Value{clntVal, reflect.ValueOf(args), reflect.ValueOf(reply)} + params := []reflect.Value{clntVal, reflect.ValueOf(args), + reflect.ValueOf(reply)} ret := method.Call(params) if len(ret) != 1 { return utils.ErrServerError @@ -1017,7 +1030,8 @@ func (smg *SMGeneric) CallBiRPC(clnt rpcclient.RpcClientConnection, serviceMetho return err } -func (smg *SMGeneric) BiRPCV1GetMaxUsage(clnt rpcclient.RpcClientConnection, ev SMGenericEvent, maxUsage *float64) error { +func (smg *SMGeneric) BiRPCV1GetMaxUsage(clnt rpcclient.RpcClientConnection, + ev SMGenericEvent, maxUsage *float64) error { maxUsageDur, err := smg.GetMaxUsage(ev) if err != nil { return utils.NewErrServerError(err) @@ -1124,6 +1138,16 @@ func (smg *SMGeneric) BiRPCV1ChargeEvent(clnt rpcclient.RpcClientConnection, ev return nil } +// Called on individual Events (eg SMS) +func (smg *SMGeneric) BiRPCV2ChargeEvent(clnt rpcclient.RpcClientConnection, ev SMGenericEvent, maxUsage *time.Duration) error { + if minMaxUsage, err := smg.ChargeEvent(ev); err != nil { + return utils.NewErrServerError(err) + } else { + *maxUsage = minMaxUsage + } + return nil +} + // Called on session end, should send the CDR to CDRS func (smg *SMGeneric) BiRPCV1ProcessCDR(clnt rpcclient.RpcClientConnection, ev SMGenericEvent, reply *string) error { if err := smg.ProcessCDR(ev); err != nil { diff --git a/sessionmanager/smgreplc_it_test.go b/sessionmanager/smgreplc_it_test.go index aa0595fda..21c664d37 100644 --- a/sessionmanager/smgreplc_it_test.go +++ b/sessionmanager/smgreplc_it_test.go @@ -91,7 +91,8 @@ func TestSMGRplcTPFromFolder(t *testing.T) { func TestSMGRplcInitiate(t *testing.T) { var pSessions []*ActiveSession - if err := smgRplcSlvRPC.Call("SMGenericV1.GetPassiveSessions", nil, &pSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { + if err := smgRplcSlvRPC.Call("SMGenericV1.GetPassiveSessions", + nil, &pSessions); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Error(err) } smgEv := SMGenericEvent{ @@ -109,18 +110,20 @@ func TestSMGRplcInitiate(t *testing.T) { utils.ANSWER_TIME: "2016-01-05 18:31:05", utils.USAGE: "1m30s", } - var maxUsage float64 - if err := smgRplcMstrRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err == nil && err.Error() != rpcclient.ErrSessionNotFound.Error() { // Update should return rpcclient.ErrSessionNotFound + var maxUsage time.Duration + if err := smgRplcMstrRPC.Call(utils.SMGenericV2UpdateSession, + smgEv, &maxUsage); err == nil && + err.Error() != rpcclient.ErrSessionNotFound.Error() { // Update should return rpcclient.ErrSessionNotFound t.Error(err) } var reply string if err := smgRplcMstrRPC.Call("SMGenericV1.TerminateSession", smgEv, &reply); err == nil && err.Error() != rpcclient.ErrSessionNotFound.Error() { // Update should return rpcclient.ErrSessionNotFound t.Error(err) } - if err := smgRplcMstrRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + if err := smgRplcMstrRPC.Call(utils.SMGenericV2InitiateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 90 { + if maxUsage != time.Duration(90*time.Second) { t.Error("Bad max usage: ", maxUsage) } time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Wait for the sessions to be populated @@ -148,10 +151,11 @@ func TestSMGRplcUpdate(t *testing.T) { utils.ACCID: "123451", utils.USAGE: "1m", } - var maxUsage float64 - if err := smgRplcSlvRPC.Call("SMGenericV1.UpdateSession", smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := smgRplcSlvRPC.Call(utils.SMGenericV2UpdateSession, + smgEv, &maxUsage); err != nil { t.Error(err) - } else if maxUsage != 60 { + } else if maxUsage != time.Duration(time.Minute) { t.Error("Bad max usage: ", maxUsage) } time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Wait for the sessions to be populated @@ -252,11 +256,11 @@ func TestSMGRplcManualReplicate(t *testing.T) { utils.USAGE: "1m30s", } for _, smgEv := range []SMGenericEvent{smgEv1, smgEv2} { - var maxUsage float64 - if err := smgRplcMstrRPC.Call("SMGenericV1.InitiateSession", smgEv, &maxUsage); err != nil { + var maxUsage time.Duration + if err := smgRplcMstrRPC.Call(utils.SMGenericV2InitiateSession, smgEv, &maxUsage); err != nil { t.Error(err) } - if maxUsage != 90 { + if maxUsage != time.Duration(90*time.Second) { t.Error("Bad max usage: ", maxUsage) } } diff --git a/utils/apitpdata.go b/utils/apitpdata.go index bd2690a91..b35034b5e 100755 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -116,13 +116,13 @@ type RateSlot struct { // Used to set the durations we need out of strings func (self *RateSlot) SetDurations() error { var err error - if self.rateUnitDur, err = ParseDurationWithSecs(self.RateUnit); err != nil { + if self.rateUnitDur, err = ParseDurationWithNanosecs(self.RateUnit); err != nil { return err } - if self.rateIncrementDur, err = ParseDurationWithSecs(self.RateIncrement); err != nil { + if self.rateIncrementDur, err = ParseDurationWithNanosecs(self.RateIncrement); err != nil { return err } - if self.groupIntervalStartDur, err = ParseDurationWithSecs(self.GroupIntervalStart); err != nil { + if self.groupIntervalStartDur, err = ParseDurationWithNanosecs(self.GroupIntervalStart); err != nil { return err } return nil @@ -738,7 +738,6 @@ type AttrExpFileCdrs struct { CdrHosts []string // If provided, it will filter cdrhost CdrSources []string // If provided, it will filter cdrsource ReqTypes []string // If provided, it will fiter reqtype - Directions []string // If provided, it will fiter direction Tenants []string // If provided, it will filter tenant Categories []string // If provided, it will filter çategory Accounts []string // If provided, it will filter account @@ -763,7 +762,6 @@ func (self *AttrExpFileCdrs) AsCDRsFilter(timezone string) (*CDRsFilter, error) OriginHosts: self.CdrHosts, Sources: self.CdrSources, RequestTypes: self.ReqTypes, - Directions: self.Directions, Tenants: self.Tenants, Categories: self.Categories, Accounts: self.Accounts, @@ -837,7 +835,6 @@ func (self *AttrGetCdrs) AsCDRsFilter(timezone string) (*CDRsFilter, error) { OriginHosts: self.CdrHosts, Sources: self.CdrSources, RequestTypes: self.ReqTypes, - Directions: self.Directions, Tenants: self.Tenants, Categories: self.Categories, Accounts: self.Accounts, @@ -881,7 +878,6 @@ type AttrRateCdrs struct { CdrHosts []string // If provided, it will filter cdrhost CdrSources []string // If provided, it will filter cdrsource ReqTypes []string // If provided, it will fiter reqtype - Directions []string // If provided, it will fiter direction Tenants []string // If provided, it will filter tenant Categories []string // If provided, it will filter çategory Accounts []string // If provided, it will filter account @@ -904,7 +900,6 @@ func (attrRateCDRs *AttrRateCdrs) AsCDRsFilter(timezone string) (*CDRsFilter, er Sources: attrRateCDRs.CdrSources, ToRs: attrRateCDRs.TORs, RequestTypes: attrRateCDRs.ReqTypes, - Directions: attrRateCDRs.Directions, Tenants: attrRateCDRs.Tenants, Categories: attrRateCDRs.Categories, Accounts: attrRateCDRs.Accounts, @@ -1015,8 +1010,6 @@ type CDRsFilter struct { NotToRs []string // Filter specific TORs out RequestTypes []string // If provided, it will fiter reqtype NotRequestTypes []string // Filter out specific request types - Directions []string // If provided, it will fiter direction - NotDirections []string // Filter out specific directions Tenants []string // If provided, it will filter tenant NotTenants []string // If provided, it will filter tenant Categories []string // If provided, it will filter çategory @@ -1027,10 +1020,6 @@ type CDRsFilter struct { NotSubjects []string // Filter out specific subjects DestinationPrefixes []string // If provided, it will filter on destination prefix NotDestinationPrefixes []string // Filter out specific destination prefixes - Suppliers []string // If provided, it will filter the supplier - NotSuppliers []string // Filter out specific suppliers - DisconnectCauses []string // Filter for disconnect Cause - NotDisconnectCauses []string // Filter out specific disconnect causes Costs []float64 // Query based on costs specified NotCosts []float64 // Filter out specific costs out from result ExtraFields map[string]string // Query based on extra fields content @@ -1047,8 +1036,6 @@ type CDRsFilter struct { UpdatedAtEnd *time.Time // End interval, smaller than MinUsage string // Start of the usage interval (>=) MaxUsage string // End of the usage interval (<) - MinPDD string // Start of the pdd interval (>=) - MaxPDD string // End of the pdd interval (<) MinCost *float64 // Start of the cost interval (>=) MaxCost *float64 // End of the usage interval (<) Unscoped bool // Include soft-deleted records in results @@ -1071,8 +1058,6 @@ type RPCCDRsFilter struct { NotToRs []string // Filter specific TORs out RequestTypes []string // If provided, it will fiter reqtype NotRequestTypes []string // Filter out specific request types - Directions []string // If provided, it will fiter direction - NotDirections []string // Filter out specific directions Tenants []string // If provided, it will filter tenant NotTenants []string // If provided, it will filter tenant Categories []string // If provided, it will filter çategory @@ -1083,10 +1068,6 @@ type RPCCDRsFilter struct { NotSubjects []string // Filter out specific subjects DestinationPrefixes []string // If provided, it will filter on destination prefix NotDestinationPrefixes []string // Filter out specific destination prefixes - Suppliers []string // If provided, it will filter the supplier - NotSuppliers []string // Filter out specific suppliers - DisconnectCauses []string // Filter for disconnect Cause - NotDisconnectCauses []string // Filter out specific disconnect causes Costs []float64 // Query based on costs specified NotCosts []float64 // Filter out specific costs out from result ExtraFields map[string]string // Query based on extra fields content @@ -1103,8 +1084,6 @@ type RPCCDRsFilter struct { UpdatedAtEnd string // End interval, smaller than MinUsage string // Start of the usage interval (>=) MaxUsage string // End of the usage interval (<) - MinPDD string // Start of the pdd interval (>=) - MaxPDD string // End of the pdd interval (<) MinCost *float64 // Start of the cost interval (>=) MaxCost *float64 // End of the usage interval (<) Paginator // Add pagination @@ -1124,8 +1103,6 @@ func (self *RPCCDRsFilter) AsCDRsFilter(timezone string) (*CDRsFilter, error) { NotSources: self.NotSources, RequestTypes: self.RequestTypes, NotRequestTypes: self.NotRequestTypes, - Directions: self.Directions, - NotDirections: self.NotDirections, Tenants: self.Tenants, NotTenants: self.NotTenants, Categories: self.Categories, @@ -1136,23 +1113,17 @@ func (self *RPCCDRsFilter) AsCDRsFilter(timezone string) (*CDRsFilter, error) { NotSubjects: self.NotSubjects, DestinationPrefixes: self.DestinationPrefixes, NotDestinationPrefixes: self.NotDestinationPrefixes, - Suppliers: self.Suppliers, - NotSuppliers: self.NotSuppliers, - DisconnectCauses: self.DisconnectCauses, - NotDisconnectCauses: self.NotDisconnectCauses, - Costs: self.Costs, - NotCosts: self.NotCosts, - ExtraFields: self.ExtraFields, - NotExtraFields: self.NotExtraFields, - OrderIDStart: self.OrderIDStart, - OrderIDEnd: self.OrderIDEnd, - MinUsage: self.MinUsage, - MaxUsage: self.MaxUsage, - MinPDD: self.MinPDD, - MaxPDD: self.MaxPDD, - MinCost: self.MinCost, - MaxCost: self.MaxCost, - Paginator: self.Paginator, + Costs: self.Costs, + NotCosts: self.NotCosts, + ExtraFields: self.ExtraFields, + NotExtraFields: self.NotExtraFields, + OrderIDStart: self.OrderIDStart, + OrderIDEnd: self.OrderIDEnd, + MinUsage: self.MinUsage, + MaxUsage: self.MaxUsage, + MinCost: self.MinCost, + MaxCost: self.MaxCost, + Paginator: self.Paginator, } if len(self.SetupTimeStart) != 0 { if sTimeStart, err := ParseTimeDetectLayout(self.SetupTimeStart, timezone); err != nil { diff --git a/utils/consts.go b/utils/consts.go index c08a0b573..66b1a396d 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -113,9 +113,10 @@ const ( TBLTPStats = "tp_stats" TBLTPThresholds = "tp_thresholds" TBLTPFilters = "tp_filters" - TBLTPLcr = "tp_lcr" - TBLSMCosts = "sm_costs" - TBLCDRs = "cdrs" + SMCostsTBL = "sm_costs" + CDRsTBL = "cdrs" + TBLTPLCRProfiles = "tp_lcr" + TBLTPLcr = "tp_lcrs" TBLVersions = "versions" TIMINGS_CSV = "Timings.csv" DESTINATIONS_CSV = "Destinations.csv" @@ -409,7 +410,6 @@ const ( UnsupportedMigrationTask = "unsupported migration task" NoStorDBConnection = "not connected to StorDB" UndefinedVersion = "undefined version" - MetaSetVersions = "*set_versions" UnsupportedDB = "unsupported database" ACCOUNT_SUMMARY = "AccountSummary" TxtSuffix = ".txt" @@ -434,13 +434,6 @@ const ( MetaEveryMinute = "*every_minute" MetaHourly = "*hourly" ID = "ID" - MetaASR = "*asr" - MetaACD = "*acd" - MetaTCD = "*tcd" - MetaACC = "*acc" - MetaTCC = "*tcc" - MetaPDD = "*pdd" - MetaDDC = "*ddc" CacheDestinations = "destinations" CacheReverseDestinations = "reverse_destinations" CacheRatingPlans = "rating_plans" @@ -491,6 +484,7 @@ const ( StatUpdate = "StatUpdate" ResourceUpdate = "ResourceUpdate" CDR = "CDR" + CDRs = "CDRs" ExpiryTime = "ExpiryTime" AllowNegative = "AllowNegative" Disabled = "Disabled" @@ -577,6 +571,26 @@ const ( MetaTpRatingProfile = "*TpRatingProfile" MetaStorDB = "*StorDB" MetaDataDB = "*DataDB" + SMGenericV2UpdateSession = "SMGenericV2.UpdateSession" + SMGenericV2InitiateSession = "SMGenericV2.InitiateSession" + SMGenericV1UpdateSession = "SMGenericV1.UpdateSession" + SMGenericV1InitiateSession = "SMGenericV1.InitiateSession" +) + +//Meta +const ( + MetaASR = "*asr" + MetaACD = "*acd" + MetaTCD = "*tcd" + MetaACC = "*acc" + MetaTCC = "*tcc" + MetaPDD = "*pdd" + MetaDDC = "*ddc" +) + +//Migrator Metas +const ( + MetaSetVersions = "*set_versions" ) func buildCacheInstRevPrefixes() { diff --git a/utils/coreutils.go b/utils/coreutils.go index 48aaba312..7599de56e 100644 --- a/utils/coreutils.go +++ b/utils/coreutils.go @@ -289,6 +289,17 @@ func ParseDurationWithSecs(durStr string) (d time.Duration, err error) { return time.ParseDuration(durStr) } +// Parses duration, considers s as time unit if not provided, seconds as float to specify subunits +func ParseDurationWithNanosecs(durStr string) (d time.Duration, err error) { + if durStr == "" { + return + } + if _, err = strconv.ParseFloat(durStr, 64); err == nil { // Seconds format considered + durStr += "ns" + } + return time.ParseDuration(durStr) +} + func AccountKey(tenant, account string) string { return fmt.Sprintf("%s:%s", tenant, account) } @@ -304,10 +315,15 @@ func MinDuration(d1, d2 time.Duration) time.Duration { // ParseZeroRatingSubject will parse the subject in the balance // returns duration if able to extract it from subject // returns error if not able to parse duration (ie: if ratingSubject is standard one) -func ParseZeroRatingSubject(rateSubj string) (time.Duration, error) { +func ParseZeroRatingSubject(tor, rateSubj string) (time.Duration, error) { rateSubj = strings.TrimSpace(rateSubj) if rateSubj == "" || rateSubj == ANY { - rateSubj = ZERO_RATING_SUBJECT_PREFIX + "1s" + switch tor { + case VOICE: + rateSubj = ZERO_RATING_SUBJECT_PREFIX + "1s" + default: + rateSubj = ZERO_RATING_SUBJECT_PREFIX + "1ns" + } } if !strings.HasPrefix(rateSubj, ZERO_RATING_SUBJECT_PREFIX) { return 0, errors.New("malformed rating subject: " + rateSubj) @@ -479,34 +495,6 @@ func LogFull(v interface{}) { log.Print(ToIJSON(v)) } -// Used to convert from generic interface type towards string value -func ConvertIfaceToString(fld interface{}) (string, bool) { - var strVal string - var converted bool - switch fld.(type) { - case string: - strVal = fld.(string) - converted = true - case int: - strVal = strconv.Itoa(fld.(int)) - converted = true - case int64: - strVal = strconv.FormatInt(fld.(int64), 10) - converted = true - case bool: - strVal = strconv.FormatBool(fld.(bool)) - converted = true - case []uint8: - var byteVal []byte - if byteVal, converted = fld.([]byte); converted { - strVal = string(byteVal) - } - default: // Maybe we are lucky and the value converts to string - strVal, converted = fld.(string) - } - return strVal, converted -} - // Simple object cloner, b should be a pointer towards a value into which we want to decode func Clone(a, b interface{}) error { buff := new(bytes.Buffer) diff --git a/utils/coreutils_test.go b/utils/coreutils_test.go index 0a89103ec..962dc3e3b 100644 --- a/utils/coreutils_test.go +++ b/utils/coreutils_test.go @@ -416,7 +416,7 @@ func TestParseZeroRatingSubject(t *testing.T) { subj := []string{"", "*zero1s", "*zero5m", "*zero10h"} dur := []time.Duration{time.Second, time.Second, 5 * time.Minute, 10 * time.Hour} for i, s := range subj { - if d, err := ParseZeroRatingSubject(s); err != nil || d != dur[i] { + if d, err := ParseZeroRatingSubject(VOICE, s); err != nil || d != dur[i] { t.Error("Error parsing rating subject: ", s, d, err) } } @@ -434,25 +434,6 @@ func TestConcatenatedKey(t *testing.T) { } } -func TestConvertIfaceToString(t *testing.T) { - val := interface{}("string1") - if resVal, converted := ConvertIfaceToString(val); !converted || resVal != "string1" { - t.Error(resVal, converted) - } - val = interface{}(123) - if resVal, converted := ConvertIfaceToString(val); !converted || resVal != "123" { - t.Error(resVal, converted) - } - val = interface{}([]byte("byte_val")) - if resVal, converted := ConvertIfaceToString(val); !converted || resVal != "byte_val" { - t.Error(resVal, converted) - } - val = interface{}(true) - if resVal, converted := ConvertIfaceToString(val); !converted || resVal != "true" { - t.Error(resVal, converted) - } -} - func TestMandatory(t *testing.T) { _, err := FmtFieldWidth("", "", 0, "", "", true) if err == nil { diff --git a/utils/errors.go b/utils/errors.go index 54471678e..a2b813d21 100644 --- a/utils/errors.go +++ b/utils/errors.go @@ -46,6 +46,7 @@ var ( ErrResourceUnavailable = errors.New("RESOURCE_UNAVAILABLE") ErrNoActiveSession = errors.New("NO_ACTIVE_SESSION") ErrPartiallyExecuted = errors.New("PARTIALLY_EXECUTED") + ErrMaxUsageExceeded = errors.New("MAX_USAGE_EXCEEDED") ) // NewCGRError initialises a new CGRError diff --git a/utils/reflect.go b/utils/reflect.go index 8b14b3de3..5af166639 100644 --- a/utils/reflect.go +++ b/utils/reflect.go @@ -30,9 +30,6 @@ func CastFieldIfToString(fld interface{}) (string, bool) { var strVal string var converted bool switch fld.(type) { - case string: - strVal = fld.(string) - converted = true case int: strVal = strconv.Itoa(fld.(int)) converted = true diff --git a/utils/reflect_test.go b/utils/reflect_test.go index 3e9c990b1..f4beaf715 100644 --- a/utils/reflect_test.go +++ b/utils/reflect_test.go @@ -218,6 +218,22 @@ func TestStringToInterface(t *testing.T) { } func TestCastFieldIfToString(t *testing.T) { + val := interface{}("string1") + if resVal, converted := CastFieldIfToString(val); !converted || resVal != "string1" { + t.Error(resVal, converted) + } + val = interface{}(123) + if resVal, converted := CastFieldIfToString(val); !converted || resVal != "123" { + t.Error(resVal, converted) + } + val = interface{}([]byte("byte_val")) + if resVal, converted := CastFieldIfToString(val); !converted || resVal != "byte_val" { + t.Error(resVal, converted) + } + val = interface{}(true) + if resVal, converted := CastFieldIfToString(val); !converted || resVal != "true" { + t.Error(resVal, converted) + } if strVal, cast := CastFieldIfToString(time.Duration(1 * time.Second)); !cast { t.Error("cannot cast time.Duration") } else if strVal != "1s" { diff --git a/utils/value_formula.go b/utils/value_formula.go index 4c292a108..9ee2217e7 100644 --- a/utils/value_formula.go +++ b/utils/value_formula.go @@ -33,9 +33,12 @@ type ValueFormula struct { Static float64 } -func ParseBalanceFilterValue(val string) (*ValueFormula, error) { - u, err := strconv.ParseFloat(val, 64) - if err == nil { +func ParseBalanceFilterValue(tor string, val string) (*ValueFormula, error) { + if tor == VOICE { // VOICE balance is parsed as nanoseconds with support for time duration strings + if d, err := ParseDurationWithNanosecs(val); err == nil { + return &ValueFormula{Static: float64(d.Nanoseconds())}, err + } + } else if u, err := strconv.ParseFloat(val, 64); err == nil { return &ValueFormula{Static: u}, err } var vf ValueFormula diff --git a/utils/value_formula_test.go b/utils/value_formula_test.go index 3a42ee9b0..2f3da0086 100644 --- a/utils/value_formula_test.go +++ b/utils/value_formula_test.go @@ -19,6 +19,7 @@ package utils import ( "encoding/json" + "reflect" "testing" "time" ) @@ -54,3 +55,24 @@ func TestValueFormulaDayYear(t *testing.T) { t.Error("error caclulating value using formula: ", x) } } + +func TestValueFormulaParseBalanceFilterValue(t *testing.T) { + eVF := &ValueFormula{Static: 10000000000.0} + if vf, err := ParseBalanceFilterValue(VOICE, "10s"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eVF, vf) { + t.Errorf("Expecting: %+v, received: %+v", eVF, vf) + } + eVF = &ValueFormula{Static: 1024.0} + if vf, err := ParseBalanceFilterValue(DATA, "1024"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eVF, vf) { + t.Errorf("Expecting: %+v, received: %+v", eVF, vf) + } + eVF = &ValueFormula{Static: 10.0} + if vf, err := ParseBalanceFilterValue(MONETARY, "10"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eVF, vf) { + t.Errorf("Expecting: %+v, received: %+v", eVF, vf) + } +}