From 4f5894b27e57878ac96d756dc5d19e06949f632e Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 24 Jul 2014 17:32:46 +0200 Subject: [PATCH] Cdrc now supports multiple field sources --- cdrc/cdrc.go | 22 +++++++------ cdrc/cdrc_test.go | 2 +- cmd/cgr-engine/cgr-engine.go | 2 +- config/config.go | 62 +++++++++++++++++++----------------- config/config_test.go | 58 ++++++++++++++++----------------- config/helpers.go | 12 +++---- config/helpers_test.go | 30 ++++++++--------- config/xmlcdrc.go | 22 ++++++------- config/xmlcdrc_test.go | 42 ++++++++++++------------ config/xmlconfig.go | 2 +- 10 files changed, 129 insertions(+), 125 deletions(-) diff --git a/cdrc/cdrc.go b/cdrc/cdrc.go index 5399f68e5..4943b5dac 100644 --- a/cdrc/cdrc.go +++ b/cdrc/cdrc.go @@ -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 diff --git a/cdrc/cdrc_test.go b/cdrc/cdrc_test.go index 7874c1e59..42e8a098c 100644 --- a/cdrc/cdrc_test.go +++ b/cdrc/cdrc_test.go @@ -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} diff --git a/cmd/cgr-engine/cgr-engine.go b/cmd/cgr-engine/cgr-engine.go index c91c0bce3..2111bc5d6 100644 --- a/cmd/cgr-engine/cgr-engine.go +++ b/cmd/cgr-engine/cgr-engine.go @@ -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 } diff --git a/config/config.go b/config/config.go index 5513c68c6..5d10aaeb5 100644 --- a/config/config.go +++ b/config/config.go @@ -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 . - 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 . + 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) + } } } } diff --git a/config/config_test.go b/config/config_test.go index fa37603af..c41eb214d 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -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" diff --git a/config/helpers.go b/config/helpers.go index 53522e241..baeb6e422 100644 --- a/config/helpers.go +++ b/config/helpers.go @@ -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 } } } diff --git a/config/helpers_test.go b/config/helpers_test.go index 11f0177bc..8f0d462ae 100644 --- a/config/helpers_test.go +++ b/config/helpers_test.go @@ -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()) diff --git a/config/xmlcdrc.go b/config/xmlcdrc.go index 68d406622..01bdd0896 100644 --- a/config/xmlcdrc.go +++ b/config/xmlcdrc.go @@ -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 diff --git a/config/xmlcdrc_test.go b/config/xmlcdrc_test.go index 9fcd1a9ba..c7f3e8d53 100644 --- a/config/xmlcdrc_test.go +++ b/config/xmlcdrc_test.go @@ -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) { /var/log/cgrates/cdrc/out freeswitch_csv - + @@ -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) diff --git a/config/xmlconfig.go b/config/xmlconfig.go index 906947c3f..5c2c903cd 100644 --- a/config/xmlconfig.go +++ b/config/xmlconfig.go @@ -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()) } }