added new dynopts function GetDurationPointerOpts

This commit is contained in:
gezimbll
2025-02-17 14:16:16 +01:00
committed by Dan Christian Bogos
parent 14515404bb
commit 18b0e4d417
2 changed files with 239 additions and 0 deletions

View File

@@ -73,6 +73,33 @@ func GetDurationOpts(ctx *context.Context, tnt string, dP utils.DataProvider, fS
return
}
// GetDurationPointerOpts checks the specified option names in order among the keys in APIOpts returning the first value it finds as *time.Duration, otherwise it
// returns the config option if at least one filter passes or the default value if none of them do
func GetDurationPointerOpts(ctx *context.Context, tnt string, dP utils.DataProvider, fS *FilterS, dynOpts []*config.DynamicDurationPointerOpt,
optNames ...string) (cfgOpt *time.Duration, err error) {
if opt, err := optIfaceFromDP(dP, optNames); err == nil {
var value time.Duration
value, err = utils.IfaceAsDuration(opt)
if err != nil {
return nil, err
}
return utils.DurationPointer(value), nil
} else if !errors.Is(err, utils.ErrNotFound) {
return nil, err
}
for _, opt := range dynOpts { // iterate through the options
if !slices.Contains([]string{utils.EmptyString, utils.MetaAny, tnt}, opt.Tenant) {
continue
}
if pass, err := fS.Pass(ctx, tnt, opt.FilterIDs, dP); err != nil { // check if the filter is passing for the DataProvider and return the option if it does
return nil, err
} else if pass {
return opt.Value(dP)
}
}
return
}
// GetStringOpts checks the specified option names in order among the keys in APIOpts returning the first value it finds as string, otherwise it
// returns the config option if at least one filter passes or the default value if none of them do
func GetStringOpts(ctx *context.Context, tnt string, dP utils.DataProvider, fS *FilterS, dynOpts []*config.DynamicStringOpt,

View File

@@ -340,6 +340,118 @@ func TestLibFiltersGetDurationOptsReturnDefaultOpt(t *testing.T) {
}
}
func TestLibFiltersGetDurationPointerOptsReturnOptFromAPIOpts(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB := NewInternalDB(nil, nil, nil)
dm := NewDataManager(dataDB, cfg.CacheCfg(), nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{
utils.OptsResourcesUsageTTL: time.Hour,
},
}
dynOpts := []*config.DynamicDurationPointerOpt{
// will never get to this opt because it will return once it
// finds the one set in APIOpts
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
}
expected := time.Hour
if rcv, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), fS, dynOpts,
"nonExistingAPIOpt", utils.OptsResourcesUsageTTL); err != nil {
t.Error(err)
} else if *rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationPointerOptsReturnConfigOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB := NewInternalDB(nil, nil, nil)
dm := NewDataManager(dataDB, cfg.CacheCfg(), nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicDurationPointerOpt{
// tenant will not be recognized, will ignore this opt
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.net", utils.DurationPointer(time.Millisecond), nil),
// filter will not pass, will ignore this opt
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.net", utils.DurationPointer(time.Second), nil),
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1001"}, "cgrates.org", utils.DurationPointer(time.Minute), nil),
}
expected := time.Minute
if rcv, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), fS, dynOpts,
utils.OptsResourcesUsageTTL); err != nil {
t.Error(err)
} else if *rcv != expected {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", expected, rcv)
}
}
func TestLibFiltersGetDurationPointerOptsFilterCheckErr(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB := NewInternalDB(nil, nil, nil)
dm := NewDataManager(dataDB, cfg.CacheCfg(), nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicDurationPointerOpt{
// function will return error after trying to parse the filter
config.NewDynamicDurationPointerOpt([]string{"*string.invalid:filter"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
}
experr := `inline parse error for string: <*string.invalid:filter>`
if _, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), fS, dynOpts,
utils.OptsResourcesUsageTTL); err == nil ||
err.Error() != experr {
t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err)
}
}
func TestLibFiltersGetDurationPointerOptsReturnDefaultOpt(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB := NewInternalDB(nil, nil, nil)
dm := NewDataManager(dataDB, cfg.CacheCfg(), nil)
fS := NewFilterS(cfg, nil, dm)
ev := &utils.CGREvent{
Tenant: "cgrates.org",
ID: "TestEvent",
Event: map[string]any{
utils.AccountField: 1001,
},
APIOpts: map[string]any{},
}
dynOpts := []*config.DynamicDurationPointerOpt{
// filter will not pass, will ignore this opt
config.NewDynamicDurationPointerOpt([]string{"*string:~*req.Account:1002"}, "cgrates.org", utils.DurationPointer(time.Second), nil),
config.NewDynamicDurationPointerOpt(nil, "", utils.DurationPointer(config.ResourcesUsageTTLDftOpt), nil),
}
if rcv, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), fS, dynOpts,
utils.OptsResourcesUsageTTL); err != nil {
t.Error(err)
} else if *rcv != config.ResourcesUsageTTLDftOpt {
t.Errorf("expected: <%+v>,\nreceived: <%+v>", config.ResourcesUsageTTLDftOpt, rcv)
}
}
func TestLibFiltersGetDurationOptsReturnOptFromAPIOpts(t *testing.T) {
cfg := config.NewDefaultCGRConfig()
dataDB := NewInternalDB(nil, nil, nil)
@@ -1563,6 +1675,106 @@ func TestDynamicDurationOptsDynVal(t *testing.T) {
}
}
func TestDynamicDurationPointerOptsDynVal(t *testing.T) {
tests := []struct {
name string
dynOpts []*config.DynamicInterfaceOpt
expVal *time.Duration
expErr error
}{
{
name: "DynOptsVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*opts.Usage",
},
},
expVal: utils.DurationPointer(time.Second * 10),
},
{
name: "DynReqVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.*acd",
},
},
expVal: utils.DurationPointer(3500000),
},
{
name: "StaticVal",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: 1000000000,
},
},
expVal: utils.DurationPointer(1 * time.Second),
},
{
name: "NotFound",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.RandomField",
},
},
expErr: utils.ErrNotFound,
},
{
name: "ValueNotConvertedCorrectly",
dynOpts: []*config.DynamicInterfaceOpt{
{
Tenant: "cgrates.org",
Value: "~*req.Usage2",
},
},
expErr: fmt.Errorf("time: invalid duration \"twenty-five\""),
},
}
ev := &utils.CGREvent{
Tenant: utils.CGRateSorg,
ID: "testIDEvent",
Event: map[string]any{
utils.AccountField: "1001",
"*acd": 3500000,
"Usage2": "twenty-five",
},
APIOpts: map[string]any{
"Usage": "10s",
},
}
fs := NewFilterS(config.CgrConfig(), nil, nil)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
dynOpts, err := config.IfaceToDurationPointerDynamicOpts(tt.dynOpts)
if err != nil {
t.Error(err)
return
}
out, err := GetDurationPointerOpts(context.Background(), "cgrates.org", ev.AsDataProvider(), fs, dynOpts, utils.OptsRatesUsage)
if tt.expErr != nil {
if err == nil {
t.Error("expected err,received nil")
}
if err.Error() != tt.expErr.Error() {
t.Errorf("expected error %v,received %v", tt.expErr, err)
}
return
}
if err != nil {
t.Errorf("unexpected err %v", err)
}
if *tt.expVal != *out {
t.Errorf("expected %v,received %v", tt.expVal, out)
}
})
}
}
func TestDynamicStringOptsDynVal(t *testing.T) {
tests := []struct {
name string