diff --git a/config/config_defaults.go b/config/config_defaults.go index b8c84761a..8a1a51427 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -197,7 +197,7 @@ const CGRATES_CFG_JSON = ` "cdrs": "internal", // address where to reach CDR Server, empty to disable CDR capturing <""|internal|x.y.z.y:1234> "reconnects": 5, // number of reconnect attempts to rater or cdrs "create_cdr": false, // create CDR out of events and sends them to CDRS component - "cdr_extra_fields": [], // extra fields to store in CDRs when creating them + "extra_fields": [], // extra fields to store in auth/CDRs when creating them "debit_interval": "10s", // interval to perform debits on. "min_call_duration": "0s", // only authorize calls with allowed duration higher than this "max_call_duration": "3h", // maximum call duration a prepaid call can last diff --git a/config/config_json_test.go b/config/config_json_test.go index c753b49c6..eac1e82b3 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -324,7 +324,7 @@ func TestSmFsJsonCfg(t *testing.T) { Cdrs: utils.StringPointer("internal"), Reconnects: utils.IntPointer(5), Create_cdr: utils.BoolPointer(false), - Cdr_extra_fields: utils.StringSlicePointer([]string{}), + Extra_fields: utils.StringSlicePointer([]string{}), Debit_interval: utils.StringPointer("10s"), Min_call_duration: utils.StringPointer("0s"), Max_call_duration: utils.StringPointer("3h"), diff --git a/config/libconfig_json.go b/config/libconfig_json.go index a3ae0494e..7c285bb3a 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -158,7 +158,7 @@ type SmFsJsonCfg struct { Cdrs *string Reconnects *int Create_cdr *bool - Cdr_extra_fields *[]string + Extra_fields *[]string Debit_interval *string Min_call_duration *string Max_call_duration *string diff --git a/config/smconfig.go b/config/smconfig.go index 0075783bd..dc03b1d6d 100644 --- a/config/smconfig.go +++ b/config/smconfig.go @@ -61,7 +61,7 @@ type SmFsConfig struct { Cdrs string Reconnects int CreateCdr bool - CdrExtraFields []*utils.RSRField + ExtraFields []*utils.RSRField DebitInterval time.Duration MinCallDuration time.Duration MaxCallDuration time.Duration @@ -94,8 +94,8 @@ func (self *SmFsConfig) loadFromJsonCfg(jsnCfg *SmFsJsonCfg) error { if jsnCfg.Create_cdr != nil { self.CreateCdr = *jsnCfg.Create_cdr } - if jsnCfg.Cdr_extra_fields != nil { - if self.CdrExtraFields, err = utils.ParseRSRFieldsFromSlice(*jsnCfg.Cdr_extra_fields); err != nil { + if jsnCfg.Extra_fields != nil { + if self.ExtraFields, err = utils.ParseRSRFieldsFromSlice(*jsnCfg.Extra_fields); err != nil { return err } } diff --git a/engine/lcr.go b/engine/lcr.go index 904e57dc5..54bef4477 100644 --- a/engine/lcr.go +++ b/engine/lcr.go @@ -57,6 +57,7 @@ type LcrRequest struct { SetupTime string Duration string IgnoreErrors bool + ExtraFields map[string]string *utils.Paginator } @@ -90,7 +91,7 @@ func (self *LcrRequest) AsCallDescriptor(timezone string) (*CallDescriptor, erro } else if callDur, err = utils.ParseDurationWithSecs(self.Duration); err != nil { return nil, err } - return &CallDescriptor{ + cd := &CallDescriptor{ Direction: self.Direction, Tenant: self.Tenant, Category: self.Category, @@ -99,7 +100,14 @@ func (self *LcrRequest) AsCallDescriptor(timezone string) (*CallDescriptor, erro Destination: self.Destination, TimeStart: timeStart, TimeEnd: timeStart.Add(callDur), - }, nil + } + if self.ExtraFields != nil { + cd.ExtraFields = make(map[string]string) + } + for key, val := range self.ExtraFields { + cd.ExtraFields[key] = val + } + return cd, nil } // A LCR reply, used in APIer and SM where we need to expose it diff --git a/sessionmanager/fsevent.go b/sessionmanager/fsevent.go index 36ad6341a..449fc47cf 100644 --- a/sessionmanager/fsevent.go +++ b/sessionmanager/fsevent.go @@ -42,7 +42,7 @@ const ( ACCOUNT = "variable_" + utils.CGR_ACCOUNT DESTINATION = "variable_" + utils.CGR_DESTINATION REQTYPE = "variable_" + utils.CGR_REQTYPE //prepaid or postpaid - Category = "variable_" + utils.CGR_CATEGORY + CATEGORY = "variable_" + utils.CGR_CATEGORY VAR_CGR_SUPPLIER = "variable_" + utils.CGR_SUPPLIER UUID = "Unique-ID" // -Unique ID for this call leg CSTMID = "variable_" + utils.CGR_TENANT @@ -101,30 +101,27 @@ func (fsev FSEvent) GetDirection(fieldName string) string { //TODO: implement direction return utils.OUT } -func (fsev FSEvent) GetSubject(fieldName string) string { - if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value - return fieldName[len(utils.STATIC_VALUE_PREFIX):] - } else if fieldName == utils.META_DEFAULT { - return utils.FirstNonEmpty(fsev[SUBJECT], fsev[USERNAME]) - } - return utils.FirstNonEmpty(fsev[fieldName], fsev[SUBJECT], fsev[USERNAME]) -} +// Account calling func (fsev FSEvent) GetAccount(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] - } else if fieldName == utils.META_DEFAULT { - return utils.FirstNonEmpty(fsev[ACCOUNT], fsev[USERNAME]) } return utils.FirstNonEmpty(fsev[fieldName], fsev[ACCOUNT], fsev[USERNAME]) } +// Rating subject being charged +func (fsev FSEvent) GetSubject(fieldName string) string { + if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value + return fieldName[len(utils.STATIC_VALUE_PREFIX):] + } + return utils.FirstNonEmpty(fsev[fieldName], fsev[SUBJECT], fsev.GetAccount(fieldName)) +} + // Charging destination number func (fsev FSEvent) GetDestination(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] - } else if fieldName == utils.META_DEFAULT { - return utils.FirstNonEmpty(fsev[DESTINATION], fsev[CALL_DEST_NR], fsev[SIP_REQ_USER]) } return utils.FirstNonEmpty(fsev[fieldName], fsev[DESTINATION], fsev[CALL_DEST_NR], fsev[SIP_REQ_USER]) } @@ -133,18 +130,14 @@ func (fsev FSEvent) GetDestination(fieldName string) string { func (fsev FSEvent) GetCallDestNr(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] - } else if fieldName == utils.META_DEFAULT { - return utils.FirstNonEmpty(fsev[CALL_DEST_NR], fsev[SIP_REQ_USER]) } return utils.FirstNonEmpty(fsev[fieldName], fsev[CALL_DEST_NR], fsev[SIP_REQ_USER]) } func (fsev FSEvent) GetCategory(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] - } else if fieldName == utils.META_DEFAULT { - return utils.FirstNonEmpty(fsev[Category], config.CgrConfig().DefaultCategory) } - return utils.FirstNonEmpty(fsev[fieldName], fsev[Category], config.CgrConfig().DefaultCategory) + return utils.FirstNonEmpty(fsev[fieldName], fsev[CATEGORY], config.CgrConfig().DefaultCategory) } func (fsev FSEvent) GetCgrId(timezone string) string { setupTime, _ := fsev.GetSetupTime(utils.META_DEFAULT, timezone) @@ -159,8 +152,6 @@ func (fsev FSEvent) GetSessionIds() []string { func (fsev FSEvent) GetTenant(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] - } else if fieldName == utils.META_DEFAULT { - return utils.FirstNonEmpty(fsev[CSTMID], config.CgrConfig().DefaultTenant) } return utils.FirstNonEmpty(fsev[fieldName], fsev[CSTMID], config.CgrConfig().DefaultTenant) } @@ -173,15 +164,13 @@ func (fsev FSEvent) GetReqType(fieldName string) string { } if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] - } else if fieldName == utils.META_DEFAULT { - return utils.FirstNonEmpty(fsev[REQTYPE], reqTypeDetected, config.CgrConfig().DefaultReqType) } return utils.FirstNonEmpty(fsev[fieldName], fsev[REQTYPE], reqTypeDetected, config.CgrConfig().DefaultReqType) } func (fsev FSEvent) MissingParameter() bool { return strings.TrimSpace(fsev.GetDirection(utils.META_DEFAULT)) == "" || - strings.TrimSpace(fsev.GetSubject(utils.META_DEFAULT)) == "" || strings.TrimSpace(fsev.GetAccount(utils.META_DEFAULT)) == "" || + strings.TrimSpace(fsev.GetSubject(utils.META_DEFAULT)) == "" || strings.TrimSpace(fsev.GetDestination(utils.META_DEFAULT)) == "" || strings.TrimSpace(fsev.GetCategory(utils.META_DEFAULT)) == "" || strings.TrimSpace(fsev.GetUUID()) == "" || @@ -257,15 +246,13 @@ func (fsev FSEvent) GetDisconnectCause(fieldName string) string { func (fsev FSEvent) GetOriginatorIP(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] - } else if fieldName == utils.META_DEFAULT { - return fsev[FS_IPv4] } return utils.FirstNonEmpty(fsev[fieldName], fsev[FS_IPv4]) } func (fsev FSEvent) GetExtraFields() map[string]string { extraFields := make(map[string]string) - for _, fldRule := range config.CgrConfig().SmFsConfig.CdrExtraFields { + for _, fldRule := range config.CgrConfig().SmFsConfig.ExtraFields { extraFields[fldRule.Id] = fsev.ParseEventValue(fldRule, config.CgrConfig().DefaultTimezone) } return extraFields @@ -388,6 +375,7 @@ func (fsev FSEvent) AsCallDescriptor() (*engine.CallDescriptor, error) { Destination: fsev.GetDestination(utils.META_DEFAULT), SetupTime: utils.FirstNonEmpty(fsev[SETUP_TIME], fsev[ANSWER_TIME]), Duration: fsev[DURATION], + ExtraFields: fsev.GetExtraFields(), } return lcrReq.AsCallDescriptor(config.CgrConfig().DefaultTimezone) } diff --git a/sessionmanager/fsevent_test.go b/sessionmanager/fsevent_test.go index 94f49c332..27027afc4 100644 --- a/sessionmanager/fsevent_test.go +++ b/sessionmanager/fsevent_test.go @@ -645,7 +645,7 @@ func TestFsEvAsStoredCdr(t *testing.T) { func TestFsEvGetExtraFields(t *testing.T) { cfg, _ := config.NewDefaultCGRConfig() - cfg.SmFsConfig.CdrExtraFields = []*utils.RSRField{&utils.RSRField{Id: "Channel-Read-Codec-Name"}, &utils.RSRField{Id: "Channel-Write-Codec-Name"}, &utils.RSRField{Id: "NonExistingHeader"}} + cfg.SmFsConfig.ExtraFields = []*utils.RSRField{&utils.RSRField{Id: "Channel-Read-Codec-Name"}, &utils.RSRField{Id: "Channel-Write-Codec-Name"}, &utils.RSRField{Id: "NonExistingHeader"}} config.SetCgrConfig(cfg) ev := new(FSEvent).AsEvent(hangupEv) expectedExtraFields := map[string]string{"Channel-Read-Codec-Name": "SPEEX", "Channel-Write-Codec-Name": "SPEEX", "NonExistingHeader": ""} diff --git a/sessionmanager/kamevent.go b/sessionmanager/kamevent.go index 24849a32b..70997225e 100644 --- a/sessionmanager/kamevent.go +++ b/sessionmanager/kamevent.go @@ -129,11 +129,12 @@ func (kev KamEvent) GetAccount(fieldName string) string { } return utils.FirstNonEmpty(kev[fieldName], kev[CGR_ACCOUNT]) } + func (kev KamEvent) GetSubject(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] } - return utils.FirstNonEmpty(kev[fieldName], kev[CGR_SUBJECT], kev[CGR_ACCOUNT]) + return utils.FirstNonEmpty(kev[fieldName], kev[CGR_SUBJECT], kev.GetAccount(fieldName)) } func (kev KamEvent) GetDestination(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value diff --git a/sessionmanager/osipsevent.go b/sessionmanager/osipsevent.go index 4a40f3d6a..97498ac53 100644 --- a/sessionmanager/osipsevent.go +++ b/sessionmanager/osipsevent.go @@ -97,13 +97,7 @@ func (osipsev *OsipsEvent) GetDirection(fieldName string) string { return utils.OUT } -func (osipsev *OsipsEvent) GetSubject(fieldName string) string { - if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value - return fieldName[len(utils.STATIC_VALUE_PREFIX):] - } - return utils.FirstNonEmpty(osipsev.osipsEvent.AttrValues[fieldName], osipsev.osipsEvent.AttrValues[CGR_SUBJECT], osipsev.GetAccount(fieldName)) -} - +// Account being charged func (osipsev *OsipsEvent) GetAccount(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):] @@ -111,6 +105,14 @@ func (osipsev *OsipsEvent) GetAccount(fieldName string) string { return utils.FirstNonEmpty(osipsev.osipsEvent.AttrValues[fieldName], osipsev.osipsEvent.AttrValues[CGR_ACCOUNT]) } +// Rating subject being charged, falls back on account if missing +func (osipsev *OsipsEvent) GetSubject(fieldName string) string { + if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value + return fieldName[len(utils.STATIC_VALUE_PREFIX):] + } + return utils.FirstNonEmpty(osipsev.osipsEvent.AttrValues[fieldName], osipsev.osipsEvent.AttrValues[CGR_SUBJECT], osipsev.GetAccount(fieldName)) +} + func (osipsev *OsipsEvent) GetDestination(fieldName string) string { if strings.HasPrefix(fieldName, utils.STATIC_VALUE_PREFIX) { // Static value return fieldName[len(utils.STATIC_VALUE_PREFIX):]