diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index c80e6ff00..2290a13db 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -543,16 +543,16 @@ type AttrSetBalance struct { Tenant string Account string BalanceType string - BalanceUuid *string - BalanceId *string - Directions *string + BalanceUUID *string + BalanceID *string + Directions *[]string Value *float64 ExpiryTime *string RatingSubject *string - Categories *string - DestinationIds *string - SharedGroups *string - TimingIds *string + Categories *[]string + DestinationIDs *[]string + SharedGroups *[]string + TimingIDs *[]string Weight *float64 Blocker *bool Disabled *bool @@ -561,7 +561,7 @@ type AttrSetBalance struct { func (attr *AttrSetBalance) SetBalance(b *engine.Balance) { if attr.Directions != nil { - b.Directions = utils.ParseStringMap(*attr.Directions) + b.Directions = utils.StringMapFromSlice(*attr.Directions) } if attr.Value != nil { b.Value = *attr.Value @@ -573,16 +573,16 @@ func (attr *AttrSetBalance) SetBalance(b *engine.Balance) { b.RatingSubject = *attr.RatingSubject } if attr.Categories != nil { - b.Categories = utils.ParseStringMap(*attr.Categories) + b.Categories = utils.StringMapFromSlice(*attr.Categories) } - if attr.DestinationIds != nil { - b.DestinationIds = utils.ParseStringMap(*attr.DestinationIds) + if attr.DestinationIDs != nil { + b.DestinationIds = utils.StringMapFromSlice(*attr.DestinationIDs) } if attr.SharedGroups != nil { - b.SharedGroups = utils.ParseStringMap(*attr.SharedGroups) + b.SharedGroups = utils.StringMapFromSlice(*attr.SharedGroups) } - if attr.TimingIds != nil { - b.TimingIDs = utils.ParseStringMap(*attr.TimingIds) + if attr.TimingIDs != nil { + b.TimingIDs = utils.StringMapFromSlice(*attr.TimingIDs) } if attr.Weight != nil { b.Weight = *attr.Weight @@ -602,8 +602,8 @@ func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - if attr.BalanceId == "" && attr.BalanceUuid == "" { - return utils.NewErrMandatoryIeMissing("BalanceId", "or", "BalanceUuid") + if attr.BalanceID == "" && attr.BalanceUUID == "" { + return utils.NewErrMandatoryIeMissing("BalanceID", "or", "BalanceUUID") } expTime, err := utils.ParseTimeDetectLayout(attr.ExpiryTime, self.Config.DefaultTimezone) if err != nil { @@ -623,17 +623,17 @@ func (self *ApierV1) SetBalance(attr *AttrAddBalance, reply *string) error { ActionType: engine.SET_BALANCE, BalanceType: attr.BalanceType, Balance: &engine.Balance{ - Uuid: attr.BalanceUuid, - Id: attr.BalanceId, + Uuuid: attr.BalanceUUID, + ID: attr.BalanceID, Value: attr.Value, ExpirationDate: expTime, RatingSubject: attr.RatingSubject, Directions: utils.ParseStringMap(attr.Directions), - DestinationIds: utils.ParseStringMap(attr.DestinationIds), + DestinationIDs: utils.ParseStringMap(attr.DestinationIDs), Categories: utils.ParseStringMap(attr.Categories), Weight: attr.Weight, SharedGroups: utils.ParseStringMap(attr.SharedGroups), - TimingIDs: utils.ParseStringMap(attr.TimingIds), + TimingIDs: utils.ParseStringMap(attr.TimingIDs), Blocker: true, Disabled: attr.Disabled, }, @@ -652,9 +652,9 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { if missing := utils.MissingStructFields(attr, []string{"Tenant", "Account", "BalanceType"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - if (attr.BalanceId == nil || *attr.BalanceId == "") && - (attr.BalanceUuid == nil || *attr.BalanceUuid == "") { - return utils.NewErrMandatoryIeMissing("BalanceId", "or", "BalanceUuid") + if (attr.BalanceID == nil || *attr.BalanceID == "") && + (attr.BalanceUUID == nil || *attr.BalanceUUID == "") { + return utils.NewErrMandatoryIeMissing("BalanceID", "or", "BalanceUUID") } var err error if attr.ExpiryTime != nil { @@ -681,8 +681,8 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { if b.IsExpired() { continue } - if (attr.BalanceUuid != nil && b.Uuid == *attr.BalanceUuid) || - (attr.BalanceId != nil && b.Id == *attr.BalanceId) { + if (attr.BalanceUUID != nil && b.Uuid == *attr.BalanceUUID) || + (attr.BalanceID != nil && b.Id == *attr.BalanceID) { previousSharedGroups = b.SharedGroups balance = b found = true @@ -697,7 +697,7 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { account.BalanceMap[attr.BalanceType] = append(account.BalanceMap[attr.BalanceType], balance) } - if attr.BalanceId != nil && *attr.BalanceId == utils.META_DEFAULT { + if attr.BalanceID != nil && *attr.BalanceID == utils.META_DEFAULT { balance.Id = utils.META_DEFAULT if attr.Value != nil { balance.Value = *attr.Value @@ -708,12 +708,12 @@ func (self *ApierV1) SetBalance(attr *AttrSetBalance, reply *string) error { if !found || !previousSharedGroups.Equal(balance.SharedGroups) { _, err = engine.Guardian.Guard(func() (interface{}, error) { - for sgId := range balance.SharedGroups { + for sgID := range balance.SharedGroups { // add shared group member - sg, err := self.RatingDb.GetSharedGroup(sgId, false) + sg, err := self.RatingDb.GetSharedGroup(sgID, false) if err != nil || sg == nil { //than is problem - utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgId)) + utils.Logger.Warning(fmt.Sprintf("Could not get shared group: %v", sgID)) } else { if _, found := sg.MemberIds[account.Id]; !found { // add member and save diff --git a/apier/v2/accounts.go b/apier/v2/accounts.go index 48e07e27f..d1cd45ed4 100644 --- a/apier/v2/accounts.go +++ b/apier/v2/accounts.go @@ -81,9 +81,9 @@ func (self *ApierV2) GetAccount(attr *utils.AttrGetAccount, reply *engine.Accoun type AttrSetAccount struct { Tenant string Account string - ActionPlanIds string + ActionPlanIds *[]string ActionPlansOverwrite bool - ActionTriggersIds string + ActionTriggersIds *[]string ActionTriggerOverwrite bool AllowNegative *bool Disabled *bool @@ -94,7 +94,7 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { if missing := utils.MissingStructFields(&attr, []string{"Tenant", "Account"}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } - var schedulerReloadNeeded = false + dirtyActionPlans := make(map[string]*engine.ActionPlan) accID := utils.AccountKey(attr.Tenant, attr.Account) var ub *engine.Account _, err := engine.Guardian.Guard(func() (interface{}, error) { @@ -105,38 +105,55 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { Id: accID, } } - if len(attr.ActionPlanIds) != 0 { + if attr.ActionPlanIds != nil { _, err := engine.Guardian.Guard(func() (interface{}, error) { - var ap *engine.ActionPlan - var err error - ap, err = self.RatingDb.GetActionPlan(attr.ActionPlanIds, false) + actionPlansMap, err := self.RatingDb.GetAllActionPlans() if err != nil { return 0, err } - if _, exists := ap.AccountIDs[accID]; !exists { - if ap.AccountIDs == nil { - ap.AccountIDs = make(utils.StringMap) + if attr.ActionPlansOverwrite { + // clean previous action plans + for actionPlanID, ap := range actionPlansMap { + if _, exists := ap.AccountIDs[accID]; exists { + delete(ap.AccountIDs, accID) + dirtyActionPlans[actionPlanID] = ap + } } - ap.AccountIDs[accID] = true - schedulerReloadNeeded = true - // create tasks - for _, at := range ap.ActionTimings { - if at.IsASAP() { - t := &engine.Task{ - Uuid: utils.GenUUID(), - AccountID: accID, - ActionsID: at.ActionsID, - } - if err = self.RatingDb.PushTask(t); err != nil { - return 0, err + } + + for _, actionPlanID := range *attr.ActionPlanIds { + ap, ok := actionPlansMap[actionPlanID] + if !ok { + return 0, utils.ErrNotFound + } + + if _, exists := ap.AccountIDs[accID]; !exists { + if ap.AccountIDs == nil { + ap.AccountIDs = make(utils.StringMap) + } + ap.AccountIDs[accID] = true + dirtyActionPlans[actionPlanID] = ap + // create tasks + for _, at := range ap.ActionTimings { + if at.IsASAP() { + t := &engine.Task{ + Uuid: utils.GenUUID(), + AccountID: accID, + ActionsID: at.ActionsID, + } + if err = self.RatingDb.PushTask(t); err != nil { + return 0, err + } } } } - if err := self.RatingDb.SetActionPlan(attr.ActionPlanIds, ap); err != nil { + } + for actionPlanID, ap := range dirtyActionPlans { + if err := self.RatingDb.SetActionPlan(actionPlanID, ap); err != nil { return 0, err } // update cache - self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + attr.ActionPlanIds}}) + self.RatingDb.CacheRatingPrefixValues(map[string][]string{utils.ACTION_PLAN_PREFIX: []string{utils.ACTION_PLAN_PREFIX + actionPlanID}}) } return 0, nil }, 0, utils.ACTION_PLAN_PREFIX) @@ -145,14 +162,30 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { } } - if len(attr.ActionTriggersIds) != 0 { - atrs, err := self.RatingDb.GetActionTriggers(attr.ActionTriggersIds) - if err != nil { - return 0, err + if attr.ActionTriggersIds != nil { + if attr.ActionTriggerOverwrite { + ub.ActionTriggers = make(engine.ActionTriggers, 0) + } + for _, actionTriggerID := range *attr.ActionTriggersIds { + atrs, err := self.RatingDb.GetActionTriggers(actionTriggerID) + if err != nil { + return 0, err + } + for _, at := range atrs { + var found bool + for _, existingAt := range ub.ActionTriggers { + if existingAt.Equals(at) { + found = true + break + } + } + if !found { + ub.ActionTriggers = append(ub.ActionTriggers, at) + } + } } - ub.ActionTriggers = atrs - ub.InitCounters() } + ub.InitCounters() if attr.AllowNegative != nil { ub.AllowNegative = *attr.AllowNegative } @@ -168,7 +201,7 @@ func (self *ApierV2) SetAccount(attr AttrSetAccount, reply *string) error { if err != nil { return utils.NewErrServerError(err) } - if attr.ReloadScheduler && schedulerReloadNeeded { + if attr.ReloadScheduler && len(dirtyActionPlans) > 0 { // reload scheduler if self.Sched != nil { self.Sched.Reload(true) diff --git a/console/account_set.go b/console/account_set.go index d81b0c38e..5906d32db 100644 --- a/console/account_set.go +++ b/console/account_set.go @@ -18,12 +18,12 @@ along with this program. If not, see package console -import "github.com/cgrates/cgrates/utils" +import "github.com/cgrates/cgrates/apier/v2" func init() { c := &CmdAddAccount{ name: "account_set", - rpcMethod: "ApierV1.SetAccount", + rpcMethod: "ApierV2.SetAccount", } commands[c.Name()] = c c.CommandExecuter = &CommandExecuter{c} @@ -33,7 +33,7 @@ func init() { type CmdAddAccount struct { name string rpcMethod string - rpcParams *utils.AttrSetAccount + rpcParams *v2.AttrSetAccount *CommandExecuter } @@ -47,7 +47,7 @@ func (self *CmdAddAccount) RpcMethod() string { func (self *CmdAddAccount) RpcParams(reset bool) interface{} { if reset || self.rpcParams == nil { - self.rpcParams = &utils.AttrSetAccount{} + self.rpcParams = &v2.AttrSetAccount{} } return self.rpcParams } diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 9fa9ace1d..658e93590 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -183,6 +183,11 @@ func (at *ActionTrigger) CreateBalance() *Balance { } } +func (at *ActionTrigger) Equals(oat *ActionTrigger) bool { + // ids only + return at.ID == oat.ID && at.UniqueID == oat.UniqueID +} + // Structure to store actions according to weight type ActionTriggers []*ActionTrigger diff --git a/engine/actions_test.go b/engine/actions_test.go index 68a693450..1c6da1a7d 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -1833,7 +1833,7 @@ func TestActionCSVFilter(t *testing.T) { if err != nil { t.Error("error getting actions: ", err) } - if len(act) != 1 || act[0].Filter != `{"Type":"*voice","Value":{"*gte":100}}` { + if len(act) != 1 || act[0].Filter != `{"*and":[{"Value":{"*lt":0}},{"Id":{"*eq":"*default"}}]}` { t.Error("Error loading actions: ", act[0].Filter) } } diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index d2e4d605f..885cca250 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -174,7 +174,7 @@ DEFEE,*cdrlog,"{""Category"":""^ddi"",""MediationRunId"":""^did_run""}",,,,,,,,, NEG,*allow_negative,,,,*monetary,*out,,,,,*unlimited,,0,10,false,false,10 BLOCK,*topup,,,bblocker,*monetary,*out,,NAT,,,*unlimited,,10,20,true,false,20 BLOCK,*topup,,,bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 -FILTER,*topup,,"{""Type"":""*voice"",""Value"":{""*gte"":100}}",bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 +FILTER,*topup,,"{""*and"":[{""Value"":{""*lt"":0}},{""Id"":{""*eq"":""*default""}}]}",bfree,*monetary,*out,,,,,*unlimited,,20,10,false,false,10 ` actionPlans = ` MORE_MINUTES,MINI,ONE_TIME_RUN,10 diff --git a/utils/map.go b/utils/map.go index 6756488b8..d303efcaf 100644 --- a/utils/map.go +++ b/utils/map.go @@ -74,15 +74,7 @@ func NewStringMap(s ...string) StringMap { } func ParseStringMap(s string) StringMap { - slice := strings.Split(s, INFIELD_SEP) - result := make(StringMap) - for _, v := range slice { - v = strings.TrimSpace(v) - if v != "" { - result[v] = true - } - } - return result + return StringMapFromSlice(strings.Split(s, INFIELD_SEP)) } func (sm StringMap) Equal(om StringMap) bool { @@ -130,6 +122,17 @@ func (sm StringMap) Slice() []string { return result } +func StringMapFromSlice(s []string) StringMap { + result := make(StringMap) + for _, v := range s { + v = strings.TrimSpace(v) + if v != "" { + result[v] = true + } + } + return result +} + func (sm StringMap) Copy(o StringMap) { for k, v := range o { sm[k] = v