diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 8d2570214..c5b22ba0a 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -140,6 +140,13 @@ func (at *ActionTrigger) sortDestinationIds() string { return strings.Join(destIds, utils.INFIELD_SEP) } +// makes a shallow copy of the receiver +func (at *ActionTrigger) Clone() *ActionTrigger { + clone := new(ActionTrigger) + *clone = *at + return clone +} + // Structure to store actions according to weight type ActionTriggers []*ActionTrigger @@ -159,3 +166,15 @@ func (atpl ActionTriggers) Less(j, i int) bool { func (atpl ActionTriggers) Sort() { sort.Sort(atpl) } + +// clone with new id(uuid) +func (atrs ActionTriggers) Clone() ActionTriggers { + // set ids to action triggers + var newATriggers ActionTriggers + for _, atr := range atrs { + newAtr := atr.Clone() + newAtr.Id = utils.GenUUID() + newATriggers = append(newATriggers, newAtr) + } + return newATriggers +} diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index 1aceb00e7..ed1b3d657 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -166,7 +166,7 @@ EE0,*allow_negative,,,*monetary,*out,,,,,*unlimited,,0,10,false,10 DEFEE,*cdrlog,"{""Category"":""^ddi"",""MediationRunId"":""^did_run""}",,,,,,,,,,,,false,10 NEG,*allow_negative,,,*monetary,*out,,,,,*unlimited,,0,10,false,10 ` - actionTimings = ` + actionPlans = ` MORE_MINUTES,MINI,ONE_TIME_RUN,10 MORE_MINUTES,SHARED,ONE_TIME_RUN,10 TOPUP10_AT,TOPUP10_AC,*asap,10 @@ -238,7 +238,7 @@ var csvr *TpReader func init() { csvr = NewTpReader(ratingStorage, accountingStorage, NewStringCSVStorage(',', destinations, timings, rates, destinationRates, ratingPlans, ratingProfiles, - sharedGroups, lcrs, actions, actionTimings, actionTriggers, accountActions, derivedCharges, cdrStats, users, aliases), "", "", 10) + sharedGroups, lcrs, actions, actionPlans, actionTriggers, accountActions, derivedCharges, cdrStats, users, aliases), "", "", 10) if err := csvr.LoadDestinations(); err != nil { log.Print("error in LoadDestinations:", err) } @@ -911,7 +911,7 @@ func TestLoadSharedGroups(t *testing.T) { } // execute action timings to fill memebers - atm := csvr.actionsTimings["MORE_MINUTES"][1] + atm := csvr.actionPlans["MORE_MINUTES"][1] atm.Execute() atm.actions, atm.stCache = nil, time.Time{} @@ -961,10 +961,10 @@ func TestLoadLCRs(t *testing.T) { } func TestLoadActionTimings(t *testing.T) { - if len(csvr.actionsTimings) != 6 { - t.Error("Failed to load action timings: ", len(csvr.actionsTimings)) + if len(csvr.actionPlans) != 6 { + t.Error("Failed to load action timings: ", len(csvr.actionPlans)) } - atm := csvr.actionsTimings["MORE_MINUTES"][0] + atm := csvr.actionPlans["MORE_MINUTES"][0] expected := &ActionPlan{ Uuid: atm.Uuid, Id: "MORE_MINUTES", @@ -992,7 +992,6 @@ func TestLoadActionTriggers(t *testing.T) { } atr := csvr.actionsTriggers["STANDARD_TRIGGER"][0] expected := &ActionTrigger{ - Id: "st0", BalanceType: utils.VOICE, BalanceDirection: OUTBOUND, ThresholdType: TRIGGER_MIN_COUNTER, @@ -1003,11 +1002,10 @@ func TestLoadActionTriggers(t *testing.T) { Executed: false, } if !reflect.DeepEqual(atr, expected) { - t.Error("Error loading action trigger: ", atr) + t.Errorf("Error loading action trigger: %+v", atr) } atr = csvr.actionsTriggers["STANDARD_TRIGGER"][1] expected = &ActionTrigger{ - Id: "st1", BalanceType: utils.VOICE, BalanceDirection: OUTBOUND, ThresholdType: TRIGGER_MAX_BALANCE, @@ -1018,7 +1016,7 @@ func TestLoadActionTriggers(t *testing.T) { Executed: false, } if !reflect.DeepEqual(atr, expected) { - t.Error("Error loading action trigger: ", atr) + t.Errorf("Error loading action trigger: %+v", atr) } } @@ -1031,8 +1029,12 @@ func TestLoadAccountActions(t *testing.T) { Id: "*out:vdf:minitsboy", ActionTriggers: csvr.actionsTriggers["STANDARD_TRIGGER"], } + // set propper uuid + for i, atr := range aa.ActionTriggers { + csvr.actionsTriggers["STANDARD_TRIGGER"][i].Id = atr.Id + } if !reflect.DeepEqual(aa, expected) { - t.Error("Error loading account action: ", aa) + t.Errorf("Error loading account action: %+v", aa.ActionTriggers[0]) } // test that it does not overwrite balances existing, err := accountingStorage.GetAccount(aa.Id) diff --git a/engine/loader_local_test.go b/engine/loader_local_test.go index 4fa79dcbe..ee72d937f 100644 --- a/engine/loader_local_test.go +++ b/engine/loader_local_test.go @@ -405,7 +405,7 @@ func TestMatchLoadCsvWithStorRating(t *testing.T) { continue } if len(refVal) != len(qVal) { - t.Errorf("Missmatched data for key: %s\n\t, reference val: %s \n\t retrieved value: %s\n on iteration: %d", key, refVal, qVal, idx) + t.Errorf("Missmatched data for key: %s\n\t reference val: %s \n\t retrieved val: %s\n on iteration: %d", key, refVal, qVal, idx) } } } diff --git a/engine/tp_reader.go b/engine/tp_reader.go index fb20ff201..3d47d141b 100644 --- a/engine/tp_reader.go +++ b/engine/tp_reader.go @@ -19,7 +19,7 @@ type TpReader struct { accountingStorage AccountingStorage lr LoadReader actions map[string][]*Action - actionsTimings map[string][]*ActionPlan + actionPlans map[string][]*ActionPlan actionsTriggers map[string]ActionTriggers accountActions map[string]*Account dirtyRpAliases []*TenantRatingSubject // used to clean aliases that might have changed @@ -48,7 +48,7 @@ func NewTpReader(rs RatingStorage, as AccountingStorage, lr LoadReader, tpid, ti accountingStorage: as, lr: lr, actions: make(map[string][]*Action), - actionsTimings: make(map[string][]*ActionPlan), + actionPlans: make(map[string][]*ActionPlan), actionsTriggers: make(map[string]ActionTriggers), rates: make(map[string]*utils.TPRate), destinations: make(map[string]*Destination), @@ -586,7 +586,7 @@ func (tpr *TpReader) LoadActionPlans() (err error) { }, ActionsId: at.ActionsId, } - tpr.actionsTimings[atId] = append(tpr.actionsTimings[atId], actTmg) + tpr.actionPlans[atId] = append(tpr.actionPlans[atId], actTmg) } } @@ -606,16 +606,11 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, atr := range atrsLst { balanceExpirationDate, _ := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) - id := atr.Id - if id == "" { - id = utils.GenUUID() - } minSleep, err := utils.ParseDurationWithSecs(atr.MinSleep) if err != nil { return err } atrs[idx] = &ActionTrigger{ - Id: id, ThresholdType: atr.ThresholdType, ThresholdValue: atr.ThresholdValue, Recurrent: atr.Recurrent, @@ -634,9 +629,6 @@ func (tpr *TpReader) LoadActionTriggers() (err error) { ActionsId: atr.ActionsId, MinQueuedItems: atr.MinQueuedItems, } - if atrs[idx].Id == "" { - atrs[idx].Id = utils.GenUUID() - } } tpr.actionsTriggers[key] = atrs } @@ -732,7 +724,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error actionTimings = append(actionTimings, actTmg) } - // write action timings + // write action triggers err = tpr.ratingStorage.SetActionPlans(accountAction.ActionPlanId, actionTimings) if err != nil { return errors.New(err.Error() + " (SetActionPlan): " + accountAction.ActionPlanId) @@ -755,10 +747,13 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error for key, atrsLst := range atrs { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, apiAtr := range atrsLst { + minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep) expTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) - atrs[idx] = &ActionTrigger{Id: utils.GenUUID(), + atrs[idx] = &ActionTrigger{ ThresholdType: apiAtr.ThresholdType, ThresholdValue: apiAtr.ThresholdValue, + Recurrent: apiAtr.Recurrent, + MinSleep: minSleep, BalanceId: apiAtr.BalanceId, BalanceType: apiAtr.BalanceType, BalanceDirection: apiAtr.BalanceDirection, @@ -779,6 +774,11 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error for _, atr := range actionTriggers { actionsIds = append(actionsIds, atr.ActionsId) } + // write action triggers + err = tpr.ratingStorage.SetActionTriggers(accountAction.ActionTriggersId, actionTriggers) + if err != nil { + return errors.New(err.Error() + " (SetActionTriggers): " + accountAction.ActionTriggersId) + } } // actions @@ -828,7 +828,7 @@ func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error Id: id, } } - ub.ActionTriggers = actionTriggers + ub.ActionTriggers = actionTriggers.Clone() if err := tpr.accountingStorage.SetAccount(ub); err != nil { return err @@ -851,19 +851,20 @@ func (tpr *TpReader) LoadAccountActions() (err error) { if _, alreadyDefined := tpr.accountActions[aa.KeyId()]; alreadyDefined { return fmt.Errorf("duplicate account action found: %s", aa.KeyId()) } - var aTriggers []*ActionTrigger + var aTriggers ActionTriggers if aa.ActionTriggersId != "" { var exists bool if aTriggers, exists = tpr.actionsTriggers[aa.ActionTriggersId]; !exists { return fmt.Errorf("could not get action triggers for tag %s", aa.ActionTriggersId) } } + ub := &Account{ Id: aa.KeyId(), - ActionTriggers: aTriggers, + ActionTriggers: aTriggers.Clone(), } tpr.accountActions[aa.KeyId()] = ub - aTimings, exists := tpr.actionsTimings[aa.ActionPlanId] + aTimings, exists := tpr.actionPlans[aa.ActionPlanId] if !exists { log.Printf("could not get action plan for tag %v", aa.ActionPlanId) // must not continue here @@ -947,10 +948,13 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { for _, atrsLst := range atrsM { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, apiAtr := range atrsLst { + minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep) expTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) - atrs[idx] = &ActionTrigger{Id: utils.GenUUID(), + atrs[idx] = &ActionTrigger{ ThresholdType: apiAtr.ThresholdType, ThresholdValue: apiAtr.ThresholdValue, + Recurrent: apiAtr.Recurrent, + MinSleep: minSleep, BalanceId: apiAtr.BalanceId, BalanceType: apiAtr.BalanceType, BalanceDirection: apiAtr.BalanceDirection, @@ -977,6 +981,11 @@ func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { // only return error if there was something there for the tag return fmt.Errorf("could not get action triggers for cdr stats id %s: %s", cs.Id, triggerTag) } + // write action triggers + err = tpr.ratingStorage.SetActionTriggers(triggerTag, triggers) + if err != nil { + return errors.New(err.Error() + " (SetActionTriggers): " + triggerTag) + } UpdateCdrStats(cs, triggers, tpStat, tpr.timezone) tpr.cdrStats[tag] = cs } @@ -1279,7 +1288,7 @@ func (tpr *TpReader) WriteToDatabase(flush, verbose bool) (err error) { if verbose { log.Print("Action Plans:") } - for k, ats := range tpr.actionsTimings { + for k, ats := range tpr.actionPlans { err = tpr.ratingStorage.SetActionPlans(k, ats) if err != nil { return err @@ -1452,7 +1461,7 @@ func (tpr *TpReader) ShowStatistics() { // actions log.Print("Actions: ", len(tpr.actions)) // action plans - log.Print("Action plans: ", len(tpr.actionsTimings)) + log.Print("Action plans: ", len(tpr.actionPlans)) // action trigers log.Print("Action trigers: ", len(tpr.actionsTriggers)) // account actions @@ -1492,7 +1501,7 @@ func (tpr *TpReader) GetLoadedIds(categ string) ([]string, error) { i++ } return keys, nil - case utils.ACTION_PREFIX: // actionsTimings + case utils.ACTION_PREFIX: // actionPlans keys := make([]string, len(tpr.actions)) i := 0 for k := range tpr.actions { @@ -1500,10 +1509,10 @@ func (tpr *TpReader) GetLoadedIds(categ string) ([]string, error) { i++ } return keys, nil - case utils.ACTION_TIMING_PREFIX: // actionsTimings - keys := make([]string, len(tpr.actionsTimings)) + case utils.ACTION_TIMING_PREFIX: // actionPlans + keys := make([]string, len(tpr.actionPlans)) i := 0 - for k := range tpr.actionsTimings { + for k := range tpr.actionPlans { keys[i] = k i++ }