From 205c8fbb2217d6ba84368ce83bf8b1b7e962b92d Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 9 Feb 2017 16:15:39 +0100 Subject: [PATCH 01/23] Speedup SMG replication, small fix in servmanager --- servmanager/servmanager.go | 4 ++-- sessionmanager/smgeneric.go | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/servmanager/servmanager.go b/servmanager/servmanager.go index f808f2f5a..5c51c70a8 100644 --- a/servmanager/servmanager.go +++ b/servmanager/servmanager.go @@ -85,8 +85,8 @@ func (srvMngr *ServiceManager) StopScheduler() error { srvMngr.Unlock() if sched == nil { return utils.NewCGRError(utils.ServiceManager, - utils.CapitalizedMessage(utils.ServiceAlreadyRunning), - utils.ServiceAlreadyRunning, + utils.CapitalizedMessage(utils.ServiceNotRunning), + utils.ServiceNotRunning, "the scheduler is not running") } srvMngr.cfg.SchedulerEnabled = false diff --git a/sessionmanager/smgeneric.go b/sessionmanager/smgeneric.go index 87be76689..2e05f523f 100644 --- a/sessionmanager/smgeneric.go +++ b/sessionmanager/smgeneric.go @@ -460,7 +460,8 @@ func (smg *SMGeneric) sessionRelocate(initialID, cgrID, newOriginID string) erro // replicateSessions will replicate session based on configuration func (smg *SMGeneric) replicateSessionsWithID(cgrID string, passiveSessions bool, smgReplConns []*SMGReplicationConn) (err error) { - if smg.cgrCfg.SmGenericConfig.DebitInterval != 0 && !passiveSessions { // Replicating active not supported + if len(smgReplConns) == 0 || + (smg.cgrCfg.SmGenericConfig.DebitInterval != 0 && !passiveSessions) { // Replicating active not supported return } ssMux := &smg.aSessionsMux @@ -470,11 +471,11 @@ func (smg *SMGeneric) replicateSessionsWithID(cgrID string, passiveSessions bool ssMp = smg.passiveSessions } ssMux.RLock() - var ss []*SMGSession - if err = utils.Clone(ssMp[cgrID], &ss); err != nil { + ss := ssMp[cgrID] + ssMux.RUnlock() + if len(ss) == 0 { return } - ssMux.RUnlock() var wg sync.WaitGroup for _, rplConn := range smgReplConns { if rplConn.Synchronous { From 29d09ed57d0abffe1c7f8c3097be776c12248116 Mon Sep 17 00:00:00 2001 From: DanB Date: Thu, 9 Feb 2017 20:37:13 +0100 Subject: [PATCH 02/23] Fix *remove_account action for recaching AccountActionPlan index --- engine/action.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/action.go b/engine/action.go index b3f220a5b..8d6d5ebf7 100644 --- a/engine/action.go +++ b/engine/action.go @@ -590,7 +590,7 @@ func removeAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac if err = ratingStorage.RemAccountActionPlans(accID, nil); err != nil { return 0, err } - if err = ratingStorage.CacheDataFromDB(utils.AccountActionPlansPrefix, []string{accID}, true); err != nil { + if err = ratingStorage.CacheDataFromDB(utils.AccountActionPlansPrefix, []string{accID}, true); err != nil && err != utils.ErrNotFound { return 0, err } return 0, nil From 56fb801b4a11c23a527826b6cd1b57b77a88f780 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 10 Feb 2017 20:18:23 +0100 Subject: [PATCH 03/23] Fix ApierV1.GetScheduledActions, unify SetAccount logic between v1 and v2 apis --- apier/v1/accounts.go | 81 +++++++++++++++++++------------------- apier/v1/apier_it_test.go | 15 ++++++- apier/v1/scheduler.go | 70 ++------------------------------ console/scheduler_queue.go | 12 +++--- scheduler/scheduler.go | 64 ++++++++++++++++++++++++++++-- 5 files changed, 124 insertions(+), 118 deletions(-) diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index 739460db4..f886c4299 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -170,10 +170,10 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) (err e if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - var schedulerReloadNeeded = false accID := utils.AccountKey(attr.Tenant, attr.Account) - var ub *engine.Account + dirtyActionPlans := make(map[string]*engine.ActionPlan) _, err = guardian.Guardian.Guard(func() (interface{}, error) { + var ub *engine.Account if bal, _ := self.AccountDb.GetAccount(accID); bal != nil { ub = bal } else { // Not found in db, create it here @@ -181,19 +181,38 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) (err e ID: accID, } } - if len(attr.ActionPlanId) != 0 { + if attr.ActionPlanId != "" { _, err := guardian.Guardian.Guard(func() (interface{}, error) { - var ap *engine.ActionPlan - ap, err := self.RatingDb.GetActionPlan(attr.ActionPlanId, false, utils.NonTransactional) - if err != nil { + acntAPids, err := self.RatingDb.GetAccountActionPlans(accID, false, utils.NonTransactional) + if err != nil && err != utils.ErrNotFound { return 0, err } - if _, exists := ap.AccountIDs[accID]; !exists { + // clean previous action plans + for i := 0; i < len(acntAPids); { + apID := acntAPids[i] + if attr.ActionPlanId == apID { + i++ // increase index since we don't remove from slice + continue + } + ap, err := self.RatingDb.GetActionPlan(apID, false, utils.NonTransactional) + if err != nil { + return 0, err + } + delete(ap.AccountIDs, accID) + dirtyActionPlans[apID] = ap + acntAPids = append(acntAPids[:i], acntAPids[i+1:]...) // remove the item from the list so we can overwrite the real list + } + if !utils.IsSliceMember(acntAPids, attr.ActionPlanId) { // Account not yet attached to action plan, do it here + ap, err := self.RatingDb.GetActionPlan(attr.ActionPlanId, false, utils.NonTransactional) + if err != nil { + return 0, err + } if ap.AccountIDs == nil { ap.AccountIDs = make(utils.StringMap) } ap.AccountIDs[accID] = true - schedulerReloadNeeded = true + dirtyActionPlans[attr.ActionPlanId] = ap + acntAPids = append(acntAPids, attr.ActionPlanId) // create tasks for _, at := range ap.ActionTimings { if at.IsASAP() { @@ -207,31 +226,20 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) (err e } } } - if err := self.RatingDb.SetActionPlan(attr.ActionPlanId, ap, true, utils.NonTransactional); err != nil { + } + apIDs := make([]string, len(dirtyActionPlans)) + i := 0 + for actionPlanID, ap := range dirtyActionPlans { + if err := self.RatingDb.SetActionPlan(actionPlanID, ap, true, utils.NonTransactional); err != nil { return 0, err } + apIDs[i] = actionPlanID + i++ } - // clean previous action plans - acntAPids, err := self.RatingDb.GetAccountActionPlans(accID, false, utils.NonTransactional) - if err != nil && err != utils.ErrNotFound { + if err := self.RatingDb.CacheDataFromDB(utils.ACTION_PLAN_PREFIX, apIDs, true); err != nil { return 0, err } - for _, apID := range acntAPids { - if apID != attr.ActionPlanId { - ap, err := self.RatingDb.GetActionPlan(apID, false, utils.NonTransactional) - if err != nil { - return 0, err - } - delete(ap.AccountIDs, accID) - if err = self.RatingDb.SetActionPlan(apID, ap, true, utils.NonTransactional); err != nil { - return 0, err - } - if err = self.RatingDb.CacheDataFromDB(utils.ACTION_PLAN_PREFIX, []string{ap.Id}, true); err != nil { - return 0, err - } - } - } - if err = self.RatingDb.SetAccountActionPlans(accID, []string{attr.ActionPlanId}, false); err != nil { + if err := self.RatingDb.SetAccountActionPlans(accID, acntAPids, true); err != nil { return 0, err } if err = self.RatingDb.CacheDataFromDB(utils.AccountActionPlansPrefix, []string{accID}, true); err != nil { @@ -243,7 +251,8 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) (err e return 0, err } } - if len(attr.ActionTriggersId) != 0 { + + if attr.ActionTriggersId != "" { atrs, err := self.RatingDb.GetActionTriggers(attr.ActionTriggersId, false, utils.NonTransactional) if err != nil { return 0, err @@ -251,6 +260,7 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) (err e ub.ActionTriggers = atrs ub.InitCounters() } + if attr.AllowNegative != nil { ub.AllowNegative = *attr.AllowNegative } @@ -266,23 +276,14 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) (err e if err != nil { return utils.NewErrServerError(err) } - if attr.ActionPlanId != "" { - if err = self.RatingDb.SetAccountActionPlans(accID, []string{attr.ActionPlanId}, false); err != nil { - return - } - if err = self.RatingDb.CacheDataFromDB(utils.AccountActionPlansPrefix, []string{accID}, true); err != nil { - return - } - - } - if attr.ReloadScheduler && schedulerReloadNeeded { + if attr.ReloadScheduler && len(dirtyActionPlans) > 0 { sched := self.ServManager.GetScheduler() if sched == nil { return errors.New(utils.SchedulerNotRunningCaps) } sched.Reload() } - *reply = OK // This will mark saving of the account, error still can show up in actionTimingsId + *reply = utils.OK // This will mark saving of the account, error still can show up in actionTimingsId return nil } diff --git a/apier/v1/apier_it_test.go b/apier/v1/apier_it_test.go index 40ebeca3c..c92db0707 100644 --- a/apier/v1/apier_it_test.go +++ b/apier/v1/apier_it_test.go @@ -38,6 +38,7 @@ import ( "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/scheduler" "github.com/cgrates/cgrates/servmanager" "github.com/cgrates/cgrates/utils" "github.com/streadway/amqp" @@ -1097,6 +1098,16 @@ func TestApierGetAccountActionPlan(t *testing.T) { } } +// Make sure we have scheduled actions +func TestApierITGetScheduledActionsForAccount(t *testing.T) { + var rply []*scheduler.ScheduledAction + if err := rater.Call("ApierV1.GetScheduledActions", scheduler.ArgsGetScheduledActions{Tenant: utils.StringPointer("cgrates.org"), Account: utils.StringPointer("dan7")}, &rply); err != nil { + t.Error("Unexpected error: ", err) + } else if len(rply) == 0 { + t.Errorf("ScheduledActions: %+v", rply) + } +} + // Test here RemoveActionTiming func TestApierRemUniqueIDActionTiming(t *testing.T) { var rmReply string @@ -1549,8 +1560,8 @@ func TestApierITRemAccountAliases(t *testing.T) { } func TestApierITGetScheduledActions(t *testing.T) { - var rply []*ScheduledActions - if err := rater.Call("ApierV1.GetScheduledActions", AttrsGetScheduledActions{}, &rply); err != nil { + var rply []*scheduler.ScheduledAction + if err := rater.Call("ApierV1.GetScheduledActions", scheduler.ArgsGetScheduledActions{}, &rply); err != nil { t.Error("Unexpected error: ", err) } } diff --git a/apier/v1/scheduler.go b/apier/v1/scheduler.go index 079672ddd..9357c68ea 100644 --- a/apier/v1/scheduler.go +++ b/apier/v1/scheduler.go @@ -21,10 +21,10 @@ import ( "errors" "fmt" "sort" - "strings" "time" "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/scheduler" "github.com/cgrates/cgrates/utils" ) @@ -99,76 +99,12 @@ import ( ] */ -type AttrsGetScheduledActions struct { - Tenant, Account string - TimeStart, TimeEnd time.Time // Filter based on next runTime - utils.Paginator -} - -type ScheduledActions struct { - NextRunTime time.Time - Accounts int - ActionsId, ActionPlanId, ActionTimingUuid string -} - -func (self *ApierV1) GetScheduledActions(attrs AttrsGetScheduledActions, reply *[]*ScheduledActions) error { +func (self *ApierV1) GetScheduledActions(args scheduler.ArgsGetScheduledActions, reply *[]*scheduler.ScheduledAction) error { sched := self.ServManager.GetScheduler() if sched == nil { return errors.New(utils.SchedulerNotRunningCaps) } - schedActions := make([]*ScheduledActions, 0) // needs to be initialized if remains empty - scheduledActions := sched.GetQueue() - for _, qActions := range scheduledActions { - sas := &ScheduledActions{ActionsId: qActions.ActionsID, ActionPlanId: qActions.GetActionPlanID(), ActionTimingUuid: qActions.Uuid, Accounts: len(qActions.GetAccountIDs())} - if attrs.SearchTerm != "" && - !(strings.Contains(sas.ActionPlanId, attrs.SearchTerm) || - strings.Contains(sas.ActionsId, attrs.SearchTerm)) { - continue - } - sas.NextRunTime = qActions.GetNextStartTime(time.Now()) - if !attrs.TimeStart.IsZero() && sas.NextRunTime.Before(attrs.TimeStart) { - continue // Filter here only requests in the filtered interval - } - if !attrs.TimeEnd.IsZero() && (sas.NextRunTime.After(attrs.TimeEnd) || sas.NextRunTime.Equal(attrs.TimeEnd)) { - continue - } - // filter on account - if attrs.Tenant != "" || attrs.Account != "" { - found := false - for accID := range qActions.GetAccountIDs() { - split := strings.Split(accID, utils.CONCATENATED_KEY_SEP) - if len(split) != 2 { - continue // malformed account id - } - if attrs.Tenant != "" && attrs.Tenant != split[0] { - continue - } - if attrs.Account != "" && attrs.Account != split[1] { - continue - } - found = true - break - } - if !found { - continue - } - } - - // we have a winner - - schedActions = append(schedActions, sas) - } - if attrs.Paginator.Offset != nil { - if *attrs.Paginator.Offset <= len(schedActions) { - schedActions = schedActions[*attrs.Paginator.Offset:] - } - } - if attrs.Paginator.Limit != nil { - if *attrs.Paginator.Limit <= len(schedActions) { - schedActions = schedActions[:*attrs.Paginator.Limit] - } - } - *reply = schedActions + *reply = sched.GetScheduledActions(args) return nil } diff --git a/console/scheduler_queue.go b/console/scheduler_queue.go index 4f7c7adc2..e003f300b 100644 --- a/console/scheduler_queue.go +++ b/console/scheduler_queue.go @@ -17,13 +17,15 @@ along with this program. If not, see */ package console -import "github.com/cgrates/cgrates/apier/v1" +import ( + "github.com/cgrates/cgrates/scheduler" +) func init() { c := &CmdGetScheduledActions{ name: "scheduler_queue", rpcMethod: "ApierV1.GetScheduledActions", - rpcParams: &v1.AttrsGetScheduledActions{}, + rpcParams: &scheduler.ArgsGetScheduledActions{}, } commands[c.Name()] = c c.CommandExecuter = &CommandExecuter{c} @@ -33,7 +35,7 @@ func init() { type CmdGetScheduledActions struct { name string rpcMethod string - rpcParams *v1.AttrsGetScheduledActions + rpcParams *scheduler.ArgsGetScheduledActions *CommandExecuter } @@ -47,7 +49,7 @@ func (self *CmdGetScheduledActions) RpcMethod() string { func (self *CmdGetScheduledActions) RpcParams(reset bool) interface{} { if reset || self.rpcParams == nil { - self.rpcParams = &v1.AttrsGetScheduledActions{} + self.rpcParams = &scheduler.ArgsGetScheduledActions{} } return self.rpcParams } @@ -57,6 +59,6 @@ func (self *CmdGetScheduledActions) PostprocessRpcParams() error { } func (self *CmdGetScheduledActions) RpcResult() interface{} { - s := make([]*v1.ScheduledActions, 0) + s := make([]*scheduler.ScheduledAction, 0) return &s } diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 374915230..163e1da60 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -20,6 +20,7 @@ package scheduler import ( "fmt" "sort" + "strings" "sync" "time" @@ -90,6 +91,7 @@ func (s *Scheduler) Loop() { utils.Logger.Info(fmt.Sprintf(" Scheduler queue length: %v", len(s.queue))) s.Lock() a0 := s.queue[0] + utils.Logger.Debug(fmt.Sprintf("Have at scheduled: %+v", a0)) utils.Logger.Info(fmt.Sprintf(" Action: %s", a0.ActionsID)) now := time.Now() start := a0.GetNextStartTime(now) @@ -174,6 +176,7 @@ func (s *Scheduler) loadActionPlans() { } at.SetAccountIDs(actionPlan.AccountIDs) // copy the accounts at.SetActionPlanID(actionPlan.Id) + utils.Logger.Debug(fmt.Sprintf("Scheduling queue, add at: %+v", at)) s.queue = append(s.queue, at) } @@ -191,11 +194,64 @@ func (s *Scheduler) restart() { } } -func (s *Scheduler) GetQueue() (queue engine.ActionTimingPriorityList) { +type ArgsGetScheduledActions struct { + Tenant, Account *string + TimeStart, TimeEnd *time.Time // Filter based on next runTime + utils.Paginator +} + +type ScheduledAction struct { + NextRunTime time.Time + Accounts int // Number of acccounts this action will run on + ActionPlanID, ActionTimingUUID, ActionsID string +} + +func (s *Scheduler) GetScheduledActions(fltr ArgsGetScheduledActions) (schedActions []*ScheduledAction) { s.RLock() - utils.Clone(s.queue, &queue) - defer s.RUnlock() - return queue + for _, at := range s.queue { + sas := &ScheduledAction{NextRunTime: at.GetNextStartTime(time.Now()), Accounts: len(at.GetAccountIDs()), + ActionPlanID: at.GetActionPlanID(), ActionTimingUUID: at.Uuid, ActionsID: at.ActionsID} + if fltr.TimeStart != nil && !fltr.TimeStart.IsZero() && sas.NextRunTime.Before(*fltr.TimeStart) { + continue // need to match the filter interval + } + if fltr.TimeEnd != nil && !fltr.TimeEnd.IsZero() && (sas.NextRunTime.After(*fltr.TimeEnd) || sas.NextRunTime.Equal(*fltr.TimeEnd)) { + continue + } + // filter on account + if fltr.Tenant != nil || fltr.Account != nil { + found := false + for accID := range at.GetAccountIDs() { + split := strings.Split(accID, utils.CONCATENATED_KEY_SEP) + if len(split) != 2 { + continue // malformed account id + } + if fltr.Tenant != nil && *fltr.Tenant != split[0] { + continue + } + if fltr.Account != nil && *fltr.Account != split[1] { + continue + } + found = true + break + } + if !found { + continue + } + } + schedActions = append(schedActions, sas) + } + if fltr.Paginator.Offset != nil { + if *fltr.Paginator.Offset <= len(schedActions) { + schedActions = schedActions[*fltr.Paginator.Offset:] + } + } + if fltr.Paginator.Limit != nil { + if *fltr.Paginator.Limit <= len(schedActions) { + schedActions = schedActions[:*fltr.Paginator.Limit] + } + } + s.RUnlock() + return } func (s *Scheduler) Shutdown() { From fc1789a4e54a8a9c09aea9b1ff29095dc10ceb1a Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 10 Feb 2017 20:48:35 +0100 Subject: [PATCH 04/23] Small fix error handling in removeAccount action --- engine/action.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/action.go b/engine/action.go index 8d6d5ebf7..07d8b2228 100644 --- a/engine/action.go +++ b/engine/action.go @@ -590,7 +590,7 @@ func removeAccountAction(ub *Account, sq *StatsQueueTriggered, a *Action, acs Ac if err = ratingStorage.RemAccountActionPlans(accID, nil); err != nil { return 0, err } - if err = ratingStorage.CacheDataFromDB(utils.AccountActionPlansPrefix, []string{accID}, true); err != nil && err != utils.ErrNotFound { + if err = ratingStorage.CacheDataFromDB(utils.AccountActionPlansPrefix, []string{accID}, true); err != nil && err.Error() != utils.ErrNotFound.Error() { return 0, err } return 0, nil From bf968fed21e8abcf4bfe47a876522a22487eae43 Mon Sep 17 00:00:00 2001 From: DanB Date: Fri, 10 Feb 2017 20:50:51 +0100 Subject: [PATCH 05/23] Remove unecessary debug logs --- scheduler/scheduler.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/scheduler/scheduler.go b/scheduler/scheduler.go index 163e1da60..ef4215ad4 100644 --- a/scheduler/scheduler.go +++ b/scheduler/scheduler.go @@ -91,7 +91,6 @@ func (s *Scheduler) Loop() { utils.Logger.Info(fmt.Sprintf(" Scheduler queue length: %v", len(s.queue))) s.Lock() a0 := s.queue[0] - utils.Logger.Debug(fmt.Sprintf("Have at scheduled: %+v", a0)) utils.Logger.Info(fmt.Sprintf(" Action: %s", a0.ActionsID)) now := time.Now() start := a0.GetNextStartTime(now) @@ -176,7 +175,6 @@ func (s *Scheduler) loadActionPlans() { } at.SetAccountIDs(actionPlan.AccountIDs) // copy the accounts at.SetActionPlanID(actionPlan.Id) - utils.Logger.Debug(fmt.Sprintf("Scheduling queue, add at: %+v", at)) s.queue = append(s.queue, at) } From 393c1acbd0a52889669569b8d758f498772ae84c Mon Sep 17 00:00:00 2001 From: DanB Date: Sun, 12 Feb 2017 19:19:47 +0100 Subject: [PATCH 06/23] Adding FieldMultiplyFactor so we can clone it centralized --- cdre/cdrexporter_test.go | 18 ---- config/cdreconfig.go | 73 +++++++------- config/cdreconfig_test.go | 48 ++++++---- config/cdrreplication.go | 44 --------- config/config.go | 53 +++-------- config/config_defaults.go | 94 +++++++------------ config/config_json_test.go | 31 +++--- config/config_test.go | 31 +++--- config/libconfig_json.go | 23 +++-- cdre/csv_test.go => engine/cdrecsv_test.go | 0 .../cdrefwv_test.go | 0 {cdre => engine}/cdrexporter.go | 0 utils/map.go | 12 +++ 13 files changed, 170 insertions(+), 257 deletions(-) delete mode 100644 cdre/cdrexporter_test.go delete mode 100644 config/cdrreplication.go rename cdre/csv_test.go => engine/cdrecsv_test.go (100%) rename cdre/fixedwidth_test.go => engine/cdrefwv_test.go (100%) rename {cdre => engine}/cdrexporter.go (100%) diff --git a/cdre/cdrexporter_test.go b/cdre/cdrexporter_test.go deleted file mode 100644 index 769aeac62..000000000 --- a/cdre/cdrexporter_test.go +++ /dev/null @@ -1,18 +0,0 @@ -/* -Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments -Copyright (C) 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 -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 WITHOUT 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 cdre diff --git a/config/cdreconfig.go b/config/cdreconfig.go index f99dd1fff..d36908ebb 100644 --- a/config/cdreconfig.go +++ b/config/cdreconfig.go @@ -17,19 +17,22 @@ along with this program. If not, see */ package config +import ( + "github.com/cgrates/cgrates/utils" +) + // One instance of CdrExporter type CdreConfig struct { - CdrFormat string - FieldSeparator rune - DataUsageMultiplyFactor float64 - SMSUsageMultiplyFactor float64 - MMSUsageMultiplyFactor float64 - GenericUsageMultiplyFactor float64 - CostMultiplyFactor float64 - ExportDirectory string - HeaderFields []*CfgCdrField - ContentFields []*CfgCdrField - TrailerFields []*CfgCdrField + ExportFormat string + ExportPath string + Synchronous bool + Attempts int + FieldSeparator rune + UsageMultiplyFactor utils.FieldMultiplyFactor + CostMultiplyFactor float64 + HeaderFields []*CfgCdrField + ContentFields []*CfgCdrField + TrailerFields []*CfgCdrField } func (self *CdreConfig) loadFromJsonCfg(jsnCfg *CdreJsonCfg) error { @@ -37,31 +40,33 @@ func (self *CdreConfig) loadFromJsonCfg(jsnCfg *CdreJsonCfg) error { return nil } var err error - if jsnCfg.Cdr_format != nil { - self.CdrFormat = *jsnCfg.Cdr_format + if jsnCfg.Export_format != nil { + self.ExportFormat = *jsnCfg.Export_format + } + if jsnCfg.Export_path != nil { + self.ExportPath = *jsnCfg.Export_path + } + if jsnCfg.Synchronous != nil { + self.Synchronous = *jsnCfg.Synchronous + } + if jsnCfg.Attempts != nil { + self.Attempts = *jsnCfg.Attempts } if jsnCfg.Field_separator != nil && len(*jsnCfg.Field_separator) > 0 { // Make sure we got at least one character so we don't get panic here sepStr := *jsnCfg.Field_separator self.FieldSeparator = rune(sepStr[0]) } - if jsnCfg.Data_usage_multiply_factor != nil { - self.DataUsageMultiplyFactor = *jsnCfg.Data_usage_multiply_factor - } - if jsnCfg.Sms_usage_multiply_factor != nil { - self.SMSUsageMultiplyFactor = *jsnCfg.Sms_usage_multiply_factor - } - if jsnCfg.Mms_usage_multiply_factor != nil { - self.MMSUsageMultiplyFactor = *jsnCfg.Mms_usage_multiply_factor - } - if jsnCfg.Generic_usage_multiply_factor != nil { - self.GenericUsageMultiplyFactor = *jsnCfg.Generic_usage_multiply_factor + if jsnCfg.Usage_multiply_factor != nil { + if self.UsageMultiplyFactor == nil { // not yet initialized + self.UsageMultiplyFactor = make(map[string]float64, len(*jsnCfg.Usage_multiply_factor)) + } + for k, v := range *jsnCfg.Usage_multiply_factor { + self.UsageMultiplyFactor[k] = v + } } if jsnCfg.Cost_multiply_factor != nil { self.CostMultiplyFactor = *jsnCfg.Cost_multiply_factor } - if jsnCfg.Export_directory != nil { - self.ExportDirectory = *jsnCfg.Export_directory - } if jsnCfg.Header_fields != nil { if self.HeaderFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*jsnCfg.Header_fields); err != nil { return err @@ -83,14 +88,16 @@ func (self *CdreConfig) loadFromJsonCfg(jsnCfg *CdreJsonCfg) error { // Clone itself into a new CdreConfig func (self *CdreConfig) Clone() *CdreConfig { clnCdre := new(CdreConfig) - clnCdre.CdrFormat = self.CdrFormat + clnCdre.ExportFormat = self.ExportFormat + clnCdre.ExportPath = self.ExportPath + clnCdre.Synchronous = self.Synchronous + clnCdre.Attempts = self.Attempts clnCdre.FieldSeparator = self.FieldSeparator - clnCdre.DataUsageMultiplyFactor = self.DataUsageMultiplyFactor - clnCdre.SMSUsageMultiplyFactor = self.SMSUsageMultiplyFactor - clnCdre.MMSUsageMultiplyFactor = self.MMSUsageMultiplyFactor - clnCdre.GenericUsageMultiplyFactor = self.GenericUsageMultiplyFactor + clnCdre.UsageMultiplyFactor = make(map[string]float64, len(self.UsageMultiplyFactor)) + for k, v := range self.UsageMultiplyFactor { + clnCdre.UsageMultiplyFactor[k] = v + } clnCdre.CostMultiplyFactor = self.CostMultiplyFactor - clnCdre.ExportDirectory = self.ExportDirectory clnCdre.HeaderFields = make([]*CfgCdrField, len(self.HeaderFields)) for idx, fld := range self.HeaderFields { clonedVal := *fld diff --git a/config/cdreconfig_test.go b/config/cdreconfig_test.go index a1fe15742..01e63ac9f 100644 --- a/config/cdreconfig_test.go +++ b/config/cdreconfig_test.go @@ -25,8 +25,8 @@ import ( ) func TestCdreCfgClone(t *testing.T) { - cgrIdRsrs, _ := utils.ParseRSRFields("cgrid", utils.INFIELD_SEP) - runIdRsrs, _ := utils.ParseRSRFields("mediation_runid", utils.INFIELD_SEP) + cgrIdRsrs := utils.ParseRSRFieldsMustCompile("cgrid", utils.INFIELD_SEP) + runIdRsrs := utils.ParseRSRFieldsMustCompile("runid", utils.INFIELD_SEP) emptyFields := []*CfgCdrField{} initContentFlds := []*CfgCdrField{ &CfgCdrField{Tag: "CgrId", @@ -35,16 +35,21 @@ func TestCdreCfgClone(t *testing.T) { Value: cgrIdRsrs}, &CfgCdrField{Tag: "RunId", Type: "*composed", - FieldId: "mediation_runid", + FieldId: "runid", Value: runIdRsrs}, } initCdreCfg := &CdreConfig{ - CdrFormat: "csv", - FieldSeparator: rune(','), - DataUsageMultiplyFactor: 1.0, - CostMultiplyFactor: 1.0, - ExportDirectory: "/var/spool/cgrates/cdre", - ContentFields: initContentFlds, + ExportFormat: utils.MetaFileCSV, + ExportPath: "/var/spool/cgrates/cdre", + Synchronous: true, + Attempts: 2, + FieldSeparator: rune(','), + UsageMultiplyFactor: map[string]float64{ + utils.ANY: 1.0, + utils.DATA: 1024, + }, + CostMultiplyFactor: 1.0, + ContentFields: initContentFlds, } eClnContentFlds := []*CfgCdrField{ &CfgCdrField{Tag: "CgrId", @@ -53,24 +58,29 @@ func TestCdreCfgClone(t *testing.T) { Value: cgrIdRsrs}, &CfgCdrField{Tag: "RunId", Type: "*composed", - FieldId: "mediation_runid", + FieldId: "runid", Value: runIdRsrs}, } eClnCdreCfg := &CdreConfig{ - CdrFormat: "csv", - FieldSeparator: rune(','), - DataUsageMultiplyFactor: 1.0, - CostMultiplyFactor: 1.0, - ExportDirectory: "/var/spool/cgrates/cdre", - HeaderFields: emptyFields, - ContentFields: eClnContentFlds, - TrailerFields: emptyFields, + ExportFormat: utils.MetaFileCSV, + ExportPath: "/var/spool/cgrates/cdre", + Synchronous: true, + Attempts: 2, + FieldSeparator: rune(','), + UsageMultiplyFactor: map[string]float64{ + utils.ANY: 1.0, + utils.DATA: 1024.0, + }, + CostMultiplyFactor: 1.0, + HeaderFields: emptyFields, + ContentFields: eClnContentFlds, + TrailerFields: emptyFields, } clnCdreCfg := initCdreCfg.Clone() if !reflect.DeepEqual(eClnCdreCfg, clnCdreCfg) { t.Errorf("Cloned result: %+v", clnCdreCfg) } - initCdreCfg.DataUsageMultiplyFactor = 1024.0 + initCdreCfg.UsageMultiplyFactor[utils.DATA] = 2048.0 if !reflect.DeepEqual(eClnCdreCfg, clnCdreCfg) { // MOdifying a field after clone should not affect cloned instance t.Errorf("Cloned result: %+v", clnCdreCfg) } diff --git a/config/cdrreplication.go b/config/cdrreplication.go deleted file mode 100644 index 370bc6c12..000000000 --- a/config/cdrreplication.go +++ /dev/null @@ -1,44 +0,0 @@ -/* -Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments -Copyright (C) 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 -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 WITHOUT 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 ( - "github.com/cgrates/cgrates/utils" -) - -type CDRReplicationCfg struct { - Transport string - Address string - Synchronous bool - Attempts int // Number of attempts if not success - CdrFilter utils.RSRFields // Only replicate if the filters here are matching - ContentFields []*CfgCdrField -} - -func (rplCfg CDRReplicationCfg) FallbackFileName() string { - fileSuffix := ".txt" - switch rplCfg.Transport { - case utils.MetaHTTPjsonCDR, utils.MetaHTTPjsonMap, utils.MetaAMQPjsonCDR, utils.MetaAMQPjsonMap: - fileSuffix = ".json" - case utils.META_HTTP_POST: - fileSuffix = ".form" - } - ffn := &utils.FallbackFileName{Module: "cdr", Transport: rplCfg.Transport, Address: rplCfg.Address, - RequestID: utils.GenUUID(), FileSuffix: fileSuffix} - return ffn.AsString() -} diff --git a/config/config.go b/config/config.go index 46cf755be..06110d40c 100644 --- a/config/config.go +++ b/config/config.go @@ -241,14 +241,14 @@ type CGRConfig struct { CDRSStoreCdrs bool // store cdrs in storDb CDRScdrAccountSummary bool CDRSSMCostRetries int - CDRSRaterConns []*HaPoolConfig // address where to reach the Rater for cost calculation: <""|internal|x.y.z.y:1234> - CDRSPubSubSConns []*HaPoolConfig // address where to reach the pubsub service: <""|internal|x.y.z.y:1234> - CDRSUserSConns []*HaPoolConfig // address where to reach the users service: <""|internal|x.y.z.y:1234> - CDRSAliaseSConns []*HaPoolConfig // address where to reach the aliases service: <""|internal|x.y.z.y:1234> - CDRSStatSConns []*HaPoolConfig // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234> - CDRSCdrReplication []*CDRReplicationCfg // Replicate raw CDRs to a number of servers - CDRStatsEnabled bool // Enable CDR Stats service - CDRStatsSaveInterval time.Duration // Save interval duration + CDRSRaterConns []*HaPoolConfig // address where to reach the Rater for cost calculation: <""|internal|x.y.z.y:1234> + CDRSPubSubSConns []*HaPoolConfig // address where to reach the pubsub service: <""|internal|x.y.z.y:1234> + CDRSUserSConns []*HaPoolConfig // address where to reach the users service: <""|internal|x.y.z.y:1234> + CDRSAliaseSConns []*HaPoolConfig // address where to reach the aliases service: <""|internal|x.y.z.y:1234> + CDRSStatSConns []*HaPoolConfig // address where to reach the cdrstats service. Empty to disable stats gathering <""|internal|x.y.z.y:1234> + CDRSOnlineCDRExports []string // list of CDRE templates to use for real-time CDR exports + CDRStatsEnabled bool // Enable CDR Stats service + CDRStatsSaveInterval time.Duration // Save interval duration CdreProfiles map[string]*CdreConfig CdrcProfiles map[string][]*CdrcConfig // Number of CDRC instances running imports, format map[dirPath][]{Configs} SmGenericConfig *SmGenericConfig @@ -336,10 +336,9 @@ func (self *CGRConfig) checkConfigSanity() error { return errors.New("CDRStatS not enabled but requested by CDRS component.") } } - for _, rplCfg := range self.CDRSCdrReplication { - if utils.IsSliceMember([]string{utils.MetaHTTPjsonMap, utils.META_HTTP_POST}, rplCfg.Transport) && - len(rplCfg.ContentFields) == 0 { - return fmt.Errorf(" No content fields defined for replication to address: <%s>", rplCfg.Address) + for _, cdrePrfl := range self.CDRSOnlineCDRExports { + if _, hasIt := self.CdreProfiles[cdrePrfl]; !hasIt { + return fmt.Errorf(" Cannot find CDR export template with ID: <%s>", cdrePrfl) } } } @@ -922,33 +921,9 @@ func (self *CGRConfig) loadFromJsonCfg(jsnCfg *CgrJsonCfg) error { self.CDRSStatSConns[idx].loadFromJsonCfg(jsnHaCfg) } } - if jsnCdrsCfg.Cdr_replication != nil { - self.CDRSCdrReplication = make([]*CDRReplicationCfg, len(*jsnCdrsCfg.Cdr_replication)) - for idx, rplJsonCfg := range *jsnCdrsCfg.Cdr_replication { - self.CDRSCdrReplication[idx] = new(CDRReplicationCfg) - if rplJsonCfg.Transport != nil { - self.CDRSCdrReplication[idx].Transport = *rplJsonCfg.Transport - } - if rplJsonCfg.Address != nil { - self.CDRSCdrReplication[idx].Address = *rplJsonCfg.Address - } - if rplJsonCfg.Synchronous != nil { - self.CDRSCdrReplication[idx].Synchronous = *rplJsonCfg.Synchronous - } - self.CDRSCdrReplication[idx].Attempts = 1 - if rplJsonCfg.Attempts != nil { - self.CDRSCdrReplication[idx].Attempts = *rplJsonCfg.Attempts - } - if rplJsonCfg.Cdr_filter != nil { - if self.CDRSCdrReplication[idx].CdrFilter, err = utils.ParseRSRFields(*rplJsonCfg.Cdr_filter, utils.INFIELD_SEP); err != nil { - return err - } - } - if rplJsonCfg.Content_fields != nil { - if self.CDRSCdrReplication[idx].ContentFields, err = CfgCdrFieldsFromCdrFieldsJsonCfg(*rplJsonCfg.Content_fields); err != nil { - return err - } - } + if jsnCdrsCfg.Online_cdr_exports != nil { + for _, expProfile := range *jsnCdrsCfg.Online_cdr_exports { + self.CDRSOnlineCDRExports = append(self.CDRSOnlineCDRExports, expProfile) } } } diff --git a/config/config_defaults.go b/config/config_defaults.go index 6d01a8c49..6736c7093 100644 --- a/config/config_defaults.go +++ b/config/config_defaults.go @@ -153,31 +153,42 @@ const CGRATES_CFG_JSON = ` "users_conns": [], // address where to reach the user service, empty to disable user profile functionality: <""|*internal|x.y.z.y:1234> "aliases_conns": [], // address where to reach the aliases service, empty to disable aliases functionality: <""|*internal|x.y.z.y:1234> "cdrstats_conns": [], // address where to reach the cdrstats service, empty to disable stats functionality<""|*internal|x.y.z.y:1234> - "cdr_replication":[ -// { // sample replication, not configured by default -// "transport": "*amqp_json_map", // mechanism to use when replicating -// "address": "http://127.0.0.1:12080/cdr_json_map", // address where to replicate -// "attempts": 1, // number of attempts for POST before failing on file -// "cdr_filter": "", // filter the CDRs being replicated -// "content_fields": [ // template of the replicated content fields -// {"tag": "CGRID", "type": "*composed", "value": "CGRID", "field_id": "CGRID"}, -// {"tag":"RunID", "type": "*composed", "value": "RunID", "field_id": "RunID"}, -// {"tag":"TOR", "type": "*composed", "value": "ToR", "field_id": "ToR"}, -// {"tag":"OriginID", "type": "*composed", "value": "OriginID", "field_id": "OriginID"}, -// {"tag":"RequestType", "type": "*composed", "value": "RequestType", "field_id": "RequestType"}, -// {"tag":"Direction", "type": "*composed", "value": "Direction", "field_id": "Direction"}, -// {"tag":"Tenant", "type": "*composed", "value": "Tenant", "field_id": "Tenant"}, -// {"tag":"Category", "type": "*composed", "value": "Category", "field_id": "Category"}, -// {"tag":"Account", "type": "*composed", "value": "Account", "field_id": "Account"}, -// {"tag":"Subject", "type": "*composed", "value": "Subject", "field_id": "Subject"}, -// {"tag":"Destination", "type": "*composed", "value": "Destination", "field_id": "Destination"}, -// {"tag":"SetupTime", "type": "*composed", "value": "SetupTime", "layout": "2006-01-02T15:04:05Z07:00", "field_id": "SetupTime"}, -// {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00", "field_id": "AnswerTime"}, -// {"tag":"Usage", "type": "*composed", "value": "Usage", "field_id": "Usage"}, -// {"tag":"Cost", "type": "*composed", "value": "Cost", "field_id": "Cost"}, -// ], -// }, - ] + "online_cdr_exports":[], // list of CDRE profiles to use for real-time CDR exports +}, + + +"cdre": { + "*default": { + "export_format": "*file_csv", // exported CDRs format <*file_csv|*file_fwv|*http_post|*http_json_cdr|*http_json_map|*amqp_json_cdr|*amqp_json_map> + "export_path": "/var/spool/cgrates/cdre", // path where the exported CDRs will be placed + "cdr_filter": "", // filter CDRs exported by this template + "synchronous": false, // block processing until export has a result + "attempts": 1, // Number of attempts if not success + "field_separator": ",", // used field separator in some export formats, eg: *file_csv + "usage_multiply_factor": { + "*any": 1 // multiply usage based on ToR field or *any for all + }, + "cost_multiply_factor": 1, // multiply cost before export, eg: add VAT + "header_fields": [], // template of the exported header fields + "content_fields": [ // template of the exported content fields + {"tag": "CGRID", "type": "*composed", "value": "CGRID"}, + {"tag":"RunID", "type": "*composed", "value": "RunID"}, + {"tag":"TOR", "type": "*composed", "value": "ToR"}, + {"tag":"OriginID", "type": "*composed", "value": "OriginID"}, + {"tag":"RequestType", "type": "*composed", "value": "RequestType"}, + {"tag":"Direction", "type": "*composed", "value": "Direction"}, + {"tag":"Tenant", "type": "*composed", "value": "Tenant"}, + {"tag":"Category", "type": "*composed", "value": "Category"}, + {"tag":"Account", "type": "*composed", "value": "Account"}, + {"tag":"Subject", "type": "*composed", "value": "Subject"}, + {"tag":"Destination", "type": "*composed", "value": "Destination"}, + {"tag":"SetupTime", "type": "*composed", "value": "SetupTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, + {"tag":"Usage", "type": "*composed", "value": "Usage"}, + {"tag":"Cost", "type": "*composed", "value": "Cost", "rounding_decimals": 4}, + ], + "trailer_fields": [], // template of the exported trailer fields + }, }, @@ -247,39 +258,6 @@ const CGRATES_CFG_JSON = ` ], -"cdre": { - "*default": { - "cdr_format": "csv", // exported CDRs format - "field_separator": ",", - "data_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from KBytes to Bytes) - "sms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from SMS unit to call duration in some billing systems) - "mms_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from MMS unit to call duration in some billing systems) - "generic_usage_multiply_factor": 1, // multiply data usage before export (eg: convert from GENERIC unit to call duration in some billing systems) - "cost_multiply_factor": 1, // multiply cost before export, eg: add VAT - "export_directory": "/var/spool/cgrates/cdre", // path where the exported CDRs will be placed - "header_fields": [], // template of the exported header fields - "content_fields": [ // template of the exported content fields - {"tag": "CGRID", "type": "*composed", "value": "CGRID"}, - {"tag":"RunID", "type": "*composed", "value": "RunID"}, - {"tag":"TOR", "type": "*composed", "value": "ToR"}, - {"tag":"OriginID", "type": "*composed", "value": "OriginID"}, - {"tag":"RequestType", "type": "*composed", "value": "RequestType"}, - {"tag":"Direction", "type": "*composed", "value": "Direction"}, - {"tag":"Tenant", "type": "*composed", "value": "Tenant"}, - {"tag":"Category", "type": "*composed", "value": "Category"}, - {"tag":"Account", "type": "*composed", "value": "Account"}, - {"tag":"Subject", "type": "*composed", "value": "Subject"}, - {"tag":"Destination", "type": "*composed", "value": "Destination"}, - {"tag":"SetupTime", "type": "*composed", "value": "SetupTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag":"AnswerTime", "type": "*composed", "value": "AnswerTime", "layout": "2006-01-02T15:04:05Z07:00"}, - {"tag":"Usage", "type": "*composed", "value": "Usage"}, - {"tag":"Cost", "type": "*composed", "value": "Cost", "rounding_decimals": 4}, - ], - "trailer_fields": [], // template of the exported trailer fields - }, -}, - - "sm_generic": { "enabled": false, // starts SessionManager service: "listen_bijson": "127.0.0.1:2014", // address where to listen for bidirectional JSON-RPC requests diff --git a/config/config_json_test.go b/config/config_json_test.go index 2cea6d44e..301d0de08 100644 --- a/config/config_json_test.go +++ b/config/config_json_test.go @@ -203,11 +203,11 @@ func TestDfCdrsJsonCfg(t *testing.T) { &HaPoolJsonCfg{ Address: utils.StringPointer("*internal"), }}, - Pubsubs_conns: &[]*HaPoolJsonCfg{}, - Users_conns: &[]*HaPoolJsonCfg{}, - Aliases_conns: &[]*HaPoolJsonCfg{}, - Cdrstats_conns: &[]*HaPoolJsonCfg{}, - Cdr_replication: &[]*CdrReplicationJsonCfg{}, + Pubsubs_conns: &[]*HaPoolJsonCfg{}, + Users_conns: &[]*HaPoolJsonCfg{}, + Aliases_conns: &[]*HaPoolJsonCfg{}, + Cdrstats_conns: &[]*HaPoolJsonCfg{}, + Online_cdr_exports: &[]string{}, } if cfg, err := dfCgrJsonCfg.CdrsJsonCfg(); err != nil { t.Error(err) @@ -282,17 +282,16 @@ func TestDfCdreJsonCfgs(t *testing.T) { } eCfg := map[string]*CdreJsonCfg{ utils.META_DEFAULT: &CdreJsonCfg{ - Cdr_format: utils.StringPointer("csv"), - Field_separator: utils.StringPointer(","), - Data_usage_multiply_factor: utils.Float64Pointer(1.0), - Sms_usage_multiply_factor: utils.Float64Pointer(1.0), - Mms_usage_multiply_factor: utils.Float64Pointer(1.0), - Generic_usage_multiply_factor: utils.Float64Pointer(1.0), - Cost_multiply_factor: utils.Float64Pointer(1.0), - Export_directory: utils.StringPointer("/var/spool/cgrates/cdre"), - Header_fields: &eFields, - Content_fields: &eContentFlds, - Trailer_fields: &eFields, + Export_format: utils.StringPointer(utils.MetaFileCSV), + Export_path: utils.StringPointer("/var/spool/cgrates/cdre"), + Synchronous: utils.BoolPointer(false), + Attempts: utils.IntPointer(1), + Field_separator: utils.StringPointer(","), + Usage_multiply_factor: &map[string]float64{utils.ANY: 1.0}, + Cost_multiply_factor: utils.Float64Pointer(1.0), + Header_fields: &eFields, + Content_fields: &eContentFlds, + Trailer_fields: &eFields, }, } if cfg, err := dfCgrJsonCfg.CdreJsonCfgs(); err != nil { diff --git a/config/config_test.go b/config/config_test.go index 5bff139c5..98aa6c4fb 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -330,10 +330,7 @@ func TestCgrCfgJSONDefaultsScheduler(t *testing.T) { func TestCgrCfgJSONDefaultsCDRS(t *testing.T) { eHaPoolCfg := []*HaPoolConfig{} - eCDRReCfg := []*CDRReplicationCfg{} - iHaPoolCfg := []*HaPoolConfig{&HaPoolConfig{Address: "*internal"}} var eCdrExtr []*utils.RSRField - if cgrCfg.CDRSEnabled != false { t.Error(cgrCfg.CDRSEnabled) } @@ -349,7 +346,7 @@ func TestCgrCfgJSONDefaultsCDRS(t *testing.T) { if cgrCfg.CDRSSMCostRetries != 5 { t.Error(cgrCfg.CDRSSMCostRetries) } - if !reflect.DeepEqual(cgrCfg.CDRSRaterConns, iHaPoolCfg) { + if !reflect.DeepEqual(cgrCfg.CDRSRaterConns, []*HaPoolConfig{&HaPoolConfig{Address: "*internal"}}) { t.Error(cgrCfg.CDRSRaterConns) } if !reflect.DeepEqual(cgrCfg.CDRSPubSubSConns, eHaPoolCfg) { @@ -364,8 +361,8 @@ func TestCgrCfgJSONDefaultsCDRS(t *testing.T) { if !reflect.DeepEqual(cgrCfg.CDRSStatSConns, eHaPoolCfg) { t.Error(cgrCfg.CDRSStatSConns) } - if !reflect.DeepEqual(cgrCfg.CDRSCdrReplication, eCDRReCfg) { - t.Error(cgrCfg.CDRSCdrReplication) + if cgrCfg.CDRSOnlineCDRExports != nil { + t.Error(cgrCfg.CDRSOnlineCDRExports) } } @@ -399,20 +396,18 @@ func TestCgrCfgJSONDefaultsCdreProfiles(t *testing.T) { } eCdreCfg := map[string]*CdreConfig{ "*default": { - CdrFormat: "csv", - FieldSeparator: ',', - DataUsageMultiplyFactor: 1, - SMSUsageMultiplyFactor: 1, - MMSUsageMultiplyFactor: 1, - GenericUsageMultiplyFactor: 1, - CostMultiplyFactor: 1, - ExportDirectory: "/var/spool/cgrates/cdre", - HeaderFields: eFields, - ContentFields: eContentFlds, - TrailerFields: eFields, + ExportFormat: utils.MetaFileCSV, + ExportPath: "/var/spool/cgrates/cdre", + Synchronous: false, + Attempts: 1, + FieldSeparator: ',', + UsageMultiplyFactor: map[string]float64{utils.ANY: 1.0}, + CostMultiplyFactor: 1.0, + HeaderFields: eFields, + ContentFields: eContentFlds, + TrailerFields: eFields, }, } - if !reflect.DeepEqual(cgrCfg.CdreProfiles, eCdreCfg) { t.Errorf("received: %+v, expecting: %+v", cgrCfg.CdreProfiles, eCdreCfg) } diff --git a/config/libconfig_json.go b/config/libconfig_json.go index 31cb010b3..112a50196 100644 --- a/config/libconfig_json.go +++ b/config/libconfig_json.go @@ -104,7 +104,7 @@ type CdrsJsonCfg struct { Users_conns *[]*HaPoolJsonCfg Aliases_conns *[]*HaPoolJsonCfg Cdrstats_conns *[]*HaPoolJsonCfg - Cdr_replication *[]*CdrReplicationJsonCfg + Online_cdr_exports *[]string } type CdrReplicationJsonCfg struct { @@ -145,17 +145,16 @@ type CdrFieldJsonCfg struct { // Cdre config section type CdreJsonCfg struct { - Cdr_format *string - Field_separator *string - Data_usage_multiply_factor *float64 - Sms_usage_multiply_factor *float64 - Mms_usage_multiply_factor *float64 - Generic_usage_multiply_factor *float64 - Cost_multiply_factor *float64 - Export_directory *string - Header_fields *[]*CdrFieldJsonCfg - Content_fields *[]*CdrFieldJsonCfg - Trailer_fields *[]*CdrFieldJsonCfg + Export_format *string + Export_path *string + Synchronous *bool + Attempts *int + Field_separator *string + Usage_multiply_factor *map[string]float64 + Cost_multiply_factor *float64 + Header_fields *[]*CdrFieldJsonCfg + Content_fields *[]*CdrFieldJsonCfg + Trailer_fields *[]*CdrFieldJsonCfg } // Cdrc config section diff --git a/cdre/csv_test.go b/engine/cdrecsv_test.go similarity index 100% rename from cdre/csv_test.go rename to engine/cdrecsv_test.go diff --git a/cdre/fixedwidth_test.go b/engine/cdrefwv_test.go similarity index 100% rename from cdre/fixedwidth_test.go rename to engine/cdrefwv_test.go diff --git a/cdre/cdrexporter.go b/engine/cdrexporter.go similarity index 100% rename from cdre/cdrexporter.go rename to engine/cdrexporter.go diff --git a/utils/map.go b/utils/map.go index 878a4dc54..3e7916119 100644 --- a/utils/map.go +++ b/utils/map.go @@ -205,3 +205,15 @@ func MergeMapsStringIface(mps ...map[string]interface{}) (outMp map[string]inter } return } + +// FieldMultiplyFactor defines multiply factors for different field values +// original defined for CDRE component +type FieldMultiplyFactor map[string]float64 + +func (fmp FieldMultiplyFactor) Clone() (cln FieldMultiplyFactor) { + cln = make(FieldMultiplyFactor, len(fmp)) + for k, v := range fmp { + cln[k] = v + } + return +} From b1a5c57a7e472c5d874e10ba2686960c2fd217d0 Mon Sep 17 00:00:00 2001 From: alin104n Date: Tue, 14 Feb 2017 06:48:31 +0200 Subject: [PATCH 07/23] SQL fields length increase --- .../mysql/create_tariffplan_tables.sql | 26 +++++++++---------- .../postgres/create_tariffplan_tables.sql | 26 +++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/data/storage/mysql/create_tariffplan_tables.sql b/data/storage/mysql/create_tariffplan_tables.sql index 945ce4106..b4cff66dd 100644 --- a/data/storage/mysql/create_tariffplan_tables.sql +++ b/data/storage/mysql/create_tariffplan_tables.sql @@ -288,21 +288,21 @@ CREATE TABLE tp_derived_chargers ( `destination_ids` varchar(64) NOT NULL, `runid` varchar(24) NOT NULL, `run_filters` varchar(256) NOT NULL, - `req_type_field` varchar(24) NOT NULL, - `direction_field` varchar(24) NOT NULL, - `tenant_field` varchar(24) NOT NULL, - `category_field` varchar(24) NOT NULL, + `req_type_field` varchar(64) NOT NULL, + `direction_field` varchar(64) NOT NULL, + `tenant_field` varchar(64) NOT NULL, + `category_field` varchar(64) NOT NULL, `account_field` varchar(64) NOT NULL, `subject_field` varchar(64) NOT NULL, - `destination_field` varchar(24) NOT NULL, - `setup_time_field` varchar(24) NOT NULL, - `pdd_field` varchar(24) NOT NULL, - `answer_time_field` varchar(24) NOT NULL, - `usage_field` varchar(24) NOT NULL, - `supplier_field` varchar(24) NOT NULL, - `disconnect_cause_field` varchar(24) NOT NULL, - `rated_field` varchar(24) NOT NULL, - `cost_field` varchar(24) NOT NULL, + `destination_field` varchar(64) NOT NULL, + `setup_time_field` varchar(64) NOT NULL, + `pdd_field` varchar(64) NOT NULL, + `answer_time_field` varchar(64) NOT NULL, + `usage_field` varchar(64) NOT NULL, + `supplier_field` varchar(64) NOT NULL, + `disconnect_cause_field` varchar(64) NOT NULL, + `rated_field` varchar(64) NOT NULL, + `cost_field` varchar(64) NOT NULL, `created_at` TIMESTAMP, PRIMARY KEY (`id`), KEY `tpid` (`tpid`) diff --git a/data/storage/postgres/create_tariffplan_tables.sql b/data/storage/postgres/create_tariffplan_tables.sql index bb633d863..04ea57b87 100644 --- a/data/storage/postgres/create_tariffplan_tables.sql +++ b/data/storage/postgres/create_tariffplan_tables.sql @@ -283,21 +283,21 @@ CREATE TABLE tp_derived_chargers ( destination_ids VARCHAR(64) NOT NULL, runid VARCHAR(24) NOT NULL, run_filters VARCHAR(256) NOT NULL, - req_type_field VARCHAR(24) NOT NULL, - direction_field VARCHAR(24) NOT NULL, - tenant_field VARCHAR(24) NOT NULL, - category_field VARCHAR(24) NOT NULL, + req_type_field VARCHAR(64) NOT NULL, + direction_field VARCHAR(64) NOT NULL, + tenant_field VARCHAR(64) NOT NULL, + category_field VARCHAR(64) NOT NULL, account_field VARCHAR(64) NOT NULL, subject_field VARCHAR(64) NOT NULL, - destination_field VARCHAR(24) NOT NULL, - setup_time_field VARCHAR(24) NOT NULL, - pdd_field VARCHAR(24) NOT NULL, - answer_time_field VARCHAR(24) NOT NULL, - usage_field VARCHAR(24) NOT NULL, - supplier_field VARCHAR(24) NOT NULL, - disconnect_cause_field VARCHAR(24) NOT NULL, - rated_field VARCHAR(24) NOT NULL, - cost_field VARCHAR(24) NOT NULL, + destination_field VARCHAR(64) NOT NULL, + setup_time_field VARCHAR(64) NOT NULL, + pdd_field VARCHAR(64) NOT NULL, + answer_time_field VARCHAR(64) NOT NULL, + usage_field VARCHAR(64) NOT NULL, + supplier_field VARCHAR(64) NOT NULL, + disconnect_cause_field VARCHAR(64) NOT NULL, + rated_field VARCHAR(64) NOT NULL, + cost_field VARCHAR(64) NOT NULL, created_at TIMESTAMP ); CREATE INDEX tpderivedchargers_tpid_idx ON tp_derived_chargers (tpid); From 6e88ae409d3db3439611243a8325f88b67f15155 Mon Sep 17 00:00:00 2001 From: alin104n Date: Tue, 14 Feb 2017 08:57:54 +0200 Subject: [PATCH 08/23] StorDB interface tests --- engine/stordb_it_test.go | 1343 +++++++++++++++++++++++++++++++++++++- 1 file changed, 1342 insertions(+), 1 deletion(-) diff --git a/engine/stordb_it_test.go b/engine/stordb_it_test.go index fb90a1225..9561684a5 100644 --- a/engine/stordb_it_test.go +++ b/engine/stordb_it_test.go @@ -22,6 +22,8 @@ package engine import ( "path" "reflect" + "runtime" + "strings" "testing" "github.com/cgrates/cgrates/config" @@ -37,6 +39,21 @@ var ( var sTestsStorDBit = []func(t *testing.T){ testStorDBitFlush, testStorDBitCRUDVersions, + testStorDBitCRUDTpTimings, + testStorDBitCRUDTpDestinations, + testStorDBitCRUDTpRates, + testStorDBitCRUDTpDestinationRates, + testStorDBitCRUDTpRatingPlans, + testStorDBitCRUDTpRatingProfiles, + testStorDBitCRUDTpSharedGroups, + testStorDBitCRUDTpActions, + testStorDBitCRUDTpActionPlans, + testStorDBitCRUDTpActionTriggers, + testStorDBitCRUDTpAccountActions, + testStorDBitCRUDTpLCRs, + testStorDBitCRUDTpDerivedChargers, + testStorDBitCRUDTpCdrStats, + testStorDBitCRUDTpUsers, } func TestStorDBitMySQL(t *testing.T) { @@ -48,10 +65,1334 @@ func TestStorDBitMySQL(t *testing.T) { t.Fatal(err) } for _, stest := range sTestsStorDBit { - t.Run("TestStorDBitMySQL", stest) + stestFullName := runtime.FuncForPC(reflect.ValueOf(stest).Pointer()).Name() + split := strings.Split(stestFullName, ".") + stestName := split[len(split)-1] + t.Run(stestName, stest) } } +func testStorDBitCRUDTpTimings(t *testing.T) { + // READ + // Fixme: Implement ErrNotfound in called method + // if rcv, err := storDB.GetTpTimings("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err, rcv) + // } + // WRITE + var snd = []TpTiming{ + TpTiming{ + Tpid: "testTPid", + Tag: "testTag1", + Years: "*any", + Months: "*any", + MonthDays: "*any", + WeekDays: "1;2;3;4;5", + Time: "01:00:00", + }, + TpTiming{ + Tpid: "testTPid", + Tag: "testTag2", + Years: "*any", + Months: "*any", + MonthDays: "*any", + WeekDays: "1;2;3;4;5", + Time: "01:00:00", + }, + } + if err := storDB.SetTpTimings(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpTimings("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].Time = "02:00:00" + snd[1].Time = "02:00:00" + if err := storDB.SetTpTimings(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpTimings("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpTimings("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpDestinations(t *testing.T) { + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTPDestinations("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + snd := []*utils.TPDestination{ + &utils.TPDestination{ + TPid: "testTPid", + Tag: "testTag1", + Prefixes: []string{`0256`, `0257`, `0723`, `+49`}, + }, + &utils.TPDestination{ + TPid: "testTPid", + Tag: "testTag2", + Prefixes: []string{`0256`, `0257`, `0723`, `+49`}, + }, + } + if err := storDB.SetTPDestinations(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTPDestinations("testTPid", ""); err != nil { + t.Error(err) + } else { + //Fixme: Ensure the order of elements returned by GetTPDestinations + if !((reflect.DeepEqual(*(snd[0]), *(rcv[0])) && reflect.DeepEqual(*(snd[1]), *(rcv[1]))) || (reflect.DeepEqual(*(snd[0]), *(rcv[1])) && reflect.DeepEqual(*(snd[1]), *(rcv[0])))) { + t.Errorf("Expecting: %+v, received: %+v", *(snd[0]), *(rcv[0])) + } + } + // UPDATE + snd[0].Prefixes = []string{`9999`, `0257`, `0723`, `+49`} + snd[1].Prefixes = []string{`9999`, `0257`, `0723`, `+49`} + if err := storDB.SetTPDestinations(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTPDestinations("testTPid", ""); err != nil { + t.Error(err) + } else { + //Fixme: Ensure the order of elements returned by GetTPDestinations + if !((reflect.DeepEqual(*(snd[0]), *(rcv[0])) && reflect.DeepEqual(*(snd[1]), *(rcv[1]))) || (reflect.DeepEqual(*(snd[0]), *(rcv[1])) && reflect.DeepEqual(*(snd[1]), *(rcv[0])))) { + t.Errorf("Expecting: %+v, received: %+v", *(snd[0]), *(rcv[0])) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTPDestinations("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpRates(t *testing.T) { + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpRates("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpRate{ + TpRate{ + Tpid: "testTPid", + Tag: "testTag1", + ConnectFee: 0.0, + Rate: 0.0, + RateUnit: "60s", + RateIncrement: "60s", + GroupIntervalStart: "0s", + }, + TpRate{ + Tpid: "testTPid", + Tag: "testTag2", + ConnectFee: 0.0, + Rate: 0.0, + RateUnit: "60s", + RateIncrement: "60s", + GroupIntervalStart: "0s", + }, + } + if err := storDB.SetTpRates(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpRates("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].GroupIntervalStart = "1s" + snd[1].GroupIntervalStart = "1s" + if err := storDB.SetTpRates(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpRates("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpRates("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpDestinationRates(t *testing.T) { + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpDestinationRates("testTPid", "", nil); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpDestinationRate{ + TpDestinationRate{ + Tpid: "testTPid", + Tag: "testTag1", + DestinationsTag: "GERMANY", + RatesTag: "RT_1CENT", + RoundingMethod: "*up", + RoundingDecimals: 0, + MaxCost: 0.0, + MaxCostStrategy: "", + }, + TpDestinationRate{ + Tpid: "testTPid", + Tag: "testTag2", + DestinationsTag: "GERMANY", + RatesTag: "RT_1CENT", + RoundingMethod: "*up", + RoundingDecimals: 0, + MaxCost: 0.0, + MaxCostStrategy: "", + }, + } + if err := storDB.SetTpDestinationRates(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpDestinationRates("testTPid", "", nil); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].MaxCostStrategy = "test" + snd[1].MaxCostStrategy = "test" + if err := storDB.SetTpDestinationRates(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpDestinationRates("testTPid", "", nil); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpDestinationRates("testTPid", "", nil); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpRatingPlans(t *testing.T) { + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpRatingPlans("testTPid", "", nil); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpRatingPlan{ + TpRatingPlan{ + Tpid: "testTPid", + Tag: "testTag1", + DestratesTag: "", + TimingTag: "ALWAYS", + Weight: 0.0, + }, + TpRatingPlan{ + Tpid: "testTPid", + Tag: "testTag2", + DestratesTag: "", + TimingTag: "ALWAYS", + Weight: 0.0, + }, + } + if err := storDB.SetTpRatingPlans(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpRatingPlans("testTPid", "", nil); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].TimingTag = "test" + snd[1].TimingTag = "test" + if err := storDB.SetTpRatingPlans(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpRatingPlans("testTPid", "", nil); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpRatingPlans("testTPid", "", nil); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpRatingProfiles(t *testing.T) { + // READ + var filter = TpRatingProfile{ + Tpid: "testTPid", + Loadid: "", + Direction: "", + Tenant: "", + Category: "", + Subject: "", + ActivationTime: "", + RatingPlanTag: "", + FallbackSubjects: "", + CdrStatQueueIds: "", + } + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpRatingProfiles(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpRatingProfile{ + TpRatingProfile{ + Tpid: "testTPid", + Loadid: "TEST_LOADID", + Direction: "*out", + Tenant: "cgrates.org", + Category: "call", + Subject: "test", + ActivationTime: "2014-07-29T15:00:00Z", + RatingPlanTag: "test", + FallbackSubjects: "", + CdrStatQueueIds: "", + }, + TpRatingProfile{ + Tpid: "testTPid", + Loadid: "TEST_LOADID2", + Direction: "*out", + Tenant: "cgrates.org", + Category: "call", + Subject: "test", + ActivationTime: "2014-07-29T15:00:00Z", + RatingPlanTag: "test", + FallbackSubjects: "", + CdrStatQueueIds: "", + }, + } + if err := storDB.SetTpRatingProfiles(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpRatingProfiles(&filter); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].CdrStatQueueIds = "test" + snd[1].CdrStatQueueIds = "test" + if err := storDB.SetTpRatingProfiles(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpRatingProfiles(&filter); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpRatingProfiles(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpSharedGroups(t *testing.T) { + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpSharedGroups("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpSharedGroup{ + TpSharedGroup{ + Tpid: "testTPid", + Tag: "testTag1", + Account: "test", + Strategy: "*lowest_cost", + RatingSubject: "test", + }, + TpSharedGroup{ + Tpid: "testTPid", + Tag: "testTag2", + Account: "test", + Strategy: "*lowest_cost", + RatingSubject: "test", + }, + } + if err := storDB.SetTpSharedGroups(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpSharedGroups("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].Strategy = "test" + snd[1].Strategy = "test" + if err := storDB.SetTpSharedGroups(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpSharedGroups("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpSharedGroups("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpActions(t *testing.T) { + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpActions("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpAction{ + TpAction{ + Tpid: "testTPid", + Tag: "testTag1", + Action: "*topup_reset", + ExtraParameters: "", + Filter: "", + BalanceTag: "", + BalanceType: "*monetary", + Directions: "*out", + Categories: "", + DestinationTags: "DST_ON_NET", + RatingSubject: "", + SharedGroups: "", + ExpiryTime: "*unlimited", + TimingTags: "", + Units: "10", + BalanceWeight: "10", + BalanceBlocker: "false", + BalanceDisabled: "false", + Weight: 11.0, + }, + TpAction{ + Tpid: "testTPid", + Tag: "testTag2", + Action: "*topup_reset", + ExtraParameters: "", + Filter: "", + BalanceTag: "", + BalanceType: "*monetary", + Directions: "*out", + Categories: "", + DestinationTags: "DST_ON_NET", + RatingSubject: "", + SharedGroups: "", + ExpiryTime: "*unlimited", + TimingTags: "", + Units: "10", + BalanceWeight: "10", + BalanceBlocker: "false", + BalanceDisabled: "false", + Weight: 11.0, + }, + } + if err := storDB.SetTpActions(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpActions("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].Weight = 12.1 + snd[1].Weight = 12.1 + if err := storDB.SetTpActions(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpActions("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpActions("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpActionPlans(t *testing.T) { + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpActionPlans("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpActionPlan{ + TpActionPlan{ + Tpid: "testTPid", + Tag: "testTag1", + ActionsTag: "test", + TimingTag: "", + Weight: 0.0, + }, + TpActionPlan{ + Tpid: "testTPid", + Tag: "testTag2", + ActionsTag: "test", + TimingTag: "", + Weight: 0.0, + }, + } + if err := storDB.SetTpActionPlans(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpActionPlans("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].Tag = "testTag1b" + snd[1].Tag = "testTag2b" + if err := storDB.SetTpActionPlans(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpActionPlans("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpActionPlans("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpActionTriggers(t *testing.T) { + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpActionTriggers("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpActionTrigger{ + TpActionTrigger{ + Tpid: "testTPid", + Tag: "testTag1", + UniqueId: "", + ThresholdType: "", + ThresholdValue: 0.0, + Recurrent: true, + MinSleep: "0", + ExpiryTime: "2014-07-29T15:00:00Z", + ActivationTime: "2014-07-29T15:00:00Z", + BalanceTag: "test", + BalanceType: "*monetary", + BalanceDirections: "*out", + BalanceCategories: "call", + BalanceDestinationTags: "", + BalanceRatingSubject: "test", + BalanceSharedGroups: "SHARED_1", + BalanceExpiryTime: "2014-07-29T15:00:00Z", + BalanceTimingTags: "T1", + BalanceWeight: "0.0", + BalanceBlocker: "false", + BalanceDisabled: "false", + MinQueuedItems: 0, + ActionsTag: "test", + Weight: 0.0, + }, + TpActionTrigger{ + Tpid: "testTPid", + Tag: "testTag2", + UniqueId: "", + ThresholdType: "", + ThresholdValue: 0.0, + Recurrent: true, + MinSleep: "0", + ExpiryTime: "2014-07-29T15:00:00Z", + ActivationTime: "2014-07-29T15:00:00Z", + BalanceTag: "test", + BalanceType: "*monetary", + BalanceDirections: "*out", + BalanceCategories: "call", + BalanceDestinationTags: "", + BalanceRatingSubject: "test", + BalanceSharedGroups: "SHARED_1", + BalanceExpiryTime: "2014-07-29T15:00:00Z", + BalanceTimingTags: "T1", + BalanceWeight: "0.0", + BalanceBlocker: "false", + BalanceDisabled: "false", + MinQueuedItems: 0, + ActionsTag: "test", + Weight: 0.0, + }, + } + if err := storDB.SetTpActionTriggers(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpActionTriggers("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].MinQueuedItems = 2 + snd[1].MinQueuedItems = 2 + if err := storDB.SetTpActionTriggers(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpActionTriggers("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpActionTriggers("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpAccountActions(t *testing.T) { + // READ + var filter = TpAccountAction{ + Tpid: "testTPid", + Loadid: "", + Tenant: "", + Account: "", + ActionPlanTag: "", + ActionTriggersTag: "", + AllowNegative: true, + Disabled: true, + } + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpAccountActions(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpAccountAction{ + TpAccountAction{ + Tpid: "testTPid", + Loadid: "TEST_LOADID", + Tenant: "cgrates.org", + Account: "1001", + ActionPlanTag: "PACKAGE_10_SHARED_A_5", + ActionTriggersTag: "STANDARD_TRIGGERS", + AllowNegative: true, + Disabled: true, + }, + TpAccountAction{ + Tpid: "testTPid", + Loadid: "TEST_LOADID", + Tenant: "cgrates.org", + Account: "1002", + ActionPlanTag: "PACKAGE_10_SHARED_A_5", + ActionTriggersTag: "STANDARD_TRIGGERS", + AllowNegative: true, + Disabled: true, + }, + } + if err := storDB.SetTpAccountActions(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpAccountActions(&filter); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].Disabled = false + snd[1].Disabled = false + if err := storDB.SetTpAccountActions(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpAccountActions(&filter); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpAccountActions(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpLCRs(t *testing.T) { + // READ + var filter = TpLcrRule{ + Tpid: "testTPid", + Direction: "", + Tenant: "", + Category: "", + Account: "", + Subject: "", + DestinationTag: "", + RpCategory: "", + Strategy: "", + StrategyParams: "", + ActivationTime: "", + Weight: 0.0, + } + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpLCRs(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpLcrRule{ + TpLcrRule{ + Tpid: "testTPid", + Direction: "*in", + Tenant: "cgrates.org", + Category: "LCR_STANDARD", + Account: "1000", + Subject: "test", + DestinationTag: "", + RpCategory: "LCR_STANDARD", + Strategy: "*lowest_cost", + StrategyParams: "", + ActivationTime: "2012-01-01T00:00:00Z", + Weight: 0.0, + }, + TpLcrRule{ + Tpid: "testTPid", + Direction: "*in", + Tenant: "cgrates.org", + Category: "LCR_STANDARD", + Account: "1000", + Subject: "test", + DestinationTag: "", + RpCategory: "LCR_STANDARD", + Strategy: "*lowest_cost", + StrategyParams: "", + ActivationTime: "2012-01-01T00:00:00Z", + Weight: 0.0, + }, + } + if err := storDB.SetTpLCRs(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpLCRs(&filter); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].StrategyParams = "test" + snd[1].StrategyParams = "test" + if err := storDB.SetTpLCRs(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpLCRs(&filter); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpLCRs(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpDerivedChargers(t *testing.T) { + // READ + var filter = TpDerivedCharger{ + Tpid: "testTPid", + Loadid: "", + Direction: "", + Tenant: "", + Category: "", + Account: "", + Subject: "", + DestinationIds: "", + Runid: "", + RunFilters: "", + ReqTypeField: "", + DirectionField: "", + TenantField: "", + CategoryField: "", + AccountField: "", + SubjectField: "", + DestinationField: "", + SetupTimeField: "", + PddField: "", + AnswerTimeField: "", + UsageField: "", + SupplierField: "", + DisconnectCauseField: "", + RatedField: "", + CostField: "", + } + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpDerivedChargers(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpDerivedCharger{ + TpDerivedCharger{ + Tpid: "testTPid", + Loadid: "TEST_LOADID", + Direction: "*out", + Tenant: "cgrates.org", + Category: "call", + Account: "1000", + Subject: "test", + DestinationIds: "", + Runid: "default", + RunFilters: "", + ReqTypeField: "test", + DirectionField: "test", + TenantField: "test", + CategoryField: "test", + AccountField: "test", + SubjectField: "test", + DestinationField: "^+49151708707", + SetupTimeField: "test", + PddField: "~pdd:s/sip:(.+)/$1/", + AnswerTimeField: "~answertime2:s/sip:(.+)/$1/", + UsageField: "test", + SupplierField: "~supplier2:s/(.+)/$1/", + DisconnectCauseField: "test", + RatedField: "test", + CostField: "0", + }, + TpDerivedCharger{ + Tpid: "testTPid", + Loadid: "TEST_LOADID2", + Direction: "*out", + Tenant: "cgrates.org", + Category: "call", + Account: "1000", + Subject: "test", + DestinationIds: "", + Runid: "default", + RunFilters: "", + ReqTypeField: "test", + DirectionField: "test", + TenantField: "test", + CategoryField: "test", + AccountField: "test", + SubjectField: "test", + DestinationField: "^+49151708707", + SetupTimeField: "test", + PddField: "~pdd:s/sip:(.+)/$1/", + AnswerTimeField: "~answertime2:s/sip:(.+)/$1/", + UsageField: "test", + SupplierField: "~supplier2:s/(.+)/$1/", + DisconnectCauseField: "test", + RatedField: "test", + CostField: "0", + }, + } + if err := storDB.SetTpDerivedChargers(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpDerivedChargers(&filter); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].CostField = "test" + snd[1].CostField = "test" + if err := storDB.SetTpDerivedChargers(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpDerivedChargers(&filter); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpDerivedChargers(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpCdrStats(t *testing.T) { + // Fixme: Implement ErrNotfound in called method + // READ + // if _, err := storDB.GetTpCdrStats("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpCdrstat{ + TpCdrstat{ + Tpid: "testTPid", + Tag: "testTag1", + QueueLength: 0, + TimeWindow: "10m", + SaveInterval: "10s", + Metrics: "ACD", + SetupInterval: "", + Tors: "", + CdrHosts: "", + CdrSources: "", + ReqTypes: "", + Directions: "", + Tenants: "test", + Categories: "", + Accounts: "", + Subjects: "1001", + DestinationIds: "1003", + PddInterval: "", + UsageInterval: "", + Suppliers: "suppl2", + DisconnectCauses: "", + MediationRunids: "*default", + RatedAccounts: "", + RatedSubjects: "", + CostInterval: "", + ActionTriggers: "CDRST1001_WARN", + }, + TpCdrstat{ + Tpid: "testTPid", + Tag: "testTag2", + QueueLength: 0, + TimeWindow: "10m", + SaveInterval: "10s", + Metrics: "ACD", + SetupInterval: "", + Tors: "", + CdrHosts: "", + CdrSources: "", + ReqTypes: "", + Directions: "", + Tenants: "test", + Categories: "", + Accounts: "", + Subjects: "1001", + DestinationIds: "1003", + PddInterval: "", + UsageInterval: "", + Suppliers: "suppl2", + DisconnectCauses: "", + MediationRunids: "*default", + RatedAccounts: "", + RatedSubjects: "", + CostInterval: "", + ActionTriggers: "CDRST1001_WARN", + }, + } + if err := storDB.SetTpCdrStats(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpCdrStats("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].Categories = "test" + snd[1].Categories = "test" + if err := storDB.SetTpCdrStats(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpCdrStats("testTPid", ""); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpCdrStats("testTPid", ""); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpUsers(t *testing.T) { + // READ + var filter = TpUser{ + Tpid: "testTPid", + Tenant: "", + UserName: "", + Masked: true, + AttributeName: "", + AttributeValue: "", + Weight: 0.0, + } + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpUsers(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpUser{ + TpUser{ + Tpid: "testTPid", + Tenant: "cgrates.org", + UserName: "1001", + Masked: true, + AttributeName: "Account", + AttributeValue: "1001", + Weight: 0.0, + }, + TpUser{ + Tpid: "testTPid", + Tenant: "cgrates2.org", + UserName: "1001", + Masked: true, + AttributeName: "Account", + AttributeValue: "1001", + Weight: 0.0, + }, + } + if err := storDB.SetTpUsers(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpUsers(&filter); err != nil { + t.Error(err) + } else { + snd[0].Id = rcv[0].Id + snd[1].Id = rcv[1].Id + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // UPDATE + snd[0].Masked = false + snd[1].Masked = false + if err := storDB.SetTpUsers(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpUsers(&filter); err != nil { + t.Error(err) + } else { + snd[0].CreatedAt = rcv[0].CreatedAt + snd[1].CreatedAt = rcv[1].CreatedAt + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpUsers(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } +} + +func testStorDBitCRUDTpAliases(t *testing.T) { + // READ + var filter = TpAlias{ + Tpid: "testTPid", + Direction: "", + Tenant: "", + Category: "", + Account: "", + Subject: "", + DestinationId: "", + Context: "", + Target: "", + Original: "", + Alias: "", + Weight: 0.0, + } + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpAliases(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } + // WRITE + var snd = []TpAlias{ + TpAlias{ + Tpid: "testTPid", + Direction: "*out", + Tenant: "cgrates.org", + Category: "call", + Account: "1006", + Subject: "1006", + DestinationId: "*any", + Context: "*rating", + Target: "Subject", + Original: "1006", + Alias: "1001", + Weight: 10.0, + }, + TpAlias{ + Tpid: "testTPid", + Direction: "*out", + Tenant: "cgrates.org", + Category: "call", + Account: "1006", + Subject: "1006", + DestinationId: "*any", + Context: "*rating", + Target: "Subject", + Original: "1006", + Alias: "1001", + Weight: 10.0, + }, + } + if err := storDB.SetTpAliases(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpAliases(&filter); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(snd, rcv) { + // Fixme: TpAlias missing CreatedAt field + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + // UPDATE + snd[0].Target = "test" + snd[1].Target = "test" + if err := storDB.SetTpAliases(snd); err != nil { + t.Error(err) + } + // READ + if rcv, err := storDB.GetTpAliases(&filter); err != nil { + t.Error(err) + } else { + if !reflect.DeepEqual(snd, rcv) { + t.Errorf("Expecting: %+v, received: %+v", snd, rcv) + } + } + // REMOVE + if err := storDB.RemTpData("", "testTPid", nil); err != nil { + t.Error(err) + } + // READ + // Fixme: Implement ErrNotfound in called method + // if _, err := storDB.GetTpAliases(&filter); err != utils.ErrNotFound { + // t.Error(err) + // } +} + func testStorDBitFlush(t *testing.T) { if err := storDB.Flush(path.Join(cfg.DataFolderPath, "storage", cfg.StorDBType)); err != nil { t.Error(err) From b95835392ce2a5abec2d858c3403b779a8f62c8a Mon Sep 17 00:00:00 2001 From: alin104n Date: Tue, 14 Feb 2017 12:26:22 +0200 Subject: [PATCH 09/23] Redundant pointer parentheses removed --- engine/stordb_it_test.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/engine/stordb_it_test.go b/engine/stordb_it_test.go index 9561684a5..4a14701c4 100644 --- a/engine/stordb_it_test.go +++ b/engine/stordb_it_test.go @@ -167,9 +167,8 @@ func testStorDBitCRUDTpDestinations(t *testing.T) { if rcv, err := storDB.GetTPDestinations("testTPid", ""); err != nil { t.Error(err) } else { - //Fixme: Ensure the order of elements returned by GetTPDestinations - if !((reflect.DeepEqual(*(snd[0]), *(rcv[0])) && reflect.DeepEqual(*(snd[1]), *(rcv[1]))) || (reflect.DeepEqual(*(snd[0]), *(rcv[1])) && reflect.DeepEqual(*(snd[1]), *(rcv[0])))) { - t.Errorf("Expecting: %+v, received: %+v", *(snd[0]), *(rcv[0])) + if !(reflect.DeepEqual(snd[0], rcv[0]) || reflect.DeepEqual(snd[0], rcv[1])) { + t.Errorf("Expecting: %+v, received: %+v", snd[0], rcv[0]) } } // UPDATE @@ -182,9 +181,8 @@ func testStorDBitCRUDTpDestinations(t *testing.T) { if rcv, err := storDB.GetTPDestinations("testTPid", ""); err != nil { t.Error(err) } else { - //Fixme: Ensure the order of elements returned by GetTPDestinations - if !((reflect.DeepEqual(*(snd[0]), *(rcv[0])) && reflect.DeepEqual(*(snd[1]), *(rcv[1]))) || (reflect.DeepEqual(*(snd[0]), *(rcv[1])) && reflect.DeepEqual(*(snd[1]), *(rcv[0])))) { - t.Errorf("Expecting: %+v, received: %+v", *(snd[0]), *(rcv[0])) + if !(reflect.DeepEqual(snd[0], rcv[0]) || reflect.DeepEqual(snd[0], rcv[1])) { + t.Errorf("Expecting: %+v, received: %+v", snd[0], rcv[0]) } } // REMOVE From e11c2a5d72a725583601172da5d60b14c2f97131 Mon Sep 17 00:00:00 2001 From: Alin Ioanovici Date: Tue, 14 Feb 2017 10:48:01 +0200 Subject: [PATCH 10/23] Update CONTRIBUTORS.md --- CONTRIBUTORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 2a843a718..860b59727 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -54,6 +54,7 @@ information, please see the [`CONTRIBUTING.md`](CONTRIBUTING.md) file. | @KuikenArjan | Arjan Kuiken | | @Dobby16 | Arjan Kuiken | | @pauls1024 | Paul Smith | +| @alin104n | Alin Ioanovici |