mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Add action type *dynamic_action_trigger
This commit is contained in:
committed by
Dan Christian Bogos
parent
0e15a7826f
commit
990f001465
172
engine/action.go
172
engine/action.go
@@ -131,7 +131,7 @@ func newActionConnCfg(source, action string, cfg *config.CGRConfig) ActionConnCf
|
||||
utils.MetaDynamicActionPlanAccounts, utils.MetaDynamicAction,
|
||||
utils.MetaDynamicDestination, utils.MetaDynamicFilter,
|
||||
utils.MetaDynamicRoute, utils.MetaDynamicRatingProfile,
|
||||
utils.MetaDynamicResource,
|
||||
utils.MetaDynamicResource, utils.MetaDynamicActionTrigger,
|
||||
}
|
||||
act := ActionConnCfg{}
|
||||
switch source {
|
||||
@@ -204,6 +204,7 @@ func init() {
|
||||
actionFuncMap[utils.MetaDynamicRatingProfile] = dynamicRatingProfile
|
||||
actionFuncMap[utils.MetaDynamicRanking] = dynamicTrend
|
||||
actionFuncMap[utils.MetaDynamicResource] = dynamicResource
|
||||
actionFuncMap[utils.MetaDynamicActionTrigger] = dynamicActionTrigger
|
||||
}
|
||||
|
||||
func getActionFunc(typ string) (f actionTypeFunc, exists bool) {
|
||||
@@ -2781,3 +2782,172 @@ func dynamicResource(_ *Account, act *Action, _ Actions, _ *FilterS, ev any,
|
||||
var reply string
|
||||
return connMgr.Call(context.Background(), connCfg.ConnIDs, utils.APIerSv1SetResourceProfile, rsc, &reply)
|
||||
}
|
||||
|
||||
// dynamicActionTrigger processes the `ExtraParameters` field from the action to
|
||||
// construct a ActionTrigger
|
||||
//
|
||||
// The ExtraParameters field format is expected as follows:
|
||||
//
|
||||
// 0 Tag: string
|
||||
// 1 UniqueId: string
|
||||
// 2 ThresholdType: string
|
||||
// 3 ThresholdValue: float
|
||||
// 4 Recurrent: bool
|
||||
// 5 MinSleep: duration
|
||||
// 6 ExpiryTime: time
|
||||
// 7 ActivationTime: time
|
||||
// 8 BalanceTag: string
|
||||
// 9 BalanceType: string
|
||||
// 10 BalanceCategories: strings separated by "&".
|
||||
// 11 BalanceDestinationIds: strings separated by "&".
|
||||
// 12 BalanceRatingSubject: string
|
||||
// 13 BalanceSharedGroup: strings separated by "&".
|
||||
// 14 BalanceExpiryTime: time
|
||||
// 15 BalanceTimingIds: strings separated by "&".
|
||||
// 16 BalanceWeight: float
|
||||
// 17 BalanceBlocker: bool
|
||||
// 18 BalanceDisabled: bool
|
||||
// 19 ActionsId: string
|
||||
// 20 Weight: float
|
||||
//
|
||||
// Parameters are separated by ";" and must be provided in the specified order.
|
||||
func dynamicActionTrigger(_ *Account, act *Action, _ Actions, _ *FilterS, ev any,
|
||||
_ SharedActionsData, connCfg ActionConnCfg) (err error) {
|
||||
cgrEv, canCast := ev.(*utils.CGREvent)
|
||||
if !canCast {
|
||||
return errors.New("Couldn't cast event to CGREvent")
|
||||
}
|
||||
dP := utils.MapStorage{ // create DataProvider from event
|
||||
utils.MetaReq: cgrEv.Event,
|
||||
utils.MetaTenant: cgrEv.Tenant,
|
||||
utils.MetaNow: time.Now(),
|
||||
utils.MetaOpts: cgrEv.APIOpts,
|
||||
}
|
||||
// Parse action parameters based on the predefined format.
|
||||
params := strings.Split(act.ExtraParameters, utils.InfieldSep)
|
||||
if len(params) != 21 {
|
||||
return fmt.Errorf("invalid number of parameters <%d> expected 21", len(params))
|
||||
}
|
||||
// parse dynamic parameters
|
||||
for i := range params {
|
||||
if params[i], err = utils.ParseParamForDataProvider(params[i], dP, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Prepare request arguments based on provided parameters.
|
||||
at := &AttrSetActionTrigger{
|
||||
GroupID: params[0],
|
||||
UniqueID: utils.FirstNonEmpty(params[1], utils.GenUUID()),
|
||||
ActionTrigger: make(map[string]any),
|
||||
}
|
||||
// populate ActionTrigger's ThresholdType
|
||||
if params[2] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.ThresholdType] = params[2]
|
||||
}
|
||||
// populate ActionTrigger's ThresholdValue
|
||||
if params[3] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.ThresholdValue], err = strconv.ParseFloat(params[3], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate ActionTrigger's Recurrent
|
||||
if params[4] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.Recurrent], err = strconv.ParseBool(params[4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate ActionTrigger's MinSleep
|
||||
if params[5] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.MinSleep], err = utils.ParseDurationWithNanosecs(params[5])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate ActionTrigger's ExpirationDate
|
||||
if params[6] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.ExpirationDate], err = utils.ParseTimeDetectLayout(params[6], config.CgrConfig().GeneralCfg().DefaultTimezone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate ActionTrigger's ActivationDate
|
||||
if params[7] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.ActivationDate], err = utils.ParseTimeDetectLayout(params[7], config.CgrConfig().GeneralCfg().DefaultTimezone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate ActionTrigger's BalanceID
|
||||
if params[8] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.BalanceID] = params[8]
|
||||
}
|
||||
// populate ActionTrigger's BalanceType
|
||||
if params[9] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.BalanceType] = params[9]
|
||||
}
|
||||
// populate ActionTrigger's BalanceCategories
|
||||
if params[10] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.BalanceCategories] = strings.Split(params[10], utils.ANDSep)
|
||||
}
|
||||
// populate ActionTrigger's BalanceDestinationIds
|
||||
if params[11] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.BalanceDestinationIds] = strings.Split(params[11], utils.ANDSep)
|
||||
}
|
||||
// populate ActionTrigger's BalanceRatingSubject
|
||||
if params[12] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.BalanceRatingSubject] = params[12]
|
||||
}
|
||||
// populate ActionTrigger's BalanceSharedGroups
|
||||
if params[13] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.BalanceSharedGroups] = strings.Split(params[13], utils.ANDSep)
|
||||
}
|
||||
// populate ActionTrigger's BalanceExpirationDate
|
||||
if params[14] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.BalanceExpirationDate], err = utils.ParseTimeDetectLayout(params[14], config.CgrConfig().GeneralCfg().DefaultTimezone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate ActionTrigger's BalanceTimingTags
|
||||
if params[15] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.BalanceTimingTags] = strings.Split(params[15], utils.ANDSep)
|
||||
}
|
||||
// populate ActionTrigger's BalanceWeight
|
||||
if params[16] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.BalanceWeight], err = strconv.ParseFloat(params[16], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate ActionTrigger's BalanceBlocker
|
||||
if params[17] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.BalanceBlocker], err = strconv.ParseBool(params[17])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate ActionTrigger's BalanceDisabled
|
||||
if params[18] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.BalanceDisabled], err = strconv.ParseBool(params[18])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate ActionTrigger's ActionsID
|
||||
if params[19] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.ActionsID] = params[19]
|
||||
}
|
||||
// populate ActionTrigger's Weight
|
||||
if params[20] != utils.EmptyString {
|
||||
at.ActionTrigger[utils.Weight], err = strconv.ParseFloat(params[20], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// create the ActionTrigger based on the populated parameters
|
||||
var reply string
|
||||
return connMgr.Call(context.Background(), connCfg.ConnIDs, utils.APIerSv1SetActionTrigger, at, &reply)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ package engine
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"time"
|
||||
@@ -314,3 +315,154 @@ func (at *ActionTrigger) FieldAsString(fldPath []string) (val string, err error)
|
||||
}
|
||||
return utils.IfaceAsString(iface), nil
|
||||
}
|
||||
|
||||
type AttrSetActionTrigger struct {
|
||||
GroupID string
|
||||
UniqueID string
|
||||
ActionTrigger map[string]any
|
||||
}
|
||||
|
||||
// UpdateActionTrigger updates the ActionTrigger if is matching
|
||||
func (attr *AttrSetActionTrigger) UpdateActionTrigger(at *ActionTrigger, timezone string) (updated bool, err error) {
|
||||
if at == nil {
|
||||
return false, errors.New("Empty ActionTrigger")
|
||||
}
|
||||
if at.ID == utils.EmptyString { // New AT, update it's data
|
||||
if attr.GroupID == utils.EmptyString {
|
||||
return false, utils.NewErrMandatoryIeMissing(utils.GroupID)
|
||||
}
|
||||
if missing := utils.MissingMapFields(attr.ActionTrigger, []string{"ThresholdType", "ThresholdValue"}); len(missing) != 0 {
|
||||
return false, utils.NewErrMandatoryIeMissing(missing...)
|
||||
}
|
||||
at.ID = attr.GroupID
|
||||
if attr.UniqueID != utils.EmptyString {
|
||||
at.UniqueID = attr.UniqueID
|
||||
}
|
||||
}
|
||||
if attr.GroupID != utils.EmptyString && attr.GroupID != at.ID {
|
||||
return
|
||||
}
|
||||
if attr.UniqueID != utils.EmptyString && attr.UniqueID != at.UniqueID {
|
||||
return
|
||||
}
|
||||
// at matches
|
||||
updated = true
|
||||
if thr, has := attr.ActionTrigger[utils.ThresholdType]; has {
|
||||
at.ThresholdType = utils.IfaceAsString(thr)
|
||||
}
|
||||
if thr, has := attr.ActionTrigger[utils.ThresholdValue]; has {
|
||||
if at.ThresholdValue, err = utils.IfaceAsFloat64(thr); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if rec, has := attr.ActionTrigger[utils.Recurrent]; has {
|
||||
if at.Recurrent, err = utils.IfaceAsBool(rec); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if exec, has := attr.ActionTrigger[utils.Executed]; has {
|
||||
if at.Executed, err = utils.IfaceAsBool(exec); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if minS, has := attr.ActionTrigger[utils.MinSleep]; has {
|
||||
if at.MinSleep, err = utils.IfaceAsDuration(minS); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if exp, has := attr.ActionTrigger[utils.ExpirationDate]; has {
|
||||
if at.ExpirationDate, err = utils.IfaceAsTime(exp, timezone); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if act, has := attr.ActionTrigger[utils.ActivationDate]; has {
|
||||
if at.ActivationDate, err = utils.IfaceAsTime(act, timezone); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if at.Balance == nil {
|
||||
at.Balance = &BalanceFilter{}
|
||||
}
|
||||
if bid, has := attr.ActionTrigger[utils.BalanceID]; has {
|
||||
at.Balance.ID = utils.StringPointer(utils.IfaceAsString(bid))
|
||||
}
|
||||
if btype, has := attr.ActionTrigger[utils.BalanceType]; has {
|
||||
at.Balance.Type = utils.StringPointer(utils.IfaceAsString(btype))
|
||||
}
|
||||
if bdest, has := attr.ActionTrigger[utils.BalanceDestinationIds]; has {
|
||||
var bdIds []string
|
||||
if bdIds, err = utils.IfaceAsSliceString(bdest); err != nil {
|
||||
return
|
||||
}
|
||||
at.Balance.DestinationIDs = utils.StringMapPointer(utils.NewStringMap(bdIds...))
|
||||
}
|
||||
if bweight, has := attr.ActionTrigger[utils.BalanceWeight]; has {
|
||||
var bw float64
|
||||
if bw, err = utils.IfaceAsFloat64(bweight); err != nil {
|
||||
return
|
||||
}
|
||||
at.Balance.Weight = utils.Float64Pointer(bw)
|
||||
}
|
||||
if exp, has := attr.ActionTrigger[utils.BalanceExpirationDate]; has {
|
||||
var balanceExpTime time.Time
|
||||
if balanceExpTime, err = utils.IfaceAsTime(exp, timezone); err != nil {
|
||||
return
|
||||
}
|
||||
at.Balance.ExpirationDate = utils.TimePointer(balanceExpTime)
|
||||
}
|
||||
if bTimeTag, has := attr.ActionTrigger[utils.BalanceTimingTags]; has {
|
||||
var timeTag []string
|
||||
if timeTag, err = utils.IfaceAsSliceString(bTimeTag); err != nil {
|
||||
return
|
||||
}
|
||||
at.Balance.TimingIDs = utils.StringMapPointer(utils.NewStringMap(timeTag...))
|
||||
}
|
||||
if brs, has := attr.ActionTrigger[utils.BalanceRatingSubject]; has {
|
||||
at.Balance.RatingSubject = utils.StringPointer(utils.IfaceAsString(brs))
|
||||
}
|
||||
if bcat, has := attr.ActionTrigger[utils.BalanceCategories]; has {
|
||||
var cat []string
|
||||
if cat, err = utils.IfaceAsSliceString(bcat); err != nil {
|
||||
return
|
||||
}
|
||||
at.Balance.Categories = utils.StringMapPointer(utils.NewStringMap(cat...))
|
||||
}
|
||||
if bsg, has := attr.ActionTrigger[utils.BalanceSharedGroups]; has {
|
||||
var shrgrps []string
|
||||
if shrgrps, err = utils.IfaceAsSliceString(bsg); err != nil {
|
||||
return
|
||||
}
|
||||
at.Balance.SharedGroups = utils.StringMapPointer(utils.NewStringMap(shrgrps...))
|
||||
}
|
||||
if bb, has := attr.ActionTrigger[utils.BalanceBlocker]; has {
|
||||
var bBlocker bool
|
||||
if bBlocker, err = utils.IfaceAsBool(bb); err != nil {
|
||||
return
|
||||
}
|
||||
at.Balance.Blocker = utils.BoolPointer(bBlocker)
|
||||
}
|
||||
if bd, has := attr.ActionTrigger[utils.BalanceDisabled]; has {
|
||||
var bDis bool
|
||||
if bDis, err = utils.IfaceAsBool(bd); err != nil {
|
||||
return
|
||||
}
|
||||
at.Balance.Disabled = utils.BoolPointer(bDis)
|
||||
}
|
||||
if minQ, has := attr.ActionTrigger[utils.MinQueuedItems]; has {
|
||||
var mQ int64
|
||||
if mQ, err = utils.IfaceAsTInt64(minQ); err != nil {
|
||||
return
|
||||
}
|
||||
at.MinQueuedItems = int(mQ)
|
||||
}
|
||||
if accID, has := attr.ActionTrigger[utils.ActionsID]; has {
|
||||
at.ActionsID = utils.IfaceAsString(accID)
|
||||
}
|
||||
|
||||
if weight, has := attr.ActionTrigger[utils.Weight]; has {
|
||||
if at.Weight, err = utils.IfaceAsFloat64(weight); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -7334,3 +7334,264 @@ func TestDynamicResource(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDynamicActionTrigger(t *testing.T) {
|
||||
tempConn := connMgr
|
||||
tmpDm := dm
|
||||
tmpCache := Cache
|
||||
defer func() {
|
||||
config.SetCgrConfig(config.NewDefaultCGRConfig())
|
||||
SetConnManager(tempConn)
|
||||
dm = tmpDm
|
||||
Cache = tmpCache
|
||||
}()
|
||||
Cache.Clear(nil)
|
||||
var at *AttrSetActionTrigger
|
||||
ccMock := &ccMock{
|
||||
calls: map[string]func(ctx *context.Context, args any, reply any) error{
|
||||
utils.APIerSv1SetActionTrigger: func(ctx *context.Context, args, reply any) error {
|
||||
var canCast bool
|
||||
if at, canCast = args.(*AttrSetActionTrigger); !canCast {
|
||||
return fmt.Errorf("couldnt cast")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
connID := utils.ConcatenatedKey(utils.MetaInternal, utils.MetaApier)
|
||||
clientconn := make(chan birpc.ClientConnector, 1)
|
||||
clientconn <- ccMock
|
||||
NewConnManager(config.NewDefaultCGRConfig(), map[string]chan birpc.ClientConnector{
|
||||
connID: clientconn,
|
||||
})
|
||||
testcases := []struct {
|
||||
name string
|
||||
extraParams string
|
||||
connIDs []string
|
||||
expAt *AttrSetActionTrigger
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "SuccessfulRequest",
|
||||
connIDs: []string{connID},
|
||||
expAt: &AttrSetActionTrigger{
|
||||
GroupID: "STANDARD_TRIGGERS",
|
||||
UniqueID: "uid",
|
||||
ActionTrigger: map[string]any{
|
||||
utils.ThresholdType: "*max_balance",
|
||||
utils.ThresholdValue: float64(20),
|
||||
utils.Recurrent: true,
|
||||
utils.MinSleep: time.Second,
|
||||
utils.ActivationDate: time.Date(2014, 7, 29, 15, 0, 0, 0, time.UTC),
|
||||
utils.BalanceID: "*default",
|
||||
utils.BalanceType: "*monetary",
|
||||
utils.BalanceCategories: []string{utils.Call, "data"},
|
||||
utils.BalanceDestinationIds: []string{"DST1", "DST2"},
|
||||
utils.BalanceRatingSubject: "SPECIAL_1002",
|
||||
utils.BalanceSharedGroups: []string{"SHRGroup1", "SHRGroup2"},
|
||||
utils.BalanceExpirationDate: time.Date(2030, 7, 29, 15, 0, 0, 0, time.UTC),
|
||||
utils.BalanceTimingTags: []string{"*asap"},
|
||||
utils.BalanceWeight: float64(10),
|
||||
utils.BalanceBlocker: true,
|
||||
utils.BalanceDisabled: true,
|
||||
utils.ActionsID: "ACT_1001",
|
||||
utils.Weight: float64(20),
|
||||
},
|
||||
},
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;true;1s;;2014-07-29T15:00:00Z;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;2030-07-29T15:00:00Z;*asap;10;true;true;ACT_1001;20",
|
||||
},
|
||||
{
|
||||
name: "SuccessfulRequestWithDynamicPaths",
|
||||
connIDs: []string{connID},
|
||||
expAt: &AttrSetActionTrigger{
|
||||
GroupID: "STANDARD_TRIGGERS_1001",
|
||||
UniqueID: "uid_1001",
|
||||
ActionTrigger: map[string]any{
|
||||
utils.ThresholdType: "*max_balance",
|
||||
utils.ThresholdValue: float64(20),
|
||||
utils.Recurrent: true,
|
||||
utils.MinSleep: time.Second,
|
||||
utils.ExpirationDate: time.Now(),
|
||||
utils.ActivationDate: time.Now(),
|
||||
utils.BalanceID: "*default",
|
||||
utils.BalanceType: "*monetary",
|
||||
utils.BalanceCategories: []string{utils.Call, "data"},
|
||||
utils.BalanceDestinationIds: []string{"DST_1001", "DST2"},
|
||||
utils.BalanceRatingSubject: "SPECIAL_1001",
|
||||
utils.BalanceSharedGroups: []string{"SHRGroup_1001", "SHRGroup2"},
|
||||
utils.BalanceExpirationDate: time.Now(),
|
||||
utils.BalanceTimingTags: []string{"*asap"},
|
||||
utils.BalanceWeight: float64(10),
|
||||
utils.BalanceBlocker: true,
|
||||
utils.BalanceDisabled: true,
|
||||
utils.ActionsID: "ACT_1001",
|
||||
utils.Weight: float64(20),
|
||||
},
|
||||
},
|
||||
extraParams: "STANDARD_TRIGGERS_<~*req.Account>;uid_<~*req.Account>;*max_balance;20;true;1s;*now;*now;*default;*monetary;call&data;DST_<~*req.Account>&DST2;SPECIAL_<~*req.Account>;SHRGroup_<~*req.Account>&SHRGroup2;*now;*asap;10;true;true;ACT_<~*req.Account>;20",
|
||||
},
|
||||
{
|
||||
name: "SuccessfulRequestEmptyFields",
|
||||
connIDs: []string{connID},
|
||||
expAt: &AttrSetActionTrigger{
|
||||
GroupID: "STANDARD_TRIGGERS",
|
||||
UniqueID: "uid",
|
||||
ActionTrigger: map[string]any{
|
||||
utils.ThresholdType: "*max_balance",
|
||||
utils.ThresholdValue: float64(20),
|
||||
},
|
||||
},
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;;;;;;;;;;;;;;;;;",
|
||||
},
|
||||
{
|
||||
name: "MissingConns",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;true;1s;;2014-07-29T15:00:00Z;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;2030-07-29T15:00:00Z;*asap;10;true;true;ACT_1001;20",
|
||||
expectedErr: "MANDATORY_IE_MISSING: [connIDs]",
|
||||
},
|
||||
{
|
||||
name: "WrongNumberOfParams",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20",
|
||||
expectedErr: "invalid number of parameters <4> expected 21",
|
||||
},
|
||||
{
|
||||
name: "ThresholdValueFail",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;BadString;true;1s;;2014-07-29T15:00:00Z;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;2030-07-29T15:00:00Z;*asap;10;true;true;ACT_1001;20",
|
||||
expectedErr: `strconv.ParseFloat: parsing "BadString": invalid syntax`,
|
||||
},
|
||||
{
|
||||
name: "RecurrentFail",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;BadString;1s;;2014-07-29T15:00:00Z;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;2030-07-29T15:00:00Z;*asap;10;true;true;ACT_1001;20",
|
||||
expectedErr: `strconv.ParseBool: parsing "BadString": invalid syntax`,
|
||||
},
|
||||
{
|
||||
name: "MinSleepFail",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;true;BadString;;2014-07-29T15:00:00Z;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;2030-07-29T15:00:00Z;*asap;10;true;true;ACT_1001;20",
|
||||
expectedErr: `time: invalid duration "BadString"`,
|
||||
},
|
||||
{
|
||||
name: "ExpirationDateBadStringFail",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;true;1s;bad String;2014-07-29T15:00:00Z;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;2030-07-29T15:00:00Z;*asap;10;true;true;ACT_1001;20",
|
||||
expectedErr: `Unsupported time format`,
|
||||
},
|
||||
{
|
||||
name: "ActivationDateBadStringFail",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;true;1s;;bad String;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;2030-07-29T15:00:00Z;*asap;10;true;true;ACT_1001;20",
|
||||
expectedErr: `Unsupported time format`,
|
||||
},
|
||||
{
|
||||
name: "BalanceExpirationDateBadStringFail",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;true;1s;;2014-07-29T15:00:00Z;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;bad String;*asap;10;true;true;ACT_1001;20",
|
||||
expectedErr: `Unsupported time format`,
|
||||
},
|
||||
{
|
||||
name: "BalanceWeightFail",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;true;1s;;2014-07-29T15:00:00Z;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;2030-07-29T15:00:00Z;*asap;BadString;true;true;ACT_1001;20",
|
||||
expectedErr: `strconv.ParseFloat: parsing "BadString": invalid syntax`,
|
||||
},
|
||||
{
|
||||
name: "BalanceBlockerFail",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;true;1s;;2014-07-29T15:00:00Z;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;2030-07-29T15:00:00Z;*asap;10;BadString;true;ACT_1001;20",
|
||||
expectedErr: `strconv.ParseBool: parsing "BadString": invalid syntax`,
|
||||
},
|
||||
{
|
||||
name: "BalanceDisabledFail",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;true;1s;;2014-07-29T15:00:00Z;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;2030-07-29T15:00:00Z;*asap;10;true;BadString;ACT_1001;20",
|
||||
expectedErr: `strconv.ParseBool: parsing "BadString": invalid syntax`,
|
||||
},
|
||||
{
|
||||
name: "WeightFail",
|
||||
extraParams: "STANDARD_TRIGGERS;uid;*max_balance;20;true;1s;;2014-07-29T15:00:00Z;*default;*monetary;call&data;DST1&DST2;SPECIAL_1002;SHRGroup1&SHRGroup2;2030-07-29T15:00:00Z;*asap;10;true;true;ACT_1001;BadString",
|
||||
expectedErr: `strconv.ParseFloat: parsing "BadString": invalid syntax`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
action := &Action{ExtraParameters: tc.extraParams}
|
||||
ev := &utils.CGREvent{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "evID",
|
||||
Time: &time.Time{},
|
||||
Event: map[string]any{
|
||||
utils.AccountField: "1001",
|
||||
},
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
at = nil
|
||||
})
|
||||
err := dynamicActionTrigger(nil, action, nil, nil, ev,
|
||||
SharedActionsData{}, ActionConnCfg{
|
||||
ConnIDs: tc.connIDs,
|
||||
})
|
||||
if tc.expectedErr != "" {
|
||||
if err == nil || err.Error() != tc.expectedErr {
|
||||
t.Errorf("expected error <%v>, received <%v>", tc.expectedErr, err)
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(at, tc.expAt) {
|
||||
if i != 1 {
|
||||
t.Errorf("Expected <%v>\nReceived\n<%v>", utils.ToJSON(tc.expAt), utils.ToJSON(at))
|
||||
} else {
|
||||
// Get the absolute difference between the times
|
||||
rcvExpDate, err := utils.IfaceAsTime(at.ActionTrigger[utils.ExpirationDate], config.CgrConfig().GeneralCfg().DefaultTimezone)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expExpDate, err := utils.IfaceAsTime(tc.expAt.ActionTrigger[utils.ExpirationDate], config.CgrConfig().GeneralCfg().DefaultTimezone)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
diff := rcvExpDate.Sub(expExpDate)
|
||||
if diff < 0 {
|
||||
diff = -diff // Make sure it's positive
|
||||
}
|
||||
// Check if difference is less than or equal to 1 second
|
||||
if diff > time.Second {
|
||||
t.Fatalf("Expected <%v>\nReceived\n<%v>", utils.ToJSON(tc.expAt), utils.ToJSON(at))
|
||||
}
|
||||
tc.expAt.ActionTrigger[utils.ExpirationDate] = rcvExpDate
|
||||
// Get the absolute difference between the times
|
||||
rcvExpDate, err = utils.IfaceAsTime(at.ActionTrigger[utils.ActivationDate], config.CgrConfig().GeneralCfg().DefaultTimezone)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expExpDate, err = utils.IfaceAsTime(tc.expAt.ActionTrigger[utils.ActivationDate], config.CgrConfig().GeneralCfg().DefaultTimezone)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
diff = rcvExpDate.Sub(expExpDate)
|
||||
if diff < 0 {
|
||||
diff = -diff // Make sure it's positive
|
||||
}
|
||||
// Check if difference is less than or equal to 1 second
|
||||
if diff > time.Second {
|
||||
t.Fatalf("Expected <%v>\nReceived\n<%v>", utils.ToJSON(tc.expAt), utils.ToJSON(at))
|
||||
}
|
||||
tc.expAt.ActionTrigger[utils.ActivationDate] = rcvExpDate
|
||||
// Get the absolute difference between the times
|
||||
rcvExpDate, err = utils.IfaceAsTime(at.ActionTrigger[utils.BalanceExpirationDate], config.CgrConfig().GeneralCfg().DefaultTimezone)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
expExpDate, err = utils.IfaceAsTime(tc.expAt.ActionTrigger[utils.BalanceExpirationDate], config.CgrConfig().GeneralCfg().DefaultTimezone)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
diff = rcvExpDate.Sub(expExpDate)
|
||||
if diff < 0 {
|
||||
diff = -diff // Make sure it's positive
|
||||
}
|
||||
// Check if difference is less than or equal to 1 second
|
||||
if diff > time.Second {
|
||||
t.Fatalf("Expected <%v>\nReceived\n<%v>", utils.ToJSON(tc.expAt), utils.ToJSON(at))
|
||||
}
|
||||
tc.expAt.ActionTrigger[utils.BalanceExpirationDate] = rcvExpDate
|
||||
if !reflect.DeepEqual(at, tc.expAt) {
|
||||
t.Errorf("Expected <%v>\nReceived\n<%v>", utils.ToJSON(tc.expAt), utils.ToJSON(at))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user