mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-16 05:39:54 +05:00
Moving Cdr* from utils to engine package so we can attach CostDetails to StoredCdr
This commit is contained in:
@@ -35,14 +35,14 @@ var (
|
||||
)
|
||||
|
||||
// Returns error if not able to properly store the CDR, mediation is async since we can always recover offline
|
||||
func storeAndMediate(storedCdr *utils.StoredCdr) error {
|
||||
func storeAndMediate(storedCdr *StoredCdr) error {
|
||||
if !cfg.CDRSStoreDisable {
|
||||
if err := storage.SetCdr(storedCdr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if stats != nil {
|
||||
go func(storedCdr *utils.StoredCdr) {
|
||||
go func(storedCdr *StoredCdr) {
|
||||
if err := stats.AppendCDR(storedCdr, nil); err != nil {
|
||||
Logger.Err(fmt.Sprintf("<CDRS> Could not append cdr to stats: %s", err.Error()))
|
||||
}
|
||||
@@ -52,7 +52,7 @@ func storeAndMediate(storedCdr *utils.StoredCdr) error {
|
||||
replicateCdr(storedCdr, cfg.CDRSCdrReplication)
|
||||
}
|
||||
if cfg.CDRSMediator == utils.INTERNAL {
|
||||
go func(storedCdr *utils.StoredCdr) {
|
||||
go func(storedCdr *StoredCdr) {
|
||||
if err := medi.RateCdr(storedCdr, true); err != nil {
|
||||
Logger.Err(fmt.Sprintf("<CDRS> Could not run mediation on CDR: %s", err.Error()))
|
||||
}
|
||||
@@ -62,13 +62,13 @@ func storeAndMediate(storedCdr *utils.StoredCdr) error {
|
||||
}
|
||||
|
||||
// ToDo: Add websocket support
|
||||
func replicateCdr(cdr *utils.StoredCdr, replCfgs []*config.CdrReplicationCfg) error {
|
||||
func replicateCdr(cdr *StoredCdr, replCfgs []*config.CdrReplicationCfg) error {
|
||||
for _, rplCfg := range replCfgs {
|
||||
switch rplCfg.Transport {
|
||||
case utils.META_HTTP_POST:
|
||||
httpClient := new(http.Client)
|
||||
errChan := make(chan error)
|
||||
go func(cdr *utils.StoredCdr, rplCfg *config.CdrReplicationCfg, errChan chan error) {
|
||||
go func(cdr *StoredCdr, rplCfg *config.CdrReplicationCfg, errChan chan error) {
|
||||
if _, err := httpClient.PostForm(fmt.Sprintf("http://%s/cdr_post", rplCfg.Server), cdr.AsHttpForm()); err != nil {
|
||||
Logger.Err(fmt.Sprintf("<CDRReplicator> Replicating CDR: %+v, got error: %s", cdr, err.Error()))
|
||||
errChan <- err
|
||||
@@ -85,7 +85,7 @@ func replicateCdr(cdr *utils.StoredCdr, replCfgs []*config.CdrReplicationCfg) er
|
||||
|
||||
// Handler for generic cgr cdr http
|
||||
func cgrCdrHandler(w http.ResponseWriter, r *http.Request) {
|
||||
cgrCdr, err := utils.NewCgrCdrFromHttpReq(r)
|
||||
cgrCdr, err := NewCgrCdrFromHttpReq(r)
|
||||
if err != nil {
|
||||
Logger.Err(fmt.Sprintf("<CDRS> Could not create CDR entry: %s", err.Error()))
|
||||
}
|
||||
@@ -134,6 +134,6 @@ func (cdrs *CDRS) RegisterHanlersToServer(server *Server) {
|
||||
}
|
||||
|
||||
// Used to internally process CDR
|
||||
func (cdrs *CDRS) ProcessCdr(cdr *utils.StoredCdr) error {
|
||||
func (cdrs *CDRS) ProcessCdr(cdr *StoredCdr) error {
|
||||
return storeAndMediate(cdr)
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ func TestCdrsHttpJsonRpcCdrReplication(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal("Could not connect to rater: ", err.Error())
|
||||
}
|
||||
testCdr1 := &utils.StoredCdr{CgrId: utils.Sha1("httpjsonrpc1", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()),
|
||||
testCdr1 := &StoredCdr{CgrId: utils.Sha1("httpjsonrpc1", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()),
|
||||
TOR: utils.VOICE, AccId: "httpjsonrpc1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: utils.META_PSEUDOPREPAID,
|
||||
Direction: "*out", 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),
|
||||
@@ -102,7 +102,7 @@ func TestCdrsHttpJsonRpcCdrReplication(t *testing.T) {
|
||||
} else if reply != utils.OK {
|
||||
t.Error("Unexpected reply received: ", reply)
|
||||
}
|
||||
var rcvedCdrs []*utils.CgrExtCdr
|
||||
var rcvedCdrs []*CgrExtCdr
|
||||
if err := cdrsHttpJsonRpc.Call("ApierV2.GetCdrs", utils.RpcCdrsFilter{CgrIds: []string{testCdr1.CgrId}}, &rcvedCdrs); err != nil {
|
||||
t.Error("Unexpected error: ", err.Error())
|
||||
} else if len(rcvedCdrs) != 1 {
|
||||
@@ -111,7 +111,7 @@ func TestCdrsHttpJsonRpcCdrReplication(t *testing.T) {
|
||||
}
|
||||
|
||||
/*
|
||||
&utils.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
&StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: 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), MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
|
||||
|
||||
@@ -75,7 +75,7 @@ type CdrStats struct {
|
||||
Triggers ActionTriggerPriotityList
|
||||
}
|
||||
|
||||
func (cs *CdrStats) AcceptCdr(cdr *utils.StoredCdr) bool {
|
||||
func (cs *CdrStats) AcceptCdr(cdr *StoredCdr) bool {
|
||||
if cdr == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
77
engine/cgrcdr.go
Normal file
77
engine/cgrcdr.go
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2012-2015 ITsysCOM
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewCgrCdrFromHttpReq(req *http.Request) (CgrCdr, error) {
|
||||
if req.Form == nil {
|
||||
if err := req.ParseForm(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cgrCdr := make(CgrCdr)
|
||||
cgrCdr[utils.CDRHOST] = req.RemoteAddr
|
||||
for k, vals := range req.Form {
|
||||
cgrCdr[k] = vals[0] // We only support the first value for now, if more are provided it is considered remote's fault
|
||||
}
|
||||
return cgrCdr, nil
|
||||
}
|
||||
|
||||
type CgrCdr map[string]string
|
||||
|
||||
func (cgrCdr CgrCdr) getCgrId() string {
|
||||
setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr[utils.SETUP_TIME])
|
||||
return utils.Sha1(cgrCdr[utils.ACCID], setupTime.UTC().String())
|
||||
}
|
||||
|
||||
func (cgrCdr CgrCdr) getExtraFields() map[string]string {
|
||||
extraFields := make(map[string]string)
|
||||
for k, v := range cgrCdr {
|
||||
if !utils.IsSliceMember(utils.PrimaryCdrFields, k) {
|
||||
extraFields[k] = v
|
||||
}
|
||||
}
|
||||
return extraFields
|
||||
}
|
||||
|
||||
func (cgrCdr CgrCdr) AsStoredCdr() *StoredCdr {
|
||||
storCdr := new(StoredCdr)
|
||||
storCdr.CgrId = cgrCdr.getCgrId()
|
||||
storCdr.TOR = cgrCdr[utils.TOR]
|
||||
storCdr.AccId = cgrCdr[utils.ACCID]
|
||||
storCdr.CdrHost = cgrCdr[utils.CDRHOST]
|
||||
storCdr.CdrSource = cgrCdr[utils.CDRSOURCE]
|
||||
storCdr.ReqType = cgrCdr[utils.REQTYPE]
|
||||
storCdr.Direction = "*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]) // Not interested to process errors, should do them if necessary in a previous step
|
||||
storCdr.AnswerTime, _ = utils.ParseTimeDetectLayout(cgrCdr[utils.ANSWER_TIME])
|
||||
storCdr.Usage, _ = utils.ParseDurationWithSecs(cgrCdr[utils.USAGE])
|
||||
storCdr.ExtraFields = cgrCdr.getExtraFields()
|
||||
storCdr.Cost = -1
|
||||
return storCdr
|
||||
}
|
||||
53
engine/cgrcdr_test.go
Normal file
53
engine/cgrcdr_test.go
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2012-2015 ITsysCOM
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
curl --data "accid=asbfdsaf&cdrhost=192.168.1.1&reqtype=rated&direction=*out&tenant=cgrates.org&tor=call&account=1001&subject=1001&destination=1002&time_answer=1383813746&duration=10&field_extr1=val_extr1&fieldextr2=valextr2" http://ipbxdev:2080/cgr
|
||||
*/
|
||||
|
||||
func TestCgrCdrInterfaces(t *testing.T) {
|
||||
var _ RawCdr = make(CgrCdr)
|
||||
}
|
||||
|
||||
func TestCgrCdrAsStoredCdr(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",
|
||||
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
|
||||
setupTime, _ := utils.ParseTimeDetectLayout(cgrCdr["setup_time"])
|
||||
expctRtCdr := &StoredCdr{CgrId: utils.Sha1(cgrCdr["accid"], setupTime.String()), TOR: utils.VOICE, AccId: cgrCdr["accid"], CdrHost: cgrCdr["cdrhost"], CdrSource: cgrCdr["cdrsource"],
|
||||
ReqType: cgrCdr["reqtype"],
|
||||
Direction: cgrCdr[utils.DIRECTION], Tenant: cgrCdr["tenant"], Category: cgrCdr[utils.CATEGORY], Account: cgrCdr["account"], Subject: cgrCdr["subject"],
|
||||
Destination: cgrCdr["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: -1}
|
||||
if storedCdr := cgrCdr.AsStoredCdr(); !reflect.DeepEqual(expctRtCdr, storedCdr) {
|
||||
t.Errorf("Expecting %v, received: %v", expctRtCdr, storedCdr)
|
||||
}
|
||||
}
|
||||
51
engine/event.go
Normal file
51
engine/event.go
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2012-2015 ITsysCOM
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Event interface {
|
||||
GetName() string
|
||||
GetCgrId() 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
|
||||
GetCallDestNr(string) string
|
||||
GetCategory(string) string
|
||||
GetTenant(string) string
|
||||
GetReqType(string) string
|
||||
GetSetupTime(string) (time.Time, error)
|
||||
GetAnswerTime(string) (time.Time, error)
|
||||
GetEndTime() (time.Time, error)
|
||||
GetDuration(string) (time.Duration, error)
|
||||
GetOriginatorIP(string) string
|
||||
GetExtraFields() map[string]string
|
||||
MissingParameter() bool
|
||||
ParseEventValue(*utils.RSRField) string
|
||||
PassesFieldFilter(*utils.RSRField) (bool, string)
|
||||
AsStoredCdr() *StoredCdr
|
||||
String() string
|
||||
AsEvent(string) Event
|
||||
}
|
||||
@@ -120,8 +120,8 @@ func (fsCdr FSCdr) searchExtraField(field string, body map[string]interface{}) (
|
||||
return
|
||||
}
|
||||
|
||||
func (fsCdr FSCdr) AsStoredCdr() *utils.StoredCdr {
|
||||
storCdr := new(utils.StoredCdr)
|
||||
func (fsCdr FSCdr) AsStoredCdr() *StoredCdr {
|
||||
storCdr := new(StoredCdr)
|
||||
storCdr.CgrId = fsCdr.getCgrId()
|
||||
storCdr.TOR = utils.VOICE
|
||||
storCdr.AccId = fsCdr.vars[FS_UUID]
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -47,9 +47,6 @@ var accountDbCsv, accountDbStor, accountDbApier AccountingStorage // Each rating
|
||||
var storDb LoadStorage
|
||||
var lCfg *config.CGRConfig
|
||||
|
||||
// Arguments received via test command
|
||||
var testLocal = flag.Bool("local", false, "Perform the tests only on local test environment, not by default.") // This flag will be passed here via "go test -local" args
|
||||
var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here")
|
||||
var tpCsvScenario = flag.String("tp_scenario", "prepaid1centpsec", "Use this scenario folder to import tp csv data from")
|
||||
|
||||
// Create connection to ratingDb
|
||||
|
||||
@@ -71,7 +71,7 @@ func (self *Mediator) getCostsFromDB(cgrid, runId string) (cc *CallCost, err err
|
||||
}
|
||||
|
||||
// Retrive the cost from engine
|
||||
func (self *Mediator) getCostFromRater(storedCdr *utils.StoredCdr) (*CallCost, error) {
|
||||
func (self *Mediator) getCostFromRater(storedCdr *StoredCdr) (*CallCost, error) {
|
||||
cc := &CallCost{}
|
||||
var err error
|
||||
if storedCdr.Usage == time.Duration(0) { // failed call, returning empty callcost, no error
|
||||
@@ -103,7 +103,7 @@ func (self *Mediator) getCostFromRater(storedCdr *utils.StoredCdr) (*CallCost, e
|
||||
return cc, err
|
||||
}
|
||||
|
||||
func (self *Mediator) rateCDR(storedCdr *utils.StoredCdr) error {
|
||||
func (self *Mediator) rateCDR(storedCdr *StoredCdr) error {
|
||||
var qryCC *CallCost
|
||||
var errCost error
|
||||
if utils.IsSliceMember([]string{utils.META_PREPAID, utils.PREPAID}, storedCdr.ReqType) { // ToDo: Get rid of PREPAID as soon as we don't want to support it backwards
|
||||
@@ -121,9 +121,9 @@ func (self *Mediator) rateCDR(storedCdr *utils.StoredCdr) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *Mediator) RateCdr(storedCdr *utils.StoredCdr, sendToStats bool) error {
|
||||
func (self *Mediator) RateCdr(storedCdr *StoredCdr, sendToStats bool) error {
|
||||
storedCdr.MediationRunId = utils.META_DEFAULT
|
||||
cdrRuns := []*utils.StoredCdr{storedCdr} // Start with initial storCdr, will add here all to be mediated
|
||||
cdrRuns := []*StoredCdr{storedCdr} // Start with initial storCdr, will add here all to be mediated
|
||||
attrsDC := utils.AttrDerivedChargers{Tenant: storedCdr.Tenant, Category: storedCdr.Category, Direction: storedCdr.Direction,
|
||||
Account: storedCdr.Account, Subject: storedCdr.Subject}
|
||||
var dcs utils.DerivedChargers
|
||||
@@ -157,7 +157,7 @@ func (self *Mediator) RateCdr(storedCdr *utils.StoredCdr, sendToStats bool) erro
|
||||
forkedCdr, err := storedCdr.ForkCdr(dc.RunId, dcReqTypeFld, dcDirFld, dcTenantFld, dcCategoryFld, dcAcntFld, dcSubjFld, dcDstFld, dcSTimeFld, dcATimeFld, dcDurFld,
|
||||
[]*utils.RSRField{}, true)
|
||||
if err != nil { // Errors on fork, cannot calculate further, write that into db for later analysis
|
||||
self.cdrDb.SetRatedCdr(&utils.StoredCdr{CgrId: storedCdr.CgrId, CdrSource: utils.FORKED_CDR, MediationRunId: dc.RunId, Cost: -1},
|
||||
self.cdrDb.SetRatedCdr(&StoredCdr{CgrId: storedCdr.CgrId, CdrSource: utils.FORKED_CDR, MediationRunId: dc.RunId, Cost: -1},
|
||||
err.Error()) // Cannot fork CDR, important just runid and error
|
||||
continue
|
||||
}
|
||||
@@ -178,7 +178,7 @@ func (self *Mediator) RateCdr(storedCdr *utils.StoredCdr, sendToStats bool) erro
|
||||
}
|
||||
}
|
||||
if sendToStats && self.stats != nil { // We send to stats only after saving to db since there are chances we cannot store and then no way to reproduce stats offline
|
||||
go func(cdr *utils.StoredCdr) { // Pass it by value since the variable will be overwritten by for
|
||||
go func(cdr *StoredCdr) { // Pass it by value since the variable will be overwritten by for
|
||||
if err := self.stats.AppendCDR(cdr, nil); err != nil {
|
||||
Logger.Err(fmt.Sprintf("<Mediator> Could not append cdr to stats: %s", err.Error()))
|
||||
}
|
||||
|
||||
@@ -174,13 +174,13 @@ func TestMediInjectCdrs(t *testing.T) {
|
||||
if !*testLocal {
|
||||
return
|
||||
}
|
||||
cgrCdr1 := utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaaaadsafdsaf", "cdrsource": TEST_SQL, utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out",
|
||||
cgrCdr1 := CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaaaadsafdsaf", "cdrsource": TEST_SQL, utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out",
|
||||
utils.TENANT: "cgrates.org", utils.CATEGORY: "call", utils.ACCOUNT: "dan", utils.SUBJECT: "dan", utils.DESTINATION: "+4986517174963",
|
||||
utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "10"}
|
||||
cgrCdr2 := utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "baaaadsafdsaf", "cdrsource": TEST_SQL, utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out",
|
||||
cgrCdr2 := CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "baaaadsafdsaf", "cdrsource": TEST_SQL, utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out",
|
||||
utils.TENANT: "cgrates.org", utils.CATEGORY: "call", utils.ACCOUNT: "dan", utils.SUBJECT: "dan", utils.DESTINATION: "+4986517173964",
|
||||
utils.ANSWER_TIME: "2013-11-07T09:42:26Z", utils.USAGE: "20"}
|
||||
for _, cdr := range []utils.CgrCdr{cgrCdr1, cgrCdr2} {
|
||||
for _, cdr := range []CgrCdr{cgrCdr1, cgrCdr2} {
|
||||
if err := cdrStor.SetCdr(cdr.AsStoredCdr()); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
24
engine/rawcdr.go
Normal file
24
engine/rawcdr.go
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
Rating system designed to be used in VoIP Carriers World
|
||||
Copyright (C) 2012-2015 ITsysCOM
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
// RawCDR is the original CDR received from external sources (eg: FreeSWITCH)
|
||||
type RawCdr interface {
|
||||
AsStoredCdr() *StoredCdr // Convert the inbound Cdr into internally used one, CgrCdr
|
||||
}
|
||||
@@ -126,7 +126,7 @@ func (rs *Responder) GetMaxSessionTime(arg CallDescriptor, reply *float64) (err
|
||||
}
|
||||
|
||||
// Returns MaxSessionTime for an event received in SessionManager, considering DerivedCharging for it
|
||||
func (rs *Responder) GetDerivedMaxSessionTime(ev utils.StoredCdr, reply *float64) error {
|
||||
func (rs *Responder) GetDerivedMaxSessionTime(ev StoredCdr, reply *float64) error {
|
||||
if rs.Bal != nil {
|
||||
return errors.New("Unsupported method on the balancer")
|
||||
}
|
||||
@@ -184,7 +184,7 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev utils.StoredCdr, reply *float64
|
||||
}
|
||||
|
||||
// Used by SM to get all the prepaid CallDescriptors attached to a session
|
||||
func (rs *Responder) GetSessionRuns(ev utils.StoredCdr, sRuns *[]*SessionRun) error {
|
||||
func (rs *Responder) GetSessionRuns(ev StoredCdr, sRuns *[]*SessionRun) error {
|
||||
if rs.Bal != nil {
|
||||
return errors.New("Unsupported method on the balancer")
|
||||
}
|
||||
@@ -230,7 +230,7 @@ func (rs *Responder) GetDerivedChargers(attrs utils.AttrDerivedChargers, dcs *ut
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rs *Responder) ProcessCdr(cdr *utils.StoredCdr, reply *string) error {
|
||||
func (rs *Responder) ProcessCdr(cdr *StoredCdr, reply *string) error {
|
||||
if rs.CdrSrv == nil {
|
||||
return errors.New("CDR_SERVER_NOT_RUNNING")
|
||||
}
|
||||
@@ -397,9 +397,9 @@ type Connector interface {
|
||||
RefundIncrements(CallDescriptor, *float64) error
|
||||
GetMaxSessionTime(CallDescriptor, *float64) error
|
||||
GetDerivedChargers(utils.AttrDerivedChargers, *utils.DerivedChargers) error
|
||||
GetDerivedMaxSessionTime(utils.StoredCdr, *float64) error
|
||||
GetSessionRuns(utils.StoredCdr, *[]*SessionRun) error
|
||||
ProcessCdr(*utils.StoredCdr, *string) error
|
||||
GetDerivedMaxSessionTime(StoredCdr, *float64) error
|
||||
GetSessionRuns(StoredCdr, *[]*SessionRun) error
|
||||
ProcessCdr(*StoredCdr, *string) error
|
||||
}
|
||||
|
||||
type RPCClientConnector struct {
|
||||
@@ -426,11 +426,11 @@ func (rcc *RPCClientConnector) GetMaxSessionTime(cd CallDescriptor, resp *float6
|
||||
return rcc.Client.Call("Responder.GetMaxSessionTime", cd, resp)
|
||||
}
|
||||
|
||||
func (rcc *RPCClientConnector) GetDerivedMaxSessionTime(ev utils.StoredCdr, reply *float64) error {
|
||||
func (rcc *RPCClientConnector) GetDerivedMaxSessionTime(ev StoredCdr, reply *float64) error {
|
||||
return rcc.Client.Call("Responder.GetDerivedMaxSessionTime", ev, reply)
|
||||
}
|
||||
|
||||
func (rcc *RPCClientConnector) GetSessionRuns(ev utils.StoredCdr, sRuns *[]*SessionRun) error {
|
||||
func (rcc *RPCClientConnector) GetSessionRuns(ev StoredCdr, sRuns *[]*SessionRun) error {
|
||||
return rcc.Client.Call("Responder.GetSessionRuns", ev, sRuns)
|
||||
}
|
||||
|
||||
@@ -438,6 +438,6 @@ func (rcc *RPCClientConnector) GetDerivedChargers(attrs utils.AttrDerivedCharger
|
||||
return rcc.Client.Call("ApierV1.GetDerivedChargers", attrs, dcs)
|
||||
}
|
||||
|
||||
func (rcc *RPCClientConnector) ProcessCdr(cdr *utils.StoredCdr, reply *string) error {
|
||||
func (rcc *RPCClientConnector) ProcessCdr(cdr *StoredCdr, reply *string) error {
|
||||
return rcc.Client.Call("CDRSV1.ProcessCdr", cdr, reply)
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func TestResponderGetDerivedChargers(t *testing.T) {
|
||||
|
||||
func TestGetDerivedMaxSessionTime(t *testing.T) {
|
||||
testTenant := "vdf"
|
||||
cdr := utils.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
cdr := StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: 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),
|
||||
MediationRunId: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
|
||||
@@ -123,7 +123,7 @@ func TestGetDerivedMaxSessionTime(t *testing.T) {
|
||||
|
||||
func TestGetSessionRuns(t *testing.T) {
|
||||
testTenant := "vdf"
|
||||
cdr := utils.StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
cdr := StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: 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), AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
|
||||
MediationRunId: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
type StatsInterface interface {
|
||||
GetValues(string, *map[string]float64) error
|
||||
GetQueueIds(int, *[]string) error
|
||||
AppendCDR(*utils.StoredCdr, *int) error
|
||||
AppendCDR(*StoredCdr, *int) error
|
||||
AddQueue(*CdrStats, *int) error
|
||||
ReloadQueues([]string, *int) error
|
||||
ResetQueues([]string, *int) error
|
||||
@@ -163,7 +163,7 @@ func (s *Stats) UpdateQueues(css []*CdrStats, out *int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Stats) AppendCDR(cdr *utils.StoredCdr, out *int) error {
|
||||
func (s *Stats) AppendCDR(cdr *StoredCdr, out *int) error {
|
||||
s.mux.RLock()
|
||||
defer s.mux.RUnlock()
|
||||
for _, sq := range s.queues {
|
||||
@@ -189,7 +189,7 @@ func (ps *ProxyStats) GetValues(sqID string, values *map[string]float64) error {
|
||||
return ps.Client.Call("Stats.GetValues", sqID, values)
|
||||
}
|
||||
|
||||
func (ps *ProxyStats) AppendCDR(cdr *utils.StoredCdr, out *int) error {
|
||||
func (ps *ProxyStats) AppendCDR(cdr *StoredCdr, out *int) error {
|
||||
return ps.Client.Call("Stats.AppendCDR", cdr, out)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,6 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
type StatsQueue struct {
|
||||
@@ -72,7 +70,7 @@ func (sq *StatsQueue) UpdateConf(conf *CdrStats) {
|
||||
}
|
||||
}
|
||||
|
||||
func (sq *StatsQueue) AppendCDR(cdr *utils.StoredCdr) {
|
||||
func (sq *StatsQueue) AppendCDR(cdr *StoredCdr) {
|
||||
sq.mux.Lock()
|
||||
defer sq.mux.Unlock()
|
||||
if sq.conf.AcceptCdr(cdr) {
|
||||
@@ -117,7 +115,7 @@ func (sq *StatsQueue) removeFromMetrics(cdr *QCdr) {
|
||||
}
|
||||
}
|
||||
|
||||
func (sq *StatsQueue) simplifyCdr(cdr *utils.StoredCdr) *QCdr {
|
||||
func (sq *StatsQueue) simplifyCdr(cdr *StoredCdr) *QCdr {
|
||||
return &QCdr{
|
||||
SetupTime: cdr.SetupTime,
|
||||
AnswerTime: cdr.AnswerTime,
|
||||
|
||||
@@ -34,7 +34,7 @@ func TestStatsQueueInit(t *testing.T) {
|
||||
|
||||
func TestStatsValue(t *testing.T) {
|
||||
sq := NewStatsQueue(&CdrStats{Metrics: []string{ASR, ACD, ACC}})
|
||||
cdr := &utils.StoredCdr{
|
||||
cdr := &StoredCdr{
|
||||
AnswerTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC),
|
||||
Usage: 10 * time.Second,
|
||||
Cost: 1,
|
||||
@@ -53,7 +53,7 @@ func TestStatsValue(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStatsSimplifyCDR(t *testing.T) {
|
||||
cdr := &utils.StoredCdr{
|
||||
cdr := &StoredCdr{
|
||||
TOR: "tor",
|
||||
AccId: "accid",
|
||||
CdrHost: "cdrhost",
|
||||
@@ -82,7 +82,7 @@ func TestStatsSimplifyCDR(t *testing.T) {
|
||||
|
||||
func TestAcceptCdr(t *testing.T) {
|
||||
sq := NewStatsQueue(nil)
|
||||
cdr := &utils.StoredCdr{
|
||||
cdr := &StoredCdr{
|
||||
TOR: "tor",
|
||||
AccId: "accid",
|
||||
CdrHost: "cdrhost",
|
||||
@@ -196,7 +196,7 @@ func TestStatsQueueIds(t *testing.T) {
|
||||
|
||||
func TestStatsAppendCdr(t *testing.T) {
|
||||
cdrStats := NewStats(dataStorage)
|
||||
cdr := &utils.StoredCdr{
|
||||
cdr := &StoredCdr{
|
||||
Tenant: "cgrates.org",
|
||||
Category: "call",
|
||||
AnswerTime: time.Now(),
|
||||
@@ -216,7 +216,7 @@ func TestStatsAppendCdr(t *testing.T) {
|
||||
|
||||
func TestStatsGetValues(t *testing.T) {
|
||||
cdrStats := NewStats(dataStorage)
|
||||
cdr := &utils.StoredCdr{
|
||||
cdr := &StoredCdr{
|
||||
Tenant: "cgrates.org",
|
||||
Category: "call",
|
||||
AnswerTime: time.Now(),
|
||||
@@ -225,7 +225,7 @@ func TestStatsGetValues(t *testing.T) {
|
||||
Cost: 10,
|
||||
}
|
||||
cdrStats.AppendCDR(cdr, nil)
|
||||
cdr = &utils.StoredCdr{
|
||||
cdr = &StoredCdr{
|
||||
Tenant: "cgrates.org",
|
||||
Category: "call",
|
||||
AnswerTime: time.Now(),
|
||||
@@ -245,7 +245,7 @@ func TestStatsGetValues(t *testing.T) {
|
||||
|
||||
func TestStatsReloadQueues(t *testing.T) {
|
||||
cdrStats := NewStats(dataStorage)
|
||||
cdr := &utils.StoredCdr{
|
||||
cdr := &StoredCdr{
|
||||
Tenant: "cgrates.org",
|
||||
Category: "call",
|
||||
AnswerTime: time.Now(),
|
||||
@@ -280,7 +280,7 @@ func TestStatsReloadQueuesWithDefault(t *testing.T) {
|
||||
cdrStats.AddQueue(&CdrStats{
|
||||
Id: utils.META_DEFAULT,
|
||||
}, nil)
|
||||
cdr := &utils.StoredCdr{
|
||||
cdr := &StoredCdr{
|
||||
Tenant: "cgrates.org",
|
||||
Category: "call",
|
||||
AnswerTime: time.Now(),
|
||||
@@ -313,7 +313,7 @@ func TestStatsReloadQueuesWithDefault(t *testing.T) {
|
||||
|
||||
func TestStatsReloadQueuesWithIds(t *testing.T) {
|
||||
cdrStats := NewStats(dataStorage)
|
||||
cdr := &utils.StoredCdr{
|
||||
cdr := &StoredCdr{
|
||||
Tenant: "cgrates.org",
|
||||
Category: "call",
|
||||
AnswerTime: time.Now(),
|
||||
@@ -345,7 +345,7 @@ func TestStatsReloadQueuesWithIds(t *testing.T) {
|
||||
|
||||
func TestStatsResetQueues(t *testing.T) {
|
||||
cdrStats := NewStats(dataStorage)
|
||||
cdr := &utils.StoredCdr{
|
||||
cdr := &StoredCdr{
|
||||
Tenant: "cgrates.org",
|
||||
Category: "call",
|
||||
AnswerTime: time.Now(),
|
||||
@@ -377,7 +377,7 @@ func TestStatsResetQueues(t *testing.T) {
|
||||
|
||||
func TestStatsResetQueuesWithIds(t *testing.T) {
|
||||
cdrStats := NewStats(dataStorage)
|
||||
cdr := &utils.StoredCdr{
|
||||
cdr := &StoredCdr{
|
||||
Tenant: "cgrates.org",
|
||||
Category: "call",
|
||||
AnswerTime: time.Now(),
|
||||
|
||||
@@ -112,9 +112,9 @@ type AccountingStorage interface {
|
||||
|
||||
type CdrStorage interface {
|
||||
Storage
|
||||
SetCdr(*utils.StoredCdr) error
|
||||
SetRatedCdr(*utils.StoredCdr, string) error
|
||||
GetStoredCdrs(*utils.CdrsFilter) ([]*utils.StoredCdr, int64, error)
|
||||
SetCdr(*StoredCdr) error
|
||||
SetRatedCdr(*StoredCdr, string) error
|
||||
GetStoredCdrs(*utils.CdrsFilter) ([]*StoredCdr, int64, error)
|
||||
RemStoredCdrs([]string) error
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ func (self *MySQLStorage) LogCallCost(cgrid, source, runid string, cc *CallCost)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *MySQLStorage) SetRatedCdr(storedCdr *utils.StoredCdr, extraInfo string) (err error) {
|
||||
func (self *MySQLStorage) SetRatedCdr(storedCdr *StoredCdr, extraInfo string) (err error) {
|
||||
_, err = self.Db.Exec(fmt.Sprintf("INSERT INTO %s (cgrid,runid,reqtype,direction,tenant,category,account,subject,destination,setup_time,answer_time,`usage`,cost,extra_info,created_at) VALUES ('%s','%s','%s','%s','%s','%s','%s','%s','%s','%s','%s',%v,%f,'%s','%s') ON DUPLICATE KEY UPDATE reqtype=values(reqtype),direction=values(direction),tenant=values(tenant),category=values(category),account=values(account),subject=values(subject),destination=values(destination),setup_time=values(setup_time),answer_time=values(answer_time),`usage`=values(`usage`),cost=values(cost),extra_info=values(extra_info), updated_at='%s'",
|
||||
utils.TBL_RATED_CDRS,
|
||||
storedCdr.CgrId,
|
||||
|
||||
@@ -431,50 +431,50 @@ func TestMySQLSetCdr(t *testing.T) {
|
||||
if !*testLocal {
|
||||
return
|
||||
}
|
||||
cgrCdr1 := &utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa1", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
cgrCdr1 := &CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa1", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
utils.CATEGORY: "call", utils.ACCOUNT: "1001", utils.SUBJECT: "1001", utils.DESTINATION: "1002", utils.SETUP_TIME: "2013-11-08T08:42:20Z",
|
||||
utils.ANSWER_TIME: "2013-11-08T08:42:26Z", utils.USAGE: "10s", "field_extr1": "val_extr1", "fieldextr2": "valextr2", utils.CDRSOURCE: TEST_SQL}
|
||||
cgrCdr2 := &utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa2", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_PREPAID, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
cgrCdr2 := &CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa2", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_PREPAID, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
utils.CATEGORY: "call", utils.ACCOUNT: "1001", utils.SUBJECT: "1001", utils.DESTINATION: "1002", utils.SETUP_TIME: "2013-11-08T08:42:22Z",
|
||||
utils.ANSWER_TIME: "2013-11-08T08:42:26Z", utils.USAGE: "20", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": TEST_SQL}
|
||||
|
||||
cgrCdr3 := &utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa3", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
cgrCdr3 := &CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa3", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
utils.CATEGORY: "premium_call", utils.ACCOUNT: "1002", utils.SUBJECT: "1002", utils.DESTINATION: "1001", utils.SETUP_TIME: "2013-11-07T08:42:24Z",
|
||||
utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "60s", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": TEST_SQL}
|
||||
|
||||
cgrCdr4 := &utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa4", utils.CDRHOST: "192.168.1.2", utils.REQTYPE: utils.META_PSEUDOPREPAID, utils.DIRECTION: "*out", utils.TENANT: "itsyscom.com",
|
||||
cgrCdr4 := &CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa4", utils.CDRHOST: "192.168.1.2", utils.REQTYPE: utils.META_PSEUDOPREPAID, utils.DIRECTION: "*out", utils.TENANT: "itsyscom.com",
|
||||
utils.CATEGORY: "call", utils.ACCOUNT: "1001", utils.SUBJECT: "1001", utils.DESTINATION: "+4986517174964", utils.SETUP_TIME: "2013-11-07T08:42:21Z",
|
||||
utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "1m2s", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": TEST_SQL}
|
||||
|
||||
cgrCdr5 := &utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa5", utils.CDRHOST: "192.168.1.2", utils.REQTYPE: utils.META_POSTPAID, utils.DIRECTION: "*out", utils.TENANT: "itsyscom.com",
|
||||
cgrCdr5 := &CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa5", utils.CDRHOST: "192.168.1.2", utils.REQTYPE: utils.META_POSTPAID, utils.DIRECTION: "*out", utils.TENANT: "itsyscom.com",
|
||||
utils.CATEGORY: "call", utils.ACCOUNT: "1002", utils.SUBJECT: "1002", utils.DESTINATION: "+4986517174963", utils.SETUP_TIME: "2013-11-07T08:42:25Z",
|
||||
utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "15s", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": TEST_SQL}
|
||||
|
||||
for _, cdr := range []*utils.CgrCdr{cgrCdr1, cgrCdr2, cgrCdr3, cgrCdr4, cgrCdr5} {
|
||||
for _, cdr := range []*CgrCdr{cgrCdr1, cgrCdr2, cgrCdr3, cgrCdr4, cgrCdr5} {
|
||||
if err := mysqlDb.SetCdr(cdr.AsStoredCdr()); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
strCdr1 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: utils.META_RATED,
|
||||
strCdr1 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: utils.META_RATED,
|
||||
Direction: "*out", 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"},
|
||||
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
|
||||
strCdr1.CgrId = utils.Sha1(strCdr1.AccId, strCdr1.SetupTime.String())
|
||||
strCdr2 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN2", ReqType: utils.META_PREPAID,
|
||||
strCdr2 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN2", ReqType: utils.META_PREPAID,
|
||||
Direction: "*out", 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(12) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
|
||||
MediationRunId: utils.DEFAULT_RUNID, Cost: 0.201}
|
||||
strCdr2.CgrId = utils.Sha1(strCdr2.AccId, strCdr2.SetupTime.String())
|
||||
strCdr3 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: utils.META_RATED,
|
||||
strCdr3 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: utils.META_RATED,
|
||||
Direction: "*out", Tenant: "itsyscom.com", Category: "call", Account: "1002", Subject: "1000", Destination: "+4986517174963",
|
||||
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"},
|
||||
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
|
||||
strCdr3.CgrId = utils.Sha1(strCdr3.AccId, strCdr3.SetupTime.String())
|
||||
|
||||
for _, cdr := range []*utils.StoredCdr{strCdr1, strCdr2, strCdr3} {
|
||||
for _, cdr := range []*StoredCdr{strCdr1, strCdr2, strCdr3} {
|
||||
if err := mysqlDb.SetCdr(cdr); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
@@ -485,26 +485,26 @@ func TestMySQLSetRatedCdr(t *testing.T) {
|
||||
if !*testLocal {
|
||||
return
|
||||
}
|
||||
strCdr1 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: utils.META_RATED,
|
||||
strCdr1 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: utils.META_RATED,
|
||||
Direction: "*out", 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"},
|
||||
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
|
||||
strCdr1.CgrId = utils.Sha1(strCdr1.AccId, strCdr1.SetupTime.String())
|
||||
strCdr2 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN", ReqType: utils.META_PREPAID,
|
||||
strCdr2 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN", ReqType: utils.META_PREPAID,
|
||||
Direction: "*out", 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(12) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
|
||||
MediationRunId: utils.DEFAULT_RUNID, Cost: 0.201}
|
||||
strCdr2.CgrId = utils.Sha1(strCdr2.AccId, strCdr2.SetupTime.String())
|
||||
strCdr3 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: utils.META_RATED,
|
||||
strCdr3 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: utils.META_RATED,
|
||||
Direction: "*out", Tenant: "itsyscom.com", Category: "call", Account: "1002", Subject: "1002", Destination: "+4986517174964",
|
||||
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"},
|
||||
MediationRunId: "wholesale_run", Cost: 1.201}
|
||||
strCdr3.CgrId = utils.Sha1(strCdr3.AccId, strCdr3.SetupTime.String())
|
||||
|
||||
for _, cdr := range []*utils.StoredCdr{strCdr1, strCdr2, strCdr3} {
|
||||
for _, cdr := range []*StoredCdr{strCdr1, strCdr2, strCdr3} {
|
||||
if err := mysqlDb.SetRatedCdr(cdr, ""); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ func (self *PostgresStorage) LogCallCost(cgrid, source, runid string, cc *CallCo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *PostgresStorage) SetRatedCdr(cdr *utils.StoredCdr, extraInfo string) (err error) {
|
||||
func (self *PostgresStorage) SetRatedCdr(cdr *StoredCdr, extraInfo string) (err error) {
|
||||
tx := self.db.Begin()
|
||||
saved := tx.Save(&TblRatedCdr{
|
||||
Cgrid: cdr.CgrId,
|
||||
|
||||
@@ -433,50 +433,50 @@ func TestPSQLSetCdr(t *testing.T) {
|
||||
if !*testLocal {
|
||||
return
|
||||
}
|
||||
cgrCdr1 := &utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa1", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
cgrCdr1 := &CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa1", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
utils.CATEGORY: "call", utils.ACCOUNT: "1001", utils.SUBJECT: "1001", utils.DESTINATION: "1002", utils.SETUP_TIME: "2013-11-08T08:42:20Z",
|
||||
utils.ANSWER_TIME: "2013-11-08T08:42:26Z", utils.USAGE: "10s", "field_extr1": "val_extr1", "fieldextr2": "valextr2", utils.CDRSOURCE: TEST_SQL}
|
||||
cgrCdr2 := &utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa2", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_PREPAID, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
cgrCdr2 := &CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa2", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_PREPAID, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
utils.CATEGORY: "call", utils.ACCOUNT: "1001", utils.SUBJECT: "1001", utils.DESTINATION: "1002", utils.SETUP_TIME: "2013-11-08T08:42:22Z",
|
||||
utils.ANSWER_TIME: "2013-11-08T08:42:26Z", utils.USAGE: "20", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": TEST_SQL}
|
||||
|
||||
cgrCdr3 := &utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa3", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
cgrCdr3 := &CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa3", utils.CDRHOST: "192.168.1.1", utils.REQTYPE: utils.META_RATED, utils.DIRECTION: "*out", utils.TENANT: "cgrates.org",
|
||||
utils.CATEGORY: "premium_call", utils.ACCOUNT: "1002", utils.SUBJECT: "1002", utils.DESTINATION: "1001", utils.SETUP_TIME: "2013-11-07T08:42:24Z",
|
||||
utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "60s", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": TEST_SQL}
|
||||
|
||||
cgrCdr4 := &utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa4", utils.CDRHOST: "192.168.1.2", utils.REQTYPE: utils.META_PSEUDOPREPAID, utils.DIRECTION: "*out", utils.TENANT: "itsyscom.com",
|
||||
cgrCdr4 := &CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa4", utils.CDRHOST: "192.168.1.2", utils.REQTYPE: utils.META_PSEUDOPREPAID, utils.DIRECTION: "*out", utils.TENANT: "itsyscom.com",
|
||||
utils.CATEGORY: "call", utils.ACCOUNT: "1001", utils.SUBJECT: "1001", utils.DESTINATION: "+4986517174964", utils.SETUP_TIME: "2013-11-07T08:42:21Z",
|
||||
utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "1m2s", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": TEST_SQL}
|
||||
|
||||
cgrCdr5 := &utils.CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa5", utils.CDRHOST: "192.168.1.2", utils.REQTYPE: utils.META_POSTPAID, utils.DIRECTION: "*out", utils.TENANT: "itsyscom.com",
|
||||
cgrCdr5 := &CgrCdr{utils.TOR: utils.VOICE, utils.ACCID: "aaa5", utils.CDRHOST: "192.168.1.2", utils.REQTYPE: utils.META_POSTPAID, utils.DIRECTION: "*out", utils.TENANT: "itsyscom.com",
|
||||
utils.CATEGORY: "call", utils.ACCOUNT: "1002", utils.SUBJECT: "1002", utils.DESTINATION: "+4986517174963", utils.SETUP_TIME: "2013-11-07T08:42:25Z",
|
||||
utils.ANSWER_TIME: "2013-11-07T08:42:26Z", utils.USAGE: "15s", "field_extr1": "val_extr1", "fieldextr2": "valextr2", "cdrsource": TEST_SQL}
|
||||
|
||||
for _, cdr := range []*utils.CgrCdr{cgrCdr1, cgrCdr2, cgrCdr3, cgrCdr4, cgrCdr5} {
|
||||
for _, cdr := range []*CgrCdr{cgrCdr1, cgrCdr2, cgrCdr3, cgrCdr4, cgrCdr5} {
|
||||
if err := psqlDb.SetCdr(cdr.AsStoredCdr()); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
}
|
||||
strCdr1 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: utils.META_RATED,
|
||||
strCdr1 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: utils.META_RATED,
|
||||
Direction: "*out", 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"},
|
||||
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
|
||||
strCdr1.CgrId = utils.Sha1(strCdr1.AccId, strCdr1.SetupTime.String())
|
||||
strCdr2 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN2", ReqType: utils.META_PREPAID,
|
||||
strCdr2 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN2", ReqType: utils.META_PREPAID,
|
||||
Direction: "*out", 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(12) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
|
||||
MediationRunId: utils.DEFAULT_RUNID, Cost: 0.201}
|
||||
strCdr2.CgrId = utils.Sha1(strCdr2.AccId, strCdr2.SetupTime.String())
|
||||
strCdr3 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: utils.META_RATED,
|
||||
strCdr3 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: utils.META_RATED,
|
||||
Direction: "*out", Tenant: "itsyscom.com", Category: "call", Account: "1002", Subject: "1000", Destination: "+4986517174963",
|
||||
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"},
|
||||
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
|
||||
strCdr3.CgrId = utils.Sha1(strCdr3.AccId, strCdr3.SetupTime.String())
|
||||
|
||||
for _, cdr := range []*utils.StoredCdr{strCdr1, strCdr2, strCdr3} {
|
||||
for _, cdr := range []*StoredCdr{strCdr1, strCdr2, strCdr3} {
|
||||
if err := psqlDb.SetCdr(cdr); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
@@ -531,26 +531,26 @@ func TestPSQLSetRatedCdr(t *testing.T) {
|
||||
if !*testLocal {
|
||||
return
|
||||
}
|
||||
strCdr1 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: utils.META_RATED,
|
||||
strCdr1 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb1", CdrHost: "192.168.1.1", CdrSource: "UNKNOWN", ReqType: utils.META_RATED,
|
||||
Direction: "*out", 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"},
|
||||
MediationRunId: utils.DEFAULT_RUNID, Cost: 1.201}
|
||||
strCdr1.CgrId = utils.Sha1(strCdr1.AccId, strCdr1.SetupTime.String())
|
||||
strCdr2 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN", ReqType: utils.META_PREPAID,
|
||||
strCdr2 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb2", CdrHost: "192.168.1.2", CdrSource: "UNKNOWN", ReqType: utils.META_PREPAID,
|
||||
Direction: "*out", 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(12) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
|
||||
MediationRunId: utils.DEFAULT_RUNID, Cost: 0.201}
|
||||
strCdr2.CgrId = utils.Sha1(strCdr2.AccId, strCdr2.SetupTime.String())
|
||||
strCdr3 := &utils.StoredCdr{TOR: utils.VOICE, AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: utils.META_RATED,
|
||||
strCdr3 := &StoredCdr{TOR: utils.VOICE, AccId: "bbb3", CdrHost: "192.168.1.1", CdrSource: TEST_SQL, ReqType: utils.META_RATED,
|
||||
Direction: "*out", Tenant: "itsyscom.com", Category: "call", Account: "1002", Subject: "1002", Destination: "+4986517174964",
|
||||
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"},
|
||||
MediationRunId: "wholesale_run", Cost: 1.201}
|
||||
strCdr3.CgrId = utils.Sha1(strCdr3.AccId, strCdr3.SetupTime.String())
|
||||
|
||||
for _, cdr := range []*utils.StoredCdr{strCdr1, strCdr2, strCdr3} {
|
||||
for _, cdr := range []*StoredCdr{strCdr1, strCdr2, strCdr3} {
|
||||
if err := psqlDb.SetRatedCdr(cdr, ""); err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
|
||||
@@ -716,7 +716,7 @@ func (self *SQLStorage) LogActionTiming(source string, at *ActionTiming, as Acti
|
||||
}
|
||||
func (self *SQLStorage) LogError(uuid, source, runid, errstr string) (err error) { return }
|
||||
|
||||
func (self *SQLStorage) SetCdr(cdr *utils.StoredCdr) error {
|
||||
func (self *SQLStorage) SetCdr(cdr *StoredCdr) error {
|
||||
extraFields, err := json.Marshal(cdr.ExtraFields)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -752,12 +752,12 @@ func (self *SQLStorage) SetCdr(cdr *utils.StoredCdr) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SQLStorage) SetRatedCdr(storedCdr *utils.StoredCdr, extraInfo string) error {
|
||||
func (self *SQLStorage) SetRatedCdr(storedCdr *StoredCdr, extraInfo string) error {
|
||||
return errors.New(utils.ERR_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
func (self *SQLStorage) GetStoredCdrs(qryFltr *utils.CdrsFilter) ([]*utils.StoredCdr, int64, error) {
|
||||
var cdrs []*utils.StoredCdr
|
||||
func (self *SQLStorage) GetStoredCdrs(qryFltr *utils.CdrsFilter) ([]*StoredCdr, int64, error) {
|
||||
var cdrs []*StoredCdr
|
||||
// Select string
|
||||
var selectStr string
|
||||
if qryFltr.IgnoreDerived { // We use different tables to query account data in case of derived
|
||||
@@ -999,7 +999,7 @@ func (self *SQLStorage) GetStoredCdrs(qryFltr *utils.CdrsFilter) ([]*utils.Store
|
||||
}
|
||||
}
|
||||
usageDur, _ := time.ParseDuration(strconv.FormatFloat(usage.Float64, 'f', -1, 64) + "s")
|
||||
storCdr := &utils.StoredCdr{
|
||||
storCdr := &StoredCdr{
|
||||
CgrId: cgrid.String, OrderId: orderid, TOR: tor.String, AccId: accid.String, CdrHost: cdrhost.String, CdrSource: cdrsrc.String, ReqType: reqtype.String,
|
||||
Direction: direction.String, Tenant: tenant.String,
|
||||
Category: category.String, Account: account.String, Subject: subject.String, Destination: destination.String,
|
||||
|
||||
509
engine/storedcdr.go
Normal file
509
engine/storedcdr.go
Normal file
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
Real-time Charging System for Telecom & ISP environments
|
||||
Copyright (C) 2012-2015 ITsysCOM GmbH
|
||||
|
||||
This program is free software: you can Storagetribute 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, ornt
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITH*out ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
)
|
||||
|
||||
// Kinda standard of internal CDR, complies to CDR interface also
|
||||
type StoredCdr struct {
|
||||
CgrId string
|
||||
OrderId int64 // Stor order id used as export order id
|
||||
TOR string // type of record, meta-field, should map to one of the TORs hardcoded inside the server <*voice|*data|*sms>
|
||||
AccId 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 <prepaid|postpaid|pseudoprepaid|rated>.
|
||||
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.
|
||||
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
|
||||
MediationRunId string
|
||||
RatedAccount string // Populated out of rating data
|
||||
RatedSubject string
|
||||
Cost float64
|
||||
CostDetails *CallCost // Attach the cost details to CDR when possible
|
||||
Rated bool // Mark the CDR as rated so we do not process it during mediation
|
||||
}
|
||||
|
||||
// Used to multiply usage on export
|
||||
func (storedCdr *StoredCdr) UsageMultiply(multiplyFactor float64, roundDecimals int) {
|
||||
storedCdr.Usage = time.Duration(int(utils.Round(float64(storedCdr.Usage.Nanoseconds())*multiplyFactor, roundDecimals, utils.ROUNDING_MIDDLE))) // Rounding down could introduce a slight loss here but only at nanoseconds level
|
||||
}
|
||||
|
||||
// Used to multiply cost on export
|
||||
func (storedCdr *StoredCdr) CostMultiply(multiplyFactor float64, roundDecimals int) {
|
||||
storedCdr.Cost = utils.Round(storedCdr.Cost*multiplyFactor, roundDecimals, utils.ROUNDING_MIDDLE)
|
||||
}
|
||||
|
||||
// Format cost as string on export
|
||||
func (storedCdr *StoredCdr) FormatCost(shiftDecimals, roundDecimals int) string {
|
||||
cost := storedCdr.Cost
|
||||
if shiftDecimals != 0 {
|
||||
cost = cost * math.Pow10(shiftDecimals)
|
||||
}
|
||||
return strconv.FormatFloat(cost, 'f', roundDecimals, 64)
|
||||
}
|
||||
|
||||
// Formats usage on export
|
||||
func (storedCdr *StoredCdr) FormatUsage(layout string) string {
|
||||
if utils.IsSliceMember([]string{utils.DATA, utils.SMS}, storedCdr.TOR) {
|
||||
return strconv.FormatFloat(utils.Round(storedCdr.Usage.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64)
|
||||
}
|
||||
switch layout {
|
||||
default:
|
||||
return strconv.FormatFloat(float64(storedCdr.Usage.Nanoseconds())/1000000000, 'f', -1, 64)
|
||||
}
|
||||
}
|
||||
|
||||
// Used to retrieve fields as string, primary fields are const labeled
|
||||
func (storedCdr *StoredCdr) FieldAsString(rsrFld *utils.RSRField) string {
|
||||
switch rsrFld.Id {
|
||||
case utils.CGRID:
|
||||
return rsrFld.ParseValue(storedCdr.CgrId)
|
||||
case utils.ORDERID:
|
||||
return rsrFld.ParseValue(strconv.FormatInt(storedCdr.OrderId, 10))
|
||||
case utils.TOR:
|
||||
return rsrFld.ParseValue(storedCdr.TOR)
|
||||
case utils.ACCID:
|
||||
return rsrFld.ParseValue(storedCdr.AccId)
|
||||
case utils.CDRHOST:
|
||||
return rsrFld.ParseValue(storedCdr.CdrHost)
|
||||
case utils.CDRSOURCE:
|
||||
return rsrFld.ParseValue(storedCdr.CdrSource)
|
||||
case utils.REQTYPE:
|
||||
return rsrFld.ParseValue(storedCdr.ReqType)
|
||||
case utils.DIRECTION:
|
||||
return rsrFld.ParseValue(storedCdr.Direction)
|
||||
case utils.TENANT:
|
||||
return rsrFld.ParseValue(storedCdr.Tenant)
|
||||
case utils.CATEGORY:
|
||||
return rsrFld.ParseValue(storedCdr.Category)
|
||||
case utils.ACCOUNT:
|
||||
return rsrFld.ParseValue(storedCdr.Account)
|
||||
case utils.SUBJECT:
|
||||
return rsrFld.ParseValue(storedCdr.Subject)
|
||||
case utils.DESTINATION:
|
||||
return rsrFld.ParseValue(storedCdr.Destination)
|
||||
case utils.SETUP_TIME:
|
||||
return rsrFld.ParseValue(storedCdr.SetupTime.Format(time.RFC3339))
|
||||
case utils.ANSWER_TIME:
|
||||
return rsrFld.ParseValue(storedCdr.AnswerTime.Format(time.RFC3339))
|
||||
case utils.USAGE:
|
||||
return strconv.FormatFloat(utils.Round(storedCdr.Usage.Seconds(), 0, utils.ROUNDING_MIDDLE), 'f', -1, 64)
|
||||
case utils.MEDI_RUNID:
|
||||
return rsrFld.ParseValue(storedCdr.MediationRunId)
|
||||
case utils.RATED_ACCOUNT:
|
||||
return rsrFld.ParseValue(storedCdr.RatedAccount)
|
||||
case utils.RATED_SUBJECT:
|
||||
return rsrFld.ParseValue(storedCdr.RatedSubject)
|
||||
case utils.COST:
|
||||
return rsrFld.ParseValue(strconv.FormatFloat(storedCdr.Cost, 'f', -1, 64)) // Recommended to use FormatCost
|
||||
default:
|
||||
return rsrFld.ParseValue(storedCdr.ExtraFields[rsrFld.Id])
|
||||
}
|
||||
}
|
||||
|
||||
func (storedCdr *StoredCdr) PassesFieldFilter(fieldFilter *utils.RSRField) (bool, string) {
|
||||
if fieldFilter == nil {
|
||||
return true, ""
|
||||
}
|
||||
if fieldFilter.IsStatic() && storedCdr.FieldAsString(&utils.RSRField{Id: fieldFilter.Id}) == storedCdr.FieldAsString(fieldFilter) {
|
||||
return true, storedCdr.FieldAsString(&utils.RSRField{Id: fieldFilter.Id})
|
||||
}
|
||||
preparedFilter := &utils.RSRField{Id: fieldFilter.Id, RSRules: make([]*utils.ReSearchReplace, len(fieldFilter.RSRules))} // Reset rules so they do not point towards same structures as original fieldFilter
|
||||
for idx := range fieldFilter.RSRules {
|
||||
// Hardcode the template with maximum of 5 groups ordered
|
||||
preparedFilter.RSRules[idx] = &utils.ReSearchReplace{SearchRegexp: fieldFilter.RSRules[idx].SearchRegexp, ReplaceTemplate: utils.FILTER_REGEXP_TPL}
|
||||
}
|
||||
preparedVal := storedCdr.FieldAsString(preparedFilter)
|
||||
filteredValue := storedCdr.FieldAsString(fieldFilter)
|
||||
if preparedFilter.RegexpMatched() && (len(preparedVal) == 0 || preparedVal == filteredValue) {
|
||||
return true, filteredValue
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func (storedCdr *StoredCdr) AsStoredCdr() *StoredCdr {
|
||||
return storedCdr
|
||||
}
|
||||
|
||||
// Ability to send the CgrCdr remotely to another CDR server, we do not include rating variables for now
|
||||
func (storedCdr *StoredCdr) AsHttpForm() url.Values {
|
||||
v := url.Values{}
|
||||
for fld, val := range storedCdr.ExtraFields {
|
||||
v.Set(fld, val)
|
||||
}
|
||||
v.Set(utils.TOR, storedCdr.TOR)
|
||||
v.Set(utils.ACCID, storedCdr.AccId)
|
||||
v.Set(utils.CDRHOST, storedCdr.CdrHost)
|
||||
v.Set(utils.CDRSOURCE, storedCdr.CdrSource)
|
||||
v.Set(utils.REQTYPE, storedCdr.ReqType)
|
||||
v.Set(utils.DIRECTION, storedCdr.Direction)
|
||||
v.Set(utils.TENANT, storedCdr.Tenant)
|
||||
v.Set(utils.CATEGORY, storedCdr.Category)
|
||||
v.Set(utils.ACCOUNT, storedCdr.Account)
|
||||
v.Set(utils.SUBJECT, storedCdr.Subject)
|
||||
v.Set(utils.DESTINATION, storedCdr.Destination)
|
||||
v.Set(utils.SETUP_TIME, storedCdr.SetupTime.Format(time.RFC3339))
|
||||
v.Set(utils.ANSWER_TIME, storedCdr.AnswerTime.Format(time.RFC3339))
|
||||
v.Set(utils.USAGE, storedCdr.FormatUsage(utils.SECONDS))
|
||||
return v
|
||||
}
|
||||
|
||||
// Used in mediation, primaryMandatory marks whether missing field out of request represents error or can be ignored
|
||||
func (storedCdr *StoredCdr) ForkCdr(runId string, reqTypeFld, directionFld, tenantFld, categFld, accountFld, subjectFld, destFld, setupTimeFld, answerTimeFld, durationFld *utils.RSRField,
|
||||
extraFlds []*utils.RSRField, primaryMandatory bool) (*StoredCdr, error) {
|
||||
if reqTypeFld == nil {
|
||||
reqTypeFld, _ = utils.NewRSRField(utils.META_DEFAULT)
|
||||
}
|
||||
if reqTypeFld.Id == utils.META_DEFAULT {
|
||||
reqTypeFld.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)
|
||||
}
|
||||
if tenantFld.Id == utils.META_DEFAULT {
|
||||
tenantFld.Id = utils.TENANT
|
||||
}
|
||||
if categFld == nil {
|
||||
categFld, _ = utils.NewRSRField(utils.META_DEFAULT)
|
||||
}
|
||||
if categFld.Id == utils.META_DEFAULT {
|
||||
categFld.Id = utils.CATEGORY
|
||||
}
|
||||
if accountFld == nil {
|
||||
accountFld, _ = utils.NewRSRField(utils.META_DEFAULT)
|
||||
}
|
||||
if accountFld.Id == utils.META_DEFAULT {
|
||||
accountFld.Id = utils.ACCOUNT
|
||||
}
|
||||
if subjectFld == nil {
|
||||
subjectFld, _ = utils.NewRSRField(utils.META_DEFAULT)
|
||||
}
|
||||
if subjectFld.Id == utils.META_DEFAULT {
|
||||
subjectFld.Id = utils.SUBJECT
|
||||
}
|
||||
if destFld == nil {
|
||||
destFld, _ = utils.NewRSRField(utils.META_DEFAULT)
|
||||
}
|
||||
if destFld.Id == utils.META_DEFAULT {
|
||||
destFld.Id = utils.DESTINATION
|
||||
}
|
||||
if setupTimeFld == nil {
|
||||
setupTimeFld, _ = utils.NewRSRField(utils.META_DEFAULT)
|
||||
}
|
||||
if setupTimeFld.Id == utils.META_DEFAULT {
|
||||
setupTimeFld.Id = utils.SETUP_TIME
|
||||
}
|
||||
if answerTimeFld == nil {
|
||||
answerTimeFld, _ = utils.NewRSRField(utils.META_DEFAULT)
|
||||
}
|
||||
if answerTimeFld.Id == utils.META_DEFAULT {
|
||||
answerTimeFld.Id = utils.ANSWER_TIME
|
||||
}
|
||||
if durationFld == nil {
|
||||
durationFld, _ = utils.NewRSRField(utils.META_DEFAULT)
|
||||
}
|
||||
if durationFld.Id == utils.META_DEFAULT {
|
||||
durationFld.Id = utils.USAGE
|
||||
}
|
||||
var err error
|
||||
frkStorCdr := new(StoredCdr)
|
||||
frkStorCdr.CgrId = storedCdr.CgrId
|
||||
frkStorCdr.TOR = storedCdr.TOR
|
||||
frkStorCdr.MediationRunId = runId
|
||||
frkStorCdr.Cost = -1.0 // Default for non-rated CDR
|
||||
frkStorCdr.AccId = storedCdr.AccId
|
||||
frkStorCdr.CdrHost = storedCdr.CdrHost
|
||||
frkStorCdr.CdrSource = storedCdr.CdrSource
|
||||
frkStorCdr.ReqType = storedCdr.FieldAsString(reqTypeFld)
|
||||
if primaryMandatory && len(frkStorCdr.ReqType) == 0 {
|
||||
return nil, errors.New(fmt.Sprintf("%s:%s:%s", utils.ERR_MANDATORY_IE_MISSING, utils.REQTYPE, reqTypeFld.Id))
|
||||
}
|
||||
frkStorCdr.Direction = storedCdr.FieldAsString(directionFld)
|
||||
if primaryMandatory && len(frkStorCdr.Direction) == 0 {
|
||||
return nil, errors.New(fmt.Sprintf("%s:%s:%s", utils.ERR_MANDATORY_IE_MISSING, utils.DIRECTION, directionFld.Id))
|
||||
}
|
||||
frkStorCdr.Tenant = storedCdr.FieldAsString(tenantFld)
|
||||
if primaryMandatory && len(frkStorCdr.Tenant) == 0 {
|
||||
return nil, errors.New(fmt.Sprintf("%s:%s:%s", utils.ERR_MANDATORY_IE_MISSING, utils.TENANT, tenantFld.Id))
|
||||
}
|
||||
frkStorCdr.Category = storedCdr.FieldAsString(categFld)
|
||||
if primaryMandatory && len(frkStorCdr.Category) == 0 {
|
||||
return nil, errors.New(fmt.Sprintf("%s:%s:%s", utils.ERR_MANDATORY_IE_MISSING, utils.CATEGORY, categFld.Id))
|
||||
}
|
||||
frkStorCdr.Account = storedCdr.FieldAsString(accountFld)
|
||||
if primaryMandatory && len(frkStorCdr.Account) == 0 {
|
||||
return nil, errors.New(fmt.Sprintf("%s:%s:%s", utils.ERR_MANDATORY_IE_MISSING, utils.ACCOUNT, accountFld.Id))
|
||||
}
|
||||
frkStorCdr.Subject = storedCdr.FieldAsString(subjectFld)
|
||||
if primaryMandatory && len(frkStorCdr.Subject) == 0 {
|
||||
return nil, errors.New(fmt.Sprintf("%s:%s:%s", utils.ERR_MANDATORY_IE_MISSING, utils.SUBJECT, subjectFld.Id))
|
||||
}
|
||||
frkStorCdr.Destination = storedCdr.FieldAsString(destFld)
|
||||
if primaryMandatory && len(frkStorCdr.Destination) == 0 && frkStorCdr.TOR == utils.VOICE {
|
||||
return nil, errors.New(fmt.Sprintf("%s:%s:%s", utils.ERR_MANDATORY_IE_MISSING, utils.DESTINATION, destFld.Id))
|
||||
}
|
||||
sTimeStr := storedCdr.FieldAsString(setupTimeFld)
|
||||
if primaryMandatory && len(sTimeStr) == 0 {
|
||||
return nil, errors.New(fmt.Sprintf("%s:%s:%s", utils.ERR_MANDATORY_IE_MISSING, utils.SETUP_TIME, setupTimeFld.Id))
|
||||
} else if frkStorCdr.SetupTime, err = utils.ParseTimeDetectLayout(sTimeStr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aTimeStr := storedCdr.FieldAsString(answerTimeFld)
|
||||
if primaryMandatory && len(aTimeStr) == 0 {
|
||||
return nil, errors.New(fmt.Sprintf("%s:%s:%s", utils.ERR_MANDATORY_IE_MISSING, utils.ANSWER_TIME, answerTimeFld.Id))
|
||||
} else if frkStorCdr.AnswerTime, err = utils.ParseTimeDetectLayout(aTimeStr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
durStr := storedCdr.FieldAsString(durationFld)
|
||||
if primaryMandatory && len(durStr) == 0 {
|
||||
return nil, errors.New(fmt.Sprintf("%s:%s:%s", utils.ERR_MANDATORY_IE_MISSING, utils.USAGE, durationFld.Id))
|
||||
} else if frkStorCdr.Usage, err = utils.ParseDurationWithSecs(durStr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
frkStorCdr.ExtraFields = make(map[string]string, len(extraFlds))
|
||||
for _, fld := range extraFlds {
|
||||
frkStorCdr.ExtraFields[fld.Id] = storedCdr.FieldAsString(fld)
|
||||
}
|
||||
return frkStorCdr, nil
|
||||
}
|
||||
|
||||
func (storedCdr *StoredCdr) AsCgrExtCdr() *CgrExtCdr {
|
||||
return &CgrExtCdr{CgrId: storedCdr.CgrId,
|
||||
OrderId: storedCdr.OrderId,
|
||||
TOR: storedCdr.TOR,
|
||||
AccId: storedCdr.AccId,
|
||||
CdrHost: storedCdr.CdrHost,
|
||||
CdrSource: storedCdr.CdrSource,
|
||||
ReqType: storedCdr.ReqType,
|
||||
Direction: storedCdr.Direction,
|
||||
Tenant: storedCdr.Tenant,
|
||||
Category: storedCdr.Category,
|
||||
Account: storedCdr.Account,
|
||||
Subject: storedCdr.Subject,
|
||||
Destination: storedCdr.Destination,
|
||||
SetupTime: storedCdr.SetupTime.Format(time.RFC3339),
|
||||
AnswerTime: storedCdr.AnswerTime.Format(time.RFC3339),
|
||||
Usage: storedCdr.FormatUsage(utils.SECONDS),
|
||||
ExtraFields: storedCdr.ExtraFields,
|
||||
MediationRunId: storedCdr.MediationRunId,
|
||||
RatedAccount: storedCdr.RatedAccount,
|
||||
RatedSubject: storedCdr.RatedSubject,
|
||||
Cost: storedCdr.Cost,
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of Event interface, used in tests
|
||||
func (storedCdr *StoredCdr) AsEvent(ignored string) Event {
|
||||
return Event(storedCdr)
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetName() string {
|
||||
return storedCdr.CdrSource
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetCgrId() string {
|
||||
return storedCdr.CgrId
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetUUID() string {
|
||||
return storedCdr.AccId
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetSessionIds() []string {
|
||||
return []string{storedCdr.GetUUID()}
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetDirection(fieldName string) string {
|
||||
if utils.IsSliceMember([]string{utils.DIRECTION, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.Direction
|
||||
}
|
||||
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
|
||||
return fieldName[len(utils.STATIC_VALUE_PREFIX):]
|
||||
}
|
||||
return storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetSubject(fieldName string) string {
|
||||
if utils.IsSliceMember([]string{utils.SUBJECT, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.Subject
|
||||
}
|
||||
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
|
||||
return fieldName[len(utils.STATIC_VALUE_PREFIX):]
|
||||
}
|
||||
return storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetAccount(fieldName string) string {
|
||||
if utils.IsSliceMember([]string{utils.ACCOUNT, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.Account
|
||||
}
|
||||
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
|
||||
return fieldName[len(utils.STATIC_VALUE_PREFIX):]
|
||||
}
|
||||
return storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetDestination(fieldName string) string {
|
||||
if utils.IsSliceMember([]string{utils.DESTINATION, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.Destination
|
||||
}
|
||||
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
|
||||
return fieldName[len(utils.STATIC_VALUE_PREFIX):]
|
||||
}
|
||||
return storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetCallDestNr(fieldName string) string {
|
||||
if utils.IsSliceMember([]string{utils.DESTINATION, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.Destination
|
||||
}
|
||||
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
|
||||
return fieldName[len(utils.STATIC_VALUE_PREFIX):]
|
||||
}
|
||||
return storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetCategory(fieldName string) string {
|
||||
if utils.IsSliceMember([]string{utils.CATEGORY, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.Category
|
||||
}
|
||||
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
|
||||
return fieldName[len(utils.STATIC_VALUE_PREFIX):]
|
||||
}
|
||||
return storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetTenant(fieldName string) string {
|
||||
if utils.IsSliceMember([]string{utils.TENANT, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.Tenant
|
||||
}
|
||||
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
|
||||
return fieldName[len(utils.STATIC_VALUE_PREFIX):]
|
||||
}
|
||||
return storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetReqType(fieldName string) string {
|
||||
if utils.IsSliceMember([]string{utils.REQTYPE, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.ReqType
|
||||
}
|
||||
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
|
||||
return fieldName[len(utils.STATIC_VALUE_PREFIX):]
|
||||
}
|
||||
return storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetSetupTime(fieldName string) (time.Time, error) {
|
||||
if utils.IsSliceMember([]string{utils.SETUP_TIME, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.SetupTime, nil
|
||||
}
|
||||
var sTimeVal string
|
||||
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
|
||||
sTimeVal = fieldName[len(utils.STATIC_VALUE_PREFIX):]
|
||||
} else {
|
||||
sTimeVal = storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
return utils.ParseTimeDetectLayout(sTimeVal)
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetAnswerTime(fieldName string) (time.Time, error) {
|
||||
if utils.IsSliceMember([]string{utils.ANSWER_TIME, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.AnswerTime, nil
|
||||
}
|
||||
var aTimeVal string
|
||||
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
|
||||
aTimeVal = fieldName[len(utils.STATIC_VALUE_PREFIX):]
|
||||
} else {
|
||||
aTimeVal = storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
return utils.ParseTimeDetectLayout(aTimeVal)
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetEndTime() (time.Time, error) {
|
||||
return storedCdr.AnswerTime.Add(storedCdr.Usage), nil
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetDuration(fieldName string) (time.Duration, error) {
|
||||
if utils.IsSliceMember([]string{utils.USAGE, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.Usage, nil
|
||||
}
|
||||
var durVal string
|
||||
if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value
|
||||
durVal = fieldName[len(utils.STATIC_VALUE_PREFIX):]
|
||||
} else {
|
||||
durVal = storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
return utils.ParseDurationWithSecs(durVal)
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetOriginatorIP(fieldName string) string {
|
||||
if utils.IsSliceMember([]string{utils.CDRHOST, utils.META_DEFAULT}, fieldName) {
|
||||
return storedCdr.CdrHost
|
||||
}
|
||||
return storedCdr.FieldAsString(&utils.RSRField{Id: fieldName})
|
||||
}
|
||||
func (storedCdr *StoredCdr) GetExtraFields() map[string]string {
|
||||
return storedCdr.ExtraFields
|
||||
}
|
||||
func (storedCdr *StoredCdr) MissingParameter() bool {
|
||||
return len(storedCdr.AccId) == 0 ||
|
||||
len(storedCdr.Category) == 0 ||
|
||||
len(storedCdr.Tenant) == 0 ||
|
||||
len(storedCdr.Account) == 0 ||
|
||||
len(storedCdr.Destination) == 0
|
||||
}
|
||||
func (storedCdr *StoredCdr) ParseEventValue(rsrFld *utils.RSRField) string {
|
||||
return storedCdr.FieldAsString(rsrFld)
|
||||
}
|
||||
func (storedCdr *StoredCdr) String() string {
|
||||
mrsh, _ := json.Marshal(storedCdr)
|
||||
return string(mrsh)
|
||||
}
|
||||
|
||||
type CgrExtCdr struct {
|
||||
CgrId string
|
||||
OrderId int64
|
||||
TOR string
|
||||
AccId string
|
||||
CdrHost string
|
||||
CdrSource string
|
||||
ReqType string
|
||||
Direction string
|
||||
Tenant string
|
||||
Category string
|
||||
Account string
|
||||
Subject string
|
||||
Destination string
|
||||
SetupTime string
|
||||
AnswerTime string
|
||||
Usage string
|
||||
ExtraFields map[string]string
|
||||
MediationRunId string
|
||||
RatedAccount string
|
||||
RatedSubject string
|
||||
Cost float64
|
||||
}
|
||||
48
engine/storedcdr_local_test.go
Normal file
48
engine/storedcdr_local_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
Real-time Charging System for Telecom & ISP environments
|
||||
Copyright (C) 2012-2015 ITsysCOM GmbH
|
||||
|
||||
This program is free software: you can Storagetribute 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 WITH*out ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Arguments received via test command
|
||||
var testLocal = flag.Bool("local", false, "Perform the tests only on local test environment, not by default.") // This flag will be passed here via "go test -local" args
|
||||
var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here")
|
||||
|
||||
// Sample HttpJsonPost, more for usage purposes
|
||||
func TestHttpJsonPost(t *testing.T) {
|
||||
if !*testLocal {
|
||||
return
|
||||
}
|
||||
cdrOut := &CgrExtCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1",
|
||||
CdrSource: utils.UNIT_TEST, ReqType: utils.META_RATED, Direction: "*out", 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(),
|
||||
MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: "0.00000001", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
|
||||
}
|
||||
if _, err := utils.HttpJsonPost("http://localhost:8000", false, cdrOut); err == nil || err.Error() != "Post http://localhost:8000: dial tcp 127.0.0.1:8000: connection refused" {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
478
engine/storedcdr_test.go
Normal file
478
engine/storedcdr_test.go
Normal file
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
Real-time Charging System for Telecom & ISP environments
|
||||
Copyright (C) 2012-2015 ITsysCOM GmbH
|
||||
|
||||
This program is free software: you can Storagetribute 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 WITH*out ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"github.com/cgrates/cgrates/utils"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestStoredCdrInterfaces(t *testing.T) {
|
||||
storedCdr := new(StoredCdr)
|
||||
var _ RawCdr = storedCdr
|
||||
var _ Event = storedCdr
|
||||
}
|
||||
|
||||
func TestFieldAsString(t *testing.T) {
|
||||
cdr := StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: 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), MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
|
||||
Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans",
|
||||
}
|
||||
if cdr.FieldAsString(&utils.RSRField{Id: utils.CGRID}) != cdr.CgrId ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.ORDERID}) != "123" ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.TOR}) != utils.VOICE ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.ACCID}) != cdr.AccId ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.CDRHOST}) != cdr.CdrHost ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.CDRSOURCE}) != cdr.CdrSource ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.REQTYPE}) != cdr.ReqType ||
|
||||
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.MEDI_RUNID}) != cdr.MediationRunId ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.COST}) != "1.01" ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.RATED_ACCOUNT}) != "dan" ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.RATED_SUBJECT}) != "dans" ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: "field_extr1"}) != cdr.ExtraFields["field_extr1"] ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: "fieldextr2"}) != cdr.ExtraFields["fieldextr2"] ||
|
||||
cdr.FieldAsString(&utils.RSRField{Id: "dummy_field"}) != "" {
|
||||
t.Error("Unexpected filed value received",
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.CGRID}) != cdr.CgrId,
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.ORDERID}) != "123",
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.TOR}) != utils.VOICE,
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.ACCID}) != cdr.AccId,
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.CDRHOST}) != cdr.CdrHost,
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.CDRSOURCE}) != cdr.CdrSource,
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.REQTYPE}) != cdr.ReqType,
|
||||
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.MEDI_RUNID}) != cdr.MediationRunId,
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.RATED_ACCOUNT}) != "dan",
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.RATED_SUBJECT}) != "dans",
|
||||
cdr.FieldAsString(&utils.RSRField{Id: utils.COST}) != "1.01",
|
||||
cdr.FieldAsString(&utils.RSRField{Id: "field_extr1"}) != cdr.ExtraFields["field_extr1"],
|
||||
cdr.FieldAsString(&utils.RSRField{Id: "fieldextr2"}) != cdr.ExtraFields["fieldextr2"],
|
||||
cdr.FieldAsString(&utils.RSRField{Id: "dummy_field"}) != "")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPassesFieldFilter(t *testing.T) {
|
||||
cdr := &StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: 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), MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
|
||||
}
|
||||
if pass, _ := cdr.PassesFieldFilter(nil); !pass {
|
||||
t.Error("Not passing filter")
|
||||
}
|
||||
acntPrefxFltr, _ := utils.NewRSRField(`~account:s/(.+)/1001/`)
|
||||
if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); !pass {
|
||||
t.Error("Not passing filter")
|
||||
}
|
||||
acntPrefxFltr, _ = utils.NewRSRField(`~account:s/^(10)\d\d$/10/`)
|
||||
if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); !pass {
|
||||
t.Error("Not passing valid filter")
|
||||
}
|
||||
acntPrefxFltr, _ = utils.NewRSRField(`~account:s/^\d(10)\d$/10/`)
|
||||
if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); pass {
|
||||
t.Error("Passing filter")
|
||||
}
|
||||
acntPrefxFltr, _ = utils.NewRSRField(`~account:s/^(10)\d\d$/010/`)
|
||||
if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); pass {
|
||||
t.Error("Passing filter")
|
||||
}
|
||||
acntPrefxFltr, _ = utils.NewRSRField(`~account:s/^1010$/1010/`)
|
||||
if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); pass {
|
||||
t.Error("Passing filter")
|
||||
}
|
||||
torFltr, _ := utils.NewRSRField(`^tor::*voice/`)
|
||||
if pass, _ := cdr.PassesFieldFilter(torFltr); !pass {
|
||||
t.Error("Not passing filter")
|
||||
}
|
||||
torFltr, _ = utils.NewRSRField(`^tor/*data/`)
|
||||
if pass, _ := cdr.PassesFieldFilter(torFltr); pass {
|
||||
t.Error("Passing filter")
|
||||
}
|
||||
inexistentFieldFltr, _ := utils.NewRSRField(`^fakefield/fakevalue/`)
|
||||
if pass, _ := cdr.PassesFieldFilter(inexistentFieldFltr); pass {
|
||||
t.Error("Passing filter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPassesFieldFilterDn1(t *testing.T) {
|
||||
cdr := &StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), Account: "futurem0005",
|
||||
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
|
||||
}
|
||||
acntPrefxFltr, _ := utils.NewRSRField(`~account:s/^\w+[shmp]\d{4}$//`)
|
||||
if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); !pass {
|
||||
t.Error("Not passing valid filter")
|
||||
}
|
||||
|
||||
cdr = &StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), Account: "futurem00005",
|
||||
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
|
||||
}
|
||||
if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); pass {
|
||||
t.Error("Should not pass filter")
|
||||
}
|
||||
cdr = &StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), Account: "0402129281",
|
||||
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
|
||||
}
|
||||
acntPrefxFltr, _ = utils.NewRSRField(`~account:s/^0\d{9}$//`)
|
||||
if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); !pass {
|
||||
t.Error("Not passing valid filter")
|
||||
}
|
||||
acntPrefxFltr, _ = utils.NewRSRField(`~account:s/^0(\d{9})$/placeholder/`)
|
||||
if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); pass {
|
||||
t.Error("Should not pass filter")
|
||||
}
|
||||
cdr = &StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), Account: "04021292812",
|
||||
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
|
||||
}
|
||||
if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); pass {
|
||||
t.Error("Should not pass filter")
|
||||
}
|
||||
cdr = &StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), Account: "0162447222",
|
||||
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
|
||||
}
|
||||
if acntPrefxFltr, err := utils.NewRSRField(`~account:s/^0\d{9}$//`); err != nil {
|
||||
t.Error("Unexpected parse error", err)
|
||||
} else if acntPrefxFltr == nil {
|
||||
t.Error("Failed parsing rule")
|
||||
} else if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); !pass {
|
||||
t.Error("Not passing valid filter")
|
||||
}
|
||||
if acntPrefxFltr, err := utils.NewRSRField(`~account:s/^\w+[shmp]\d{4}$//`); err != nil {
|
||||
t.Error("Unexpected parse error", err)
|
||||
} else if acntPrefxFltr == nil {
|
||||
t.Error("Failed parsing rule")
|
||||
} else if pass, _ := cdr.PassesFieldFilter(acntPrefxFltr); pass {
|
||||
t.Error("Should not pass filter")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUsageMultiply(t *testing.T) {
|
||||
cdr := StoredCdr{Usage: time.Duration(10) * time.Second}
|
||||
if cdr.UsageMultiply(1024.0, 0); cdr.Usage != time.Duration(10240)*time.Second {
|
||||
t.Errorf("Unexpected usage after multiply: %v", cdr.Usage.Nanoseconds())
|
||||
}
|
||||
cdr = StoredCdr{Usage: time.Duration(10240) * time.Second} // Simulate conversion back, gives out a bit odd result but this can be rounded on export
|
||||
expectDuration, _ := time.ParseDuration("10.000005120s")
|
||||
if cdr.UsageMultiply(0.000976563, 0); cdr.Usage != expectDuration {
|
||||
t.Errorf("Unexpected usage after multiply: %v", cdr.Usage.Nanoseconds())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCostMultiply(t *testing.T) {
|
||||
cdr := StoredCdr{Cost: 1.01}
|
||||
if cdr.CostMultiply(1.19, 4); cdr.Cost != 1.2019 {
|
||||
t.Errorf("Unexpected cost after multiply: %v", cdr.Cost)
|
||||
}
|
||||
cdr = StoredCdr{Cost: 1.01}
|
||||
if cdr.CostMultiply(1000, 0); cdr.Cost != 1010 {
|
||||
t.Errorf("Unexpected cost after multiply: %v", cdr.Cost)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatCost(t *testing.T) {
|
||||
cdr := StoredCdr{Cost: 1.01}
|
||||
if cdr.FormatCost(0, 4) != "1.0100" {
|
||||
t.Error("Unexpected format of the cost: ", cdr.FormatCost(0, 4))
|
||||
}
|
||||
cdr = StoredCdr{Cost: 1.01001}
|
||||
if cdr.FormatCost(0, 4) != "1.0100" {
|
||||
t.Error("Unexpected format of the cost: ", cdr.FormatCost(0, 4))
|
||||
}
|
||||
if cdr.FormatCost(2, 0) != "101" {
|
||||
t.Error("Unexpected format of the cost: ", cdr.FormatCost(2, 0))
|
||||
}
|
||||
if cdr.FormatCost(1, 0) != "10" {
|
||||
t.Error("Unexpected format of the cost: ", cdr.FormatCost(1, 0))
|
||||
}
|
||||
if cdr.FormatCost(2, 3) != "101.001" {
|
||||
t.Error("Unexpected format of the cost: ", cdr.FormatCost(2, 3))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFormatUsage(t *testing.T) {
|
||||
cdr := StoredCdr{Usage: time.Duration(10) * time.Second}
|
||||
if cdr.FormatUsage(utils.SECONDS) != "10" {
|
||||
t.Error("Wrong usage format: ", cdr.FormatUsage(utils.SECONDS))
|
||||
}
|
||||
if cdr.FormatUsage("default") != "10" {
|
||||
t.Error("Wrong usage format: ", cdr.FormatUsage("default"))
|
||||
}
|
||||
cdr = StoredCdr{TOR: DATA, Usage: time.Duration(1640113000000000)}
|
||||
if cdr.FormatUsage("default") != "1640113" {
|
||||
t.Error("Wrong usage format: ", cdr.FormatUsage("default"))
|
||||
}
|
||||
cdr = StoredCdr{Usage: time.Duration(2) * time.Millisecond}
|
||||
if cdr.FormatUsage("default") != "0.002" {
|
||||
t.Error("Wrong usage format: ", cdr.FormatUsage("default"))
|
||||
}
|
||||
cdr = StoredCdr{Usage: time.Duration(1002) * time.Millisecond}
|
||||
if cdr.FormatUsage("default") != "1.002" {
|
||||
t.Error("Wrong usage format: ", cdr.FormatUsage("default"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoredCdrAsHttpForm(t *testing.T) {
|
||||
storCdr := StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: 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), MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, RatedSubject: "dans", Cost: 1.01,
|
||||
}
|
||||
cdrForm := storCdr.AsHttpForm()
|
||||
if cdrForm.Get(utils.TOR) != utils.VOICE {
|
||||
t.Errorf("Expected: %s, received: %s", utils.VOICE, cdrForm.Get(utils.TOR))
|
||||
}
|
||||
if cdrForm.Get(utils.ACCID) != "dsafdsaf" {
|
||||
t.Errorf("Expected: %s, received: %s", "dsafdsaf", cdrForm.Get(utils.ACCID))
|
||||
}
|
||||
if cdrForm.Get(utils.CDRHOST) != "192.168.1.1" {
|
||||
t.Errorf("Expected: %s, received: %s", "192.168.1.1", cdrForm.Get(utils.CDRHOST))
|
||||
}
|
||||
if cdrForm.Get(utils.CDRSOURCE) != utils.UNIT_TEST {
|
||||
t.Errorf("Expected: %s, received: %s", utils.UNIT_TEST, cdrForm.Get(utils.CDRSOURCE))
|
||||
}
|
||||
if cdrForm.Get(utils.REQTYPE) != utils.META_RATED {
|
||||
t.Errorf("Expected: %s, received: %s", utils.META_RATED, cdrForm.Get(utils.REQTYPE))
|
||||
}
|
||||
if cdrForm.Get(utils.DIRECTION) != "*out" {
|
||||
t.Errorf("Expected: %s, received: %s", "*out", cdrForm.Get(utils.DIRECTION))
|
||||
}
|
||||
if cdrForm.Get(utils.TENANT) != "cgrates.org" {
|
||||
t.Errorf("Expected: %s, received: %s", "cgrates.org", cdrForm.Get(utils.TENANT))
|
||||
}
|
||||
if cdrForm.Get(utils.CATEGORY) != "call" {
|
||||
t.Errorf("Expected: %s, received: %s", "call", cdrForm.Get(utils.CATEGORY))
|
||||
}
|
||||
if cdrForm.Get(utils.ACCOUNT) != "1001" {
|
||||
t.Errorf("Expected: %s, received: %s", "1001", cdrForm.Get(utils.ACCOUNT))
|
||||
}
|
||||
if cdrForm.Get(utils.SUBJECT) != "1001" {
|
||||
t.Errorf("Expected: %s, received: %s", "1001", cdrForm.Get(utils.SUBJECT))
|
||||
}
|
||||
if cdrForm.Get(utils.DESTINATION) != "1002" {
|
||||
t.Errorf("Expected: %s, received: %s", "1002", cdrForm.Get(utils.DESTINATION))
|
||||
}
|
||||
if cdrForm.Get(utils.SETUP_TIME) != "2013-11-07T08:42:20Z" {
|
||||
t.Errorf("Expected: %s, received: %s", "2013-11-07T08:42:20Z", cdrForm.Get(utils.SETUP_TIME))
|
||||
}
|
||||
if cdrForm.Get(utils.ANSWER_TIME) != "2013-11-07T08:42:26Z" {
|
||||
t.Errorf("Expected: %s, received: %s", "2013-11-07T08:42:26Z", cdrForm.Get(utils.ANSWER_TIME))
|
||||
}
|
||||
if cdrForm.Get(utils.USAGE) != "10" {
|
||||
t.Errorf("Expected: %s, received: %s", "10", cdrForm.Get(utils.USAGE))
|
||||
}
|
||||
if cdrForm.Get("field_extr1") != "val_extr1" {
|
||||
t.Errorf("Expected: %s, received: %s", "val_extr1", cdrForm.Get("field_extr1"))
|
||||
}
|
||||
if cdrForm.Get("fieldextr2") != "valextr2" {
|
||||
t.Errorf("Expected: %s, received: %s", "valextr2", cdrForm.Get("fieldextr2"))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoredCdrForkCdr(t *testing.T) {
|
||||
storCdr := StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE,
|
||||
AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: 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), MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "field_extr2": "valextr2"}, Cost: 1.01, RatedSubject: "dans",
|
||||
}
|
||||
rtSampleCdrOut, err := storCdr.ForkCdr("sample_run1", &utils.RSRField{Id: utils.REQTYPE}, &utils.RSRField{Id: utils.DIRECTION}, &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.ANSWER_TIME}, &utils.RSRField{Id: utils.USAGE},
|
||||
[]*utils.RSRField{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "field_extr2"}}, true)
|
||||
if err != nil {
|
||||
t.Error("Unexpected error received", err)
|
||||
}
|
||||
expctSplRatedCdr := &StoredCdr{CgrId: storCdr.CgrId, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: 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, ExtraFields: map[string]string{"field_extr1": "val_extr1", "field_extr2": "valextr2"}, MediationRunId: "sample_run1", Cost: -1}
|
||||
if !reflect.DeepEqual(expctSplRatedCdr, rtSampleCdrOut) {
|
||||
t.Errorf("Expected: %v, received: %v", expctSplRatedCdr, rtSampleCdrOut)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoredCdrForkCdrStaticVals(t *testing.T) {
|
||||
storCdr := StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: 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), MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
|
||||
}
|
||||
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")
|
||||
rsrStFS, _ := utils.NewRSRField("^first_subject")
|
||||
rsrStST, _ := utils.NewRSRField("^2013-12-07T08:42:24Z")
|
||||
rsrStAT, _ := utils.NewRSRField("^2013-12-07T08:42:26Z")
|
||||
rsrStDur, _ := utils.NewRSRField("^12s")
|
||||
rtCdrOut2, err := storCdr.ForkCdr("wholesale_run", rsrStPostpaid, rsrStIn, rsrStCgr, rsrStPC, rsrStFA, rsrStFS, &utils.RSRField{Id: "destination"}, rsrStST, rsrStAT, rsrStDur,
|
||||
[]*utils.RSRField{}, true)
|
||||
|
||||
if err != nil {
|
||||
t.Error("Unexpected error received", err)
|
||||
}
|
||||
expctRatedCdr2 := &StoredCdr{CgrId: storCdr.CgrId, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_POSTPAID,
|
||||
Direction: "*in", 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,
|
||||
ExtraFields: map[string]string{}, MediationRunId: "wholesale_run", Cost: -1}
|
||||
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: "direction"}, &utils.RSRField{Id: "tenant"}, &utils.RSRField{Id: "tor"}, &utils.RSRField{Id: "account"},
|
||||
&utils.RSRField{Id: "subject"}, &utils.RSRField{Id: "destination"}, &utils.RSRField{Id: "setup_time"}, &utils.RSRField{Id: "answer_time"}, &utils.RSRField{Id: "duration"},
|
||||
[]*utils.RSRField{}, true)
|
||||
if err == nil {
|
||||
t.Error("Failed to detect missing header")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoredCdrForkCdrFromMetaDefaults(t *testing.T) {
|
||||
storCdr := StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: 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), MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
|
||||
}
|
||||
expctCdr := &StoredCdr{CgrId: storCdr.CgrId, TOR: utils.VOICE, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: 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,
|
||||
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
|
||||
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{&utils.RSRField{Id: "field_extr1"}, &utils.RSRField{Id: "fieldextr2"}}, true)
|
||||
if err != nil {
|
||||
t.Fatal("Unexpected error received", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expctCdr, cdrOut) {
|
||||
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,
|
||||
[]*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) {
|
||||
t.Errorf("Expected: %v, received: %v", expctCdr, cdrOut)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoredCdrAsCgrExtCdr(t *testing.T) {
|
||||
storCdr := StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE,
|
||||
AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: 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), MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: time.Duration(10), ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans",
|
||||
}
|
||||
expectOutCdr := &CgrExtCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE,
|
||||
AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: utils.UNIT_TEST, ReqType: utils.META_RATED, Direction: "*out",
|
||||
Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
|
||||
SetupTime: "2013-11-07T08:42:20Z", AnswerTime: "2013-11-07T08:42:26Z", MediationRunId: utils.DEFAULT_RUNID,
|
||||
Usage: "0.00000001", ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01, RatedAccount: "dan", RatedSubject: "dans",
|
||||
}
|
||||
if cdrOut := storCdr.AsCgrExtCdr(); !reflect.DeepEqual(expectOutCdr, cdrOut) {
|
||||
t.Errorf("Expected: %+v, received: %+v", expectOutCdr, cdrOut)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoredCdrEventFields(t *testing.T) {
|
||||
cdr := &StoredCdr{CgrId: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, TOR: utils.VOICE, AccId: "dsafdsaf",
|
||||
CdrHost: "192.168.1.1", CdrSource: "test", ReqType: 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),
|
||||
MediationRunId: utils.DEFAULT_RUNID, Usage: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"},
|
||||
Cost: 1.01, RatedAccount: "dan", RatedSubject: "dan"}
|
||||
if ev := cdr.AsEvent(""); ev != Event(cdr) {
|
||||
t.Error("Received: ", ev)
|
||||
}
|
||||
if res := cdr.GetName(); res != "test" {
|
||||
t.Error("Received: ", res)
|
||||
}
|
||||
if res := cdr.GetCgrId(); res != utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()) {
|
||||
t.Error("Received: ", res)
|
||||
}
|
||||
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)
|
||||
}
|
||||
if res := cdr.GetAccount(utils.META_DEFAULT); res != "dan" {
|
||||
t.Error("Received: ", res)
|
||||
}
|
||||
if res := cdr.GetDestination(utils.META_DEFAULT); res != "1002" {
|
||||
t.Error("Received: ", res)
|
||||
}
|
||||
if res := cdr.GetCallDestNr(utils.META_DEFAULT); res != "1002" {
|
||||
t.Error("Received: ", res)
|
||||
}
|
||||
if res := cdr.GetCategory(utils.META_DEFAULT); res != "call" {
|
||||
t.Error("Received: ", res)
|
||||
}
|
||||
if res := cdr.GetTenant(utils.META_DEFAULT); res != "cgrates.org" {
|
||||
t.Error("Received: ", res)
|
||||
}
|
||||
if res := cdr.GetReqType(utils.META_DEFAULT); res != utils.META_RATED {
|
||||
t.Error("Received: ", res)
|
||||
}
|
||||
if st, _ := cdr.GetSetupTime(utils.META_DEFAULT); st != time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC) {
|
||||
t.Error("Received: ", st)
|
||||
}
|
||||
if at, _ := cdr.GetAnswerTime(utils.META_DEFAULT); at != time.Date(2013, 11, 7, 8, 42, 27, 0, time.UTC) {
|
||||
t.Error("Received: ", at)
|
||||
}
|
||||
if et, _ := cdr.GetEndTime(); et != time.Date(2013, 11, 7, 8, 42, 37, 0, time.UTC) {
|
||||
t.Error("Received: ", et)
|
||||
}
|
||||
if dur, _ := cdr.GetDuration(utils.META_DEFAULT); dur != cdr.Usage {
|
||||
t.Error("Received: ", dur)
|
||||
}
|
||||
if res := cdr.GetOriginatorIP(utils.META_DEFAULT); res != cdr.CdrHost {
|
||||
t.Error("Received: ", res)
|
||||
}
|
||||
if extraFlds := cdr.GetExtraFields(); !reflect.DeepEqual(cdr.ExtraFields, extraFlds) {
|
||||
t.Error("Received: ", extraFlds)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user