From dcdd696932a3bc7ee01bc749e31e9b1964a08732 Mon Sep 17 00:00:00 2001 From: porosnicuadrian Date: Fri, 11 Feb 2022 17:10:14 +0200 Subject: [PATCH] Added ratePRofileRatesFOrEvent apis + tests --- apis/rates.go | 5 + apis/rates_it_test.go | 145 ++++++++++++++++-- .../samples/preload_internal/cgrates.json | 2 +- data/conf/samples/rates_internal/cgrates.json | 2 + data/conf/samples/rates_mongo/cgrates.json | 2 + rates/rates.go | 55 +++++++ utils/consts.go | 10 +- 7 files changed, 207 insertions(+), 14 deletions(-) diff --git a/apis/rates.go b/apis/rates.go index 10b1963de..8a4f60163 100644 --- a/apis/rates.go +++ b/apis/rates.go @@ -268,6 +268,11 @@ func (rSv1 *RateSv1) RateProfilesForEvent(ctx *context.Context, args *utils.CGRE return rSv1.rS.V1RateProfilesForEvent(ctx, args, rpIDs) } +// RateProfilesForEvent returns the list of rates that are matching the event from a specific profile +func (rSv1 *RateSv1) RateProfileRatesForEvent(ctx *context.Context, args *utils.CGREvent, rateIDs *[]string) (err error) { + return rSv1.rS.V1RateProfileRatesForEvent(ctx, args, rateIDs) +} + // CostForEvent returs the costs for the event and all the rate profile information func (rSv1 *RateSv1) CostForEvent(ctx *context.Context, args *utils.CGREvent, rpCost *utils.RateProfileCost) (err error) { return rSv1.rS.V1CostForEvent(ctx, args, rpCost) diff --git a/apis/rates_it_test.go b/apis/rates_it_test.go index 76b65d5ef..e9cba186e 100644 --- a/apis/rates_it_test.go +++ b/apis/rates_it_test.go @@ -76,6 +76,7 @@ var ( testRateProfileSetMultipleRatesInProfile, testRateProfileUpdateProfileRatesOverwrite, testRateProfilesForEventMatchingEvents, + testRateProfileRatesForEventMatchingEvents, testRateSKillEngine, } ) @@ -1999,21 +2000,18 @@ func testRateProfilesForEventMatchingEvents(t *testing.T) { } ratePrf4 := &utils.APIRateProfile{ RateProfile: &utils.RateProfile{ - Tenant: utils.CGRateSorg, - ID: "RT_104", - FilterIDs: []string{"*exists:~*req.Destination:"}, - MinCost: utils.NewDecimal(22, 2), - MaxCost: utils.NewDecimal(500000, 6), - MaxCostStrategy: "*free", + Tenant: utils.CGRateSorg, + ID: "RT_104", + FilterIDs: []string{"*exists:~*req.Destination:"}, Rates: map[string]*utils.Rate{ - "RT_Destination": { - ID: "RT_Destination", + "RT_Everytime": { + ID: "RT_Everytime", Weights: utils.DynamicWeights{ { Weight: 0, }, }, - ActivationTimes: "* 16 * * *", + ActivationTimes: "* 14 * * *", IntervalRates: []*utils.IntervalRate{ { IntervalStart: utils.NewDecimal(0, 0), @@ -2149,6 +2147,135 @@ func testRateProfilesForEventMatchingEvents(t *testing.T) { } } +func testRateProfileRatesForEventMatchingEvents(t *testing.T) { + // now for 2 of our profiles, we will set some rates and see what rates are matching our event + ratePrf1 := &utils.APIRateProfile{ + RateProfile: &utils.RateProfile{ + Tenant: utils.CGRateSorg, + ID: "RT_CGR202", + //FilterIDs: []string{"*lte:~*opts.*usage:2m"}, + Rates: map[string]*utils.Rate{ + "RT_ALWAYS": { + ID: "RT_ALWAYS", + FilterIDs: []string{"*prefix:~*req.Destination:2023", "*string:~*opts.*usage:1m"}, + ActivationTimes: "* * * * *", + IntervalRates: []*utils.IntervalRate{ + { + IntervalStart: utils.NewDecimal(0, 0), + FixedFee: utils.NewDecimal(22, 2), + Increment: utils.NewDecimal(int64(5*time.Second), 0), + RecurrentFee: utils.NewDecimal(int64(5*time.Second), 0), + Unit: utils.NewDecimal(int64(time.Minute), 0), + }, + }, + }, + "RT_WEEKEND": { + ID: "RT_WEEKEND", + ActivationTimes: "* * * * 5-6", + FilterIDs: []string{"*prefix:~*req.Destination:2023"}, + IntervalRates: []*utils.IntervalRate{ + { + IntervalStart: utils.NewDecimal(0, 0), + Increment: utils.NewDecimal(int64(time.Second), 0), + RecurrentFee: utils.NewDecimal(int64(time.Second), 0), + Unit: utils.NewDecimal(int64(time.Minute), 0), + }, + }, + }, + "RT_SPECIAL": { + ID: "RT_SPECIAL", + ActivationTimes: "* 12 * * *", + FilterIDs: []string{"*string:~*req.Account:2021", + "*string:~*req.Destination:2023"}, + IntervalRates: []*utils.IntervalRate{ + { + IntervalStart: utils.NewDecimal(int64(50*time.Second), 0), + Increment: utils.NewDecimal(int64(2*time.Second), 0), + RecurrentFee: utils.NewDecimal(int64(2*time.Second), 0), + Unit: utils.NewDecimal(int64(time.Minute), 0), + }, + }, + }, + "RT_DIFFERENT": { + ID: "RT_DIFFERENT", + ActivationTimes: "10 12 * * *", + //FilterIDs: []string{"*lte:~*opts.*usage:2m"}, + IntervalRates: []*utils.IntervalRate{ + { + IntervalStart: utils.NewDecimal(int64(50*time.Second), 0), + Increment: utils.NewDecimal(int64(2*time.Second), 0), + RecurrentFee: utils.NewDecimal(int64(2*time.Second), 0), + Unit: utils.NewDecimal(int64(time.Minute), 0), + }, + }, + }, + }, + }, + } + var reply string + if err := rateSRPC.Call(context.Background(), utils.AdminSv1SetRateProfile, + ratePrf1, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Error(err) + } + + expected := []string{"RT_DIFFERENT"} + var rateIDs []string + if err := rateSRPC.Call(context.Background(), utils.RateSv1RateProfileRatesForEvent, + &utils.CGREvent{ + Tenant: "cgrates.org", + Event: map[string]interface{}{ + "Account": "2021", + }, + APIOpts: map[string]interface{}{ + utils.MetaRateProfileID: "RT_CGR202", + utils.MetaUsage: "1m", + }, + }, &rateIDs); err != nil { + t.Error(err) + } else { + sort.Slice(expected, func(i, j int) bool { + return expected[i] < expected[j] + }) + sort.Slice(rateIDs, func(i, j int) bool { + return rateIDs[i] < rateIDs[j] + }) + if !reflect.DeepEqual(expected, rateIDs) { + t.Errorf("Expected %+v, received %+v", expected, rateIDs) + } + } + + /* + expected := []string{"RT_DIFFERENT"} + var rateIDs []string + if err := rateSRPC.Call(context.Background(), utils.RateSv1RateProfileRatesForEvent, + &utils.CGREvent{ + Tenant: "cgrates.org", + Event: map[string]interface{}{ + "Account": "2021", + utils.Destination: "2023", + }, + APIOpts: map[string]interface{}{ + utils.MetaRateProfileID: "RT_CGR202", + utils.MetaUsage: "1m", + }, + }, &rateIDs); err != nil { + t.Error(err) + } else { + sort.Slice(expected, func(i, j int) bool { + return expected[i] < expected[j] + }) + sort.Slice(rateIDs, func(i, j int) bool { + return rateIDs[i] < rateIDs[j] + }) + if !reflect.DeepEqual(expected, rateIDs) { + t.Errorf("Expected %+v, received %+v", expected, rateIDs) + } + } + */ +} + //Kill the engine when it is about to be finished func testRateSKillEngine(t *testing.T) { if err := engine.KillEngine(100); err != nil { diff --git a/data/conf/samples/preload_internal/cgrates.json b/data/conf/samples/preload_internal/cgrates.json index 2f736d5c4..e1d00afd1 100644 --- a/data/conf/samples/preload_internal/cgrates.json +++ b/data/conf/samples/preload_internal/cgrates.json @@ -107,7 +107,7 @@ {"tag": "Tenant", "path": "Tenant", "type": "*variable", "value": "~*req.0", "mandatory": true}, {"tag": "ID", "path": "ID", "type": "*variable", "value": "~*req.1", "mandatory": true}, {"tag": "FilterIDs", "path": "FilterIDs", "type": "*variable", "value": "~*req.2"}, - {"tag": "Weight", "path": "Weight", "type": "*variable", "value": "~*req.3"}, + {"tag": "Weights", "path": "Weights", "type": "*variable", "value": "~*req.3"}, {"tag": "Schedule", "path": "Schedule", "type": "*variable", "value": "~*req.4"}, // {"tag": "TargetType", "path": "TargetType", "type": "*variable", "value": "~*req.5"}, {"tag": "TargetIDs", "path": "Targets[<~*req.5>]", "type": "*variable", "value": "~*req.6"}, diff --git a/data/conf/samples/rates_internal/cgrates.json b/data/conf/samples/rates_internal/cgrates.json index 5cf3ee5c1..b8da91ece 100644 --- a/data/conf/samples/rates_internal/cgrates.json +++ b/data/conf/samples/rates_internal/cgrates.json @@ -13,6 +13,8 @@ "rates": { "enabled": true, + "prefix_indexed_fields": ["*req.Destination"], + "exists_indexed_fields": ["*req.Destination"], }, "admins": { diff --git a/data/conf/samples/rates_mongo/cgrates.json b/data/conf/samples/rates_mongo/cgrates.json index e65c87380..188a24538 100644 --- a/data/conf/samples/rates_mongo/cgrates.json +++ b/data/conf/samples/rates_mongo/cgrates.json @@ -17,6 +17,8 @@ "rates": { "enabled": true, + "prefix_indexed_fields": ["*req.Destination"], + "exists_indexed_fields": ["*req.Destination"], }, "admins": { diff --git a/rates/rates.go b/rates/rates.go index 36e140e80..d862e5942 100644 --- a/rates/rates.go +++ b/rates/rates.go @@ -267,6 +267,61 @@ func (rS *RateS) V1RateProfilesForEvent(ctx *context.Context, args *utils.CGREve return } +// RateProfilesForEvent returns the list of rates that are matching the event from a specific profile +func (rS *RateS) V1RateProfileRatesForEvent(ctx *context.Context, args *utils.CGREvent, rtIDs *[]string) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.CGREventString) + } + if _, has := args.APIOpts[utils.MetaRateProfileID]; !has { + return utils.NewErrMandatoryIeMissing(utils.MetaRateProfileID) + } + rpID := utils.IfaceAsString(args.APIOpts[utils.MetaRateProfileID]) + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = rS.cfg.GeneralCfg().DefaultTenant + } + evNM := args.AsDataProvider() + var rateIDs utils.StringSet + if rateIDs, err = engine.MatchingItemIDsForEvent(ctx, + evNM, + rS.cfg.RateSCfg().RateStringIndexedFields, + rS.cfg.RateSCfg().RatePrefixIndexedFields, + rS.cfg.RateSCfg().RateSuffixIndexedFields, + rS.cfg.RateSCfg().RateExistsIndexedFields, + rS.cfg.RateSCfg().RateNotExistsIndexedFields, + rS.dm, + utils.CacheRateFilterIndexes, + utils.ConcatenatedKey(tnt, rpID), + rS.cfg.RateSCfg().RateIndexedSelects, + rS.cfg.RateSCfg().RateNestedFields, + ); err != nil { + return + } + if len(rateIDs) == 0 { + return utils.ErrNotFound + } + var ratesMtched []string + for _, rateID := range rateIDs.AsSlice() { + var rp *utils.RateProfile + if rp, err = rS.dm.GetRateProfile(ctx, tnt, rpID, true, true, utils.NonTransactional); err != nil { + return + } + rate := rp.Rates[rateID] + var pass bool + if pass, err = rS.fltrS.Pass(ctx, tnt, rate.FilterIDs, evNM); err != nil { + return + } else if !pass { + continue + } + ratesMtched = append(ratesMtched, rateID) + } + if len(ratesMtched) == 0 { + return utils.ErrNotFound + } + *rtIDs = ratesMtched + return +} + // V1CostForEvent will be called to calculate the cost for an event func (rS *RateS) V1CostForEvent(ctx *context.Context, args *utils.CGREvent, rpCost *utils.RateProfileCost) (err error) { var rPfIDs []string diff --git a/utils/consts.go b/utils/consts.go index bf64b8dc4..19c0a40a9 100644 --- a/utils/consts.go +++ b/utils/consts.go @@ -381,6 +381,7 @@ const ( MetaEEs = "*ees" MetaRateS = "*rates" MetaRateSOverwrite = "*rateSOverwrite" + MetaRateProfileID = "*rateProfileID" MetaContinue = "*continue" MetaUp = "*up" Migrator = "migrator" @@ -1297,10 +1298,11 @@ const ( ) const ( - RateSv1 = "RateSv1" - RateSv1CostForEvent = "RateSv1.CostForEvent" - RateSv1RateProfilesForEvent = "RateSv1.RateProfilesForEvent" - RateSv1Ping = "RateSv1.Ping" + RateSv1 = "RateSv1" + RateSv1CostForEvent = "RateSv1.CostForEvent" + RateSv1RateProfilesForEvent = "RateSv1.RateProfilesForEvent" + RateSv1RateProfileRatesForEvent = "RateSv1.RateProfileRatesForEvent" + RateSv1Ping = "RateSv1.Ping" ) const (