func GetUB() *Account { uc := &UnitCounter{ Counters: CounterFilters{&CounterFilter{Value: 1}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET"))}}}, } at := &ActionTrigger{ ID: "some_uuid", ThresholdValue: 100.0, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), }, Weight: 10.0, ActionsID: "Commando", } var zeroTime time.Time zeroTime = zeroTime.UTC() // for deep equal to find location ub := &Account{ ID: "rif", AllowNegative: true, BalanceMap: map[string]Balances{utils.SMS: Balances{&Balance{Value: 14, ExpirationDate: zeroTime}}, utils.DATA: Balances{&Balance{Value: 1024, ExpirationDate: zeroTime}}, utils.VOICE: Balances{&Balance{Weight: 20, DestinationIDs: utils.NewStringMap("NAT")}, &Balance{Weight: 10, DestinationIDs: utils.NewStringMap("RET")}}}, UnitCounters: UnitCounters{utils.SMS: []*UnitCounter{uc, uc}}, ActionTriggers: ActionTriggers{at, at, at}, } return ub }
func (bf *BalanceFilter) Clone() *BalanceFilter { result := &BalanceFilter{} if bf.Uuid != nil { result.Uuid = new(string) *result.Uuid = *bf.Uuid } if bf.ID != nil { result.ID = new(string) *result.ID = *bf.ID } if bf.Value != nil { result.Value = new(utils.ValueFormula) *result.Value = *bf.Value } if bf.RatingSubject != nil { result.RatingSubject = new(string) *result.RatingSubject = *bf.RatingSubject } if bf.Type != nil { result.Type = new(string) *result.Type = *bf.Type } if bf.ExpirationDate != nil { result.ExpirationDate = new(time.Time) *result.ExpirationDate = *bf.ExpirationDate } if bf.Weight != nil { result.Weight = new(float64) *result.Weight = *bf.Weight } if bf.Disabled != nil { result.Disabled = new(bool) *result.Disabled = *bf.Disabled } if bf.Blocker != nil { result.Blocker = new(bool) *result.Blocker = *bf.Blocker } if bf.Factor != nil { result.Factor = new(ValueFactor) *result.Factor = *bf.Factor } if bf.Directions != nil { result.Directions = utils.StringMapPointer(bf.Directions.Clone()) } if bf.DestinationIDs != nil { result.DestinationIDs = utils.StringMapPointer(bf.DestinationIDs.Clone()) } if bf.Categories != nil { result.Categories = utils.StringMapPointer(bf.Categories.Clone()) } if bf.SharedGroups != nil { result.SharedGroups = utils.StringMapPointer(bf.SharedGroups.Clone()) } if bf.TimingIDs != nil { result.TimingIDs = utils.StringMapPointer(bf.TimingIDs.Clone()) } return result }
func (self *ApierV1) RemoveBalances(attr *utils.AttrSetBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } var expTime *time.Time if attr.ExpiryTime != nil { expTimeVal, err := utils.ParseTimeDetectLayout(*attr.ExpiryTime, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() return err } expTime = &expTimeVal } accID := utils.AccountKey(attr.Tenant, attr.Account) if _, err := self.AccountDb.GetAccount(accID); err != nil { return utils.ErrNotFound } at := &engine.ActionTiming{} at.SetAccountIDs(utils.StringMap{accID: true}) a := &engine.Action{ ActionType: engine.REMOVE_BALANCE, Balance: &engine.BalanceFilter{ Uuid: attr.BalanceUUID, ID: attr.BalanceID, Type: utils.StringPointer(attr.BalanceType), ExpirationDate: expTime, RatingSubject: attr.RatingSubject, Weight: attr.Weight, Blocker: attr.Blocker, Disabled: attr.Disabled, }, } if attr.Value != nil { a.Balance.Value = &utils.ValueFormula{Static: *attr.Value} } if attr.Directions != nil { a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions)) } if attr.DestinationIds != nil { a.Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.DestinationIds)) } if attr.Categories != nil { a.Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(*attr.Categories)) } if attr.SharedGroups != nil { a.Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(*attr.SharedGroups)) } if attr.TimingIds != nil { a.Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.TimingIds)) } at.SetActions(engine.Actions{a}) if err := at.Execute(); err != nil { *reply = err.Error() return err } *reply = OK return nil }
func testOnStorITCacheActions(t *testing.T) { acts := Actions{ &Action{ Id: "MINI", ActionType: TOPUP_RESET, ExpirationString: UNLIMITED, Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Uuid: utils.StringPointer(utils.GenUUID()), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: &utils.ValueFormula{Static: 10, Params: make(map[string]interface{})}, Weight: utils.Float64Pointer(10), Disabled: utils.BoolPointer(false), Timings: make([]*RITiming, 0), Blocker: utils.BoolPointer(false), }, }, &Action{ Id: "MINI", ActionType: TOPUP, ExpirationString: UNLIMITED, Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Uuid: utils.StringPointer(utils.GenUUID()), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: &utils.ValueFormula{Static: 100, Params: make(map[string]interface{})}, Weight: utils.Float64Pointer(10), RatingSubject: utils.StringPointer("test"), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Disabled: utils.BoolPointer(false), Timings: make([]*RITiming, 0), Blocker: utils.BoolPointer(false), }, }, } if err := onStor.SetActions(acts[0].Id, acts, utils.NonTransactional); err != nil { t.Error(err) } if _, hasIt := cache.Get(utils.ACTION_PREFIX + acts[0].Id); hasIt { t.Error("Already in cache") } if err := onStor.CacheDataFromDB(utils.ACTION_PREFIX, []string{acts[0].Id}, false); err != nil { t.Error(err) } if itm, hasIt := cache.Get(utils.ACTION_PREFIX + acts[0].Id); !hasIt { t.Error("Did not cache") } else if rcv := itm.(Actions); !reflect.DeepEqual(acts, rcv) { t.Errorf("Expecting: %+v, received: %+v", acts, rcv) } }
func (self *ApierV1) SetActions(attrs V1AttrSetActions, reply *string) (err error) { if missing := utils.MissingStructFields(&attrs, []string{"ActionsId", "Actions"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } for _, action := range attrs.Actions { requiredFields := []string{"Identifier", "Weight"} if action.BalanceType != "" { // Add some inter-dependent parameters - if balanceType then we are not talking about simply calling actions requiredFields = append(requiredFields, "Direction", "Units") } if missing := utils.MissingStructFields(action, requiredFields); len(missing) != 0 { return fmt.Errorf("%s:Action:%s:%v", utils.ErrMandatoryIeMissing.Error(), action.Identifier, missing) } } if !attrs.Overwrite { if exists, err := self.RatingDb.HasData(utils.ACTION_PREFIX, attrs.ActionsId); err != nil { return utils.NewErrServerError(err) } else if exists { return utils.ErrExists } } storeActions := make(engine.Actions, len(attrs.Actions)) for idx, apiAct := range attrs.Actions { a := &engine.Action{ Id: attrs.ActionsId, ActionType: apiAct.Identifier, Weight: apiAct.Weight, ExpirationString: apiAct.ExpiryTime, ExtraParameters: apiAct.ExtraParameters, Filter: apiAct.Filter, Balance: &engine.BalanceFilter{ // TODO: update this part Uuid: utils.StringPointer(apiAct.BalanceUuid), ID: utils.StringPointer(apiAct.BalanceId), Type: utils.StringPointer(apiAct.BalanceType), Value: &utils.ValueFormula{Static: apiAct.Units}, Weight: apiAct.BalanceWeight, Directions: utils.StringMapPointer(utils.ParseStringMap(apiAct.Directions)), DestinationIDs: utils.StringMapPointer(utils.ParseStringMap(apiAct.DestinationIds)), RatingSubject: utils.StringPointer(apiAct.RatingSubject), SharedGroups: utils.StringMapPointer(utils.ParseStringMap(apiAct.SharedGroups)), }, } storeActions[idx] = a } if err := self.RatingDb.SetActions(attrs.ActionsId, storeActions, utils.NonTransactional); err != nil { return utils.NewErrServerError(err) } if err = self.RatingDb.CacheDataFromDB(utils.ACTION_PREFIX, []string{attrs.ActionsId}, true); err != nil { utils.NewErrServerError(err) } *reply = OK return nil }
func TestLoadAccountActions(t *testing.T) { if len(csvr.accountActions) != 17 { t.Error("Failed to load account actions: ", len(csvr.accountActions)) } aa := csvr.accountActions["vdf:minitsboy"] expected := &Account{ ID: "vdf:minitsboy", UnitCounters: UnitCounters{ utils.VOICE: []*UnitCounter{ &UnitCounter{ CounterType: "*event", Counters: CounterFilters{ &CounterFilter{ Value: 0, Filter: &BalanceFilter{ ID: utils.StringPointer("st0"), Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap("*out")), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), SharedGroups: nil, Categories: nil, TimingIDs: nil, }, }, }, }, }, }, ActionTriggers: csvr.actionsTriggers["STANDARD_TRIGGER"], } // set propper uuid for i, atr := range aa.ActionTriggers { csvr.actionsTriggers["STANDARD_TRIGGER"][i].ID = atr.ID } for i, b := range aa.UnitCounters[utils.VOICE][0].Counters { expected.UnitCounters[utils.VOICE][0].Counters[i].Filter.ID = b.Filter.ID } if !reflect.DeepEqual(aa.UnitCounters[utils.VOICE][0].Counters[0], expected.UnitCounters[utils.VOICE][0].Counters[0]) { t.Errorf("Error loading account action: %+v", utils.ToIJSON(aa.UnitCounters[utils.VOICE][0].Counters[0].Filter)) } // test that it does not overwrite balances existing, err := accountingStorage.GetAccount(aa.ID) if err != nil || len(existing.BalanceMap) != 2 { t.Errorf("The account was not set before load: %+v", existing) } accountingStorage.SetAccount(aa) existing, err = accountingStorage.GetAccount(aa.ID) if err != nil || len(existing.BalanceMap) != 2 { t.Errorf("The set account altered the balances: %+v", existing) } }
func TestLoadActionTriggers(t *testing.T) { if len(csvr.actionsTriggers) != 7 { t.Error("Failed to load action triggers: ", len(csvr.actionsTriggers)) } atr := csvr.actionsTriggers["STANDARD_TRIGGER"][0] expected := &ActionTrigger{ ID: "STANDARD_TRIGGER", UniqueID: "st0", ThresholdType: utils.TRIGGER_MIN_EVENT_COUNTER, ThresholdValue: 10, Balance: &BalanceFilter{ ID: nil, Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY_O2")), Categories: nil, TimingIDs: nil, SharedGroups: nil, Disabled: nil, Blocker: nil, }, Weight: 10, ActionsID: "SOME_1", Executed: false, } if !reflect.DeepEqual(atr, expected) { t.Errorf("Error loading action trigger: %+v", utils.ToIJSON(atr.Balance)) } atr = csvr.actionsTriggers["STANDARD_TRIGGER"][1] expected = &ActionTrigger{ ID: "STANDARD_TRIGGER", UniqueID: "st1", ThresholdType: utils.TRIGGER_MAX_BALANCE, ThresholdValue: 200, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("GERMANY")), Categories: nil, TimingIDs: nil, SharedGroups: nil, }, Weight: 10, ActionsID: "SOME_2", Executed: false, } if !reflect.DeepEqual(atr, expected) { t.Errorf("Error loading action trigger: %+v", atr) } }
func TestBalanceMatchActionTriggerSharedGroup(t *testing.T) { at := &ActionTrigger{Balance: &BalanceFilter{SharedGroups: utils.StringMapPointer(utils.NewStringMap("test"))}} b := &Balance{SharedGroups: utils.NewStringMap("test")} if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.SharedGroups = utils.NewStringMap("test1") if b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.SharedGroups = utils.NewStringMap("") if b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } b.SharedGroups = utils.NewStringMap("test") at.Balance.SharedGroups = nil if !b.MatchActionTrigger(at) { t.Errorf("Error matching action trigger: %+v %+v", b, at) } }
func (mig MigratorRC8) migrateActionTriggersInt() error { keys, err := mig.db.Cmd("KEYS", utils.ACTION_TRIGGER_PREFIX+"*").List() if err != nil { return err } newAtrsMap := make(map[string]engine.ActionTriggers, len(keys)) for _, key := range keys { log.Printf("Migrating action trigger: %s...", key) var oldAtrs ActionTriggers1 var values []byte if values, err = mig.db.Cmd("GET", key).Bytes(); err == nil { if err := mig.ms.Unmarshal(values, &oldAtrs); err != nil { return err } } newAtrs := make(engine.ActionTriggers, len(oldAtrs)) for index, oldAtr := range oldAtrs { at := &engine.ActionTrigger{ ID: oldAtr.ID, UniqueID: oldAtr.UniqueID, ThresholdType: oldAtr.ThresholdType, ThresholdValue: oldAtr.ThresholdValue, Recurrent: oldAtr.Recurrent, MinSleep: oldAtr.MinSleep, Weight: oldAtr.Weight, ActionsID: oldAtr.ActionsId, MinQueuedItems: oldAtr.MinQueuedItems, Executed: oldAtr.Executed, } bf := &engine.BalanceFilter{} if oldAtr.BalanceId != "" { bf.ID = utils.StringPointer(oldAtr.BalanceId) } if oldAtr.BalanceType != "" { bf.Type = utils.StringPointer(oldAtr.BalanceType) } if oldAtr.BalanceRatingSubject != "" { bf.RatingSubject = utils.StringPointer(oldAtr.BalanceRatingSubject) } if !oldAtr.BalanceDirections.IsEmpty() { bf.Directions = utils.StringMapPointer(oldAtr.BalanceDirections) } if !oldAtr.BalanceDestinationIds.IsEmpty() { bf.DestinationIDs = utils.StringMapPointer(oldAtr.BalanceDestinationIds) } if !oldAtr.BalanceTimingTags.IsEmpty() { bf.TimingIDs = utils.StringMapPointer(oldAtr.BalanceTimingTags) } if !oldAtr.BalanceCategories.IsEmpty() { bf.Categories = utils.StringMapPointer(oldAtr.BalanceCategories) } if !oldAtr.BalanceSharedGroups.IsEmpty() { bf.SharedGroups = utils.StringMapPointer(oldAtr.BalanceSharedGroups) } if oldAtr.BalanceWeight != 0 { bf.Weight = utils.Float64Pointer(oldAtr.BalanceWeight) } if oldAtr.BalanceDisabled != false { bf.Disabled = utils.BoolPointer(oldAtr.BalanceDisabled) } if !oldAtr.BalanceExpirationDate.IsZero() { bf.ExpirationDate = utils.TimePointer(oldAtr.BalanceExpirationDate) } at.Balance = bf newAtrs[index] = at } newAtrsMap[key] = newAtrs } // write data back for key, atrs := range newAtrsMap { result, err := mig.ms.Marshal(&atrs) if err != nil { return err } if err = mig.db.Cmd("SET", key, result).Err; err != nil { return err } } return nil }
func (self *ApierV1) SetActionTrigger(attr AttrSetActionTrigger, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"GroupID"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } atrs, _ := self.RatingDb.GetActionTriggers(attr.GroupID, false, utils.NonTransactional) var newAtr *engine.ActionTrigger if attr.UniqueID != "" { //search for exiting one for _, atr := range atrs { if atr.UniqueID == attr.UniqueID { newAtr = atr break } } } if newAtr == nil { newAtr = &engine.ActionTrigger{} atrs = append(atrs, newAtr) } newAtr.ID = attr.GroupID if attr.UniqueID != "" { newAtr.UniqueID = attr.UniqueID } else { newAtr.UniqueID = utils.GenUUID() } if attr.ThresholdType != nil { newAtr.ThresholdType = *attr.ThresholdType } if attr.ThresholdValue != nil { newAtr.ThresholdValue = *attr.ThresholdValue } if attr.Recurrent != nil { newAtr.Recurrent = *attr.Recurrent } if attr.MinSleep != nil { minSleep, err := utils.ParseDurationWithSecs(*attr.MinSleep) if err != nil { *reply = err.Error() return err } newAtr.MinSleep = minSleep } if attr.ExpirationDate != nil { expTime, err := utils.ParseTimeDetectLayout(*attr.ExpirationDate, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() return err } newAtr.ExpirationDate = expTime } if attr.ActivationDate != nil { actTime, err := utils.ParseTimeDetectLayout(*attr.ActivationDate, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() return err } newAtr.ActivationDate = actTime } newAtr.Balance = &engine.BalanceFilter{} if attr.BalanceID != nil { newAtr.Balance.ID = attr.BalanceID } if attr.BalanceType != nil { newAtr.Balance.Type = attr.BalanceType } if attr.BalanceDirections != nil { newAtr.Balance.Directions = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDirections...)) } if attr.BalanceDestinationIds != nil { newAtr.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...)) } if attr.BalanceWeight != nil { newAtr.Balance.Weight = attr.BalanceWeight } if attr.BalanceExpirationDate != nil { balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate) if err != nil { *reply = err.Error() return err } newAtr.Balance.ExpirationDate = &balanceExpTime } if attr.BalanceTimingTags != nil { newAtr.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...)) } if attr.BalanceRatingSubject != nil { newAtr.Balance.RatingSubject = attr.BalanceRatingSubject } if attr.BalanceCategories != nil { newAtr.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...)) } if attr.BalanceSharedGroups != nil { newAtr.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...)) } if attr.BalanceBlocker != nil { newAtr.Balance.Blocker = attr.BalanceBlocker } if attr.BalanceDisabled != nil { newAtr.Balance.Disabled = attr.BalanceDisabled } if attr.MinQueuedItems != nil { newAtr.MinQueuedItems = *attr.MinQueuedItems } if attr.ActionsID != nil { newAtr.ActionsID = *attr.ActionsID } if err := self.RatingDb.SetActionTriggers(attr.GroupID, atrs, utils.NonTransactional); err != nil { *reply = err.Error() return err } //no cache for action triggers *reply = utils.OK return nil }
func (self *ApierV1) SetAccountActionTriggers(attr AttrSetAccountActionTriggers, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } accID := utils.AccountKey(attr.Tenant, attr.Account) var account *engine.Account _, err := engine.Guardian.Guard(func() (interface{}, error) { if acc, err := self.AccountDb.GetAccount(accID); err == nil { account = acc } else { return 0, err } for _, at := range account.ActionTriggers { if (attr.UniqueID == "" || at.UniqueID == attr.UniqueID) && (attr.GroupID == "" || at.ID == attr.GroupID) { // we have a winner if attr.ThresholdType != nil { at.ThresholdType = *attr.ThresholdType } if attr.ThresholdValue != nil { at.ThresholdValue = *attr.ThresholdValue } if attr.Recurrent != nil { at.Recurrent = *attr.Recurrent } if attr.Executed != nil { at.Executed = *attr.Executed } if attr.MinSleep != nil { minSleep, err := utils.ParseDurationWithSecs(*attr.MinSleep) if err != nil { return 0, err } at.MinSleep = minSleep } if attr.ExpirationDate != nil { expTime, err := utils.ParseTimeDetectLayout(*attr.ExpirationDate, self.Config.DefaultTimezone) if err != nil { return 0, err } at.ExpirationDate = expTime } if attr.ActivationDate != nil { actTime, err := utils.ParseTimeDetectLayout(*attr.ActivationDate, self.Config.DefaultTimezone) if err != nil { return 0, err } at.ActivationDate = actTime } at.Balance = &engine.BalanceFilter{} if attr.BalanceID != nil { at.Balance.ID = attr.BalanceID } if attr.BalanceType != nil { at.Balance.Type = attr.BalanceType } if attr.BalanceDirections != nil { at.Balance.Directions = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDirections...)) } if attr.BalanceDestinationIds != nil { at.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...)) } if attr.BalanceWeight != nil { at.Balance.Weight = attr.BalanceWeight } if attr.BalanceExpirationDate != nil { balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate) if err != nil { return 0, err } at.Balance.ExpirationDate = &balanceExpTime } if attr.BalanceTimingTags != nil { at.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...)) } if attr.BalanceRatingSubject != nil { at.Balance.RatingSubject = attr.BalanceRatingSubject } if attr.BalanceCategories != nil { at.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...)) } if attr.BalanceSharedGroups != nil { at.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...)) } if attr.BalanceBlocker != nil { at.Balance.Blocker = attr.BalanceBlocker } if attr.BalanceDisabled != nil { at.Balance.Disabled = attr.BalanceDisabled } if attr.MinQueuedItems != nil { at.MinQueuedItems = *attr.MinQueuedItems } if attr.ActionsID != nil { at.ActionsID = *attr.ActionsID } } } account.ExecuteActionTriggers(nil) if err := self.AccountDb.SetAccount(account); err != nil { return 0, err } return 0, nil }, 0, accID) if err != nil { *reply = err.Error() return err } *reply = utils.OK return nil }
func (attr *AttrSetAccountActionTriggers) UpdateActionTrigger(at *engine.ActionTrigger, timezone string) (updated bool, err error) { if at == nil { return false, errors.New("Empty ActionTrigger") } if at.ID == "" { // New AT, update it's data if missing := utils.MissingStructFields(attr, []string{"GroupID", "ThresholdType", "ThresholdValue"}); len(missing) != 0 { return false, utils.NewErrMandatoryIeMissing(missing...) } at.ID = *attr.GroupID if attr.UniqueID != nil { at.UniqueID = *attr.UniqueID } } if attr.GroupID != nil && *attr.GroupID != at.ID { return } if attr.UniqueID != nil && *attr.UniqueID != at.UniqueID { return } // at matches updated = true if attr.ThresholdType != nil { at.ThresholdType = *attr.ThresholdType } if attr.ThresholdValue != nil { at.ThresholdValue = *attr.ThresholdValue } if attr.Recurrent != nil { at.Recurrent = *attr.Recurrent } if attr.Executed != nil { at.Executed = *attr.Executed } if attr.MinSleep != nil { if at.MinSleep, err = utils.ParseDurationWithSecs(*attr.MinSleep); err != nil { return } } if attr.ExpirationDate != nil { if at.ExpirationDate, err = utils.ParseTimeDetectLayout(*attr.ExpirationDate, timezone); err != nil { return } } if attr.ActivationDate != nil { if at.ActivationDate, err = utils.ParseTimeDetectLayout(*attr.ActivationDate, timezone); err != nil { return } } if at.Balance == nil { at.Balance = &engine.BalanceFilter{} } if attr.BalanceID != nil { at.Balance.ID = attr.BalanceID } if attr.BalanceType != nil { at.Balance.Type = attr.BalanceType } if attr.BalanceDirections != nil { at.Balance.Directions = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDirections...)) } if attr.BalanceDestinationIds != nil { at.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceDestinationIds...)) } if attr.BalanceWeight != nil { at.Balance.Weight = attr.BalanceWeight } if attr.BalanceExpirationDate != nil { balanceExpTime, err := utils.ParseDate(*attr.BalanceExpirationDate) if err != nil { return false, err } at.Balance.ExpirationDate = &balanceExpTime } if attr.BalanceTimingTags != nil { at.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceTimingTags...)) } if attr.BalanceRatingSubject != nil { at.Balance.RatingSubject = attr.BalanceRatingSubject } if attr.BalanceCategories != nil { at.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceCategories...)) } if attr.BalanceSharedGroups != nil { at.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(*attr.BalanceSharedGroups...)) } if attr.BalanceBlocker != nil { at.Balance.Blocker = attr.BalanceBlocker } if attr.BalanceDisabled != nil { at.Balance.Disabled = attr.BalanceDisabled } if attr.MinQueuedItems != nil { at.MinQueuedItems = *attr.MinQueuedItems } if attr.ActionsID != nil { at.ActionsID = *attr.ActionsID } return }
func TestLoadActions(t *testing.T) { if len(csvr.actions) != 16 { t.Error("Failed to load actions: ", len(csvr.actions)) } as1 := csvr.actions["MINI"] expected := []*Action{ &Action{ Id: "MINI", ActionType: TOPUP_RESET, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Uuid: as1[0].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: &utils.ValueFormula{Static: 10}, Weight: utils.Float64Pointer(10), DestinationIDs: nil, TimingIDs: nil, SharedGroups: nil, Categories: nil, Disabled: utils.BoolPointer(false), Blocker: utils.BoolPointer(false), }, }, &Action{ Id: "MINI", ActionType: TOPUP, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Uuid: as1[1].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: &utils.ValueFormula{Static: 100}, Weight: utils.Float64Pointer(10), RatingSubject: utils.StringPointer("test"), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), TimingIDs: nil, SharedGroups: nil, Categories: nil, Disabled: utils.BoolPointer(false), Blocker: utils.BoolPointer(false), }, }, } if !reflect.DeepEqual(as1, expected) { t.Errorf("Error loading action1: %s", utils.ToIJSON(as1)) } as2 := csvr.actions["SHARED"] expected = []*Action{ &Action{ Id: "SHARED", ActionType: TOPUP, ExpirationString: UNLIMITED, Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: nil, Uuid: as2[0].Balance.Uuid, Value: &utils.ValueFormula{Static: 100}, Weight: utils.Float64Pointer(10), SharedGroups: utils.StringMapPointer(utils.NewStringMap("SG1")), TimingIDs: nil, Categories: nil, Disabled: utils.BoolPointer(false), Blocker: utils.BoolPointer(false), }, }, } if !reflect.DeepEqual(as2, expected) { t.Errorf("Error loading action: %s", utils.ToIJSON(as2)) } as3 := csvr.actions["DEFEE"] expected = []*Action{ &Action{ Id: "DEFEE", ActionType: CDRLOG, ExtraParameters: `{"Category":"^ddi","MediationRunId":"^did_run"}`, Weight: 10, Balance: &BalanceFilter{ Uuid: as3[0].Balance.Uuid, Directions: nil, DestinationIDs: nil, TimingIDs: nil, Categories: nil, SharedGroups: nil, Blocker: utils.BoolPointer(false), Disabled: utils.BoolPointer(false), }, }, } if !reflect.DeepEqual(as3, expected) { t.Errorf("Error loading action: %+v", as3[0].Balance) } asGnrc := csvr.actions["TOPUP_RST_GNR_1000"] //TOPUP_RST_GNR_1000,*topup_reset,"{""*voice"": 60.0,""*data"":1024.0,""*sms"":1.0}",,,*generic,*out,,*any,,,*unlimited,,1000,20,false,false,10 expected = []*Action{ &Action{ Id: "TOPUP_RST_GNR_1000", ActionType: TOPUP_RESET, ExtraParameters: `{"*voice": 60.0,"*data":1024.0,"*sms":1.0}`, Weight: 10, ExpirationString: utils.UNLIMITED, Balance: &BalanceFilter{ Uuid: asGnrc[0].Balance.Uuid, Type: utils.StringPointer(utils.GENERIC), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), //DestinationIDs: utils.StringMapPointer(utils.NewStringMap("*any")), Value: &utils.ValueFormula{Static: 1000}, Weight: utils.Float64Pointer(20), Disabled: utils.BoolPointer(false), Blocker: utils.BoolPointer(false), }, }, } if !reflect.DeepEqual(asGnrc, expected) { t.Errorf("Expecting: %+v, received: %+v", expected[0].Balance, asGnrc[0].Balance) } }
func (mig MigratorRC8) migrateAccountsInt() error { keys, err := mig.db.Cmd("KEYS", utils.ACCOUNT_PREFIX+"*").List() if err != nil { return err } newAccounts := make([]*engine.Account, 0) var migratedKeys []string // get existing accounts for _, key := range keys { log.Printf("Migrating account: %s...", key) values, err := mig.db.Cmd("GET", key).Bytes() if err != nil { continue } var oldAcc Account1 if err = mig.ms.Unmarshal(values, &oldAcc); err != nil { return err } // transfer data into new structurse newAcc := &engine.Account{ ID: oldAcc.Id, BalanceMap: make(map[string]engine.Balances, len(oldAcc.BalanceMap)), UnitCounters: make(engine.UnitCounters), ActionTriggers: make(engine.ActionTriggers, len(oldAcc.ActionTriggers)), AllowNegative: oldAcc.AllowNegative, Disabled: oldAcc.Disabled, } // balances balanceErr := false for key, oldBalChain := range oldAcc.BalanceMap { newAcc.BalanceMap[key] = make(engine.Balances, len(oldBalChain)) for index, oldBal := range oldBalChain { newAcc.BalanceMap[key][index] = &engine.Balance{ Uuid: oldBal.Uuid, ID: oldBal.Id, Value: oldBal.Value, Directions: oldBal.Directions, ExpirationDate: oldBal.ExpirationDate, Weight: oldBal.Weight, DestinationIDs: oldBal.DestinationIds, RatingSubject: oldBal.RatingSubject, Categories: oldBal.Categories, SharedGroups: oldBal.SharedGroups, Timings: oldBal.Timings, TimingIDs: oldBal.TimingIDs, Disabled: oldBal.Disabled, Factor: oldBal.Factor, Blocker: oldBal.Blocker, } } } if balanceErr { continue } // unit counters for _, oldUc := range oldAcc.UnitCounters { newUc := &engine.UnitCounter{ Counters: make(engine.CounterFilters, len(oldUc.Balances)), } for index, oldUcBal := range oldUc.Balances { b := &engine.Balance{ Uuid: oldUcBal.Uuid, ID: oldUcBal.Id, Value: oldUcBal.Value, Directions: oldUcBal.Directions, ExpirationDate: oldUcBal.ExpirationDate, Weight: oldUcBal.Weight, DestinationIDs: oldUcBal.DestinationIds, RatingSubject: oldUcBal.RatingSubject, Categories: oldUcBal.Categories, SharedGroups: oldUcBal.SharedGroups, Timings: oldUcBal.Timings, TimingIDs: oldUcBal.TimingIDs, Disabled: oldUcBal.Disabled, Factor: oldUcBal.Factor, Blocker: oldUcBal.Blocker, } bf := &engine.BalanceFilter{} bf.LoadFromBalance(b) cf := &engine.CounterFilter{ Value: oldUcBal.Value, Filter: bf, } newUc.Counters[index] = cf } newAcc.UnitCounters[oldUc.BalanceType] = append(newAcc.UnitCounters[oldUc.BalanceType], newUc) } // action triggers for index, oldAtr := range oldAcc.ActionTriggers { at := &engine.ActionTrigger{ ID: oldAtr.ID, UniqueID: oldAtr.UniqueID, ThresholdType: oldAtr.ThresholdType, ThresholdValue: oldAtr.ThresholdValue, Recurrent: oldAtr.Recurrent, MinSleep: oldAtr.MinSleep, Weight: oldAtr.Weight, ActionsID: oldAtr.ActionsId, MinQueuedItems: oldAtr.MinQueuedItems, Executed: oldAtr.Executed, } bf := &engine.BalanceFilter{} if oldAtr.BalanceId != "" { bf.ID = utils.StringPointer(oldAtr.BalanceId) } if oldAtr.BalanceType != "" { bf.Type = utils.StringPointer(oldAtr.BalanceType) } if oldAtr.BalanceRatingSubject != "" { bf.RatingSubject = utils.StringPointer(oldAtr.BalanceRatingSubject) } if !oldAtr.BalanceDirections.IsEmpty() { bf.Directions = utils.StringMapPointer(oldAtr.BalanceDirections) } if !oldAtr.BalanceDestinationIds.IsEmpty() { bf.DestinationIDs = utils.StringMapPointer(oldAtr.BalanceDestinationIds) } if !oldAtr.BalanceTimingTags.IsEmpty() { bf.TimingIDs = utils.StringMapPointer(oldAtr.BalanceTimingTags) } if !oldAtr.BalanceCategories.IsEmpty() { bf.Categories = utils.StringMapPointer(oldAtr.BalanceCategories) } if !oldAtr.BalanceSharedGroups.IsEmpty() { bf.SharedGroups = utils.StringMapPointer(oldAtr.BalanceSharedGroups) } if oldAtr.BalanceWeight != 0 { bf.Weight = utils.Float64Pointer(oldAtr.BalanceWeight) } if oldAtr.BalanceDisabled != false { bf.Disabled = utils.BoolPointer(oldAtr.BalanceDisabled) } if !oldAtr.BalanceExpirationDate.IsZero() { bf.ExpirationDate = utils.TimePointer(oldAtr.BalanceExpirationDate) } at.Balance = bf newAcc.ActionTriggers[index] = at } newAcc.InitCounters() newAccounts = append(newAccounts, newAcc) migratedKeys = append(migratedKeys, key) } // write data back for _, newAcc := range newAccounts { result, err := mig.ms.Marshal(newAcc) if err != nil { return err } if err := mig.db.Cmd("SET", utils.ACCOUNT_PREFIX+newAcc.ID, result).Err; err != nil { return err } } notMigrated := len(keys) - len(migratedKeys) if notMigrated > 0 { log.Printf("WARNING: there are %d accounts that failed migration!", notMigrated) } return err }
func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType", "Value"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } var expTime *time.Time if attr.ExpiryTime != nil { expTimeVal, err := utils.ParseTimeDetectLayout(*attr.ExpiryTime, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() return err } expTime = &expTimeVal } accID := utils.AccountKey(attr.Tenant, attr.Account) if _, err := self.AccountDb.GetAccount(accID); err != nil { // create account if not exists account := &engine.Account{ ID: accID, } if err := self.AccountDb.SetAccount(account); err != nil { *reply = err.Error() return err } } at := &engine.ActionTiming{} at.SetAccountIDs(utils.StringMap{accID: true}) if attr.Overwrite { aType += "_reset" // => *topup_reset/*debit_reset } a := &engine.Action{ ActionType: aType, Balance: &engine.BalanceFilter{ Uuid: attr.BalanceUuid, ID: attr.BalanceId, Type: utils.StringPointer(attr.BalanceType), Value: &utils.ValueFormula{Static: attr.Value}, ExpirationDate: expTime, RatingSubject: attr.RatingSubject, Weight: attr.Weight, Blocker: attr.Blocker, Disabled: attr.Disabled, }, } if attr.Directions != nil { a.Balance.Directions = utils.StringMapPointer(utils.ParseStringMap(*attr.Directions)) } if attr.DestinationIds != nil { a.Balance.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.DestinationIds)) } if attr.Categories != nil { a.Balance.Categories = utils.StringMapPointer(utils.ParseStringMap(*attr.Categories)) } if attr.SharedGroups != nil { a.Balance.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(*attr.SharedGroups)) } if attr.TimingIds != nil { a.Balance.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(*attr.TimingIds)) } at.SetActions(engine.Actions{a}) if err := at.Execute(); err != nil { *reply = err.Error() return err } *reply = OK return nil }
func (self *ApierV1) SetActions(attrs utils.AttrSetActions, reply *string) error { if missing := utils.MissingStructFields(&attrs, []string{"ActionsId", "Actions"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } for _, action := range attrs.Actions { requiredFields := []string{"Identifier", "Weight"} if action.BalanceType != "" { // Add some inter-dependent parameters - if balanceType then we are not talking about simply calling actions requiredFields = append(requiredFields, "Direction", "Units") } if missing := utils.MissingStructFields(action, requiredFields); len(missing) != 0 { return fmt.Errorf("%s:Action:%s:%v", utils.ErrMandatoryIeMissing.Error(), action.Identifier, missing) } } if !attrs.Overwrite { if exists, err := self.RatingDb.HasData(utils.ACTION_PREFIX, attrs.ActionsId); err != nil { return utils.NewErrServerError(err) } else if exists { return utils.ErrExists } } storeActions := make(engine.Actions, len(attrs.Actions)) for idx, apiAct := range attrs.Actions { var vf *utils.ValueFormula if apiAct.Units != "" { if x, err := utils.ParseBalanceFilterValue(apiAct.Units); err == nil { vf = x } else { return err } } var weight *float64 if apiAct.BalanceWeight != "" { if x, err := strconv.ParseFloat(apiAct.BalanceWeight, 64); err == nil { weight = &x } else { return err } } a := &engine.Action{ Id: attrs.ActionsId, ActionType: apiAct.Identifier, Weight: apiAct.Weight, ExpirationString: apiAct.ExpiryTime, ExtraParameters: apiAct.ExtraParameters, Filter: apiAct.Filter, Balance: &engine.BalanceFilter{ // TODO: update this part Uuid: utils.StringPointer(apiAct.BalanceUuid), ID: utils.StringPointer(apiAct.BalanceId), Type: utils.StringPointer(apiAct.BalanceType), Value: vf, Weight: weight, Directions: utils.StringMapPointer(utils.ParseStringMap(apiAct.Directions)), DestinationIDs: utils.StringMapPointer(utils.ParseStringMap(apiAct.DestinationIds)), RatingSubject: utils.StringPointer(apiAct.RatingSubject), SharedGroups: utils.StringMapPointer(utils.ParseStringMap(apiAct.SharedGroups)), }, } storeActions[idx] = a } if err := self.RatingDb.SetActions(attrs.ActionsId, storeActions); err != nil { return utils.NewErrServerError(err) } self.RatingDb.CacheRatingPrefixValues("SetActionsAPI", map[string][]string{utils.ACTION_PREFIX: []string{utils.ACTION_PREFIX + attrs.ActionsId}}) *reply = OK return nil }
func (mig MigratorRC8) migrateAccounts() error { keys, err := mig.db.Cmd("KEYS", OLD_ACCOUNT_PREFIX+"*").List() if err != nil { return err } newAccounts := make([]*engine.Account, 0) var migratedKeys []string // get existing accounts for _, key := range keys { log.Printf("Migrating account: %s...", key) values, err := mig.db.Cmd("GET", key).Bytes() if err != nil { continue } var oldAcc Account if err = mig.ms.Unmarshal(values, &oldAcc); err != nil { return err } // transfer data into new structurse newAcc := &engine.Account{ ID: oldAcc.Id, BalanceMap: make(map[string]engine.Balances, len(oldAcc.BalanceMap)), UnitCounters: make(engine.UnitCounters, len(oldAcc.UnitCounters)), ActionTriggers: make(engine.ActionTriggers, len(oldAcc.ActionTriggers)), AllowNegative: oldAcc.AllowNegative, Disabled: oldAcc.Disabled, } // fix id idElements := strings.Split(newAcc.ID, utils.CONCATENATED_KEY_SEP) if len(idElements) != 3 { log.Printf("Malformed account ID %s", oldAcc.Id) continue } newAcc.ID = fmt.Sprintf("%s:%s", idElements[1], idElements[2]) // balances balanceErr := false for oldBalKey, oldBalChain := range oldAcc.BalanceMap { keyElements := strings.Split(oldBalKey, "*") if len(keyElements) != 3 { log.Printf("Malformed balance key in %s: %s", oldAcc.Id, oldBalKey) balanceErr = true break } newBalKey := "*" + keyElements[1] newBalDirection := "*" + keyElements[2] newAcc.BalanceMap[newBalKey] = make(engine.Balances, len(oldBalChain)) for index, oldBal := range oldBalChain { // check default to set new id if oldBal.IsDefault() { oldBal.Id = utils.META_DEFAULT } newAcc.BalanceMap[newBalKey][index] = &engine.Balance{ Uuid: oldBal.Uuid, ID: oldBal.Id, Value: oldBal.Value, Directions: utils.ParseStringMap(newBalDirection), ExpirationDate: oldBal.ExpirationDate, Weight: oldBal.Weight, DestinationIDs: utils.ParseStringMap(oldBal.DestinationIds), RatingSubject: oldBal.RatingSubject, Categories: utils.ParseStringMap(oldBal.Category), SharedGroups: utils.ParseStringMap(oldBal.SharedGroup), Timings: oldBal.Timings, TimingIDs: utils.ParseStringMap(oldBal.TimingIDs), Disabled: oldBal.Disabled, } } } if balanceErr { continue } // unit counters for _, oldUc := range oldAcc.UnitCounters { newUc := &engine.UnitCounter{ Counters: make(engine.CounterFilters, len(oldUc.Balances)), } for index, oldUcBal := range oldUc.Balances { bf := &engine.BalanceFilter{} if oldUcBal.Uuid != "" { bf.Uuid = utils.StringPointer(oldUcBal.Uuid) } if oldUcBal.Id != "" { bf.ID = utils.StringPointer(oldUcBal.Id) } if oldUc.BalanceType != "" { bf.Type = utils.StringPointer(oldUc.BalanceType) } // the value was used for counter value /*if oldUcBal.Value != 0 { bf.Value = utils.Float64Pointer(oldUcBal.Value) }*/ if oldUc.Direction != "" { bf.Directions = utils.StringMapPointer(utils.ParseStringMap(oldUc.Direction)) } if !oldUcBal.ExpirationDate.IsZero() { bf.ExpirationDate = utils.TimePointer(oldUcBal.ExpirationDate) } if oldUcBal.Weight != 0 { bf.Weight = utils.Float64Pointer(oldUcBal.Weight) } if oldUcBal.DestinationIds != "" { bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.DestinationIds)) } if oldUcBal.RatingSubject != "" { bf.RatingSubject = utils.StringPointer(oldUcBal.RatingSubject) } if oldUcBal.Category != "" { bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.Category)) } if oldUcBal.SharedGroup != "" { bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.SharedGroup)) } if oldUcBal.TimingIDs != "" { bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldUcBal.TimingIDs)) } if oldUcBal.Disabled != false { bf.Disabled = utils.BoolPointer(oldUcBal.Disabled) } bf.Timings = oldUcBal.Timings cf := &engine.CounterFilter{ Value: oldUcBal.Value, Filter: bf, } newUc.Counters[index] = cf } newAcc.UnitCounters[oldUc.BalanceType] = append(newAcc.UnitCounters[oldUc.BalanceType], newUc) } // action triggers for index, oldAtr := range oldAcc.ActionTriggers { at := &engine.ActionTrigger{ UniqueID: oldAtr.Id, ThresholdType: oldAtr.ThresholdType, ThresholdValue: oldAtr.ThresholdValue, Recurrent: oldAtr.Recurrent, MinSleep: oldAtr.MinSleep, Weight: oldAtr.Weight, ActionsID: oldAtr.ActionsId, MinQueuedItems: oldAtr.MinQueuedItems, Executed: oldAtr.Executed, } bf := &engine.BalanceFilter{} if oldAtr.BalanceId != "" { bf.ID = utils.StringPointer(oldAtr.BalanceId) } if oldAtr.BalanceType != "" { bf.Type = utils.StringPointer(oldAtr.BalanceType) } if oldAtr.BalanceRatingSubject != "" { bf.RatingSubject = utils.StringPointer(oldAtr.BalanceRatingSubject) } if oldAtr.BalanceDirection != "" { bf.Directions = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDirection)) } if oldAtr.BalanceDestinationIds != "" { bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceDestinationIds)) } if oldAtr.BalanceTimingTags != "" { bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceTimingTags)) } if oldAtr.BalanceCategory != "" { bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceCategory)) } if oldAtr.BalanceSharedGroup != "" { bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldAtr.BalanceSharedGroup)) } if oldAtr.BalanceWeight != 0 { bf.Weight = utils.Float64Pointer(oldAtr.BalanceWeight) } if oldAtr.BalanceDisabled != false { bf.Disabled = utils.BoolPointer(oldAtr.BalanceDisabled) } if !oldAtr.BalanceExpirationDate.IsZero() { bf.ExpirationDate = utils.TimePointer(oldAtr.BalanceExpirationDate) } at.Balance = bf newAcc.ActionTriggers[index] = at if newAcc.ActionTriggers[index].ThresholdType == "*min_counter" || newAcc.ActionTriggers[index].ThresholdType == "*max_counter" { newAcc.ActionTriggers[index].ThresholdType = strings.Replace(newAcc.ActionTriggers[index].ThresholdType, "_", "_event_", 1) } } newAcc.InitCounters() newAccounts = append(newAccounts, newAcc) migratedKeys = append(migratedKeys, key) } // write data back for _, newAcc := range newAccounts { result, err := mig.ms.Marshal(newAcc) if err != nil { return err } if err := mig.db.Cmd("SET", utils.ACCOUNT_PREFIX+newAcc.ID, result).Err; err != nil { return err } } // delete old data log.Printf("Deleting migrated accounts...") for _, key := range migratedKeys { if err := mig.db.Cmd("DEL", key).Err; err != nil { return err } } notMigrated := len(keys) - len(migratedKeys) if notMigrated > 0 { log.Printf("WARNING: there are %d accounts that failed migration!", notMigrated) } return err }
func TestUnitCountersKeepValuesAfterInit(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(20), }, }, &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR22", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET")), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Weight: utils.Float64Pointer(10), }, }, }, } a.InitCounters() a.UnitCounters.addUnits(10, utils.VOICE, &CallCost{Destination: "0723045326"}, nil) if len(a.UnitCounters) != 3 || len(a.UnitCounters[utils.VOICE][0].Counters) != 2 || a.UnitCounters[utils.VOICE][0].Counters[0].Value != 10 || a.UnitCounters[utils.VOICE][0].Counters[1].Value != 10 { for key, counters := range a.UnitCounters { t.Log(key) for _, uc := range counters { t.Logf("UC: %+v", uc) for _, b := range uc.Counters { t.Logf("B: %+v", b) } } } t.Errorf("Error adding unit counters: %v", len(a.UnitCounters)) } a.InitCounters() if len(a.UnitCounters) != 3 || len(a.UnitCounters[utils.VOICE][0].Counters) != 2 || a.UnitCounters[utils.VOICE][0].Counters[0].Value != 10 || a.UnitCounters[utils.VOICE][0].Counters[1].Value != 10 { for key, counters := range a.UnitCounters { t.Log(key) for _, uc := range counters { t.Logf("UC: %+v", uc) for _, b := range uc.Counters { t.Logf("B: %+v", b) } } } t.Errorf("Error keeping counter values after init: %v", len(a.UnitCounters)) } }
func TestUnitCountersCountAllMonetary(t *testing.T) { a := &Account{ ActionTriggers: ActionTriggers{ &ActionTrigger{ UniqueID: "TestTR1", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR11", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR2", ThresholdType: utils.TRIGGER_MAX_EVENT_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR3", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR4", ThresholdType: utils.TRIGGER_MAX_BALANCE_COUNTER, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, &ActionTrigger{ UniqueID: "TestTR5", ThresholdType: utils.TRIGGER_MAX_BALANCE, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.SMS), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT, utils.IN)), Weight: utils.Float64Pointer(10), }, }, }, } a.InitCounters() a.UnitCounters.addUnits(10, utils.MONETARY, &CallCost{}, nil) if len(a.UnitCounters) != 3 || len(a.UnitCounters[utils.MONETARY][0].Counters) != 2 || a.UnitCounters[utils.MONETARY][0].Counters[0].Value != 10 || a.UnitCounters[utils.MONETARY][0].Counters[1].Value != 10 { for key, counters := range a.UnitCounters { t.Log(key) for _, uc := range counters { t.Logf("UC: %+v", uc) for _, b := range uc.Counters { t.Logf("B: %+v", b) } } } t.Errorf("Error Initializing adding unit counters: %v", len(a.UnitCounters)) } }
func TestUnitsCounterAddBalanceExists(t *testing.T) { uc := &UnitCounter{ Counters: CounterFilters{&CounterFilter{Value: 1}, &CounterFilter{Value: 10, Filter: &BalanceFilter{Weight: utils.Float64Pointer(20), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT"))}}, &CounterFilter{Filter: &BalanceFilter{Weight: utils.Float64Pointer(10), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("RET"))}}}, } UnitCounters{utils.SMS: []*UnitCounter{uc}}.addUnits(5, utils.SMS, &CallCost{Destination: "0723"}, nil) if len(uc.Counters) != 3 || uc.Counters[1].Value != 15 { t.Error("Error adding minute bucket!") } }
func TestLoadActions(t *testing.T) { if len(csvr.actions) != 15 { t.Error("Failed to load actions: ", len(csvr.actions)) } as1 := csvr.actions["MINI"] expected := []*Action{ &Action{ Id: "MINI", ActionType: TOPUP_RESET, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Uuid: as1[0].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: &utils.ValueFormula{Static: 10}, Weight: utils.Float64Pointer(10), DestinationIDs: nil, TimingIDs: nil, SharedGroups: nil, Categories: nil, Disabled: utils.BoolPointer(false), Blocker: utils.BoolPointer(false), }, }, &Action{ Id: "MINI", ActionType: TOPUP, ExpirationString: UNLIMITED, ExtraParameters: "", Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.VOICE), Uuid: as1[1].Balance.Uuid, Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), Value: &utils.ValueFormula{Static: 100}, Weight: utils.Float64Pointer(10), RatingSubject: utils.StringPointer("test"), DestinationIDs: utils.StringMapPointer(utils.NewStringMap("NAT")), TimingIDs: nil, SharedGroups: nil, Categories: nil, Disabled: utils.BoolPointer(false), Blocker: utils.BoolPointer(false), }, }, } if !reflect.DeepEqual(as1, expected) { t.Errorf("Error loading action1: %s", utils.ToIJSON(as1)) } as2 := csvr.actions["SHARED"] expected = []*Action{ &Action{ Id: "SHARED", ActionType: TOPUP, ExpirationString: UNLIMITED, Weight: 10, Balance: &BalanceFilter{ Type: utils.StringPointer(utils.MONETARY), Directions: utils.StringMapPointer(utils.NewStringMap(utils.OUT)), DestinationIDs: nil, Uuid: as2[0].Balance.Uuid, Value: &utils.ValueFormula{Static: 100}, Weight: utils.Float64Pointer(10), SharedGroups: utils.StringMapPointer(utils.NewStringMap("SG1")), TimingIDs: nil, Categories: nil, Disabled: utils.BoolPointer(false), Blocker: utils.BoolPointer(false), }, }, } if !reflect.DeepEqual(as2, expected) { t.Errorf("Error loading action: %s", utils.ToIJSON(as2)) } as3 := csvr.actions["DEFEE"] expected = []*Action{ &Action{ Id: "DEFEE", ActionType: CDRLOG, ExtraParameters: `{"Category":"^ddi","MediationRunId":"^did_run"}`, Weight: 10, Balance: &BalanceFilter{ Uuid: as3[0].Balance.Uuid, Directions: nil, DestinationIDs: nil, TimingIDs: nil, Categories: nil, SharedGroups: nil, Blocker: utils.BoolPointer(false), Disabled: utils.BoolPointer(false), }, }, } if !reflect.DeepEqual(as3, expected) { t.Errorf("Error loading action: %+v", as3[0].Balance) } }
func (mig MigratorRC8) migrateActionsInt() error { keys, err := mig.db.Cmd("KEYS", utils.ACTION_PREFIX+"*").List() if err != nil { return err } newAcsMap := make(map[string]engine.Actions, len(keys)) for _, key := range keys { log.Printf("Migrating action: %s...", key) var oldAcs Actions1 var values []byte if values, err = mig.db.Cmd("GET", key).Bytes(); err == nil { if err := mig.ms.Unmarshal(values, &oldAcs); err != nil { return err } } newAcs := make(engine.Actions, len(oldAcs)) for index, oldAc := range oldAcs { a := &engine.Action{ Id: oldAc.Id, ActionType: oldAc.ActionType, ExtraParameters: oldAc.ExtraParameters, ExpirationString: oldAc.ExpirationString, Filter: oldAc.Filter, Weight: oldAc.Weight, Balance: &engine.BalanceFilter{}, } bf := a.Balance if oldAc.Balance.Uuid != "" { bf.Uuid = utils.StringPointer(oldAc.Balance.Uuid) } if oldAc.Balance.Id != "" { bf.ID = utils.StringPointer(oldAc.Balance.Id) } if oldAc.BalanceType != "" { bf.Type = utils.StringPointer(oldAc.BalanceType) } if oldAc.Balance.Value != 0 { bf.Value = &utils.ValueFormula{Static: oldAc.Balance.Value} } if oldAc.Balance.RatingSubject != "" { bf.RatingSubject = utils.StringPointer(oldAc.Balance.RatingSubject) } if !oldAc.Balance.DestinationIds.IsEmpty() { bf.DestinationIDs = utils.StringMapPointer(oldAc.Balance.DestinationIds) } if !oldAc.Balance.TimingIDs.IsEmpty() { bf.TimingIDs = utils.StringMapPointer(oldAc.Balance.TimingIDs) } if !oldAc.Balance.Categories.IsEmpty() { bf.Categories = utils.StringMapPointer(oldAc.Balance.Categories) } if !oldAc.Balance.SharedGroups.IsEmpty() { bf.SharedGroups = utils.StringMapPointer(oldAc.Balance.SharedGroups) } if oldAc.Balance.Weight != 0 { bf.Weight = utils.Float64Pointer(oldAc.Balance.Weight) } if oldAc.Balance.Disabled != false { bf.Disabled = utils.BoolPointer(oldAc.Balance.Disabled) } if !oldAc.Balance.ExpirationDate.IsZero() { bf.ExpirationDate = utils.TimePointer(oldAc.Balance.ExpirationDate) } bf.Timings = oldAc.Balance.Timings newAcs[index] = a } newAcsMap[key] = newAcs } // write data back for key, acs := range newAcsMap { result, err := mig.ms.Marshal(&acs) if err != nil { return err } if err = mig.db.Cmd("SET", key, result).Err; err != nil { return err } } return nil }