From bba4a878f0dbb474e773e8a7bbbb07a17030f960 Mon Sep 17 00:00:00 2001 From: DanB Date: Tue, 22 Apr 2014 13:13:39 +0200 Subject: [PATCH] Refactoring configuration for DerivedCharging --- config/config.go | 177 +---------------- config/config_test.go | 52 +---- config/derivedchargers.go | 178 ++++++++++++++++-- config/derivedchargers_test.go | 87 +++++++++ config/helpers.go | 12 +- config/test_data.txt | 35 ++-- data/conf/cgrates.cfg | 121 ++++++------ data/storage/mysql/create_mediator_tables.sql | 2 +- utils/consts.go | 3 +- utils/rsrfield.go | 2 +- 10 files changed, 340 insertions(+), 329 deletions(-) diff --git a/config/config.go b/config/config.go index cc033d119..106ef67ab 100644 --- a/config/config.go +++ b/config/config.go @@ -123,17 +123,6 @@ type CGRConfig struct { SMRaterReconnects int // Number of reconnect attempts to rater SMDebitInterval int // the period to be debited in advanced during a call (in seconds) SMMaxCallDuration time.Duration // The maximum duration of a call - SMRunIds []string // Identifiers of additional sessions control. - SMReqTypeFields []string // Name of request type fields to be used during additional sessions control <""|*default|field_name>. - SMDirectionFields []string // Name of direction fields to be used during additional sessions control <""|*default|field_name>. - SMTenantFields []string // Name of tenant fields to be used during additional sessions control <""|*default|field_name>. - SMTORFields []string // Name of tor fields to be used during additional sessions control <""|*default|field_name>. - SMAccountFields []string // Name of account fields to be used during additional sessions control <""|*default|field_name>. - SMSubjectFields []string // Name of fields to be used during additional sessions control <""|*default|field_name>. - SMDestFields []string // Name of destination fields to be used during additional sessions control <""|*default|field_name>. - SMSetupTimeFields []string // Name of setup_time fields to be used during additional sessions control <""|*default|field_name>. - SMAnswerTimeFields []string // Name of answer_time fields to be used during additional sessions control <""|*default|field_name>. - SMDurationFields []string // Name of duration fields to be used during additional sessions control <""|*default|field_name>. MediatorEnabled bool // Starts Mediator service: . MediatorRater string // Address where to reach the Rater: MediatorRaterReconnects int // Number of reconnects to rater before giving up. @@ -148,7 +137,7 @@ type CGRConfig struct { MediatorSetupTimeFields []string // Name of setup_time fields to be used during mediation. Use index numbers in case of .csv cdrs. MediatorAnswerTimeFields []string // Name of answer_time fields to be used during mediation. Use index numbers in case of .csv cdrs. MediatorDurationFields []string // Name of duration fields to be used during mediation. Use index numbers in case of .csv cdrs. - DerivedChargers DerivedChargers // System wide pseudosessions which will be executed in case of no particular ones defined per account + DerivedChargers DerivedChargers // System wide derived chargers, added to the account level ones FreeswitchServer string // freeswitch address host:port FreeswitchPass string // FS socket password FreeswitchReconnects int // number of times to attempt reconnect after connect fails @@ -228,17 +217,6 @@ func (self *CGRConfig) setDefaults() error { self.MediatorEnabled = false self.MediatorRater = "internal" self.MediatorRaterReconnects = 3 - self.MediatorRunIds = []string{} - self.MediatorSubjectFields = []string{} - self.MediatorReqTypeFields = []string{} - self.MediatorDirectionFields = []string{} - self.MediatorTenantFields = []string{} - self.MediatorTORFields = []string{} - self.MediatorAccountFields = []string{} - self.MediatorDestFields = []string{} - self.MediatorSetupTimeFields = []string{} - self.MediatorAnswerTimeFields = []string{} - self.MediatorDurationFields = []string{} self.DerivedChargers = make(DerivedChargers, 0) self.SMEnabled = false self.SMSwitchType = FS @@ -246,17 +224,6 @@ func (self *CGRConfig) setDefaults() error { self.SMRaterReconnects = 3 self.SMDebitInterval = 10 self.SMMaxCallDuration = time.Duration(3) * time.Hour - self.SMRunIds = []string{} - self.SMReqTypeFields = []string{} - self.SMDirectionFields = []string{} - self.SMTenantFields = []string{} - self.SMTORFields = []string{} - self.SMAccountFields = []string{} - self.SMSubjectFields = []string{} - self.SMDestFields = []string{} - self.SMSetupTimeFields = []string{} - self.SMAnswerTimeFields = []string{} - self.SMDurationFields = []string{} self.FreeswitchServer = "127.0.0.1:8021" self.FreeswitchPass = "ClueCon" self.FreeswitchReconnects = 5 @@ -298,32 +265,6 @@ func (self *CGRConfig) checkConfigSanity() error { return errors.New("Need XmlTemplate for fixed_width cdr export") } } - // SessionManager should have same fields config length for session emulation - if len(self.SMReqTypeFields) != len(self.SMRunIds) || - len(self.SMDirectionFields) != len(self.SMRunIds) || - len(self.SMTenantFields) != len(self.SMRunIds) || - len(self.SMTORFields) != len(self.SMRunIds) || - len(self.SMAccountFields) != len(self.SMRunIds) || - len(self.SMSubjectFields) != len(self.SMRunIds) || - len(self.SMDestFields) != len(self.SMRunIds) || - len(self.SMSetupTimeFields) != len(self.SMRunIds) || - len(self.SMAnswerTimeFields) != len(self.SMRunIds) || - len(self.SMDurationFields) != len(self.SMRunIds) { - return errors.New(" Inconsistent fields length for SessionManager session emulation") - } - // Mediator needs to have consistent extra fields definition - if len(self.MediatorReqTypeFields) != len(self.MediatorRunIds) || - len(self.MediatorDirectionFields) != len(self.MediatorRunIds) || - len(self.MediatorTenantFields) != len(self.MediatorRunIds) || - len(self.MediatorTORFields) != len(self.MediatorRunIds) || - len(self.MediatorAccountFields) != len(self.MediatorRunIds) || - len(self.MediatorSubjectFields) != len(self.MediatorRunIds) || - len(self.MediatorDestFields) != len(self.MediatorRunIds) || - len(self.MediatorSetupTimeFields) != len(self.MediatorRunIds) || - len(self.MediatorAnswerTimeFields) != len(self.MediatorRunIds) || - len(self.MediatorDurationFields) != len(self.MediatorRunIds) { - return errors.New(" Inconsistent fields length for Mediator extra fields") - } return nil } @@ -585,7 +526,8 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) { cfg.CdrcDurationField, _ = c.GetString("cdrc", "duration_field") } if hasOpt = c.HasOption("cdrc", "extra_fields"); hasOpt { - if cfg.CdrcExtraFields, err = ConfigSlice(c, "cdrc", "extra_fields"); err != nil { + eFldsStr, _ := c.GetString("cdrc", "extra_fields") + if cfg.CdrcExtraFields, err = ConfigSlice(eFldsStr); err != nil { return nil, err } } @@ -598,61 +540,6 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) { if hasOpt = c.HasOption("mediator", "rater_reconnects"); hasOpt { cfg.MediatorRaterReconnects, _ = c.GetInt("mediator", "rater_reconnects") } - if hasOpt = c.HasOption("mediator", "run_ids"); hasOpt { - if cfg.MediatorRunIds, err = ConfigSlice(c, "mediator", "run_ids"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("mediator", "subject_fields"); hasOpt { - if cfg.MediatorSubjectFields, err = ConfigSlice(c, "mediator", "subject_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("mediator", "reqtype_fields"); hasOpt { - if cfg.MediatorReqTypeFields, err = ConfigSlice(c, "mediator", "reqtype_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("mediator", "direction_fields"); hasOpt { - if cfg.MediatorDirectionFields, err = ConfigSlice(c, "mediator", "direction_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("mediator", "tenant_fields"); hasOpt { - if cfg.MediatorTenantFields, err = ConfigSlice(c, "mediator", "tenant_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("mediator", "tor_fields"); hasOpt { - if cfg.MediatorTORFields, err = ConfigSlice(c, "mediator", "tor_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("mediator", "account_fields"); hasOpt { - if cfg.MediatorAccountFields, err = ConfigSlice(c, "mediator", "account_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("mediator", "destination_fields"); hasOpt { - if cfg.MediatorDestFields, err = ConfigSlice(c, "mediator", "destination_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("mediator", "setup_time_fields"); hasOpt { - if cfg.MediatorSetupTimeFields, err = ConfigSlice(c, "mediator", "setup_time_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("mediator", "answer_time_fields"); hasOpt { - if cfg.MediatorAnswerTimeFields, err = ConfigSlice(c, "mediator", "answer_time_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("mediator", "duration_fields"); hasOpt { - if cfg.MediatorDurationFields, err = ConfigSlice(c, "mediator", "duration_fields"); err != nil { - return nil, err - } - } if hasOpt = c.HasOption("session_manager", "enabled"); hasOpt { cfg.SMEnabled, _ = c.GetBool("session_manager", "enabled") } @@ -674,61 +561,6 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) { return nil, err } } - if hasOpt = c.HasOption("session_manager", "run_ids"); hasOpt { - if cfg.SMRunIds, err = ConfigSlice(c, "session_manager", "run_ids"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("session_manager", "reqtype_fields"); hasOpt { - if cfg.SMReqTypeFields, err = ConfigSlice(c, "session_manager", "reqtype_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("session_manager", "direction_fields"); hasOpt { - if cfg.SMDirectionFields, err = ConfigSlice(c, "session_manager", "direction_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("session_manager", "tenant_fields"); hasOpt { - if cfg.SMTenantFields, err = ConfigSlice(c, "session_manager", "tenant_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("session_manager", "tor_fields"); hasOpt { - if cfg.SMTORFields, err = ConfigSlice(c, "session_manager", "tor_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("session_manager", "account_fields"); hasOpt { - if cfg.SMAccountFields, err = ConfigSlice(c, "session_manager", "account_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("session_manager", "subject_fields"); hasOpt { - if cfg.SMSubjectFields, err = ConfigSlice(c, "session_manager", "subject_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("session_manager", "destination_fields"); hasOpt { - if cfg.SMDestFields, err = ConfigSlice(c, "session_manager", "destination_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("session_manager", "setup_time_fields"); hasOpt { - if cfg.SMSetupTimeFields, err = ConfigSlice(c, "session_manager", "setup_time_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("session_manager", "answer_time_fields"); hasOpt { - if cfg.SMAnswerTimeFields, err = ConfigSlice(c, "session_manager", "answer_time_fields"); err != nil { - return nil, err - } - } - if hasOpt = c.HasOption("session_manager", "duration_fields"); hasOpt { - if cfg.SMDurationFields, err = ConfigSlice(c, "session_manager", "duration_fields"); err != nil { - return nil, err - } - } if hasOpt = c.HasOption("freeswitch", "server"); hasOpt { cfg.FreeswitchServer, _ = c.GetString("freeswitch", "server") } @@ -738,6 +570,9 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) { if hasOpt = c.HasOption("freeswitch", "reconnects"); hasOpt { cfg.FreeswitchReconnects, _ = c.GetInt("freeswitch", "reconnects") } + if cfg.DerivedChargers, err = ParseCfgDerivedCharging(c); err != nil { + return nil, err + } if hasOpt = c.HasOption("history_agent", "enabled"); hasOpt { cfg.HistoryAgentEnabled, _ = c.GetBool("history_agent", "enabled") } diff --git a/config/config_test.go b/config/config_test.go index c5123c8c3..188db0afa 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -110,38 +110,16 @@ func TestDefaults(t *testing.T) { eCfg.MediatorEnabled = false eCfg.MediatorRater = "internal" eCfg.MediatorRaterReconnects = 3 - eCfg.MediatorRunIds = []string{} - eCfg.MediatorSubjectFields = []string{} - eCfg.MediatorReqTypeFields = []string{} - eCfg.MediatorDirectionFields = []string{} - eCfg.MediatorTenantFields = []string{} - eCfg.MediatorTORFields = []string{} - eCfg.MediatorAccountFields = []string{} - eCfg.MediatorDestFields = []string{} - eCfg.MediatorSetupTimeFields = []string{} - eCfg.MediatorAnswerTimeFields = []string{} - eCfg.DerivedChargers = make(DerivedChargers, 0) - eCfg.MediatorDurationFields = []string{} eCfg.SMEnabled = false eCfg.SMSwitchType = FS eCfg.SMRater = "internal" eCfg.SMRaterReconnects = 3 eCfg.SMDebitInterval = 10 eCfg.SMMaxCallDuration = time.Duration(3) * time.Hour - eCfg.SMRunIds = []string{} - eCfg.SMReqTypeFields = []string{} - eCfg.SMDirectionFields = []string{} - eCfg.SMTenantFields = []string{} - eCfg.SMTORFields = []string{} - eCfg.SMAccountFields = []string{} - eCfg.SMSubjectFields = []string{} - eCfg.SMDestFields = []string{} - eCfg.SMSetupTimeFields = []string{} - eCfg.SMAnswerTimeFields = []string{} - eCfg.SMDurationFields = []string{} eCfg.FreeswitchServer = "127.0.0.1:8021" eCfg.FreeswitchPass = "ClueCon" eCfg.FreeswitchReconnects = 5 + eCfg.DerivedChargers = make(DerivedChargers, 0) eCfg.HistoryAgentEnabled = false eCfg.HistoryServer = "internal" eCfg.HistoryServerEnabled = false @@ -184,10 +162,6 @@ func TestSanityCheck(t *testing.T) { if err := cfg.checkConfigSanity(); err != nil { t.Error("Invalid defaults: ", err) } - cfg.SMSubjectFields = []string{"sample1", "sample2", "sample3"} - if err := cfg.checkConfigSanity(); err == nil { - t.Error("Failed to detect config insanity") - } cfg = &CGRConfig{} cfg.CdreCdrFormat = utils.CDRE_FIXED_WIDTH if err := cfg.checkConfigSanity(); err == nil { @@ -269,37 +243,17 @@ func TestConfigFromFile(t *testing.T) { eCfg.MediatorEnabled = true eCfg.MediatorRater = "test" eCfg.MediatorRaterReconnects = 99 - eCfg.MediatorRunIds = []string{"test"} - eCfg.MediatorSubjectFields = []string{"test"} - eCfg.MediatorReqTypeFields = []string{"test"} - eCfg.MediatorDirectionFields = []string{"test"} - eCfg.MediatorTenantFields = []string{"test"} - eCfg.MediatorTORFields = []string{"test"} - eCfg.MediatorAccountFields = []string{"test"} - eCfg.MediatorDestFields = []string{"test"} - eCfg.MediatorSetupTimeFields = []string{"test"} - eCfg.MediatorAnswerTimeFields = []string{"test"} - eCfg.MediatorDurationFields = []string{"test"} eCfg.SMEnabled = true eCfg.SMSwitchType = "test" eCfg.SMRater = "test" eCfg.SMRaterReconnects = 99 eCfg.SMDebitInterval = 99 eCfg.SMMaxCallDuration = time.Duration(99) * time.Second - eCfg.SMRunIds = []string{"test"} - eCfg.SMReqTypeFields = []string{"test"} - eCfg.SMDirectionFields = []string{"test"} - eCfg.SMTenantFields = []string{"test"} - eCfg.SMTORFields = []string{"test"} - eCfg.SMAccountFields = []string{"test"} - eCfg.SMSubjectFields = []string{"test"} - eCfg.SMDestFields = []string{"test"} - eCfg.SMSetupTimeFields = []string{"test"} - eCfg.SMAnswerTimeFields = []string{"test"} - eCfg.SMDurationFields = []string{"test"} eCfg.FreeswitchServer = "test" eCfg.FreeswitchPass = "test" eCfg.FreeswitchReconnects = 99 + eCfg.DerivedChargers = DerivedChargers{&DerivedCharger{RunId: "test", ReqTypeField: "test", DirectionField: "test", TenantField: "test", + TorField: "test", AccountField: "test", SubjectField: "test", DestinationField: "test", SetupTimeField: "test", AnswerTimeField: "test", DurationField: "test"}} eCfg.HistoryAgentEnabled = true eCfg.HistoryServer = "test" eCfg.HistoryServerEnabled = true diff --git a/config/derivedchargers.go b/config/derivedchargers.go index 5e624e1f4..b1769d847 100644 --- a/config/derivedchargers.go +++ b/config/derivedchargers.go @@ -19,22 +19,103 @@ along with this program. If not, see package config import ( + "code.google.com/p/goconf/conf" "errors" "github.com/cgrates/cgrates/utils" + "strings" ) +// Wraps regexp compiling in case of rsr fields +func NewDerivedCharger(runId, reqTypeFld, dirFld, tenantFld, totFld, acntFld, subjFld, dstFld, sTimeFld, aTimeFld, durFld string) (dc *DerivedCharger, err error) { + if len(runId) == 0 { + return nil, errors.New("Empty run id field") + } + dc = &DerivedCharger{RunId: runId} + dc.ReqTypeField = reqTypeFld + if strings.HasPrefix(dc.ReqTypeField, utils.REGEXP_PREFIX) { + if dc.rsrReqTypeField, err = utils.NewRSRField(dc.ReqTypeField); err != nil { + return nil, err + } + } + dc.DirectionField = dirFld + if strings.HasPrefix(dc.DirectionField, utils.REGEXP_PREFIX) { + if dc.rsrDirectionField, err = utils.NewRSRField(dc.DirectionField); err != nil { + return nil, err + } + } + dc.TenantField = tenantFld + if strings.HasPrefix(dc.TenantField, utils.REGEXP_PREFIX) { + if dc.rsrTenantField, err = utils.NewRSRField(dc.TenantField); err != nil { + return nil, err + } + } + dc.TorField = totFld + if strings.HasPrefix(dc.TorField, utils.REGEXP_PREFIX) { + if dc.rsrTorField, err = utils.NewRSRField(dc.TorField); err != nil { + return nil, err + } + } + dc.AccountField = acntFld + if strings.HasPrefix(dc.AccountField, utils.REGEXP_PREFIX) { + if dc.rsrAccountField, err = utils.NewRSRField(dc.AccountField); err != nil { + return nil, err + } + } + dc.SubjectField = subjFld + if strings.HasPrefix(dc.SubjectField, utils.REGEXP_PREFIX) { + if dc.rsrSubjectField, err = utils.NewRSRField(dc.SubjectField); err != nil { + return nil, err + } + } + dc.DestinationField = dstFld + if strings.HasPrefix(dc.DestinationField, utils.REGEXP_PREFIX) { + if dc.rsrDestinationField, err = utils.NewRSRField(dc.DestinationField); err != nil { + return nil, err + } + } + dc.SetupTimeField = sTimeFld + if strings.HasPrefix(dc.SetupTimeField, utils.REGEXP_PREFIX) { + if dc.rsrSetupTimeField, err = utils.NewRSRField(dc.SetupTimeField); err != nil { + return nil, err + } + } + dc.AnswerTimeField = aTimeFld + if strings.HasPrefix(dc.AnswerTimeField, utils.REGEXP_PREFIX) { + if dc.rsrAnswerTimeField, err = utils.NewRSRField(dc.AnswerTimeField); err != nil { + return nil, err + } + } + dc.DurationField = durFld + if strings.HasPrefix(dc.DurationField, utils.REGEXP_PREFIX) { + if dc.rsrDurationField, err = utils.NewRSRField(dc.DurationField); err != nil { + return nil, err + } + } + return dc, nil +} + type DerivedCharger struct { - RunId string // Unique runId in the chain - ReqTypeField string // Field containing request type info, number in case of csv source, '^' as prefix in case of static values - DirectionField string // Field containing direction info - TenantField string // Field containing tenant info - TorField string // Field containing tor info - AccountField string // Field containing account information - SubjectField string // Field containing subject information - DestinationField string // Field containing destination information - SetupTimeField string // Field containing setup time information - AnswerTimeField string // Field containing answer time information - DurationField string // Field containing duration information + RunId string // Unique runId in the chain + ReqTypeField string // Field containing request type info, number in case of csv source, '^' as prefix in case of static values + DirectionField string // Field containing direction info + TenantField string // Field containing tenant info + TorField string // Field containing tor info + AccountField string // Field containing account information + SubjectField string // Field containing subject information + DestinationField string // Field containing destination information + SetupTimeField string // Field containing setup time information + AnswerTimeField string // Field containing answer time information + DurationField string // Field containing duration information + rsrReqTypeField *utils.RSRField // Storage for compiled Regexp in case of RSRFields + rsrDirectionField *utils.RSRField + rsrTenantField *utils.RSRField + rsrTorField *utils.RSRField + rsrAccountField *utils.RSRField + rsrSubjectField *utils.RSRField + rsrDestinationField *utils.RSRField + rsrSetupTimeField *utils.RSRField + rsrAnswerTimeField *utils.RSRField + rsrDurationField *utils.RSRField } type DerivedChargers []*DerivedCharger @@ -51,3 +132,78 @@ func (dcs DerivedChargers) Append(dc *DerivedCharger) (DerivedChargers, error) { } return append(dcs, dc), nil } + +// Parse the configuration file and returns DerivedChargers instance if no errors +func ParseCfgDerivedCharging(c *conf.ConfigFile) (dcs DerivedChargers, err error) { + var runIds, reqTypeFlds, directionFlds, tenantFlds, torFlds, acntFlds, subjFlds, dstFlds, sTimeFlds, aTimeFlds, durFlds []string + cfgVal, _ := c.GetString("derived_charging", "run_ids") + if runIds, err = ConfigSlice(cfgVal); err != nil { + return nil, err + } + cfgVal, _ = c.GetString("derived_charging", "reqtype_fields") + if reqTypeFlds, err = ConfigSlice(cfgVal); err != nil { + return nil, err + } + cfgVal, _ = c.GetString("derived_charging", "direction_fields") + if directionFlds, err = ConfigSlice(cfgVal); err != nil { + return nil, err + } + cfgVal, _ = c.GetString("derived_charging", "tenant_fields") + if tenantFlds, err = ConfigSlice(cfgVal); err != nil { + return nil, err + } + cfgVal, _ = c.GetString("derived_charging", "tor_fields") + if torFlds, err = ConfigSlice(cfgVal); err != nil { + return nil, err + } + cfgVal, _ = c.GetString("derived_charging", "account_fields") + if acntFlds, err = ConfigSlice(cfgVal); err != nil { + return nil, err + } + cfgVal, _ = c.GetString("derived_charging", "subject_fields") + if subjFlds, err = ConfigSlice(cfgVal); err != nil { + return nil, err + } + cfgVal, _ = c.GetString("derived_charging", "destination_fields") + if dstFlds, err = ConfigSlice(cfgVal); err != nil { + return nil, err + } + cfgVal, _ = c.GetString("derived_charging", "setup_time_fields") + if sTimeFlds, err = ConfigSlice(cfgVal); err != nil { + return nil, err + } + cfgVal, _ = c.GetString("derived_charging", "answer_time_fields") + if aTimeFlds, err = ConfigSlice(cfgVal); err != nil { + return nil, err + } + cfgVal, _ = c.GetString("derived_charging", "duration_fields") + if durFlds, err = ConfigSlice(cfgVal); err != nil { + return nil, err + } + // We need all to be the same length + if len(reqTypeFlds) != len(runIds) || + len(directionFlds) != len(runIds) || + len(tenantFlds) != len(runIds) || + len(torFlds) != len(runIds) || + len(acntFlds) != len(runIds) || + len(subjFlds) != len(runIds) || + len(dstFlds) != len(runIds) || + len(sTimeFlds) != len(runIds) || + len(aTimeFlds) != len(runIds) || + len(durFlds) != len(runIds) { + return nil, errors.New(" Inconsistent fields length in derivated_charging section") + } + // Create the individual chargers and append them to the final instance + dcs = make(DerivedChargers, 0) + for runIdx, runId := range runIds { + dc, err := NewDerivedCharger(runId, reqTypeFlds[runIdx], directionFlds[runIdx], tenantFlds[runIdx], torFlds[runIdx], + acntFlds[runIdx], subjFlds[runIdx], dstFlds[runIdx], sTimeFlds[runIdx], aTimeFlds[runIdx], durFlds[runIdx]) + if err != nil { + return nil, err + } + if dcs, err = dcs.Append(dc); err != nil { + return nil, err + } + } + return dcs, nil +} diff --git a/config/derivedchargers_test.go b/config/derivedchargers_test.go index 131b5d26c..c300d9070 100644 --- a/config/derivedchargers_test.go +++ b/config/derivedchargers_test.go @@ -20,6 +20,7 @@ package config import ( "github.com/cgrates/cgrates/utils" + "reflect" "testing" ) @@ -43,3 +44,89 @@ func TestAppendDerivedChargers(t *testing.T) { t.Error("Failed to detect duplicate runid") } } + +func TestNewDerivedCharger(t *testing.T) { + edc1 := &DerivedCharger{ + RunId: "test1", + ReqTypeField: "reqtype1", + DirectionField: "direction1", + TenantField: "tenant1", + TorField: "tor1", + AccountField: "account1", + SubjectField: "subject1", + DestinationField: "destination1", + SetupTimeField: "setuptime1", + AnswerTimeField: "answertime1", + DurationField: "duration1", + } + if dc1, err := NewDerivedCharger("test1", "reqtype1", "direction1", "tenant1", "tor1", "account1", "subject1", "destination1", + "setuptime1", "answertime1", "duration1"); err != nil { + t.Error("Unexpected error", err.Error) + } else if !reflect.DeepEqual(edc1, dc1) { + t.Errorf("Expecting: %v, received: %v", edc1, dc1) + } + edc2 := &DerivedCharger{ + RunId: "test2", + ReqTypeField: "~reqtype2:s/sip:(.+)/$1/", + DirectionField: "~direction2:s/sip:(.+)/$1/", + TenantField: "~tenant2:s/sip:(.+)/$1/", + TorField: "~tor2:s/sip:(.+)/$1/", + AccountField: "~account2:s/sip:(.+)/$1/", + SubjectField: "~subject2:s/sip:(.+)/$1/", + DestinationField: "~destination2:s/sip:(.+)/$1/", + SetupTimeField: "~setuptime2:s/sip:(.+)/$1/", + AnswerTimeField: "~answertime2:s/sip:(.+)/$1/", + DurationField: "~duration2:s/sip:(.+)/$1/", + } + edc2.rsrReqTypeField, _ = utils.NewRSRField("~reqtype2:s/sip:(.+)/$1/") + edc2.rsrDirectionField, _ = utils.NewRSRField("~direction2:s/sip:(.+)/$1/") + edc2.rsrTenantField, _ = utils.NewRSRField("~tenant2:s/sip:(.+)/$1/") + edc2.rsrTorField, _ = utils.NewRSRField("~tor2:s/sip:(.+)/$1/") + edc2.rsrAccountField, _ = utils.NewRSRField("~account2:s/sip:(.+)/$1/") + edc2.rsrSubjectField, _ = utils.NewRSRField("~subject2:s/sip:(.+)/$1/") + edc2.rsrDestinationField, _ = utils.NewRSRField("~destination2:s/sip:(.+)/$1/") + edc2.rsrSetupTimeField, _ = utils.NewRSRField("~setuptime2:s/sip:(.+)/$1/") + edc2.rsrAnswerTimeField, _ = utils.NewRSRField("~answertime2:s/sip:(.+)/$1/") + edc2.rsrDurationField, _ = utils.NewRSRField("~duration2:s/sip:(.+)/$1/") + if dc2, err := NewDerivedCharger("test2", + "~reqtype2:s/sip:(.+)/$1/", + "~direction2:s/sip:(.+)/$1/", + "~tenant2:s/sip:(.+)/$1/", + "~tor2:s/sip:(.+)/$1/", + "~account2:s/sip:(.+)/$1/", + "~subject2:s/sip:(.+)/$1/", + "~destination2:s/sip:(.+)/$1/", + "~setuptime2:s/sip:(.+)/$1/", + "~answertime2:s/sip:(.+)/$1/", + "~duration2:s/sip:(.+)/$1/"); err != nil { + t.Error("Unexpected error", err.Error) + } else if !reflect.DeepEqual(edc2, dc2) { + t.Errorf("Expecting: %v, received: %v", edc2, dc2) + } +} + +func TestParseCfgDerivedCharging(t *testing.T) { + eFieldsCfg := []byte(`[derived_charging] +run_ids = run1, run2 +reqtype_fields = test1, test2 +direction_fields = test1, test2 +tenant_fields = test1, test2 +tor_fields = test1, test2 +account_fields = test1, test2 +subject_fields = test1, test2 +destination_fields = test1, test2 +setup_time_fields = test1, test2 +answer_time_fields = test1, test2 +duration_fields = test1, test2 +`) + edcs := DerivedChargers{ + &DerivedCharger{RunId: "run1", ReqTypeField: "test1", DirectionField: "test1", TenantField: "test1", TorField: "test1", + AccountField: "test1", SubjectField: "test1", DestinationField: "test1", SetupTimeField: "test1", AnswerTimeField: "test1", DurationField: "test1"}, + &DerivedCharger{RunId: "run2", ReqTypeField: "test2", DirectionField: "test2", TenantField: "test2", TorField: "test2", + AccountField: "test2", SubjectField: "test2", DestinationField: "test2", SetupTimeField: "test2", AnswerTimeField: "test2", DurationField: "test2"}} + if cfg, err := NewCGRConfigFromBytes(eFieldsCfg); err != nil { + t.Error("Could not parse the config", err.Error()) + } else if !reflect.DeepEqual(cfg.DerivedChargers, edcs) { + t.Errorf("Expecting: %v, received: %v", edcs, cfg.DerivedChargers) + } +} diff --git a/config/helpers.go b/config/helpers.go index b43d0eabc..190feaa46 100644 --- a/config/helpers.go +++ b/config/helpers.go @@ -19,7 +19,6 @@ along with this program. If not, see package config import ( - "code.google.com/p/goconf/conf" "errors" "strings" @@ -27,20 +26,17 @@ import ( ) // Adds support for slice values in config -func ConfigSlice(c *conf.ConfigFile, section, valName string) ([]string, error) { - sliceStr, errGet := c.GetString(section, valName) - if errGet != nil { - return nil, errGet - } - cfgValStrs := strings.Split(sliceStr, ",") // If need arrises, we can make the separator configurable +func ConfigSlice(cfgVal string) ([]string, error) { + cfgValStrs := strings.Split(cfgVal, ",") // If need arrises, we can make the separator configurable if len(cfgValStrs) == 1 && cfgValStrs[0] == "" { // Prevents returning iterable with empty value return []string{}, nil } - for _, elm := range cfgValStrs { + for idx, elm := range cfgValStrs { if elm == "" { //One empty element is presented when splitting empty string return nil, errors.New("Empty values in config slice") } + cfgValStrs[idx] = strings.TrimSpace(elm) // By default spaces are not removed so we do it here to avoid unpredicted results in config } return cfgValStrs, nil } diff --git a/config/test_data.txt b/config/test_data.txt index 21ad69960..1ca710e63 100644 --- a/config/test_data.txt +++ b/config/test_data.txt @@ -81,17 +81,6 @@ extra_fields = test # Field identifiers of the fields to add in extra fields s enabled = true # Starts Mediator service: . rater = test # Address where to reach the Rater: rater_reconnects = 99 # Number of reconnects to rater before giving up. -run_ids = test # Identifiers for each mediation run on CDRs -subject_fields = test # Name of subject fields to be used during mediation. Use index numbers in case of .csv cdrs. -reqtype_fields = test # Name of request type fields to be used during mediation. Use index number in case of .csv cdrs. -direction_fields = test # Name of direction fields to be used during mediation. Use index numbers in case of .csv cdrs. -tenant_fields = test # Name of tenant fields to be used during mediation. Use index numbers in case of .csv cdrs. -tor_fields = test # Name of tor fields to be used during mediation. Use index numbers in case of .csv cdrs. -account_fields = test # Name of account fields to be used during mediation. Use index numbers in case of .csv cdrs. -destination_fields = test # Name of destination fields to be used during mediation. Use index numbers in case of .csv cdrs. -setup_time_fields = test # Name of setup_time fields to be used during mediation. Use index numbers in case of .csv cdrs. -answer_time_fields = test # Name of answer_time fields to be used during mediation. Use index numbers in case of .csv cdrs. -duration_fields = test # Name of duration fields to be used during mediation. Use index numbers in case of .csv cdrs. [session_manager] enabled = true # Starts SessionManager service: . @@ -100,23 +89,25 @@ rater = test # Address where to reach the Rater. rater_reconnects = 99 # Number of reconnects to rater before giving up. debit_interval = 99 # Interval to perform debits on. max_call_duration = 99 # Maximum call duration a prepaid call can last -run_ids = test # Identifiers of additional sessions control. -reqtype_fields = test # Name of request type fields to be used during additional sessions control <""|*default|field_name>. -direction_fields = test # Name of direction fields to be used during additional sessions control <""|*default|field_name>. -tenant_fields = test # Name of tenant fields to be used during additional sessions control <""|*default|field_name>. -tor_fields = test # Name of tor fields to be used during additional sessions control <""|*default|field_name>. -account_fields = test # Name of account fields to be used during additional sessions control <""|*default|field_name>. -subject_fields = test # Name of fields to be used during additional sessions control <""|*default|field_name>. -destination_fields = test # Name of destination fields to be used during additional sessions control <""|*default|field_name>. -setup_time_fields = test # Name of setup_time fields to be used during additional sessions control <""|*default|field_name>. -answer_time_fields = test # Name of answer_time fields to be used during additional sessions control <""|*default|field_name>. -duration_fields = test # Name of duration fields to be used during additional sessions control <""|*default|field_name>. [freeswitch] server = test # Adress where to connect to FreeSWITCH socket. passwd = test # FreeSWITCH socket password. reconnects = 99 # Number of attempts on connect failure. +[derived_charging] +run_ids = test # Identifiers of additional sessions control. +reqtype_fields = test # Name of request type fields to be used during additional sessions control <""|*default|field_name>. +direction_fields = test # Name of direction fields to be used during additional sessions control <""|*default|field_name>. +tenant_fields = test # Name of tenant fields to be used during additional sessions control <""|*default|field_name>. +tor_fields = test # Name of tor fields to be used during additional sessions control <""|*default|field_name>. +account_fields = test # Name of account fields to be used during additional sessions control <""|*default|field_name>. +subject_fields = test # Name of fields to be used during additional sessions control <""|*default|field_name>. +destination_fields = test # Name of destination fields to be used during additional sessions control <""|*default|field_name>. +setup_time_fields = test # Name of setup_time fields to be used during additional sessions control <""|*default|field_name>. +answer_time_fields = test # Name of answer_time fields to be used during additional sessions control <""|*default|field_name>. +duration_fields = test # Name of duration fields to be used during additional sessions control <""|*default|field_name>. + [history_server] enabled = true # Starts History service: . history_dir = test # Location on disk where to store history files. diff --git a/data/conf/cgrates.cfg b/data/conf/cgrates.cfg index 3e2494f61..e076857eb 100644 --- a/data/conf/cgrates.cfg +++ b/data/conf/cgrates.cfg @@ -6,34 +6,34 @@ [global] # ratingdb_type = redis # Rating subsystem database: . -# ratingdb_host = 127.0.0.1 # Rating subsystem database host address. +# ratingdb_host = 127.0.0.1 # Rating subsystem database host address. # ratingdb_port = 6379 # Rating subsystem port to reach the database. # ratingdb_name = 10 # Rating subsystem database name to connect to. -# ratingdb_user = # Rating subsystem username to use when connecting to database. -# ratingdb_passwd = # Rating subsystem password to use when connecting to database. +# ratingdb_user = # Rating subsystem username to use when connecting to database. +# ratingdb_passwd = # Rating subsystem password to use when connecting to database. # accountdb_type = redis # Accounting subsystem database: . -# accountdb_host = 127.0.0.1 # Accounting subsystem database host address. +# accountdb_host = 127.0.0.1 # Accounting subsystem database host address. # accountdb_port = 6379 # Accounting subsystem port to reach the database. # accountdb_name = 11 # Accounting subsystem database name to connect to. -# accountdb_user = # Accounting subsystem username to use when connecting to database. +# accountdb_user = # Accounting subsystem username to use when connecting to database. # accountdb_passwd = # Accounting subsystem password to use when connecting to database. # stordb_type = mysql # Stor database type to use: # stordb_host = 127.0.0.1 # The host to connect to. Values that start with / are for UNIX domain sockets. # stordb_port = 3306 # The port to reach the logdb. # stordb_name = cgrates # The name of the log database to connect to. # stordb_user = cgrates # Username to use when connecting to stordb. -# stordb_passwd = CGRateS.org # Password to use when connecting to stordb. +# stordb_passwd = CGRateS.org # Password to use when connecting to stordb. # dbdata_encoding = msgpack # The encoding used to store object data in strings: -# rpc_json_listen = 127.0.0.1:2012 # RPC JSON listening address -# rpc_gob_listen = 127.0.0.1:2013 # RPC GOB listening address -# http_listen = 127.0.0.1:2080 # HTTP listening address +# rpc_json_listen = 127.0.0.1:2012 # RPC JSON listening address +# rpc_gob_listen = 127.0.0.1:2013 # RPC GOB listening address +# http_listen = 127.0.0.1:2080 # HTTP listening address # default_reqtype = rated # Default request type to consider when missing from requests: <""|prepaid|postpaid|pseudoprepaid|rated>. # default_tor = call # Default Type of Record to consider when missing from requests. -# default_tenant = cgrates.org # Default Tenant to consider when missing from requests. +# default_tenant = cgrates.org # Default Tenant to consider when missing from requests. # default_subject = cgrates # Default rating Subject to consider when missing from requests. # rounding_method = *middle # Rounding method for floats/costs: <*up|*middle|*down> # rounding_decimals = 4 # Number of decimals to round float/costs at -# xmlcfg_path = # Path towards additional config defined in xml file +# xmlcfg_path = # Path towards additional config defined in xml file [balancer] # enabled = false # Start Balancer service: . @@ -51,88 +51,79 @@ # mediator = # Address where to reach the Mediator. Empty for disabling mediation. <""|internal> [cdre] -# cdr_format = csv # Exported CDRs format -# mask_destination_id = # Destination id containing called addresses to be masked on export -# mask_length = 0 # Length of the destination suffix to be masked -# cost_shift_digits = 0 # Shift cost on export with the number of digits digits defined here (eg: convert from Eur to cent). +# cdr_format = csv # Exported CDRs format +# mask_destination_id = # Destination id containing called addresses to be masked on export +# mask_length = 0 # Length of the destination suffix to be masked +# cost_shift_digits = 0 # Shift cost on export with the number of digits digits defined here (eg: convert from Eur to cent). # export_dir = /var/log/cgrates/cdrexport/csv # Path where the exported CDRs will be placed # export_template = cgrid,mediation_runid,accid,cdrhost,reqtype,direction,tenant,tor,account,subject,destination,setup_time,answer_time,duration,cost - # Exported fields template <""|fld1,fld2|*xml:instance_name> + # Exported fields template <""|fld1,fld2|*xml:instance_name> [cdrc] -# enabled = false # Enable CDR client functionality -# cdrs = internal # Address where to reach CDR server. -# cdrs_method = http_cgr # Mechanism to use when posting CDRs on server -# run_delay = 0 # Sleep interval in seconds between consecutive runs, 0 to use automation via inotify -# cdr_type = csv # CDR file format . +# enabled = false # Enable CDR client functionality +# cdrs = internal # Address where to reach CDR server. +# cdrs_method = http_cgr # Mechanism to use when posting CDRs on server +# run_delay = 0 # Sleep interval in seconds between consecutive runs, 0 to use automation via inotify +# cdr_type = csv # CDR file format . # cdr_in_dir = /var/log/cgrates/cdrc/in # Absolute path towards the directory where the CDRs are stored. # cdr_out_dir = /var/log/cgrates/cdrc/out # Absolute path towards the directory where processed CDRs will be moved. -# cdr_source_id = freeswitch_csv # Free form field, tag identifying the source of the CDRs within CGRS database. -# accid_field = 0 # Accounting id field identifier. Use index number in case of .csv cdrs. -# reqtype_field = 1 # Request type field identifier. Use index number in case of .csv cdrs. -# direction_field = 2 # Direction field identifier. Use index numbers in case of .csv cdrs. -# tenant_field = 3 # Tenant field identifier. Use index numbers in case of .csv cdrs. -# tor_field = 4 # Type of Record field identifier. Use index numbers in case of .csv cdrs. -# account_field = 5 # Account field identifier. Use index numbers in case of .csv cdrs. -# subject_field = 6 # Subject field identifier. Use index numbers in case of .csv CDRs. -# destination_field = 7 # Destination field identifier. Use index numbers in case of .csv cdrs. -# setup_time_field = 8 # Setup time field identifier. Use index numbers in case of .csv cdrs. -# answer_time_field = 9 # Answer time field identifier. Use index numbers in case of .csv cdrs. -# duration_field = 10 # Duration field identifier. Use index numbers in case of .csv cdrs. -# extra_fields = # Extra fields identifiers. For .csv, format: :[...,:] +# cdr_source_id = freeswitch_csv # Free form field, tag identifying the source of the CDRs within CGRS database. +# accid_field = 0 # Accounting id field identifier. Use index number in case of .csv cdrs. +# reqtype_field = 1 # Request type field identifier. Use index number in case of .csv cdrs. +# direction_field = 2 # Direction field identifier. Use index numbers in case of .csv cdrs. +# tenant_field = 3 # Tenant field identifier. Use index numbers in case of .csv cdrs. +# tor_field = 4 # Type of Record field identifier. Use index numbers in case of .csv cdrs. +# account_field = 5 # Account field identifier. Use index numbers in case of .csv cdrs. +# subject_field = 6 # Subject field identifier. Use index numbers in case of .csv CDRs. +# destination_field = 7 # Destination field identifier. Use index numbers in case of .csv cdrs. +# setup_time_field = 8 # Setup time field identifier. Use index numbers in case of .csv cdrs. +# answer_time_field = 9 # Answer time field identifier. Use index numbers in case of .csv cdrs. +# duration_field = 10 # Duration field identifier. Use index numbers in case of .csv cdrs. +# extra_fields = # Extra fields identifiers. For .csv, format: :[...,:] [mediator] # enabled = false # Starts Mediator service: . # rater = internal # Address where to reach the Rater: -# rater_reconnects = 3 # Number of reconnects to rater before giving up. -# run_ids = # Identifiers of each extra mediation to run on CDRs -# reqtype_fields = # Name of request type fields to be used during extra mediation. Use index number in case of .csv cdrs. -# direction_fields = # Name of direction fields to be used during extra mediation. Use index numbers in case of .csv cdrs. -# tenant_fields = # Name of tenant fields to be used during extra mediation. Use index numbers in case of .csv cdrs. -# tor_fields = # Name of tor fields to be used during extra mediation. Use index numbers in case of .csv cdrs. -# account_fields = # Name of account fields to be used during extra mediation. Use index numbers in case of .csv cdrs. -# subject_fields = # Name of fields to be used during extra mediation. Use index numbers in case of .csv cdrs. -# destination_fields = # Name of destination fields to be used during extra mediation. Use index numbers in case of .csv cdrs. -# setup_time_fields = # Name of setup_time fields to be used during extra mediation. Use index numbers in case of .csv cdrs. -# answer_time_fields = # Name of answer_time fields to be used during extra mediation. Use index numbers in case of .csv cdrs. -# duration_fields = # Name of duration fields to be used during extra mediation. Use index numbers in case of .csv cdrs. +# rater_reconnects = 3 # Number of reconnects to rater before giving up. [session_manager] # enabled = false # Starts SessionManager service: . -# switch_type = freeswitch # Defines the type of switch behind: . +# switch_type = freeswitch # Defines the type of switch behind: . # rater = internal # Address where to reach the Rater. -# rater_reconnects = 3 # Number of reconnects to rater before giving up. -# debit_interval = 10 # Interval to perform debits on. -# max_call_duration = 3h # Maximum call duration a prepaid call can last +# rater_reconnects = 3 # Number of reconnects to rater before giving up. +# debit_interval = 10 # Interval to perform debits on. +# max_call_duration = 3h # Maximum call duration a prepaid call can last + +[freeswitch] +# server = 127.0.0.1:8021 # Adress where to connect to FreeSWITCH socket. +# passwd = ClueCon # FreeSWITCH socket password. +# reconnects = 5 # Number of attempts on connect failure. + +[derived_charging] # run_ids = # Identifiers of additional sessions control. # reqtype_fields = # Name of request type fields to be used during additional sessions control <""|*default|field_name>. -# direction_fields = # Name of direction fields to be used during additional sessions control <""|*default|field_name>. +# direction_fields = # Name of direction fields to be used during additional sessions control <""|*default|field_name>. # tenant_fields = # Name of tenant fields to be used during additional sessions control <""|*default|field_name>. # tor_fields = # Name of tor fields to be used during additional sessions control <""|*default|field_name>. # account_fields = # Name of account fields to be used during additional sessions control <""|*default|field_name>. # subject_fields = # Name of fields to be used during additional sessions control <""|*default|field_name>. -# destination_fields = # Name of destination fields to be used during additional sessions control <""|*default|field_name>. -# setup_time_fields = # Name of setup_time fields to be used during additional sessions control <""|*default|field_name>. -# answer_time_fields = # Name of answer_time fields to be used during additional sessions control <""|*default|field_name>. -# duration_fields = # Name of duration fields to be used during additional sessions control <""|*default|field_name>. - -[freeswitch] -# server = 127.0.0.1:8021 # Adress where to connect to FreeSWITCH socket. -# passwd = ClueCon # FreeSWITCH socket password. -# reconnects = 5 # Number of attempts on connect failure. +# destination_fields = # Name of destination fields to be used during additional sessions control <""|*default|field_name>. +# setup_time_fields = # Name of setup_time fields to be used during additional sessions control <""|*default|field_name>. +# answer_time_fields = # Name of answer_time fields to be used during additional sessions control <""|*default|field_name>. +# duration_fields = # Name of duration fields to be used during additional sessions control <""|*default|field_name>. [history_server] -# enabled = false # Starts History service: . +# enabled = false # Starts History service: . # history_dir = /var/log/cgrates/history # Location on disk where to store history files. -# save_interval = 1s # Interval to save changed cache into .git archive +# save_interval = 1s # Interval to save changed cache into .git archive [history_agent] # enabled = false # Starts History as a client: . # server = internal # Address where to reach the master history server: [mailer] -# server = localhost # The server to use when sending emails out -# auth_user = cgrates # Authenticate to email server using this user -# auth_passwd = CGRateS.org # Authenticate to email server with this password +# server = localhost # The server to use when sending emails out +# auth_user = cgrates # Authenticate to email server using this user +# auth_passwd = CGRateS.org # Authenticate to email server with this password # from_address = cgr-mailer@localhost.localdomain # From address used when sending emails out diff --git a/data/storage/mysql/create_mediator_tables.sql b/data/storage/mysql/create_mediator_tables.sql index 69278db09..da820dcda 100644 --- a/data/storage/mysql/create_mediator_tables.sql +++ b/data/storage/mysql/create_mediator_tables.sql @@ -1,6 +1,6 @@ -- --- Table structure for table `rater_cdrs` +-- Table structure for table `rated_cdrs` -- DROP TABLE IF EXISTS `rated_cdrs`; CREATE TABLE `rated_cdrs` ( diff --git a/utils/consts.go b/utils/consts.go index 612821ca8..90b0bc19a 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -66,7 +66,7 @@ const ( COMMENT_CHAR = '#' CSV_SEP = ',' FALLBACK_SEP = ';' - REGEXP_SEP = "~" + REGEXP_PREFIX = "~" JSON = "json" MSGPACK = "msgpack" CSV_LOAD = "CSVLOAD" @@ -99,6 +99,7 @@ const ( CDRE = "cdre" MASK_CHAR = "*" CONCATENATED_KEY_SEP = ":" + META_DEFAULT = "*default" ) var ( diff --git a/utils/rsrfield.go b/utils/rsrfield.go index baa28efe1..d7566ab95 100644 --- a/utils/rsrfield.go +++ b/utils/rsrfield.go @@ -43,7 +43,7 @@ func NewRSRField(fldStr string) (*RSRField, error) { if len(fldStr) == 0 { return nil, nil } - if !strings.HasPrefix(fldStr, REGEXP_SEP) { + if !strings.HasPrefix(fldStr, REGEXP_PREFIX) { return &RSRField{Id: fldStr}, nil } if fldId, reSrcRepl, err := ParseSearchReplaceFromFieldRule(fldStr); err != nil {