From 262f1dafa3b046c8a28bfbae918c6388a9627787 Mon Sep 17 00:00:00 2001 From: arberkatellari Date: Thu, 7 Aug 2025 16:07:05 +0200 Subject: [PATCH] Add action type *dynamicAction --- actions/dynamic.go | 292 ++++- actions/libactions.go | 4 + data/tariffplans/dynamic_tps/Actions.csv | 5 + data/tariffplans/dynamic_tps/Attributes.csv | 5 + data/tariffplans/dynamic_tps/Filters.csv | 6 + data/tariffplans/dynamic_tps/IPs.csv | 5 + data/tariffplans/dynamic_tps/Rankings.csv | 6 + data/tariffplans/dynamic_tps/Rates.csv | 5 + data/tariffplans/dynamic_tps/Resources.csv | 5 + data/tariffplans/dynamic_tps/Routes.csv | 5 + data/tariffplans/dynamic_tps/Stats.csv | 5 + data/tariffplans/dynamic_tps/Thresholds.csv | 5 + data/tariffplans/dynamic_tps/Trends.csv | 6 + engine/thresholds.go | 4 + engine/thresholds_test.go | 4 +- general_tests/dynamic_thresholds_it_test.go | 1134 ++++++++++++++++++- utils/consts.go | 1 + 17 files changed, 1468 insertions(+), 29 deletions(-) create mode 100644 data/tariffplans/dynamic_tps/Actions.csv create mode 100644 data/tariffplans/dynamic_tps/Attributes.csv create mode 100644 data/tariffplans/dynamic_tps/Filters.csv create mode 100644 data/tariffplans/dynamic_tps/IPs.csv create mode 100644 data/tariffplans/dynamic_tps/Rankings.csv create mode 100644 data/tariffplans/dynamic_tps/Rates.csv create mode 100644 data/tariffplans/dynamic_tps/Resources.csv create mode 100644 data/tariffplans/dynamic_tps/Routes.csv create mode 100644 data/tariffplans/dynamic_tps/Stats.csv create mode 100644 data/tariffplans/dynamic_tps/Thresholds.csv create mode 100644 data/tariffplans/dynamic_tps/Trends.csv diff --git a/actions/dynamic.go b/actions/dynamic.go index 01a0196f3..2c4646c1f 100644 --- a/actions/dynamic.go +++ b/actions/dynamic.go @@ -62,7 +62,8 @@ func parseParamStringToMap(paramStr string, targetMap map[string]any) error { // 7 Blocker: bool, should always be true // 8 ActionProfileIDs: strings separated by "&". // 9 Async: bool -// 10 APIOpts: set of key-value pairs (separated by "&"). +// 10 EeIDs: strings separated by "&". +// 11 APIOpts: set of key-value pairs (separated by "&"). // // Parameters are separated by ";" and must be provided in the specified order. type actDynamicThreshold struct { @@ -116,8 +117,8 @@ func (aL *actDynamicThreshold) execute(ctx *context.Context, data utils.MapStora for _, diktat := range diktats { params := strings.Split(utils.IfaceAsString(diktat.Opts[utils.MetaTemplate]), utils.InfieldSep) - if len(params) != 11 { - return fmt.Errorf("invalid number of parameters <%d> expected 11", len(params)) + if len(params) != 12 { + return fmt.Errorf("invalid number of parameters <%d> expected 12", len(params)) } // parse dynamic parameters for i := range params { @@ -193,9 +194,15 @@ func (aL *actDynamicThreshold) execute(ctx *context.Context, data utils.MapStora return err } } - // populate Threshold's APIOpts + // populate Threshold's EeIDs if params[10] != utils.EmptyString { - if err := parseParamStringToMap(params[10], args.APIOpts); err != nil { + args.EeIDs = strings.Split(params[10], utils.ANDSep) + utils.Logger.Crit(args.EeIDs[0]) + + } + // populate Threshold's APIOpts + if params[11] != utils.EmptyString { + if err := parseParamStringToMap(params[11], args.APIOpts); err != nil { return err } } @@ -1896,3 +1903,278 @@ func (aL *actDynamicIP) execute(ctx *context.Context, data utils.MapStorage, trg } return } + +// actDynamicAction processes the `ActionDiktatsOpts` field from the action to construct a ActionProfile +// +// The ActionDiktatsOpts field format is expected as follows: +// +// 0 Tenant: string +// 1 ID: string +// 2 FilterIDs: strings separated by "&". +// 3 Weights: strings separated by "&". +// 4 Blockers: strings separated by "&". +// 5 Schedule: string +// 6 TargetType: string +// 7 TargetIDs: strings separated by "&". +// 8 ActionID: string +// 9 ActionFilterIDs: strings separated by "&". +// 10 ActionTTL: duration +// 11 ActionType: string +// 12 ActionOpts: set of key-value pairs (separated by "&"). +// 13 ActionWeights: strings separated by "&". +// 14 ActionBlockers: strings separated by "&". +// 15 ActionDiktatsID: string +// 16 ActionDiktatsFilterIDs: strings separated by "&". +// 17 ActionDiktatsOpts: set of key-value pairs (separated by "&"). +// 18 ActionDiktatsWeights: strings separated by "&". +// 19 ActionDiktatsBlockers: strings separated by "&". +// 20 APIOpts: set of key-value pairs (separated by "&"). +// +// Parameters are separated by ";" and must be provided in the specified order. +type actDynamicAction struct { + config *config.CGRConfig + connMgr *engine.ConnManager + fltrS *engine.FilterS + aCfg *utils.APAction + tnt string + cgrEv *utils.CGREvent +} + +func (aL *actDynamicAction) id() string { + return aL.aCfg.ID +} + +func (aL *actDynamicAction) cfg() *utils.APAction { + return aL.aCfg +} + +// execute implements actioner interface +func (aL *actDynamicAction) execute(ctx *context.Context, data utils.MapStorage, trgID string) (err error) { + if len(aL.config.ActionSCfg().AdminSConns) == 0 { + return fmt.Errorf("no connection with AdminS") + } + data[utils.MetaNow] = time.Now() + data[utils.MetaTenant] = utils.FirstNonEmpty(aL.cgrEv.Tenant, aL.tnt, + config.CgrConfig().GeneralCfg().DefaultTenant) + // Parse action parameters based on the predefined format. + if len(aL.aCfg.Diktats) == 0 { + return fmt.Errorf("No diktats were specified for action <%v>", aL.aCfg.ID) + } + weights := make(map[string]float64) // stores sorting weights by Diktat ID + diktats := make([]*utils.APDiktat, 0) // list of diktats which have *template in opts, will be weight sorted later + for _, diktat := range aL.aCfg.Diktats { + if pass, err := aL.fltrS.Pass(ctx, aL.tnt, diktat.FilterIDs, data); err != nil { + return err + } else if !pass { + continue + } + weight, err := engine.WeightFromDynamics(ctx, diktat.Weights, aL.fltrS, aL.tnt, data) + if err != nil { + return err + } + weights[diktat.ID] = weight + diktats = append(diktats, diktat) + } + // Sort by weight (higher values first). + slices.SortFunc(diktats, func(a, b *utils.APDiktat) int { + return cmp.Compare(weights[b.ID], weights[a.ID]) + }) + for _, diktat := range diktats { + params := strings.Split(utils.IfaceAsString(diktat.Opts[utils.MetaTemplate]), + 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], data, false); err != nil { + return err + } + } + // Prepare request arguments based on provided parameters. + args := &utils.ActionProfileWithAPIOpts{ + ActionProfile: &utils.ActionProfile{ + Tenant: params[0], + ID: params[1], + Schedule: params[5], + Targets: make(map[string]utils.StringSet), + Actions: []*utils.APAction{ + { + ID: params[8], + Type: params[11], + Opts: make(map[string]any), + Diktats: []*utils.APDiktat{ + { + ID: params[15], + Opts: make(map[string]any), + }, + }, + }, + }, + }, + APIOpts: make(map[string]any), + } + // populate ActionProfile's FilterIDs + if params[2] != utils.EmptyString { + args.FilterIDs = strings.Split(params[2], utils.ANDSep) + } + // populate ActionProfile's Weights + if params[3] != utils.EmptyString { + args.Weights = utils.DynamicWeights{&utils.DynamicWeight{}} + wghtSplit := strings.Split(params[3], utils.ANDSep) + if len(wghtSplit) > 2 { + return utils.ErrUnsupportedFormat + } + if wghtSplit[0] != utils.EmptyString { + args.Weights[0].FilterIDs = []string{wghtSplit[0]} + } + if wghtSplit[1] != utils.EmptyString { + args.Weights[0].Weight, err = strconv.ParseFloat(wghtSplit[1], 64) + if err != nil { + return err + } + } + } + // populate ActionProfile's Blockers + if params[4] != utils.EmptyString { + args.Blockers = utils.DynamicBlockers{&utils.DynamicBlocker{}} + blckrSplit := strings.Split(params[4], utils.ANDSep) + if len(blckrSplit) > 2 { + return utils.ErrUnsupportedFormat + } + if blckrSplit[0] != utils.EmptyString { + args.Blockers[0].FilterIDs = []string{blckrSplit[0]} + } + if blckrSplit[1] != utils.EmptyString { + args.Blockers[0].Blocker, err = strconv.ParseBool(blckrSplit[1]) + if err != nil { + return err + } + } + } + // populate ActionProfile's Targets + if params[6] != utils.EmptyString { + args.Targets[params[6]] = utils.NewStringSet(strings.Split(params[7], utils.ANDSep)) + } + // populate ActionProfile's Action + if params[8] != utils.EmptyString { + // populate Action's FilterIDs + if params[9] != utils.EmptyString { + args.Actions[0].FilterIDs = strings.Split(params[9], utils.ANDSep) + } + // populate Action's TTL + if params[10] != utils.EmptyString { + args.Actions[0].TTL, err = utils.ParseDurationWithNanosecs(params[10]) + if err != nil { + return err + } + } + // populate Action's Opts + if params[12] != utils.EmptyString { + if err := parseParamStringToMap(params[12], args.Actions[0].Opts); err != nil { + return err + } + } + // populate Action's Weights + if params[13] != utils.EmptyString { + args.Actions[0].Weights = utils.DynamicWeights{&utils.DynamicWeight{}} + wghtSplit := strings.Split(params[13], utils.ANDSep) + if len(wghtSplit) > 2 { + return utils.ErrUnsupportedFormat + } + if wghtSplit[0] != utils.EmptyString { + args.Actions[0].Weights[0].FilterIDs = []string{wghtSplit[0]} + } + if wghtSplit[1] != utils.EmptyString { + args.Actions[0].Weights[0].Weight, err = strconv.ParseFloat(wghtSplit[1], 64) + if err != nil { + return err + } + } + } + // populate Action's Blocker + if params[14] != utils.EmptyString { + args.Actions[0].Blockers = utils.DynamicBlockers{&utils.DynamicBlocker{}} + blckrSplit := strings.Split(params[14], utils.ANDSep) + if len(blckrSplit) > 2 { + return utils.ErrUnsupportedFormat + } + if blckrSplit[0] != utils.EmptyString { + args.Actions[0].Blockers[0].FilterIDs = []string{blckrSplit[0]} + } + if blckrSplit[1] != utils.EmptyString { + args.Actions[0].Blockers[0].Blocker, err = strconv.ParseBool(blckrSplit[1]) + if err != nil { + return err + } + } + } + // populate Action's Diktat + if params[15] != utils.EmptyString { + // populate Diktat's FilterIDs + if params[16] != utils.EmptyString { + args.Actions[0].Diktats[0].FilterIDs = strings.Split(params[16], utils.ANDSep) + } + // populate Diktat's Opts + if params[17] != utils.EmptyString { + if err := parseParamStringToMap(params[17], args.Actions[0].Diktats[0].Opts); err != nil { + return err + } + } + // populate Diktat's Weights + if params[18] != utils.EmptyString { + args.Actions[0].Diktats[0].Weights = utils.DynamicWeights{&utils.DynamicWeight{}} + wghtSplit := strings.Split(params[18], utils.ANDSep) + if len(wghtSplit) > 2 { + return utils.ErrUnsupportedFormat + } + if wghtSplit[0] != utils.EmptyString { + args.Actions[0].Diktats[0].Weights[0].FilterIDs = []string{wghtSplit[0]} + } + if wghtSplit[1] != utils.EmptyString { + args.Actions[0].Diktats[0].Weights[0].Weight, err = strconv.ParseFloat(wghtSplit[1], 64) + if err != nil { + return err + } + } + } + // populate Diktat's Blocker + if params[19] != utils.EmptyString { + args.Actions[0].Diktats[0].Blockers = utils.DynamicBlockers{&utils.DynamicBlocker{}} + blckrSplit := strings.Split(params[19], utils.ANDSep) + if len(blckrSplit) > 2 { + return utils.ErrUnsupportedFormat + } + if blckrSplit[0] != utils.EmptyString { + args.Actions[0].Diktats[0].Blockers[0].FilterIDs = []string{blckrSplit[0]} + } + if blckrSplit[1] != utils.EmptyString { + args.Actions[0].Diktats[0].Blockers[0].Blocker, err = strconv.ParseBool(blckrSplit[1]) + if err != nil { + return err + } + } + } + } + } + // populate ActionProfile's APIOpts + if params[20] != utils.EmptyString { + if err := parseParamStringToMap(params[20], args.APIOpts); err != nil { + return err + } + } + + // create the ActionProfile based on the populated parameters + var rply string + if err = aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns, + utils.AdminSv1SetActionProfile, args, &rply); err != nil { + return err + } + if blocker, err := engine.BlockerFromDynamics(ctx, diktat.Blockers, aL.fltrS, aL.tnt, data); err != nil { + return err + } else if blocker { + break + } + } + return +} diff --git a/actions/libactions.go b/actions/libactions.go index c742a2b00..741b7f5e8 100644 --- a/actions/libactions.go +++ b/actions/libactions.go @@ -53,6 +53,8 @@ func actionTarget(act string) string { return utils.MetaRates case utils.MetaDynamicIP: return utils.MetaIPs + case utils.MetaDynamicAction: + return utils.MetaActions default: return utils.MetaNone } @@ -169,6 +171,8 @@ func newActioner(ctx *context.Context, cgrEv *utils.CGREvent, cfg *config.CGRCon return &actDynamicRate{cfg, connMgr, fltrS, aCfg, tnt, cgrEv}, nil case utils.MetaDynamicIP: return &actDynamicIP{cfg, connMgr, fltrS, aCfg, tnt, cgrEv}, nil + case utils.MetaDynamicAction: + return &actDynamicAction{cfg, connMgr, fltrS, aCfg, tnt, cgrEv}, nil default: return nil, fmt.Errorf("unsupported action type: <%s>", aCfg.Type) diff --git a/data/tariffplans/dynamic_tps/Actions.csv b/data/tariffplans/dynamic_tps/Actions.csv new file mode 100644 index 000000000..3c02343ad --- /dev/null +++ b/data/tariffplans/dynamic_tps/Actions.csv @@ -0,0 +1,5 @@ +#Tenant[0];ID[1];FilterIDs[2];Weights[3];Blockers[4];Schedule[5];TargetType[6];TargetIDs[7];ActionID[8];ActionFilterIDs[9];ActionTTL[10];ActionType[11];ActionOpts[12];ActionWeights[13];ActionBlockers[14];ActionDiktatsID[15];ActionDiktatsFilterIDs[16];ActionDiktatsOpts[17];ActionDiktatsWeights[18];ActionDiktatsBlockers[19];APIOpts[20] + +cgrates.org;ONE_TIME_ACT;;&10;;*asap;*accounts;1001&1002;TOPUP;;0s;*addBalance;;;;ADDBALUNITS;;*balancePath:*balance.TestBalance.Units&*balanceValue:10;;; + +*tenant;DYNAMICLY_Action_<~*req.Account>;*string:~*req.Account:1002&*string:~*req.Account:1003;*string:~*req.Account:1002&10;*string:~*req.Account:1002&true;*asap;*accounts;1001&1002;TOPUP;*string:~*req.Account:1002&*string:~*req.Account:1003;10s;*addBalance;~*opts;*string:~*req.Account:1002&20;*string:~*req.Account:1002&true;ADDBALUNITS;*string:~*req.Account:1002&*string:~*req.Account:1003;*balancePath:*balance.TestBalance.Units&*balanceValue:10;*string:~*req.Account:1002&20;*string:~*req.Account:1002&true;~*opts \ No newline at end of file diff --git a/data/tariffplans/dynamic_tps/Attributes.csv b/data/tariffplans/dynamic_tps/Attributes.csv new file mode 100644 index 000000000..90cc19149 --- /dev/null +++ b/data/tariffplans/dynamic_tps/Attributes.csv @@ -0,0 +1,5 @@ +#Tenant[0];ID[1];FilterIDs[2];Weights[3];Blockers[4];AttributeFilterIDs[5];AttributeBlockers[6];Path[7];Type[8];Value[9];APIOpts[10] + +cgrates.org;ATTR_ACNT_1001;*string:~*opts.*context:*sessions&FLTR_ACCOUNT_1001;&10;&false;;;*req.OfficeGroup;*constant;Marketing; + +*tenant;DYNAMICLY_ATTR_<~*req.Account>;*string:~*req.Account:<~*req.Account>;*string:~*req.Account:<~*req.Account>&30;*string:~*req.Account:<~*req.Account>&true;*string:~*req.Account:<~*req.Account>;*string:~*req.Account:<~*req.Account>&true;*req.Subject;*constant;SUPPLIER1;~*opts \ No newline at end of file diff --git a/data/tariffplans/dynamic_tps/Filters.csv b/data/tariffplans/dynamic_tps/Filters.csv new file mode 100644 index 000000000..5219bd0cd --- /dev/null +++ b/data/tariffplans/dynamic_tps/Filters.csv @@ -0,0 +1,6 @@ +#Tenant[0];ID[1];Type[2];Path[3];Values[4];APIOpts[5] + +cgrates.org;FLTR_ACCOUNT_1001;*string;~*req.Account;1001; +cgrates.org;FLTR_1;*string;~*req.Account;1003&1002; + +*tenant;DYNAMICLY_FLTR_<~*req.Account>;*string;~*req.Account;1003&1002;~*opts \ No newline at end of file diff --git a/data/tariffplans/dynamic_tps/IPs.csv b/data/tariffplans/dynamic_tps/IPs.csv new file mode 100644 index 000000000..cdd1a65e3 --- /dev/null +++ b/data/tariffplans/dynamic_tps/IPs.csv @@ -0,0 +1,5 @@ +#Tenant[0];ID[1];FilterIDs[2];Weights[3];TTL[4];Stored[5];PoolID[6];PoolFilterIDs[7];PoolType[8];PoolRange[9];PoolStrategy[10];PoolMessage[11];PoolWeights[12];PoolBlockers[13];APIOpts[14] + +cgrates.org;IPs1;*string:~*req.Account:1001;&10;1s;true;POOL1;*string:~*req.Destination:2001;*ipv4;172.16.1.1/24;*ascending;alloc_success;&15;; + +*tenant;DYNAMICLY_IP_<~*req.Account>;*string:~*req.Account:1002&*string:~*req.Account:1003;*string:~*req.Account:1002&10;1s;true;Pool1;*string:~*req.Account:1002&*string:~*req.Account:1003;*ipv4;172.16.1.1/24;*ascending;alloc_success;*string:~*req.Account:1002&20;*string:~*req.Account:1002&true;~*opts \ No newline at end of file diff --git a/data/tariffplans/dynamic_tps/Rankings.csv b/data/tariffplans/dynamic_tps/Rankings.csv new file mode 100644 index 000000000..19c25c302 --- /dev/null +++ b/data/tariffplans/dynamic_tps/Rankings.csv @@ -0,0 +1,6 @@ +#Tenant[0];Id[1];Schedule[2];StatIDs[3];MetricIDs[4];Sorting[5];SortingParameters[6];Stored[7];ThresholdIDs[8];APIOpts[9] + +cgrates.org;RANK1;@every 1s;Stats1&Stats2&Stats3&Stats4;;*asc;*acc&*pdd;false;; +cgrates.org;RANK2;@every 1s;Stats3&Stats4&Stats1&Stats2;;*desc;*acc&*pdd;false;; + +*tenant;DYNAMICLY_RNK_<~*req.Account>;@every 1s;Stats1&Stats2;*acc&*tcc;*asc;*acc&*pdd;true;THID1&THID2;~*opts \ No newline at end of file diff --git a/data/tariffplans/dynamic_tps/Rates.csv b/data/tariffplans/dynamic_tps/Rates.csv new file mode 100644 index 000000000..8a5500cb0 --- /dev/null +++ b/data/tariffplans/dynamic_tps/Rates.csv @@ -0,0 +1,5 @@ +#Tenant[0];ID[1];FilterIDs[2];Weights[3];MinCost[4];MaxCost[5];MaxCostStrategy[6];RateID[7];RateFilterIDs[8];RateActivationStart[9];RateWeights[10];RateBlocker[11];RateIntervalStart[12];RateFixedFee[13];RateRecurrentFee[14];RateUnit[15];RateIncrement[16];APIOpts[17] + +cgrates.org;RT_SPECIAL_1002;*string:~*req.Account:1002;&10;0;0;*free;RT_ALWAYS;;"* * * * *";&0;false;0s;;0.01;1m;1s; + +*tenant;DYNAMICLY_RNK_<~*req.Account>;@every 1s;Stats1&Stats2;*acc&*tcc;*asc;*acc&*pdd;true;THID1&THID2;~*opts \ No newline at end of file diff --git a/data/tariffplans/dynamic_tps/Resources.csv b/data/tariffplans/dynamic_tps/Resources.csv new file mode 100644 index 000000000..b9cb4422c --- /dev/null +++ b/data/tariffplans/dynamic_tps/Resources.csv @@ -0,0 +1,5 @@ +#Tenant[0];Id[1];FilterIDs[2];Weights[3];TTL[4];Limit[5];AllocationMessage[6];Blocker[7];Stored[8];ThresholdIDs[9];APIOpts[10] + +cgrates.org;RES_ACNT_1001;FLTR_ACCOUNT_1001;&10;1h;1;;false;false;; + +*tenant;DYNAMICLY_RES_<~*req.Account>;*string:~*req.Account:<~*req.Account>;*string:~*req.Account:<~*req.Account>&30;5s;5;alloc_msg;true;true;THID1&THID2;~*opts \ No newline at end of file diff --git a/data/tariffplans/dynamic_tps/Routes.csv b/data/tariffplans/dynamic_tps/Routes.csv new file mode 100644 index 000000000..1279457ac --- /dev/null +++ b/data/tariffplans/dynamic_tps/Routes.csv @@ -0,0 +1,5 @@ +#Tenant[0];ID[1];FilterIDs[2];Weights[3];Blockers[4];Sorting[5];SortingParameters[6];RouteID[7];RouteFilterIDs[8];RouteAccountIDs[9];RouteRateProfileIDs[10];RouteResourceIDs[11];RouteStatIDs[12];RouteWeights[13];RouteBlockers[14];RouteParameters[15];APIOpts[16] + +cgrates.org;ROUTE_ACNT_1001;FLTR_ACCOUNT_1001;&10;;*weight;;route1;;;;;;&20;;; + +*tenant;DYNAMICLY_RT_<~*req.Account>;*string:~*req.Account:1002&*string:~*req.Account:1003;*string:~*req.Account:1002&10;*string:~*req.Account:1002&true;*weight;*dcc;route1;*string:~*req.Account:1002&*string:~*req.Account:1003;1002&1003;RTP1&RTP2;RSC1&RSC2;STAT1&STAT2;*string:~*req.Account:1002&10;*string:~*req.Account:1002&true;rtParam1;~*opts \ No newline at end of file diff --git a/data/tariffplans/dynamic_tps/Stats.csv b/data/tariffplans/dynamic_tps/Stats.csv new file mode 100644 index 000000000..c36a56689 --- /dev/null +++ b/data/tariffplans/dynamic_tps/Stats.csv @@ -0,0 +1,5 @@ +#Tenant[0];Id[1];FilterIDs[2];Weights[3];Blockers[4];QueueLength[5];TTL[6];MinItems[7];Stored[8];ThresholdIDs[9];MetricIDs[10];MetricFilterIDs[11];MetricBlockers[12];APIOpts[13] + +cgrates.org;Stat_1;FLTR_STAT_1;&30;&true;100;10s;0;false;*none;*acd&*tcd&*asr;;; + +*tenant;DYNAMICLY_STAT_<~*req.Account>;*string:~*req.Account:1002;*string:~*req.Account:1002&30;*string:~*req.Account:1002&true;100;-1;0;false;*none;*tcc&*tcd;*string:~*req.Account:1002;*string:~*req.Account:1002&true;~*opts \ No newline at end of file diff --git a/data/tariffplans/dynamic_tps/Thresholds.csv b/data/tariffplans/dynamic_tps/Thresholds.csv new file mode 100644 index 000000000..4e9f29808 --- /dev/null +++ b/data/tariffplans/dynamic_tps/Thresholds.csv @@ -0,0 +1,5 @@ +#Tenant[0];Id[1];FilterIDs[2];Weight[3];MaxHits[4];MinHits[5];MinSleep[6];Blocker[7];ActionProfileIDs[8];Async[9];EeIDs[10];APIOpts[11] + +cgrates.org;THD_ACNT_1001;FLTR_ACCOUNT_1001;&10;-1;0;0;false;TOPUP_MONETARY_10;false;; + +*tenant;DYNAMICLY_THD_<~*req.Account>;*string:~*req.Account:1002;*string:~*req.Account:1002&10;1;1;1s;false;ACT_LOG_WARNING;true;EEID1&EEID2;~*opts \ No newline at end of file diff --git a/data/tariffplans/dynamic_tps/Trends.csv b/data/tariffplans/dynamic_tps/Trends.csv new file mode 100644 index 000000000..4f0c23fe7 --- /dev/null +++ b/data/tariffplans/dynamic_tps/Trends.csv @@ -0,0 +1,6 @@ +#Tenant[0];Id[1];Schedule[2];StatID[3];Metrics[4];TTL[5];QueueLength[6];MinItems[7];CorrelationType[8];Tolerance[9];Stored[10];ThresholdIDs[11];APIOpts[12] + +cgrates.org;TREND_1;@every 1s;Stats1_1;*acc;-1;-1;1;*last;1;false;*none +cgrates.org;TREND_2;@every 1s;Stats1_2;*tcc;-1;-1;1;*last;1;false;*none + +*tenant;DYNAMICLY_TRND_<~*req.Account>;@every 1s;Stats1_1;*acc&*tcc;-1;-1;1;*last;1;true;THID1&THID2;~*opts \ No newline at end of file diff --git a/engine/thresholds.go b/engine/thresholds.go index 3a57ca9e5..635253979 100644 --- a/engine/thresholds.go +++ b/engine/thresholds.go @@ -80,6 +80,10 @@ func (tp *ThresholdProfile) Clone() *ThresholdProfile { if tp.Weights != nil { clone.Weights = tp.Weights.Clone() } + if tp.EeIDs != nil { + clone.EeIDs = make([]string, len(tp.EeIDs)) + copy(clone.EeIDs, tp.EeIDs) + } return clone } diff --git a/engine/thresholds_test.go b/engine/thresholds_test.go index 5c57c6067..93e0d8b0e 100644 --- a/engine/thresholds_test.go +++ b/engine/thresholds_test.go @@ -3382,8 +3382,8 @@ func TestThresholdProfileClone(t *testing.T) { if cloned.lkID != "" { t.Errorf("lkID should not be cloned, got: %s", cloned.lkID) } - if len(cloned.EeIDs) != 0 { - t.Errorf("EeIDs should not be cloned, got: %v", cloned.EeIDs) + if !reflect.DeepEqual(cloned.EeIDs, orig.EeIDs) { + t.Errorf("EeIDs mismatch: got %v, want %v", cloned.EeIDs, orig.EeIDs) } var nilTP *ThresholdProfile diff --git a/general_tests/dynamic_thresholds_it_test.go b/general_tests/dynamic_thresholds_it_test.go index 4da3d9712..d64f2fa27 100644 --- a/general_tests/dynamic_thresholds_it_test.go +++ b/general_tests/dynamic_thresholds_it_test.go @@ -40,6 +40,24 @@ func TestDynThdIT(t *testing.T) { case utils.MetaInternal: dbCfg = engine.InternalDBCfg case utils.MetaMySQL: + dbCfg = engine.DBCfg{ + StorDB: &engine.DBParams{ + Type: utils.StringPointer(utils.MetaMySQL), + Port: utils.IntPointer(3306), + Host: utils.StringPointer("127.0.0.1"), + Name: utils.StringPointer("cgrates"), + User: utils.StringPointer("cgrates"), + Password: utils.StringPointer("CGRateS.org"), + }, + DataDB: &engine.DBParams{ + Type: utils.StringPointer(utils.MetaRedis), + Port: utils.IntPointer(6379), + Host: utils.StringPointer("127.0.0.1"), + Name: utils.StringPointer("10"), + User: utils.StringPointer("cgrates"), + Password: utils.StringPointer(""), + }, + } case utils.MetaMongo: dbCfg = engine.MongoDBCfg case utils.MetaPostgres: @@ -264,6 +282,7 @@ func TestDynThdIT(t *testing.T) { utils.MetaRoutes: {"someID": {}}, utils.MetaRates: {"someID": {}}, utils.MetaIPs: {"someID": {}}, + utils.MetaActions: {"someID": {}}, }, Actions: []*utils.APAction{ { @@ -274,7 +293,7 @@ func TestDynThdIT(t *testing.T) { ID: "CreateDynamicThreshold1002", FilterIDs: []string{"*string:~*req.Account:1002"}, Opts: map[string]any{ - "*template": "*tenant;DYNAMICLY_THD_<~*req.Account>;*string:~*req.Account:1002;*string:~*req.Account:1002&10;1;1;1s;false;ACT_LOG_WARNING;true;~*opts", + "*template": "*tenant;DYNAMICLY_THD_<~*req.Account>;*string:~*req.Account:1002;*string:~*req.Account:1002&10;1;1;1s;false;ACT_LOG_WARNING;true;EEID1&EEID2;~*opts", }, Weights: utils.DynamicWeights{ { @@ -286,7 +305,7 @@ func TestDynThdIT(t *testing.T) { ID: "CreateDynamicThreshold1002NotFoundFilter", FilterIDs: []string{"*string:~*req.Account:1003"}, Opts: map[string]any{ - "*template": "*tenant;DYNAMICLY_THD_2_<~*req.Account>;*string:~*req.Account:1002;*string:~*req.Account:1002&10;1;1;1s;false;ACT_LOG_WARNING;true;~*opts", + "*template": "*tenant;DYNAMICLY_THD_2_<~*req.Account>;*string:~*req.Account:1002;*string:~*req.Account:1002&10;1;1;1s;false;ACT_LOG_WARNING;true;EEID1&EEID2;~*opts", }, Weights: utils.DynamicWeights{ { @@ -297,7 +316,7 @@ func TestDynThdIT(t *testing.T) { { ID: "CreateDynamicThreshold1002Blocker", Opts: map[string]any{ - "*template": "*tenant;DYNAMICLY_THD_3_<~*req.Account>;*string:~*req.Account:1002;*string:~*req.Account:1002&10;1;1;1s;false;ACT_LOG_WARNING;true;~*opts", + "*template": "*tenant;DYNAMICLY_THD_3_<~*req.Account>;*string:~*req.Account:1002;*string:~*req.Account:1002&10;1;1;1s;false;ACT_LOG_WARNING;true;EEID1&EEID2;~*opts", }, Weights: utils.DynamicWeights{ { @@ -313,7 +332,7 @@ func TestDynThdIT(t *testing.T) { { ID: "CreateDynamicThreshold1002Blocked", Opts: map[string]any{ - "*template": "*tenant;DYNAMICLY_THD_4_<~*req.Account>;*string:~*req.Account:1002;*string:~*req.Account:1002&10;1;1;1s;false;ACT_LOG_WARNING;true;~*opts", + "*template": "*tenant;DYNAMICLY_THD_4_<~*req.Account>;*string:~*req.Account:1002;*string:~*req.Account:1002&10;1;1;1s;false;ACT_LOG_WARNING;true;EEID1&EEID2;~*opts", }, Weights: utils.DynamicWeights{ { @@ -368,7 +387,7 @@ func TestDynThdIT(t *testing.T) { }, }, { - ID: "CreateDynamicStat10022Blocked", + ID: "CreateDynamicStat1002Blocked", Opts: map[string]any{ "*template": "*tenant;DYNAMICLY_STAT_4_<~*req.Account>;*string:~*req.Account:1002;*string:~*req.Account:1002&30;*string:~*req.Account:1002&true;100;-1;0;false;*none;*tcc&*tcd;*string:~*req.Account:1002;*string:~*req.Account:1002&true;~*opts", }, @@ -397,8 +416,8 @@ func TestDynThdIT(t *testing.T) { }, }, { - ID: "CreateDynamicAttribute1002", - FilterIDs: []string{"*string:~*req.Account:1003NotFoundFilter"}, + ID: "CreateDynamicAttribute1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, Opts: map[string]any{ "*template": "*tenant;DYNAMICLY_ATTR_2_<~*req.Account>;*string:~*req.Account:<~*req.Account>;*string:~*req.Account:<~*req.Account>&30;*string:~*req.Account:<~*req.Account>&true;*string:~*req.Account:<~*req.Account>;*string:~*req.Account:<~*req.Account>&true;*req.Subject;*constant;SUPPLIER1;~*opts", }, @@ -409,7 +428,7 @@ func TestDynThdIT(t *testing.T) { }, }, { - ID: "CreateDynamicAttribute1002Blockers", + ID: "CreateDynamicAttribute1002Blocker", Opts: map[string]any{ "*template": "*tenant;DYNAMICLY_ATTR_3_<~*req.Account>;*string:~*req.Account:<~*req.Account>;*string:~*req.Account:<~*req.Account>&30;*string:~*req.Account:<~*req.Account>&true;*string:~*req.Account:<~*req.Account>;*string:~*req.Account:<~*req.Account>&true;*req.Subject;*constant;SUPPLIER1;~*opts", }, @@ -836,6 +855,63 @@ func TestDynThdIT(t *testing.T) { }, }, }, + { + ID: "Dynamic_Action_ID", + Type: utils.MetaDynamicAction, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicAction1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + "*template": "*tenant;DYNAMICLY_Action_<~*req.Account>;*string:~*req.Account:1002&*string:~*req.Account:1003;*string:~*req.Account:1002&10;*string:~*req.Account:1002&true;*asap;*accounts;1001&1002;TOPUP;*string:~*req.Account:1002&*string:~*req.Account:1003;10s;*addBalance;~*opts;*string:~*req.Account:1002&20;*string:~*req.Account:1002&true;ADDBALUNITS;*string:~*req.Account:1002&*string:~*req.Account:1003;*balancePath:*balance.TestBalance.Units&*balanceValue:10;*string:~*req.Account:1002&20;*string:~*req.Account:1002&true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicAction1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + "*template": "*tenant;DYNAMICLY_Action_2_<~*req.Account>;*string:~*req.Account:1002&*string:~*req.Account:1003;*string:~*req.Account:1002&10;*string:~*req.Account:1002&true;*asap;*accounts;1001&1002;TOPUP;*string:~*req.Account:1002&*string:~*req.Account:1003;10s;*addBalance;~*opts;*string:~*req.Account:1002&20;*string:~*req.Account:1002&true;ADDBALUNITS;*string:~*req.Account:1002&*string:~*req.Account:1003;*balancePath:*balance.TestBalance.Units&*balanceValue:10;*string:~*req.Account:1002&20;*string:~*req.Account:1002&true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicAction1002Blocker", + Opts: map[string]any{ + "*template": "*tenant;DYNAMICLY_Action_3_<~*req.Account>;*string:~*req.Account:1002&*string:~*req.Account:1003;*string:~*req.Account:1002&10;*string:~*req.Account:1002&true;*asap;*accounts;1001&1002;TOPUP;*string:~*req.Account:1002&*string:~*req.Account:1003;10s;*addBalance;~*opts;*string:~*req.Account:1002&20;*string:~*req.Account:1002&true;ADDBALUNITS;*string:~*req.Account:1002&*string:~*req.Account:1003;*balancePath:*balance.TestBalance.Units&*balanceValue:10;*string:~*req.Account:1002&20;*string:~*req.Account:1002&true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicAction1002Blocked", + Opts: map[string]any{ + "*template": "*tenant;DYNAMICLY_Action_4_<~*req.Account>;*string:~*req.Account:1002&*string:~*req.Account:1003;*string:~*req.Account:1002&10;*string:~*req.Account:1002&true;*asap;*accounts;1001&1002;TOPUP;*string:~*req.Account:1002&*string:~*req.Account:1003;10s;*addBalance;~*opts;*string:~*req.Account:1002&20;*string:~*req.Account:1002&true;ADDBALUNITS;*string:~*req.Account:1002&*string:~*req.Account:1003;*balancePath:*balance.TestBalance.Units&*balanceValue:10;*string:~*req.Account:1002&20;*string:~*req.Account:1002&true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, }, }, } @@ -979,6 +1055,7 @@ func TestDynThdIT(t *testing.T) { }, ActionProfileIDs: []string{"ACT_LOG_WARNING"}, Async: true, + EeIDs: []string{"EEID1", "EEID2"}, }, { Tenant: utils.CGRateSorg, @@ -996,6 +1073,7 @@ func TestDynThdIT(t *testing.T) { }, ActionProfileIDs: []string{"ACT_LOG_WARNING"}, Async: true, + EeIDs: []string{"EEID1", "EEID2"}, }, } if !reflect.DeepEqual(thrsholds, exp) { @@ -1212,7 +1290,7 @@ func TestDynThdIT(t *testing.T) { }) exp := []*utils.ResourceProfile{ { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_RES_3_1002", FilterIDs: []string{"*string:~*req.Account:1002"}, UsageTTL: 5 * time.Second, @@ -1229,7 +1307,7 @@ func TestDynThdIT(t *testing.T) { ThresholdIDs: []string{"THID1", "THID2"}, }, { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_RES_1002", FilterIDs: []string{"*string:~*req.Account:1002"}, UsageTTL: 5 * time.Second, @@ -1268,7 +1346,7 @@ func TestDynThdIT(t *testing.T) { }) exp := []*utils.TrendProfile{ { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_TRND_3_1002", Schedule: "@every 1s", StatID: "Stats1_1", @@ -1283,7 +1361,7 @@ func TestDynThdIT(t *testing.T) { }, { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_TRND_1002", Schedule: "@every 1s", StatID: "Stats1_1", @@ -1319,7 +1397,7 @@ func TestDynThdIT(t *testing.T) { }) exp := []*utils.RankingProfile{ { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_RNK_3_1002", Schedule: "@every 1s", StatIDs: []string{"Stats1", "Stats2"}, @@ -1331,7 +1409,7 @@ func TestDynThdIT(t *testing.T) { }, { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_RNK_1002", Schedule: "@every 1s", StatIDs: []string{"Stats1", "Stats2"}, @@ -1364,7 +1442,7 @@ func TestDynThdIT(t *testing.T) { }) exp := []*engine.Filter{ { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_FLTR_3_1002", Rules: []*engine.FilterRule{ { @@ -1376,7 +1454,7 @@ func TestDynThdIT(t *testing.T) { }, { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_FLTR_1002", Rules: []*engine.FilterRule{ { @@ -1409,7 +1487,7 @@ func TestDynThdIT(t *testing.T) { }) exp := []*utils.RouteProfile{ { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_RT_3_1002", FilterIDs: []string{ "*string:~*req.Account:1002", @@ -1478,7 +1556,7 @@ func TestDynThdIT(t *testing.T) { }, { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_RT_1002", FilterIDs: []string{ "*string:~*req.Account:1002", @@ -1568,7 +1646,7 @@ func TestDynThdIT(t *testing.T) { }) exp := []*utils.RateProfile{ { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_RATE_3_1002", FilterIDs: []string{ "*string:~*req.Account:1002", @@ -1616,7 +1694,7 @@ func TestDynThdIT(t *testing.T) { }, { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_RATE_1002", FilterIDs: []string{ "*string:~*req.Account:1002", @@ -1685,7 +1763,7 @@ func TestDynThdIT(t *testing.T) { }) exp := []*utils.IPProfile{ { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_IP_3_1002", FilterIDs: []string{ "*string:~*req.Account:1002", @@ -1731,7 +1809,7 @@ func TestDynThdIT(t *testing.T) { }, { - Tenant: "cgrates.org", + Tenant: utils.CGRateSorg, ID: "DYNAMICLY_IP_1002", FilterIDs: []string{ "*string:~*req.Account:1002", @@ -1782,4 +1860,1016 @@ func TestDynThdIT(t *testing.T) { } }) + t.Run("GetDynamicActionProfile", func(t *testing.T) { + var rcv []*utils.ActionProfile + if err := client.Call(context.Background(), utils.AdminSv1GetActionProfiles, + &utils.ArgsItemIDs{ + Tenant: utils.CGRateSorg, + }, &rcv); err != nil { + t.Errorf("AdminSv1GetActionProfiles failed unexpectedly: %v", err) + } + if len(rcv) != 4 { + t.Fatalf("AdminSv1GetActionProfiles len(rcv)=%v, want 4", len(rcv)) + } + sort.Slice(rcv, func(i, j int) bool { + return rcv[i].ID > rcv[j].ID + }) + exp := []*utils.ActionProfile{ + { + Tenant: utils.CGRateSorg, + ID: "DYNAMIC_THRESHOLD_ACTION", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + Targets: map[string]utils.StringSet{ + "*actions": {}, + "*attributes": {}, + "*filters": {}, + "*ips": {}, + "*rankings": {}, + "*rates": {}, + "*resources": {}, + "*routes": {}, + "*stats": {}, + "*thresholds": {}, + "*trends": {}, + }, + Actions: []*utils.APAction{ + { + ID: "Dynamic_Threshold_ID", + Type: utils.MetaDynamicThreshold, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicThreshold1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_THD_\u003c~*req.Account\u003e;*string:~*req.Account:1002;*string:~*req.Account:1002\u002610;1;1;1s;false;ACT_LOG_WARNING;true;EEID1\u0026EEID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicThreshold1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_THD_2_\u003c~*req.Account\u003e;*string:~*req.Account:1002;*string:~*req.Account:1002\u002610;1;1;1s;false;ACT_LOG_WARNING;true;EEID1\u0026EEID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicThreshold1002Blocker", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_THD_3_\u003c~*req.Account\u003e;*string:~*req.Account:1002;*string:~*req.Account:1002\u002610;1;1;1s;false;ACT_LOG_WARNING;true;EEID1\u0026EEID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicThreshold1002Blocked", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_THD_4_\u003c~*req.Account\u003e;*string:~*req.Account:1002;*string:~*req.Account:1002\u002610;1;1;1s;false;ACT_LOG_WARNING;true;EEID1\u0026EEID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, + { + ID: "Dynamic_Stats_ID", + Type: utils.MetaDynamicStats, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicStat1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_STAT_\u003c~*req.Account\u003e;*string:~*req.Account:1002;*string:~*req.Account:1002\u002630;*string:~*req.Account:1002\u0026true;100;-1;0;false;*none;*tcc\u0026*tcd;*string:~*req.Account:1002;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicStat1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_STAT_2_\u003c~*req.Account\u003e;*string:~*req.Account:1002;*string:~*req.Account:1002\u002630;*string:~*req.Account:1002\u0026true;100;-1;0;false;*none;*tcc\u0026*tcd;*string:~*req.Account:1002;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicStat1002Blocker", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_STAT_3_\u003c~*req.Account\u003e;*string:~*req.Account:1002;*string:~*req.Account:1002\u002630;*string:~*req.Account:1002\u0026true;100;-1;0;false;*none;*tcc\u0026*tcd;*string:~*req.Account:1002;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicStat1002Blocked", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_STAT_4_\u003c~*req.Account\u003e;*string:~*req.Account:1002;*string:~*req.Account:1002\u002630;*string:~*req.Account:1002\u0026true;100;-1;0;false;*none;*tcc\u0026*tcd;*string:~*req.Account:1002;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, + { + ID: "Dynamic_Attribute_ID", + Type: utils.MetaDynamicAttribute, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicAttribute1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_ATTR_\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u002630;*string:~*req.Account:\u003c~*req.Account\u003e\u0026true;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u0026true;*req.Subject;*constant;SUPPLIER1;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicAttribute1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_ATTR_2_\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u002630;*string:~*req.Account:\u003c~*req.Account\u003e\u0026true;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u0026true;*req.Subject;*constant;SUPPLIER1;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicAttribute1002Blocker", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_ATTR_3_\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u002630;*string:~*req.Account:\u003c~*req.Account\u003e\u0026true;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u0026true;*req.Subject;*constant;SUPPLIER1;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicAttribute1002Blocked", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_ATTR_4_\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u002630;*string:~*req.Account:\u003c~*req.Account\u003e\u0026true;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u0026true;*req.Subject;*constant;SUPPLIER1;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, + { + ID: "Dynamic_Resource_ID", + Type: utils.MetaDynamicResource, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicResource1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RES_\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u002630;5s;5;alloc_msg;true;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicResource1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RES_2_\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u002630;5s;5;alloc_msg;true;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicResource1002Blocker", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RES_3_\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u002630;5s;5;alloc_msg;true;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicResource1002Blocked", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RES_4_\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e;*string:~*req.Account:\u003c~*req.Account\u003e\u002630;5s;5;alloc_msg;true;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, + { + ID: "Dynamic_Trend_ID", + Type: utils.MetaDynamicTrend, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicTrend1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_TRND_\u003c~*req.Account\u003e;@every 1s;Stats1_1;*acc\u0026*tcc;-1;-1;1;*last;1;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicTrend1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_TRND_2_\u003c~*req.Account\u003e;@every 1s;Stats1_1;*acc\u0026*tcc;-1;-1;1;*last;1;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicTrend1002Blocker", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_TRND_3_\u003c~*req.Account\u003e;@every 1s;Stats1_1;*acc\u0026*tcc;-1;-1;1;*last;1;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicTrend1002Blocked", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_TRND_4_\u003c~*req.Account\u003e;@every 1s;Stats1_1;*acc\u0026*tcc;-1;-1;1;*last;1;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, + { + ID: "Dynamic_Ranking_ID", + Type: utils.MetaDynamicRanking, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicRanking1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RNK_\u003c~*req.Account\u003e;@every 1s;Stats1\u0026Stats2;*acc\u0026*tcc;*asc;*acc\u0026*pdd;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicRanking1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RNK_2_\u003c~*req.Account\u003e;@every 1s;Stats1\u0026Stats2;*acc\u0026*tcc;*asc;*acc\u0026*pdd;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicRanking1002Blocker", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RNK_3_\u003c~*req.Account\u003e;@every 1s;Stats1\u0026Stats2;*acc\u0026*tcc;*asc;*acc\u0026*pdd;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicRanking1002Blocked", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RNK_4_\u003c~*req.Account\u003e;@every 1s;Stats1\u0026Stats2;*acc\u0026*tcc;*asc;*acc\u0026*pdd;true;THID1\u0026THID2;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, + { + ID: "Dynamic_Filter_ID", + Type: utils.MetaDynamicFilter, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicFilter1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_FLTR_\u003c~*req.Account\u003e;*string;~*req.Account;1003\u00261002;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicFilter1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_FLTR_2_\u003c~*req.Account\u003e;*string;~*req.Account;1003\u00261002;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicFilter1002Blocker", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_FLTR_3_\u003c~*req.Account\u003e;*string;~*req.Account;1003\u00261002;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicFilter1002Blocked", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_FLTR_4_\u003c~*req.Account\u003e;*string;~*req.Account;1003\u00261002;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, + { + ID: "Dynamic_Route_ID", + Type: utils.MetaDynamicRoute, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicRoute1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RT_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;*weight;*dcc;route1;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;1002\u00261003;RTP1\u0026RTP2;RSC1\u0026RSC2;STAT1\u0026STAT2;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;rtParam1;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicRoute1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RT_2_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;*weight;*dcc;route1;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;1002\u00261003;RTP1\u0026RTP2;RSC1\u0026RSC2;STAT1\u0026STAT2;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;rtParam1;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicRoute1002Blocker", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RT_3_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;*weight;*dcc;route1;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;1002\u00261003;RTP1\u0026RTP2;RSC1\u0026RSC2;STAT1\u0026STAT2;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;rtParam1;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicRoute1002Blocked", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RT_4_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;*weight;*dcc;route1;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;1002\u00261003;RTP1\u0026RTP2;RSC1\u0026RSC2;STAT1\u0026STAT2;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;rtParam1;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, + { + ID: "Dynamic_Rate_ID", + Type: utils.MetaDynamicRate, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicRate1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RATE_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;5;10;*free;RT_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;* * * * *;*string:~*req.Account:1002\u002620;true;0s;5;0.01;1m;1s;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicRate1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RATE_2_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;5;10;*free;RT_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;* * * * *;*string:~*req.Account:1002\u002620;true;0s;5;0.01;1m;1s;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicRate1002Blocker", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RATE_3_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;5;10;*free;RT_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;* * * * *;*string:~*req.Account:1002\u002620;true;0s;5;0.01;1m;1s;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicRate1002Blocked", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_RATE_4_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;5;10;*free;RT_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;* * * * *;*string:~*req.Account:1002\u002620;true;0s;5;0.01;1m;1s;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, + { + ID: "Dynamic_IP_ID", + Type: utils.MetaDynamicIP, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicIP1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_IP_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;1s;true;Pool1;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*ipv4;172.16.1.1/24;*ascending;alloc_success;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicIP1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_IP_2_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;1s;true;Pool1;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*ipv4;172.16.1.1/24;*ascending;alloc_success;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicIP1002Blocker", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_IP_3_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;1s;true;Pool1;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*ipv4;172.16.1.1/24;*ascending;alloc_success;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicIP1002Blocked", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_IP_4_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;1s;true;Pool1;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*ipv4;172.16.1.1/24;*ascending;alloc_success;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, + { + ID: "Dynamic_Action_ID", + Type: utils.MetaDynamicAction, + Diktats: []*utils.APDiktat{ + { + ID: "CreateDynamicAction1002", + FilterIDs: []string{"*string:~*req.Account:1002"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_Action_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;*asap;*accounts;1001\u00261002;TOPUP;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;10s;*addBalance;~*opts;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;ADDBALUNITS;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*balancePath:*balance.TestBalance.Units\u0026*balanceValue:10;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 50, + }, + }, + }, + { + ID: "CreateDynamicAction1002NotFoundFilter", + FilterIDs: []string{"*string:~*req.Account:1003"}, + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_Action_2_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;*asap;*accounts;1001\u00261002;TOPUP;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;10s;*addBalance;~*opts;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;ADDBALUNITS;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*balancePath:*balance.TestBalance.Units\u0026*balanceValue:10;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 90, + }, + }, + }, + { + ID: "CreateDynamicAction1002Blocker", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_Action_3_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;*asap;*accounts;1001\u00261002;TOPUP;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;10s;*addBalance;~*opts;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;ADDBALUNITS;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*balancePath:*balance.TestBalance.Units\u0026*balanceValue:10;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + Blocker: true, + }, + }, + }, + { + ID: "CreateDynamicAction1002Blocked", + Opts: map[string]any{ + utils.MetaTemplate: "*tenant;DYNAMICLY_Action_4_\u003c~*req.Account\u003e;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*string:~*req.Account:1002\u002610;*string:~*req.Account:1002\u0026true;*asap;*accounts;1001\u00261002;TOPUP;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;10s;*addBalance;~*opts;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;ADDBALUNITS;*string:~*req.Account:1002\u0026*string:~*req.Account:1003;*balancePath:*balance.TestBalance.Units\u0026*balanceValue:10;*string:~*req.Account:1002\u002620;*string:~*req.Account:1002\u0026true;~*opts", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + }, + }, + }, + }, + { + Tenant: utils.CGRateSorg, + ID: "DYNAMICLY_Action_3_1002", + FilterIDs: []string{ + "*string:~*req.Account:1002", + "*string:~*req.Account:1003", + }, + Weights: utils.DynamicWeights{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Weight: 10, + }, + }, + Blockers: utils.DynamicBlockers{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Blocker: true, + }, + }, + Schedule: utils.MetaASAP, + Targets: map[string]utils.StringSet{ + utils.MetaAccounts: { + "1001": struct{}{}, + "1002": struct{}{}, + }, + }, + Actions: []*utils.APAction{ + { + ID: "TOPUP", + FilterIDs: []string{ + "*string:~*req.Account:1002", + "*string:~*req.Account:1003", + }, + TTL: 10 * time.Second, + Type: utils.MetaAddBalance, + Opts: map[string]any{ + "{\"*acntProfileIDs\"": "\"1002\",\"*actProfileIDs\":[\"DYNAMIC_THRESHOLD_ACTION\"],\"*usage\":5000000000}", + }, + Weights: utils.DynamicWeights{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Blocker: true, + }, + }, + Diktats: []*utils.APDiktat{ + { + ID: "ADDBALUNITS", + FilterIDs: []string{ + "*string:~*req.Account:1002", + "*string:~*req.Account:1003", + }, + Opts: map[string]any{ + "*balancePath": "*balance.TestBalance.Units", + "*balanceValue": "10", + }, + Weights: utils.DynamicWeights{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Blocker: true, + }, + }, + }, + }, + }, + }, + }, + { + + Tenant: utils.CGRateSorg, + ID: "DYNAMICLY_Action_1002", + FilterIDs: []string{ + "*string:~*req.Account:1002", + "*string:~*req.Account:1003", + }, + Weights: utils.DynamicWeights{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Weight: 10, + }, + }, + Blockers: utils.DynamicBlockers{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Blocker: true, + }, + }, + Schedule: utils.MetaASAP, + Targets: map[string]utils.StringSet{ + utils.MetaAccounts: { + "1001": struct{}{}, + "1002": struct{}{}, + }, + }, + Actions: []*utils.APAction{ + { + ID: "TOPUP", + FilterIDs: []string{ + "*string:~*req.Account:1002", + "*string:~*req.Account:1003", + }, + TTL: 10 * time.Second, + Type: utils.MetaAddBalance, + Opts: map[string]any{ + "{\"*acntProfileIDs\"": "\"1002\",\"*actProfileIDs\":[\"DYNAMIC_THRESHOLD_ACTION\"],\"*usage\":5000000000}", + }, + Weights: utils.DynamicWeights{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Blocker: true, + }, + }, + Diktats: []*utils.APDiktat{ + { + ID: "ADDBALUNITS", + FilterIDs: []string{ + "*string:~*req.Account:1002", + "*string:~*req.Account:1003", + }, + Opts: map[string]any{ + "*balancePath": "*balance.TestBalance.Units", + "*balanceValue": "10", + }, + Weights: utils.DynamicWeights{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Weight: 20, + }, + }, + Blockers: utils.DynamicBlockers{ + { + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Blocker: true, + }, + }, + }, + }, + }, + }, + }, + { + + Tenant: utils.CGRateSorg, + ID: "1002", + Schedule: utils.MetaASAP, + Targets: map[string]utils.StringSet{ + utils.MetaAccounts: {}, + }, + Actions: []*utils.APAction{ + { + ID: "SET_NEW_BAL", + TTL: 0, + Type: utils.MetaSetBalance, + Diktats: []*utils.APDiktat{ + { + ID: "SetVoiceID", + FilterIDs: []string{ + "*string:~*req.Account:1002", + }, + Opts: map[string]any{ + "*balancePath": "*balance.VOICE.ID", + "*balanceValue": "testBalanceIDMonetary", + }, + Weights: utils.DynamicWeights{ + { + Weight: 14, + }, + }, + }, + { + ID: "SetMonetaryType", + Opts: map[string]any{ + "*balancePath": "*balance.MONETARY.Type", + "*balanceValue": "*concrete", + }, + Weights: utils.DynamicWeights{ + { + Weight: 13, + }, + }, + }, + { + ID: "SetMonetaryUnits", + Opts: map[string]any{ + "*balancePath": "*balance.MONETARY.Units", + "*balanceValue": "1048576", + }, + Weights: utils.DynamicWeights{ + { + Weight: 12, + }, + }, + }, + { + ID: "SetMonetaryWeights", + Opts: map[string]any{ + "*balancePath": "*balance.MONETARY.Weights", + "*balanceValue": "`;2`", + }, + Weights: utils.DynamicWeights{ + { + Weight: 11, + }, + }, + }, + { + ID: "SetMonetaryCostIncrements", + Opts: map[string]any{ + "*balancePath": "*balance.MONETARY.CostIncrements", + "*balanceValue": "`*string:~*req.ToR:*data;1024;0;0.01`", + }, + Weights: utils.DynamicWeights{ + { + Weight: 9, + }, + }, + Blockers: utils.DynamicBlockers{{Blocker: true}}, + }, + { + ID: "SetVoiceIDNotFoundFilter", + FilterIDs: []string{ + "*string:~*req.Account:1003", + }, + Opts: map[string]any{ + "*balancePath": "*balance.VOICE.ID", + "*balanceValue": "testBalanceIDMonetaryNOTFOUND", + }, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }, + }, + }, + { + ID: "SetVoiceIDBlocked", + Opts: map[string]any{ + "*balancePath": "*balance.VOICE.ID", + "*balanceValue": "testBalanceIDMonetaryBLOCKED", + }, + Weights: utils.DynamicWeights{ + { + Weight: 8, + }, + }, + }, + }, + }, + { + ID: "SET_ADD_BAL", + TTL: 0, + Type: utils.MetaAddBalance, + Diktats: []*utils.APDiktat{ + { + ID: "AddVoiceID", + Opts: map[string]any{ + "*balancePath": "*balance.VOICE.ID", + "*balanceValue": "testBalanceID", + }, + }, + { + ID: "AddVoiceType", + Opts: map[string]any{ + "*balancePath": "*balance.VOICE.Type", + "*balanceValue": "*abstract", + }, + }, + { + ID: "AddVoiceFilterIDs", + Opts: map[string]any{ + "*balancePath": "*balance.VOICE.FilterIDs", + "*balanceValue": "`*string:~*req.ToR:*voice`", + }, + }, + { + ID: "AddVoiceUnits", + Opts: map[string]any{ + "*balancePath": "*balance.VOICE.Units", + "*balanceValue": "3600000000000", + }, + }, + { + ID: "AddVoiceWeights", + Opts: map[string]any{ + "*balancePath": "*balance.VOICE.Weights", + "*balanceValue": "`;2`", + }, + }, + { + ID: "AddVoiceCostIncrements", + Opts: map[string]any{ + "*balancePath": "*balance.VOICE.CostIncrements", + "*balanceValue": "`*string:~*req.ToR:*voice;1000000000;0;0.01`", + }, + }, + }, + }, + }, + }, + } + + if !reflect.DeepEqual(exp, rcv) { + t.Errorf("Expected <%v>\nReceived <%v>", utils.ToJSON(exp), utils.ToJSON(rcv)) + } + }) + } diff --git a/utils/consts.go b/utils/consts.go index 630fd9c87..415046824 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -1157,6 +1157,7 @@ const ( MetaDynamicRoute = "*dynamicRoute" MetaDynamicRate = "*dynamicRate" MetaDynamicIP = "*dynamicIP" + MetaDynamicAction = "*dynamicAction" // Diktats Opts Fields MetaBalancePath = "*balancePath"