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 (tpr *TpReader) LoadActions() (err error) { tps, err := tpr.lr.GetTpActions(tpr.tpid, "") if err != nil { return err } storActs, err := TpActions(tps).GetActions() if err != nil { return err } // map[string][]*Action for tag, tpacts := range storActs { acts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { acts[idx] = &Action{ Id: tag + strconv.Itoa(idx), ActionType: tpact.Identifier, BalanceType: tpact.BalanceType, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Balance: &Balance{ Id: tpact.BalanceId, Value: tpact.Units, Weight: tpact.BalanceWeight, RatingSubject: tpact.RatingSubject, Categories: utils.ParseStringMap(tpact.Categories), Directions: utils.ParseStringMap(tpact.Directions), DestinationIds: utils.ParseStringMap(tpact.DestinationIds), SharedGroups: utils.ParseStringMap(tpact.SharedGroups), TimingIDs: utils.ParseStringMap(tpact.TimingTags), }, } // load action timings from tags if tpact.TimingTags != "" { timingIds := strings.Split(tpact.TimingTags, utils.INFIELD_SEP) for _, timingID := range timingIds { if timing, found := tpr.timings[timingID]; found { acts[idx].Balance.Timings = append(acts[idx].Balance.Timings, &RITiming{ Years: timing.Years, Months: timing.Months, MonthDays: timing.MonthDays, WeekDays: timing.WeekDays, StartTime: timing.StartTime, EndTime: timing.EndTime, }) } else { return fmt.Errorf("could not find timing: %v", timingID) } } } } tpr.actions[tag] = acts } return nil }
func (self *ApierV1) modifyBalance(aType string, attr *AttrAddBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() return err } 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 } at.SetActions(engine.Actions{ &engine.Action{ ActionType: aType, BalanceType: attr.BalanceType, Balance: &engine.Balance{ Uuid: attr.BalanceUuid, Id: attr.BalanceId, Value: attr.Value, ExpirationDate: expTime, RatingSubject: attr.RatingSubject, Directions: utils.ParseStringMap(attr.Directions), DestinationIds: utils.ParseStringMap(attr.DestinationIds), Categories: utils.ParseStringMap(attr.Categories), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), TimingIDs: utils.ParseStringMap(attr.TimingIds), Blocker: attr.Blocker, Disabled: attr.Disabled, }, }, }) if err := at.Execute(); err != nil { *reply = err.Error() return err } *reply = OK return nil }
func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { expTime, err := utils.ParseDate(attr.ExpiryTime) if err != nil { *reply = err.Error() return err } tag := utils.ConcatenatedKey(attr.Tenant, attr.Account) if _, err := self.AccountDb.GetAccount(tag); err != nil { // create user balance if not exists account := &engine.Account{ Id: tag, } if err := self.AccountDb.SetAccount(account); err != nil { *reply = err.Error() return err } } at := &engine.ActionPlan{ AccountIds: []string{tag}, } aType := engine.DEBIT // reverse the sign as it is a debit attr.Value = -attr.Value if attr.Overwrite { aType = engine.DEBIT_RESET } at.SetActions(engine.Actions{ &engine.Action{ ActionType: aType, BalanceType: attr.BalanceType, Balance: &engine.Balance{ Uuid: attr.BalanceUuid, Id: attr.BalanceId, Value: attr.Value, ExpirationDate: expTime, RatingSubject: attr.RatingSubject, Directions: utils.ParseStringMap(attr.Directions), DestinationIds: utils.ParseStringMap(attr.DestinationIds), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), Disabled: attr.Disabled, }, }, }) if err := at.Execute(); err != nil { *reply = err.Error() return err } *reply = OK return nil }
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 (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 { a := &engine.Action{ Id: utils.GenUUID(), ActionType: apiAct.Identifier, BalanceType: apiAct.BalanceType, Weight: apiAct.Weight, ExpirationString: apiAct.ExpiryTime, ExtraParameters: apiAct.ExtraParameters, Filter: apiAct.Filter, Balance: &engine.Balance{ Uuid: utils.GenUUID(), Id: apiAct.BalanceId, Value: apiAct.Units, Weight: apiAct.BalanceWeight, Directions: utils.ParseStringMap(apiAct.Directions), DestinationIds: utils.ParseStringMap(apiAct.DestinationIds), RatingSubject: apiAct.RatingSubject, SharedGroups: utils.ParseStringMap(apiAct.SharedGroups), }, } storeActions[idx] = a } if err := self.RatingDb.SetActions(attrs.ActionsId, storeActions); err != nil { return utils.NewErrServerError(err) } self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PREFIX: []string{utils.ACTION_PREFIX + attrs.ActionsId}}) *reply = OK return nil }
func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) if err != nil { *reply = err.Error() return err } 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}) at.SetActions(engine.Actions{ &engine.Action{ ActionType: engine.REMOVE_BALANCE, BalanceType: attr.BalanceType, Balance: &engine.Balance{ Uuid: attr.BalanceUuid, Id: attr.BalanceId, Value: attr.Value, ExpirationDate: expTime, RatingSubject: attr.RatingSubject, Directions: utils.ParseStringMap(attr.Directions), DestinationIds: utils.ParseStringMap(attr.DestinationIds), Categories: utils.ParseStringMap(attr.Categories), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), TimingIDs: utils.ParseStringMap(attr.TimingIds), Blocker: attr.Blocker, Disabled: attr.Disabled, }, }, }) if err := at.Execute(); err != nil { *reply = err.Error() return err } *reply = OK return nil }
func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string) error { if attr.BalanceDirection == "" { attr.BalanceDirection = utils.OUT } balExpiryTime, err := utils.ParseTimeDetectLayout(attr.BalanceExpiryTime, self.Config.DefaultTimezone) if err != nil { return utils.NewErrServerError(err) } at := &engine.ActionTrigger{ Id: attr.ActionTriggersId, ThresholdType: attr.ThresholdType, ThresholdValue: attr.ThresholdValue, BalanceId: attr.BalanceId, BalanceType: attr.BalanceType, BalanceDirections: utils.ParseStringMap(attr.BalanceDirection), BalanceDestinationIds: utils.ParseStringMap(attr.BalanceDestinationIds), BalanceWeight: attr.BalanceWeight, BalanceExpirationDate: balExpiryTime, Weight: attr.Weight, ActionsId: attr.ActionsId, Executed: false, } tag := utils.AccountKey(attr.Tenant, attr.Account) _, err = engine.Guardian.Guard(func() (interface{}, error) { userBalance, err := self.AccountDb.GetAccount(tag) if err != nil { return 0, err } userBalance.ActionTriggers = append(userBalance.ActionTriggers, at) if err = self.AccountDb.SetAccount(userBalance); err != nil { return 0, err } return 0, nil }, 0, tag) if err != nil { *reply = err.Error() return err } *reply = OK return nil }
func (tpr *TpReader) LoadActionTriggers() (err error) { tps, err := tpr.lr.GetTpActionTriggers(tpr.tpid, "") if err != nil { return err } storAts, err := TpActionTriggers(tps).GetActionTriggers() if err != nil { return err } for key, atrsLst := range storAts { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, atr := range atrsLst { balanceExpirationDate, _ := utils.ParseTimeDetectLayout(atr.BalanceExpirationDate, tpr.timezone) minSleep, err := utils.ParseDurationWithSecs(atr.MinSleep) if err != nil { return err } atrs[idx] = &ActionTrigger{ ThresholdType: atr.ThresholdType, ThresholdValue: atr.ThresholdValue, Recurrent: atr.Recurrent, MinSleep: minSleep, BalanceId: atr.BalanceId, BalanceType: atr.BalanceType, BalanceDirections: utils.ParseStringMap(atr.BalanceDirections), BalanceDestinationIds: utils.ParseStringMap(atr.BalanceDestinationIds), BalanceWeight: atr.BalanceWeight, BalanceExpirationDate: balanceExpirationDate, BalanceTimingTags: utils.ParseStringMap(atr.BalanceTimingTags), BalanceRatingSubject: atr.BalanceRatingSubject, BalanceCategories: utils.ParseStringMap(atr.BalanceCategories), BalanceSharedGroups: utils.ParseStringMap(atr.BalanceSharedGroups), Weight: atr.Weight, ActionsId: atr.ActionsId, MinQueuedItems: atr.MinQueuedItems, } } tpr.actionsTriggers[key] = atrs } return nil }
func (self *ApierV1) RemoveBalances(attr *AttrAddBalance, reply *string) error { expTime, err := utils.ParseDate(attr.ExpiryTime) if err != nil { *reply = err.Error() return err } accId := utils.ConcatenatedKey(attr.Tenant, attr.Account) if _, err := self.AccountDb.GetAccount(accId); err != nil { return utils.ErrNotFound } at := &engine.ActionPlan{ AccountIds: []string{accId}, } at.SetActions(engine.Actions{ &engine.Action{ ActionType: engine.REMOVE_BALANCE, BalanceType: attr.BalanceType, Balance: &engine.Balance{ Uuid: attr.BalanceUuid, Id: attr.BalanceId, Value: attr.Value, ExpirationDate: expTime, RatingSubject: attr.RatingSubject, Directions: utils.ParseStringMap(attr.Directions), DestinationIds: utils.ParseStringMap(attr.DestinationIds), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), Disabled: attr.Disabled, }, }, }) if err := at.Execute(); err != nil { *reply = err.Error() return err } *reply = OK return nil }
// returns true if the field of the action timing are equeal to the non empty // fields of the action func (at *ActionTrigger) Match(a *Action) bool { if a == nil { return true } // if we have Id than we can draw an early conclusion if a.Id != "" { match, _ := regexp.MatchString(a.Id, at.Id) return match } id := a.BalanceType == "" || at.BalanceType == a.BalanceType thresholdType, thresholdValue, direction, destinationId, weight, ratingSubject, categories, sharedGroup, timings, disabled := true, true, true, true, true, true, true, true, true, true if a.ExtraParameters != "" { t := struct { ThresholdType string ThresholdValue float64 DestinationIds string BalanceDirections string BalanceWeight float64 BalanceRatingSubject string BalanceCategories string BalanceSharedGroups string BalanceTimingTags string BalanceDisabled bool }{} json.Unmarshal([]byte(a.ExtraParameters), &t) thresholdType = t.ThresholdType == "" || at.ThresholdType == t.ThresholdType thresholdValue = t.ThresholdValue == 0 || at.ThresholdValue == t.ThresholdValue direction = len(t.BalanceDirections) == 0 || at.BalanceDirections.Equal(utils.ParseStringMap(t.BalanceDirections)) destinationId = len(t.DestinationIds) == 0 || at.BalanceDestinationIds.Equal(utils.ParseStringMap(t.DestinationIds)) categories = len(t.BalanceCategories) == 0 || at.BalanceCategories.Equal(utils.ParseStringMap(t.BalanceCategories)) timings = len(t.BalanceTimingTags) == 0 || at.BalanceTimingTags.Equal(utils.ParseStringMap(t.BalanceTimingTags)) sharedGroup = len(t.BalanceSharedGroups) == 0 || at.BalanceSharedGroups.Equal(utils.ParseStringMap(t.BalanceSharedGroups)) weight = t.BalanceWeight == 0 || at.BalanceWeight == t.BalanceWeight ratingSubject = t.BalanceRatingSubject == "" || at.BalanceRatingSubject == t.BalanceRatingSubject disabled = at.BalanceDisabled == t.BalanceDisabled } return id && direction && thresholdType && thresholdValue && destinationId && weight && ratingSubject && categories && sharedGroup && timings && disabled }
func (tpr *TpReader) LoadDerivedChargersFiltered(filter *TpDerivedCharger, save bool) (err error) { tps, err := tpr.lr.GetTpDerivedChargers(filter) if err != nil { return err } storDcs, err := TpDerivedChargers(tps).GetDerivedChargers() if err != nil { return err } for _, tpDcs := range storDcs { tag := tpDcs.GetDerivedChargersKey() if _, hasIt := tpr.derivedChargers[tag]; !hasIt { tpr.derivedChargers[tag] = &utils.DerivedChargers{ DestinationIds: make(utils.StringMap), Chargers: make([]*utils.DerivedCharger, 0), } // Load object map since we use this method also from LoadDerivedChargers } for _, tpDc := range tpDcs.DerivedChargers { dc, err := utils.NewDerivedCharger(tpDc.RunId, tpDc.RunFilters, tpDc.ReqTypeField, tpDc.DirectionField, tpDc.TenantField, tpDc.CategoryField, tpDc.AccountField, tpDc.SubjectField, tpDc.DestinationField, tpDc.SetupTimeField, tpDc.PddField, tpDc.AnswerTimeField, tpDc.UsageField, tpDc.SupplierField, tpDc.DisconnectCauseField, tpDc.RatedField, tpDc.CostField) if err != nil { return err } tpr.derivedChargers[tag].DestinationIds.Copy(utils.ParseStringMap(tpDcs.DestinationIds)) tpr.derivedChargers[tag].Chargers = append(tpr.derivedChargers[tag].Chargers, dc) } } if save { for dcsKey, dcs := range tpr.derivedChargers { if err := tpr.ratingStorage.SetDerivedChargers(dcsKey, dcs); err != nil { return err } } } return nil }
func (tpr *TpReader) LoadCdrStatsFiltered(tag string, save bool) (err error) { tps, err := tpr.lr.GetTpCdrStats(tpr.tpid, tag) if err != nil { return err } storStats, err := TpCdrStats(tps).GetCdrStats() if err != nil { return err } var actionsIds []string // collect action ids for tag, tpStats := range storStats { for _, tpStat := range tpStats { var cs *CdrStats var exists bool if cs, exists = tpr.cdrStats[tag]; !exists { cs = &CdrStats{Id: tag} } // action triggers triggerTag := tpStat.ActionTriggers if triggerTag != "" { _, exists := tpr.actionsTriggers[triggerTag] if !exists { tpatrs, err := tpr.lr.GetTpActionTriggers(tpr.tpid, triggerTag) if err != nil { return errors.New(err.Error() + " (ActionTriggers): " + triggerTag) } atrsM, err := TpActionTriggers(tpatrs).GetActionTriggers() if err != nil { return err } 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{ ThresholdType: apiAtr.ThresholdType, ThresholdValue: apiAtr.ThresholdValue, Recurrent: apiAtr.Recurrent, MinSleep: minSleep, BalanceId: apiAtr.BalanceId, BalanceType: apiAtr.BalanceType, BalanceDirections: utils.ParseStringMap(apiAtr.BalanceDirections), BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds), BalanceWeight: apiAtr.BalanceWeight, BalanceExpirationDate: expTime, BalanceRatingSubject: apiAtr.BalanceRatingSubject, BalanceCategories: utils.ParseStringMap(apiAtr.BalanceCategories), BalanceSharedGroups: utils.ParseStringMap(apiAtr.BalanceSharedGroups), BalanceTimingTags: utils.ParseStringMap(apiAtr.BalanceTimingTags), Weight: apiAtr.Weight, ActionsId: apiAtr.ActionsId, } } tpr.actionsTriggers[triggerTag] = atrs } } // collect action ids from triggers for _, atr := range tpr.actionsTriggers[triggerTag] { actionsIds = append(actionsIds, atr.ActionsId) } } triggers, exists := tpr.actionsTriggers[triggerTag] if triggerTag != "" && !exists { // 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 } } // actions for _, actId := range actionsIds { _, exists := tpr.actions[actId] if !exists { tpas, err := tpr.lr.GetTpActions(tpr.tpid, actId) if err != nil { return err } as, err := TpActions(tpas).GetActions() if err != nil { return err } for tag, tpacts := range as { enacts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { enacts[idx] = &Action{ Id: tag + strconv.Itoa(idx), ActionType: tpact.Identifier, BalanceType: tpact.BalanceType, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Balance: &Balance{ Value: tpact.Units, Weight: tpact.BalanceWeight, RatingSubject: tpact.RatingSubject, Directions: utils.ParseStringMap(tpact.Directions), DestinationIds: utils.ParseStringMap(tpact.DestinationIds), SharedGroups: utils.ParseStringMap(tpact.SharedGroups), TimingIDs: utils.ParseStringMap(tpact.TimingTags), }, } } tpr.actions[tag] = enacts } } } if save { // write actions for k, as := range tpr.actions { err = tpr.ratingStorage.SetActions(k, as) if err != nil { return err } } for _, stat := range tpr.cdrStats { if err := tpr.ratingStorage.SetCdrStats(stat); err != nil { return err } } } return nil }
func (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error { accountActions, err := tpr.lr.GetTpAccountActions(qriedAA) if err != nil { return errors.New(err.Error() + ": " + fmt.Sprintf("%+v", qriedAA)) } storAas, err := TpAccountActions(accountActions).GetAccountActions() if err != nil { return err } for _, accountAction := range storAas { id := accountAction.KeyId() var actionsIds []string // collects action ids // action timings if accountAction.ActionPlanId != "" { // get old userBalanceIds exitingAccountIds := make(utils.StringMap) existingActionPlan, err := tpr.ratingStorage.GetActionPlan(accountAction.ActionPlanId, true) if err == nil && existingActionPlan != nil { exitingAccountIds = existingActionPlan.AccountIDs } tpap, err := tpr.lr.GetTpActionPlans(tpr.tpid, accountAction.ActionPlanId) if err != nil { return errors.New(err.Error() + " (ActionPlan): " + accountAction.ActionPlanId) } else if len(tpap) == 0 { return fmt.Errorf("no action plan with id <%s>", accountAction.ActionPlanId) } aps, err := TpActionPlans(tpap).GetActionPlans() if err != nil { return err } var actionPlan *ActionPlan ats := aps[accountAction.ActionPlanId] for _, at := range ats { // Check action exists before saving it inside actionTiming key // ToDo: try saving the key after the actions was retrieved in order to save one query here. if actions, err := tpr.lr.GetTpActions(tpr.tpid, at.ActionsId); err != nil { return errors.New(err.Error() + " (Actions): " + at.ActionsId) } else if len(actions) == 0 { return fmt.Errorf("no action with id <%s>", at.ActionsId) } var t *utils.TPTiming if at.TimingId != utils.ASAP { tptm, err := tpr.lr.GetTpTimings(tpr.tpid, at.TimingId) if err != nil { return errors.New(err.Error() + " (Timing): " + at.TimingId) } else if len(tptm) == 0 { return fmt.Errorf("no timing with id <%s>", at.TimingId) } tm, err := TpTimings(tptm).GetTimings() if err != nil { return err } t = tm[at.TimingId] } else { t = tpr.timings[at.TimingId] // *asap } if actionPlan == nil { actionPlan = &ActionPlan{ Id: accountAction.ActionPlanId, } } actionPlan.ActionTimings = append(actionPlan.ActionTimings, &ActionTiming{ Uuid: utils.GenUUID(), Weight: at.Weight, Timing: &RateInterval{ Timing: &RITiming{ Months: t.Months, MonthDays: t.MonthDays, WeekDays: t.WeekDays, StartTime: t.StartTime, }, }, ActionsID: at.ActionsId, }) // collect action ids from timings actionsIds = append(actionsIds, at.ActionsId) exitingAccountIds[id] = true actionPlan.AccountIDs = exitingAccountIds } // write tasks for _, at := range actionPlan.ActionTimings { if at.IsASAP() { for accID := range actionPlan.AccountIDs { t := &Task{ Uuid: utils.GenUUID(), AccountID: accID, ActionsID: at.ActionsID, } if err = tpr.ratingStorage.PushTask(t); err != nil { return err } } } } // write action plan err = tpr.ratingStorage.SetActionPlan(accountAction.ActionPlanId, actionPlan, false) if err != nil { return errors.New(err.Error() + " (SetActionPlan): " + accountAction.ActionPlanId) } } // action triggers var actionTriggers ActionTriggers //ActionTriggerPriotityList []*ActionTrigger if accountAction.ActionTriggersId != "" { tpatrs, err := tpr.lr.GetTpActionTriggers(tpr.tpid, accountAction.ActionTriggersId) if err != nil { return errors.New(err.Error() + " (ActionTriggers): " + accountAction.ActionTriggersId) } atrs, err := TpActionTriggers(tpatrs).GetActionTriggers() if err != nil { return err } atrsMap := make(map[string][]*ActionTrigger) for key, atrsLst := range atrs { atrs := make([]*ActionTrigger, len(atrsLst)) for idx, apiAtr := range atrsLst { minSleep, _ := utils.ParseDurationWithSecs(apiAtr.MinSleep) balanceExpTime, _ := utils.ParseDate(apiAtr.BalanceExpirationDate) expTime, _ := utils.ParseTimeDetectLayout(apiAtr.ExpirationDate, tpr.timezone) actTime, _ := utils.ParseTimeDetectLayout(apiAtr.ActivationDate, tpr.timezone) if apiAtr.UniqueID == "" { apiAtr.UniqueID = utils.GenUUID() } atrs[idx] = &ActionTrigger{ ID: key, UniqueID: apiAtr.UniqueID, ThresholdType: apiAtr.ThresholdType, ThresholdValue: apiAtr.ThresholdValue, Recurrent: apiAtr.Recurrent, MinSleep: minSleep, ExpirationDate: expTime, ActivationDate: actTime, BalanceId: apiAtr.BalanceId, BalanceType: apiAtr.BalanceType, BalanceDirections: utils.ParseStringMap(apiAtr.BalanceDirections), BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds), BalanceWeight: apiAtr.BalanceWeight, BalanceExpirationDate: balanceExpTime, BalanceTimingTags: utils.ParseStringMap(apiAtr.BalanceTimingTags), BalanceRatingSubject: apiAtr.BalanceRatingSubject, BalanceCategories: utils.ParseStringMap(apiAtr.BalanceCategories), BalanceSharedGroups: utils.ParseStringMap(apiAtr.BalanceSharedGroups), BalanceBlocker: apiAtr.BalanceBlocker, BalanceDisabled: apiAtr.BalanceDisabled, Weight: apiAtr.Weight, ActionsId: apiAtr.ActionsId, } } atrsMap[key] = atrs } actionTriggers = atrsMap[accountAction.ActionTriggersId] // collect action ids from triggers 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 acts := make(map[string][]*Action) for _, actId := range actionsIds { tpas, err := tpr.lr.GetTpActions(tpr.tpid, actId) if err != nil { return err } as, err := TpActions(tpas).GetActions() if err != nil { return err } for tag, tpacts := range as { enacts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { // check filter field if len(tpact.Filter) > 0 { if _, err := structmatcher.NewStructMatcher(tpact.Filter); err != nil { return fmt.Errorf("error parsing action %s filter field: %v", tag, err) } } enacts[idx] = &Action{ Id: tag + strconv.Itoa(idx), ActionType: tpact.Identifier, BalanceType: tpact.BalanceType, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Filter: tpact.Filter, Balance: &Balance{ Id: tpact.BalanceId, Value: tpact.Units, Weight: tpact.BalanceWeight, RatingSubject: tpact.RatingSubject, Categories: utils.ParseStringMap(tpact.Categories), Directions: utils.ParseStringMap(tpact.Directions), DestinationIds: utils.ParseStringMap(tpact.DestinationIds), SharedGroups: utils.ParseStringMap(tpact.SharedGroups), TimingIDs: utils.ParseStringMap(tpact.TimingTags), Blocker: tpact.BalanceBlocker, Disabled: tpact.BalanceDisabled, }, } } acts[tag] = enacts } } // write actions for k, as := range acts { err = tpr.ratingStorage.SetActions(k, as) if err != nil { return err } } ub, err := tpr.accountingStorage.GetAccount(id) if err != nil { ub = &Account{ Id: id, } } ub.ActionTriggers = actionTriggers // init counters ub.InitCounters() if err := tpr.accountingStorage.SetAccount(ub); err != nil { return err } } return nil }
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 does not exist 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 { return err } *reply = OK return nil }
func (mig MigratorRC8) migrateActions() 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 Actions 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, 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 != "" { bf.DestinationIDs = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.DestinationIds)) } if oldAc.Balance.TimingIDs != "" { bf.TimingIDs = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.TimingIDs)) } if oldAc.Balance.Category != "" { bf.Categories = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.Category)) } if oldAc.Balance.SharedGroup != "" { bf.SharedGroups = utils.StringMapPointer(utils.ParseStringMap(oldAc.Balance.SharedGroup)) } 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 }
func (mig MigratorRC8) migrateActionTriggers() 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 ActionTriggers 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{ 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 newAtrs[index] = at if newAtrs[index].ThresholdType == "*min_counter" || newAtrs[index].ThresholdType == "*max_counter" { newAtrs[index].ThresholdType = strings.Replace(newAtrs[index].ThresholdType, "_", "_event_", 1) } } 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 (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 (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 (tpr *TpReader) LoadAccountActionsFiltered(qriedAA *TpAccountAction) error { accountActions, err := tpr.lr.GetTpAccountActions(qriedAA) if err != nil { return errors.New(err.Error() + ": " + fmt.Sprintf("%+v", qriedAA)) } storAas, err := TpAccountActions(accountActions).GetAccountActions() if err != nil { return err } for _, accountAction := range storAas { id := accountAction.KeyId() var actionsIds []string // collects action ids // action timings if accountAction.ActionPlanId != "" { // get old userBalanceIds var exitingAccountIds []string existingActionPlans, err := tpr.ratingStorage.GetActionPlans(accountAction.ActionPlanId, true) if err == nil && len(existingActionPlans) > 0 { // all action timings from a specific tag shuld have the same list of user balances from the first one exitingAccountIds = existingActionPlans[0].AccountIds } tpap, err := tpr.lr.GetTpActionPlans(tpr.tpid, accountAction.ActionPlanId) if err != nil { return errors.New(err.Error() + " (ActionPlan): " + accountAction.ActionPlanId) } else if len(tpap) == 0 { return fmt.Errorf("no action plan with id <%s>", accountAction.ActionPlanId) } aps, err := TpActionPlans(tpap).GetActionPlans() if err != nil { return err } var actionTimings []*ActionPlan ats := aps[accountAction.ActionPlanId] for _, at := range ats { // Check action exists before saving it inside actionTiming key // ToDo: try saving the key after the actions was retrieved in order to save one query here. if actions, err := tpr.lr.GetTpActions(tpr.tpid, at.ActionsId); err != nil { return errors.New(err.Error() + " (Actions): " + at.ActionsId) } else if len(actions) == 0 { return fmt.Errorf("no action with id <%s>", at.ActionsId) } var t *utils.TPTiming if at.TimingId != utils.ASAP { tptm, err := tpr.lr.GetTpTimings(tpr.tpid, at.TimingId) if err != nil { return errors.New(err.Error() + " (Timing): " + at.TimingId) } else if len(tptm) == 0 { return fmt.Errorf("no timing with id <%s>", at.TimingId) } tm, err := TpTimings(tptm).GetTimings() if err != nil { return err } t = tm[at.TimingId] } else { t = tpr.timings[at.TimingId] // *asap } actPln := &ActionPlan{ Uuid: utils.GenUUID(), Id: accountAction.ActionPlanId, Weight: at.Weight, Timing: &RateInterval{ Timing: &RITiming{ Months: t.Months, MonthDays: t.MonthDays, WeekDays: t.WeekDays, StartTime: t.StartTime, }, }, ActionsId: at.ActionsId, } // collect action ids from timings actionsIds = append(actionsIds, actPln.ActionsId) //add user balance id if no already in found := false for _, ubId := range exitingAccountIds { if ubId == id { found = true break } } if !found { actPln.AccountIds = append(exitingAccountIds, id) } actionTimings = append(actionTimings, actPln) } // write action triggers err = tpr.ratingStorage.SetActionPlans(accountAction.ActionPlanId, actionTimings) if err != nil { return errors.New(err.Error() + " (SetActionPlan): " + accountAction.ActionPlanId) } } // action triggers var actionTriggers ActionTriggers //ActionTriggerPriotityList []*ActionTrigger if accountAction.ActionTriggersId != "" { tpatrs, err := tpr.lr.GetTpActionTriggers(tpr.tpid, accountAction.ActionTriggersId) if err != nil { return errors.New(err.Error() + " (ActionTriggers): " + accountAction.ActionTriggersId) } atrs, err := TpActionTriggers(tpatrs).GetActionTriggers() if err != nil { return err } atrsMap := make(map[string][]*ActionTrigger) 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{ ThresholdType: apiAtr.ThresholdType, ThresholdValue: apiAtr.ThresholdValue, Recurrent: apiAtr.Recurrent, MinSleep: minSleep, BalanceId: apiAtr.BalanceId, BalanceType: apiAtr.BalanceType, BalanceDirections: utils.ParseStringMap(apiAtr.BalanceDirections), BalanceDestinationIds: utils.ParseStringMap(apiAtr.BalanceDestinationIds), BalanceWeight: apiAtr.BalanceWeight, BalanceExpirationDate: expTime, BalanceRatingSubject: apiAtr.BalanceRatingSubject, BalanceCategories: utils.ParseStringMap(apiAtr.BalanceCategories), BalanceSharedGroups: utils.ParseStringMap(apiAtr.BalanceSharedGroups), Weight: apiAtr.Weight, ActionsId: apiAtr.ActionsId, } } atrsMap[key] = atrs } actionTriggers = atrsMap[accountAction.ActionTriggersId] // collect action ids from triggers 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 acts := make(map[string][]*Action) for _, actId := range actionsIds { tpas, err := tpr.lr.GetTpActions(tpr.tpid, actId) if err != nil { return err } as, err := TpActions(tpas).GetActions() if err != nil { return err } for tag, tpacts := range as { enacts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { enacts[idx] = &Action{ Id: tag + strconv.Itoa(idx), ActionType: tpact.Identifier, BalanceType: tpact.BalanceType, Weight: tpact.Weight, ExtraParameters: tpact.ExtraParameters, ExpirationString: tpact.ExpiryTime, Balance: &Balance{ Value: tpact.Units, Weight: tpact.BalanceWeight, RatingSubject: tpact.RatingSubject, Directions: utils.ParseStringMap(tpact.Directions), DestinationIds: utils.ParseStringMap(tpact.DestinationIds), SharedGroups: utils.ParseStringMap(tpact.SharedGroups), TimingIDs: utils.ParseStringMap(tpact.TimingTags), }, } } acts[tag] = enacts } } // write actions for k, as := range acts { err = tpr.ratingStorage.SetActions(k, as) if err != nil { return err } } ub, err := tpr.accountingStorage.GetAccount(id) if err != nil { ub = &Account{ Id: id, } } ub.ActionTriggers = actionTriggers.Clone() // init counters ub.InitCounters() if err := tpr.accountingStorage.SetAccount(ub); err != nil { return err } } return nil }