diff --git a/config/cdrscfg.go b/config/cdrscfg.go index fcbf03504..d9c958275 100644 --- a/config/cdrscfg.go +++ b/config/cdrscfg.go @@ -25,16 +25,17 @@ import ( ) type CdrsCfg struct { - Enabled bool // Enable CDR Server service - ExtraFields []*utils.RSRField // Extra fields to store in CDRs - StoreCdrs bool // store cdrs in storDb - SMCostRetries int - ChargerSConns []string - RaterConns []string - AttributeSConns []string - ThresholdSConns []string - StatSConns []string - OnlineCDRExports []string // list of CDRE templates to use for real-time CDR exports + Enabled bool // Enable CDR Server service + ExtraFields []*utils.RSRField // Extra fields to store in CDRs + ExtraFieldsAliases map[string]string + StoreCdrs bool // store cdrs in storDb + SMCostRetries int + ChargerSConns []string + RaterConns []string + AttributeSConns []string + ThresholdSConns []string + StatSConns []string + OnlineCDRExports []string // list of CDRE templates to use for real-time CDR exports } // loadFromJsonCfg loads Cdrs config from JsonCfg @@ -50,6 +51,12 @@ func (cdrscfg *CdrsCfg) loadFromJsonCfg(jsnCdrsCfg *CdrsJsonCfg) (err error) { return err } } + if jsnCdrsCfg.Extra_fields_aliases != nil { + cdrscfg.ExtraFieldsAliases = make(map[string]string) + for key, val := range *jsnCdrsCfg.Extra_fields_aliases { + cdrscfg.ExtraFieldsAliases[key] = val + } + } if jsnCdrsCfg.Store_cdrs != nil { cdrscfg.StoreCdrs = *jsnCdrsCfg.Store_cdrs } @@ -125,6 +132,12 @@ func (cdrscfg *CdrsCfg) AsMapInterface() map[string]any { for i, item := range cdrscfg.ExtraFields { extraFields[i] = item.Rules } + + extraFieldsAliases := make(map[string]string) + for key, val := range cdrscfg.ExtraFieldsAliases { + extraFieldsAliases[key] = val + } + onlineCDRExports := make([]string, len(cdrscfg.OnlineCDRExports)) for i, item := range cdrscfg.OnlineCDRExports { onlineCDRExports[i] = item @@ -182,6 +195,7 @@ func (cdrscfg *CdrsCfg) AsMapInterface() map[string]any { return map[string]any{ utils.EnabledCfg: cdrscfg.Enabled, utils.ExtraFieldsCfg: extraFields, + utils.ExtraFieldAliases: extraFieldsAliases, utils.StoreCdrsCfg: cdrscfg.StoreCdrs, utils.SMCostRetriesCfg: cdrscfg.SMCostRetries, utils.ChargerSConnsCfg: chargerSConns, diff --git a/config/cdrscfg_test.go b/config/cdrscfg_test.go index afa494e06..e2ba256ab 100644 --- a/config/cdrscfg_test.go +++ b/config/cdrscfg_test.go @@ -113,6 +113,7 @@ func TestCdrsCfgAsMapInterface(t *testing.T) { eMap := map[string]any{ "enabled": false, "extra_fields": []string{}, + "extra_fields_aliases": map[string]string{}, "store_cdrs": true, "session_cost_retries": 5, "chargers_conns": []string{"*localhost"}, @@ -150,6 +151,7 @@ func TestCdrsCfgAsMapInterface(t *testing.T) { eMap = map[string]any{ "enabled": true, "extra_fields": []string{"PayPalAccount", "LCRProfile", "ResourceID"}, + "extra_fields_aliases": map[string]string{}, "store_cdrs": true, "session_cost_retries": 9, "chargers_conns": []string{"*internal"}, @@ -245,6 +247,7 @@ func TestCdrsCfgAsMapInterface2(t *testing.T) { utils.ThresholdSConnsCfg: slc, utils.StatSConnsCfg: slc, utils.OnlineCDRExportsCfg: slc, + utils.ExtraFieldAliases: map[string]string{}, } rcv := cdrscfg.AsMapInterface() diff --git a/config/libconfig_json.go b/config/libconfig_json.go index d07f2a979..1c0ec0ed6 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -141,6 +141,7 @@ type SchedulerJsonCfg struct { type CdrsJsonCfg struct { Enabled *bool Extra_fields *[]string + Extra_fields_aliases *map[string]string Store_cdrs *bool Session_cost_retries *int Chargers_conns *[]string diff --git a/engine/fscdr.go b/engine/fscdr.go index 4b0817752..5208f2497 100644 --- a/engine/fscdr.go +++ b/engine/fscdr.go @@ -77,6 +77,7 @@ func (fsCdr FSCdr) getCGRID() string { func (fsCdr FSCdr) getExtraFields() map[string]string { extraFields := make(map[string]string, len(fsCdr.cgrCfg.CdrsCfg().ExtraFields)) + extraFieldsAliases := fsCdr.cgrCfg.CdrsCfg().ExtraFieldsAliases for _, field := range fsCdr.cgrCfg.CdrsCfg().ExtraFields { origFieldVal, foundInVars := fsCdr.vars[field.Id] if strings.HasPrefix(field.Id, utils.STATIC_VALUE_PREFIX) { // Support for static values injected in the CDRS. it will show up as {^value:value} @@ -86,9 +87,12 @@ func (fsCdr FSCdr) getExtraFields() map[string]string { origFieldVal = fsCdr.searchExtraField(field.Id, fsCdr.body) } if parsed, err := field.Parse(origFieldVal); err == nil { + if alias, has := extraFieldsAliases[field.Id]; has && alias != utils.EmptyString { + extraFields[alias] = parsed + continue + } extraFields[field.Id] = parsed } - } return extraFields } diff --git a/engine/fscdr_test.go b/engine/fscdr_test.go index 65f00f1a5..0ce1656b5 100644 --- a/engine/fscdr_test.go +++ b/engine/fscdr_test.go @@ -548,3 +548,78 @@ func TestFSCdrNewFSCdr(t *testing.T) { t.Error(rcv) } } + +func TestFsCdrExtraFieldsAliases(t *testing.T) { + tests := []struct { + name string + eFieldsCfg string + expected map[string]string + }{ + { + name: "extra_fields_aliases", + eFieldsCfg: `{ + "cdrs": { + "extra_fields":["~sip_user_agent:s/([A-Za-z]*).+/$1/","cgr_notify"], + "extra_fields_aliases":{"sip_user_agent":"sip_ua","cgr_notify":"reply_notify"}, + },}`, + expected: map[string]string{ + "sip_ua": "Jitsi", + "reply_notify": "AUTH_OK", + }, + }, + { + name: "extra_fields", + eFieldsCfg: `{ + "cdrs": { + "extra_fields": ["read_codec","call_timeout","~sip_user_agent:s/([A-Za-z]*).+/$1/"], + }}`, + expected: map[string]string{ + "read_codec": "PCMU", + "call_timeout": "30", + "sip_user_agent": "Jitsi", + }, + }, + { + name: "extra_fields_aliases without a value", + eFieldsCfg: `{ + "cdrs": { + "extra_fields": ["hangup_cause","read_codec","call_timeout"], + "extra_fields_aliases": {"read_codec":"","call_timeout":"timeout"}, + }}`, + expected: map[string]string{ + "read_codec": "PCMU", + "hangup_cause": "NORMAL_CLEARING", + "timeout": "30", + }, + }, + { + name: "alias key not defined in extra_fields", + eFieldsCfg: `{ + "cdrs": { + "extra_fields": ["sip_contact_user"], + "extra_fields_aliases": {"sip_contact_user":"user","sip_received_ip":"rec_ip"}, + }}`, + expected: map[string]string{ + "user": "1001", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + fsCdrCfg, err := config.NewCGRConfigFromJsonStringWithDefaults(tt.eFieldsCfg) + if err != nil { + t.Error("Could not parse the config", err) + } + fsCdr, err := NewFSCdr(body, fsCdrCfg) + if err != nil { + t.Error("Could not parse cdr", err.Error()) + } + extraFields := fsCdr.getExtraFields() + if !reflect.DeepEqual(tt.expected, extraFields) { + t.Errorf("expected %+v,\nreceived %+v", tt.expected, extraFields) + } + }) + } + +} diff --git a/utils/consts.go b/utils/consts.go index b78736f2e..03afff5dc 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -1718,6 +1718,7 @@ const ( // CdrsCfg const ( ExtraFieldsCfg = "extra_fields" + ExtraFieldAliases = "extra_fields_aliases" StoreCdrsCfg = "store_cdrs" SMCostRetriesCfg = "session_cost_retries" ChargerSConnsCfg = "chargers_conns"