Refactoring CDRs to support RSRFields

This commit is contained in:
DanB
2014-05-05 20:14:40 +02:00
parent 7bda45fcce
commit f7abbacfe5
39 changed files with 848 additions and 1205 deletions

View File

@@ -19,10 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package utils
import (
"errors"
"fmt"
"net/http"
"strings"
"time"
)
@@ -78,7 +75,7 @@ func (cgrCdr CgrCdr) GetDestination() string {
}
func (cgrCdr CgrCdr) GetCategory() string {
return cgrCdr[Category]
return cgrCdr[CATEGORY]
}
func (cgrCdr CgrCdr) GetTenant() string {
@@ -108,133 +105,23 @@ func (cgrCdr CgrCdr) GetDuration() (time.Duration, error) {
return ParseDurationWithSecs(cgrCdr[DURATION])
}
// Used in mediation, fieldsMandatory marks whether missing field out of request represents error or can be ignored
// If the fields in parameters start with ^ their value is considered instead of dynamically retrieving it from CDR
func (cgrCdr CgrCdr) ForkCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) {
if IsSliceMember([]string{runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld, answerTimeFld, durationFld}, "") {
return nil, errors.New(fmt.Sprintf("%s:FieldName", ERR_MANDATORY_IE_MISSING)) // All input field names are mandatory
}
var err error
var hasKey bool
var sTimeStr, aTimeStr, durStr string
rtCdr := new(StoredCdr)
rtCdr.MediationRunId = runId
rtCdr.Cost = -1.0 // Default for non-rated CDR
if rtCdr.AccId, hasKey = cgrCdr[ACCID]; !hasKey {
if fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, ACCID))
}
}
// MetaDefault will automatically be converted to their standard values
if reqTypeFld == META_DEFAULT {
reqTypeFld = REQTYPE
}
if directionFld == META_DEFAULT {
directionFld = DIRECTION
}
if tenantFld == META_DEFAULT {
tenantFld = TENANT
}
if torFld == META_DEFAULT {
torFld = Category
}
if accountFld == META_DEFAULT {
accountFld = ACCOUNT
}
if subjectFld == META_DEFAULT {
subjectFld = SUBJECT
}
if destFld == META_DEFAULT {
destFld = DESTINATION
}
if setupTimeFld == META_DEFAULT {
setupTimeFld = SETUP_TIME
}
if answerTimeFld == META_DEFAULT {
answerTimeFld = ANSWER_TIME
}
if durationFld == META_DEFAULT {
durationFld = DURATION
}
if rtCdr.CdrHost, hasKey = cgrCdr[CDRHOST]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, CDRHOST))
}
if rtCdr.CdrSource, hasKey = cgrCdr[CDRSOURCE]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, CDRSOURCE))
}
if strings.HasPrefix(reqTypeFld, STATIC_VALUE_PREFIX) { // Values starting with prefix are not dynamically populated
rtCdr.ReqType = reqTypeFld[1:]
} else if rtCdr.ReqType, hasKey = cgrCdr[reqTypeFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, reqTypeFld))
}
if strings.HasPrefix(directionFld, STATIC_VALUE_PREFIX) {
rtCdr.Direction = directionFld[1:]
} else if rtCdr.Direction, hasKey = cgrCdr[directionFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, directionFld))
}
if strings.HasPrefix(tenantFld, STATIC_VALUE_PREFIX) {
rtCdr.Tenant = tenantFld[1:]
} else if rtCdr.Tenant, hasKey = cgrCdr[tenantFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, tenantFld))
}
if strings.HasPrefix(torFld, STATIC_VALUE_PREFIX) {
rtCdr.Category = torFld[1:]
} else if rtCdr.Category, hasKey = cgrCdr[torFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, torFld))
}
if strings.HasPrefix(accountFld, STATIC_VALUE_PREFIX) {
rtCdr.Account = accountFld[1:]
} else if rtCdr.Account, hasKey = cgrCdr[accountFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, accountFld))
}
if strings.HasPrefix(subjectFld, STATIC_VALUE_PREFIX) {
rtCdr.Subject = subjectFld[1:]
} else if rtCdr.Subject, hasKey = cgrCdr[subjectFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, subjectFld))
}
if strings.HasPrefix(destFld, STATIC_VALUE_PREFIX) {
rtCdr.Destination = destFld[1:]
} else if rtCdr.Destination, hasKey = cgrCdr[destFld]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, destFld))
}
if sTimeStr, hasKey = cgrCdr[setupTimeFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(setupTimeFld, STATIC_VALUE_PREFIX) {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, setupTimeFld))
} else {
if strings.HasPrefix(setupTimeFld, STATIC_VALUE_PREFIX) {
sTimeStr = setupTimeFld[1:]
}
if rtCdr.SetupTime, err = ParseTimeDetectLayout(sTimeStr); err != nil && fieldsMandatory {
return nil, err
}
}
if aTimeStr, hasKey = cgrCdr[answerTimeFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(answerTimeFld, STATIC_VALUE_PREFIX) {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, answerTimeFld))
} else {
if strings.HasPrefix(answerTimeFld, STATIC_VALUE_PREFIX) {
aTimeStr = answerTimeFld[1:]
}
if rtCdr.AnswerTime, err = ParseTimeDetectLayout(aTimeStr); err != nil && fieldsMandatory {
return nil, err
}
}
if durStr, hasKey = cgrCdr[durationFld]; !hasKey && fieldsMandatory && !strings.HasPrefix(durationFld, STATIC_VALUE_PREFIX) {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, durationFld))
} else {
if strings.HasPrefix(durationFld, STATIC_VALUE_PREFIX) {
durStr = durationFld[1:]
}
if rtCdr.Duration, err = ParseDurationWithSecs(durStr); err != nil && fieldsMandatory {
return nil, err
}
}
rtCdr.ExtraFields = make(map[string]string, len(extraFlds))
for _, fldName := range extraFlds {
if fldVal, hasKey := cgrCdr[fldName]; !hasKey && fieldsMandatory {
return nil, errors.New(fmt.Sprintf("%s:%s", ERR_MANDATORY_IE_MISSING, fldName))
} else {
rtCdr.ExtraFields[fldName] = fldVal
}
}
rtCdr.CgrId = Sha1(rtCdr.AccId, rtCdr.SetupTime.String())
return rtCdr, nil
func (cgrCdr CgrCdr) AsStoredCdr() *StoredCdr {
storCdr := new(StoredCdr)
storCdr.CgrId = cgrCdr.GetCgrId()
storCdr.AccId = cgrCdr.GetAccId()
storCdr.CdrHost = cgrCdr.GetCdrHost()
storCdr.CdrSource = cgrCdr.GetCdrSource()
storCdr.ReqType = cgrCdr.GetReqType()
storCdr.Direction = cgrCdr.GetDirection()
storCdr.Tenant = cgrCdr.GetTenant()
storCdr.Category = cgrCdr.GetCategory()
storCdr.Account = cgrCdr.GetAccount()
storCdr.Subject = cgrCdr.GetSubject()
storCdr.Destination = cgrCdr.GetDestination()
storCdr.SetupTime, _ = cgrCdr.GetSetupTime() // Not interested to process errors, should do them if necessary in a previous step
storCdr.AnswerTime, _ = cgrCdr.GetAnswerTime()
storCdr.Duration, _ = cgrCdr.GetDuration()
storCdr.ExtraFields = cgrCdr.GetExtraFields()
storCdr.Cost = -1
return storCdr
}

