mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Add FilterIDs, Weights & Blockers functionality to dynamic type actions
This commit is contained in:
committed by
Dan Christian Bogos
parent
2ab652ebdd
commit
076dd821a9
@@ -19,7 +19,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
package actions
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -66,6 +68,7 @@ func parseParamStringToMap(paramStr string, targetMap map[string]any) error {
|
||||
type actDynamicThreshold struct {
|
||||
config *config.CGRConfig
|
||||
connMgr *engine.ConnManager
|
||||
fltrS *engine.FilterS
|
||||
aCfg *utils.APAction
|
||||
tnt string
|
||||
cgrEv *utils.CGREvent
|
||||
@@ -91,95 +94,127 @@ func (aL *actDynamicThreshold) execute(ctx *context.Context, data utils.MapStora
|
||||
if len(aL.aCfg.Diktats) == 0 {
|
||||
return fmt.Errorf("No diktats were speified for action <%v>", aL.aCfg.ID)
|
||||
}
|
||||
params := strings.Split(utils.IfaceAsString(aL.aCfg.Diktats[0].Opts[utils.MetaTemplate]),
|
||||
utils.InfieldSep)
|
||||
if len(params) != 11 {
|
||||
return fmt.Errorf("invalid number of parameters <%d> expected 11", len(params))
|
||||
}
|
||||
// parse dynamic parameters
|
||||
for i := range params {
|
||||
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
|
||||
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 _, has := diktat.Opts[utils.MetaTemplate]; !has {
|
||||
continue
|
||||
}
|
||||
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)
|
||||
}
|
||||
// Prepare request arguments based on provided parameters.
|
||||
args := &engine.ThresholdProfileWithAPIOpts{
|
||||
ThresholdProfile: &engine.ThresholdProfile{
|
||||
Tenant: params[0],
|
||||
ID: params[1],
|
||||
},
|
||||
APIOpts: make(map[string]any),
|
||||
}
|
||||
// populate Threshold's FilterIDs
|
||||
if params[2] != utils.EmptyString {
|
||||
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
|
||||
}
|
||||
// populate Threshold's Weight
|
||||
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
|
||||
// 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) != 11 {
|
||||
return fmt.Errorf("invalid number of parameters <%d> expected 11", len(params))
|
||||
}
|
||||
if wghtSplit[0] != utils.EmptyString {
|
||||
args.Weights[0].FilterIDs = []string{wghtSplit[0]}
|
||||
// parse dynamic parameters
|
||||
for i := range params {
|
||||
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if wghtSplit[1] != utils.EmptyString {
|
||||
args.Weights[0].Weight, err = strconv.ParseFloat(wghtSplit[1], 64)
|
||||
// Prepare request arguments based on provided parameters.
|
||||
args := &engine.ThresholdProfileWithAPIOpts{
|
||||
ThresholdProfile: &engine.ThresholdProfile{
|
||||
Tenant: params[0],
|
||||
ID: params[1],
|
||||
},
|
||||
APIOpts: make(map[string]any),
|
||||
}
|
||||
// populate Threshold's FilterIDs
|
||||
if params[2] != utils.EmptyString {
|
||||
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
|
||||
}
|
||||
// populate Threshold's Weight
|
||||
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 Threshold's MaxHits
|
||||
if params[4] != utils.EmptyString {
|
||||
args.MaxHits, err = strconv.Atoi(params[4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate Threshold's MaxHits
|
||||
if params[4] != utils.EmptyString {
|
||||
args.MaxHits, err = strconv.Atoi(params[4])
|
||||
if err != nil {
|
||||
// populate Threshold's MinHits
|
||||
if params[5] != utils.EmptyString {
|
||||
args.MinHits, err = strconv.Atoi(params[5])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Threshold's MinSleep
|
||||
if params[6] != utils.EmptyString {
|
||||
args.MinSleep, err = utils.ParseDurationWithNanosecs(params[6])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Threshold's Blocker
|
||||
if params[7] != utils.EmptyString {
|
||||
args.Blocker, err = strconv.ParseBool(params[7])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Threshold's ActionProfileIDs
|
||||
if params[8] != utils.EmptyString {
|
||||
args.ActionProfileIDs = strings.Split(params[8], utils.ANDSep)
|
||||
}
|
||||
// populate Threshold's Async bool
|
||||
if params[9] != utils.EmptyString {
|
||||
args.Async, err = strconv.ParseBool(params[9])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Threshold's APIOpts
|
||||
if params[10] != utils.EmptyString {
|
||||
if err := parseParamStringToMap(params[10], args.APIOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// create the ThresholdProfile based on the populated parameters
|
||||
var rply string
|
||||
if err = aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
|
||||
utils.AdminSv1SetThresholdProfile, args, &rply); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Threshold's MinHits
|
||||
if params[5] != utils.EmptyString {
|
||||
args.MinHits, err = strconv.Atoi(params[5])
|
||||
if err != nil {
|
||||
if blocker, err := engine.BlockerFromDynamics(ctx, diktat.Blockers, aL.fltrS, aL.tnt, data); err != nil {
|
||||
return err
|
||||
} else if blocker {
|
||||
break
|
||||
}
|
||||
}
|
||||
// populate Threshold's MinSleep
|
||||
if params[6] != utils.EmptyString {
|
||||
args.MinSleep, err = utils.ParseDurationWithNanosecs(params[6])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Threshold's Blocker
|
||||
if params[7] != utils.EmptyString {
|
||||
args.Blocker, err = strconv.ParseBool(params[7])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Threshold's ActionProfileIDs
|
||||
if params[8] != utils.EmptyString {
|
||||
args.ActionProfileIDs = strings.Split(params[8], utils.ANDSep)
|
||||
}
|
||||
// populate Threshold's Async bool
|
||||
if params[9] != utils.EmptyString {
|
||||
args.Async, err = strconv.ParseBool(params[9])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Threshold's APIOpts
|
||||
if params[10] != utils.EmptyString {
|
||||
if err := parseParamStringToMap(params[10], args.APIOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// create the ThresholdProfile based on the populated parameters
|
||||
var rply string
|
||||
return aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
|
||||
utils.AdminSv1SetThresholdProfile, args, &rply)
|
||||
return
|
||||
}
|
||||
|
||||
// actDynamicStats processes the `ActionDiktatsOpts` field from the action to construct a StatQueueProfile
|
||||
@@ -205,6 +240,7 @@ func (aL *actDynamicThreshold) execute(ctx *context.Context, data utils.MapStora
|
||||
type actDynamicStats struct {
|
||||
config *config.CGRConfig
|
||||
connMgr *engine.ConnManager
|
||||
fltrS *engine.FilterS
|
||||
aCfg *utils.APAction
|
||||
tnt string
|
||||
cgrEv *utils.CGREvent
|
||||
@@ -230,140 +266,172 @@ func (aL *actDynamicStats) execute(ctx *context.Context, data utils.MapStorage,
|
||||
if len(aL.aCfg.Diktats) == 0 {
|
||||
return fmt.Errorf("No diktats were speified for action <%v>", aL.aCfg.ID)
|
||||
}
|
||||
params := strings.Split(utils.IfaceAsString(aL.aCfg.Diktats[0].Opts[utils.MetaTemplate]),
|
||||
utils.InfieldSep)
|
||||
if len(params) != 14 {
|
||||
return fmt.Errorf("invalid number of parameters <%d> expected 14", len(params))
|
||||
}
|
||||
// parse dynamic parameters
|
||||
for i := range params {
|
||||
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
|
||||
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 _, has := diktat.Opts[utils.MetaTemplate]; !has {
|
||||
continue
|
||||
}
|
||||
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)
|
||||
}
|
||||
// Prepare request arguments based on provided parameters.
|
||||
args := &engine.StatQueueProfileWithAPIOpts{
|
||||
StatQueueProfile: &engine.StatQueueProfile{
|
||||
Tenant: params[0],
|
||||
ID: params[1],
|
||||
},
|
||||
APIOpts: make(map[string]any),
|
||||
}
|
||||
// populate Stat's FilterIDs
|
||||
if params[2] != utils.EmptyString {
|
||||
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
|
||||
}
|
||||
// populate Stat'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
|
||||
// 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) != 14 {
|
||||
return fmt.Errorf("invalid number of parameters <%d> expected 14", len(params))
|
||||
}
|
||||
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 {
|
||||
// parse dynamic parameters
|
||||
for i := range params {
|
||||
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate Stat'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
|
||||
// Prepare request arguments based on provided parameters.
|
||||
args := &engine.StatQueueProfileWithAPIOpts{
|
||||
StatQueueProfile: &engine.StatQueueProfile{
|
||||
Tenant: params[0],
|
||||
ID: params[1],
|
||||
},
|
||||
APIOpts: make(map[string]any),
|
||||
}
|
||||
if blckrSplit[0] != utils.EmptyString {
|
||||
args.Blockers[0].FilterIDs = []string{blckrSplit[0]}
|
||||
// populate Stat's FilterIDs
|
||||
if params[2] != utils.EmptyString {
|
||||
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
|
||||
}
|
||||
if blckrSplit[1] != utils.EmptyString {
|
||||
args.Blockers[0].Blocker, err = strconv.ParseBool(blckrSplit[1])
|
||||
if err != nil {
|
||||
return err
|
||||
// populate Stat'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
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate Stat's QueueLengh
|
||||
if params[5] != utils.EmptyString {
|
||||
args.QueueLength, err = strconv.Atoi(params[5])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Stat's TTL
|
||||
if params[6] != utils.EmptyString {
|
||||
args.TTL, err = utils.ParseDurationWithNanosecs(params[6])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Stat's MinItems
|
||||
if params[7] != utils.EmptyString {
|
||||
args.MinItems, err = strconv.Atoi(params[7])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Stat's Stored
|
||||
if params[8] != utils.EmptyString {
|
||||
args.Stored, err = strconv.ParseBool(params[8])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Stat's ThresholdIDs
|
||||
if params[9] != utils.EmptyString {
|
||||
args.ThresholdIDs = strings.Split(params[9], utils.ANDSep)
|
||||
}
|
||||
// populate Stat's MetricID
|
||||
if params[10] != utils.EmptyString {
|
||||
metrics := strings.Split(params[10], utils.ANDSep)
|
||||
args.Metrics = make([]*engine.MetricWithFilters, len(metrics))
|
||||
for i, strM := range metrics {
|
||||
args.Metrics[i] = &engine.MetricWithFilters{MetricID: strM}
|
||||
}
|
||||
}
|
||||
// populate Stat's metricFliterIDs
|
||||
if params[11] != utils.EmptyString {
|
||||
metricFliters := strings.Split(params[11], utils.ANDSep)
|
||||
for i := range args.Metrics {
|
||||
args.Metrics[i].FilterIDs = metricFliters
|
||||
}
|
||||
}
|
||||
// populate Stat's metricBlockers
|
||||
if params[12] != utils.EmptyString {
|
||||
blckrSplit := strings.Split(params[12], utils.ANDSep)
|
||||
if len(blckrSplit) > 2 {
|
||||
return utils.ErrUnsupportedFormat
|
||||
}
|
||||
for i := range args.Metrics {
|
||||
args.Metrics[i].Blockers = utils.DynamicBlockers{&utils.DynamicBlocker{}}
|
||||
if blckrSplit[0] != utils.EmptyString {
|
||||
args.Metrics[i].Blockers[0].FilterIDs = []string{blckrSplit[0]}
|
||||
if wghtSplit[0] != utils.EmptyString {
|
||||
args.Weights[0].FilterIDs = []string{wghtSplit[0]}
|
||||
}
|
||||
if blckrSplit[1] != utils.EmptyString {
|
||||
args.Metrics[i].Blockers[0].Blocker, err = strconv.ParseBool(blckrSplit[1])
|
||||
if wghtSplit[1] != utils.EmptyString {
|
||||
args.Weights[0].Weight, err = strconv.ParseFloat(wghtSplit[1], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate Stat's APIOpts
|
||||
if params[13] != utils.EmptyString {
|
||||
if err := parseParamStringToMap(params[13], args.APIOpts); err != nil {
|
||||
// populate Stat'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 Stat's QueueLengh
|
||||
if params[5] != utils.EmptyString {
|
||||
args.QueueLength, err = strconv.Atoi(params[5])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Stat's TTL
|
||||
if params[6] != utils.EmptyString {
|
||||
args.TTL, err = utils.ParseDurationWithNanosecs(params[6])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Stat's MinItems
|
||||
if params[7] != utils.EmptyString {
|
||||
args.MinItems, err = strconv.Atoi(params[7])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Stat's Stored
|
||||
if params[8] != utils.EmptyString {
|
||||
args.Stored, err = strconv.ParseBool(params[8])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// populate Stat's ThresholdIDs
|
||||
if params[9] != utils.EmptyString {
|
||||
args.ThresholdIDs = strings.Split(params[9], utils.ANDSep)
|
||||
}
|
||||
// populate Stat's MetricID
|
||||
if params[10] != utils.EmptyString {
|
||||
metrics := strings.Split(params[10], utils.ANDSep)
|
||||
args.Metrics = make([]*engine.MetricWithFilters, len(metrics))
|
||||
for i, strM := range metrics {
|
||||
args.Metrics[i] = &engine.MetricWithFilters{MetricID: strM}
|
||||
}
|
||||
}
|
||||
// populate Stat's metricFliterIDs
|
||||
if params[11] != utils.EmptyString {
|
||||
metricFliters := strings.Split(params[11], utils.ANDSep)
|
||||
for i := range args.Metrics {
|
||||
args.Metrics[i].FilterIDs = metricFliters
|
||||
}
|
||||
}
|
||||
// populate Stat's metricBlockers
|
||||
if params[12] != utils.EmptyString {
|
||||
blckrSplit := strings.Split(params[12], utils.ANDSep)
|
||||
if len(blckrSplit) > 2 {
|
||||
return utils.ErrUnsupportedFormat
|
||||
}
|
||||
for i := range args.Metrics {
|
||||
args.Metrics[i].Blockers = utils.DynamicBlockers{&utils.DynamicBlocker{}}
|
||||
if blckrSplit[0] != utils.EmptyString {
|
||||
args.Metrics[i].Blockers[0].FilterIDs = []string{blckrSplit[0]}
|
||||
}
|
||||
if blckrSplit[1] != utils.EmptyString {
|
||||
args.Metrics[i].Blockers[0].Blocker, err = strconv.ParseBool(blckrSplit[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate Stat's APIOpts
|
||||
if params[13] != utils.EmptyString {
|
||||
if err := parseParamStringToMap(params[13], args.APIOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// create the StatQueueProfile based on the populated parameters
|
||||
var rply string
|
||||
if err = aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
|
||||
utils.AdminSv1SetStatQueueProfile, 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
|
||||
}
|
||||
}
|
||||
|
||||
// create the StatQueueProfile based on the populated parameters
|
||||
var rply string
|
||||
return aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
|
||||
utils.AdminSv1SetStatQueueProfile, args, &rply)
|
||||
return
|
||||
}
|
||||
|
||||
// actDynamicAttribute processes the `ActionDiktatsOpts` field from the action to construct a AttributeProfile
|
||||
@@ -386,6 +454,7 @@ func (aL *actDynamicStats) execute(ctx *context.Context, data utils.MapStorage,
|
||||
type actDynamicAttribute struct {
|
||||
config *config.CGRConfig
|
||||
connMgr *engine.ConnManager
|
||||
fltrS *engine.FilterS
|
||||
aCfg *utils.APAction
|
||||
tnt string
|
||||
cgrEv *utils.CGREvent
|
||||
@@ -411,105 +480,137 @@ func (aL *actDynamicAttribute) execute(ctx *context.Context, data utils.MapStora
|
||||
if len(aL.aCfg.Diktats) == 0 {
|
||||
return fmt.Errorf("No diktats were speified for action <%v>", aL.aCfg.ID)
|
||||
}
|
||||
params := strings.Split(utils.IfaceAsString(aL.aCfg.Diktats[0].Opts[utils.MetaTemplate]),
|
||||
utils.InfieldSep)
|
||||
if len(params) != 11 {
|
||||
return fmt.Errorf("invalid number of parameters <%d> expected 11", len(params))
|
||||
}
|
||||
// parse dynamic parameters
|
||||
for i := range params {
|
||||
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
|
||||
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 _, has := diktat.Opts[utils.MetaTemplate]; !has {
|
||||
continue
|
||||
}
|
||||
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)
|
||||
}
|
||||
// Prepare request arguments based on provided parameters.
|
||||
args := &utils.APIAttributeProfileWithAPIOpts{
|
||||
APIAttributeProfile: &utils.APIAttributeProfile{
|
||||
Tenant: params[0],
|
||||
ID: params[1],
|
||||
},
|
||||
APIOpts: make(map[string]any),
|
||||
}
|
||||
// populate Attribute's FilterIDs
|
||||
if params[2] != utils.EmptyString {
|
||||
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
|
||||
}
|
||||
// populate Attribute'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
|
||||
// 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) != 11 {
|
||||
return fmt.Errorf("invalid number of parameters <%d> expected 11", len(params))
|
||||
}
|
||||
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 {
|
||||
// parse dynamic parameters
|
||||
for i := range params {
|
||||
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate Attribute'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
|
||||
// Prepare request arguments based on provided parameters.
|
||||
args := &utils.APIAttributeProfileWithAPIOpts{
|
||||
APIAttributeProfile: &utils.APIAttributeProfile{
|
||||
Tenant: params[0],
|
||||
ID: params[1],
|
||||
},
|
||||
APIOpts: make(map[string]any),
|
||||
}
|
||||
if blckrSplit[0] != utils.EmptyString {
|
||||
args.Blockers[0].FilterIDs = []string{blckrSplit[0]}
|
||||
// populate Attribute's FilterIDs
|
||||
if params[2] != utils.EmptyString {
|
||||
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
|
||||
}
|
||||
if blckrSplit[1] != utils.EmptyString {
|
||||
args.Blockers[0].Blocker, err = strconv.ParseBool(blckrSplit[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate Attribute's Attributes
|
||||
if params[7] != utils.EmptyString {
|
||||
var attrFltrIDs []string
|
||||
if params[5] != utils.EmptyString {
|
||||
attrFltrIDs = strings.Split(params[5], utils.ANDSep)
|
||||
}
|
||||
var attrFltrBlckrs utils.DynamicBlockers
|
||||
if params[6] != utils.EmptyString {
|
||||
attrFltrBlckrs = utils.DynamicBlockers{&utils.DynamicBlocker{}}
|
||||
blckrSplit := strings.Split(params[6], utils.ANDSep)
|
||||
if len(blckrSplit) > 2 {
|
||||
// populate Attribute'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 blckrSplit[0] != utils.EmptyString {
|
||||
attrFltrBlckrs[0].FilterIDs = []string{blckrSplit[0]}
|
||||
if wghtSplit[0] != utils.EmptyString {
|
||||
args.Weights[0].FilterIDs = []string{wghtSplit[0]}
|
||||
}
|
||||
if blckrSplit[1] != utils.EmptyString {
|
||||
attrFltrBlckrs[0].Blocker, err = strconv.ParseBool(blckrSplit[1])
|
||||
if wghtSplit[1] != utils.EmptyString {
|
||||
args.Weights[0].Weight, err = strconv.ParseFloat(wghtSplit[1], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
args.Attributes = append(args.Attributes, &utils.ExternalAttribute{
|
||||
FilterIDs: attrFltrIDs,
|
||||
Blockers: attrFltrBlckrs,
|
||||
Path: params[7],
|
||||
Type: params[8],
|
||||
Value: params[9],
|
||||
})
|
||||
}
|
||||
// populate Attribute's APIOpts
|
||||
if params[10] != utils.EmptyString {
|
||||
if err := parseParamStringToMap(params[10], args.APIOpts); err != nil {
|
||||
// populate Attribute'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 Attribute's Attributes
|
||||
if params[7] != utils.EmptyString {
|
||||
var attrFltrIDs []string
|
||||
if params[5] != utils.EmptyString {
|
||||
attrFltrIDs = strings.Split(params[5], utils.ANDSep)
|
||||
}
|
||||
var attrFltrBlckrs utils.DynamicBlockers
|
||||
if params[6] != utils.EmptyString {
|
||||
attrFltrBlckrs = utils.DynamicBlockers{&utils.DynamicBlocker{}}
|
||||
blckrSplit := strings.Split(params[6], utils.ANDSep)
|
||||
if len(blckrSplit) > 2 {
|
||||
return utils.ErrUnsupportedFormat
|
||||
}
|
||||
if blckrSplit[0] != utils.EmptyString {
|
||||
attrFltrBlckrs[0].FilterIDs = []string{blckrSplit[0]}
|
||||
}
|
||||
if blckrSplit[1] != utils.EmptyString {
|
||||
attrFltrBlckrs[0].Blocker, err = strconv.ParseBool(blckrSplit[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
args.Attributes = append(args.Attributes, &utils.ExternalAttribute{
|
||||
FilterIDs: attrFltrIDs,
|
||||
Blockers: attrFltrBlckrs,
|
||||
Path: params[7],
|
||||
Type: params[8],
|
||||
Value: params[9],
|
||||
})
|
||||
}
|
||||
// populate Attribute's APIOpts
|
||||
if params[10] != utils.EmptyString {
|
||||
if err := parseParamStringToMap(params[10], args.APIOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// create the AttributeProfile based on the populated parameters
|
||||
var rply string
|
||||
if err = aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
|
||||
utils.AdminSv1SetAttributeProfile, 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
|
||||
}
|
||||
}
|
||||
|
||||
// create the AttributeProfile based on the populated parameters
|
||||
var rply string
|
||||
return aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
|
||||
utils.AdminSv1SetAttributeProfile, args, &rply)
|
||||
return
|
||||
}
|
||||
|
||||
// actDynamicResource processes the `ActionDiktatsOpts` field from the action to construct a ResourceProfile
|
||||
@@ -532,6 +633,7 @@ func (aL *actDynamicAttribute) execute(ctx *context.Context, data utils.MapStora
|
||||
type actDynamicResource struct {
|
||||
config *config.CGRConfig
|
||||
connMgr *engine.ConnManager
|
||||
fltrS *engine.FilterS
|
||||
aCfg *utils.APAction
|
||||
tnt string
|
||||
cgrEv *utils.CGREvent
|
||||
@@ -557,88 +659,120 @@ func (aL *actDynamicResource) execute(ctx *context.Context, data utils.MapStorag
|
||||
if len(aL.aCfg.Diktats) == 0 {
|
||||
return fmt.Errorf("No diktats were speified for action <%v>", aL.aCfg.ID)
|
||||
}
|
||||
params := strings.Split(utils.IfaceAsString(aL.aCfg.Diktats[0].Opts[utils.MetaTemplate]),
|
||||
utils.InfieldSep)
|
||||
if len(params) != 11 {
|
||||
return fmt.Errorf("invalid number of parameters <%d> expected 11", len(params))
|
||||
}
|
||||
// parse dynamic parameters
|
||||
for i := range params {
|
||||
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
|
||||
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 _, has := diktat.Opts[utils.MetaTemplate]; !has {
|
||||
continue
|
||||
}
|
||||
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)
|
||||
}
|
||||
// Prepare request arguments based on provided parameters.
|
||||
args := &utils.ResourceProfileWithAPIOpts{
|
||||
ResourceProfile: &utils.ResourceProfile{
|
||||
Tenant: params[0],
|
||||
ID: params[1],
|
||||
AllocationMessage: params[6],
|
||||
},
|
||||
APIOpts: make(map[string]any),
|
||||
}
|
||||
// populate Resource's FilterIDs
|
||||
if params[2] != utils.EmptyString {
|
||||
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
|
||||
}
|
||||
// populate Resource'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
|
||||
// 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) != 11 {
|
||||
return fmt.Errorf("invalid number of parameters <%d> expected 11", len(params))
|
||||
}
|
||||
if wghtSplit[0] != utils.EmptyString {
|
||||
args.Weights[0].FilterIDs = []string{wghtSplit[0]}
|
||||
// parse dynamic parameters
|
||||
for i := range params {
|
||||
if params[i], err = utils.ParseParamForDataProvider(params[i], data, false); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if wghtSplit[1] != utils.EmptyString {
|
||||
args.Weights[0].Weight, err = strconv.ParseFloat(wghtSplit[1], 64)
|
||||
// Prepare request arguments based on provided parameters.
|
||||
args := &utils.ResourceProfileWithAPIOpts{
|
||||
ResourceProfile: &utils.ResourceProfile{
|
||||
Tenant: params[0],
|
||||
ID: params[1],
|
||||
AllocationMessage: params[6],
|
||||
},
|
||||
APIOpts: make(map[string]any),
|
||||
}
|
||||
// populate Resource's FilterIDs
|
||||
if params[2] != utils.EmptyString {
|
||||
args.FilterIDs = strings.Split(params[2], utils.ANDSep)
|
||||
}
|
||||
// populate Resource'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 Resource's UsageTTL
|
||||
if params[4] != utils.EmptyString {
|
||||
args.UsageTTL, err = utils.ParseDurationWithNanosecs(params[4])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate Resource's UsageTTL
|
||||
if params[4] != utils.EmptyString {
|
||||
args.UsageTTL, err = utils.ParseDurationWithNanosecs(params[4])
|
||||
if err != nil {
|
||||
return err
|
||||
// populate Resource's Limit
|
||||
if params[5] != utils.EmptyString {
|
||||
args.Limit, err = strconv.ParseFloat(params[5], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate Resource's Limit
|
||||
if params[5] != utils.EmptyString {
|
||||
args.Limit, err = strconv.ParseFloat(params[5], 64)
|
||||
if err != nil {
|
||||
return err
|
||||
// populate Resource's Blocker
|
||||
if params[7] != utils.EmptyString {
|
||||
args.Blocker, err = strconv.ParseBool(params[7])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate Resource's Blocker
|
||||
if params[7] != utils.EmptyString {
|
||||
args.Blocker, err = strconv.ParseBool(params[7])
|
||||
if err != nil {
|
||||
return err
|
||||
// populate Resource's Stored
|
||||
if params[8] != utils.EmptyString {
|
||||
args.Stored, err = strconv.ParseBool(params[8])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
// populate Resource's Stored
|
||||
if params[8] != utils.EmptyString {
|
||||
args.Stored, err = strconv.ParseBool(params[8])
|
||||
if err != nil {
|
||||
return err
|
||||
// populate Resource's ThresholdIDs
|
||||
if params[9] != utils.EmptyString {
|
||||
args.ThresholdIDs = strings.Split(params[9], utils.ANDSep)
|
||||
}
|
||||
}
|
||||
// populate Resource's ThresholdIDs
|
||||
if params[9] != utils.EmptyString {
|
||||
args.ThresholdIDs = strings.Split(params[9], utils.ANDSep)
|
||||
}
|
||||
// populate Resource's APIOpts
|
||||
if params[10] != utils.EmptyString {
|
||||
if err := parseParamStringToMap(params[10], args.APIOpts); err != nil {
|
||||
return err
|
||||
// populate Resource's APIOpts
|
||||
if params[10] != utils.EmptyString {
|
||||
if err := parseParamStringToMap(params[10], args.APIOpts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create the ResourceProfile based on the populated parameters
|
||||
var rply string
|
||||
return aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
|
||||
utils.AdminSv1SetResourceProfile, args, &rply)
|
||||
// create the ResourceProfile based on the populated parameters
|
||||
var rply string
|
||||
if err = aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
|
||||
utils.AdminSv1SetResourceProfile, 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
|
||||
}
|
||||
|
||||
@@ -138,13 +138,13 @@ func newActioner(ctx *context.Context, cgrEv *utils.CGREvent, cfg *config.CGRCon
|
||||
case utils.MetaRemBalance:
|
||||
return &actRemBalance{cfg, connMgr, aCfg, tnt}, nil
|
||||
case utils.MetaDynamicThreshold:
|
||||
return &actDynamicThreshold{cfg, connMgr, aCfg, tnt, cgrEv}, nil
|
||||
return &actDynamicThreshold{cfg, connMgr, fltrS, aCfg, tnt, cgrEv}, nil
|
||||
case utils.MetaDynamicStats:
|
||||
return &actDynamicStats{cfg, connMgr, aCfg, tnt, cgrEv}, nil
|
||||
return &actDynamicStats{cfg, connMgr, fltrS, aCfg, tnt, cgrEv}, nil
|
||||
case utils.MetaDynamicAttribute:
|
||||
return &actDynamicAttribute{cfg, connMgr, aCfg, tnt, cgrEv}, nil
|
||||
return &actDynamicAttribute{cfg, connMgr, fltrS, aCfg, tnt, cgrEv}, nil
|
||||
case utils.MetaDynamicResource:
|
||||
return &actDynamicResource{cfg, connMgr, aCfg, tnt, cgrEv}, nil
|
||||
return &actDynamicResource{cfg, connMgr, fltrS, aCfg, tnt, cgrEv}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported action type: <%s>", aCfg.Type)
|
||||
|
||||
|
||||
@@ -209,10 +209,55 @@ func TestDynThdIT(t *testing.T) {
|
||||
Type: utils.MetaDynamicThreshold,
|
||||
Diktats: []*utils.APDiktat{
|
||||
{
|
||||
ID: "CreateDynamicThreshold1001",
|
||||
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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 90,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -221,10 +266,55 @@ func TestDynThdIT(t *testing.T) {
|
||||
Type: utils.MetaDynamicStats,
|
||||
Diktats: []*utils.APDiktat{
|
||||
{
|
||||
ID: "CreateDynamicStat1001",
|
||||
ID: "CreateDynamicStat1002",
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Opts: map[string]any{
|
||||
"*template": "*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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "CreateDynamicStat1002NotFoundFilter",
|
||||
FilterIDs: []string{"*string:~*req.Account:1003"},
|
||||
Opts: map[string]any{
|
||||
"*template": "*tenant;DYNAMICLY_STAT_2_<~*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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 90,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "CreateDynamicStat1002Blocker",
|
||||
Opts: map[string]any{
|
||||
"*template": "*tenant;DYNAMICLY_STAT_3_<~*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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "CreateDynamicStat10022Blocked",
|
||||
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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -233,22 +323,112 @@ func TestDynThdIT(t *testing.T) {
|
||||
Type: utils.MetaDynamicAttribute,
|
||||
Diktats: []*utils.APDiktat{
|
||||
{
|
||||
ID: "CreateDynamicAttribute1001",
|
||||
ID: "CreateDynamicAttribute1002",
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Opts: map[string]any{
|
||||
"*template": "*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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "CreateDynamicAttribute1002",
|
||||
FilterIDs: []string{"*string:~*req.Account:1003NotFoundFilter"},
|
||||
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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 90,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "CreateDynamicAttribute1002Blockers",
|
||||
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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "CreateDynamicAttribute1002Blocked",
|
||||
Opts: map[string]any{
|
||||
"*template": "*tenant;DYNAMICLY_ATTR_4_<~*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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "Dynamic_Attribute_ID",
|
||||
ID: "Dynamic_Resource_ID",
|
||||
Type: utils.MetaDynamicResource,
|
||||
Diktats: []*utils.APDiktat{
|
||||
{
|
||||
ID: "CreateDynamicResource1001",
|
||||
ID: "CreateDynamicResource1002",
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Opts: map[string]any{
|
||||
"*template": "*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",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 50,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "CreateDynamicResource1002NotFoundFilter",
|
||||
FilterIDs: []string{"*string:~*req.Account:1003"},
|
||||
Opts: map[string]any{
|
||||
"*template": "*tenant;DYNAMICLY_RES_2_<~*req.Account>;*string:~*req.Account:<~*req.Account>;*string:~*req.Account:<~*req.Account>&30;5s;5;alloc_msg;true;true;THID1&THID2;~*opts",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 90,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "CreateDynamicResource1002Blocker",
|
||||
Opts: map[string]any{
|
||||
"*template": "*tenant;DYNAMICLY_RES_3_<~*req.Account>;*string:~*req.Account:<~*req.Account>;*string:~*req.Account:<~*req.Account>&30;5s;5;alloc_msg;true;true;THID1&THID2;~*opts",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 20,
|
||||
},
|
||||
},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "CreateDynamicResource1002Blocked",
|
||||
Opts: map[string]any{
|
||||
"*template": "*tenant;DYNAMICLY_RES_4_<~*req.Account>;*string:~*req.Account:<~*req.Account>;*string:~*req.Account:<~*req.Account>&30;5s;5;alloc_msg;true;true;THID1&THID2;~*opts",
|
||||
},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -360,8 +540,8 @@ func TestDynThdIT(t *testing.T) {
|
||||
}, &thrsholds); err != nil {
|
||||
t.Errorf("AdminSv1GetThresholdProfiles failed unexpectedly: %v", err)
|
||||
}
|
||||
if len(thrsholds) != 2 {
|
||||
t.Fatalf("AdminSv1GetThresholdProfiles len(thrsholds)=%v, want 2", len(thrsholds))
|
||||
if len(thrsholds) != 3 {
|
||||
t.Fatalf("AdminSv1GetThresholdProfiles len(thrsholds)=%v, want 3", len(thrsholds))
|
||||
}
|
||||
sort.Slice(thrsholds, func(i, j int) bool {
|
||||
return thrsholds[i].ID > thrsholds[j].ID
|
||||
@@ -379,6 +559,23 @@ func TestDynThdIT(t *testing.T) {
|
||||
ActionProfileIDs: []string{"DYNAMIC_THRESHOLD_ACTION"},
|
||||
Async: true,
|
||||
},
|
||||
{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "DYNAMICLY_THD_3_1002",
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
MaxHits: 1,
|
||||
MinHits: 1,
|
||||
MinSleep: time.Second,
|
||||
Blocker: false,
|
||||
Weights: utils.DynamicWeights{
|
||||
&utils.DynamicWeight{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Weight: 10,
|
||||
},
|
||||
},
|
||||
ActionProfileIDs: []string{"ACT_LOG_WARNING"},
|
||||
Async: true,
|
||||
},
|
||||
{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "DYNAMICLY_THD_1002",
|
||||
@@ -403,60 +600,110 @@ func TestDynThdIT(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("GetDynamicStatQueueProfile", func(t *testing.T) {
|
||||
exp := &engine.StatQueueProfile{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "DYNAMICLY_STAT_1002",
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Weight: 30,
|
||||
exp := []*engine.StatQueueProfile{
|
||||
{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "DYNAMICLY_STAT_3_1002",
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Weight: 30,
|
||||
},
|
||||
},
|
||||
},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blocker: true,
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
QueueLength: 100,
|
||||
TTL: -1,
|
||||
MinItems: 0,
|
||||
Stored: false,
|
||||
ThresholdIDs: []string{utils.MetaNone},
|
||||
Metrics: []*engine.MetricWithFilters{
|
||||
{
|
||||
MetricID: utils.MetaTCC,
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blocker: true,
|
||||
QueueLength: 100,
|
||||
TTL: -1,
|
||||
MinItems: 0,
|
||||
Stored: false,
|
||||
ThresholdIDs: []string{utils.MetaNone},
|
||||
Metrics: []*engine.MetricWithFilters{
|
||||
{
|
||||
MetricID: utils.MetaTCC,
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MetricID: utils.MetaTCD,
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MetricID: utils.MetaTCD,
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blocker: true,
|
||||
},
|
||||
{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "DYNAMICLY_STAT_1002",
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Weight: 30,
|
||||
},
|
||||
},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
QueueLength: 100,
|
||||
TTL: -1,
|
||||
MinItems: 0,
|
||||
Stored: false,
|
||||
ThresholdIDs: []string{utils.MetaNone},
|
||||
Metrics: []*engine.MetricWithFilters{
|
||||
{
|
||||
MetricID: utils.MetaTCC,
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MetricID: utils.MetaTCD,
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var rply *engine.StatQueueProfile
|
||||
if err := client.Call(context.Background(), utils.AdminSv1GetStatQueueProfile, &utils.TenantIDWithAPIOpts{
|
||||
TenantID: &utils.TenantID{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "DYNAMICLY_STAT_1002",
|
||||
},
|
||||
var rply []*engine.StatQueueProfile
|
||||
if err := client.Call(context.Background(), utils.AdminSv1GetStatQueueProfiles, &utils.ArgsItemIDs{
|
||||
Tenant: utils.CGRateSorg,
|
||||
}, &rply); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(exp, rply) {
|
||||
} else if len(rply) != 2 {
|
||||
t.Fatalf("AdminSv1GetStatQueueProfiles len(rply)=%v, want 2", len(rply))
|
||||
}
|
||||
sort.Slice(rply, func(i, j int) bool {
|
||||
return rply[i].ID > rply[j].ID
|
||||
})
|
||||
|
||||
if !reflect.DeepEqual(exp, rply) {
|
||||
t.Errorf("Expected <%v> \n received <%v>", utils.ToJSON(exp), utils.ToJSON(rply))
|
||||
}
|
||||
})
|
||||
@@ -469,11 +716,44 @@ func TestDynThdIT(t *testing.T) {
|
||||
}, &attrs); err != nil {
|
||||
t.Errorf("AdminSv1GetAttributeProfiles failed unexpectedly: %v", err)
|
||||
}
|
||||
if len(attrs) != 1 {
|
||||
t.Fatalf("AdminSv1GetAttributeProfiles len(attrs)=%v, want 1", len(attrs))
|
||||
if len(attrs) != 2 {
|
||||
t.Fatalf("AdminSv1GetAttributeProfiles len(attrs)=%v, want 2", len(attrs))
|
||||
}
|
||||
|
||||
sort.Slice(attrs, func(i, j int) bool {
|
||||
return attrs[i].ID > attrs[j].ID
|
||||
})
|
||||
exp := []*utils.APIAttributeProfile{
|
||||
{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "DYNAMICLY_ATTR_3_1002",
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Weight: 30,
|
||||
},
|
||||
},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
Attributes: []*utils.ExternalAttribute{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blockers: utils.DynamicBlockers{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Blocker: true,
|
||||
},
|
||||
},
|
||||
Path: "*req.Subject",
|
||||
Type: "*constant",
|
||||
Value: "SUPPLIER1",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "DYNAMICLY_ATTR_1002",
|
||||
@@ -520,11 +800,30 @@ func TestDynThdIT(t *testing.T) {
|
||||
}, &rsc); err != nil {
|
||||
t.Errorf("AdminSv1GetResourceProfiles failed unexpectedly: %v", err)
|
||||
}
|
||||
if len(rsc) != 1 {
|
||||
t.Fatalf("AdminSv1GetResourceProfiles len(rsc)=%v, want 1", len(rsc))
|
||||
if len(rsc) != 2 {
|
||||
t.Fatalf("AdminSv1GetResourceProfiles len(rsc)=%v, want 2", len(rsc))
|
||||
}
|
||||
|
||||
sort.Slice(rsc, func(i, j int) bool {
|
||||
return rsc[i].ID > rsc[j].ID
|
||||
})
|
||||
exp := []*utils.ResourceProfile{
|
||||
{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "DYNAMICLY_RES_3_1002",
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
UsageTTL: 5 * time.Second,
|
||||
Limit: 5,
|
||||
AllocationMessage: "alloc_msg",
|
||||
Blocker: true,
|
||||
Stored: true,
|
||||
Weights: utils.DynamicWeights{
|
||||
{
|
||||
FilterIDs: []string{"*string:~*req.Account:1002"},
|
||||
Weight: 30,
|
||||
},
|
||||
},
|
||||
ThresholdIDs: []string{"THID1", "THID2"},
|
||||
},
|
||||
{
|
||||
Tenant: "cgrates.org",
|
||||
ID: "DYNAMICLY_RES_1002",
|
||||
|
||||
Reference in New Issue
Block a user