mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Add action type *dynamic_attribute
This commit is contained in:
committed by
Dan Christian Bogos
parent
2cd8c4fe38
commit
3059b768a9
@@ -363,3 +363,148 @@ func (aL *actDynamicStats) execute(ctx *context.Context, data utils.MapStorage,
|
||||
return aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
|
||||
utils.AdminSv1SetStatQueueProfile, args, &rply)
|
||||
}
|
||||
|
||||
// actDynamicAttribute processes the `ActionValue` field from the action to construct a AttributeProfile
|
||||
//
|
||||
// The ActionValue field format is expected as follows:
|
||||
//
|
||||
// 0 Tenant: string
|
||||
// 1 ID: string
|
||||
// 2 FilterIDs: strings separated by "&".
|
||||
// 3 Weights: strings separated by "&".
|
||||
// 4 Blocker: strings separated by "&".
|
||||
// 5 AttributeFilterIDs: strings separated by "&".
|
||||
// 6 AttributeBlockers: strings separated by "&".
|
||||
// 7 Path: string
|
||||
// 8 Type: string
|
||||
// 9 Value: strings separated by "&".
|
||||
// 10 APIOpts: set of key-value pairs (separated by "&").
|
||||
//
|
||||
// Parameters are separated by ";" and must be provided in the specified order.
|
||||
type actDynamicAttribute struct {
|
||||
config *config.CGRConfig
|
||||
connMgr *engine.ConnManager
|
||||
aCfg *utils.APAction
|
||||
tnt string
|
||||
cgrEv *utils.CGREvent
|
||||
}
|
||||
|
||||
func (aL *actDynamicAttribute) id() string {
|
||||
return aL.aCfg.ID
|
||||
}
|
||||
|
||||
func (aL *actDynamicAttribute) cfg() *utils.APAction {
|
||||
return aL.aCfg
|
||||
}
|
||||
|
||||
// execute implements actioner interface
|
||||
func (aL *actDynamicAttribute) 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 speified for action <%v>", aL.aCfg.ID)
|
||||
}
|
||||
params := strings.Split(aL.aCfg.Diktats[0].Value, 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 {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
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 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
|
||||
return aL.connMgr.Call(ctx, aL.config.ActionSCfg().AdminSConns,
|
||||
utils.AdminSv1SetAttributeProfile, args, &rply)
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ func actionTarget(act string) string {
|
||||
return utils.MetaThresholds
|
||||
case utils.MetaAddBalance, utils.MetaSetBalance, utils.MetaRemBalance:
|
||||
return utils.MetaAccounts
|
||||
case utils.MetaDynamicAttribute:
|
||||
return utils.MetaAttributes
|
||||
default:
|
||||
return utils.MetaNone
|
||||
}
|
||||
@@ -137,6 +139,8 @@ func newActioner(ctx *context.Context, cgrEv *utils.CGREvent, cfg *config.CGRCon
|
||||
return &actDynamicThreshold{cfg, connMgr, aCfg, tnt, cgrEv}, nil
|
||||
case utils.MetaDynamicStats:
|
||||
return &actDynamicStats{cfg, connMgr, aCfg, tnt, cgrEv}, nil
|
||||
case utils.MetaDynamicAttribute:
|
||||
return &actDynamicAttribute{cfg, connMgr, aCfg, tnt, cgrEv}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported action type: <%s>", aCfg.Type)
|
||||
|
||||
|
||||
@@ -168,9 +168,11 @@ func TestDynThdIT(t *testing.T) {
|
||||
Targets: map[string]utils.StringSet{
|
||||
utils.MetaThresholds: {"someID": {}},
|
||||
utils.MetaStats: {"someID": {}},
|
||||
utils.MetaAttributes: {"someID": {}},
|
||||
},
|
||||
Actions: []*utils.APAction{
|
||||
{
|
||||
ID: "Dynamic_Threshold_ID",
|
||||
Type: utils.MetaDynamicThreshold,
|
||||
Diktats: []*utils.APDiktat{
|
||||
{
|
||||
@@ -180,6 +182,7 @@ func TestDynThdIT(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "Dynamic_Stats_ID",
|
||||
Type: utils.MetaDynamicStats,
|
||||
Diktats: []*utils.APDiktat{
|
||||
{
|
||||
@@ -188,6 +191,16 @@ func TestDynThdIT(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "Dynamic_Attribute_ID",
|
||||
Type: utils.MetaDynamicAttribute,
|
||||
Diktats: []*utils.APDiktat{
|
||||
{
|
||||
Path: "ExtraParameters",
|
||||
Value: "*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",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -388,7 +401,8 @@ func TestDynThdIT(t *testing.T) {
|
||||
if err := client.Call(context.Background(), utils.AdminSv1GetStatQueueProfile, &utils.TenantIDWithAPIOpts{
|
||||
TenantID: &utils.TenantID{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "DYNAMICLY_STAT_1002"},
|
||||
ID: "DYNAMICLY_STAT_1002",
|
||||
},
|
||||
}, &rply); err != nil {
|
||||
t.Error(err)
|
||||
} else if !reflect.DeepEqual(exp, rply) {
|
||||
@@ -396,4 +410,55 @@ func TestDynThdIT(t *testing.T) {
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("GetDynamicAttributeProfile", func(t *testing.T) {
|
||||
var attrs []*utils.APIAttributeProfile
|
||||
if err := client.Call(context.Background(), utils.AdminSv1GetAttributeProfiles,
|
||||
&utils.ArgsItemIDs{
|
||||
Tenant: utils.CGRateSorg,
|
||||
}, &attrs); err != nil {
|
||||
t.Errorf("AdminSv1GetAttributeProfiles failed unexpectedly: %v", err)
|
||||
}
|
||||
if len(attrs) != 1 {
|
||||
t.Fatalf("AdminSv1GetAttributeProfiles len(attrs)=%v, want 1", len(attrs))
|
||||
}
|
||||
|
||||
exp := []*utils.APIAttributeProfile{
|
||||
{
|
||||
Tenant: utils.CGRateSorg,
|
||||
ID: "DYNAMICLY_ATTR_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",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(exp, attrs) {
|
||||
t.Errorf("Expected attributes to be the same. Before shutdown \n<%v>\nAfter rebooting <%v>", utils.ToJSON(attrs), utils.ToJSON(exp))
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
@@ -1132,6 +1132,7 @@ const (
|
||||
DynaprepaidActionplansCfg = "dynaprepaid_actionprofile"
|
||||
MetaDynamicThreshold = "*dynamic_threshold"
|
||||
MetaDynamicStats = "*dynamic_stats"
|
||||
MetaDynamicAttribute = "*dynamic_attribute"
|
||||
)
|
||||
|
||||
// Migrator Metas
|
||||
|
||||
Reference in New Issue
Block a user