From 2a253892c59a004501e555756bee850dc2a7de42 Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 23 Apr 2014 20:48:43 +0200 Subject: [PATCH] Refactoring the code around derived charging to support integration with responder for internal requests, some of the copyright headers updated with new slogan --- README.md | 2 +- apier/accounts.go | 4 +- apier/apier.go | 4 +- config/config.go | 73 ++++---- config/config_test.go | 6 +- config/derivedchargers.go | 209 ---------------------- config/helpers.go | 80 ++++++++- config/helpers_test.go | 30 +++- config/test_data.txt | 1 + data/conf/cgrates.cfg | 6 +- engine/loader_csv.go | 11 +- engine/loader_csv_test.go | 7 +- engine/loader_db.go | 5 +- engine/responder.go | 22 ++- engine/storage_interface.go | 5 +- engine/storage_map.go | 11 +- engine/storage_redis.go | 11 +- engine/storage_redis_local_test.go | 6 +- importer.go | 4 +- sessionmanager/fssessionmanager.go | 4 +- sessionmanager/sessionmanager.go | 4 +- utils/apitpdata.go | 4 + utils/derivedchargers.go | 136 ++++++++++++++ {config => utils}/derivedchargers_test.go | 53 ++---- utils/researchreplace.go | 3 + utils/researchreplace_test.go | 10 ++ utils/storedcdr_test.go | 3 + 27 files changed, 381 insertions(+), 333 deletions(-) delete mode 100644 config/derivedchargers.go create mode 100644 utils/derivedchargers.go rename {config => utils}/derivedchargers_test.go (60%) diff --git a/README.md b/README.md index e45d1f1c5..215409f56 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Rating system for Telecom & ISP environments ## +## Real-time Charging System for Telecom & ISP environments ## [![Build Status](https://drone.io/github.com/cgrates/cgrates/status.png)](https://drone.io/github.com/cgrates/cgrates/latest) [![Build Status](https://secure.travis-ci.org/cgrates/cgrates.png)](http://travis-ci.org/cgrates/cgrates) diff --git a/apier/accounts.go b/apier/accounts.go index de4bd9fc7..92dbd5adb 100644 --- a/apier/accounts.go +++ b/apier/accounts.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/apier/apier.go b/apier/apier.go index b1da3d780..23bdb1f4f 100644 --- a/apier/apier.go +++ b/apier/apier.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/config/config.go b/config/config.go index 106ef67ab..b7d24ff49 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -119,37 +119,38 @@ type CGRConfig struct { CdrcExtraFields []string // Extra fields to extract, special format in case of .csv "field1:index1,field2:index2" SMEnabled bool SMSwitchType string - SMRater string // address where to access rater. Can be internal, direct rater address or the address of a balancer - 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 - MediatorEnabled bool // Starts Mediator service: . - MediatorRater string // Address where to reach the Rater: - MediatorRaterReconnects int // Number of reconnects to rater before giving up. - MediatorRunIds []string // Identifiers for each mediation run on CDRs - MediatorReqTypeFields []string // Name of request type fields to be used during mediation. Use index number in case of .csv cdrs. - MediatorDirectionFields []string // Name of direction fields to be used during mediation. Use index numbers in case of .csv cdrs. - MediatorTenantFields []string // Name of tenant fields to be used during mediation. Use index numbers in case of .csv cdrs. - MediatorTORFields []string // Name of tor fields to be used during mediation. Use index numbers in case of .csv cdrs. - MediatorAccountFields []string // Name of account fields to be used during mediation. Use index numbers in case of .csv cdrs. - MediatorSubjectFields []string // Name of subject fields to be used during mediation. Use index numbers in case of .csv cdrs. - MediatorDestFields []string // Name of destination fields to be used during mediation. Use index numbers in case of .csv cdrs. - 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 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 - HistoryAgentEnabled bool // Starts History as an agent: . - HistoryServer string // Address where to reach the master history server: - HistoryServerEnabled bool // Starts History as server: . - HistoryDir string // Location on disk where to store history files. - HistorySaveInterval time.Duration // The timout duration between history writes - MailerServer string // The server to use when sending emails out - MailerAuthUser string // Authenticate to email server using this user - MailerAuthPass string // Authenticate to email server with this password - MailerFromAddr string // From address used when sending emails out + SMRater string // address where to access rater. Can be internal, direct rater address or the address of a balancer + 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 + MediatorEnabled bool // Starts Mediator service: . + MediatorRater string // Address where to reach the Rater: + MediatorRaterReconnects int // Number of reconnects to rater before giving up. + MediatorRunIds []string // Identifiers for each mediation run on CDRs + MediatorReqTypeFields []string // Name of request type fields to be used during mediation. Use index number in case of .csv cdrs. + MediatorDirectionFields []string // Name of direction fields to be used during mediation. Use index numbers in case of .csv cdrs. + MediatorTenantFields []string // Name of tenant fields to be used during mediation. Use index numbers in case of .csv cdrs. + MediatorTORFields []string // Name of tor fields to be used during mediation. Use index numbers in case of .csv cdrs. + MediatorAccountFields []string // Name of account fields to be used during mediation. Use index numbers in case of .csv cdrs. + MediatorSubjectFields []string // Name of subject fields to be used during mediation. Use index numbers in case of .csv cdrs. + MediatorDestFields []string // Name of destination fields to be used during mediation. Use index numbers in case of .csv cdrs. + 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 utils.DerivedChargers // System wide derived chargers, added to the account level ones + CombinedDerivedChargers bool // Combine accounts specific derived_chargers with server configured + FreeswitchServer string // freeswitch address host:port + FreeswitchPass string // FS socket password + FreeswitchReconnects int // number of times to attempt reconnect after connect fails + HistoryAgentEnabled bool // Starts History as an agent: . + HistoryServer string // Address where to reach the master history server: + HistoryServerEnabled bool // Starts History as server: . + HistoryDir string // Location on disk where to store history files. + HistorySaveInterval time.Duration // The timout duration between history writes + MailerServer string // The server to use when sending emails out + MailerAuthUser string // Authenticate to email server using this user + MailerAuthPass string // Authenticate to email server with this password + MailerFromAddr string // From address used when sending emails out } func (self *CGRConfig) setDefaults() error { @@ -217,7 +218,8 @@ func (self *CGRConfig) setDefaults() error { self.MediatorEnabled = false self.MediatorRater = "internal" self.MediatorRaterReconnects = 3 - self.DerivedChargers = make(DerivedChargers, 0) + self.DerivedChargers = make(utils.DerivedChargers, 0) + self.CombinedDerivedChargers = true self.SMEnabled = false self.SMSwitchType = FS self.SMRater = "internal" @@ -573,6 +575,9 @@ func loadConfig(c *conf.ConfigFile) (*CGRConfig, error) { if cfg.DerivedChargers, err = ParseCfgDerivedCharging(c); err != nil { return nil, err } + if hasOpt = c.HasOption("derived_charging", "combined_chargers"); hasOpt { + cfg.CombinedDerivedChargers, _ = c.GetBool("derived_charging", "combined_chargers") + } 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 188db0afa..db2ec1093 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -119,7 +119,8 @@ func TestDefaults(t *testing.T) { eCfg.FreeswitchServer = "127.0.0.1:8021" eCfg.FreeswitchPass = "ClueCon" eCfg.FreeswitchReconnects = 5 - eCfg.DerivedChargers = make(DerivedChargers, 0) + eCfg.DerivedChargers = make(utils.DerivedChargers, 0) + eCfg.CombinedDerivedChargers = true eCfg.HistoryAgentEnabled = false eCfg.HistoryServer = "internal" eCfg.HistoryServerEnabled = false @@ -252,8 +253,9 @@ func TestConfigFromFile(t *testing.T) { eCfg.FreeswitchServer = "test" eCfg.FreeswitchPass = "test" eCfg.FreeswitchReconnects = 99 - eCfg.DerivedChargers = DerivedChargers{&DerivedCharger{RunId: "test", ReqTypeField: "test", DirectionField: "test", TenantField: "test", + eCfg.DerivedChargers = utils.DerivedChargers{&utils.DerivedCharger{RunId: "test", ReqTypeField: "test", DirectionField: "test", TenantField: "test", TorField: "test", AccountField: "test", SubjectField: "test", DestinationField: "test", SetupTimeField: "test", AnswerTimeField: "test", DurationField: "test"}} + eCfg.CombinedDerivedChargers = true eCfg.HistoryAgentEnabled = true eCfg.HistoryServer = "test" eCfg.HistoryServerEnabled = true diff --git a/config/derivedchargers.go b/config/derivedchargers.go deleted file mode 100644 index b1769d847..000000000 --- a/config/derivedchargers.go +++ /dev/null @@ -1,209 +0,0 @@ -/* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2012-2014 ITsysCOM GmbH - -This program is free software: you can Storagetribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITH*out ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -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 - 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 - -// Precheck that RunId is unique -func (dcs DerivedChargers) Append(dc *DerivedCharger) (DerivedChargers, error) { - if dc.RunId == utils.DEFAULT_RUNID { - return nil, errors.New("Reserved RunId") - } - for _, dcLocal := range dcs { - if dcLocal.RunId == dc.RunId { - return nil, errors.New("Duplicated RunId") - } - } - 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/helpers.go b/config/helpers.go index 190feaa46..258c55384 100644 --- a/config/helpers.go +++ b/config/helpers.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +19,7 @@ along with this program. If not, see package config import ( + "code.google.com/p/goconf/conf" "errors" "strings" @@ -60,3 +61,78 @@ func ParseRSRFields(configVal string) ([]*utils.RSRField, error) { } return rsrFields, nil } + +// Parse the configuration file and returns utils.DerivedChargers instance if no errors +func ParseCfgDerivedCharging(c *conf.ConfigFile) (dcs utils.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(utils.DerivedChargers, 0) + for runIdx, runId := range runIds { + dc, err := utils.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/helpers_test.go b/config/helpers_test.go index 0c9081b4e..29fd6c257 100644 --- a/config/helpers_test.go +++ b/config/helpers_test.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,3 +37,29 @@ func TestParseRSRFields(t *testing.T) { t.Errorf("Unexpected value of parsed fields") } } + +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 := utils.DerivedChargers{ + &utils.DerivedCharger{RunId: "run1", ReqTypeField: "test1", DirectionField: "test1", TenantField: "test1", TorField: "test1", + AccountField: "test1", SubjectField: "test1", DestinationField: "test1", SetupTimeField: "test1", AnswerTimeField: "test1", DurationField: "test1"}, + &utils.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/test_data.txt b/config/test_data.txt index 1ca710e63..206d684e1 100644 --- a/config/test_data.txt +++ b/config/test_data.txt @@ -107,6 +107,7 @@ destination_fields = test # Name of destination fields to be used during additio 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>. +combined_chargers = true # Combine accounts specific derived_chargers with server configured ones . [history_server] enabled = true # Starts History service: . diff --git a/data/conf/cgrates.cfg b/data/conf/cgrates.cfg index e076857eb..aa7255655 100644 --- a/data/conf/cgrates.cfg +++ b/data/conf/cgrates.cfg @@ -1,8 +1,8 @@ -# CGRateS Configuration file +# Real-time Charging System for Telecom & ISP environments +# Copyright (C) 2012-2014 ITsysCOM GmbH # # This file contains the default configuration hardcoded into CGRateS. # This is what you get when you load CGRateS with an empty configuration file. -# [global] must exist in all files, rest of the configuration is inter-changeable. [global] # ratingdb_type = redis # Rating subsystem database: . @@ -58,7 +58,6 @@ # 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> - [cdrc] # enabled = false # Enable CDR client functionality # cdrs = internal # Address where to reach CDR server. @@ -111,6 +110,7 @@ # 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>. +# combined_chargers = true # Combine accounts specific derived_chargers with server configured ones . [history_server] # enabled = false # Starts History service: . diff --git a/engine/loader_csv.go b/engine/loader_csv.go index f940f2a1e..2df37fb32 100644 --- a/engine/loader_csv.go +++ b/engine/loader_csv.go @@ -27,7 +27,6 @@ import ( "strconv" "strings" - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) @@ -51,7 +50,7 @@ type CSVReader struct { ratingPlans map[string]*RatingPlan ratingProfiles map[string]*RatingProfile sharedGroups map[string]*SharedGroup - derivedChargers map[string]config.DerivedChargers + derivedChargers map[string]utils.DerivedChargers // file names destinationsFn, ratesFn, destinationratesFn, timingsFn, destinationratetimingsFn, ratingprofilesFn, sharedgroupsFn, actionsFn, actiontimingsFn, actiontriggersFn, accountactionsFn, derivedChargersFn string @@ -74,7 +73,7 @@ func NewFileCSVReader(dataStorage RatingStorage, accountingStorage AccountingSto c.ratingPlans = make(map[string]*RatingPlan) c.ratingProfiles = make(map[string]*RatingProfile) c.sharedGroups = make(map[string]*SharedGroup) - c.derivedChargers = make(map[string]config.DerivedChargers) + c.derivedChargers = make(map[string]utils.DerivedChargers) c.readerFunc = openFileCSVReader c.rpAliases = make(map[string]string) c.accAliases = make(map[string]string) @@ -753,10 +752,10 @@ func (csvr *CSVReader) LoadDerivedChargers() (err error) { defer fp.Close() } for record, err := csvReader.Read(); err == nil; record, err = csvReader.Read() { - tag := utils.ConcatenatedKey(record[0], record[1], record[2], record[3], record[4]) + tag := utils.DerivedChargersKey(record[0], record[1], record[2], record[3], record[4]) _, found := csvr.derivedChargers[tag] if found { - if csvr.derivedChargers[tag], err = csvr.derivedChargers[tag].Append(&config.DerivedCharger{ + if csvr.derivedChargers[tag], err = csvr.derivedChargers[tag].Append(&utils.DerivedCharger{ RunId: ValueOrDefault(record[5], "*default"), ReqTypeField: ValueOrDefault(record[6], "*default"), DirectionField: ValueOrDefault(record[7], "*default"), @@ -775,7 +774,7 @@ func (csvr *CSVReader) LoadDerivedChargers() (err error) { if record[5] == utils.DEFAULT_RUNID { return errors.New("Reserved RunId") } - csvr.derivedChargers[tag] = config.DerivedChargers{&config.DerivedCharger{ + csvr.derivedChargers[tag] = utils.DerivedChargers{&utils.DerivedCharger{ RunId: ValueOrDefault(record[5], "*default"), ReqTypeField: ValueOrDefault(record[6], "*default"), DirectionField: ValueOrDefault(record[7], "*default"), diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 2cfa863cc..412051f0b 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -23,7 +23,6 @@ import ( "testing" "time" - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) @@ -834,10 +833,10 @@ func TestLoadDerivedChargers(t *testing.T) { if len(csvr.derivedChargers) != 2 { t.Error("Failed to load derivedChargers: ", csvr.derivedChargers) } - expCharger1 := config.DerivedChargers{ - &config.DerivedCharger{RunId: "extra1", ReqTypeField: "^prepaid", DirectionField: "*default", TenantField: "*default", TorField: "*default", + expCharger1 := utils.DerivedChargers{ + &utils.DerivedCharger{RunId: "extra1", ReqTypeField: "^prepaid", DirectionField: "*default", TenantField: "*default", TorField: "*default", AccountField: "rif", SubjectField: "rif", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", DurationField: "*default"}, - &config.DerivedCharger{RunId: "extra2", ReqTypeField: "*default", DirectionField: "*default", TenantField: "*default", TorField: "*default", + &utils.DerivedCharger{RunId: "extra2", ReqTypeField: "*default", DirectionField: "*default", TenantField: "*default", TorField: "*default", AccountField: "ivo", SubjectField: "ivo", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", DurationField: "*default"}, } keyCharger1 := utils.ConcatenatedKey("cgrates.org", "call", "*out", "dan", "dan") diff --git a/engine/loader_db.go b/engine/loader_db.go index d5baee716..890bab2a8 100644 --- a/engine/loader_db.go +++ b/engine/loader_db.go @@ -24,7 +24,6 @@ import ( "log" "strings" - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) @@ -48,7 +47,7 @@ type DbReader struct { ratingPlans map[string]*RatingPlan ratingProfiles map[string]*RatingProfile sharedGroups map[string]*SharedGroup - derivedChargers map[string]config.DerivedChargers + derivedChargers map[string]utils.DerivedChargers } func NewDbReader(storDB LoadStorage, ratingDb RatingStorage, accountDb AccountingStorage, tpid string) *DbReader { @@ -66,7 +65,7 @@ func NewDbReader(storDB LoadStorage, ratingDb RatingStorage, accountDb Accountin c.rpAliases = make(map[string]string) c.accAliases = make(map[string]string) c.accountActions = make(map[string]*Account) - c.derivedChargers = make(map[string]config.DerivedChargers) + c.derivedChargers = make(map[string]utils.DerivedChargers) return c } diff --git a/engine/responder.go b/engine/responder.go index b050fb298..75907f2ed 100644 --- a/engine/responder.go +++ b/engine/responder.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,6 +28,8 @@ import ( "time" "github.com/cgrates/cgrates/balancer2go" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/utils" ) type Responder struct { @@ -114,6 +116,17 @@ func (rs *Responder) GetMaxSessionTime(arg CallDescriptor, reply *float64) (err return } +func (rs *Responder) DerivedChargers(attrs utils.AttrDerivedChargers, dcs *utils.DerivedChargers) error { + // ToDo: Make it work with balancer if needed + + if dcsH, err := HandleGetDerivedChargers(accountingStorage, config.CgrConfig(), attrs); err != nil { + return err + } else if dcsH != nil { + *dcs = dcsH + } + return nil +} + func (rs *Responder) FlushCache(arg CallDescriptor, reply *float64) (err error) { if rs.Bal != nil { *reply, err = rs.callMethod(&arg, "Responder.FlushCache") @@ -319,6 +332,7 @@ type Connector interface { MaxDebit(CallDescriptor, *CallCost) error RefundIncrements(CallDescriptor, *float64) error GetMaxSessionTime(CallDescriptor, *float64) error + DerivedChargers(utils.AttrDerivedChargers, utils.DerivedChargers) error } type RPCClientConnector struct { @@ -344,3 +358,7 @@ func (rcc *RPCClientConnector) RefundIncrements(cd CallDescriptor, resp *float64 func (rcc *RPCClientConnector) GetMaxSessionTime(cd CallDescriptor, resp *float64) error { return rcc.Client.Call("Responder.GetMaxSessionTime", cd, resp) } + +func (rcc *RPCClientConnector) DerivedChargers(attrs utils.AttrDerivedChargers, dcs utils.DerivedChargers) error { + return rcc.Client.Call("ApierV1.DerivedChargers", attrs, dcs) +} diff --git a/engine/storage_interface.go b/engine/storage_interface.go index d62d25979..c0c6aa468 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -23,7 +23,6 @@ import ( "encoding/gob" "encoding/json" - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" "github.com/ugorji/go/codec" "labix.org/v2/mgo/bson" @@ -102,8 +101,8 @@ type AccountingStorage interface { GetActionTimings(string) (ActionPlan, error) SetActionTimings(string, ActionPlan) error GetAllActionTimings() (map[string]ActionPlan, error) - GetDerivedChargers(string, bool) (config.DerivedChargers, error) - SetDerivedChargers(string, config.DerivedChargers) error + GetDerivedChargers(string, bool) (utils.DerivedChargers, error) + SetDerivedChargers(string, utils.DerivedChargers) error } type CdrStorage interface { diff --git a/engine/storage_map.go b/engine/storage_map.go index 7589a5f6f..29bdeec12 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriems World -Copyright (C) 2013 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,7 +29,6 @@ import ( "time" "github.com/cgrates/cgrates/cache2go" - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" ) @@ -438,10 +437,10 @@ func (ms *MapStorage) GetAllActionTimings() (ats map[string]ActionPlan, err erro return } -func (ms *MapStorage) GetDerivedChargers(key string, checkDb bool) (dcs config.DerivedChargers, err error) { +func (ms *MapStorage) GetDerivedChargers(key string, checkDb bool) (dcs utils.DerivedChargers, err error) { key = DERIVEDCHARGERS_PREFIX + key if x, err := cache2go.GetCached(key); err == nil { - return x.(config.DerivedChargers), nil + return x.(utils.DerivedChargers), nil } if !checkDb { return nil, errors.New(utils.ERR_NOT_FOUND) @@ -455,7 +454,7 @@ func (ms *MapStorage) GetDerivedChargers(key string, checkDb bool) (dcs config.D return } -func (ms *MapStorage) SetDerivedChargers(key string, dcs config.DerivedChargers) error { +func (ms *MapStorage) SetDerivedChargers(key string, dcs utils.DerivedChargers) error { result, err := ms.ms.Marshal(dcs) ms.dict[DERIVEDCHARGERS_PREFIX+key] = result return err diff --git a/engine/storage_redis.go b/engine/storage_redis.go index 991b96bdb..6cdd1444f 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,7 +25,6 @@ import ( "fmt" "github.com/cgrates/cgrates/cache2go" - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/utils" "github.com/hoisie/redis" @@ -544,10 +543,10 @@ func (rs *RedisStorage) GetAllActionTimings() (ats map[string]ActionPlan, err er return } -func (rs *RedisStorage) GetDerivedChargers(key string, checkDb bool) (dcs config.DerivedChargers, err error) { +func (rs *RedisStorage) GetDerivedChargers(key string, checkDb bool) (dcs utils.DerivedChargers, err error) { key = DERIVEDCHARGERS_PREFIX + key if x, err := cache2go.GetCached(key); err == nil { - return x.(config.DerivedChargers), nil + return x.(utils.DerivedChargers), nil } if !checkDb { return nil, errors.New(utils.ERR_NOT_FOUND) @@ -560,7 +559,7 @@ func (rs *RedisStorage) GetDerivedChargers(key string, checkDb bool) (dcs config return dcs, err } -func (rs *RedisStorage) SetDerivedChargers(key string, dcs config.DerivedChargers) (err error) { +func (rs *RedisStorage) SetDerivedChargers(key string, dcs utils.DerivedChargers) (err error) { if len(dcs) == 0 { _, err = rs.db.Del(DERIVEDCHARGERS_PREFIX + key) return err diff --git a/engine/storage_redis_local_test.go b/engine/storage_redis_local_test.go index cea287ccd..dd0aaa419 100644 --- a/engine/storage_redis_local_test.go +++ b/engine/storage_redis_local_test.go @@ -56,10 +56,10 @@ func TestSetGetDerivedCharges(t *testing.T) { return } keyCharger1 := utils.ConcatenatedKey("cgrates.org", "call", "*out", "dan", "dan") - charger1 := config.DerivedChargers{ - &config.DerivedCharger{RunId: "extra1", ReqTypeField: "^prepaid", DirectionField: "*default", TenantField: "*default", TorField: "*default", + charger1 := utils.DerivedChargers{ + &utils.DerivedCharger{RunId: "extra1", ReqTypeField: "^prepaid", DirectionField: "*default", TenantField: "*default", TorField: "*default", AccountField: "rif", SubjectField: "rif", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", DurationField: "*default"}, - &config.DerivedCharger{RunId: "extra2", ReqTypeField: "*default", DirectionField: "*default", TenantField: "*default", TorField: "*default", + &utils.DerivedCharger{RunId: "extra2", ReqTypeField: "*default", DirectionField: "*default", TenantField: "*default", TorField: "*default", AccountField: "ivo", SubjectField: "ivo", DestinationField: "*default", SetupTimeField: "*default", AnswerTimeField: "*default", DurationField: "*default"}, } if err := rds.SetDerivedChargers(keyCharger1, charger1); err != nil { diff --git a/importer.go b/importer.go index 6d7c8a6ff..83695b92f 100644 --- a/importer.go +++ b/importer.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/sessionmanager/fssessionmanager.go b/sessionmanager/fssessionmanager.go index 69875abb3..fb1401c79 100644 --- a/sessionmanager/fssessionmanager.go +++ b/sessionmanager/fssessionmanager.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/sessionmanager/sessionmanager.go b/sessionmanager/sessionmanager.go index f00042496..2367c0a6b 100644 --- a/sessionmanager/sessionmanager.go +++ b/sessionmanager/sessionmanager.go @@ -1,6 +1,6 @@ /* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 9ef7beb19..76bb55342 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -377,3 +377,7 @@ type AttrLoadTpFromFolder struct { type AttrGetDestination struct { Id string } + +type AttrDerivedChargers struct { + Tenant, Tor, Direction, Account, Subject string +} diff --git a/utils/derivedchargers.go b/utils/derivedchargers.go new file mode 100644 index 000000000..908cc2960 --- /dev/null +++ b/utils/derivedchargers.go @@ -0,0 +1,136 @@ +/* +Real-time Charging System for Telecom & ISP environments +Copyright (C) 2012-2014 ITsysCOM GmbH + +This program is free software: you can Storagetribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITH*out ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package utils + +import ( + "errors" + "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, REGEXP_PREFIX) { + if dc.rsrReqTypeField, err = NewRSRField(dc.ReqTypeField); err != nil { + return nil, err + } + } + dc.DirectionField = dirFld + if strings.HasPrefix(dc.DirectionField, REGEXP_PREFIX) { + if dc.rsrDirectionField, err = NewRSRField(dc.DirectionField); err != nil { + return nil, err + } + } + dc.TenantField = tenantFld + if strings.HasPrefix(dc.TenantField, REGEXP_PREFIX) { + if dc.rsrTenantField, err = NewRSRField(dc.TenantField); err != nil { + return nil, err + } + } + dc.TorField = totFld + if strings.HasPrefix(dc.TorField, REGEXP_PREFIX) { + if dc.rsrTorField, err = NewRSRField(dc.TorField); err != nil { + return nil, err + } + } + dc.AccountField = acntFld + if strings.HasPrefix(dc.AccountField, REGEXP_PREFIX) { + if dc.rsrAccountField, err = NewRSRField(dc.AccountField); err != nil { + return nil, err + } + } + dc.SubjectField = subjFld + if strings.HasPrefix(dc.SubjectField, REGEXP_PREFIX) { + if dc.rsrSubjectField, err = NewRSRField(dc.SubjectField); err != nil { + return nil, err + } + } + dc.DestinationField = dstFld + if strings.HasPrefix(dc.DestinationField, REGEXP_PREFIX) { + if dc.rsrDestinationField, err = NewRSRField(dc.DestinationField); err != nil { + return nil, err + } + } + dc.SetupTimeField = sTimeFld + if strings.HasPrefix(dc.SetupTimeField, REGEXP_PREFIX) { + if dc.rsrSetupTimeField, err = NewRSRField(dc.SetupTimeField); err != nil { + return nil, err + } + } + dc.AnswerTimeField = aTimeFld + if strings.HasPrefix(dc.AnswerTimeField, REGEXP_PREFIX) { + if dc.rsrAnswerTimeField, err = NewRSRField(dc.AnswerTimeField); err != nil { + return nil, err + } + } + dc.DurationField = durFld + if strings.HasPrefix(dc.DurationField, REGEXP_PREFIX) { + if dc.rsrDurationField, err = 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 + rsrReqTypeField *RSRField // Storage for compiled Regexp in case of RSRFields + rsrDirectionField *RSRField + rsrTenantField *RSRField + rsrTorField *RSRField + rsrAccountField *RSRField + rsrSubjectField *RSRField + rsrDestinationField *RSRField + rsrSetupTimeField *RSRField + rsrAnswerTimeField *RSRField + rsrDurationField *RSRField +} + +func DerivedChargersKey(tenant, tor, direction, account, subject string) string { + return ConcatenatedKey(tenant, tor, direction, account, subject) +} + +type DerivedChargers []*DerivedCharger + +// Precheck that RunId is unique +func (dcs DerivedChargers) Append(dc *DerivedCharger) (DerivedChargers, error) { + if dc.RunId == DEFAULT_RUNID { + return nil, errors.New("Reserved RunId") + } + for _, dcLocal := range dcs { + if dcLocal.RunId == dc.RunId { + return nil, errors.New("Duplicated RunId") + } + } + return append(dcs, dc), nil +} diff --git a/config/derivedchargers_test.go b/utils/derivedchargers_test.go similarity index 60% rename from config/derivedchargers_test.go rename to utils/derivedchargers_test.go index c300d9070..8635733c1 100644 --- a/config/derivedchargers_test.go +++ b/utils/derivedchargers_test.go @@ -1,5 +1,5 @@ /* -Rating system for Telecom Environments +Real-time Charging System for Telecom & ISP environments Copyright (C) 2012-2014 ITsysCOM GmbH This program is free software: you can Storagetribute it and/or modify @@ -16,10 +16,9 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -package config +package utils import ( - "github.com/cgrates/cgrates/utils" "reflect" "testing" ) @@ -27,7 +26,7 @@ import ( func TestAppendDerivedChargers(t *testing.T) { var err error dcs := make(DerivedChargers, 0) - if _, err := dcs.Append(&DerivedCharger{RunId: utils.DEFAULT_RUNID}); err == nil { + if _, err := dcs.Append(&DerivedCharger{RunId: DEFAULT_RUNID}); err == nil { t.Error("Failed to detect using of the default runid") } if dcs, err = dcs.Append(&DerivedCharger{RunId: "FIRST_RUNID"}); err != nil { @@ -78,16 +77,16 @@ func TestNewDerivedCharger(t *testing.T) { 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/") + edc2.rsrReqTypeField, _ = NewRSRField("~reqtype2:s/sip:(.+)/$1/") + edc2.rsrDirectionField, _ = NewRSRField("~direction2:s/sip:(.+)/$1/") + edc2.rsrTenantField, _ = NewRSRField("~tenant2:s/sip:(.+)/$1/") + edc2.rsrTorField, _ = NewRSRField("~tor2:s/sip:(.+)/$1/") + edc2.rsrAccountField, _ = NewRSRField("~account2:s/sip:(.+)/$1/") + edc2.rsrSubjectField, _ = NewRSRField("~subject2:s/sip:(.+)/$1/") + edc2.rsrDestinationField, _ = NewRSRField("~destination2:s/sip:(.+)/$1/") + edc2.rsrSetupTimeField, _ = NewRSRField("~setuptime2:s/sip:(.+)/$1/") + edc2.rsrAnswerTimeField, _ = NewRSRField("~answertime2:s/sip:(.+)/$1/") + edc2.rsrDurationField, _ = NewRSRField("~duration2:s/sip:(.+)/$1/") if dc2, err := NewDerivedCharger("test2", "~reqtype2:s/sip:(.+)/$1/", "~direction2:s/sip:(.+)/$1/", @@ -105,28 +104,8 @@ func TestNewDerivedCharger(t *testing.T) { } } -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) +func TestDerivedChargersKey(t *testing.T) { + if dcKey := DerivedChargersKey("cgrates.org", "call", "*out", "dan", "dan"); dcKey != "cgrates.org:call:*out:dan:dan" { + t.Error("Unexpected derived chargers key: ", dcKey) } } diff --git a/utils/researchreplace.go b/utils/researchreplace.go index 668fa2795..8cb8e6ea6 100644 --- a/utils/researchreplace.go +++ b/utils/researchreplace.go @@ -34,6 +34,9 @@ func (rsr *ReSearchReplace) Process(source string) string { } res := []byte{} match := rsr.SearchRegexp.FindStringSubmatchIndex(source) + if match == nil { + return source // No match returns unaltered source, so we can play with national vs international dialing + } res = rsr.SearchRegexp.ExpandString(res, rsr.ReplaceTemplate, source, match) return string(res) } diff --git a/utils/researchreplace_test.go b/utils/researchreplace_test.go index f6a6a5116..28c113ce8 100644 --- a/utils/researchreplace_test.go +++ b/utils/researchreplace_test.go @@ -49,3 +49,13 @@ func TestProcessReSearchReplace3(t *testing.T) { //"MatchedDestId":"CST_31800_DE t.Error("Unexpected output from SearchReplace: ", outStr) } } + +func TestProcessReSearchReplace4(t *testing.T) { + rsr := &ReSearchReplace{regexp.MustCompile(`^\+49(\d+)`), "0$1"} + if outStr := rsr.Process("+4986517174963"); outStr != "086517174963" { + t.Error("Unexpected output from SearchReplace: ", outStr) + } + if outStr := rsr.Process("+186517174963"); outStr != "+186517174963" { + t.Error("Unexpected output from SearchReplace: ", outStr) + } +} diff --git a/utils/storedcdr_test.go b/utils/storedcdr_test.go index 1368efe01..639f0e820 100644 --- a/utils/storedcdr_test.go +++ b/utils/storedcdr_test.go @@ -199,6 +199,9 @@ func TestFormatCost(t *testing.T) { if cdr.FormatCost(2, 0) != "101" { t.Error("Unexpected format of the cost: ", cdr.FormatCost(2, 0)) } + if cdr.FormatCost(1, 0) != "10" { + t.Error("Unexpected format of the cost: ", cdr.FormatCost(1, 0)) + } if cdr.FormatCost(2, 3) != "101.001" { t.Error("Unexpected format of the cost: ", cdr.FormatCost(2, 3)) }