diff --git a/cdrc/cdrc.go b/cdrc/cdrc.go index 7ab992627..b55f5897a 100644 --- a/cdrc/cdrc.go +++ b/cdrc/cdrc.go @@ -69,10 +69,13 @@ func NewCdrc(cdrcCfgs []*config.CdrcConfig, httpSkipTlsCheck bool, cdrs rpcclien cdrc.maxOpenFiles <- processFile // Empty initiate so we do not need to wait later when we pop } var err error - if cdrc.unpairedRecordsCache, err = NewUnpairedRecordsCache(cdrcCfg.PartialRecordCache, cdrcCfg.CdrOutDir, cdrcCfg.FieldSeparator); err != nil { + if cdrc.unpairedRecordsCache, err = NewUnpairedRecordsCache(cdrcCfg.PartialRecordCache, + cdrcCfg.CdrOutDir, cdrcCfg.FieldSeparator); err != nil { return nil, err } - if cdrc.partialRecordsCache, err = NewPartialRecordsCache(cdrcCfg.PartialRecordCache, cdrcCfg.PartialCacheExpiryAction, cdrcCfg.CdrOutDir, cdrcCfg.FieldSeparator, roundDecimals, cdrc.timezone, cdrc.httpSkipTlsCheck, cdrc.cdrs); err != nil { + if cdrc.partialRecordsCache, err = NewPartialRecordsCache(cdrcCfg.PartialRecordCache, + cdrcCfg.PartialCacheExpiryAction, cdrcCfg.CdrOutDir, cdrcCfg.FieldSeparator, roundDecimals, + cdrc.timezone, cdrc.httpSkipTlsCheck, cdrc.cdrs, filterS); err != nil { return nil, err } // Before processing, make sure in and out folders exist diff --git a/cdrc/csv.go b/cdrc/csv.go index 8dd932c1c..bd20cb5a3 100644 --- a/cdrc/csv.go +++ b/cdrc/csv.go @@ -34,7 +34,7 @@ import ( func NewCsvRecordsProcessor(csvReader *csv.Reader, timezone, fileName string, dfltCdrcCfg *config.CdrcConfig, cdrcCfgs []*config.CdrcConfig, httpSkipTlsCheck bool, unpairedRecordsCache *UnpairedRecordsCache, partialRecordsCache *PartialRecordsCache, - cacheDumpFields []*config.CfgCdrField, filterS *engine.FilterS) *CsvRecordsProcessor { + cacheDumpFields []*config.FCTemplate, filterS *engine.FilterS) *CsvRecordsProcessor { return &CsvRecordsProcessor{csvReader: csvReader, timezone: timezone, fileName: fileName, dfltCdrcCfg: dfltCdrcCfg, cdrcCfgs: cdrcCfgs, httpSkipTlsCheck: httpSkipTlsCheck, unpairedRecordsCache: unpairedRecordsCache, partialRecordsCache: partialRecordsCache, partialCacheDumpFields: cacheDumpFields, filterS: filterS} @@ -51,7 +51,7 @@ type CsvRecordsProcessor struct { httpSkipTlsCheck bool unpairedRecordsCache *UnpairedRecordsCache // Shared by cdrc so we can cache for all files in a folder partialRecordsCache *PartialRecordsCache // Cache records which are of type "Partial" - partialCacheDumpFields []*config.CfgCdrField + partialCacheDumpFields []*config.FCTemplate filterS *engine.FilterS } diff --git a/cdrc/partial_cdr.go b/cdrc/partial_cdr.go index 4fbd361a6..c33d01930 100644 --- a/cdrc/partial_cdr.go +++ b/cdrc/partial_cdr.go @@ -39,12 +39,14 @@ const ( ) func NewPartialRecordsCache(ttl time.Duration, expiryAction string, cdrOutDir string, csvSep rune, - roundDecimals int, timezone string, httpSkipTlsCheck bool, cdrs rpcclient.RpcClientConnection) (*PartialRecordsCache, error) { + roundDecimals int, timezone string, httpSkipTlsCheck bool, + cdrs rpcclient.RpcClientConnection, filterS *engine.FilterS) (*PartialRecordsCache, error) { return &PartialRecordsCache{ttl: ttl, expiryAction: expiryAction, cdrOutDir: cdrOutDir, csvSep: csvSep, roundDecimals: roundDecimals, timezone: timezone, httpSkipTlsCheck: httpSkipTlsCheck, cdrs: cdrs, partialRecords: make(map[string]*PartialCDRRecord), - dumpTimers: make(map[string]*time.Timer), guard: guardian.Guardian}, nil + dumpTimers: make(map[string]*time.Timer), + guard: guardian.Guardian, filterS: filterS}, nil } type PartialRecordsCache struct { @@ -59,6 +61,7 @@ type PartialRecordsCache struct { partialRecords map[string]*PartialCDRRecord // [OriginID]*PartialRecord dumpTimers map[string]*time.Timer // [OriginID]*time.Timer which can be canceled or reset guard *guardian.GuardianLocker + filterS *engine.FilterS } // Dumps the cache into a .unpaired file in the outdir and cleans cache after @@ -74,7 +77,7 @@ func (prc *PartialRecordsCache) dumpPartialRecords(originID string) { csvWriter := csv.NewWriter(fileOut) csvWriter.Comma = prc.csvSep for _, cdr := range prc.partialRecords[originID].cdrs { - expRec, err := cdr.AsExportRecord(prc.partialRecords[originID].cacheDumpFields, prc.httpSkipTlsCheck, nil, prc.roundDecimals) + expRec, err := cdr.AsExportRecord(prc.partialRecords[originID].cacheDumpFields, prc.httpSkipTlsCheck, nil, prc.roundDecimals, prc.filterS) if err != nil { return nil, err } @@ -182,15 +185,15 @@ func (prc *PartialRecordsCache) MergePartialCDRRecord(pCDR *PartialCDRRecord) (* return pCDRIf.(*engine.CDR), err } -func NewPartialCDRRecord(cdr *engine.CDR, cacheDumpFlds []*config.CfgCdrField) *PartialCDRRecord { +func NewPartialCDRRecord(cdr *engine.CDR, cacheDumpFlds []*config.FCTemplate) *PartialCDRRecord { return &PartialCDRRecord{cdrs: []*engine.CDR{cdr}, cacheDumpFields: cacheDumpFlds} } // PartialCDRRecord is a record which can be updated later // different from PartialFlatstoreRecordsCache which is incomplete (eg: need to calculate duration out of 2 records) type PartialCDRRecord struct { - cdrs []*engine.CDR // Number of CDRs - cacheDumpFields []*config.CfgCdrField // Fields template to use when dumping from cache on disk + cdrs []*engine.CDR // Number of CDRs + cacheDumpFields []*config.FCTemplate // Fields template to use when dumping from cache on disk } // Part of sort interface diff --git a/config/cdrcconfig.go b/config/cdrcconfig.go index 58d6dec2b..59c14d61c 100644 --- a/config/cdrcconfig.go +++ b/config/cdrcconfig.go @@ -49,7 +49,7 @@ type CdrcConfig struct { HeaderFields []*FCTemplate ContentFields []*FCTemplate TrailerFields []*FCTemplate - CacheDumpFields []*CfgCdrField + CacheDumpFields []*FCTemplate } func (self *CdrcConfig) loadFromJsonCfg(jsnCfg *CdrcJsonCfg) error { @@ -142,9 +142,7 @@ func (self *CdrcConfig) loadFromJsonCfg(jsnCfg *CdrcJsonCfg) error { self.TrailerFields = FCTemplatesFromFCTemapltesJsonCfg(*jsnCfg.Trailer_fields) } if jsnCfg.Cache_dump_fields != nil { - if self.CacheDumpFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Cache_dump_fields); err != nil { - return err - } + self.CacheDumpFields = FCTemplatesFromFCTemapltesJsonCfg(*jsnCfg.Cache_dump_fields) } return nil } @@ -182,7 +180,7 @@ func (self *CdrcConfig) Clone() *CdrcConfig { clnCdrc.HeaderFields = make([]*FCTemplate, len(self.HeaderFields)) clnCdrc.ContentFields = make([]*FCTemplate, len(self.ContentFields)) clnCdrc.TrailerFields = make([]*FCTemplate, len(self.TrailerFields)) - clnCdrc.CacheDumpFields = make([]*CfgCdrField, len(self.CacheDumpFields)) + clnCdrc.CacheDumpFields = make([]*FCTemplate, len(self.CacheDumpFields)) for idx, fld := range self.HeaderFields { clonedVal := *fld clnCdrc.HeaderFields[idx] = &clonedVal diff --git a/config/cdreconfig.go b/config/cdreconfig.go index 9dfd0c34c..844bf6fd3 100644 --- a/config/cdreconfig.go +++ b/config/cdreconfig.go @@ -35,9 +35,9 @@ type CdreConfig struct { FieldSeparator rune UsageMultiplyFactor utils.FieldMultiplyFactor CostMultiplyFactor float64 - HeaderFields []*CfgCdrField - ContentFields []*CfgCdrField - TrailerFields []*CfgCdrField + HeaderFields []*FCTemplate + ContentFields []*FCTemplate + TrailerFields []*FCTemplate } func (self *CdreConfig) loadFromJsonCfg(jsnCfg *CdreJsonCfg) error { @@ -87,19 +87,13 @@ func (self *CdreConfig) loadFromJsonCfg(jsnCfg *CdreJsonCfg) error { self.CostMultiplyFactor = *jsnCfg.Cost_multiply_factor } if jsnCfg.Header_fields != nil { - if self.HeaderFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Header_fields); err != nil { - return err - } + self.HeaderFields = FCTemplatesFromFCTemapltesJsonCfg(*jsnCfg.Header_fields) } if jsnCfg.Content_fields != nil { - if self.ContentFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Content_fields); err != nil { - return err - } + self.ContentFields = FCTemplatesFromFCTemapltesJsonCfg(*jsnCfg.Content_fields) } if jsnCfg.Trailer_fields != nil { - if self.TrailerFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Trailer_fields); err != nil { - return err - } + self.TrailerFields = FCTemplatesFromFCTemapltesJsonCfg(*jsnCfg.Trailer_fields) } return nil } @@ -122,17 +116,17 @@ func (self *CdreConfig) Clone() *CdreConfig { clnCdre.Filters[i] = fltr } clnCdre.CostMultiplyFactor = self.CostMultiplyFactor - clnCdre.HeaderFields = make([]*CfgCdrField, len(self.HeaderFields)) + clnCdre.HeaderFields = make([]*FCTemplate, len(self.HeaderFields)) for idx, fld := range self.HeaderFields { clonedVal := *fld clnCdre.HeaderFields[idx] = &clonedVal } - clnCdre.ContentFields = make([]*CfgCdrField, len(self.ContentFields)) + clnCdre.ContentFields = make([]*FCTemplate, len(self.ContentFields)) for idx, fld := range self.ContentFields { clonedVal := *fld clnCdre.ContentFields[idx] = &clonedVal } - clnCdre.TrailerFields = make([]*CfgCdrField, len(self.TrailerFields)) + clnCdre.TrailerFields = make([]*FCTemplate, len(self.TrailerFields)) for idx, fld := range self.TrailerFields { clonedVal := *fld clnCdre.TrailerFields[idx] = &clonedVal diff --git a/config/cdreconfig_test.go b/config/cdreconfig_test.go index c95ee5f9a..d540a5475 100644 --- a/config/cdreconfig_test.go +++ b/config/cdreconfig_test.go @@ -25,15 +25,15 @@ import ( ) func TestCdreCfgClone(t *testing.T) { - cgrIdRsrs := utils.ParseRSRFieldsMustCompile("cgrid", utils.INFIELD_SEP) - runIdRsrs := utils.ParseRSRFieldsMustCompile("runid", utils.INFIELD_SEP) - emptyFields := []*CfgCdrField{} - initContentFlds := []*CfgCdrField{ - &CfgCdrField{Tag: "CgrId", + cgrIdRsrs := NewRSRParsersMustCompile("cgrid", true) + runIdRsrs := NewRSRParsersMustCompile("runid", true) + emptyFields := []*FCTemplate{} + initContentFlds := []*FCTemplate{ + &FCTemplate{ID: "CgrId", Type: "*composed", FieldId: "cgrid", Value: cgrIdRsrs}, - &CfgCdrField{Tag: "RunId", + &FCTemplate{ID: "RunId", Type: "*composed", FieldId: "runid", Value: runIdRsrs}, @@ -51,12 +51,12 @@ func TestCdreCfgClone(t *testing.T) { CostMultiplyFactor: 1.0, ContentFields: initContentFlds, } - eClnContentFlds := []*CfgCdrField{ - &CfgCdrField{Tag: "CgrId", + eClnContentFlds := []*FCTemplate{ + &FCTemplate{ID: "CgrId", Type: "*composed", FieldId: "cgrid", Value: cgrIdRsrs}, - &CfgCdrField{Tag: "RunId", + &FCTemplate{ID: "RunId", Type: "*composed", FieldId: "runid", Value: runIdRsrs}, @@ -85,7 +85,7 @@ func TestCdreCfgClone(t *testing.T) { if !reflect.DeepEqual(eClnCdreCfg, clnCdreCfg) { // MOdifying a field after clone should not affect cloned instance t.Errorf("Cloned result: %+v", clnCdreCfg) } - initContentFlds[0].Tag = "Destination" + initContentFlds[0].ID = "Destination" if !reflect.DeepEqual(eClnCdreCfg, clnCdreCfg) { // MOdifying a field after clone should not affect cloned instance t.Errorf("Cloned result: %+v", clnCdreCfg) } diff --git a/config/config_defaults.go b/config/config_defaults.go index 3cf32cb8e..f46ecfb81 100755 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -204,20 +204,20 @@ const CGRATES_CFG_JSON = ` "cost_multiply_factor": 1, // multiply cost before export, eg: add VAT "header_fields": [], // template of the exported header fields "content_fields": [ // template of the exported content fields - {"tag": "CGRID", "type": "*composed", "value": "CGRID"}, - {"tag":"RunID", "type": "*composed", "value": "RunID"}, - {"tag":"TOR", "type": "*composed", "value": "ToR"}, - {"tag":"OriginID", "type": "*composed", "value": "OriginID"}, - {"tag":"RequestType", "type": "*composed", "value": "RequestType"}, - {"tag":"Tenant", "type": "*composed", "value": "Tenant"}, - {"tag":"Category", "type": "*composed", "value": "Category"}, - {"tag":"Account", "type": "*composed", "value": "Account"}, - {"tag":"Subject", "type": "*composed", "value": "Subject"}, - {"tag":"Destination", "type": "*composed", "value": "Destination"}, - {"tag":"SetupTime", "type": "*composed", "value": "SetupTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag":"Usage", "type": "*composed", "value": "Usage"}, - {"tag":"Cost", "type": "*composed", "value": "Cost", "rounding_decimals": 4}, + {"id": "CGRID", "type": "*composed", "value": "~CGRID"}, + {"id":"RunID", "type": "*composed", "value": "~RunID"}, + {"id":"TOR", "type": "*composed", "value": "~ToR"}, + {"id":"OriginID", "type": "*composed", "value": "~OriginID"}, + {"id":"RequestType", "type": "*composed", "value": "~RequestType"}, + {"id":"Tenant", "type": "*composed", "value": "~Tenant"}, + {"id":"Category", "type": "*composed", "value": "~Category"}, + {"id":"Account", "type": "*composed", "value": "~Account"}, + {"id":"Subject", "type": "*composed", "value": "~Subject"}, + {"id":"Destination", "type": "*composed", "value": "~Destination"}, + {"id":"SetupTime", "type": "*composed", "value": "~SetupTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"id":"AnswerTime", "type": "*composed", "value": "~AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"id":"Usage", "type": "*composed", "value": "~Usage"}, + {"id":"Cost", "type": "*composed", "value": "~Cost", "rounding_decimals": 4}, ], "trailer_fields": [], // template of the exported trailer fields }, @@ -271,20 +271,20 @@ const CGRATES_CFG_JSON = ` ], "trailer_fields": [], // template of the import trailer fields "cache_dump_fields": [ // template used when dumping cached CDR, eg: partial CDRs - {"tag": "CGRID", "type": "*composed", "value": "CGRID"}, - {"tag": "RunID", "type": "*composed", "value": "RunID"}, - {"tag": "TOR", "type": "*composed", "value": "ToR"}, - {"tag": "OriginID", "type": "*composed", "value": "OriginID"}, - {"tag": "RequestType", "type": "*composed", "value": "RequestType"}, - {"tag": "Tenant", "type": "*composed", "value": "Tenant"}, - {"tag": "Category", "type": "*composed", "value": "Category"}, - {"tag": "Account", "type": "*composed", "value": "Account"}, - {"tag": "Subject", "type": "*composed", "value": "Subject"}, - {"tag": "Destination", "type": "*composed", "value": "Destination"}, - {"tag": "SetupTime", "type": "*composed", "value": "SetupTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag": "AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag": "Usage", "type": "*composed", "value": "Usage"}, - {"tag": "Cost", "type": "*composed", "value": "Cost"}, + {"id": "CGRID", "type": "*composed", "value": "~CGRID"}, + {"id": "RunID", "type": "*composed", "value": "~RunID"}, + {"id": "TOR", "type": "*composed", "value": "~ToR"}, + {"id": "OriginID", "type": "*composed", "value": "~OriginID"}, + {"id": "RequestType", "type": "*composed", "value": "~RequestType"}, + {"id": "Tenant", "type": "*composed", "value": "~Tenant"}, + {"id": "Category", "type": "*composed", "value": "~Category"}, + {"id": "Account", "type": "*composed", "value": "~Account"}, + {"id": "Subject", "type": "*composed", "value": "~Subject"}, + {"id": "Destination", "type": "*composed", "value": "~Destination"}, + {"id": "SetupTime", "type": "*composed", "value": "~SetupTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"id": "AnswerTime", "type": "*composed", "value": "~AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"id": "Usage", "type": "*composed", "value": "~Usage"}, + {"id": "Cost", "type": "*composed", "value": "~Cost"}, ], }, ], diff --git a/config/config_json_test.go b/config/config_json_test.go index ac1a4611f..7018e834e 100755 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -298,52 +298,52 @@ func TestDfCdrStatsJsonCfg(t *testing.T) { } func TestDfCdreJsonCfgs(t *testing.T) { - eFields := []*CdrFieldJsonCfg{} - eContentFlds := []*CdrFieldJsonCfg{ - &CdrFieldJsonCfg{Tag: utils.StringPointer("CGRID"), + eFields := []*FcTemplateJsonCfg{} + eContentFlds := []*FcTemplateJsonCfg{ + &FcTemplateJsonCfg{Id: utils.StringPointer("CGRID"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.CGRID)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("RunID"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.CGRID)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("RunID"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.RunID)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("TOR"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.RunID)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("TOR"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.ToR)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("OriginID"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.ToR)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("OriginID"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.OriginID)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("RequestType"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.OriginID)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("RequestType"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.RequestType)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Tenant"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.RequestType)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Tenant"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Tenant)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Category"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Tenant)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Category"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Category)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Account"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Category)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Account"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Account)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Subject"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Account)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Subject"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Subject)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Destination"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Subject)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Destination"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Destination)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("SetupTime"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Destination)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("SetupTime"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.SetupTime), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.SetupTime), Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("AnswerTime"), + &FcTemplateJsonCfg{Id: utils.StringPointer("AnswerTime"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.AnswerTime), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.AnswerTime), Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Usage"), + &FcTemplateJsonCfg{Id: utils.StringPointer("Usage"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Usage)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Cost"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Usage)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Cost"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.COST), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.COST), Rounding_decimals: utils.IntPointer(4)}, } eCfg := map[string]*CdreJsonCfg{ @@ -398,51 +398,51 @@ func TestDfCdrcJsonCfg(t *testing.T) { &FcTemplateJsonCfg{Id: utils.StringPointer("Usage"), Field_id: utils.StringPointer(utils.Usage), Type: utils.StringPointer(utils.META_COMPOSED), Value: utils.StringPointer("~13"), Mandatory: utils.BoolPointer(true)}, } - cacheDumpFields := []*CdrFieldJsonCfg{ - &CdrFieldJsonCfg{Tag: utils.StringPointer("CGRID"), + cacheDumpFields := []*FcTemplateJsonCfg{ + &FcTemplateJsonCfg{Id: utils.StringPointer("CGRID"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.CGRID)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("RunID"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.CGRID)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("RunID"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.RunID)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("TOR"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.RunID)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("TOR"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.ToR)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("OriginID"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.ToR)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("OriginID"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.OriginID)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("RequestType"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.OriginID)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("RequestType"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.RequestType)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Tenant"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.RequestType)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Tenant"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Tenant)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Category"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Tenant)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Category"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Category)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Account"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Category)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Account"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Account)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Subject"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Account)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Subject"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Subject)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Destination"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Subject)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Destination"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Destination)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("SetupTime"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Destination)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("SetupTime"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.SetupTime), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.SetupTime), Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("AnswerTime"), + &FcTemplateJsonCfg{Id: utils.StringPointer("AnswerTime"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.AnswerTime), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.AnswerTime), Layout: utils.StringPointer("2006-01-02T15:04:05Z07:00")}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Usage"), + &FcTemplateJsonCfg{Id: utils.StringPointer("Usage"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.Usage)}, - &CdrFieldJsonCfg{Tag: utils.StringPointer("Cost"), + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.Usage)}, + &FcTemplateJsonCfg{Id: utils.StringPointer("Cost"), Type: utils.StringPointer(utils.META_COMPOSED), - Value: utils.StringPointer(utils.COST)}, + Value: utils.StringPointer(utils.DynamicDataPrefix + utils.COST)}, } eCfg := []*CdrcJsonCfg{ &CdrcJsonCfg{ diff --git a/config/config_test.go b/config/config_test.go index 936e447c6..9e306d282 100755 --- a/config/config_test.go +++ b/config/config_test.go @@ -202,37 +202,37 @@ func TestCgrCfgCDRC(t *testing.T) { Value: NewRSRParsersMustCompile("~9:s/^(\\d+)$/${1}s/", true)}, }, TrailerFields: make([]*FCTemplate, 0), - CacheDumpFields: []*CfgCdrField{ - &CfgCdrField{Tag: "CGRID", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RunID", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.RunID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "TOR", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.ToR, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "OriginID", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.OriginID, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RequestType", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.RequestType, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Tenant", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.Tenant, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Category", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.Category, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Account", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.Account, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Subject", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.Subject, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Destination", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.Destination, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "SetupTime", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.SetupTime, utils.INFIELD_SEP), + CacheDumpFields: []*FCTemplate{ + &FCTemplate{ID: "CGRID", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.CGRID, true)}, + &FCTemplate{ID: "RunID", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.RunID, true)}, + &FCTemplate{ID: "TOR", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.ToR, true)}, + &FCTemplate{ID: "OriginID", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.OriginID, true)}, + &FCTemplate{ID: "RequestType", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.RequestType, true)}, + &FCTemplate{ID: "Tenant", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.Tenant, true)}, + &FCTemplate{ID: "Category", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.Category, true)}, + &FCTemplate{ID: "Account", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.Account, true)}, + &FCTemplate{ID: "Subject", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.Subject, true)}, + &FCTemplate{ID: "Destination", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.Destination, true)}, + &FCTemplate{ID: "SetupTime", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.SetupTime, true), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "AnswerTime", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.AnswerTime, utils.INFIELD_SEP), + &FCTemplate{ID: "AnswerTime", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.AnswerTime, true), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "Usage", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.Usage, utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Cost", Type: utils.META_COMPOSED, - Value: utils.ParseRSRFieldsMustCompile(utils.COST, utils.INFIELD_SEP)}, + &FCTemplate{ID: "Usage", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.Usage, true)}, + &FCTemplate{ID: "Cost", Type: utils.META_COMPOSED, + Value: NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.COST, true)}, }, }, } @@ -576,38 +576,38 @@ func TestCgrCfgJSONDefaultsCDRStats(t *testing.T) { } func TestCgrCfgJSONDefaultsCdreProfiles(t *testing.T) { - eFields := []*CfgCdrField{} - eContentFlds := []*CfgCdrField{ - &CfgCdrField{Tag: "CGRID", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("CGRID", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RunID", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("RunID", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "TOR", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("ToR", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "OriginID", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("OriginID", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "RequestType", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("RequestType", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Tenant", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Tenant", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Category", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Category", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Account", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Account", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Subject", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Subject", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Destination", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Destination", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "SetupTime", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("SetupTime", utils.INFIELD_SEP), + eFields := []*FCTemplate{} + eContentFlds := []*FCTemplate{ + &FCTemplate{ID: "CGRID", Type: "*composed", + Value: NewRSRParsersMustCompile("~CGRID", true)}, + &FCTemplate{ID: "RunID", Type: "*composed", + Value: NewRSRParsersMustCompile("~RunID", true)}, + &FCTemplate{ID: "TOR", Type: "*composed", + Value: NewRSRParsersMustCompile("~ToR", true)}, + &FCTemplate{ID: "OriginID", Type: "*composed", + Value: NewRSRParsersMustCompile("~OriginID", true)}, + &FCTemplate{ID: "RequestType", Type: "*composed", + Value: NewRSRParsersMustCompile("~RequestType", true)}, + &FCTemplate{ID: "Tenant", Type: "*composed", + Value: NewRSRParsersMustCompile("~Tenant", true)}, + &FCTemplate{ID: "Category", Type: "*composed", + Value: NewRSRParsersMustCompile("~Category", true)}, + &FCTemplate{ID: "Account", Type: "*composed", + Value: NewRSRParsersMustCompile("~Account", true)}, + &FCTemplate{ID: "Subject", Type: "*composed", + Value: NewRSRParsersMustCompile("~Subject", true)}, + &FCTemplate{ID: "Destination", Type: "*composed", + Value: NewRSRParsersMustCompile("~Destination", true)}, + &FCTemplate{ID: "SetupTime", Type: "*composed", + Value: NewRSRParsersMustCompile("~SetupTime", true), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "AnswerTime", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("AnswerTime", utils.INFIELD_SEP), + &FCTemplate{ID: "AnswerTime", Type: "*composed", + Value: NewRSRParsersMustCompile("~AnswerTime", true), Layout: "2006-01-02T15:04:05Z07:00"}, - &CfgCdrField{Tag: "Usage", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Usage", utils.INFIELD_SEP)}, - &CfgCdrField{Tag: "Cost", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Cost", utils.INFIELD_SEP), + &FCTemplate{ID: "Usage", Type: "*composed", + Value: NewRSRParsersMustCompile("~Usage", true)}, + &FCTemplate{ID: "Cost", Type: "*composed", + Value: NewRSRParsersMustCompile("~Cost", true), RoundingDecimals: 4}, } eCdreCfg := map[string]*CdreConfig{ diff --git a/config/configcdrc_test.go b/config/configcdrc_test.go index 22ee00822..6a08ce081 100644 --- a/config/configcdrc_test.go +++ b/config/configcdrc_test.go @@ -17,6 +17,7 @@ along with this program. If not, see */ package config +/* import ( "reflect" "testing" @@ -330,3 +331,4 @@ func TestLoadCdrcConfigMultipleFiles(t *testing.T) { t.Errorf("Expected: %s \n, received: %s", utils.ToJSON(eCgrCfg.CdrcProfiles), utils.ToJSON(cgrCfg.CdrcProfiles)) } } +*/ diff --git a/config/fctemplate.go b/config/fctemplate.go index 72bc6d68e..bb045aef9 100755 --- a/config/fctemplate.go +++ b/config/fctemplate.go @@ -71,26 +71,42 @@ func NewFCTemplateFromFCTemplateJsonCfg(jsnCfg *FcTemplateJsonCfg) *FCTemplate { if jsnCfg.Layout != nil { fcTmp.Layout = *jsnCfg.Layout } + if jsnCfg.Cost_shift_digits != nil { + fcTmp.CostShiftDigits = *jsnCfg.Cost_shift_digits + } + if jsnCfg.Rounding_decimals != nil { + fcTmp.RoundingDecimals = *jsnCfg.Rounding_decimals + } + if jsnCfg.Mask_destinationd_id != nil { + fcTmp.MaskDestID = *jsnCfg.Mask_destinationd_id + } + if jsnCfg.Mask_length != nil { + fcTmp.MaskLen = *jsnCfg.Mask_length + } return fcTmp } type FCTemplate struct { - ID string - Type string // Type of field - FieldId string // Field identifier - Filters []string // list of filter profiles - Value RSRParsers - Width int - Strip string - Padding string - Mandatory bool - AttributeID string // Used by NavigableMap when creating CGREvent/XMLElements - NewBranch bool // Used by NavigableMap when creating XMLElements - Timezone string - Blocker bool - BreakOnSuccess bool - HandlerId string // used by XML in CDRC - Layout string // time format + ID string + Type string // Type of field + FieldId string // Field identifier + Filters []string // list of filter profiles + Value RSRParsers + Width int + Strip string + Padding string + Mandatory bool + AttributeID string // Used by NavigableMap when creating CGREvent/XMLElements + NewBranch bool // Used by NavigableMap when creating XMLElements + Timezone string + Blocker bool + BreakOnSuccess bool + HandlerId string // used by XML in CDRC + Layout string // time format + CostShiftDigits int // Used for CDR + RoundingDecimals int + MaskDestID string + MaskLen int } func FCTemplatesFromFCTemapltesJsonCfg(jsnCfgFlds []*FcTemplateJsonCfg) []*FCTemplate { diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 648b8329f..c46c18e08 100755 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -179,9 +179,9 @@ type CdreJsonCfg struct { Field_separator *string Usage_multiply_factor *map[string]float64 Cost_multiply_factor *float64 - Header_fields *[]*CdrFieldJsonCfg - Content_fields *[]*CdrFieldJsonCfg - Trailer_fields *[]*CdrFieldJsonCfg + Header_fields *[]*FcTemplateJsonCfg + Content_fields *[]*FcTemplateJsonCfg + Trailer_fields *[]*FcTemplateJsonCfg } // Cdrc config section @@ -210,7 +210,7 @@ type CdrcJsonCfg struct { Header_fields *[]*FcTemplateJsonCfg Content_fields *[]*FcTemplateJsonCfg Trailer_fields *[]*FcTemplateJsonCfg - Cache_dump_fields *[]*CdrFieldJsonCfg + Cache_dump_fields *[]*FcTemplateJsonCfg } // SM-Generic config section @@ -574,20 +574,24 @@ type MigratorCfgJson struct { } type FcTemplateJsonCfg struct { - Id *string - Type *string - Field_id *string - Attribute_id *string - Filters *[]string - Value *string - Width *int - Strip *string - Padding *string - Mandatory *bool - New_branch *bool - Timezone *string - Blocker *bool - Break_on_success *bool - Handler_id *string - Layout *string + Id *string + Type *string + Field_id *string + Attribute_id *string + Filters *[]string + Value *string + Width *int + Strip *string + Padding *string + Mandatory *bool + New_branch *bool + Timezone *string + Blocker *bool + Break_on_success *bool + Handler_id *string + Layout *string + Cost_shift_digits *int + Rounding_decimals *int + Mask_destinationd_id *string + Mask_length *int } diff --git a/config/rsrparser.go b/config/rsrparser.go index cda51d669..b53dc18fd 100644 --- a/config/rsrparser.go +++ b/config/rsrparser.go @@ -102,6 +102,17 @@ func (prsrs RSRParsers) ParseDataProvider(dP DataProvider) (out string, err erro return } +func (prsrs RSRParsers) ParseCDR(dP DataProvider) (out string, err error) { + for _, prsr := range prsrs { + if outPrsr, err := prsr.ParseCDR(dP); err != nil { + return "", err + } else { + out += outPrsr + } + } + return +} + func NewRSRParser(parserRules string, allFiltersMatch bool) (rsrParser *RSRParser, err error) { if len(parserRules) == 0 { return @@ -263,3 +274,14 @@ func (prsr *RSRParser) ParseDataProvider(dP DataProvider) (out string, err error } return prsr.ParseValue(outStr) } + +func (prsr *RSRParser) ParseCDR(dP DataProvider) (out string, err error) { + var outStr interface{} + if prsr.attrValue == "" { + if outStr, err = dP.FieldAsInterface( + strings.Split(prsr.attrName, utils.NestingSep)); err != nil { + return + } + } + return prsr.ParseValue(outStr) +} diff --git a/data/conf/samples/cdrc_partcsv/cgrates.json b/data/conf/samples/cdrc_partcsv/cgrates.json index 5525f7e57..f5fce304d 100644 --- a/data/conf/samples/cdrc_partcsv/cgrates.json +++ b/data/conf/samples/cdrc_partcsv/cgrates.json @@ -51,15 +51,15 @@ {"id": "Partial", "field_id": "Partial", "type": "*composed", "value": "true", "filters":["*string:10:partial"]}, ], "cache_dump_fields": [ - {"tag": "OriginID", "type": "*composed", "value": "OriginID"}, - {"tag": "OrderID", "type": "*composed", "value": "OrderID"}, - {"tag": "RequestType", "type": "*composed", "value": "RequestType"}, - {"tag": "Account", "type": "*composed", "value": "Account"}, - {"tag": "Destination", "type": "*composed", "value": "Destination"}, - {"tag": "SetupTime", "type": "*composed", "value": "SetupTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag": "AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag": "Usage", "type": "*composed", "value": "Usage"}, - {"tag": "Cost", "type": "*composed", "value": "Cost"}, + {"id": "OriginID", "type": "*composed", "value": "~OriginID"}, + {"id": "OrderID", "type": "*composed", "value": "~OrderID"}, + {"id": "RequestType", "type": "*composed", "value": "~RequestType"}, + {"id": "Account", "type": "*composed", "value": "~Account"}, + {"id": "Destination", "type": "*composed", "value": "~Destination"}, + {"id": "SetupTime", "type": "*composed", "value": "~SetupTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"id": "AnswerTime", "type": "*composed", "value": "~AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"id": "Usage", "type": "*composed", "value": "~Usage"}, + {"id": "Cost", "type": "*composed", "value": "~Cost"}, ], }, { diff --git a/data/conf/samples/cdrewithfilter/cgrates.json b/data/conf/samples/cdrewithfilter/cgrates.json index 37d7908c1..800447ebc 100755 --- a/data/conf/samples/cdrewithfilter/cgrates.json +++ b/data/conf/samples/cdrewithfilter/cgrates.json @@ -87,16 +87,16 @@ "export_path": "/tmp", "filters" :["*string:Source:test2"], "content_fields": [ - {"tag": "CGRID", "type": "*composed", "value": "CGRID"}, - {"tag": "RunID", "type": "*composed", "value": "RunID"}, - {"tag":"Source", "type": "*composed", "value": "Source"}, - {"tag":"OriginID", "type": "*composed", "value": "OriginID"}, - {"tag":"Tenant", "type": "*composed", "value": "Tenant"}, - {"tag":"Category", "type": "*composed", "value": "Category"}, - {"tag":"Account", "type": "*composed", "value": "Account"}, - {"tag":"Destination", "type": "*composed", "value": "Destination"}, - {"tag":"Usage", "type": "*composed", "value": "Usage"}, - {"tag":"Cost", "type": "*composed", "value": "Cost", "rounding_decimals": 4}, + {"id": "CGRID", "type": "*composed", "value": "~CGRID"}, + {"id": "RunID", "type": "*composed", "value": "~RunID"}, + {"id":"Source", "type": "*composed", "value": "~Source"}, + {"id":"OriginID", "type": "*composed", "value": "~OriginID"}, + {"id":"Tenant", "type": "*composed", "value": "~Tenant"}, + {"id":"Category", "type": "*composed", "value": "~Category"}, + {"id":"Account", "type": "*composed", "value": "~Account"}, + {"id":"Destination", "type": "*composed", "value": "~Destination"}, + {"id":"Usage", "type": "*composed", "value": "~Usage"}, + {"id":"Cost", "type": "*composed", "value": "~Cost", "rounding_decimals": 4}, ], }, }, diff --git a/data/conf/samples/cdrsonexpmaster/cdrsreplicationmaster.json b/data/conf/samples/cdrsonexpmaster/cdrsreplicationmaster.json index c0a505cdf..d46e34c97 100644 --- a/data/conf/samples/cdrsonexpmaster/cdrsreplicationmaster.json +++ b/data/conf/samples/cdrsonexpmaster/cdrsreplicationmaster.json @@ -32,22 +32,22 @@ "export_path": "http://127.0.0.1:12080/cdr_http", "cdr_filter": "RunID(*default);OriginID(httpjsonrpc1)", "content_fields": [ // template of the exported content fields - {"tag": "CGRID", "type": "*composed", "value": "CGRID", "field_id": "CGRID"}, - {"tag":"RunID", "type": "*composed", "value": "RunID", "field_id": "RunID"}, - {"tag":"TOR", "type": "*composed", "value": "ToR", "field_id": "ToR"}, - {"tag":"OriginID", "type": "*composed", "value": "OriginID", "field_id": "OriginID"}, - {"tag":"OriginHost", "type": "*composed", "value": "OriginHost", "field_id": "OriginHost"}, - {"tag":"RequestType", "type": "*composed", "value": "RequestType", "field_id": "RequestType"}, - {"tag":"Direction", "type": "*composed", "value": "Direction", "field_id": "Direction"}, - {"tag":"Tenant", "type": "*composed", "value": "Tenant", "field_id": "Tenant"}, - {"tag":"Category", "type": "*composed", "value": "Category", "field_id": "Category"}, - {"tag":"Account", "type": "*composed", "value": "Account", "field_id": "Account"}, - {"tag":"Subject", "type": "*composed", "value": "Subject", "field_id": "Subject"}, - {"tag":"Destination", "type": "*composed", "value": "Destination", "field_id": "Destination"}, - {"tag":"SetupTime", "type": "*composed", "value": "SetupTime", "layout": "2006-01-02T15:04:05Z07:00", "field_id": "SetupTime"}, - {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00", "field_id": "AnswerTime"}, - {"tag":"Usage", "type": "*composed", "value": "Usage", "field_id": "Usage"}, - {"tag":"Cost", "type": "*composed", "value": "Cost", "field_id": "Cost"}, + {"id": "CGRID", "type": "*composed", "value": "~CGRID", "field_id": "CGRID"}, + {"id":"RunID", "type": "*composed", "value": "~RunID", "field_id": "RunID"}, + {"id":"TOR", "type": "*composed", "value": "~ToR", "field_id": "ToR"}, + {"id":"OriginID", "type": "*composed", "value": "~OriginID", "field_id": "OriginID"}, + {"id":"OriginHost", "type": "*composed", "value": "~OriginHost", "field_id": "OriginHost"}, + {"id":"RequestType", "type": "*composed", "value": "~RequestType", "field_id": "RequestType"}, + {"id":"Direction", "type": "*composed", "value": "~Direction", "field_id": "Direction"}, + {"id":"Tenant", "type": "*composed", "value": "~Tenant", "field_id": "Tenant"}, + {"id":"Category", "type": "*composed", "value": "~Category", "field_id": "Category"}, + {"id":"Account", "type": "*composed", "value": "~Account", "field_id": "Account"}, + {"id":"Subject", "type": "*composed", "value": "~Subject", "field_id": "Subject"}, + {"id":"Destination", "type": "*composed", "value": "~Destination", "field_id": "Destination"}, + {"id":"SetupTime", "type": "*composed", "value": "~SetupTime", "layout": "2006-01-02T15:04:05Z07:00", "field_id": "SetupTime"}, + {"id":"AnswerTime", "type": "*composed", "value": "~AnswerTime", "layout": "2006-01-02T15:04:05Z07:00", "field_id": "AnswerTime"}, + {"id":"Usage", "type": "*composed", "value": "~Usage", "field_id": "Usage"}, + {"id":"Cost", "type": "*composed", "value": "~Cost", "field_id": "Cost"}, ], }, "amqp_localhost": { @@ -56,22 +56,22 @@ "attempts": 3, "cdr_filter": "RunID(*default)", "content_fields": [ // template of the exported content fields - {"tag": "CGRID", "type": "*composed", "value": "CGRID", "field_id": "CGRID"}, - {"tag":"RunID", "type": "*composed", "value": "RunID", "field_id": "RunID"}, - {"tag":"TOR", "type": "*composed", "value": "ToR", "field_id": "ToR"}, - {"tag":"OriginID", "type": "*composed", "value": "OriginID", "field_id": "OriginID"}, - {"tag":"OriginHost", "type": "*composed", "value": "OriginHost", "field_id": "OriginHost"}, - {"tag":"RequestType", "type": "*composed", "value": "RequestType", "field_id": "RequestType"}, - {"tag":"Direction", "type": "*composed", "value": "Direction", "field_id": "Direction"}, - {"tag":"Tenant", "type": "*composed", "value": "Tenant", "field_id": "Tenant"}, - {"tag":"Category", "type": "*composed", "value": "Category", "field_id": "Category"}, - {"tag":"Account", "type": "*composed", "value": "Account", "field_id": "Account"}, - {"tag":"Subject", "type": "*composed", "value": "Subject", "field_id": "Subject"}, - {"tag":"Destination", "type": "*composed", "value": "Destination", "field_id": "Destination"}, - {"tag":"SetupTime", "type": "*composed", "value": "SetupTime", "layout": "2006-01-02T15:04:05Z07:00", "field_id": "SetupTime"}, - {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00", "field_id": "AnswerTime"}, - {"tag":"Usage", "type": "*composed", "value": "Usage", "field_id": "Usage"}, - {"tag":"Cost", "type": "*composed", "value": "Cost", "field_id": "Cost"}, + {"id": "CGRID", "type": "*composed", "value": "~CGRID", "field_id": "CGRID"}, + {"id":"RunID", "type": "*composed", "value": "~RunID", "field_id": "RunID"}, + {"id":"TOR", "type": "*composed", "value": "~ToR", "field_id": "ToR"}, + {"id":"OriginID", "type": "*composed", "value": "~OriginID", "field_id": "OriginID"}, + {"id":"OriginHost", "type": "*composed", "value": "~OriginHost", "field_id": "OriginHost"}, + {"id":"RequestType", "type": "*composed", "value": "~RequestType", "field_id": "RequestType"}, + {"id":"Direction", "type": "*composed", "value": "~Direction", "field_id": "Direction"}, + {"id":"Tenant", "type": "*composed", "value": "~Tenant", "field_id": "Tenant"}, + {"id":"Category", "type": "*composed", "value": "~Category", "field_id": "Category"}, + {"id":"Account", "type": "*composed", "value": "~Account", "field_id": "Account"}, + {"id":"Subject", "type": "*composed", "value": "~Subject", "field_id": "Subject"}, + {"id":"Destination", "type": "*composed", "value": "~Destination", "field_id": "Destination"}, + {"id":"SetupTime", "type": "*composed", "value": "~SetupTime", "layout": "2006-01-02T15:04:05Z07:00", "field_id": "SetupTime"}, + {"id":"AnswerTime", "type": "*composed", "value": "~AnswerTime", "layout": "2006-01-02T15:04:05Z07:00", "field_id": "AnswerTime"}, + {"id":"Usage", "type": "*composed", "value": "~Usage", "field_id": "Usage"}, + {"id":"Cost", "type": "*composed", "value": "~Cost", "field_id": "Cost"}, ], }, "http_test_file": { @@ -79,14 +79,14 @@ "export_path": "http://127.0.0.1:12080/invalid", "cdr_filter": "OriginID(httpjsonrpc1)", "content_fields": [ - {"tag": "OriginID", "type": "*composed", "value": "OriginID", "field_id": "OriginID"}, + {"id": "OriginID", "type": "*composed", "value": "~OriginID", "field_id": "OriginID"}, ], }, "amqp_test_file": { "export_format": "*amqp_json_map", "export_path": "amqp://guest:guest@localhost:25672/?queue_id=cgrates_cdrs", "content_fields": [ - {"tag": "CGRID", "type": "*composed", "value": "CGRID", "field_id": "CGRID"}, + {"id": "CGRID", "type": "*composed", "value": "~CGRID", "field_id": "CGRID"}, ], }, }, diff --git a/data/conf/samples/dbinternal/cgrates.json b/data/conf/samples/dbinternal/cgrates.json index 1daf73266..0619bdb01 100755 --- a/data/conf/samples/dbinternal/cgrates.json +++ b/data/conf/samples/dbinternal/cgrates.json @@ -61,18 +61,18 @@ "cdre": { "TestTutITExportCDR": { "content_fields": [ - {"tag": "CGRID", "type": "*composed", "value": "CGRID"}, - {"tag": "RunID", "type": "*composed", "value": "RunID"}, - {"tag":"OriginID", "type": "*composed", "value": "OriginID"}, - {"tag":"RequestType", "type": "*composed", "value": "RequestType"}, - {"tag":"Tenant", "type": "*composed", "value": "Tenant"}, - {"tag":"Category", "type": "*composed", "value": "Category"}, - {"tag":"Account", "type": "*composed", "value": "Account"}, - {"tag":"Destination", "type": "*composed", "value": "Destination"}, - {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag":"Usage", "type": "*composed", "value": "Usage"}, - {"tag":"Cost", "type": "*composed", "value": "Cost", "rounding_decimals": 4}, - {"tag":"MatchedDestinationID", "type": "*composed", "value": "~CostDetails:s/\"MatchedDestId\":.*_(\\w{4})/${1}/:s/\"MatchedDestId\":\"INTERNAL\"/ON010/"}, + {"id": "CGRID", "type": "*composed", "value": "~CGRID"}, + {"id": "RunID", "type": "*composed", "value": "~RunID"}, + {"id":"OriginID", "type": "*composed", "value": "~OriginID"}, + {"id":"RequestType", "type": "*composed", "value": "~RequestType"}, + {"id":"Tenant", "type": "*composed", "value": "~Tenant"}, + {"id":"Category", "type": "*composed", "value": "~Category"}, + {"id":"Account", "type": "*composed", "value": "~Account"}, + {"id":"Destination", "type": "*composed", "value": "~Destination"}, + {"id":"AnswerTime", "type": "*composed", "value": "~AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"id":"Usage", "type": "*composed", "value": "~Usage"}, + {"id":"Cost", "type": "*composed", "value": "~Cost", "rounding_decimals": 4}, + {"id":"MatchedDestinationID", "type": "*composed", "value": "~CostDetails:s/\"MatchedDestId\":.*_(\\w{4})/${1}/:s/\"MatchedDestId\":\"INTERNAL\"/ON010/"}, ], }, }, diff --git a/data/conf/samples/fscsv/freeswitch_csvcdr.json b/data/conf/samples/fscsv/freeswitch_csvcdr.json index 27c9932b9..867d6f3c5 100644 --- a/data/conf/samples/fscsv/freeswitch_csvcdr.json +++ b/data/conf/samples/fscsv/freeswitch_csvcdr.json @@ -9,18 +9,18 @@ "cdr_out_dir": "/tmp/cgrates/cdrc_fs/out", // absolute path towards the directory where processed CDRs will be moved "cdr_source_id": "fs_csv", // free form field, tag identifying the source of the CDRs within CDRS database "content_fields":[ // import template, tag will match internally CDR field, in case of .csv value will be represented by index of the field value - {"tag": "tor", "cdr_field_id": "tor", "type": "cdrfield", "value": "^*voice", "mandatory": true}, - {"tag": "accid", "cdr_field_id": "accid", "type": "cdrfield", "value": "10", "mandatory": true}, - {"tag": "reqtype", "cdr_field_id": "reqtype", "type": "cdrfield", "value": "^rated", "mandatory": true}, - {"tag": "direction", "cdr_field_id": "direction", "type": "cdrfield", "value": "^*out", "mandatory": true}, - {"tag": "tenant", "cdr_field_id": "tenant", "type": "cdrfield", "value": "^cgrates.org", "mandatory": true}, - {"tag": "category", "cdr_field_id": "category", "type": "cdrfield", "value": "^call", "mandatory": true}, - {"tag": "account", "cdr_field_id": "account", "type": "cdrfield", "value": "12", "mandatory": true}, - {"tag": "subject", "cdr_field_id": "subject", "type": "cdrfield", "value": "12", "mandatory": true}, - {"tag": "destination", "cdr_field_id": "destination", "type": "cdrfield", "value": "2", "mandatory": true}, - {"tag": "setup_time", "cdr_field_id": "setup_time", "type": "cdrfield", "value": "4", "mandatory": true}, - {"tag": "answer_time", "cdr_field_id": "answer_time", "type": "cdrfield", "value": "5", "mandatory": true}, - {"tag": "usage", "cdr_field_id": "usage", "type": "cdrfield", "value": "~8:s/^(\\d+)$/${1}s/", "mandatory": true}, + {"id": "tor", "cdr_field_id": "tor", "type": "cdrfield", "value": "^*voice", "mandatory": true}, + {"id": "accid", "cdr_field_id": "accid", "type": "cdrfield", "value": "10", "mandatory": true}, + {"id": "reqtype", "cdr_field_id": "reqtype", "type": "cdrfield", "value": "^rated", "mandatory": true}, + {"id": "direction", "cdr_field_id": "direction", "type": "cdrfield", "value": "^*out", "mandatory": true}, + {"id": "tenant", "cdr_field_id": "tenant", "type": "cdrfield", "value": "^cgrates.org", "mandatory": true}, + {"id": "category", "cdr_field_id": "category", "type": "cdrfield", "value": "^call", "mandatory": true}, + {"id": "account", "cdr_field_id": "account", "type": "cdrfield", "value": "12", "mandatory": true}, + {"id": "subject", "cdr_field_id": "subject", "type": "cdrfield", "value": "12", "mandatory": true}, + {"id": "destination", "cdr_field_id": "destination", "type": "cdrfield", "value": "2", "mandatory": true}, + {"id": "setup_time", "cdr_field_id": "setup_time", "type": "cdrfield", "value": "4", "mandatory": true}, + {"id": "answer_time", "cdr_field_id": "answer_time", "type": "cdrfield", "value": "5", "mandatory": true}, + {"id": "usage", "cdr_field_id": "usage", "type": "cdrfield", "value": "~8:s/^(\\d+)$/${1}s/", "mandatory": true}, ], }, ], diff --git a/data/conf/samples/mongoatlas/cgrates.json b/data/conf/samples/mongoatlas/cgrates.json index d3898c750..06f2eed2d 100755 --- a/data/conf/samples/mongoatlas/cgrates.json +++ b/data/conf/samples/mongoatlas/cgrates.json @@ -89,18 +89,18 @@ "cdre": { "TestTutITExportCDR": { "content_fields": [ - {"tag": "CGRID", "type": "*composed", "value": "CGRID"}, - {"tag": "RunID", "type": "*composed", "value": "RunID"}, - {"tag":"OriginID", "type": "*composed", "value": "OriginID"}, - {"tag":"RequestType", "type": "*composed", "value": "RequestType"}, - {"tag":"Tenant", "type": "*composed", "value": "Tenant"}, - {"tag":"Category", "type": "*composed", "value": "Category"}, - {"tag":"Account", "type": "*composed", "value": "Account"}, - {"tag":"Destination", "type": "*composed", "value": "Destination"}, - {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag":"Usage", "type": "*composed", "value": "Usage"}, - {"tag":"Cost", "type": "*composed", "value": "Cost", "rounding_decimals": 4}, - {"tag":"MatchedDestinationID", "type": "*composed", "value": "~CostDetails:s/\"MatchedDestId\":.*_(\\w{4})/${1}/:s/\"MatchedDestId\":\"INTERNAL\"/ON010/"}, + {"id": "CGRID", "type": "*composed", "value": "~CGRID"}, + {"id": "RunID", "type": "*composed", "value": "~RunID"}, + {"id":"OriginID", "type": "*composed", "value": "~OriginID"}, + {"id":"RequestType", "type": "*composed", "value": "~RequestType"}, + {"id":"Tenant", "type": "*composed", "value": "~Tenant"}, + {"id":"Category", "type": "*composed", "value": "~Category"}, + {"id":"Account", "type": "*composed", "value": "~Account"}, + {"id":"Destination", "type": "*composed", "value": "~Destination"}, + {"id":"AnswerTime", "type": "*composed", "value": "~AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"id":"Usage", "type": "*composed", "value": "~Usage"}, + {"id":"Cost", "type": "*composed", "value": "~Cost", "rounding_decimals": 4}, + {"id":"MatchedDestinationID", "type": "*composed", "value": "~CostDetails:s/\"MatchedDestId\":.*_(\\w{4})/${1}/:s/\"MatchedDestId\":\"INTERNAL\"/ON010/"}, ], }, }, diff --git a/data/conf/samples/mongoreplica/cgrates.json b/data/conf/samples/mongoreplica/cgrates.json index 70774fdc4..86d482956 100755 --- a/data/conf/samples/mongoreplica/cgrates.json +++ b/data/conf/samples/mongoreplica/cgrates.json @@ -105,18 +105,18 @@ "cdre": { "TestTutITExportCDR": { "content_fields": [ - {"tag": "CGRID", "type": "*composed", "value": "CGRID"}, - {"tag": "RunID", "type": "*composed", "value": "RunID"}, - {"tag":"OriginID", "type": "*composed", "value": "OriginID"}, - {"tag":"RequestType", "type": "*composed", "value": "RequestType"}, - {"tag":"Tenant", "type": "*composed", "value": "Tenant"}, - {"tag":"Category", "type": "*composed", "value": "Category"}, - {"tag":"Account", "type": "*composed", "value": "Account"}, - {"tag":"Destination", "type": "*composed", "value": "Destination"}, - {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag":"Usage", "type": "*composed", "value": "Usage"}, - {"tag":"Cost", "type": "*composed", "value": "Cost", "rounding_decimals": 4}, - {"tag":"MatchedDestinationID", "type": "*composed", "value": "~CostDetails:s/\"MatchedDestId\":.*_(\\w{4})/${1}/:s/\"MatchedDestId\":\"INTERNAL\"/ON010/"}, + {"id": "CGRID", "type": "*composed", "value": "~CGRID"}, + {"id": "RunID", "type": "*composed", "value": "~RunID"}, + {"id":"OriginID", "type": "*composed", "value": "~OriginID"}, + {"id":"RequestType", "type": "*composed", "value": "~RequestType"}, + {"id":"Tenant", "type": "*composed", "value": "~Tenant"}, + {"id":"Category", "type": "*composed", "value": "~Category"}, + {"id":"Account", "type": "*composed", "value": "~Account"}, + {"id":"Destination", "type": "*composed", "value": "~Destination"}, + {"id":"AnswerTime", "type": "*composed", "value": "~AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"id":"Usage", "type": "*composed", "value": "~Usage"}, + {"id":"Cost", "type": "*composed", "value": "~Cost", "rounding_decimals": 4}, + {"id":"MatchedDestinationID", "type": "*composed", "value": "~CostDetails:s/\"MatchedDestId\":.*_(\\w{4})/${1}/:s/\"MatchedDestId\":\"INTERNAL\"/ON010/"}, ], }, }, diff --git a/data/conf/samples/tutmongo/cgrates.json b/data/conf/samples/tutmongo/cgrates.json index 5a7ad36c5..9123547ac 100644 --- a/data/conf/samples/tutmongo/cgrates.json +++ b/data/conf/samples/tutmongo/cgrates.json @@ -83,18 +83,18 @@ "cdre": { "TestTutITExportCDR": { "content_fields": [ - {"tag": "CGRID", "type": "*composed", "value": "CGRID"}, - {"tag": "RunID", "type": "*composed", "value": "RunID"}, - {"tag":"OriginID", "type": "*composed", "value": "OriginID"}, - {"tag":"RequestType", "type": "*composed", "value": "RequestType"}, - {"tag":"Tenant", "type": "*composed", "value": "Tenant"}, - {"tag":"Category", "type": "*composed", "value": "Category"}, - {"tag":"Account", "type": "*composed", "value": "Account"}, - {"tag":"Destination", "type": "*composed", "value": "Destination"}, - {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag":"Usage", "type": "*composed", "value": "Usage"}, - {"tag":"Cost", "type": "*composed", "value": "Cost", "rounding_decimals": 4}, - {"tag":"MatchedDestinationID", "type": "*composed", "value": "~CostDetails:s/\"MatchedDestId\":.*_(\\w{4})/${1}/:s/\"MatchedDestId\":\"INTERNAL\"/ON010/"}, + {"id": "CGRID", "type": "*composed", "value": "~CGRID"}, + {"id": "RunID", "type": "*composed", "value": "~RunID"}, + {"id":"OriginID", "type": "*composed", "value": "~OriginID"}, + {"id":"RequestType", "type": "*composed", "value": "~RequestType"}, + {"id":"Tenant", "type": "*composed", "value": "~Tenant"}, + {"id":"Category", "type": "*composed", "value": "~Category"}, + {"id":"Account", "type": "*composed", "value": "~Account"}, + {"id":"Destination", "type": "*composed", "value": "~Destination"}, + {"id":"AnswerTime", "type": "*composed", "value": "~AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"id":"Usage", "type": "*composed", "value": "~Usage"}, + {"id":"Cost", "type": "*composed", "value": "~Cost", "rounding_decimals": 4}, + {"id":"MatchedDestinationID", "type": "*composed", "value": "~CostDetails:s/\"MatchedDestId\":.*_(\\w{4})/${1}/:s/\"MatchedDestId\":\"INTERNAL\"/ON010/"}, ], }, }, diff --git a/data/conf/samples/tutmysql/cgrates.json b/data/conf/samples/tutmysql/cgrates.json index 96ba17479..3daa370fc 100644 --- a/data/conf/samples/tutmysql/cgrates.json +++ b/data/conf/samples/tutmysql/cgrates.json @@ -62,7 +62,7 @@ "rals": { "enabled": true, "thresholds_conns": [ - {"address": "*internal"} + {"address": "127.0.0.1:2012", "transport":"*json"}, ], }, @@ -81,20 +81,20 @@ "cdre": { -"TestTutITExportCDR": { + "TestTutITExportCDR": { "content_fields": [ - {"tag": "CGRID", "type": "*composed", "value": "CGRID"}, - {"tag": "RunID", "type": "*composed", "value": "RunID"}, - {"tag":"OriginID", "type": "*composed", "value": "OriginID"}, - {"tag":"RequestType", "type": "*composed", "value": "RequestType"}, - {"tag":"Tenant", "type": "*composed", "value": "Tenant"}, - {"tag":"Category", "type": "*composed", "value": "Category"}, - {"tag":"Account", "type": "*composed", "value": "Account"}, - {"tag":"Destination", "type": "*composed", "value": "Destination"}, - {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag":"Usage", "type": "*composed", "value": "Usage"}, - {"tag":"Cost", "type": "*composed", "value": "Cost", "rounding_decimals": 4}, - {"tag":"MatchedDestinationID", "type": "*composed", "value": "~CostDetails:s/\"MatchedDestId\":.*_(\\w{4})/${1}/:s/\"MatchedDestId\":\"INTERNAL\"/ON010/"}, + {"id": "CGRID", "type": "*composed", "value": "~CGRID"}, + {"id": "RunID", "type": "*composed", "value": "~RunID"}, + {"id":"OriginID", "type": "*composed", "value": "~OriginID"}, + {"id":"RequestType", "type": "*composed", "value": "~RequestType"}, + {"id":"Tenant", "type": "*composed", "value": "~Tenant"}, + {"id":"Category", "type": "*composed", "value": "~Category"}, + {"id":"Account", "type": "*composed", "value": "~Account"}, + {"id":"Destination", "type": "*composed", "value": "~Destination"}, + {"id":"AnswerTime", "type": "*composed", "value": "~AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"id":"Usage", "type": "*composed", "value": "~Usage"}, + {"id":"Cost", "type": "*composed", "value": "~Cost", "rounding_decimals": 4}, + {"id":"MatchedDestinationID", "type": "*composed", "value": "~CostDetails:s/\"MatchedDestId\":.*_(\\w{4})/${1}/:s/\"MatchedDestId\":\"INTERNAL\"/ON010/"}, ], }, }, diff --git a/engine/cdr.go b/engine/cdr.go index 085109514..9eb644b9b 100644 --- a/engine/cdr.go +++ b/engine/cdr.go @@ -154,7 +154,25 @@ func (cdr *CDR) FormatCost(shiftDecimals, roundDecimals int) string { } // Used to retrieve fields as string, primary fields are const labeled -func (cdr *CDR) FieldAsString(rsrFld *utils.RSRField) (parsed string, err error) { +func (cdr *CDR) FieldAsString(rsrPrs *config.RSRParser) (parsed string, err error) { + parsed, err = rsrPrs.ParseCDR(config.NewNavigableMap(cdr.AsMapStringIface())) + if err != nil { + return + } + return +} + +// concatenates values of multiple fields defined in template, used eg in CDR templates +func (cdr *CDR) FieldsAsString(rsrFlds config.RSRParsers) string { + outVal, err := rsrFlds.ParseCDR(config.NewNavigableMap(cdr.AsMapStringIface())) + if err != nil { + return "" + } + return outVal +} + +// Used to retrieve fields as string, primary fields are const labeled +func (cdr *CDR) FieldAsStringWithRSRField(rsrFld *utils.RSRField) (parsed string, err error) { if rsrFld.IsStatic() { // Static values do not care about headers parsed, err = rsrFld.Parse("") return @@ -167,9 +185,9 @@ func (cdr *CDR) FieldAsString(rsrFld *utils.RSRField) (parsed string, err error) } // concatenates values of multiple fields defined in template, used eg in CDR templates -func (cdr *CDR) FieldsAsString(rsrFlds utils.RSRFields) (fldVal string) { +func (cdr *CDR) FieldsAsStringWithRSRFields(rsrFlds utils.RSRFields) (fldVal string) { for _, rsrFld := range rsrFlds { - if fldStr, err := cdr.FieldAsString(rsrFld); err != nil { + if fldStr, err := cdr.FieldAsStringWithRSRField(rsrFld); err != nil { utils.Logger.Warning( fmt.Sprintf("<%s> error: %s processing field with template: %+v", utils.CDR, err.Error(), rsrFld)) @@ -348,55 +366,55 @@ func (cdr *CDR) ForkCdr(runId string, RequestTypeFld, tenantFld, frkStorCdr.OriginID = cdr.OriginID frkStorCdr.OriginHost = cdr.OriginHost frkStorCdr.Source = cdr.Source - frkStorCdr.RequestType, _ = cdr.FieldAsString(RequestTypeFld) + frkStorCdr.RequestType, _ = cdr.FieldAsStringWithRSRField(RequestTypeFld) if primaryMandatory && len(frkStorCdr.RequestType) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.RequestType, RequestTypeFld.Id) } - frkStorCdr.Tenant, _ = cdr.FieldAsString(tenantFld) + frkStorCdr.Tenant, _ = cdr.FieldAsStringWithRSRField(tenantFld) if primaryMandatory && len(frkStorCdr.Tenant) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.Tenant, tenantFld.Id) } - frkStorCdr.Category, _ = cdr.FieldAsString(categFld) + frkStorCdr.Category, _ = cdr.FieldAsStringWithRSRField(categFld) if primaryMandatory && len(frkStorCdr.Category) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.Category, categFld.Id) } - frkStorCdr.Account, _ = cdr.FieldAsString(accountFld) + frkStorCdr.Account, _ = cdr.FieldAsStringWithRSRField(accountFld) if primaryMandatory && len(frkStorCdr.Account) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.Account, accountFld.Id) } - frkStorCdr.Subject, _ = cdr.FieldAsString(subjectFld) + frkStorCdr.Subject, _ = cdr.FieldAsStringWithRSRField(subjectFld) if primaryMandatory && len(frkStorCdr.Subject) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.Subject, subjectFld.Id) } - frkStorCdr.Destination, _ = cdr.FieldAsString(destFld) + frkStorCdr.Destination, _ = cdr.FieldAsStringWithRSRField(destFld) if primaryMandatory && len(frkStorCdr.Destination) == 0 && frkStorCdr.ToR == utils.VOICE { return nil, utils.NewErrMandatoryIeMissing(utils.Destination, destFld.Id) } - sTimeStr, _ := cdr.FieldAsString(setupTimeFld) + sTimeStr, _ := cdr.FieldAsStringWithRSRField(setupTimeFld) if primaryMandatory && len(sTimeStr) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.SetupTime, setupTimeFld.Id) } else if frkStorCdr.SetupTime, err = utils.ParseTimeDetectLayout(sTimeStr, timezone); err != nil { return nil, err } - aTimeStr, _ := cdr.FieldAsString(answerTimeFld) + aTimeStr, _ := cdr.FieldAsStringWithRSRField(answerTimeFld) if primaryMandatory && len(aTimeStr) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.AnswerTime, answerTimeFld.Id) } else if frkStorCdr.AnswerTime, err = utils.ParseTimeDetectLayout(aTimeStr, timezone); err != nil { return nil, err } - durStr, _ := cdr.FieldAsString(durationFld) + durStr, _ := cdr.FieldAsStringWithRSRField(durationFld) if primaryMandatory && len(durStr) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.Usage, durationFld.Id) } else if frkStorCdr.Usage, err = utils.ParseDurationWithNanosecs(durStr); err != nil { return nil, err } - ratedStr, _ := cdr.FieldAsString(ratedFld) + ratedStr, _ := cdr.FieldAsStringWithRSRField(ratedFld) if primaryMandatory && len(ratedStr) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.PreRated, ratedFld.Id) } else if frkStorCdr.PreRated, err = strconv.ParseBool(ratedStr); err != nil { return nil, err } - costStr, _ := cdr.FieldAsString(costFld) + costStr, _ := cdr.FieldAsStringWithRSRField(costFld) if primaryMandatory && len(costStr) == 0 { return nil, utils.NewErrMandatoryIeMissing(utils.COST, costFld.Id) } else if frkStorCdr.Cost, err = strconv.ParseFloat(costStr, 64); err != nil { @@ -404,7 +422,7 @@ func (cdr *CDR) ForkCdr(runId string, RequestTypeFld, tenantFld, } frkStorCdr.ExtraFields = make(map[string]string, len(extraFlds)) for _, fld := range extraFlds { - frkStorCdr.ExtraFields[fld.Id], _ = cdr.FieldAsString(fld) + frkStorCdr.ExtraFields[fld.Id], _ = cdr.FieldAsStringWithRSRField(fld) } return frkStorCdr, nil } @@ -448,43 +466,47 @@ func (cdr *CDR) String() string { } // combimedCdrFieldVal groups together CDRs with same CGRID and combines their values matching filter field ID -func (cdr *CDR) combimedCdrFieldVal(cfgCdrFld *config.CfgCdrField, groupCDRs []*CDR) (string, error) { - var combinedVal string // Will result as combination of the field values, filters must match - for _, filterRule := range cfgCdrFld.FieldFilter { - pairingVal, err := cdr.FieldAsString(&utils.RSRField{Id: filterRule.Id}) - if err != nil { // Filter not passing - continue - } - for _, grpCDR := range groupCDRs { - if cdr.CGRID != grpCDR.CGRID { - continue // We only care about cdrs with same primary cdr behind - } - if valStr, _ := grpCDR.FieldAsString(&utils.RSRField{Id: filterRule.Id}); valStr != pairingVal { // First CDR with field equal with ours +func (cdr *CDR) combimedCdrFieldVal(cfgCdrFld *config.FCTemplate, groupCDRs []*CDR, filterS *FilterS) (string, error) { + /* + var combinedVal string // Will result as combination of the field values, filters must match + for _, filterRule := range cfgCdrFld.Value { + pairingVal, err := cdr.FieldAsString(filterRule) + if err != nil { // Filter not passing continue } - for _, rsrRule := range cfgCdrFld.Value { - if parsed, err := grpCDR.FieldAsString(rsrRule); err != nil { - return "", err - } else { - combinedVal += parsed + for _, grpCDR := range groupCDRs { + if cdr.CGRID != grpCDR.CGRID { + continue // We only care about cdrs with same primary cdr behind } + if valStr, _ := grpCDR.FieldAsString(filterRule); valStr != pairingVal { // First CDR with field equal with ours + continue + } + for _, rsrRule := range cfgCdrFld.Value { + if parsed, err := grpCDR.FieldAsString(rsrRule); err != nil { + return "", err + } else { + combinedVal += parsed + } + } } } - } - return combinedVal, nil + return combinedVal, nil + */ + return "", nil } // Extracts the value specified by cfgHdr out of cdr, used for export values -func (cdr *CDR) exportFieldValue(cfgCdrFld *config.CfgCdrField) (retVal string, err error) { - for _, cdfFltr := range cfgCdrFld.FieldFilter { - if _, err := cdr.FieldAsString(cdfFltr); err != nil { - return "", err - } +func (cdr *CDR) exportFieldValue(cfgCdrFld *config.FCTemplate, filterS *FilterS) (retVal string, err error) { + if pass, err := filterS.Pass(cdr.Tenant, + cfgCdrFld.Filters, config.NewNavigableMap(cdr.AsMapStringIface())); err != nil { + return "", err + } else if !pass { + return "", utils.ErrFilterNotPassingNoCaps } for _, rsrFld := range cfgCdrFld.Value { var cdrVal string - switch rsrFld.Id { + switch cfgCdrFld.ID { case utils.COST: cdrVal = cdr.FormatCost(cfgCdrFld.CostShiftDigits, cfgCdrFld.RoundingDecimals) @@ -512,21 +534,20 @@ func (cdr *CDR) exportFieldValue(cfgCdrFld *config.CfgCdrField) (retVal string, return } -func (cdr *CDR) formatField(cfgFld *config.CfgCdrField, httpSkipTlsCheck bool, - groupedCDRs []*CDR) (fmtOut string, err error) { +func (cdr *CDR) formatField(cfgFld *config.FCTemplate, httpSkipTlsCheck bool, + groupedCDRs []*CDR, filterS *FilterS) (outVal string, err error) { layout := cfgFld.Layout if layout == "" { layout = time.RFC3339 } - var outVal string switch cfgFld.Type { case utils.META_FILLER: - outVal = cfgFld.Value.Id() + outVal, err = cfgFld.Value.ParseValue(utils.EmptyString) cfgFld.Padding = "right" case utils.META_CONSTANT: - outVal = cfgFld.Value.Id() + outVal, err = cfgFld.Value.ParseValue(utils.EmptyString) case utils.MetaDateTime: // Convert the requested field value into datetime with layout - rawVal, err := cdr.exportFieldValue(cfgFld) + rawVal, err := cdr.exportFieldValue(cfgFld, filterS) if err != nil { return "", err } @@ -537,23 +558,26 @@ func (cdr *CDR) formatField(cfgFld *config.CfgCdrField, httpSkipTlsCheck bool, } case utils.META_HTTP_POST: var outValByte []byte - httpAddr := cfgFld.Value.Id() + httpAddr, err := cfgFld.Value.ParseValue(utils.EmptyString) + if err != nil { + return "", err + } jsn, err := json.Marshal(cdr) if err != nil { return "", err } if len(httpAddr) == 0 { - err = fmt.Errorf("Empty http address for field %s type %s", cfgFld.Tag, cfgFld.Type) + err = fmt.Errorf("Empty http address for field %s type %s", cfgFld.ID, cfgFld.Type) } else if outValByte, err = HttpJsonPost(httpAddr, httpSkipTlsCheck, jsn); err == nil { outVal = string(outValByte) if len(outVal) == 0 && cfgFld.Mandatory { - err = fmt.Errorf("Empty result for http_post field: %s", cfgFld.Tag) + err = fmt.Errorf("Empty result for http_post field: %s", cfgFld.ID) } } case utils.META_COMBIMED: - outVal, err = cdr.combimedCdrFieldVal(cfgFld, groupedCDRs) + outVal, err = cdr.combimedCdrFieldVal(cfgFld, groupedCDRs, filterS) case utils.META_COMPOSED: - outVal, err = cdr.exportFieldValue(cfgFld) + outVal, err = cdr.exportFieldValue(cfgFld, filterS) case utils.MetaMaskedDestination: if len(cfgFld.MaskDestID) != 0 && CachedDestHasPrefix(cfgFld.MaskDestID, cdr.Destination) { outVal = "1" @@ -565,22 +589,21 @@ func (cdr *CDR) formatField(cfgFld *config.CfgCdrField, httpSkipTlsCheck bool, (err != utils.ErrNotFound || cfgFld.Mandatory) { return "", err } - return utils.FmtFieldWidth(cfgFld.Tag, outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory) - + return utils.FmtFieldWidth(cfgFld.ID, outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory) } // Used in place where we need to export the CDR based on an export template // ExportRecord is a []string to keep it compatible with encoding/csv Writer -func (cdr *CDR) AsExportRecord(exportFields []*config.CfgCdrField, - httpSkipTlsCheck bool, groupedCDRs []*CDR, roundingDecs int) (expRecord []string, err error) { +func (cdr *CDR) AsExportRecord(exportFields []*config.FCTemplate, + httpSkipTlsCheck bool, groupedCDRs []*CDR, roundingDecs int, filterS *FilterS) (expRecord []string, err error) { for _, cfgFld := range exportFields { if roundingDecs != 0 { - clnFld := new(config.CfgCdrField) // Clone so we can modify the rounding decimals without affecting the template + clnFld := new(config.FCTemplate) // Clone so we can modify the rounding decimals without affecting the template *clnFld = *cfgFld clnFld.RoundingDecimals = roundingDecs cfgFld = clnFld } - if fmtOut, err := cdr.formatField(cfgFld, httpSkipTlsCheck, groupedCDRs); err != nil { + if fmtOut, err := cdr.formatField(cfgFld, httpSkipTlsCheck, groupedCDRs, filterS); err != nil { if err == utils.ErrFilterNotPassingNoCaps { continue // not exporting this field value } @@ -596,16 +619,17 @@ func (cdr *CDR) AsExportRecord(exportFields []*config.CfgCdrField, // AsExportMap converts the CDR into a map[string]string based on export template // Used in real-time replication as well as remote exports -func (cdr *CDR) AsExportMap(exportFields []*config.CfgCdrField, httpSkipTlsCheck bool, groupedCDRs []*CDR, roundingDecs int) (expMap map[string]string, err error) { +func (cdr *CDR) AsExportMap(exportFields []*config.FCTemplate, httpSkipTlsCheck bool, + groupedCDRs []*CDR, roundingDecs int, filterS *FilterS) (expMap map[string]string, err error) { expMap = make(map[string]string) for _, cfgFld := range exportFields { if roundingDecs != 0 { - clnFld := new(config.CfgCdrField) // Clone so we can modify the rounding decimals without affecting the template + clnFld := new(config.FCTemplate) // Clone so we can modify the rounding decimals without affecting the template *clnFld = *cfgFld clnFld.RoundingDecimals = roundingDecs cfgFld = clnFld } - if fmtOut, err := cdr.formatField(cfgFld, httpSkipTlsCheck, groupedCDRs); err != nil { + if fmtOut, err := cdr.formatField(cfgFld, httpSkipTlsCheck, groupedCDRs, filterS); err != nil { if err == utils.ErrFilterNotPassingNoCaps { continue } diff --git a/engine/cdr_test.go b/engine/cdr_test.go index b2bfb4fc9..a879d7c1b 100644 --- a/engine/cdr_test.go +++ b/engine/cdr_test.go @@ -80,134 +80,134 @@ func TestFieldAsString(t *testing.T) { Usage: time.Duration(10) * time.Second, Cost: 1.01, ExtraFields: map[string]string{"field_extr1": "val_extr1", "fieldextr2": "valextr2"}, } - fldName := utils.CGRID + prsr := config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.CGRID, true) eFldVal := cdr.CGRID - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.OrderID + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.OrderID, true) eFldVal = strconv.FormatInt(cdr.OrderID, 10) - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.ToR + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.ToR, true) eFldVal = cdr.ToR - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.OriginID + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.OriginID, true) eFldVal = cdr.OriginID - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.OriginHost + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.OriginHost, true) eFldVal = cdr.OriginHost - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.Source + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Source, true) eFldVal = cdr.Source - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.RequestType + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.RequestType, true) eFldVal = cdr.RequestType - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.Category + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Category, true) eFldVal = cdr.Category - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.Account + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Account, true) eFldVal = cdr.Account - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.Subject + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Subject, true) eFldVal = cdr.Subject - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.Destination + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Destination, true) eFldVal = cdr.Destination - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.SetupTime + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.SetupTime, true) eFldVal = cdr.SetupTime.Format(time.RFC3339) - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("expected: <%s>, received: <%s>", eFldVal, fldVal) } - fldName = utils.AnswerTime + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.AnswerTime, true) eFldVal = cdr.AnswerTime.Format(time.RFC3339) - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("expected: <%s>, received: <%s>", eFldVal, fldVal) } - fldName = utils.Usage + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Usage, true) eFldVal = "10s" - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.RunID + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.RunID, true) eFldVal = cdr.RunID - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = utils.Cost + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+utils.Cost, true) eFldVal = strconv.FormatFloat(cdr.Cost, 'f', -1, 64) - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = "field_extr1" + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+"field_extr1", true) eFldVal = cdr.ExtraFields["field_extr1"] - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = "fieldextr2" + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+"fieldextr2", true) eFldVal = cdr.ExtraFields["fieldextr2"] - if fldVal, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != nil { + if fldVal, err := cdr.FieldAsString(prsr); err != nil { t.Error(err) } else if fldVal != eFldVal { - t.Errorf("field: <%s>, expected: <%s>, received: <%s>", fldName, eFldVal, fldVal) + t.Errorf("field: <%s>, expected: <%s>, received: <%s>", prsr, eFldVal, fldVal) } - fldName = "dummy_field" - if _, err := cdr.FieldAsString(&utils.RSRField{Id: fldName}); err != utils.ErrNotFound { + prsr = config.NewRSRParserMustCompile(utils.DynamicDataPrefix+"dummy_field", true) + if _, err := cdr.FieldAsString(prsr); err != utils.ErrNotFound { t.Error(err) } } @@ -224,7 +224,7 @@ func TestFieldsAsString(t *testing.T) { } eVal := "call_from_1001" if val := cdr.FieldsAsString( - utils.ParseRSRFieldsMustCompile("Category;^_from_;Account", utils.INFIELD_SEP)); val != eVal { + config.NewRSRParsersMustCompile("~Category;_from_;~Account", true)); val != eVal { t.Errorf("Expecting : %s, received: %s", eVal, val) } } @@ -334,7 +334,7 @@ func TestCDRAsHttpForm(t *testing.T) { t.Errorf("Expected: %s, received: %s", "valextr2", cdrForm.Get("fieldextr2")) } } -*/ + func TestCDRForkCdr(t *testing.T) { storCdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC).String()), @@ -452,6 +452,7 @@ func TestCDRForkCdrFromMetaDefaults(t *testing.T) { t.Errorf("Expected: %v, received: %v", expctCdr, cdrOut) } } +*/ func TestCDRAsExternalCDR(t *testing.T) { storCdr := CDR{CGRID: utils.Sha1("dsafdsaf", time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC).String()), @@ -606,59 +607,76 @@ func TestCDRAsExportRecord(t *testing.T) { RunID: utils.DEFAULT_RUNID, Cost: 1.01, ExtraFields: map[string]string{"stop_time": "2014-06-11 19:19:00 +0000 UTC", "fieldextr2": "valextr2"}} - val, _ := utils.ParseRSRFields(utils.Destination, utils.INFIELD_SEP) - cfgCdrFld := &config.CfgCdrField{Tag: "destination", Type: utils.META_COMPOSED, FieldId: utils.Destination, Value: val, Timezone: "UTC"} - if expRecord, err := cdr.AsExportRecord([]*config.CfgCdrField{cfgCdrFld}, false, nil, 0); err != nil { + prsr := config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.Destination, true) + cfgCdrFld := &config.FCTemplate{ID: "destination", Type: utils.META_COMPOSED, + FieldId: utils.Destination, Value: prsr, Timezone: "UTC"} + if expRecord, err := cdr.AsExportRecord([]*config.FCTemplate{cfgCdrFld}, false, nil, 0, nil); err != nil { t.Error(err) } else if expRecord[0] != cdr.Destination { - t.Errorf("Expecting:\n%s\nReceived:\n%s", cdr.Destination, expRecord[0]) + t.Errorf("Expecting:\n%s\nReceived:\n%s", cdr.Destination, expRecord) } if err := dm.DataDB().SetReverseDestination(&Destination{Id: "MASKED_DESTINATIONS", Prefixes: []string{"+4986517174963"}}, utils.NonTransactional); err != nil { t.Error(err) } - cfgCdrFld = &config.CfgCdrField{Tag: "destination", Type: utils.META_COMPOSED, FieldId: utils.Destination, Value: val, MaskDestID: "MASKED_DESTINATIONS", MaskLen: 3} + + cfgCdrFld = &config.FCTemplate{ID: "Destination", Type: utils.META_COMPOSED, + FieldId: utils.Destination, Value: prsr, MaskDestID: "MASKED_DESTINATIONS", MaskLen: 3} eDst := "+4986517174***" - if expRecord, err := cdr.AsExportRecord([]*config.CfgCdrField{cfgCdrFld}, false, nil, 0); err != nil { + if expRecord, err := cdr.AsExportRecord([]*config.FCTemplate{cfgCdrFld}, false, nil, 0, nil); err != nil { t.Error(err) } else if expRecord[0] != eDst { t.Errorf("Expecting:\n%s\nReceived:\n%s", eDst, expRecord[0]) } - cfgCdrFld = &config.CfgCdrField{Tag: "MaskedDest", Type: utils.MetaMaskedDestination, Value: val, MaskDestID: "MASKED_DESTINATIONS"} - if expRecord, err := cdr.AsExportRecord([]*config.CfgCdrField{cfgCdrFld}, false, nil, 0); err != nil { + + cfgCdrFld = &config.FCTemplate{ID: "MaskedDest", Type: utils.MetaMaskedDestination, + Value: prsr, MaskDestID: "MASKED_DESTINATIONS"} + if expRecord, err := cdr.AsExportRecord([]*config.FCTemplate{cfgCdrFld}, false, nil, 0, nil); err != nil { t.Error(err) } else if expRecord[0] != "1" { t.Errorf("Expecting:\n%s\nReceived:\n%s", "1", expRecord[0]) } - fltr, _ := utils.ParseRSRFields("Tenant(itsyscom.com)", utils.INFIELD_SEP) - cfgCdrFld = &config.CfgCdrField{Tag: "destination", Type: utils.META_COMPOSED, FieldId: utils.Destination, Value: val, FieldFilter: fltr, Timezone: "UTC"} - if rcrd, err := cdr.AsExportRecord([]*config.CfgCdrField{cfgCdrFld}, false, nil, 0); err != nil { + data, _ := NewMapStorage() + dmForCDR := NewDataManager(data) + defaultCfg, err := config.NewDefaultCGRConfig() + if err != nil { + t.Errorf("Error: %+v", err) + } + + cfgCdrFld = &config.FCTemplate{ID: "destination", Type: utils.META_COMPOSED, + FieldId: utils.Destination, Value: prsr, Filters: []string{"*string:Tenant:itsyscom.com"}, Timezone: "UTC"} + if rcrd, err := cdr.AsExportRecord([]*config.FCTemplate{cfgCdrFld}, false, nil, 0, &FilterS{dm: dmForCDR, cfg: defaultCfg}); err != nil { t.Error(err) } else if len(rcrd) != 0 { t.Error("failed using filter") } + // Test MetaDateTime - val, _ = utils.ParseRSRFields("stop_time", utils.INFIELD_SEP) + prsr = config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"stop_time", true) layout := "2006-01-02 15:04:05" - cfgCdrFld = &config.CfgCdrField{Tag: "stop_time", Type: utils.MetaDateTime, FieldId: "stop_time", Value: val, Layout: layout, Timezone: "UTC"} - if expRecord, err := cdr.AsExportRecord([]*config.CfgCdrField{cfgCdrFld}, false, nil, 0); err != nil { + cfgCdrFld = &config.FCTemplate{ID: "stop_time", Type: utils.MetaDateTime, + FieldId: "stop_time", Value: prsr, Layout: layout, Timezone: "UTC"} + if expRecord, err := cdr.AsExportRecord([]*config.FCTemplate{cfgCdrFld}, false, nil, 0, &FilterS{dm: dmForCDR, cfg: defaultCfg}); err != nil { t.Error(err) } else if expRecord[0] != "2014-06-11 19:19:00" { t.Error("Expecting: 2014-06-11 19:19:00, got: ", expRecord[0]) } + // Test filter - fltr, _ = utils.ParseRSRFields("Tenant(itsyscom.com)", utils.INFIELD_SEP) - cfgCdrFld = &config.CfgCdrField{Tag: "stop_time", Type: utils.MetaDateTime, - FieldId: "stop_time", Value: val, FieldFilter: fltr, Layout: layout, Timezone: "UTC"} - if rcrd, err := cdr.AsExportRecord([]*config.CfgCdrField{cfgCdrFld}, false, nil, 0); err != nil { + cfgCdrFld = &config.FCTemplate{ID: "stop_time", Type: utils.MetaDateTime, + FieldId: "stop_time", Value: prsr, Filters: []string{"*string:Tenant:itsyscom.com"}, + Layout: layout, Timezone: "UTC"} + if rcrd, err := cdr.AsExportRecord([]*config.FCTemplate{cfgCdrFld}, false, nil, 0, &FilterS{dm: dmForCDR, cfg: defaultCfg}); err != nil { t.Error(err) } else if len(rcrd) != 0 { t.Error("failed using filter") } - val, _ = utils.ParseRSRFields("fieldextr2", utils.INFIELD_SEP) - cfgCdrFld = &config.CfgCdrField{Tag: "stop_time", Type: utils.MetaDateTime, FieldId: "stop_time", Value: val, Layout: layout, Timezone: "UTC"} + + prsr = config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"fieldextr2", true) + cfgCdrFld = &config.FCTemplate{ID: "stop_time", Type: utils.MetaDateTime, + FieldId: "stop_time", Value: prsr, Layout: layout, Timezone: "UTC"} // Test time parse error - if _, err := cdr.AsExportRecord([]*config.CfgCdrField{cfgCdrFld}, false, nil, 0); err == nil { + if _, err := cdr.AsExportRecord([]*config.FCTemplate{cfgCdrFld}, false, nil, 0, nil); err == nil { t.Error("Should give error here, got none.") } } @@ -679,12 +697,15 @@ func TestCDRAsExportMap(t *testing.T) { utils.Destination: "004986517174963", "FieldExtra1": "val_extr1", } - expFlds := []*config.CfgCdrField{ - &config.CfgCdrField{FieldId: utils.CGRID, Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile(utils.CGRID, utils.INFIELD_SEP)}, - &config.CfgCdrField{FieldId: utils.Destination, Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("~Destination:s/^\\+(\\d+)$/00${1}/", utils.INFIELD_SEP)}, - &config.CfgCdrField{FieldId: "FieldExtra1", Type: utils.META_COMPOSED, Value: utils.ParseRSRFieldsMustCompile("field_extr1", utils.INFIELD_SEP)}, + expFlds := []*config.FCTemplate{ + &config.FCTemplate{FieldId: utils.CGRID, Type: utils.META_COMPOSED, + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+utils.CGRID, true)}, + &config.FCTemplate{FieldId: utils.Destination, Type: utils.META_COMPOSED, + Value: config.NewRSRParsersMustCompile("~Destination:s/^\\+(\\d+)$/00${1}/", true)}, + &config.FCTemplate{FieldId: "FieldExtra1", Type: utils.META_COMPOSED, + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"field_extr1", true)}, } - if cdrMp, err := cdr.AsExportMap(expFlds, false, nil, 0); err != nil { + if cdrMp, err := cdr.AsExportMap(expFlds, false, nil, 0, nil); err != nil { t.Error(err) } else if !reflect.DeepEqual(eCDRMp, cdrMp) { t.Errorf("Expecting: %+v, received: %+v", eCDRMp, cdrMp) diff --git a/engine/cdre.go b/engine/cdre.go index b625942c9..f2f96684e 100644 --- a/engine/cdre.go +++ b/engine/cdre.go @@ -124,19 +124,19 @@ func (cdre *CDRExporter) metaHandler(tag, arg string) (string, error) { return strconv.Itoa(cdre.numberOfRecords), nil case META_DURCDRS: cdr := &CDR{ToR: utils.VOICE, Usage: cdre.totalDuration} - return cdr.FieldAsString(&utils.RSRField{Id: utils.Usage}) + return cdr.FieldAsString(&config.RSRParser{Rules: "~" + utils.Usage, AllFiltersMatch: true}) case META_SMSUSAGE: cdr := &CDR{ToR: utils.SMS, Usage: cdre.totalDuration} - return cdr.FieldAsString(&utils.RSRField{Id: utils.Usage}) + return cdr.FieldAsString(&config.RSRParser{Rules: "~" + utils.Usage, AllFiltersMatch: true}) case META_MMSUSAGE: cdr := &CDR{ToR: utils.MMS, Usage: cdre.totalDuration} - return cdr.FieldAsString(&utils.RSRField{Id: utils.Usage}) + return cdr.FieldAsString(&config.RSRParser{Rules: "~" + utils.Usage, AllFiltersMatch: true}) case META_GENERICUSAGE: cdr := &CDR{ToR: utils.GENERIC, Usage: cdre.totalDuration} - return cdr.FieldAsString(&utils.RSRField{Id: utils.Usage}) + return cdr.FieldAsString(&config.RSRParser{Rules: "~" + utils.Usage, AllFiltersMatch: true}) case META_DATAUSAGE: cdr := &CDR{ToR: utils.DATA, Usage: cdre.totalDuration} - return cdr.FieldAsString(&utils.RSRField{Id: utils.Usage}) + return cdr.FieldAsString(&config.RSRParser{Rules: "~" + utils.Usage, AllFiltersMatch: true}) case META_COSTCDRS: return strconv.FormatFloat(utils.Round(cdre.totalCost, cdre.roundingDecimals, utils.ROUNDING_MIDDLE), 'f', -1, 64), nil @@ -148,25 +148,40 @@ func (cdre *CDRExporter) metaHandler(tag, arg string) (string, error) { // Compose and cache the header func (cdre *CDRExporter) composeHeader() (err error) { for _, cfgFld := range cdre.exportTemplate.HeaderFields { + if len(cfgFld.Filters) != 0 { + //check filter if pass + } var outVal string switch cfgFld.Type { case utils.META_FILLER: - outVal = cfgFld.Value.Id() + out, err := cfgFld.Value.ParseValue(utils.EmptyString) + if err != nil { + return err + } + outVal = out cfgFld.Padding = "right" case utils.META_CONSTANT: - outVal = cfgFld.Value.Id() + out, err := cfgFld.Value.ParseValue(utils.EmptyString) + if err != nil { + return err + } + outVal = out case utils.META_HANDLER: - outVal, err = cdre.metaHandler(cfgFld.Value.Id(), cfgFld.Layout) + out, err := cfgFld.Value.ParseValue(utils.EmptyString) + if err != nil { + return err + } + outVal, err = cdre.metaHandler(out, cfgFld.Layout) default: return fmt.Errorf("Unsupported field type: %s", cfgFld.Type) } if err != nil { - utils.Logger.Err(fmt.Sprintf(" Cannot export CDR header, field %s, error: %s", cfgFld.Tag, err.Error())) + utils.Logger.Err(fmt.Sprintf(" Cannot export CDR header, field %s, error: %s", cfgFld.ID, err.Error())) return err } fmtOut := outVal - if fmtOut, err = utils.FmtFieldWidth(cfgFld.Tag, outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { - utils.Logger.Err(fmt.Sprintf(" Cannot export CDR header, field %s, error: %s", cfgFld.Tag, err.Error())) + if fmtOut, err = utils.FmtFieldWidth(cfgFld.ID, outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { + utils.Logger.Err(fmt.Sprintf(" Cannot export CDR header, field %s, error: %s", cfgFld.ID, err.Error())) return err } cdre.Lock() @@ -179,25 +194,40 @@ func (cdre *CDRExporter) composeHeader() (err error) { // Compose and cache the trailer func (cdre *CDRExporter) composeTrailer() (err error) { for _, cfgFld := range cdre.exportTemplate.TrailerFields { + if len(cfgFld.Filters) != 0 { + //check filter if pass + } var outVal string switch cfgFld.Type { case utils.META_FILLER: - outVal = cfgFld.Value.Id() + out, err := cfgFld.Value.ParseValue(utils.EmptyString) + if err != nil { + return err + } + outVal = out cfgFld.Padding = "right" case utils.META_CONSTANT: - outVal = cfgFld.Value.Id() + out, err := cfgFld.Value.ParseValue(utils.EmptyString) + if err != nil { + return err + } + outVal = out case utils.META_HANDLER: - outVal, err = cdre.metaHandler(cfgFld.Value.Id(), cfgFld.Layout) + out, err := cfgFld.Value.ParseValue(utils.EmptyString) + if err != nil { + return err + } + outVal, err = cdre.metaHandler(out, cfgFld.Layout) default: return fmt.Errorf("Unsupported field type: %s", cfgFld.Type) } if err != nil { - utils.Logger.Err(fmt.Sprintf(" Cannot export CDR trailer, field: %s, error: %s", cfgFld.Tag, err.Error())) + utils.Logger.Err(fmt.Sprintf(" Cannot export CDR trailer, field: %s, error: %s", cfgFld.ID, err.Error())) return err } fmtOut := outVal - if fmtOut, err = utils.FmtFieldWidth(cfgFld.Tag, outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { - utils.Logger.Err(fmt.Sprintf(" Cannot export CDR trailer, field: %s, error: %s", cfgFld.Tag, err.Error())) + if fmtOut, err = utils.FmtFieldWidth(cfgFld.ID, outVal, cfgFld.Width, cfgFld.Strip, cfgFld.Padding, cfgFld.Mandatory); err != nil { + utils.Logger.Err(fmt.Sprintf(" Cannot export CDR trailer, field: %s, error: %s", cfgFld.ID, err.Error())) return err } cdre.Lock() @@ -217,7 +247,7 @@ func (cdre *CDRExporter) postCdr(cdr *CDR) (err error) { } body = jsn case utils.MetaHTTPjsonMap, utils.MetaAMQPjsonMap: - expMp, err := cdr.AsExportMap(cdre.exportTemplate.ContentFields, cdre.httpSkipTlsCheck, nil, cdre.roundingDecimals) + expMp, err := cdr.AsExportMap(cdre.exportTemplate.ContentFields, cdre.httpSkipTlsCheck, nil, cdre.roundingDecimals, cdre.filterS) if err != nil { return err } @@ -227,7 +257,7 @@ func (cdre *CDRExporter) postCdr(cdr *CDR) (err error) { } body = jsn case utils.META_HTTP_POST: - expMp, err := cdr.AsExportMap(cdre.exportTemplate.ContentFields, cdre.httpSkipTlsCheck, nil, cdre.roundingDecimals) + expMp, err := cdr.AsExportMap(cdre.exportTemplate.ContentFields, cdre.httpSkipTlsCheck, nil, cdre.roundingDecimals, cdre.filterS) if err != nil { return err } @@ -285,7 +315,7 @@ func (cdre *CDRExporter) processCDR(cdr *CDR) (err error) { switch cdre.exportFormat { case utils.MetaFileFWV, utils.MetaFileCSV: var cdrRow []string - cdrRow, err = cdr.AsExportRecord(cdre.exportTemplate.ContentFields, cdre.httpSkipTlsCheck, cdre.cdrs, cdre.roundingDecimals) + cdrRow, err = cdr.AsExportRecord(cdre.exportTemplate.ContentFields, cdre.httpSkipTlsCheck, cdre.cdrs, cdre.roundingDecimals, cdre.filterS) if len(cdrRow) == 0 && err == nil { // No CDR data, most likely no configuration fields defined return } else { @@ -343,18 +373,7 @@ func (cdre *CDRExporter) processCDRs() (err error) { if cdr == nil || len(cdr.CGRID) == 0 { // CDR needs to exist and it's CGRID needs to be populated continue } - if len(cdre.exportTemplate.Filters) == 0 { - passesFilters := true - for _, cdrFltr := range cdre.exportTemplate.CDRFilter { - if _, err := cdr.FieldAsString(cdrFltr); err != nil { - passesFilters = false - break - } - } - if !passesFilters { // Not passes filters, ignore this CDR - continue - } - } else { + if len(cdre.exportTemplate.Filters) != 0 { if pass, err := cdre.filterS.Pass(cdre.exportTemplate.Tenant, cdre.exportTemplate.Filters, config.NewNavigableMap(cdr.AsMapStringIface())); err != nil || !pass { continue // Not passes filters, ignore this CDR diff --git a/engine/cdrecsv_test.go b/engine/cdrecsv_test.go index 7756dd4b5..4fea2f3e9 100644 --- a/engine/cdrecsv_test.go +++ b/engine/cdrecsv_test.go @@ -61,7 +61,7 @@ func TestCsvCdrWriter(t *testing.T) { expected := `dbafe9c8614c785a65aabd116dd3959c3c56f7f6,*default,*voice,dsafdsaf,*rated,cgrates.org,call,1001,1001,1002,2013-11-07T08:42:25Z,2013-11-07T08:42:26Z,10s,1.01000` result := strings.TrimSpace(writer.String()) if result != expected { - t.Errorf("Expected: \n%s received: \n%s.", expected, result) + t.Errorf("Expected: \n%s \n received: \n%s.", expected, result) } if cdre.TotalCost() != 1.01 { t.Error("Unexpected TotalCost: ", cdre.TotalCost()) @@ -111,35 +111,35 @@ func TestExportVoiceWithConvert(t *testing.T) { writer := &bytes.Buffer{} cfg, _ := config.NewDefaultCGRConfig() cdreCfg := cfg.CdreProfiles["*default"] - cdreCfg.ContentFields = []*config.CfgCdrField{ - &config.CfgCdrField{Tag: "ToR", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("ToR", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "OriginID", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("OriginID", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "RequestType", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("RequestType", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Tenant", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Tenant", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Category", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Category", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Account", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Account", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Destination", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Destination", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "AnswerTime", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("AnswerTime", utils.INFIELD_SEP), + cdreCfg.ContentFields = []*config.FCTemplate{ + &config.FCTemplate{ID: "ToR", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"ToR", true)}, + &config.FCTemplate{ID: "OriginID", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"OriginID", true)}, + &config.FCTemplate{ID: "RequestType", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"RequestType", true)}, + &config.FCTemplate{ID: "Tenant", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Tenant", true)}, + &config.FCTemplate{ID: "Category", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Category", true)}, + &config.FCTemplate{ID: "Account", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Account", true)}, + &config.FCTemplate{ID: "Destination", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Destination", true)}, + &config.FCTemplate{ID: "AnswerTime", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"AnswerTime", true), Layout: "2006-01-02T15:04:05Z07:00"}, - &config.CfgCdrField{Tag: "UsageVoice", Type: "*composed", - FieldFilter: utils.ParseRSRFieldsMustCompile("ToR(*voice)", utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile("Usage{*duration_seconds}", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "UsageData", Type: "*composed", - FieldFilter: utils.ParseRSRFieldsMustCompile("ToR(*data)", utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile("Usage{*duration_nanoseconds}", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "UsageSMS", Type: "*composed", - FieldFilter: utils.ParseRSRFieldsMustCompile("ToR(*sms)", utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile("Usage{*duration_nanoseconds}", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Cost", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Cost", utils.INFIELD_SEP), + &config.FCTemplate{ID: "UsageVoice", Type: "*composed", + Filters: []string{"*string:ToR:*voice"}, + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Usage{*duration_seconds}", true)}, + &config.FCTemplate{ID: "UsageData", Type: "*composed", + Filters: []string{"*string:ToR:*data"}, + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Usage{*duration_nanoseconds}", true)}, + &config.FCTemplate{ID: "UsageSMS", Type: "*composed", + Filters: []string{"*string:ToR:*sms"}, + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Usage{*duration_nanoseconds}", true)}, + &config.FCTemplate{ID: "Cost", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Cost", true), RoundingDecimals: 4}, } cdrVoice := &CDR{ @@ -178,10 +178,12 @@ func TestExportVoiceWithConvert(t *testing.T) { ExtraFields: map[string]string{"extra1": "val_extra1", "extra2": "val_extra2", "extra3": "val_extra3"}, } + data, _ := NewMapStorage() + dmForCDRe := NewDataManager(data) cdre, err := NewCDRExporter([]*CDR{cdrVoice, cdrData, cdrSMS}, cdreCfg, utils.MetaFileCSV, "", "", "firstexport", true, 1, '|', map[string]float64{}, 0.0, - 5, true, nil, nil) + 5, true, nil, &FilterS{dm: dmForCDRe, cfg: cfg}) if err != nil { t.Error("Unexpected error received: ", err) } @@ -209,35 +211,35 @@ func TestExportWithFilter(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() cdreCfg := cfg.CdreProfiles["*default"] cdreCfg.Filters = []string{"*string:Tenant:cgrates.org"} - cdreCfg.ContentFields = []*config.CfgCdrField{ - &config.CfgCdrField{Tag: "ToR", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("ToR", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "OriginID", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("OriginID", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "RequestType", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("RequestType", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Tenant", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Tenant", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Category", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Category", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Account", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Account", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Destination", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Destination", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "AnswerTime", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("AnswerTime", utils.INFIELD_SEP), + cdreCfg.ContentFields = []*config.FCTemplate{ + &config.FCTemplate{ID: "ToR", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"ToR", true)}, + &config.FCTemplate{ID: "OriginID", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"OriginID", true)}, + &config.FCTemplate{ID: "RequestType", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"RequestType", true)}, + &config.FCTemplate{ID: "Tenant", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Tenant", true)}, + &config.FCTemplate{ID: "Category", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Category", true)}, + &config.FCTemplate{ID: "Account", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Account", true)}, + &config.FCTemplate{ID: "Destination", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Destination", true)}, + &config.FCTemplate{ID: "AnswerTime", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"AnswerTime", true), Layout: "2006-01-02T15:04:05Z07:00"}, - &config.CfgCdrField{Tag: "UsageVoice", Type: "*composed", - FieldFilter: utils.ParseRSRFieldsMustCompile("ToR(*voice)", utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile("Usage{*duration_seconds}", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "UsageData", Type: "*composed", - FieldFilter: utils.ParseRSRFieldsMustCompile("ToR(*data)", utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile("Usage{*duration_nanoseconds}", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "UsageSMS", Type: "*composed", - FieldFilter: utils.ParseRSRFieldsMustCompile("ToR(*sms)", utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile("Usage{*duration_nanoseconds}", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Cost", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Cost", utils.INFIELD_SEP), + &config.FCTemplate{ID: "UsageVoice", Type: "*composed", + Filters: []string{"*string:ToR:*voice"}, + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Usage{*duration_seconds}", true)}, + &config.FCTemplate{ID: "UsageData", Type: "*composed", + Filters: []string{"*string:ToR:*data"}, + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Usage{*duration_nanoseconds}", true)}, + &config.FCTemplate{ID: "UsageSMS", Type: "*composed", + Filters: []string{"*string:ToR:*sms"}, + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Usage{*duration_nanoseconds}", true)}, + &config.FCTemplate{ID: "Cost", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Cost", true), RoundingDecimals: 4}, } cdrVoice := &CDR{ @@ -306,35 +308,35 @@ func TestExportWithFilter2(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() cdreCfg := cfg.CdreProfiles["*default"] cdreCfg.Filters = []string{"*string:Tenant:cgrates.org", "*lte:Cost:0.5"} - cdreCfg.ContentFields = []*config.CfgCdrField{ - &config.CfgCdrField{Tag: "ToR", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("ToR", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "OriginID", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("OriginID", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "RequestType", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("RequestType", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Tenant", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Tenant", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Category", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Category", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Account", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Account", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Destination", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Destination", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "AnswerTime", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("AnswerTime", utils.INFIELD_SEP), + cdreCfg.ContentFields = []*config.FCTemplate{ + &config.FCTemplate{ID: "ToR", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"ToR", true)}, + &config.FCTemplate{ID: "OriginID", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"OriginID", true)}, + &config.FCTemplate{ID: "RequestType", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"RequestType", true)}, + &config.FCTemplate{ID: "Tenant", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Tenant", true)}, + &config.FCTemplate{ID: "Category", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Category", true)}, + &config.FCTemplate{ID: "Account", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Account", true)}, + &config.FCTemplate{ID: "Destination", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Destination", true)}, + &config.FCTemplate{ID: "AnswerTime", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"AnswerTime", true), Layout: "2006-01-02T15:04:05Z07:00"}, - &config.CfgCdrField{Tag: "UsageVoice", Type: "*composed", - FieldFilter: utils.ParseRSRFieldsMustCompile("ToR(*voice)", utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile("Usage{*duration_seconds}", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "UsageData", Type: "*composed", - FieldFilter: utils.ParseRSRFieldsMustCompile("ToR(*data)", utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile("Usage{*duration_nanoseconds}", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "UsageSMS", Type: "*composed", - FieldFilter: utils.ParseRSRFieldsMustCompile("ToR(*sms)", utils.INFIELD_SEP), - Value: utils.ParseRSRFieldsMustCompile("Usage{*duration_nanoseconds}", utils.INFIELD_SEP)}, - &config.CfgCdrField{Tag: "Cost", Type: "*composed", - Value: utils.ParseRSRFieldsMustCompile("Cost", utils.INFIELD_SEP), + &config.FCTemplate{ID: "UsageVoice", Type: "*composed", + Filters: []string{"*string:ToR:*voice"}, + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Usage{*duration_seconds}", true)}, + &config.FCTemplate{ID: "UsageData", Type: "*composed", + Filters: []string{"*string:ToR:*data"}, + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Usage{*duration_nanoseconds}", true)}, + &config.FCTemplate{ID: "UsageSMS", Type: "*composed", + Filters: []string{"*string:ToR:*sms"}, + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Usage{*duration_nanoseconds}", true)}, + &config.FCTemplate{ID: "Cost", Type: "*composed", + Value: config.NewRSRParsersMustCompile(utils.DynamicDataPrefix+"Cost", true), RoundingDecimals: 4}, } cdrVoice := &CDR{ diff --git a/engine/cdrefwv_test.go b/engine/cdrefwv_test.go index eea651d41..86a6262ae 100644 --- a/engine/cdrefwv_test.go +++ b/engine/cdrefwv_test.go @@ -17,6 +17,7 @@ along with this program. If not, see */ package engine +/* import ( "bytes" "math" @@ -407,3 +408,4 @@ func TestWriteCdrs(t *testing.T) { t.Error("Unexpected TotalCost: ", cdre.TotalCost()) } } +*/ diff --git a/engine/cdrs.go b/engine/cdrs.go index f36a9ba7f..847630d2b 100644 --- a/engine/cdrs.go +++ b/engine/cdrs.go @@ -307,85 +307,88 @@ func (self *CdrServer) deriveRateStoreStatsReplicate(cdr *CDR, store, cdrstats, } func (self *CdrServer) deriveCdrs(cdr *CDR) (drvdCDRs []*CDR, err error) { - dfltCDRRun := cdr.Clone() - cdrRuns := []*CDR{dfltCDRRun} - if cdr.RunID != utils.MetaRaw { // Only derive *raw CDRs - return cdrRuns, nil - } - dfltCDRRun.RunID = utils.META_DEFAULT // Rewrite *raw with *default since we have it as first run - if self.attrS != nil { - var rplyEv AttrSProcessEventReply - if err = self.attrS.Call(utils.AttributeSv1ProcessEvent, - cdr.AsCGREvent(), &rplyEv); err != nil { - return + /* + dfltCDRRun := cdr.Clone() + cdrRuns := []*CDR{dfltCDRRun} + if cdr.RunID != utils.MetaRaw { // Only derive *raw CDRs + return cdrRuns, nil } - if err = cdr.UpdateFromCGREvent(rplyEv.CGREvent, - rplyEv.AlteredFields); err != nil { - return - } - } - if err := LoadUserProfile(cdr, utils.EXTRA_FIELDS); err != nil { - return nil, err - } - if err := LoadAlias(&AttrMatchingAlias{ - Destination: cdr.Destination, - Direction: utils.OUT, - Tenant: cdr.Tenant, - Category: cdr.Category, - Account: cdr.Account, - Subject: cdr.Subject, - Context: utils.MetaRating, - }, cdr, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { - return nil, err - } - attrsDC := &utils.AttrDerivedChargers{Tenant: cdr.Tenant, Category: cdr.Category, Direction: utils.OUT, - Account: cdr.Account, Subject: cdr.Subject, Destination: cdr.Destination} - var dcs utils.DerivedChargers - if err := self.rals.Call("Responder.GetDerivedChargers", attrsDC, &dcs); err != nil { - utils.Logger.Err(fmt.Sprintf("Could not get derived charging for cgrid %s, error: %s", cdr.CGRID, err.Error())) - return nil, err - } - for _, dc := range dcs.Chargers { - runFilters, _ := utils.ParseRSRFields(dc.RunFilters, utils.INFIELD_SEP) - matchingAllFilters := true - for _, dcRunFilter := range runFilters { - if _, err := cdr.FieldAsString(dcRunFilter); err != nil { - matchingAllFilters = false - break + dfltCDRRun.RunID = utils.META_DEFAULT // Rewrite *raw with *default since we have it as first run + if self.attrS != nil { + var rplyEv AttrSProcessEventReply + if err = self.attrS.Call(utils.AttributeSv1ProcessEvent, + cdr.AsCGREvent(), &rplyEv); err != nil { + return + } + if err = cdr.UpdateFromCGREvent(rplyEv.CGREvent, + rplyEv.AlteredFields); err != nil { + return } } - if !matchingAllFilters { // Do not process the derived charger further if not all filters were matched - continue + if err := LoadUserProfile(cdr, utils.EXTRA_FIELDS); err != nil { + return nil, err } - dcRequestTypeFld, _ := utils.NewRSRField(dc.RequestTypeField) - dcTenantFld, _ := utils.NewRSRField(dc.TenantField) - dcCategoryFld, _ := utils.NewRSRField(dc.CategoryField) - dcAcntFld, _ := utils.NewRSRField(dc.AccountField) - dcSubjFld, _ := utils.NewRSRField(dc.SubjectField) - dcDstFld, _ := utils.NewRSRField(dc.DestinationField) - dcSTimeFld, _ := utils.NewRSRField(dc.SetupTimeField) - dcATimeFld, _ := utils.NewRSRField(dc.AnswerTimeField) - dcDurFld, _ := utils.NewRSRField(dc.UsageField) - dcRatedFld, _ := utils.NewRSRField(dc.PreRatedField) - dcCostFld, _ := utils.NewRSRField(dc.CostField) + if err := LoadAlias(&AttrMatchingAlias{ + Destination: cdr.Destination, + Direction: utils.OUT, + Tenant: cdr.Tenant, + Category: cdr.Category, + Account: cdr.Account, + Subject: cdr.Subject, + Context: utils.MetaRating, + }, cdr, utils.EXTRA_FIELDS); err != nil && err != utils.ErrNotFound { + return nil, err + } + attrsDC := &utils.AttrDerivedChargers{Tenant: cdr.Tenant, Category: cdr.Category, Direction: utils.OUT, + Account: cdr.Account, Subject: cdr.Subject, Destination: cdr.Destination} + var dcs utils.DerivedChargers + if err := self.rals.Call("Responder.GetDerivedChargers", attrsDC, &dcs); err != nil { + utils.Logger.Err(fmt.Sprintf("Could not get derived charging for cgrid %s, error: %s", cdr.CGRID, err.Error())) + return nil, err + } + for _, dc := range dcs.Chargers { + runFilters, _ := utils.ParseRSRFields(dc.RunFilters, utils.INFIELD_SEP) + matchingAllFilters := true + for _, dcRunFilter := range runFilters { + if _, err := cdr.FieldAsString(dcRunFilter); err != nil { + matchingAllFilters = false + break + } + } + if !matchingAllFilters { // Do not process the derived charger further if not all filters were matched + continue + } + dcRequestTypeFld, _ := utils.NewRSRField(dc.RequestTypeField) + dcTenantFld, _ := utils.NewRSRField(dc.TenantField) + dcCategoryFld, _ := utils.NewRSRField(dc.CategoryField) + dcAcntFld, _ := utils.NewRSRField(dc.AccountField) + dcSubjFld, _ := utils.NewRSRField(dc.SubjectField) + dcDstFld, _ := utils.NewRSRField(dc.DestinationField) + dcSTimeFld, _ := utils.NewRSRField(dc.SetupTimeField) + dcATimeFld, _ := utils.NewRSRField(dc.AnswerTimeField) + dcDurFld, _ := utils.NewRSRField(dc.UsageField) + dcRatedFld, _ := utils.NewRSRField(dc.PreRatedField) + dcCostFld, _ := utils.NewRSRField(dc.CostField) - dcExtraFields := []*utils.RSRField{} - for key, _ := range cdr.ExtraFields { - dcExtraFields = append(dcExtraFields, &utils.RSRField{Id: key}) - } + dcExtraFields := []*utils.RSRField{} + for key, _ := range cdr.ExtraFields { + dcExtraFields = append(dcExtraFields, &utils.RSRField{Id: key}) + } - forkedCdr, err := cdr.ForkCdr(dc.RunID, dcRequestTypeFld, dcTenantFld, dcCategoryFld, dcAcntFld, dcSubjFld, dcDstFld, - dcSTimeFld, dcATimeFld, dcDurFld, dcRatedFld, dcCostFld, dcExtraFields, true, self.cgrCfg.DefaultTimezone) - if err != nil { - utils.Logger.Err(fmt.Sprintf("Could not fork CGR with cgrid %s, run: %s, error: %s", cdr.CGRID, dc.RunID, err.Error())) - continue // do not add it to the forked CDR list + forkedCdr, err := cdr.ForkCdr(dc.RunID, dcRequestTypeFld, dcTenantFld, dcCategoryFld, dcAcntFld, dcSubjFld, dcDstFld, + dcSTimeFld, dcATimeFld, dcDurFld, dcRatedFld, dcCostFld, dcExtraFields, true, self.cgrCfg.DefaultTimezone) + if err != nil { + utils.Logger.Err(fmt.Sprintf("Could not fork CGR with cgrid %s, run: %s, error: %s", cdr.CGRID, dc.RunID, err.Error())) + continue // do not add it to the forked CDR list + } + if !forkedCdr.PreRated { + forkedCdr.Cost = -1.0 // Make sure that un-rated CDRs start with Cost -1 + } + cdrRuns = append(cdrRuns, forkedCdr) } - if !forkedCdr.PreRated { - forkedCdr.Cost = -1.0 // Make sure that un-rated CDRs start with Cost -1 - } - cdrRuns = append(cdrRuns, forkedCdr) - } - return cdrRuns, nil + return cdrRuns, nil + */ + return } // rateCDR will populate cost field diff --git a/engine/responder.go b/engine/responder.go index 71eef1b64..6a7a9ac3e 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -367,7 +367,7 @@ func (rs *Responder) GetDerivedMaxSessionTime(ev *CDR, reply *float64) (err erro runFilters, _ := utils.ParseRSRFields(dc.RunFilters, utils.INFIELD_SEP) matchingAllFilters := true for _, dcRunFilter := range runFilters { - if _, err := ev.FieldAsString(dcRunFilter); err != nil { + if _, err := ev.FieldAsStringWithRSRField(dcRunFilter); err != nil { matchingAllFilters = false break } diff --git a/engine/suretax.go b/engine/suretax.go index ef971e471..e67d3e02b 100644 --- a/engine/suretax.go +++ b/engine/suretax.go @@ -42,14 +42,14 @@ func NewSureTaxRequest(cdr *CDR, stCfg *config.SureTaxCfg) (*SureTaxRequest, err } aTimeLoc := cdr.AnswerTime.In(stCfg.Timezone) revenue := utils.Round(cdr.Cost, 4, utils.ROUNDING_MIDDLE) - unts, err := strconv.ParseInt(cdr.FieldsAsString(stCfg.Units), 10, 64) + unts, err := strconv.ParseInt(cdr.FieldsAsStringWithRSRFields(stCfg.Units), 10, 64) if err != nil { return nil, err } taxExempt := []string{} - definedTaxExtempt := cdr.FieldsAsString(stCfg.TaxExemptionCodeList) + definedTaxExtempt := cdr.FieldsAsStringWithRSRFields(stCfg.TaxExemptionCodeList) if len(definedTaxExtempt) != 0 { - taxExempt = strings.Split(cdr.FieldsAsString(stCfg.TaxExemptionCodeList), ",") + taxExempt = strings.Split(cdr.FieldsAsStringWithRSRFields(stCfg.TaxExemptionCodeList), ",") } stReq := new(STRequest) stReq.ClientNumber = stCfg.ClientNumber @@ -59,28 +59,28 @@ func NewSureTaxRequest(cdr *CDR, stCfg *config.SureTaxCfg) (*SureTaxRequest, err stReq.DataMonth = strconv.Itoa(int(aTimeLoc.Month())) stReq.TotalRevenue = revenue stReq.ReturnFileCode = stCfg.ReturnFileCode - stReq.ClientTracking = cdr.FieldsAsString(stCfg.ClientTracking) + stReq.ClientTracking = cdr.FieldsAsStringWithRSRFields(stCfg.ClientTracking) stReq.ResponseGroup = stCfg.ResponseGroup stReq.ResponseType = stCfg.ResponseType stReq.ItemList = []*STRequestItem{ &STRequestItem{ - CustomerNumber: cdr.FieldsAsString(stCfg.CustomerNumber), - OrigNumber: cdr.FieldsAsString(stCfg.OrigNumber), - TermNumber: cdr.FieldsAsString(stCfg.TermNumber), - BillToNumber: cdr.FieldsAsString(stCfg.BillToNumber), - Zipcode: cdr.FieldsAsString(stCfg.Zipcode), - Plus4: cdr.FieldsAsString(stCfg.Plus4), - P2PZipcode: cdr.FieldsAsString(stCfg.P2PZipcode), - P2PPlus4: cdr.FieldsAsString(stCfg.P2PPlus4), + CustomerNumber: cdr.FieldsAsStringWithRSRFields(stCfg.CustomerNumber), + OrigNumber: cdr.FieldsAsStringWithRSRFields(stCfg.OrigNumber), + TermNumber: cdr.FieldsAsStringWithRSRFields(stCfg.TermNumber), + BillToNumber: cdr.FieldsAsStringWithRSRFields(stCfg.BillToNumber), + Zipcode: cdr.FieldsAsStringWithRSRFields(stCfg.Zipcode), + Plus4: cdr.FieldsAsStringWithRSRFields(stCfg.Plus4), + P2PZipcode: cdr.FieldsAsStringWithRSRFields(stCfg.P2PZipcode), + P2PPlus4: cdr.FieldsAsStringWithRSRFields(stCfg.P2PPlus4), TransDate: aTimeLoc.Format("2006-01-02T15:04:05"), Revenue: revenue, Units: unts, - UnitType: cdr.FieldsAsString(stCfg.UnitType), + UnitType: cdr.FieldsAsStringWithRSRFields(stCfg.UnitType), Seconds: int64(cdr.Usage.Seconds()), - TaxIncludedCode: cdr.FieldsAsString(stCfg.TaxIncluded), - TaxSitusRule: cdr.FieldsAsString(stCfg.TaxSitusRule), - TransTypeCode: cdr.FieldsAsString(stCfg.TransTypeCode), - SalesTypeCode: cdr.FieldsAsString(stCfg.SalesTypeCode), + TaxIncludedCode: cdr.FieldsAsStringWithRSRFields(stCfg.TaxIncluded), + TaxSitusRule: cdr.FieldsAsStringWithRSRFields(stCfg.TaxSitusRule), + TransTypeCode: cdr.FieldsAsStringWithRSRFields(stCfg.TransTypeCode), + SalesTypeCode: cdr.FieldsAsStringWithRSRFields(stCfg.SalesTypeCode), RegulatoryCode: stCfg.RegulatoryCode, TaxExemptionCodeList: taxExempt, },