View File

@@ -28,9 +28,13 @@ import (
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 TestCgrCdrFields(t *testing.T) {
cgrCdr := CgrCdr{"accid": "dsafdsaf", "cdrhost": "192.168.1.1", "reqtype": "rated", "direction": "*out", "tenant": "cgrates.org", "tor": "call",
"account": "1001", "subject": "1001", "destination": "1002", "setup_time": "2013-11-07T08:42:20Z", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
cgrCdr := CgrCdr{ACCID: "dsafdsaf", CDRHOST: "192.168.1.1", REQTYPE: "rated", DIRECTION: "*out", TENANT: "cgrates.org", CATEGORY: "call",
ACCOUNT: "1001", SUBJECT: "1001", DESTINATION: "1002", SETUP_TIME: "2013-11-07T08:42:20Z", ANSWER_TIME: "2013-11-07T08:42:26Z", DURATION: "10",
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
setupTime, _ := ParseTimeDetectLayout("2013-11-07T08:42:20Z")
if cgrCdr.GetCgrId() != Sha1("dsafdsaf", setupTime.String()) {
@@ -85,76 +89,16 @@ func TestCgrCdrFields(t *testing.T) {
}
}
func TestCgrCdrForkCdr(t *testing.T) {
sampleCdr1 := &CgrCdr{"accid": "dsafdsaf", "cdrhost": "192.168.1.1", "cdrsource": "source_test", "reqtype": "rated", "direction": "*out", "tenant": "cgrates.org", "tor": "call",
"account": "1001", "subject": "1001", "destination": "1002", "setup_time": "2013-11-07T08:42:24Z", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
func TestCgrCdrAsStoredCdr(t *testing.T) {
cgrCdr := CgrCdr{ACCID: "dsafdsaf", CDRHOST: "192.168.1.1", CDRSOURCE: "internal_test", REQTYPE: "rated", DIRECTION: "*out", TENANT: "cgrates.org", CATEGORY: "call",
ACCOUNT: "1001", SUBJECT: "1001", DESTINATION: "1002", SETUP_TIME: "2013-11-07T08:42:20Z", ANSWER_TIME: "2013-11-07T08:42:26Z", DURATION: "10",
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
rtSampleCdrOut, err := sampleCdr1.ForkCdr("sample_run1", "reqtype", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration",
[]string{}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
setupTime1 := time.Date(2013, 11, 7, 8, 42, 24, 0, time.UTC)
expctSplRatedCdr := &StoredCdr{CgrId: Sha1("dsafdsaf", setupTime1.String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: setupTime1, AnswerTime: time.Unix(1383813746, 0).UTC(),
Duration: 10000000000, ExtraFields: map[string]string{}, MediationRunId: "sample_run1", Cost: -1}
if !reflect.DeepEqual(expctSplRatedCdr, rtSampleCdrOut) {
t.Errorf("Expected: %v, received: %v", expctSplRatedCdr, rtSampleCdrOut)
}
cgrCdr := &CgrCdr{"accid": "dsafdsaf", "cdrhost": "192.168.1.1", "cdrsource": "source_test", "reqtype": "rated", "direction": "*out", "tenant": "cgrates.org", "tor": "call",
"account": "1001", "subject": "1001", "destination": "1002", "setup_time": "2013-11-07T08:42:24Z", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
rtCdrOut, err := cgrCdr.ForkCdr("wholesale_run", "reqtype", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration",
[]string{"field_extr1", "fieldextr2"}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
setupTime, _ := ParseTimeDetectLayout("2013-11-07T08:42:24Z")
expctRatedCdr := &StoredCdr{CgrId: Sha1("dsafdsaf", setupTime.String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: time.Unix(1383813744, 0).UTC(), AnswerTime: time.Unix(1383813746, 0).UTC(),
Duration: 10000000000, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
if !reflect.DeepEqual(rtCdrOut, expctRatedCdr) {
t.Errorf("Received: %v, expected: %v", rtCdrOut, expctRatedCdr)
}
rtCdrOut2, err := cgrCdr.ForkCdr("wholesale_run", "^postpaid", "^*in", "^cgrates.com", "^premium_call", "^first_account", "^first_subject", "destination",
"^2013-12-07T08:42:24Z", "^2013-12-07T08:42:26Z", "^12s", []string{"field_extr1", "fieldextr2"}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
expctRatedCdr2 := &StoredCdr{CgrId: Sha1("dsafdsaf", time.Date(2013, 12, 7, 8, 42, 24, 0, time.UTC).String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "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), Duration: time.Duration(12) * time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
if !reflect.DeepEqual(rtCdrOut2, expctRatedCdr2) {
t.Errorf("Received: %v, expected: %v", rtCdrOut2, expctRatedCdr2)
}
_, err = cgrCdr.ForkCdr("wholesale_run", "dummy_header", "direction", "tenant", "tor", "account", "subject", "destination", "setup_time", "answer_time", "duration",
[]string{"field_extr1", "fieldextr2"}, true)
if err == nil {
t.Error("Failed to detect missing header")
}
}
func TestCgrCdrForkCdrFromMetaDefaults(t *testing.T) {
cgrCdr := &CgrCdr{"accid": "dsafdsaf", "cdrhost": "192.168.1.1", "cdrsource": "source_test", "reqtype": "rated", "direction": "*out", "tenant": "cgrates.org", "tor": "call",
"account": "1001", "subject": "1001", "destination": "1002", "setup_time": "2013-11-07T08:42:24Z", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
setupTime := time.Date(2013, 11, 7, 8, 42, 24, 0, time.UTC)
expctCdr := &StoredCdr{CgrId: Sha1("dsafdsaf", setupTime.String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "source_test", ReqType: "rated",
Direction: "*out", Tenant: "cgrates.org", Category: "call", Account: "1001", Subject: "1001", Destination: "1002",
SetupTime: setupTime, AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC),
Duration: time.Duration(10) * time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: "wholesale_run", Cost: -1}
cdrOut, err := cgrCdr.ForkCdr("wholesale_run", META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT, META_DEFAULT,
META_DEFAULT, META_DEFAULT, META_DEFAULT, []string{"field_extr1", "fieldextr2"}, true)
if err != nil {
t.Fatal("Unexpected error received", err)
}
if !reflect.DeepEqual(expctCdr, cdrOut) {
t.Errorf("Expected: %v, received: %v", expctCdr, cdrOut)
setupTime, _ := ParseTimeDetectLayout(cgrCdr["setup_time"])
expctRtCdr := &StoredCdr{CgrId: Sha1(cgrCdr["accid"], setupTime.String()), AccId: cgrCdr["accid"], CdrHost: cgrCdr["cdrhost"], CdrSource: cgrCdr["cdrsource"], ReqType: cgrCdr["reqtype"],
Direction: cgrCdr[DIRECTION], Tenant: cgrCdr["tenant"], Category: cgrCdr[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), Duration: 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)
}
}

View File

@@ -81,7 +81,7 @@ const (
REQTYPE = "reqtype"
DIRECTION = "direction"
TENANT = "tenant"
Category = "tor"
CATEGORY = "category"
ACCOUNT = "account"
SUBJECT = "subject"
DESTINATION = "destination"
@@ -92,7 +92,7 @@ const (
COST = "cost"
DEFAULT_RUNID = "default"
STATIC_VALUE_PREFIX = "^"
CDRE_CSV = "csv"
CSV = "csv"
CDRE_DRYRUN = "dry_run"
INTERNAL = "internal"
ZERO_RATING_SUBJECT_PREFIX = "*zero"
@@ -104,8 +104,11 @@ const (
CONCATENATED_KEY_SEP = ":"
META_DEFAULT = "*default"
FORKED_CDR = "forked_cdr"
UNIT_TEST = "UNIT_TEST"
HDR_VAL_SEP = "/"
)
var (
CdreCdrFormats = []string{CDRE_CSV, CDRE_DRYRUN, CDRE_FIXED_WIDTH}
CdreCdrFormats = []string{CSV, CDRE_DRYRUN, CDRE_FIXED_WIDTH}
PrimaryCdrFields = []string{ACCID, CDRHOST, CDRSOURCE, REQTYPE, DIRECTION, TENANT, CATEGORY, ACCOUNT, SUBJECT, DESTINATION, SETUP_TIME, ANSWER_TIME, DURATION}
)

View File

@@ -18,28 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package utils
import (
"time"
)
var PrimaryCdrFields []string = []string{ACCID, CDRHOST, CDRSOURCE, REQTYPE, DIRECTION, TENANT, Category, ACCOUNT, SUBJECT, DESTINATION, SETUP_TIME, ANSWER_TIME, DURATION}
// RawCDR is the type containing all the original CDR fields, needs it as it is for later usage
type RawCDR interface {
GetCgrId() string
GetAccId() string
GetCdrHost() string
GetCdrSource() string
GetDirection() string
GetSubject() string
GetAccount() string
GetDestination() string
GetCategory() string
GetTenant() string
GetReqType() string
GetSetupTime() (time.Time, error) // Time when the call was set-up
GetAnswerTime() (time.Time, error) // Time when the call was answered
GetDuration() (time.Duration, error)
GetExtraFields() map[string]string //Stores extra CDR Fields
ForkCdr(string, string, string, string, string, string, string, string, string, string, string, []string, bool) (*StoredCdr, error) // Based on fields queried will return a particular instance of RatedCDR
// 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
}

View File

@@ -28,6 +28,15 @@ func NewRSRField(fldStr string) (*RSRField, error) {
if len(fldStr) == 0 {
return nil, nil
}
if strings.HasPrefix(fldStr, STATIC_VALUE_PREFIX) { // Special case when RSR is defined as static header/value
var staticHdr, staticVal string
if splt := strings.Split(fldStr, HDR_VAL_SEP); len(splt) == 2 { // Using | as separator since ':' is often use in date/time fields
staticHdr, staticVal = splt[0][1:], splt[1]
} else {
staticHdr, staticVal = splt[0][1:], splt[0][1:] // If no split, header will remain as original, value as header without the prefix
}
return &RSRField{Id: staticHdr, staticValue: staticVal}, nil
}
if !strings.HasPrefix(fldStr, REGEXP_PREFIX) {
return &RSRField{Id: fldStr}, nil
}
@@ -53,12 +62,16 @@ func NewRSRField(fldStr string) (*RSRField, error) {
}
type RSRField struct {
Id string // Identifier
RSRules []*ReSearchReplace // Rules to use when processing field value
Id string // Identifier
RSRules []*ReSearchReplace // Rules to use when processing field value
staticValue string // If defined, enforces parsing always to this value
}
// Parse the field value from a string
func (rsrf *RSRField) ParseValue(value string) string {
if len(rsrf.staticValue) != 0 { // Enforce parsing of static values
return rsrf.staticValue
}
if len(value) == 0 {
return value
}

View File

@@ -80,3 +80,20 @@ func TestConvertPlusNationalAnd00(t *testing.T) {
t.Errorf("Expecting: 003186517174963, received: %s", parsedVal)
}
}
func TestRSRParseStatic(t *testing.T) {
if rsrField, err := NewRSRField("^static_header/static_value"); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rsrField, &RSRField{Id: "static_header", staticValue: "static_value"}) {
t.Errorf("Unexpected RSRField received: %v", rsrField)
} else if parsed := rsrField.ParseValue("dynamic_value"); parsed != "static_value" {
t.Errorf("Expected: %s, received: %s", "static_value", parsed)
}
if rsrField, err := NewRSRField(`^static_hdrvalue`); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rsrField, &RSRField{Id: "static_hdrvalue", staticValue: "static_hdrvalue"}) {
t.Errorf("Unexpected RSRField received: %v", rsrField)
} else if parsed := rsrField.ParseValue("dynamic_value"); parsed != "static_hdrvalue" {
t.Errorf("Expected: %s, received: %s", "static_hdrvalue", parsed)
}
}

View File

@@ -19,40 +19,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package utils
import (
"errors"
"fmt"
"math"
"net/url"
"strconv"
"time"
)
func NewStoredCdrFromRawCDR(rawcdr RawCDR) (*StoredCdr, error) {
var err error
strCdr := new(StoredCdr)
strCdr.CgrId = rawcdr.GetCgrId()
strCdr.AccId = rawcdr.GetAccId()
strCdr.CdrHost = rawcdr.GetCdrHost()
strCdr.CdrSource = rawcdr.GetCdrSource()
strCdr.ReqType = rawcdr.GetReqType()
strCdr.Direction = rawcdr.GetDirection()
strCdr.Tenant = rawcdr.GetTenant()
strCdr.Category = rawcdr.GetCategory()
strCdr.Account = rawcdr.GetAccount()
strCdr.Subject = rawcdr.GetSubject()
strCdr.Destination = rawcdr.GetDestination()
if strCdr.SetupTime, err = rawcdr.GetSetupTime(); err != nil {
return nil, err
}
if strCdr.AnswerTime, err = rawcdr.GetAnswerTime(); err != nil {
return nil, err
}
strCdr.Duration, _ = rawcdr.GetDuration()
strCdr.ExtraFields = rawcdr.GetExtraFields()
strCdr.MediationRunId = DEFAULT_RUNID
strCdr.Cost = -1
return strCdr, nil
}
// Rated CDR as extracted from StorDb. Kinda standard of internal CDR, complies to CDR interface also
// 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
@@ -74,68 +49,6 @@ type StoredCdr struct {
Cost float64
}
// Methods maintaining RawCDR interface
func (storedCdr *StoredCdr) GetCgrId() string {
return storedCdr.CgrId
}
func (storedCdr *StoredCdr) GetAccId() string {
return storedCdr.AccId
}
func (storedCdr *StoredCdr) GetCdrHost() string {
return storedCdr.CdrHost
}
func (storedCdr *StoredCdr) GetCdrSource() string {
return storedCdr.CdrSource
}
func (storedCdr *StoredCdr) GetDirection() string {
return storedCdr.Direction
}
func (storedCdr *StoredCdr) GetSubject() string {
return storedCdr.Subject
}
func (storedCdr *StoredCdr) GetAccount() string {
return storedCdr.Account
}
func (storedCdr *StoredCdr) GetDestination() string {
return storedCdr.Destination
}
func (storedCdr *StoredCdr) GetCategory() string {
return storedCdr.Category
}
func (storedCdr *StoredCdr) GetTenant() string {
return storedCdr.Tenant
}
func (storedCdr *StoredCdr) GetReqType() string {
return storedCdr.ReqType
}
func (storedCdr *StoredCdr) GetSetupTime() (time.Time, error) {
return storedCdr.SetupTime, nil
}
func (storedCdr *StoredCdr) GetAnswerTime() (time.Time, error) {
return storedCdr.AnswerTime, nil
}
func (storedCdr *StoredCdr) GetDuration() (time.Duration, error) {
return storedCdr.Duration, nil
}
func (storedCdr *StoredCdr) GetExtraFields() map[string]string {
return storedCdr.ExtraFields
}
// Return cost as string, formated with number of decimals configured
func (storedCdr *StoredCdr) FormatCost(shiftDecimals, roundDecimals int) string {
cost := storedCdr.Cost
@@ -145,81 +58,165 @@ func (storedCdr *StoredCdr) FormatCost(shiftDecimals, roundDecimals int) string
return strconv.FormatFloat(cost, 'f', roundDecimals, 64)
}
// Converts part of the rated Cdr as httpForm used to post remotely to CDRS
func (storedCdr *StoredCdr) AsRawCdrHttpForm() url.Values {
// Used to retrieve fields as string, primary fields are const labeled
func (storedCdr *StoredCdr) FieldAsString(rsrFld *RSRField) string {
switch rsrFld.Id {
case CGRID:
return rsrFld.ParseValue(storedCdr.CgrId)
case ORDERID:
return rsrFld.ParseValue(strconv.FormatInt(storedCdr.OrderId, 10))
case ACCID:
return rsrFld.ParseValue(storedCdr.AccId)
case CDRHOST:
return rsrFld.ParseValue(storedCdr.CdrHost)
case CDRSOURCE:
return rsrFld.ParseValue(storedCdr.CdrSource)
case REQTYPE:
return rsrFld.ParseValue(storedCdr.ReqType)
case DIRECTION:
return rsrFld.ParseValue(storedCdr.Direction)
case TENANT:
return rsrFld.ParseValue(storedCdr.Tenant)
case CATEGORY:
return rsrFld.ParseValue(storedCdr.Category)
case ACCOUNT:
return rsrFld.ParseValue(storedCdr.Account)
case SUBJECT:
return rsrFld.ParseValue(storedCdr.Subject)
case DESTINATION:
return rsrFld.ParseValue(storedCdr.Destination)
case SETUP_TIME:
return rsrFld.ParseValue(storedCdr.SetupTime.String())
case ANSWER_TIME:
return rsrFld.ParseValue(storedCdr.AnswerTime.String())
case DURATION:
return rsrFld.ParseValue(strconv.FormatFloat(storedCdr.Duration.Seconds(), 'f', -1, 64))
case MEDI_RUNID:
return rsrFld.ParseValue(storedCdr.MediationRunId)
case 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) AsStoredCdr() *StoredCdr {
return storedCdr
}
// Ability to send the CgrCdr remotely to another CDR server
func (storedCdr *StoredCdr) AsHttpForm() url.Values {
v := url.Values{}
for fld, val := range storedCdr.ExtraFields {
v.Set(fld, val)
}
v.Set(ACCID, storedCdr.AccId)
v.Set(CDRHOST, storedCdr.CdrHost)
v.Set(CDRSOURCE, storedCdr.CdrSource)
v.Set(REQTYPE, storedCdr.ReqType)
v.Set(DIRECTION, storedCdr.Direction)
v.Set(TENANT, storedCdr.Tenant)
v.Set(Category, storedCdr.Category)
v.Set(CATEGORY, storedCdr.Category)
v.Set(ACCOUNT, storedCdr.Account)
v.Set(SUBJECT, storedCdr.Subject)
v.Set(DESTINATION, storedCdr.Destination)
v.Set(SETUP_TIME, storedCdr.SetupTime.String())
v.Set(ANSWER_TIME, storedCdr.AnswerTime.String())
v.Set(DURATION, strconv.FormatFloat(storedCdr.Duration.Seconds(), 'f', -1, 64))
for fld, val := range storedCdr.ExtraFields {
v.Set(fld, val)
}
return v
}
// Used to export fields as string, primary fields are const labeled
func (storedCdr *StoredCdr) ExportFieldValue(fldName string) string {
switch fldName {
case CGRID:
return storedCdr.CgrId
case ORDERID:
return strconv.FormatInt(storedCdr.OrderId, 10)
case ACCID:
return storedCdr.AccId
case CDRHOST:
return storedCdr.CdrHost
case CDRSOURCE:
return storedCdr.CdrSource
case REQTYPE:
return storedCdr.ReqType
case DIRECTION:
return storedCdr.Direction
case TENANT:
return storedCdr.Tenant
case Category:
return storedCdr.Category
case ACCOUNT:
return storedCdr.Account
case SUBJECT:
return storedCdr.Subject
case DESTINATION:
return storedCdr.Destination
case SETUP_TIME:
return storedCdr.SetupTime.String()
case ANSWER_TIME:
return storedCdr.AnswerTime.String()
case DURATION:
return strconv.FormatFloat(storedCdr.Duration.Seconds(), 'f', -1, 64)
case MEDI_RUNID:
return storedCdr.MediationRunId
case COST:
return strconv.FormatFloat(storedCdr.Cost, 'f', -1, 64) // Recommended to use FormatCost
default:
return storedCdr.ExtraFields[fldName]
// 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 *RSRField,
extraFlds []*RSRField, primaryMandatory bool) (*StoredCdr, error) {
// MetaDefault will automatically be converted to their standard values
if reqTypeFld.Id == META_DEFAULT {
reqTypeFld.Id = REQTYPE
}
}
// Converts to CgrCdr, so we can fork with less code
func (storedCdr *StoredCdr) AsCgrCdr() CgrCdr {
cgrCdr := make(CgrCdr)
for _, fldName := range PrimaryCdrFields {
cgrCdr[fldName] = storedCdr.ExportFieldValue(fldName)
if directionFld.Id == META_DEFAULT {
directionFld.Id = DIRECTION
}
return cgrCdr
}
func (storedCdr *StoredCdr) ForkCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld,
setupTimeFld, answerTimeFld, durationFld string, extraFlds []string, fieldsMandatory bool) (*StoredCdr, error) {
return storedCdr.AsCgrCdr().ForkCdr(runId, reqTypeFld, directionFld, tenantFld, torFld, accountFld, subjectFld, destFld,
setupTimeFld, answerTimeFld, durationFld, extraFlds, fieldsMandatory)
if tenantFld.Id == META_DEFAULT {
tenantFld.Id = TENANT
}
if categFld.Id == META_DEFAULT {
categFld.Id = CATEGORY
}
if accountFld.Id == META_DEFAULT {
accountFld.Id = ACCOUNT
}
if subjectFld.Id == META_DEFAULT {
subjectFld.Id = SUBJECT
}
if destFld.Id == META_DEFAULT {
destFld.Id = DESTINATION
}
if setupTimeFld.Id == META_DEFAULT {
setupTimeFld.Id = SETUP_TIME
}
if answerTimeFld.Id == META_DEFAULT {
answerTimeFld.Id = ANSWER_TIME
}
if durationFld.Id == META_DEFAULT {
durationFld.Id = DURATION
}
var err error
frkStorCdr := new(StoredCdr)
frkStorCdr.CgrId = storedCdr.CgrId
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", ERR_MANDATORY_IE_MISSING, REQTYPE, reqTypeFld.Id))
}
frkStorCdr.Direction = storedCdr.FieldAsString(directionFld)
if primaryMandatory && len(frkStorCdr.Direction) == 0 {
return nil, errors.New(fmt.Sprintf("%s:%s:%s", ERR_MANDATORY_IE_MISSING, DIRECTION, directionFld.Id))
}
frkStorCdr.Tenant = storedCdr.FieldAsString(tenantFld)
if primaryMandatory && len(frkStorCdr.Tenant) == 0 {
return nil, errors.New(fmt.Sprintf("%s:%s:%s", ERR_MANDATORY_IE_MISSING, TENANT, tenantFld.Id))
}
frkStorCdr.Category = storedCdr.FieldAsString(categFld)
if primaryMandatory && len(frkStorCdr.Category) == 0 {
return nil, errors.New(fmt.Sprintf("%s:%s:%s", ERR_MANDATORY_IE_MISSING, CATEGORY, categFld.Id))
}
frkStorCdr.Account = storedCdr.FieldAsString(accountFld)
if primaryMandatory && len(frkStorCdr.Account) == 0 {
return nil, errors.New(fmt.Sprintf("%s:%s:%s", ERR_MANDATORY_IE_MISSING, ACCOUNT, accountFld.Id))
}
frkStorCdr.Subject = storedCdr.FieldAsString(subjectFld)
if primaryMandatory && len(frkStorCdr.Subject) == 0 {
return nil, errors.New(fmt.Sprintf("%s:%s:%s", ERR_MANDATORY_IE_MISSING, SUBJECT, subjectFld.Id))
}
frkStorCdr.Destination = storedCdr.FieldAsString(destFld)
if primaryMandatory && len(frkStorCdr.Destination) == 0 {
return nil, errors.New(fmt.Sprintf("%s:%s:%s", ERR_MANDATORY_IE_MISSING, DESTINATION, destFld.Id))
}
sTimeStr := storedCdr.FieldAsString(setupTimeFld)
if primaryMandatory && len(sTimeStr) == 0 {
return nil, errors.New(fmt.Sprintf("%s:%s:%s", ERR_MANDATORY_IE_MISSING, SETUP_TIME, setupTimeFld.Id))
} else if frkStorCdr.SetupTime, err = 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", ERR_MANDATORY_IE_MISSING, ANSWER_TIME, answerTimeFld.Id))
} else if frkStorCdr.AnswerTime, err = 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", ERR_MANDATORY_IE_MISSING, DURATION, durationFld.Id))
} else if frkStorCdr.Duration, err = 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
}

View File

@@ -25,166 +25,54 @@ import (
)
func TestStoredCdrInterfaces(t *testing.T) {
ratedCdr := new(StoredCdr)
var _ RawCDR = ratedCdr
storedCdr := new(StoredCdr)
var _ RawCdr = storedCdr
}
func TestNewStoredCdrFromRawCDR(t *testing.T) {
cgrCdr := CgrCdr{"accid": "dsafdsaf", "cdrhost": "192.168.1.1", "cdrsource": "internal_test", "reqtype": "rated", "direction": "*out", "tenant": "cgrates.org", "tor": "call",
"account": "1001", "subject": "1001", "destination": "1002", "setup_time": "2013-11-07T08:42:20Z", "answer_time": "2013-11-07T08:42:26Z", "duration": "10",
"field_extr1": "val_extr1", "fieldextr2": "valextr2"}
setupTime, _ := ParseTimeDetectLayout(cgrCdr["setup_time"])
expctRtCdr := &StoredCdr{CgrId: Sha1(cgrCdr["accid"], setupTime.String()), AccId: cgrCdr["accid"], CdrHost: cgrCdr["cdrhost"], CdrSource: cgrCdr["cdrsource"], ReqType: cgrCdr["reqtype"],
Direction: cgrCdr["direction"], Tenant: cgrCdr["tenant"], Category: cgrCdr["tor"], 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), Duration: time.Duration(10) * time.Second,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, MediationRunId: DEFAULT_RUNID, Cost: -1}
if rt, err := NewStoredCdrFromRawCDR(cgrCdr); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(rt, expctRtCdr) {
t.Errorf("Received %v, expected: %v", rt, expctRtCdr)
}
}
func TestStoredCdrFields(t *testing.T) {
ratedCdr := StoredCdr{CgrId: Sha1("dsafdsaf", time.Unix(1383813746, 0).String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", ReqType: "rated", Direction: "*out", Tenant: "cgrates.org",
Category: "call", Account: "1001", Subject: "1001", Destination: "1002", SetupTime: time.Unix(1383813746, 0), AnswerTime: time.Unix(1383813746, 0), Duration: 10,
ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}
if ratedCdr.GetCgrId() != Sha1("dsafdsaf", time.Unix(1383813746, 0).String()) {
t.Error("Error parsing cdr: ", ratedCdr)
}
if ratedCdr.GetAccId() != "dsafdsaf" {
t.Error("Error parsing cdr: ", ratedCdr)
}
if ratedCdr.GetCdrHost() != "192.168.1.1" {
t.Error("Error parsing cdr: ", ratedCdr)
}
if ratedCdr.GetDirection() != "*out" {
t.Error("Error parsing cdr: ", ratedCdr)
}
if ratedCdr.GetSubject() != "1001" {
t.Error("Error parsing cdr: ", ratedCdr)
}
if ratedCdr.GetAccount() != "1001" {
t.Error("Error parsing cdr: ", ratedCdr)
}
if ratedCdr.GetDestination() != "1002" {
t.Error("Error parsing cdr: ", ratedCdr)
}
if ratedCdr.GetCategory() != "call" {
t.Error("Error parsing cdr: ", ratedCdr)
}
if ratedCdr.GetTenant() != "cgrates.org" {
t.Error("Error parsing cdr: ", ratedCdr)
}
if ratedCdr.GetReqType() != RATED {
t.Error("Error parsing cdr: ", ratedCdr)
}
setupTime, _ := ratedCdr.GetSetupTime()
expectedSTime, _ := time.Parse(time.RFC3339, "2013-11-07T08:42:26Z")
if setupTime.UTC() != expectedSTime {
t.Error("Error parsing cdr: ", ratedCdr)
}
answerTime, _ := ratedCdr.GetAnswerTime()
expectedATime, _ := time.Parse(time.RFC3339, "2013-11-07T08:42:26Z")
if answerTime.UTC() != expectedATime {
t.Error("Error parsing cdr: ", ratedCdr)
}
dur, _ := ratedCdr.GetDuration()
if dur != 10 {
t.Error("Error parsing cdr: ", ratedCdr)
}
extraFields := ratedCdr.GetExtraFields()
if len(extraFields) != 2 {
t.Error("Error parsing extra fields: ", extraFields)
}
if extraFields["field_extr1"] != "val_extr1" {
t.Error("Error parsing extra fields: ", extraFields)
}
if ratedCdr.Cost != 1.01 {
t.Error("Error parsing cdr: ", ratedCdr)
}
}
func TestAsRawCdrHttpForm(t *testing.T) {
ratedCdr := StoredCdr{CgrId: Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "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),
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}
cdrForm := ratedCdr.AsRawCdrHttpForm()
if cdrForm.Get(ACCID) != ratedCdr.AccId {
t.Errorf("Expected: %s, received: %s", ratedCdr.AccId, cdrForm.Get(ACCID))
}
if cdrForm.Get(CDRHOST) != ratedCdr.CdrHost {
t.Errorf("Expected: %s, received: %s", ratedCdr.CdrHost, cdrForm.Get(CDRHOST))
}
if cdrForm.Get(CDRSOURCE) != ratedCdr.CdrSource {
t.Errorf("Expected: %s, received: %s", ratedCdr.CdrSource, cdrForm.Get(CDRSOURCE))
}
if cdrForm.Get(REQTYPE) != ratedCdr.ReqType {
t.Errorf("Expected: %s, received: %s", ratedCdr.ReqType, cdrForm.Get(REQTYPE))
}
if cdrForm.Get(DIRECTION) != ratedCdr.Direction {
t.Errorf("Expected: %s, received: %s", ratedCdr.Direction, cdrForm.Get(DIRECTION))
}
if cdrForm.Get(TENANT) != ratedCdr.Tenant {
t.Errorf("Expected: %s, received: %s", ratedCdr.Tenant, cdrForm.Get(TENANT))
}
if cdrForm.Get(Category) != ratedCdr.Category {
t.Errorf("Expected: %s, received: %s", ratedCdr.Category, cdrForm.Get(Category))
}
if cdrForm.Get(ACCOUNT) != ratedCdr.Account {
t.Errorf("Expected: %s, received: %s", ratedCdr.Account, cdrForm.Get(ACCOUNT))
}
if cdrForm.Get(SUBJECT) != ratedCdr.Subject {
t.Errorf("Expected: %s, received: %s", ratedCdr.Subject, cdrForm.Get(SUBJECT))
}
if cdrForm.Get(DESTINATION) != ratedCdr.Destination {
t.Errorf("Expected: %s, received: %s", ratedCdr.Destination, cdrForm.Get(DESTINATION))
}
if cdrForm.Get(SETUP_TIME) != "2013-11-07 08:42:20 +0000 UTC" {
t.Errorf("Expected: %s, received: %s", "2013-11-07 08:42:26 +0000 UTC", cdrForm.Get(SETUP_TIME))
}
if cdrForm.Get(ANSWER_TIME) != "2013-11-07 08:42:26 +0000 UTC" {
t.Errorf("Expected: %s, received: %s", "2013-11-07 08:42:26 +0000 UTC", cdrForm.Get(ANSWER_TIME))
}
if cdrForm.Get(DURATION) != "10" {
t.Errorf("Expected: %s, received: %s", "10", cdrForm.Get(DURATION))
}
if cdrForm.Get("field_extr1") != ratedCdr.ExtraFields["field_extr1"] {
t.Errorf("Expected: %s, received: %s", ratedCdr.ExtraFields["field_extr1"], cdrForm.Get("field_extr1"))
}
if cdrForm.Get("fieldextr2") != ratedCdr.ExtraFields["fieldextr2"] {
t.Errorf("Expected: %s, received: %s", ratedCdr.ExtraFields["fieldextr2"], cdrForm.Get("fieldextr2"))
}
}
func TestExportFieldValue(t *testing.T) {
func TestFieldAsString(t *testing.T) {
cdr := StoredCdr{CgrId: Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: "test", ReqType: "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: DEFAULT_RUNID,
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}
if cdr.ExportFieldValue(CGRID) != cdr.CgrId ||
cdr.ExportFieldValue(ORDERID) != "123" ||
cdr.ExportFieldValue(ACCID) != cdr.AccId ||
cdr.ExportFieldValue(CDRHOST) != cdr.CdrHost ||
cdr.ExportFieldValue(CDRSOURCE) != cdr.CdrSource ||
cdr.ExportFieldValue(REQTYPE) != cdr.ReqType ||
cdr.ExportFieldValue(DIRECTION) != cdr.Direction ||
cdr.ExportFieldValue(TENANT) != cdr.Tenant ||
cdr.ExportFieldValue(Category) != cdr.Category ||
cdr.ExportFieldValue(ACCOUNT) != cdr.Account ||
cdr.ExportFieldValue(SUBJECT) != cdr.Subject ||
cdr.ExportFieldValue(DESTINATION) != cdr.Destination ||
cdr.ExportFieldValue(SETUP_TIME) != cdr.SetupTime.String() ||
cdr.ExportFieldValue(ANSWER_TIME) != cdr.AnswerTime.String() ||
cdr.ExportFieldValue(DURATION) != "10" ||
cdr.ExportFieldValue(MEDI_RUNID) != cdr.MediationRunId ||
cdr.ExportFieldValue(COST) != "1.01" ||
cdr.ExportFieldValue("field_extr1") != cdr.ExtraFields["field_extr1"] ||
cdr.ExportFieldValue("fieldextr2") != cdr.ExtraFields["fieldextr2"] ||
cdr.ExportFieldValue("dummy_field") != "" {
t.Error("Unexpected filed value received")
if cdr.FieldAsString(&RSRField{Id: CGRID}) != cdr.CgrId ||
cdr.FieldAsString(&RSRField{Id: ORDERID}) != "123" ||
cdr.FieldAsString(&RSRField{Id: ACCID}) != cdr.AccId ||
cdr.FieldAsString(&RSRField{Id: CDRHOST}) != cdr.CdrHost ||
cdr.FieldAsString(&RSRField{Id: CDRSOURCE}) != cdr.CdrSource ||
cdr.FieldAsString(&RSRField{Id: REQTYPE}) != cdr.ReqType ||
cdr.FieldAsString(&RSRField{Id: DIRECTION}) != cdr.Direction ||
cdr.FieldAsString(&RSRField{Id: CATEGORY}) != cdr.Category ||
cdr.FieldAsString(&RSRField{Id: ACCOUNT}) != cdr.Account ||
cdr.FieldAsString(&RSRField{Id: SUBJECT}) != cdr.Subject ||
cdr.FieldAsString(&RSRField{Id: DESTINATION}) != cdr.Destination ||
cdr.FieldAsString(&RSRField{Id: SETUP_TIME}) != cdr.SetupTime.String() ||
cdr.FieldAsString(&RSRField{Id: ANSWER_TIME}) != cdr.AnswerTime.String() ||
cdr.FieldAsString(&RSRField{Id: DURATION}) != "10" ||
cdr.FieldAsString(&RSRField{Id: MEDI_RUNID}) != cdr.MediationRunId ||
cdr.FieldAsString(&RSRField{Id: COST}) != "1.01" ||
cdr.FieldAsString(&RSRField{Id: "field_extr1"}) != cdr.ExtraFields["field_extr1"] ||
cdr.FieldAsString(&RSRField{Id: "fieldextr2"}) != cdr.ExtraFields["fieldextr2"] ||
cdr.FieldAsString(&RSRField{Id: "dummy_field"}) != "" {
t.Error("Unexpected filed value received",
cdr.FieldAsString(&RSRField{Id: CGRID}) != cdr.CgrId,
cdr.FieldAsString(&RSRField{Id: ORDERID}) != "123",
cdr.FieldAsString(&RSRField{Id: ACCID}) != cdr.AccId,
cdr.FieldAsString(&RSRField{Id: CDRHOST}) != cdr.CdrHost,
cdr.FieldAsString(&RSRField{Id: CDRSOURCE}) != cdr.CdrSource,
cdr.FieldAsString(&RSRField{Id: REQTYPE}) != cdr.ReqType,
cdr.FieldAsString(&RSRField{Id: DIRECTION}) != cdr.Direction,
cdr.FieldAsString(&RSRField{Id: CATEGORY}) != cdr.Category,
cdr.FieldAsString(&RSRField{Id: ACCOUNT}) != cdr.Account,
cdr.FieldAsString(&RSRField{Id: SUBJECT}) != cdr.Subject,
cdr.FieldAsString(&RSRField{Id: DESTINATION}) != cdr.Destination,
cdr.FieldAsString(&RSRField{Id: SETUP_TIME}) != cdr.SetupTime.String(),
cdr.FieldAsString(&RSRField{Id: ANSWER_TIME}) != cdr.AnswerTime.String(),
cdr.FieldAsString(&RSRField{Id: DURATION}) != "10",
cdr.FieldAsString(&RSRField{Id: MEDI_RUNID}) != cdr.MediationRunId,
cdr.FieldAsString(&RSRField{Id: COST}) != "1.01",
cdr.FieldAsString(&RSRField{Id: "field_extr1"}) != cdr.ExtraFields["field_extr1"],
cdr.FieldAsString(&RSRField{Id: "fieldextr2"}) != cdr.ExtraFields["fieldextr2"],
cdr.FieldAsString(&RSRField{Id: "dummy_field"}) != "")
}
}
@@ -207,3 +95,138 @@ func TestFormatCost(t *testing.T) {
t.Error("Unexpected format of the cost: ", cdr.FormatCost(2, 3))
}
}
func TestStoredCdrAsHttpForm(t *testing.T) {
storCdr := StoredCdr{CgrId: Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
CdrSource: UNIT_TEST, ReqType: "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: DEFAULT_RUNID,
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}
cdrForm := storCdr.AsHttpForm()
if cdrForm.Get(ACCID) != "dsafdsaf" {
t.Errorf("Expected: %s, received: %s", "dsafdsaf", cdrForm.Get(ACCID))
}
if cdrForm.Get(CDRHOST) != "192.168.1.1" {
t.Errorf("Expected: %s, received: %s", "192.168.1.1", cdrForm.Get(CDRHOST))
}
if cdrForm.Get(CDRSOURCE) != UNIT_TEST {
t.Errorf("Expected: %s, received: %s", UNIT_TEST, cdrForm.Get(CDRSOURCE))
}
if cdrForm.Get(REQTYPE) != "rated" {
t.Errorf("Expected: %s, received: %s", "rated", cdrForm.Get(REQTYPE))
}
if cdrForm.Get(DIRECTION) != "*out" {
t.Errorf("Expected: %s, received: %s", "*out", cdrForm.Get(DIRECTION))
}
if cdrForm.Get(TENANT) != "cgrates.org" {
t.Errorf("Expected: %s, received: %s", "cgrates.org", cdrForm.Get(TENANT))
}
if cdrForm.Get(CATEGORY) != "call" {
t.Errorf("Expected: %s, received: %s", "call", cdrForm.Get(CATEGORY))
}
if cdrForm.Get(ACCOUNT) != "1001" {
t.Errorf("Expected: %s, received: %s", "1001", cdrForm.Get(ACCOUNT))
}
if cdrForm.Get(SUBJECT) != "1001" {
t.Errorf("Expected: %s, received: %s", "1001", cdrForm.Get(SUBJECT))
}
if cdrForm.Get(DESTINATION) != "1002" {
t.Errorf("Expected: %s, received: %s", "1002", cdrForm.Get(DESTINATION))
}
if cdrForm.Get(SETUP_TIME) != "2013-11-07 08:42:20 +0000 UTC" {
t.Errorf("Expected: %s, received: %s", "2013-11-07 08:42:20 +0000 UTC", cdrForm.Get(SETUP_TIME))
}
if cdrForm.Get(ANSWER_TIME) != "2013-11-07 08:42:26 +0000 UTC" {
t.Errorf("Expected: %s, received: %s", "2013-11-07 08:42:26 +0000 UTC", cdrForm.Get(ANSWER_TIME))
}
if cdrForm.Get(DURATION) != "10" {
t.Errorf("Expected: %s, received: %s", "10", cdrForm.Get(DURATION))
}
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: Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), OrderId: 123, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
CdrSource: UNIT_TEST, ReqType: "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: DEFAULT_RUNID,
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "field_extr2": "valextr2"}, Cost: 1.01,
}
rtSampleCdrOut, err := storCdr.ForkCdr("sample_run1", &RSRField{Id: REQTYPE}, &RSRField{Id: DIRECTION}, &RSRField{Id: TENANT}, &RSRField{Id: CATEGORY},
&RSRField{Id: ACCOUNT}, &RSRField{Id: SUBJECT}, &RSRField{Id: DESTINATION}, &RSRField{Id: SETUP_TIME}, &RSRField{Id: ANSWER_TIME}, &RSRField{Id: DURATION},
[]*RSRField{&RSRField{Id: "field_extr1"}, &RSRField{Id: "field_extr2"}}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
expctSplRatedCdr := &StoredCdr{CgrId: storCdr.CgrId, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: UNIT_TEST, ReqType: "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),
Duration: 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: Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 123, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
CdrSource: UNIT_TEST, ReqType: "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: DEFAULT_RUNID,
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}
rsrStPostpaid, _ := NewRSRField("^postpaid")
rsrStIn, _ := NewRSRField("^*in")
rsrStCgr, _ := NewRSRField("^cgrates.com")
rsrStPC, _ := NewRSRField("^premium_call")
rsrStFA, _ := NewRSRField("^first_account")
rsrStFS, _ := NewRSRField("^first_subject")
rsrStST, _ := NewRSRField("^2013-12-07T08:42:24Z")
rsrStAT, _ := NewRSRField("^2013-12-07T08:42:26Z")
rsrStDur, _ := NewRSRField("^12s")
rtCdrOut2, err := storCdr.ForkCdr("wholesale_run", rsrStPostpaid, rsrStIn, rsrStCgr, rsrStPC, rsrStFA, rsrStFS, &RSRField{Id: "destination"}, rsrStST, rsrStAT, rsrStDur,
[]*RSRField{}, true)
if err != nil {
t.Error("Unexpected error received", err)
}
expctRatedCdr2 := &StoredCdr{CgrId: storCdr.CgrId, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: UNIT_TEST, ReqType: "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), Duration: 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", &RSRField{Id: "dummy_header"}, &RSRField{Id: "direction"}, &RSRField{Id: "tenant"}, &RSRField{Id: "tor"}, &RSRField{Id: "account"},
&RSRField{Id: "subject"}, &RSRField{Id: "destination"}, &RSRField{Id: "setup_time"}, &RSRField{Id: "answer_time"}, &RSRField{Id: "duration"},
[]*RSRField{}, true)
if err == nil {
t.Error("Failed to detect missing header")
}
}
func TestStoredCdrForkCdrFromMetaDefaults(t *testing.T) {
storCdr := StoredCdr{CgrId: Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), OrderId: 123, AccId: "dsafdsaf", CdrHost: "192.168.1.1",
CdrSource: UNIT_TEST, ReqType: "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: DEFAULT_RUNID,
Duration: time.Duration(10) * time.Second, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, Cost: 1.01,
}
expctCdr := &StoredCdr{CgrId: storCdr.CgrId, AccId: "dsafdsaf", CdrHost: "192.168.1.1", CdrSource: UNIT_TEST, ReqType: "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),
Duration: 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", &RSRField{Id: META_DEFAULT}, &RSRField{Id: META_DEFAULT}, &RSRField{Id: META_DEFAULT}, &RSRField{Id: META_DEFAULT},
&RSRField{Id: META_DEFAULT}, &RSRField{Id: META_DEFAULT}, &RSRField{Id: META_DEFAULT},
&RSRField{Id: META_DEFAULT}, &RSRField{Id: META_DEFAULT}, &RSRField{Id: META_DEFAULT}, []*RSRField{&RSRField{Id: "field_extr1"}, &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)
}
}