diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index c5a5e1e16..72e31c52a 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -21,6 +21,7 @@ package v1 import ( "errors" "fmt" + "regexp" "time" "github.com/cgrates/cgrates/engine" @@ -120,7 +121,7 @@ type AttrRemAcntActionTriggers struct { Tenant string // Tenant he account belongs to Account string // Account name Direction string // Traffic direction - ActionTriggerId string // Id filtering only specific id to remove + ActionTriggerId string // Id filtering only specific id to remove (can be regexp pattern) } // Returns a list of ActionTriggers on an account @@ -135,7 +136,8 @@ func (self *ApierV1) RemAccountActionTriggers(attrs AttrRemAcntActionTriggers, r return 0, err } for idx, actr := range ub.ActionTriggers { - if len(attrs.ActionTriggerId) != 0 && actr.Id != attrs.ActionTriggerId { // Empty actionTriggerId will match always + match, _ := regexp.MatchString(attrs.ActionTriggerId, actr.Id) + if len(attrs.ActionTriggerId) != 0 && !match { continue } if len(ub.ActionTriggers) != 1 { // Remove by index diff --git a/apier/v1/apier.go b/apier/v1/apier.go index e88744241..803f3f651 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -534,6 +534,7 @@ func (self *ApierV1) SetActionPlan(attrs AttrSetActionPlan, reply *string) error } type AttrAddActionTrigger struct { + Id string Tenant string Account string ThresholdType string @@ -559,7 +560,7 @@ func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string return fmt.Errorf("%s:%s", utils.ERR_SERVER_ERROR, err.Error()) } at := &engine.ActionTrigger{ - Id: utils.GenUUID(), + Id: attr.Id, ThresholdType: attr.ThresholdType, ThresholdValue: attr.ThresholdValue, BalanceId: attr.BalanceId, @@ -596,6 +597,7 @@ func (self *ApierV1) AddTriggeredAction(attr AttrAddActionTrigger, reply *string } type AttrResetTriggeredAction struct { + Id string Tenant string Account string Direction string @@ -609,35 +611,41 @@ type AttrResetTriggeredAction struct { } func (self *ApierV1) ResetTriggeredActions(attr AttrResetTriggeredAction, reply *string) error { - if attr.Direction == "" { - attr.Direction = engine.OUTBOUND - } - extraParameters, err := json.Marshal(struct { - ThresholdType string - ThresholdValue float64 - DestinationId string - BalanceWeight float64 - BalanceRatingSubject string - BalanceSharedGroup string - }{ - attr.ThresholdType, - attr.ThresholdValue, - attr.DestinationId, - attr.BalanceWeight, - attr.BalanceRatingSubject, - attr.BalanceSharedGroup, - }) - if err != nil { - *reply = err.Error() - return err - } - a := &engine.Action{ - BalanceType: attr.BalanceType, - Direction: attr.Direction, - ExtraParameters: string(extraParameters), + var a *engine.Action + if attr.Id != "" { + // we can identify the trigge by the id + a = &engine.Action{Id: attr.Id} + } else { + if attr.Direction == "" { + attr.Direction = engine.OUTBOUND + } + extraParameters, err := json.Marshal(struct { + ThresholdType string + ThresholdValue float64 + DestinationId string + BalanceWeight float64 + BalanceRatingSubject string + BalanceSharedGroup string + }{ + attr.ThresholdType, + attr.ThresholdValue, + attr.DestinationId, + attr.BalanceWeight, + attr.BalanceRatingSubject, + attr.BalanceSharedGroup, + }) + if err != nil { + *reply = err.Error() + return err + } + a = &engine.Action{ + BalanceType: attr.BalanceType, + Direction: attr.Direction, + ExtraParameters: string(extraParameters), + } } accID := utils.AccountKey(attr.Tenant, attr.Account, attr.Direction) - _, err = engine.AccLock.Guard(accID, func() (float64, error) { + _, err := engine.AccLock.Guard(accID, func() (float64, error) { acc, err := self.AccountDb.GetAccount(accID) if err != nil { return 0, err diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 8b2b67729..60c48116a 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -21,6 +21,7 @@ package engine import ( "encoding/json" "fmt" + "regexp" "sort" "time" @@ -28,7 +29,7 @@ import ( ) type ActionTrigger struct { - Id string // uniquely identify the trigger + Id string // for visual identification ThresholdType string //*min_counter, *max_counter, *min_balance, *max_balance // stats: *min_asr, *max_asr, *min_acd, *max_acd, *min_acc, *max_acc ThresholdValue float64 @@ -102,6 +103,11 @@ 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 direction := a.Direction == "" || at.BalanceDirection == a.Direction thresholdType, thresholdValue, destinationId, weight, ratingSubject, category, sharedGroup := true, true, true, true, true, true, true diff --git a/engine/loader_csv.go b/engine/loader_csv.go index 54313a002..d75edd56a 100644 --- a/engine/loader_csv.go +++ b/engine/loader_csv.go @@ -679,7 +679,7 @@ func (csvr *CSVReader) LoadActions() (err error) { return fmt.Errorf("Could not parse action weight: %v", err) } a := &Action{ - Id: utils.GenUUID(), + Id: tag, ActionType: record[ACTSCSVIDX_ACTION], BalanceType: record[ACTSCSVIDX_BALANCE_TYPE], Direction: record[ACTSCSVIDX_DIRECTION], @@ -719,6 +719,12 @@ func (csvr *CSVReader) LoadActions() (err error) { if _, err := utils.ParseDate(a.ExpirationString); err != nil { return fmt.Errorf("Could not parse expiration time: %v", err) } + // update Id + idx := 0 + if previous, ok := csvr.actions[tag]; ok { + idx = len(previous) + } + a.Id = a.Id + strconv.Itoa(idx) csvr.actions[tag] = append(csvr.actions[tag], a) } return @@ -815,7 +821,7 @@ func (csvr *CSVReader) LoadActionTriggers() (err error) { } at := &ActionTrigger{ - Id: utils.GenUUID(), + Id: tag, ThresholdType: record[ATRIGCSVIDX_THRESHOLD_TYPE], ThresholdValue: value, Recurrent: recurrent, @@ -834,6 +840,12 @@ func (csvr *CSVReader) LoadActionTriggers() (err error) { ActionsId: record[ATRIGCSVIDX_ACTIONS_TAG], Weight: weight, } + // update Id + idx := 0 + if previous, ok := csvr.actionsTriggers[tag]; ok { + idx = len(previous) + } + at.Id = at.Id + strconv.Itoa(idx) csvr.actionsTriggers[tag] = append(csvr.actionsTriggers[tag], at) } return diff --git a/engine/loader_csv_test.go b/engine/loader_csv_test.go index e7b781daa..9dfaffb78 100644 --- a/engine/loader_csv_test.go +++ b/engine/loader_csv_test.go @@ -696,7 +696,7 @@ func TestLoadActions(t *testing.T) { as1 := csvr.actions["MINI"] expected := []*Action{ &Action{ - Id: as1[0].Id, + Id: "MINI0", ActionType: TOPUP_RESET, BalanceType: CREDIT, Direction: OUTBOUND, @@ -710,7 +710,7 @@ func TestLoadActions(t *testing.T) { }, }, &Action{ - Id: as1[1].Id, + Id: "MINI1", ActionType: TOPUP, BalanceType: MINUTES, Direction: OUTBOUND, @@ -732,7 +732,7 @@ func TestLoadActions(t *testing.T) { as2 := csvr.actions["SHARED"] expected = []*Action{ &Action{ - Id: as2[0].Id, + Id: "SHARED0", ActionType: TOPUP, BalanceType: CREDIT, Direction: OUTBOUND, @@ -867,7 +867,7 @@ func TestLoadActionTriggers(t *testing.T) { } atr := csvr.actionsTriggers["STANDARD_TRIGGER"][0] expected := &ActionTrigger{ - Id: atr.Id, + Id: "STANDARD_TRIGGER0", BalanceType: MINUTES, BalanceDirection: OUTBOUND, ThresholdType: TRIGGER_MIN_COUNTER, @@ -882,7 +882,7 @@ func TestLoadActionTriggers(t *testing.T) { } atr = csvr.actionsTriggers["STANDARD_TRIGGER"][1] expected = &ActionTrigger{ - Id: atr.Id, + Id: "STANDARD_TRIGGER1", BalanceType: MINUTES, BalanceDirection: OUTBOUND, ThresholdType: TRIGGER_MAX_BALANCE, diff --git a/engine/loader_db.go b/engine/loader_db.go index 8b12209eb..8b9dd4336 100644 --- a/engine/loader_db.go +++ b/engine/loader_db.go @@ -22,6 +22,7 @@ import ( "errors" "fmt" "log" + "strconv" "strings" "github.com/cgrates/cgrates/utils" @@ -561,7 +562,7 @@ func (dbr *DbReader) LoadActions() (err error) { acts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { acts[idx] = &Action{ - Id: utils.GenUUID(), + Id: tag + strconv.Itoa(idx), ActionType: tpact.Identifier, BalanceType: tpact.BalanceType, Direction: tpact.Direction, @@ -650,7 +651,7 @@ func (dbr *DbReader) LoadActionTriggers() (err error) { for idx, apiAtr := range atrsLst { balance_expiration_date, _ := utils.ParseTimeDetectLayout(apiAtr.BalanceExpirationDate) atrs[idx] = &ActionTrigger{ - Id: utils.GenUUID(), + Id: key + strconv.Itoa(idx), ThresholdType: apiAtr.ThresholdType, ThresholdValue: apiAtr.ThresholdValue, Recurrent: apiAtr.Recurrent, @@ -841,7 +842,7 @@ func (dbr *DbReader) LoadAccountActionsFiltered(qriedAA *utils.TPAccountActions) enacts := make([]*Action, len(tpacts)) for idx, tpact := range tpacts { enacts[idx] = &Action{ - Id: utils.GenUUID(), + Id: tag + strconv.Itoa(idx), ActionType: tpact.Identifier, BalanceType: tpact.BalanceType, Direction: tpact.Direction, diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 197a94b6e..63395e2b6 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -608,10 +608,10 @@ func (self *SQLStorage) SetTPActionTriggers(tpid string, ats map[string][]*utils tx.Rollback() return err } - for _, at := range aTriggers { + for idx, at := range aTriggers { saved := tx.Save(TpActionTrigger{ Tpid: tpid, - Tag: atId, + Tag: atId + strconv.Itoa(idx), ThresholdType: at.ThresholdType, ThresholdValue: at.ThresholdValue, Recurrent: at.Recurrent, diff --git a/utils/apitpdata.go b/utils/apitpdata.go index 80d91ea04..0384fe3b3 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -514,7 +514,7 @@ type TPActionTiming struct { type TPActionTriggers struct { TPid string // Tariff plan id - ActionTriggersId string // Profile id + ActionTriggersId string // action trigger id ActionTriggers []*TPActionTrigger // Set of triggers grouped in this profile } @@ -531,6 +531,7 @@ func (self *TPActionTriggers) AsExportSlice() [][]string { } type TPActionTrigger struct { + Id string ThresholdType string // This threshold type ThresholdValue float64 // Threshold Recurrent bool // reset executed flag each run