Cdrc now supports multiple field sources

This commit is contained in:
DanB
2014-07-24 17:32:46 +02:00
parent 36282cbc17
commit 4f5894b27e
10 changed files with 129 additions and 125 deletions

View File

@@ -43,7 +43,7 @@ const (
FS_CSV = "freeswitch_csv"
)
func NewCdrc(cdrsAddress, cdrType, cdrInDir, cdrOutDir, cdrSourceId string, runDelay time.Duration, csvSep string, cdrFields map[string]*utils.RSRField, cdrServer *cdrs.CDRS) (*Cdrc, error) {
func NewCdrc(cdrsAddress, cdrType, cdrInDir, cdrOutDir, cdrSourceId string, runDelay time.Duration, csvSep string, cdrFields map[string][]*utils.RSRField, cdrServer *cdrs.CDRS) (*Cdrc, error) {
if len(csvSep) != 1 {
return nil, fmt.Errorf("Unsupported csv separator: %s", csvSep)
}
@@ -68,7 +68,7 @@ type Cdrc struct {
cdrSourceId string
runDelay time.Duration
csvSep rune
cdrFields map[string]*utils.RSRField
cdrFields map[string][]*utils.RSRField
cdrServer *cdrs.CDRS // Reference towards internal cdrServer if that is the case
httpClient *http.Client
}
@@ -89,16 +89,18 @@ func (self *Cdrc) Run() error {
func (self *Cdrc) recordToStoredCdr(record []string) (*utils.StoredCdr, error) {
storedCdr := &utils.StoredCdr{CdrHost: "0.0.0.0", CdrSource: self.cdrSourceId, ExtraFields: make(map[string]string), Cost: -1}
var err error
for cfgFieldName, cfgFieldRSR := range self.cdrFields {
for cfgFieldName, cfgFieldRSRs := range self.cdrFields {
var fieldVal string
if utils.IsSliceMember([]string{CSV, FS_CSV}, self.cdrType) {
if strings.HasPrefix(cfgFieldRSR.Id, utils.STATIC_VALUE_PREFIX) {
fieldVal = cfgFieldRSR.ParseValue("PLACEHOLDER")
} else { // Dynamic value extracted using index
if cfgFieldIdx, _ := strconv.Atoi(cfgFieldRSR.Id); len(record) <= cfgFieldIdx {
return nil, fmt.Errorf("Ignoring record: %v - cannot extract field %s", record, cfgFieldName)
} else {
fieldVal = cfgFieldRSR.ParseValue(record[cfgFieldIdx])
for _, cfgFieldRSR := range cfgFieldRSRs {
if strings.HasPrefix(cfgFieldRSR.Id, utils.STATIC_VALUE_PREFIX) {
fieldVal += cfgFieldRSR.ParseValue("PLACEHOLDER")
} else { // Dynamic value extracted using index
if cfgFieldIdx, _ := strconv.Atoi(cfgFieldRSR.Id); len(record) <= cfgFieldIdx {
return nil, fmt.Errorf("Ignoring record: %v - cannot extract field %s", record, cfgFieldName)
} else {
fieldVal += cfgFieldRSR.ParseValue(record[cfgFieldIdx])
}
}
}
} else { // Modify here when we add more supported cdr formats

View File

@@ -34,7 +34,7 @@ import (
func TestRecordForkCdr(t *testing.T) {
cgrConfig, _ := config.NewDefaultCGRConfig()
cgrConfig.CdrcCdrFields["supplier"] = &utils.RSRField{Id: "14"}
cgrConfig.CdrcCdrFields["supplier"] = []*utils.RSRField{&utils.RSRField{Id: "14"}}
csvSepRune, _ := utf8.DecodeRune([]byte(cgrConfig.CdrcCsvSep))
cdrc := &Cdrc{cgrConfig.CdrcCdrs, cgrConfig.CdrcCdrType, cgrConfig.CdrcCdrInDir, cgrConfig.CdrcCdrOutDir, cgrConfig.CdrcSourceId, cgrConfig.CdrcRunDelay, csvSepRune,
cgrConfig.CdrcCdrFields, new(cdrs.CDRS), nil}

View File

@@ -124,7 +124,7 @@ func startMediator(responder *engine.Responder, loggerDb engine.LogStorage, cdrD
}
// Fires up a cdrc instance
func startCdrc(cdrsChan chan struct{}, cdrsAddress, cdrType, cdrInDir, cdrOutDir, cdrSourceId string, runDelay time.Duration, csvSep string, cdrFields map[string]*utils.RSRField) {
func startCdrc(cdrsChan chan struct{}, cdrsAddress, cdrType, cdrInDir, cdrOutDir, cdrSourceId string, runDelay time.Duration, csvSep string, cdrFields map[string][]*utils.RSRField) {
if cdrsAddress == utils.INTERNAL {
<-cdrsChan // Wait for CDRServer to come up before start processing
}

View File

@@ -88,20 +88,20 @@ type CGRConfig struct {
RaterBalancer string // balancer address host:port
BalancerEnabled bool
SchedulerEnabled bool
CDRSEnabled bool // Enable CDR Server service
CDRSExtraFields []*utils.RSRField // Extra fields to store in CDRs
CDRSMediator string // Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
CdrStatsConfigs []*CdrStatsConfig // Active cdr stats configuration instances
CdreDefaultInstance *CdreConfig // Will be used in the case no specific one selected by API
CdrcEnabled bool // Enable CDR client functionality
CdrcCdrs string // Address where to reach CDR server
CdrcRunDelay time.Duration // Sleep interval between consecutive runs, 0 to use automation via inotify
CdrcCdrType string // CDR file format <csv>.
CdrcCsvSep string // Separator used in case of csv files. One character only supported.
CdrcCdrInDir string // Absolute path towards the directory where the CDRs are stored.
CdrcCdrOutDir string // Absolute path towards the directory where processed CDRs will be moved.
CdrcSourceId string // Tag identifying the source of the CDRs within CGRS database.
CdrcCdrFields map[string]*utils.RSRField // FieldName/RSRField format. Index number in case of .csv cdrs.
CDRSEnabled bool // Enable CDR Server service
CDRSExtraFields []*utils.RSRField // Extra fields to store in CDRs
CDRSMediator string // Address where to reach the Mediator. Empty for disabling mediation. <""|internal>
CdrStatsConfigs []*CdrStatsConfig // Active cdr stats configuration instances
CdreDefaultInstance *CdreConfig // Will be used in the case no specific one selected by API
CdrcEnabled bool // Enable CDR client functionality
CdrcCdrs string // Address where to reach CDR server
CdrcRunDelay time.Duration // Sleep interval between consecutive runs, 0 to use automation via inotify
CdrcCdrType string // CDR file format <csv>.
CdrcCsvSep string // Separator used in case of csv files. One character only supported.
CdrcCdrInDir string // Absolute path towards the directory where the CDRs are stored.
CdrcCdrOutDir string // Absolute path towards the directory where processed CDRs will be moved.
CdrcSourceId string // Tag identifying the source of the CDRs within CGRS database.
CdrcCdrFields map[string][]*utils.RSRField // FieldName/RSRField format. Index number in case of .csv cdrs.
SMEnabled bool
SMSwitchType string
SMRater string // address where to access rater. Can be internal, direct rater address or the address of a balancer
@@ -174,19 +174,19 @@ func (self *CGRConfig) setDefaults() error {
self.CdrcCdrInDir = "/var/log/cgrates/cdrc/in"
self.CdrcCdrOutDir = "/var/log/cgrates/cdrc/out"
self.CdrcSourceId = "csv"
self.CdrcCdrFields = map[string]*utils.RSRField{
utils.TOR: &utils.RSRField{Id: "2"},
utils.ACCID: &utils.RSRField{Id: "3"},
utils.REQTYPE: &utils.RSRField{Id: "4"},
utils.DIRECTION: &utils.RSRField{Id: "5"},
utils.TENANT: &utils.RSRField{Id: "6"},
utils.CATEGORY: &utils.RSRField{Id: "7"},
utils.ACCOUNT: &utils.RSRField{Id: "8"},
utils.SUBJECT: &utils.RSRField{Id: "9"},
utils.DESTINATION: &utils.RSRField{Id: "10"},
utils.SETUP_TIME: &utils.RSRField{Id: "11"},
utils.ANSWER_TIME: &utils.RSRField{Id: "12"},
utils.USAGE: &utils.RSRField{Id: "13"},
self.CdrcCdrFields = map[string][]*utils.RSRField{
utils.TOR: []*utils.RSRField{&utils.RSRField{Id: "2"}},
utils.ACCID: []*utils.RSRField{&utils.RSRField{Id: "3"}},
utils.REQTYPE: []*utils.RSRField{&utils.RSRField{Id: "4"}},
utils.DIRECTION: []*utils.RSRField{&utils.RSRField{Id: "5"}},
utils.TENANT: []*utils.RSRField{&utils.RSRField{Id: "6"}},
utils.CATEGORY: []*utils.RSRField{&utils.RSRField{Id: "7"}},
utils.ACCOUNT: []*utils.RSRField{&utils.RSRField{Id: "8"}},
utils.SUBJECT: []*utils.RSRField{&utils.RSRField{Id: "9"}},
utils.DESTINATION: []*utils.RSRField{&utils.RSRField{Id: "10"}},
utils.SETUP_TIME: []*utils.RSRField{&utils.RSRField{Id: "11"}},
utils.ANSWER_TIME: []*utils.RSRField{&utils.RSRField{Id: "12"}},
utils.USAGE: []*utils.RSRField{&utils.RSRField{Id: "13"}},
}
self.MediatorEnabled = false
self.MediatorRater = "internal"
@@ -221,9 +221,11 @@ func (self *CGRConfig) checkConfigSanity() error {
return errors.New("CdrC enabled but no fields to be processed defined!")
}
if self.CdrcCdrType == utils.CSV {
for _, rsrFld := range self.CdrcCdrFields {
if _, errConv := strconv.Atoi(rsrFld.Id); errConv != nil {
return fmt.Errorf("CDR fields must be indices in case of .csv files, have instead: %s", rsrFld.Id)
for _, rsrFldLst := range self.CdrcCdrFields {
for _, rsrFld := range rsrFldLst {
if _, errConv := strconv.Atoi(rsrFld.Id); errConv != nil {
return fmt.Errorf("CDR fields must be indices in case of .csv files, have instead: %s", rsrFld.Id)
}
}
}
}

View File

@@ -91,19 +91,19 @@ func TestDefaults(t *testing.T) {
eCfg.CdrcCdrInDir = "/var/log/cgrates/cdrc/in"
eCfg.CdrcCdrOutDir = "/var/log/cgrates/cdrc/out"
eCfg.CdrcSourceId = "csv"
eCfg.CdrcCdrFields = map[string]*utils.RSRField{
utils.TOR: &utils.RSRField{Id: "2"},
utils.ACCID: &utils.RSRField{Id: "3"},
utils.REQTYPE: &utils.RSRField{Id: "4"},
utils.DIRECTION: &utils.RSRField{Id: "5"},
utils.TENANT: &utils.RSRField{Id: "6"},
utils.CATEGORY: &utils.RSRField{Id: "7"},
utils.ACCOUNT: &utils.RSRField{Id: "8"},
utils.SUBJECT: &utils.RSRField{Id: "9"},
utils.DESTINATION: &utils.RSRField{Id: "10"},
utils.SETUP_TIME: &utils.RSRField{Id: "11"},
utils.ANSWER_TIME: &utils.RSRField{Id: "12"},
utils.USAGE: &utils.RSRField{Id: "13"},
eCfg.CdrcCdrFields = map[string][]*utils.RSRField{
utils.TOR: []*utils.RSRField{&utils.RSRField{Id: "2"}},
utils.ACCID: []*utils.RSRField{&utils.RSRField{Id: "3"}},
utils.REQTYPE: []*utils.RSRField{&utils.RSRField{Id: "4"}},
utils.DIRECTION: []*utils.RSRField{&utils.RSRField{Id: "5"}},
utils.TENANT: []*utils.RSRField{&utils.RSRField{Id: "6"}},
utils.CATEGORY: []*utils.RSRField{&utils.RSRField{Id: "7"}},
utils.ACCOUNT: []*utils.RSRField{&utils.RSRField{Id: "8"}},
utils.SUBJECT: []*utils.RSRField{&utils.RSRField{Id: "9"}},
utils.DESTINATION: []*utils.RSRField{&utils.RSRField{Id: "10"}},
utils.SETUP_TIME: []*utils.RSRField{&utils.RSRField{Id: "11"}},
utils.ANSWER_TIME: []*utils.RSRField{&utils.RSRField{Id: "12"}},
utils.USAGE: []*utils.RSRField{&utils.RSRField{Id: "13"}},
}
eCfg.MediatorEnabled = false
eCfg.MediatorRater = "internal"
@@ -151,11 +151,11 @@ func TestSanityCheck(t *testing.T) {
t.Error("Failed to detect missing CDR fields definition")
}
cfg.CdrcCdrType = utils.CSV
cfg.CdrcCdrFields = map[string]*utils.RSRField{utils.ACCID: &utils.RSRField{Id: "test"}}
cfg.CdrcCdrFields = map[string][]*utils.RSRField{utils.ACCID: []*utils.RSRField{&utils.RSRField{Id: "test"}}}
if err := cfg.checkConfigSanity(); err == nil {
t.Error("Failed to detect improper use of CDR field names")
}
cfg.CdrcCdrFields = map[string]*utils.RSRField{"extra1": &utils.RSRField{Id: "test"}}
cfg.CdrcCdrFields = map[string][]*utils.RSRField{"extra1": []*utils.RSRField{&utils.RSRField{Id: "test"}}}
if err := cfg.checkConfigSanity(); err == nil {
t.Error("Failed to detect improper use of CDR field names")
}
@@ -225,20 +225,20 @@ func TestConfigFromFile(t *testing.T) {
eCfg.CdrcCdrInDir = "test"
eCfg.CdrcCdrOutDir = "test"
eCfg.CdrcSourceId = "test"
eCfg.CdrcCdrFields = map[string]*utils.RSRField{
utils.TOR: &utils.RSRField{Id: "test"},
utils.ACCID: &utils.RSRField{Id: "test"},
utils.REQTYPE: &utils.RSRField{Id: "test"},
utils.DIRECTION: &utils.RSRField{Id: "test"},
utils.TENANT: &utils.RSRField{Id: "test"},
utils.CATEGORY: &utils.RSRField{Id: "test"},
utils.ACCOUNT: &utils.RSRField{Id: "test"},
utils.SUBJECT: &utils.RSRField{Id: "test"},
utils.DESTINATION: &utils.RSRField{Id: "test"},
utils.SETUP_TIME: &utils.RSRField{Id: "test"},
utils.ANSWER_TIME: &utils.RSRField{Id: "test"},
utils.USAGE: &utils.RSRField{Id: "test"},
"test": &utils.RSRField{Id: "test"},
eCfg.CdrcCdrFields = map[string][]*utils.RSRField{
utils.TOR: []*utils.RSRField{&utils.RSRField{Id: "test"}},
utils.ACCID: []*utils.RSRField{&utils.RSRField{Id: "test"}},
utils.REQTYPE: []*utils.RSRField{&utils.RSRField{Id: "test"}},
utils.DIRECTION: []*utils.RSRField{&utils.RSRField{Id: "test"}},
utils.TENANT: []*utils.RSRField{&utils.RSRField{Id: "test"}},
utils.CATEGORY: []*utils.RSRField{&utils.RSRField{Id: "test"}},
utils.ACCOUNT: []*utils.RSRField{&utils.RSRField{Id: "test"}},
utils.SUBJECT: []*utils.RSRField{&utils.RSRField{Id: "test"}},
utils.DESTINATION: []*utils.RSRField{&utils.RSRField{Id: "test"}},
utils.SETUP_TIME: []*utils.RSRField{&utils.RSRField{Id: "test"}},
utils.ANSWER_TIME: []*utils.RSRField{&utils.RSRField{Id: "test"}},
utils.USAGE: []*utils.RSRField{&utils.RSRField{Id: "test"}},
"test": []*utils.RSRField{&utils.RSRField{Id: "test"}},
}
eCfg.MediatorEnabled = true
eCfg.MediatorRater = "test"

View File

@@ -120,8 +120,8 @@ func ParseCfgDerivedCharging(c *conf.ConfigFile) (dcs utils.DerivedChargers, err
}
func ParseCdrcCdrFields(torFld, accIdFld, reqtypeFld, directionFld, tenantFld, categoryFld, acntFld, subjectFld, destFld,
setupTimeFld, answerTimeFld, durFld, extraFlds string) (map[string]*utils.RSRField, error) {
cdrcCdrFlds := make(map[string]*utils.RSRField)
setupTimeFld, answerTimeFld, durFld, extraFlds string) (map[string][]*utils.RSRField, error) {
cdrcCdrFlds := make(map[string][]*utils.RSRField)
if len(extraFlds) != 0 {
if sepExtraFlds, err := ConfigSlice(extraFlds); err != nil {
return nil, err
@@ -131,10 +131,10 @@ func ParseCdrcCdrFields(torFld, accIdFld, reqtypeFld, directionFld, tenantFld, c
if spltLbl := strings.Split(fldStr, utils.CONCATENATED_KEY_SEP); len(spltLbl) != 2 {
return nil, fmt.Errorf("Wrong format for cdrc.extra_fields: %s", fldStr)
} else {
if rsrFld, err := utils.NewRSRField(spltLbl[1]); err != nil {
if rsrFlds, err := utils.ParseRSRFields(spltLbl[1], utils.INFIELD_SEP); err != nil {
return nil, err
} else {
cdrcCdrFlds[spltLbl[0]] = rsrFld
cdrcCdrFlds[spltLbl[0]] = rsrFlds
}
}
}
@@ -144,10 +144,10 @@ func ParseCdrcCdrFields(torFld, accIdFld, reqtypeFld, directionFld, tenantFld, c
utils.CATEGORY: categoryFld, utils.ACCOUNT: acntFld, utils.SUBJECT: subjectFld, utils.DESTINATION: destFld, utils.SETUP_TIME: setupTimeFld,
utils.ANSWER_TIME: answerTimeFld, utils.USAGE: durFld} {
if len(fldVal) != 0 {
if rsrFld, err := utils.NewRSRField(fldVal); err != nil {
if rsrFlds, err := utils.ParseRSRFields(fldVal, utils.INFIELD_SEP); err != nil {
return nil, err
} else {
cdrcCdrFlds[fldTag] = rsrFld
cdrcCdrFlds[fldTag] = rsrFlds
}
}
}

View File

@@ -116,21 +116,21 @@ answer_time_field = answertime1
usage_field = duration1
extra_fields = extra1:extraval1,extra2:extraval1
`)
eCdrcCdrFlds := map[string]*utils.RSRField{
utils.TOR: &utils.RSRField{Id: "tor1"},
utils.ACCID: &utils.RSRField{Id: "accid1"},
utils.REQTYPE: &utils.RSRField{Id: "reqtype1"},
utils.DIRECTION: &utils.RSRField{Id: "direction1"},
utils.TENANT: &utils.RSRField{Id: "tenant1"},
utils.CATEGORY: &utils.RSRField{Id: "category1"},
utils.ACCOUNT: &utils.RSRField{Id: "account1"},
utils.SUBJECT: &utils.RSRField{Id: "subject1"},
utils.DESTINATION: &utils.RSRField{Id: "destination1"},
utils.SETUP_TIME: &utils.RSRField{Id: "setuptime1"},
utils.ANSWER_TIME: &utils.RSRField{Id: "answertime1"},
utils.USAGE: &utils.RSRField{Id: "duration1"},
"extra1": &utils.RSRField{Id: "extraval1"},
"extra2": &utils.RSRField{Id: "extraval1"},
eCdrcCdrFlds := map[string][]*utils.RSRField{
utils.TOR: []*utils.RSRField{&utils.RSRField{Id: "tor1"}},
utils.ACCID: []*utils.RSRField{&utils.RSRField{Id: "accid1"}},
utils.REQTYPE: []*utils.RSRField{&utils.RSRField{Id: "reqtype1"}},
utils.DIRECTION: []*utils.RSRField{&utils.RSRField{Id: "direction1"}},
utils.TENANT: []*utils.RSRField{&utils.RSRField{Id: "tenant1"}},
utils.CATEGORY: []*utils.RSRField{&utils.RSRField{Id: "category1"}},
utils.ACCOUNT: []*utils.RSRField{&utils.RSRField{Id: "account1"}},
utils.SUBJECT: []*utils.RSRField{&utils.RSRField{Id: "subject1"}},
utils.DESTINATION: []*utils.RSRField{&utils.RSRField{Id: "destination1"}},
utils.SETUP_TIME: []*utils.RSRField{&utils.RSRField{Id: "setuptime1"}},
utils.ANSWER_TIME: []*utils.RSRField{&utils.RSRField{Id: "answertime1"}},
utils.USAGE: []*utils.RSRField{&utils.RSRField{Id: "duration1"}},
"extra1": []*utils.RSRField{&utils.RSRField{Id: "extraval1"}},
"extra2": []*utils.RSRField{&utils.RSRField{Id: "extraval1"}},
}
if cfg, err := NewCGRConfigFromBytes(eFieldsCfg); err != nil {
t.Error("Could not parse the config", err.Error())

View File

@@ -57,30 +57,30 @@ func (cdrcCfg *CgrXmlCdrcCfg) setDefaults() error {
cdrcCfg.CdrSourceId = dfCfg.CdrcSourceId
}
if len(cdrcCfg.CdrFields) == 0 {
for key, cfgRsrField := range dfCfg.CdrcCdrFields {
cdrcCfg.CdrFields = append(cdrcCfg.CdrFields, &CdrcField{Id: key, Value: cfgRsrField.Id, rsrField: cfgRsrField})
for key, cfgRsrFields := range dfCfg.CdrcCdrFields {
cdrcCfg.CdrFields = append(cdrcCfg.CdrFields, &CdrcField{Id: key, Value: "PLACEHOLDER", rsrFields: cfgRsrFields})
}
}
return nil
}
func (cdrcCfg *CgrXmlCdrcCfg) CdrRSRFields() map[string]*utils.RSRField {
rsrFields := make(map[string]*utils.RSRField)
func (cdrcCfg *CgrXmlCdrcCfg) CdrRSRFields() map[string][]*utils.RSRField {
rsrFields := make(map[string][]*utils.RSRField)
for _, fld := range cdrcCfg.CdrFields {
rsrFields[fld.Id] = fld.rsrField
rsrFields[fld.Id] = fld.rsrFields
}
return rsrFields
}
type CdrcField struct {
XMLName xml.Name `xml:"field"`
Id string `xml:"id,attr"`
Value string `xml:"value,attr"`
rsrField *utils.RSRField
XMLName xml.Name `xml:"field"`
Id string `xml:"id,attr"`
Value string `xml:"value,attr"`
rsrFields []*utils.RSRField
}
func (cdrcFld *CdrcField) PopulateRSRField() (err error) {
if cdrcFld.rsrField, err = utils.NewRSRField(cdrcFld.Value); err != nil {
func (cdrcFld *CdrcField) PopulateRSRFields() (err error) {
if cdrcFld.rsrFields, err = utils.ParseRSRFields(cdrcFld.Value, utils.INFIELD_SEP); err != nil {
return err
}
return nil

View File

@@ -30,15 +30,15 @@ var cfgDocCdrc *CgrXmlCfgDocument // Will be populated by first test
func TestPopulateRSRFIeld(t *testing.T) {
cdrcField := CdrcField{Id: "TEST1", Value: `~effective_caller_id_number:s/(\d+)/+$1/`}
if err := cdrcField.PopulateRSRField(); err != nil {
if err := cdrcField.PopulateRSRFields(); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if cdrcField.rsrField == nil {
} else if cdrcField.rsrFields == nil {
t.Error("Failed loading the RSRField")
}
cdrcField = CdrcField{Id: "TEST2", Value: `99`}
if err := cdrcField.PopulateRSRField(); err != nil {
if err := cdrcField.PopulateRSRFields(); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if cdrcField.rsrField == nil {
} else if cdrcField.rsrFields == nil {
t.Error("Failed loading the RSRField")
}
}
@@ -87,7 +87,7 @@ func TestParseXmlCdrcConfig(t *testing.T) {
<cdr_out_dir>/var/log/cgrates/cdrc/out</cdr_out_dir>
<cdr_source_id>freeswitch_csv</cdr_source_id>
<fields>
<field id="accid" value="0" />
<field id="accid" value="0;13" />
<field id="reqtype" value="1" />
<field id="direction" value="2" />
<field id="tenant" value="3" />
@@ -123,7 +123,7 @@ func TestGetCdrcCfgs(t *testing.T) {
expectCdrc := &CgrXmlCdrcCfg{Enabled: true, CdrsAddress: "internal", CdrType: "csv", CsvSeparator: ",",
RunDelay: 0, CdrInDir: "/var/log/cgrates/cdrc/in", CdrOutDir: "/var/log/cgrates/cdrc/out", CdrSourceId: "freeswitch_csv"}
cdrFlds := []*CdrcField{
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.ACCID, Value: "0"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.ACCID, Value: "0;13"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.REQTYPE, Value: "1"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.DIRECTION, Value: "2"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: utils.TENANT, Value: "3"},
@@ -137,7 +137,7 @@ func TestGetCdrcCfgs(t *testing.T) {
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: "extr1", Value: "11"},
&CdrcField{XMLName: xml.Name{Local: "field"}, Id: "extr2", Value: "12"}}
for _, fld := range cdrFlds {
fld.PopulateRSRField()
fld.PopulateRSRFields()
}
expectCdrc.CdrFields = cdrFlds
if !reflect.DeepEqual(expectCdrc, cdrcfgs["CDRC-CSV1"]) {
@@ -150,20 +150,20 @@ func TestCdrRSRFields(t *testing.T) {
if cdrcfgs == nil {
t.Error("No config instance returned")
}
eRSRFields := map[string]*utils.RSRField{
utils.ACCID: &utils.RSRField{Id: "0"},
utils.REQTYPE: &utils.RSRField{Id: "1"},
utils.DIRECTION: &utils.RSRField{Id: "2"},
utils.TENANT: &utils.RSRField{Id: "3"},
utils.CATEGORY: &utils.RSRField{Id: "4"},
utils.ACCOUNT: &utils.RSRField{Id: "5"},
utils.SUBJECT: &utils.RSRField{Id: "6"},
utils.DESTINATION: &utils.RSRField{Id: "7"},
utils.SETUP_TIME: &utils.RSRField{Id: "8"},
utils.ANSWER_TIME: &utils.RSRField{Id: "9"},
utils.USAGE: &utils.RSRField{Id: "10"},
"extr1": &utils.RSRField{Id: "11"},
"extr2": &utils.RSRField{Id: "12"},
eRSRFields := map[string][]*utils.RSRField{
utils.ACCID: []*utils.RSRField{&utils.RSRField{Id: "0"}, &utils.RSRField{Id: "13"}},
utils.REQTYPE: []*utils.RSRField{&utils.RSRField{Id: "1"}},
utils.DIRECTION: []*utils.RSRField{&utils.RSRField{Id: "2"}},
utils.TENANT: []*utils.RSRField{&utils.RSRField{Id: "3"}},
utils.CATEGORY: []*utils.RSRField{&utils.RSRField{Id: "4"}},
utils.ACCOUNT: []*utils.RSRField{&utils.RSRField{Id: "5"}},
utils.SUBJECT: []*utils.RSRField{&utils.RSRField{Id: "6"}},
utils.DESTINATION: []*utils.RSRField{&utils.RSRField{Id: "7"}},
utils.SETUP_TIME: []*utils.RSRField{&utils.RSRField{Id: "8"}},
utils.ANSWER_TIME: []*utils.RSRField{&utils.RSRField{Id: "9"}},
utils.USAGE: []*utils.RSRField{&utils.RSRField{Id: "10"}},
"extr1": []*utils.RSRField{&utils.RSRField{Id: "11"}},
"extr2": []*utils.RSRField{&utils.RSRField{Id: "12"}},
}
if rsrFields := cdrcfgs["CDRC-CSV1"].CdrRSRFields(); !reflect.DeepEqual(rsrFields, eRSRFields) {
t.Errorf("Expecting: %v, received: %v", eRSRFields, rsrFields)

View File

@@ -85,7 +85,7 @@ func (xmlCfg *CgrXmlCfgDocument) cacheCdrcCfgs() error {
}
// Cache rsr fields
for _, fld := range cdrcCfg.CdrFields {
if err := fld.PopulateRSRField(); err != nil {
if err := fld.PopulateRSRFields(); err != nil {
return fmt.Errorf("Populating field %s, error: %s", fld.Id, err.Error())
}
}