From c762de5c28e8fe0c9c0c975f69d28fa180fdd615 Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Thu, 6 Mar 2025 19:39:13 +0200 Subject: [PATCH] move rankings to dedicated package --- admins/rankings.go | 175 ++++++++++++++++++ apis/rankings.go | 39 +--- apis/routes_it_test.go | 78 ++++---- engine/datadbmock.go | 12 +- engine/datamanager.go | 20 +- engine/dynamicdp.go | 2 +- engine/filters_test.go | 6 +- engine/model_helpers.go | 6 +- engine/storage_interface.go | 8 +- engine/storage_internal_datadb.go | 12 +- engine/storage_mongo_datadb.go | 12 +- engine/storage_redis.go | 8 +- engine/tpreader.go | 2 +- general_tests/rankings_schedule_it_test.go | 2 +- general_tests/rankings_stored_it_test.go | 12 +- loaders/libloader.go | 2 +- loaders/loader.go | 2 +- rankings/apis.go | 131 +++++++++++++ {engine => rankings}/rankings.go | 162 +++------------- {apis => rankings}/rankings_it_test.go | 32 ++-- {engine => rankings}/rankings_test.go | 47 ++--- services/rankings.go | 5 +- tpes/tpe_rankings.go | 2 +- engine/librankings.go => utils/rankings.go | 153 +++++++++------ .../rankings_test.go | 80 ++++---- 25 files changed, 607 insertions(+), 403 deletions(-) create mode 100644 admins/rankings.go create mode 100644 rankings/apis.go rename {engine => rankings}/rankings.go (72%) rename {apis => rankings}/rankings_it_test.go (95%) rename {engine => rankings}/rankings_test.go (81%) rename engine/librankings.go => utils/rankings.go (79%) rename engine/librankings_test.go => utils/rankings_test.go (90%) diff --git a/admins/rankings.go b/admins/rankings.go new file mode 100644 index 000000000..2e5af8705 --- /dev/null +++ b/admins/rankings.go @@ -0,0 +1,175 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package admins + +import ( + "fmt" + "time" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/utils" +) + +// V1GetRankingProfile returns a Ranking profile +func (a *AdminS) V1GetRankingProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.RankingProfile) (err error) { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = a.cfg.GeneralCfg().DefaultTenant + } + sCfg, err := a.dm.GetRankingProfile(ctx, tnt, arg.ID, + true, true, utils.NonTransactional) + if err != nil { + return utils.APIErrorHandler(err) + } + *reply = *sCfg + return +} + +// V1GetRankingProfileIDs returns list of RankingProfile IDs registered for a tenant +func (a *AdminS) V1GetRankingProfileIDs(ctx *context.Context, args *utils.ArgsItemIDs, rngPrfIDs *[]string) (err error) { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = a.cfg.GeneralCfg().DefaultTenant + } + prfx := utils.RankingProfilePrefix + tnt + utils.ConcatenatedKeySep + lenPrfx := len(prfx) + prfx += args.ItemsPrefix + var keys []string + if keys, err = a.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil { + return + } + if len(keys) == 0 { + return utils.ErrNotFound + } + retIDs := make([]string, len(keys)) + for i, key := range keys { + retIDs[i] = key[lenPrfx:] + } + var limit, offset, maxItems int + if limit, offset, maxItems, err = utils.GetPaginateOpts(args.APIOpts); err != nil { + return + } + *rngPrfIDs, err = utils.Paginate(retIDs, limit, offset, maxItems) + return +} + +// V1GetRankingProfiles returns a list of ranking profiles registered for a tenant +func (a *AdminS) V1GetRankingProfiles(ctx *context.Context, args *utils.ArgsItemIDs, rgPrfs *[]*utils.RankingProfile) (err error) { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = a.cfg.GeneralCfg().DefaultTenant + } + var sqPrfIDs []string + if err = a.V1GetRankingProfileIDs(ctx, args, &sqPrfIDs); err != nil { + return + } + *rgPrfs = make([]*utils.RankingProfile, 0, len(sqPrfIDs)) + for _, sqPrfID := range sqPrfIDs { + var rgPrf *utils.RankingProfile + rgPrf, err = a.dm.GetRankingProfile(ctx, tnt, sqPrfID, true, true, utils.NonTransactional) + if err != nil { + return utils.APIErrorHandler(err) + } + *rgPrfs = append(*rgPrfs, rgPrf) + } + return +} + +// V1GetRankingProfilesCount returns the total number of RankingProfileIDs registered for a tenant +// returns ErrNotFound in case of 0 RankingProfileIDs +func (a *AdminS) V1GetRankingProfilesCount(ctx *context.Context, args *utils.ArgsItemIDs, reply *int) (err error) { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = a.cfg.GeneralCfg().DefaultTenant + } + prfx := utils.RankingProfilePrefix + tnt + utils.ConcatenatedKeySep + args.ItemsPrefix + var keys []string + if keys, err = a.dm.DataDB().GetKeysForPrefix(ctx, prfx); err != nil { + return err + } + if len(keys) == 0 { + return utils.ErrNotFound + } + *reply = len(keys) + return +} + +// V1SetRankingProfile alters/creates a RankingProfile +func (adms *AdminS) V1SetRankingProfile(ctx *context.Context, arg *utils.RankingProfileWithAPIOpts, reply *string) (err error) { + if missing := utils.MissingStructFields(arg.RankingProfile, []string{utils.ID}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if arg.Tenant == utils.EmptyString { + arg.Tenant = adms.cfg.GeneralCfg().DefaultTenant + } + if err = adms.dm.SetRankingProfile(ctx, arg.RankingProfile); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheRankingProfiles and store it in database + loadID := time.Now().UnixNano() + if err = adms.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheRankingProfiles: loadID}); err != nil { + return utils.APIErrorHandler(err) + } + // delay if needed before cache call + if adms.cfg.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", adms.cfg.GeneralCfg().CachingDelay)) + time.Sleep(adms.cfg.GeneralCfg().CachingDelay) + } + //handle caching for RankingProfile + if err = adms.CallCache(ctx, utils.IfaceAsString(arg.APIOpts[utils.MetaCache]), arg.Tenant, utils.CacheRankingProfiles, + arg.TenantID(), utils.EmptyString, nil, arg.APIOpts); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} + +// V1RemoveRankingProfile remove a specific ranking configuration +func (a *AdminS) V1RemoveRankingProfile(ctx *context.Context, args *utils.TenantIDWithAPIOpts, reply *string) error { + if missing := utils.MissingStructFields(args, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = a.cfg.GeneralCfg().DefaultTenant + } + if err := a.dm.RemoveRankingProfile(ctx, tnt, args.ID); err != nil { + return utils.APIErrorHandler(err) + } + // delay if needed before cache call + if a.cfg.GeneralCfg().CachingDelay != 0 { + utils.Logger.Info(fmt.Sprintf(" Delaying cache call for %v", a.cfg.GeneralCfg().CachingDelay)) + time.Sleep(a.cfg.GeneralCfg().CachingDelay) + } + //handle caching for RankingProfile + if err := a.CallCache(ctx, utils.IfaceAsString(args.APIOpts[utils.MetaCache]), tnt, utils.CacheRankingProfiles, + utils.ConcatenatedKey(tnt, args.ID), utils.EmptyString, nil, args.APIOpts); err != nil { + return utils.APIErrorHandler(err) + } + //generate a loadID for CacheRankingProfiles and store it in database + loadID := time.Now().UnixNano() + if err := a.dm.SetLoadIDs(ctx, map[string]int64{utils.CacheRankingProfiles: loadID}); err != nil { + return utils.APIErrorHandler(err) + } + *reply = utils.OK + return nil +} diff --git a/apis/rankings.go b/apis/rankings.go index 1dad91b38..e4c296769 100644 --- a/apis/rankings.go +++ b/apis/rankings.go @@ -23,12 +23,11 @@ import ( "time" "github.com/cgrates/birpc/context" - "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" ) // GetRankingProfile returns a Ranking profile -func (adms *AdminSv1) GetRankingProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.RankingProfile) (err error) { +func (adms *AdminSv1) GetRankingProfile(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.RankingProfile) (err error) { if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing return utils.NewErrMandatoryIeMissing(missing...) } @@ -74,7 +73,7 @@ func (adms *AdminSv1) GetRankingProfileIDs(ctx *context.Context, args *utils.Arg } // GetRankingProfiles returns a list of ranking profiles registered for a tenant -func (admS *AdminSv1) GetRankingProfiles(ctx *context.Context, args *utils.ArgsItemIDs, rgPrfs *[]*engine.RankingProfile) (err error) { +func (admS *AdminSv1) GetRankingProfiles(ctx *context.Context, args *utils.ArgsItemIDs, rgPrfs *[]*utils.RankingProfile) (err error) { tnt := args.Tenant if tnt == utils.EmptyString { tnt = admS.cfg.GeneralCfg().DefaultTenant @@ -83,9 +82,9 @@ func (admS *AdminSv1) GetRankingProfiles(ctx *context.Context, args *utils.ArgsI if err = admS.GetRankingProfileIDs(ctx, args, &sqPrfIDs); err != nil { return } - *rgPrfs = make([]*engine.RankingProfile, 0, len(sqPrfIDs)) + *rgPrfs = make([]*utils.RankingProfile, 0, len(sqPrfIDs)) for _, sqPrfID := range sqPrfIDs { - var rgPrf *engine.RankingProfile + var rgPrf *utils.RankingProfile rgPrf, err = admS.dm.GetRankingProfile(ctx, tnt, sqPrfID, true, true, utils.NonTransactional) if err != nil { return utils.APIErrorHandler(err) @@ -115,7 +114,7 @@ func (admS *AdminSv1) GetRankingProfilesCount(ctx *context.Context, args *utils. } // SetRankingProfile alters/creates a RankingProfile -func (adms *AdminSv1) SetRankingProfile(ctx *context.Context, arg *engine.RankingProfileWithAPIOpts, reply *string) (err error) { +func (adms *AdminSv1) SetRankingProfile(ctx *context.Context, arg *utils.RankingProfileWithAPIOpts, reply *string) (err error) { if missing := utils.MissingStructFields(arg.RankingProfile, []string{utils.ID}); len(missing) != 0 { return utils.NewErrMandatoryIeMissing(missing...) } @@ -174,31 +173,3 @@ func (adms *AdminSv1) RemoveRankingProfile(ctx *context.Context, args *utils.Ten *reply = utils.OK return nil } - -// NewRankingSv1 initializes RankingSV1 -func NewRankingSv1(rnks *engine.RankingS) *RankingSv1 { - return &RankingSv1{ - rnkS: rnks, - } -} - -type RankingSv1 struct { - rnkS *engine.RankingS -} - -// GetRankingSummary returns summary of the last updated ranking -func (rnks *RankingSv1) GetRankingSummary(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.RankingSummary) error { - return rnks.rnkS.V1GetRankingSummary(ctx, arg, reply) -} - -func (rnkS *RankingSv1) GetRanking(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *engine.Ranking) (err error) { - return rnkS.rnkS.V1GetRanking(ctx, arg, reply) -} - -func (rnkS *RankingSv1) GetSchedule(ctx *context.Context, args *utils.ArgScheduledRankings, schedRankings *[]utils.ScheduledRanking) (err error) { - return rnkS.rnkS.V1GetSchedule(ctx, args, schedRankings) -} - -func (rnkS *RankingSv1) ScheduleQueries(ctx *context.Context, args *utils.ArgScheduleRankingQueries, scheduled *int) (err error) { - return rnkS.rnkS.V1ScheduleQueries(ctx, args, scheduled) -} diff --git a/apis/routes_it_test.go b/apis/routes_it_test.go index 807c8e49e..8b5ff99ab 100644 --- a/apis/routes_it_test.go +++ b/apis/routes_it_test.go @@ -81,56 +81,56 @@ var ( func TestRoutesIT(t *testing.T) { switch *utils.DBType { case utils.MetaInternal: - raConfigDIR = "routes_internal" + roConfigDIR = "routes_internal" case utils.MetaMongo: - raConfigDIR = "routes_mongo" + roConfigDIR = "routes_mongo" case utils.MetaMySQL: - raConfigDIR = "routes_mysql" + roConfigDIR = "routes_mysql" case utils.MetaPostgres: t.SkipNow() default: t.Fatal("Unknown Database type") } for _, stest := range sTestsRo { - t.Run(raConfigDIR, stest) + t.Run(roConfigDIR, stest) } } func testRouteSInitCfg(t *testing.T) { var err error - raCfgPath = path.Join(*utils.DataDir, "conf", "samples", raConfigDIR) - raCfg, err = config.NewCGRConfigFromPath(context.Background(), raCfgPath) + roCfgPath = path.Join(*utils.DataDir, "conf", "samples", roConfigDIR) + roCfg, err = config.NewCGRConfigFromPath(context.Background(), roCfgPath) if err != nil { t.Error(err) } } func testRouteSInitDataDB(t *testing.T) { - if err := engine.InitDataDB(raCfg); err != nil { + if err := engine.InitDataDB(roCfg); err != nil { t.Fatal(err) } } func testRouteSResetStorDB(t *testing.T) { - if err := engine.InitStorDB(raCfg); err != nil { + if err := engine.InitStorDB(roCfg); err != nil { t.Fatal(err) } } // Start CGR Engine func testRoutesStartEngine(t *testing.T) { - if _, err := engine.StopStartEngine(raCfgPath, *utils.WaitRater); err != nil { + if _, err := engine.StopStartEngine(roCfgPath, *utils.WaitRater); err != nil { t.Fatal(err) } } func testRoutesRPCConn(t *testing.T) { - raRPC = engine.NewRPCClient(t, raCfg.ListenCfg(), *utils.Encoding) + roRPC = engine.NewRPCClient(t, roCfg.ListenCfg(), *utils.Encoding) } func testRoutesGetRouteProfileBeforeSet(t *testing.T) { var replyRouteProfile engine.RouteProfile - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfile, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{ Tenant: "cgrates.org", @@ -142,7 +142,7 @@ func testRoutesGetRouteProfileBeforeSet(t *testing.T) { func testRoutesGetRouteProfilesBeforeSet(t *testing.T) { var replyRouteProfiles *[]*engine.RouteProfile - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfiles, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfiles, &utils.ArgsItemIDs{ Tenant: "cgrates.org", }, &replyRouteProfiles); err == nil || err.Error() != utils.ErrNotFound.Error() { @@ -152,7 +152,7 @@ func testRoutesGetRouteProfilesBeforeSet(t *testing.T) { func testRoutesGetRouteProfileIDsBeforeSet(t *testing.T) { var replyRouteProfileIDs []string - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, &utils.ArgsItemIDs{ Tenant: "cgrates.org", }, &replyRouteProfileIDs); err == nil || err.Error() != utils.ErrNotFound.Error() { @@ -162,7 +162,7 @@ func testRoutesGetRouteProfileIDsBeforeSet(t *testing.T) { func testRoutesGetRouteProfileCountBeforeSet(t *testing.T) { var replyCount int - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, &utils.ArgsItemIDs{ Tenant: "cgrates.org", }, &replyCount); err == nil || err.Error() != utils.ErrNotFound.Error() { @@ -283,7 +283,7 @@ func testRoutesSetRouteProfiles(t *testing.T) { var reply string for _, routeProfile := range routeProfiles { - if err := raRPC.Call(context.Background(), utils.AdminSv1SetRouteProfile, + if err := roRPC.Call(context.Background(), utils.AdminSv1SetRouteProfile, routeProfile, &reply); err != nil { t.Error(err) } else if reply != utils.OK { @@ -316,7 +316,7 @@ func testRoutesGetRouteProfileAfterSet(t *testing.T) { }, } var replyRouteProfile engine.RouteProfile - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfile, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{ Tenant: "cgrates.org", @@ -332,7 +332,7 @@ func testRoutesGetRouteProfileAfterSet(t *testing.T) { func testRoutesGetRouteProfileIDsAfterSet(t *testing.T) { expectedIDs := []string{"TestA_ROUTE1", "TestA_ROUTE2", "TestA_ROUTE3", "TestB_ROUTE1", "TestB_ROUTE2"} var replyRouteProfileIDs []string - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, &utils.ArgsItemIDs{ Tenant: "cgrates.org", }, &replyRouteProfileIDs); err != nil { @@ -345,7 +345,7 @@ func testRoutesGetRouteProfileIDsAfterSet(t *testing.T) { } expectedIDs = []string{"TestA_ROUTE1", "TestA_ROUTE2", "TestA_ROUTE3"} - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, &utils.ArgsItemIDs{ Tenant: "cgrates.org", ItemsPrefix: "TestA", @@ -359,7 +359,7 @@ func testRoutesGetRouteProfileIDsAfterSet(t *testing.T) { } expectedIDs = []string{"TestB_ROUTE1", "TestB_ROUTE2"} - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, &utils.ArgsItemIDs{ Tenant: "cgrates.org", ItemsPrefix: "TestB", @@ -375,7 +375,7 @@ func testRoutesGetRouteProfileIDsAfterSet(t *testing.T) { func testRoutesGetRouteProfileCountAfterSet(t *testing.T) { var replyCount int - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, &utils.ArgsItemIDs{ Tenant: "cgrates.org", }, &replyCount); err != nil { @@ -384,7 +384,7 @@ func testRoutesGetRouteProfileCountAfterSet(t *testing.T) { t.Errorf("expected <%+v>, \nreceived: <%+v>", 0, replyCount) } - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, &utils.ArgsItemIDs{ Tenant: "cgrates.org", ItemsPrefix: "TestA", @@ -394,7 +394,7 @@ func testRoutesGetRouteProfileCountAfterSet(t *testing.T) { t.Errorf("expected <%+v>, \nreceived: <%+v>", 0, replyCount) } - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, &utils.ArgsItemIDs{ Tenant: "cgrates.org", ItemsPrefix: "TestB", @@ -504,7 +504,7 @@ func testRoutesGetRouteProfilesAfterSet(t *testing.T) { }, } var replyRouteProfiles []*engine.RouteProfile - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfiles, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfiles, &utils.ArgsItemIDs{ Tenant: "cgrates.org", }, &replyRouteProfiles); err != nil { @@ -522,7 +522,7 @@ func testRoutesGetRouteProfilesAfterSet(t *testing.T) { func testRoutesRemoveRouteProfile(t *testing.T) { var reply string - if err := raRPC.Call(context.Background(), utils.AdminSv1RemoveRouteProfile, + if err := roRPC.Call(context.Background(), utils.AdminSv1RemoveRouteProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{ Tenant: "cgrates.org", @@ -536,7 +536,7 @@ func testRoutesRemoveRouteProfile(t *testing.T) { func testRoutesGetRouteProfileAfterRemove(t *testing.T) { var replyRouteProfile engine.RouteProfile - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfile, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{ Tenant: "cgrates.org", @@ -549,7 +549,7 @@ func testRoutesGetRouteProfileAfterRemove(t *testing.T) { func testRoutesGetRouteProfileIDsAfterRemove(t *testing.T) { expectedIDs := []string{"TestA_ROUTE1", "TestA_ROUTE3", "TestB_ROUTE1", "TestB_ROUTE2"} var replyRouteProfileIDs []string - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, &utils.ArgsItemIDs{ Tenant: "cgrates.org", }, &replyRouteProfileIDs); err != nil { @@ -562,7 +562,7 @@ func testRoutesGetRouteProfileIDsAfterRemove(t *testing.T) { } expectedIDs = []string{"TestA_ROUTE1", "TestA_ROUTE3"} - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, &utils.ArgsItemIDs{ Tenant: "cgrates.org", ItemsPrefix: "TestA", @@ -576,7 +576,7 @@ func testRoutesGetRouteProfileIDsAfterRemove(t *testing.T) { } expectedIDs = []string{"TestB_ROUTE1", "TestB_ROUTE2"} - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, &utils.ArgsItemIDs{ Tenant: "cgrates.org", ItemsPrefix: "TestB", @@ -592,7 +592,7 @@ func testRoutesGetRouteProfileIDsAfterRemove(t *testing.T) { func testRoutesGetRouteProfileCountAfterRemove(t *testing.T) { var replyCount int - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, &utils.ArgsItemIDs{ Tenant: "cgrates.org", }, &replyCount); err != nil { @@ -601,7 +601,7 @@ func testRoutesGetRouteProfileCountAfterRemove(t *testing.T) { t.Errorf("expected <%+v>, \nreceived: <%+v>", 0, replyCount) } - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, &utils.ArgsItemIDs{ Tenant: "cgrates.org", ItemsPrefix: "TestA", @@ -611,7 +611,7 @@ func testRoutesGetRouteProfileCountAfterRemove(t *testing.T) { t.Errorf("expected <%+v>, \nreceived: <%+v>", 0, replyCount) } - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfilesCount, &utils.ArgsItemIDs{ Tenant: "cgrates.org", ItemsPrefix: "TestB", @@ -699,7 +699,7 @@ func testRoutesGetRouteProfilesAfterRemove(t *testing.T) { }, } var replyRouteProfiles []*engine.RouteProfile - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfiles, + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfiles, &utils.ArgsItemIDs{ Tenant: "cgrates.org", }, &replyRouteProfiles); err != nil { @@ -721,7 +721,7 @@ func testRoutesBlockerRemoveRouteProfiles(t *testing.T) { } expected := []string{"TestA_ROUTE1", "TestA_ROUTE3", "TestB_ROUTE1", "TestB_ROUTE2"} var routeProfileIDs []string - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, args, &routeProfileIDs); err != nil { + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, args, &routeProfileIDs); err != nil { t.Fatal(err) } else { sort.Strings(routeProfileIDs) @@ -737,11 +737,11 @@ func testRoutesBlockerRemoveRouteProfiles(t *testing.T) { ID: routeProfileID, }, } - if err := raRPC.Call(context.Background(), utils.AdminSv1RemoveRouteProfile, argsRem, &reply); err != nil { + if err := roRPC.Call(context.Background(), utils.AdminSv1RemoveRouteProfile, argsRem, &reply); err != nil { t.Fatal(err) } } - if err := raRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, args, &routeProfileIDs); err == nil || + if err := roRPC.Call(context.Background(), utils.AdminSv1GetRouteProfileIDs, args, &routeProfileIDs); err == nil || err.Error() != utils.ErrNotFound.Error() { t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) } @@ -839,7 +839,7 @@ func testRoutesBlockerSetRouteProfiles(t *testing.T) { var reply string for _, routeProfile := range routeProfiles { - if err := raRPC.Call(context.Background(), utils.AdminSv1SetRouteProfile, + if err := roRPC.Call(context.Background(), utils.AdminSv1SetRouteProfile, routeProfile, &reply); err != nil { t.Error(err) } else if reply != utils.OK { @@ -921,7 +921,7 @@ func testRoutesBlockerGetRouteProfilesForEvent(t *testing.T) { }, } var reply []*engine.RouteProfile - if err := raRPC.Call(context.Background(), utils.RouteSv1GetRouteProfilesForEvent, args, &reply); err != nil { + if err := roRPC.Call(context.Background(), utils.RouteSv1GetRouteProfilesForEvent, args, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, expected) { t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expected), utils.ToJSON(reply)) @@ -989,7 +989,7 @@ func testRoutesBlockerSetRouteProfile(t *testing.T) { } var reply string - if err := raRPC.Call(context.Background(), utils.AdminSv1SetRouteProfile, + if err := roRPC.Call(context.Background(), utils.AdminSv1SetRouteProfile, routeProfile, &reply); err != nil { t.Error(err) } else if reply != utils.OK { @@ -1038,7 +1038,7 @@ func testRoutesBlockerGetRoutes(t *testing.T) { } var reply engine.SortedRoutesList - if err := raRPC.Call(context.Background(), utils.RouteSv1GetRoutes, args, &reply); err != nil { + if err := roRPC.Call(context.Background(), utils.RouteSv1GetRoutes, args, &reply); err != nil { t.Error(err) } else if !reflect.DeepEqual(reply, expected) { t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(expected), utils.ToJSON(reply)) diff --git a/engine/datadbmock.go b/engine/datadbmock.go index 2c2e9e7c4..f496fdc6e 100644 --- a/engine/datadbmock.go +++ b/engine/datadbmock.go @@ -51,8 +51,8 @@ type DataDBMock struct { SetTrendProfileDrvF func(ctx *context.Context, tr *utils.TrendProfile) (err error) GetTrendProfileDrvF func(ctx *context.Context, tenant string, id string) (sq *utils.TrendProfile, err error) RemTrendProfileDrvF func(ctx *context.Context, tenant string, id string) (err error) - SetRankingProfileDrvF func(ctx *context.Context, sq *RankingProfile) (err error) - GetRankingProfileDrvF func(ctx *context.Context, tenant string, id string) (sq *RankingProfile, err error) + SetRankingProfileDrvF func(ctx *context.Context, sq *utils.RankingProfile) (err error) + GetRankingProfileDrvF func(ctx *context.Context, tenant string, id string) (sq *utils.RankingProfile, err error) RemRankingProfileDrvF func(ctx *context.Context, tenant string, id string) (err error) GetStatQueueProfileDrvF func(ctx *context.Context, tenant, id string) (sq *StatQueueProfile, err error) SetStatQueueProfileDrvF func(ctx *context.Context, sq *StatQueueProfile) (err error) @@ -228,14 +228,14 @@ func (dbM *DataDBMock) RemStatQueueDrv(ctx *context.Context, tenant, id string) } return utils.ErrNotImplemented } -func (dbM *DataDBMock) GetRankingProfileDrv(ctx *context.Context, tenant, id string) (sg *RankingProfile, err error) { +func (dbM *DataDBMock) GetRankingProfileDrv(ctx *context.Context, tenant, id string) (sg *utils.RankingProfile, err error) { if dbM.GetStatQueueProfileDrvF != nil { return dbM.GetRankingProfileDrvF(ctx, tenant, id) } return nil, utils.ErrNotImplemented } -func (dbM *DataDBMock) SetRankingProfileDrv(ctx *context.Context, rg *RankingProfile) (err error) { +func (dbM *DataDBMock) SetRankingProfileDrv(ctx *context.Context, rg *utils.RankingProfile) (err error) { if dbM.SetRankingProfileDrvF(ctx, rg) != nil { return dbM.SetRankingProfileDrvF(ctx, rg) } @@ -249,11 +249,11 @@ func (dbM *DataDBMock) RemRankingProfileDrv(ctx *context.Context, tenant string, return utils.ErrNotImplemented } -func (dbM *DataDBMock) GetRankingDrv(ctx *context.Context, tenant, id string) (*Ranking, error) { +func (dbM *DataDBMock) GetRankingDrv(ctx *context.Context, tenant, id string) (*utils.Ranking, error) { return nil, utils.ErrNotImplemented } -func (dbM *DataDBMock) SetRankingDrv(ctx *context.Context, _ *Ranking) error { +func (dbM *DataDBMock) SetRankingDrv(ctx *context.Context, _ *utils.Ranking) error { return utils.ErrNotImplemented } diff --git a/engine/datamanager.go b/engine/datamanager.go index 4595c4d78..9f6b2a485 100644 --- a/engine/datamanager.go +++ b/engine/datamanager.go @@ -175,7 +175,7 @@ func (dm *DataManager) CacheDataFromDB(ctx *context.Context, prfx string, ids [] guardian.Guardian.UnguardIDs(lkID) case utils.RankingProfilePrefix: tntID := utils.NewTenantID(dataID) - lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, rankingProfileLockKey(tntID.Tenant, tntID.ID)) + lkID := guardian.Guardian.GuardIDs("", dm.cfg.GeneralCfg().LockingTimeout, utils.RankingProfileLockKey(tntID.Tenant, tntID.ID)) _, err = dm.GetRankingProfile(ctx, tntID.Tenant, tntID.ID, false, true, utils.NonTransactional) guardian.Guardian.UnguardIDs(lkID) case utils.TrendProfilePrefix: @@ -1199,14 +1199,14 @@ func (dm *DataManager) RemoveTrendProfile(ctx *context.Context, tenant, id strin return dm.RemoveTrend(ctx, tenant, id) } -func (dm *DataManager) GetRankingProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (rgp *RankingProfile, err error) { +func (dm *DataManager) GetRankingProfile(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (rgp *utils.RankingProfile, err error) { tntID := utils.ConcatenatedKey(tenant, id) if cacheRead { if x, ok := Cache.Get(utils.CacheRankingProfiles, tntID); ok { if x == nil { return nil, utils.ErrNotFound } - return x.(*RankingProfile), nil + return x.(*utils.RankingProfile), nil } } if dm == nil { @@ -1279,7 +1279,7 @@ func (dm *DataManager) GetRankingProfileIDs(ctx *context.Context, tenants []stri return } -func (dm *DataManager) SetRankingProfile(ctx *context.Context, rnp *RankingProfile) (err error) { +func (dm *DataManager) SetRankingProfile(ctx *context.Context, rnp *utils.RankingProfile) (err error) { if dm == nil { return utils.ErrNoDatabaseConn } @@ -1295,14 +1295,14 @@ func (dm *DataManager) SetRankingProfile(ctx *context.Context, rnp *RankingProfi dm.cfg.DataDbCfg().RplFiltered, utils.RankingProfilePrefix, rnp.TenantID(), utils.ReplicatorSv1SetRankingProfile, - &RankingProfileWithAPIOpts{ + &utils.RankingProfileWithAPIOpts{ RankingProfile: rnp, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, dm.cfg.DataDbCfg().RplCache, utils.EmptyString)}) } if oldRnk == nil || oldRnk.Sorting != rnp.Sorting || oldRnk.Schedule != rnp.Schedule { - if err = dm.SetRanking(ctx, NewRankingFromProfile(rnp)); err != nil { + if err = dm.SetRanking(ctx, utils.NewRankingFromProfile(rnp)); err != nil { return } } @@ -1335,14 +1335,14 @@ func (dm *DataManager) RemoveRankingProfile(ctx *context.Context, tenant, id str } return } -func (dm *DataManager) GetRanking(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (rn *Ranking, err error) { +func (dm *DataManager) GetRanking(ctx *context.Context, tenant, id string, cacheRead, cacheWrite bool, transactionID string) (rn *utils.Ranking, err error) { tntID := utils.ConcatenatedKey(tenant, id) if cacheRead { if x, ok := Cache.Get(utils.CacheRankings, tntID); ok { if x == nil { return nil, utils.ErrNotFound } - return x.(*Ranking), nil + return x.(*utils.Ranking), nil } } if dm == nil { @@ -1384,7 +1384,7 @@ func (dm *DataManager) GetRanking(ctx *context.Context, tenant, id string, cache } // SetRanking stores Ranking in dataDB -func (dm *DataManager) SetRanking(ctx *context.Context, rn *Ranking) (err error) { +func (dm *DataManager) SetRanking(ctx *context.Context, rn *utils.Ranking) (err error) { if dm == nil { return utils.ErrNoDatabaseConn } @@ -1396,7 +1396,7 @@ func (dm *DataManager) SetRanking(ctx *context.Context, rn *Ranking) (err error) dm.cfg.DataDbCfg().RplFiltered, utils.RankingPrefix, rn.TenantID(), // this are used to get the host IDs from cache utils.ReplicatorSv1SetRanking, - &RankingWithAPIOpts{ + &utils.RankingWithAPIOpts{ Ranking: rn, APIOpts: utils.GenerateDBItemOpts(itm.APIKey, itm.RouteID, dm.cfg.DataDbCfg().RplCache, utils.EmptyString)}); err != nil { diff --git a/engine/dynamicdp.go b/engine/dynamicdp.go index d9d1552d6..009bcb5ef 100644 --- a/engine/dynamicdp.go +++ b/engine/dynamicdp.go @@ -140,7 +140,7 @@ func (dDP *dynamicDP) fieldAsInterface(fldPath []string) (val any, err error) { return dp.FieldAsInterface(fldPath[2:]) case utils.MetaRankings: // sample of fieldName : ~*rankings.RankingID.SortedStatIDs[0] - var rankingSum RankingSummary + var rankingSum utils.RankingSummary if err := connMgr.Call(context.TODO(), dDP.rnkConns, utils.RankingSv1GetRankingSummary, &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: dDP.tenant, ID: fldPath[1]}}, &rankingSum); err != nil { return nil, err } diff --git a/engine/filters_test.go b/engine/filters_test.go index c1fa55d01..7e25923a2 100644 --- a/engine/filters_test.go +++ b/engine/filters_test.go @@ -2633,14 +2633,14 @@ func TestFilterRanking(t *testing.T) { return fmt.Errorf("wrong args") } if argTntID.ID == "Ranking1" && argTntID.Tenant == "cgrates.org" { - rn := Ranking{ + rn := utils.Ranking{ Tenant: "cgrates.org", ID: "Ranking1", LastUpdate: time.Now(), SortedStatIDs: []string{"Stat5", "Stat6", "Stat7", "Stat4", "Stat3", "Stat1", "Stat2"}, } - rnS := rn.asRankingSummary() - *reply.(*RankingSummary) = *rnS + rnS := rn.AsRankingSummary() + *reply.(*utils.RankingSummary) = *rnS return nil } return utils.ErrNotFound diff --git a/engine/model_helpers.go b/engine/model_helpers.go index 35c97bec9..a6ff77c04 100644 --- a/engine/model_helpers.go +++ b/engine/model_helpers.go @@ -672,8 +672,8 @@ func APItoModelTPRanking(tpRG *utils.TPRankingProfile) (mdls RankingMdls) { return } -func APItoRanking(tpRG *utils.TPRankingProfile) (rg *RankingProfile, err error) { - rg = &RankingProfile{ +func APItoRanking(tpRG *utils.TPRankingProfile) (rg *utils.RankingProfile, err error) { + rg = &utils.RankingProfile{ Tenant: tpRG.Tenant, ID: tpRG.ID, Schedule: tpRG.Schedule, @@ -691,7 +691,7 @@ func APItoRanking(tpRG *utils.TPRankingProfile) (rg *RankingProfile, err error) return rg, nil } -func RankingProfileToAPI(rg *RankingProfile) (tpRG *utils.TPRankingProfile) { +func RankingProfileToAPI(rg *utils.RankingProfile) (tpRG *utils.TPRankingProfile) { tpRG = &utils.TPRankingProfile{ Tenant: rg.Tenant, ID: rg.ID, diff --git a/engine/storage_interface.go b/engine/storage_interface.go index 98e405113..1401a19de 100644 --- a/engine/storage_interface.go +++ b/engine/storage_interface.go @@ -64,11 +64,11 @@ type DataDB interface { GetThresholdDrv(*context.Context, string, string) (*Threshold, error) SetThresholdDrv(*context.Context, *Threshold) error RemoveThresholdDrv(*context.Context, string, string) error - SetRankingProfileDrv(ctx *context.Context, rp *RankingProfile) (err error) - GetRankingProfileDrv(ctx *context.Context, tenant string, id string) (sq *RankingProfile, err error) + SetRankingProfileDrv(ctx *context.Context, rp *utils.RankingProfile) (err error) + GetRankingProfileDrv(ctx *context.Context, tenant string, id string) (sq *utils.RankingProfile, err error) RemRankingProfileDrv(ctx *context.Context, tenant string, id string) (err error) - SetRankingDrv(ctx *context.Context, rn *Ranking) (err error) - GetRankingDrv(ctx *context.Context, tenant string, id string) (sq *Ranking, err error) + SetRankingDrv(ctx *context.Context, rn *utils.Ranking) (err error) + GetRankingDrv(ctx *context.Context, tenant string, id string) (sq *utils.Ranking, err error) RemoveRankingDrv(ctx *context.Context, tenant string, id string) (err error) SetTrendProfileDrv(ctx *context.Context, sq *utils.TrendProfile) (err error) GetTrendProfileDrv(ctx *context.Context, tenant string, id string) (sq *utils.TrendProfile, err error) diff --git a/engine/storage_internal_datadb.go b/engine/storage_internal_datadb.go index 3d63e75d1..a6440efe9 100644 --- a/engine/storage_internal_datadb.go +++ b/engine/storage_internal_datadb.go @@ -256,15 +256,15 @@ func (iDB *InternalDB) RemStatQueueProfileDrv(_ *context.Context, tenant, id str return } -func (iDB *InternalDB) GetRankingProfileDrv(_ *context.Context, tenant, id string) (sg *RankingProfile, err error) { +func (iDB *InternalDB) GetRankingProfileDrv(_ *context.Context, tenant, id string) (sg *utils.RankingProfile, err error) { x, ok := iDB.db.Get(utils.CacheRankingProfiles, utils.ConcatenatedKey(tenant, id)) if !ok || x == nil { return nil, utils.ErrNotFound } - return x.(*RankingProfile), nil + return x.(*utils.RankingProfile), nil } -func (iDB *InternalDB) SetRankingProfileDrv(_ *context.Context, sgp *RankingProfile) (err error) { +func (iDB *InternalDB) SetRankingProfileDrv(_ *context.Context, sgp *utils.RankingProfile) (err error) { iDB.db.Set(utils.CacheRankingProfiles, sgp.TenantID(), sgp, nil, true, utils.NonTransactional) return nil } @@ -274,15 +274,15 @@ func (iDB *InternalDB) RemRankingProfileDrv(_ *context.Context, tenant, id strin return nil } -func (iDB *InternalDB) GetRankingDrv(_ *context.Context, tenant, id string) (rn *Ranking, err error) { +func (iDB *InternalDB) GetRankingDrv(_ *context.Context, tenant, id string) (rn *utils.Ranking, err error) { x, ok := iDB.db.Get(utils.CacheRankings, utils.ConcatenatedKey(tenant, id)) if !ok || x == nil { return nil, utils.ErrNotFound } - return x.(*Ranking), nil + return x.(*utils.Ranking), nil } -func (iDB *InternalDB) SetRankingDrv(_ *context.Context, rn *Ranking) (err error) { +func (iDB *InternalDB) SetRankingDrv(_ *context.Context, rn *utils.Ranking) (err error) { iDB.db.Set(utils.CacheRankings, rn.TenantID(), rn, nil, true, utils.NonTransactional) return diff --git a/engine/storage_mongo_datadb.go b/engine/storage_mongo_datadb.go index e06b46130..1158aff92 100644 --- a/engine/storage_mongo_datadb.go +++ b/engine/storage_mongo_datadb.go @@ -739,8 +739,8 @@ func (ms *MongoStorage) RemoveResourceDrv(ctx *context.Context, tenant, id strin }) } -func (ms *MongoStorage) GetRankingProfileDrv(ctx *context.Context, tenant, id string) (*RankingProfile, error) { - rgProfile := new(RankingProfile) +func (ms *MongoStorage) GetRankingProfileDrv(ctx *context.Context, tenant, id string) (*utils.RankingProfile, error) { + rgProfile := new(utils.RankingProfile) err := ms.query(ctx, func(sctx mongo.SessionContext) error { sr := ms.getCol(ColRgp).FindOne(sctx, bson.M{"tenant": tenant, "id": id}) decodeErr := sr.Decode(rgProfile) @@ -752,7 +752,7 @@ func (ms *MongoStorage) GetRankingProfileDrv(ctx *context.Context, tenant, id st return rgProfile, err } -func (ms *MongoStorage) SetRankingProfileDrv(ctx *context.Context, sgp *RankingProfile) (err error) { +func (ms *MongoStorage) SetRankingProfileDrv(ctx *context.Context, sgp *utils.RankingProfile) (err error) { return ms.query(ctx, func(sctx mongo.SessionContext) error { _, err := ms.getCol(ColRgp).UpdateOne(sctx, bson.M{"tenant": sgp.Tenant, "id": sgp.ID}, bson.M{"$set": sgp}, @@ -772,8 +772,8 @@ func (ms *MongoStorage) RemRankingProfileDrv(ctx *context.Context, tenant, id st } -func (ms *MongoStorage) GetRankingDrv(ctx *context.Context, tenant, id string) (*Ranking, error) { - rn := new(Ranking) +func (ms *MongoStorage) GetRankingDrv(ctx *context.Context, tenant, id string) (*utils.Ranking, error) { + rn := new(utils.Ranking) err := ms.query(ctx, func(sctx mongo.SessionContext) error { sr := ms.getCol(ColRnk).FindOne(sctx, bson.M{"tenant": tenant, "id": id}) decodeErr := sr.Decode(rn) @@ -784,7 +784,7 @@ func (ms *MongoStorage) GetRankingDrv(ctx *context.Context, tenant, id string) ( }) return rn, err } -func (ms *MongoStorage) SetRankingDrv(ctx *context.Context, rn *Ranking) error { +func (ms *MongoStorage) SetRankingDrv(ctx *context.Context, rn *utils.Ranking) error { return ms.query(ctx, func(sctx mongo.SessionContext) error { _, err := ms.getCol(ColRnk).UpdateOne(sctx, bson.M{"tenant": rn.Tenant, "id": rn.ID}, bson.M{"$set": rn}, diff --git a/engine/storage_redis.go b/engine/storage_redis.go index fafd70b49..7618e4ea2 100644 --- a/engine/storage_redis.go +++ b/engine/storage_redis.go @@ -577,7 +577,7 @@ func (rs *RedisStorage) RemoveTrendDrv(ctx *context.Context, tenant, id string) return rs.Cmd(nil, redisDEL, utils.TrendPrefix+utils.ConcatenatedKey(tenant, id)) } -func (rs *RedisStorage) SetRankingProfileDrv(ctx *context.Context, sg *RankingProfile) (err error) { +func (rs *RedisStorage) SetRankingProfileDrv(ctx *context.Context, sg *utils.RankingProfile) (err error) { var result []byte if result, err = rs.ms.Marshal(sg); err != nil { return @@ -585,7 +585,7 @@ func (rs *RedisStorage) SetRankingProfileDrv(ctx *context.Context, sg *RankingPr return rs.Cmd(nil, redisSET, utils.RankingProfilePrefix+utils.ConcatenatedKey(sg.Tenant, sg.ID), string(result)) } -func (rs *RedisStorage) GetRankingProfileDrv(ctx *context.Context, tenant string, id string) (sg *RankingProfile, err error) { +func (rs *RedisStorage) GetRankingProfileDrv(ctx *context.Context, tenant string, id string) (sg *utils.RankingProfile, err error) { var values []byte if err = rs.Cmd(&values, redisGET, utils.RankingProfilePrefix+utils.ConcatenatedKey(tenant, id)); err != nil { return @@ -601,7 +601,7 @@ func (rs *RedisStorage) RemRankingProfileDrv(ctx *context.Context, tenant string return rs.Cmd(nil, redisDEL, utils.RankingProfilePrefix+utils.ConcatenatedKey(tenant, id)) } -func (rs *RedisStorage) GetRankingDrv(ctx *context.Context, tenant, id string) (rn *Ranking, err error) { +func (rs *RedisStorage) GetRankingDrv(ctx *context.Context, tenant, id string) (rn *utils.Ranking, err error) { var values []byte if err = rs.Cmd(&values, redisGET, utils.RankingPrefix+utils.ConcatenatedKey(tenant, id)); err != nil { return @@ -613,7 +613,7 @@ func (rs *RedisStorage) GetRankingDrv(ctx *context.Context, tenant, id string) ( return rn, err } -func (rs *RedisStorage) SetRankingDrv(_ *context.Context, rn *Ranking) (err error) { +func (rs *RedisStorage) SetRankingDrv(_ *context.Context, rn *utils.Ranking) (err error) { var result []byte if result, err = rs.ms.Marshal(rn); err != nil { return diff --git a/engine/tpreader.go b/engine/tpreader.go index 948f851c7..feb1f1061 100644 --- a/engine/tpreader.go +++ b/engine/tpreader.go @@ -466,7 +466,7 @@ func (tpr *TpReader) WriteToDatabase(verbose, disableReverse bool) (err error) { log.Print("RankingProfiles:") } for _, tpRN := range tpr.rgProfiles { - var rn *RankingProfile + var rn *utils.RankingProfile if rn, err = APItoRanking(tpRN); err != nil { return } diff --git a/general_tests/rankings_schedule_it_test.go b/general_tests/rankings_schedule_it_test.go index c5c72dd32..f135fb3fc 100644 --- a/general_tests/rankings_schedule_it_test.go +++ b/general_tests/rankings_schedule_it_test.go @@ -131,7 +131,7 @@ cgrates.org,Stats4,*string:~*req.Account:1004,;30,,,-1,0,,,*acc;*acd;*pdd,,`, time.Sleep(1 * time.Second) t.Run("GetRankings", func(t *testing.T) { - var rnk engine.Ranking + var rnk utils.Ranking sortedStatIds := []string{"Stats3", "Stats2", "Stats1", "Stats4"} if err := client.Call(context.Background(), utils.RankingSv1GetRanking, &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "RANK1"}}, &rnk); err != nil { t.Error(err) diff --git a/general_tests/rankings_stored_it_test.go b/general_tests/rankings_stored_it_test.go index d003fd082..d4577d878 100644 --- a/general_tests/rankings_stored_it_test.go +++ b/general_tests/rankings_stored_it_test.go @@ -124,12 +124,12 @@ cgrates.org,Stats4,*string:~*req.Account:1004,,,,-1,,,,*acc;*acd;*pdd,,`} }) t.Run("GetRankingsAfterStoreInterval", func(t *testing.T) { - rankingsChan := make(chan *engine.Ranking, 1) + rankingsChan := make(chan *utils.Ranking, 1) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() go func() { ticker := time.NewTicker(600 * time.Millisecond) - var rnk engine.Ranking + var rnk utils.Ranking for { select { case <-ticker.C: @@ -213,7 +213,7 @@ cgrates.org,Stats4,*string:~*req.Account:1004,,,,-1,,,,*acc;*acd;*pdd,,`} }) t.Run("GetRankingsNotStored", func(t *testing.T) { - metricsChan := make(chan *engine.Ranking, 1) + metricsChan := make(chan *utils.Ranking, 1) ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) defer cancel() go func() { @@ -221,7 +221,7 @@ cgrates.org,Stats4,*string:~*req.Account:1004,,,,-1,,,,*acc;*acd;*pdd,,`} ticker := time.NewTicker(700 * time.Millisecond) select { case <-ticker.C: - var rnk engine.Ranking + var rnk utils.Ranking err := client.Call(context.Background(), utils.RankingSv1GetRanking, &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "RANK1"}}, &rnk) if err != nil { if err.Error() != utils.ErrNotFound.Error() { @@ -273,7 +273,7 @@ cgrates.org,Stats4,*string:~*req.Account:1004,,,,-1,,,,*acc;*acd;*pdd,,`} } }) t.Run("GetRankingsStoredUnlimited", func(t *testing.T) { - rankingChan := make(chan *engine.Ranking, 1) + rankingChan := make(chan *utils.Ranking, 1) ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() go func() { @@ -281,7 +281,7 @@ cgrates.org,Stats4,*string:~*req.Account:1004,,,,-1,,,,*acc;*acd;*pdd,,`} for { select { case <-ticker.C: - var rnk engine.Ranking + var rnk utils.Ranking err := client.Call(context.Background(), utils.RankingSv1GetRanking, &utils.TenantIDWithAPIOpts{TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "RANK1"}}, &rnk) if err != nil { if err.Error() != utils.ErrNotFound.Error() { diff --git a/loaders/libloader.go b/loaders/libloader.go index 1102e84cc..8bb70c768 100644 --- a/loaders/libloader.go +++ b/loaders/libloader.go @@ -316,7 +316,7 @@ func newProfileFunc(lType string) func() profile { } case utils.MetaRankings: return func() profile { - return new(engine.RankingProfile) + return new(utils.RankingProfile) } default: return func() profile { return nil } diff --git a/loaders/loader.go b/loaders/loader.go index a5318715c..f8c3a69b0 100644 --- a/loaders/loader.go +++ b/loaders/loader.go @@ -112,7 +112,7 @@ func setToDB(ctx *context.Context, dm *engine.DataManager, lType string, data pr case utils.MetaTrends: return dm.SetTrendProfile(ctx, data.(*utils.TrendProfile)) case utils.MetaRankings: - return dm.SetRankingProfile(ctx, data.(*engine.RankingProfile)) + return dm.SetRankingProfile(ctx, data.(*utils.RankingProfile)) } return } diff --git a/rankings/apis.go b/rankings/apis.go new file mode 100644 index 000000000..bd822650d --- /dev/null +++ b/rankings/apis.go @@ -0,0 +1,131 @@ +/* +Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments +Copyright (C) ITsysCOM GmbH + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see +*/ + +package rankings + +import ( + "slices" + "strings" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/utils" + "github.com/cgrates/cron" +) + +// V1ScheduleQueries is the query for manually re-/scheduling Ranking Queries +func (rkS *RankingS) V1ScheduleQueries(ctx *context.Context, args *utils.ArgScheduleRankingQueries, scheduled *int) (err error) { + if sched, errSched := rkS.scheduleRankingQueries(ctx, args.Tenant, args.RankingIDs); errSched != nil { + return errSched + } else { + *scheduled = sched + } + return +} + +// V1GetRanking is the API to return the Ranking instance +func (rkS *RankingS) V1GetRanking(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, retRanking *utils.Ranking) (err error) { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + var rk *utils.Ranking + if rk, err = rkS.dm.GetRanking(ctx, arg.Tenant, arg.ID, true, true, utils.NonTransactional); err != nil { + return + } + rk.RLock() + defer rk.RUnlock() + retRanking.Tenant = rk.Tenant // avoid vet complaining for mutex copying + retRanking.ID = rk.ID + retRanking.Metrics = make(map[string]map[string]float64) + for statID, metrics := range rk.Metrics { + retRanking.Metrics[statID] = make(map[string]float64) + for metricID, val := range metrics { + retRanking.Metrics[statID][metricID] = val + } + } + retRanking.LastUpdate = rk.LastUpdate + retRanking.Sorting = rk.Sorting + + retRanking.SortingParameters = make([]string, len(rk.SortingParameters)) + copy(retRanking.SortingParameters, rk.SortingParameters) + + retRanking.SortedStatIDs = make([]string, len(rk.SortedStatIDs)) + copy(retRanking.SortedStatIDs, rk.SortedStatIDs) + return +} + +// V1GetSchedule returns the active schedule for Ranking queries +func (rkS *RankingS) V1GetSchedule(ctx *context.Context, args *utils.ArgScheduledRankings, schedRankings *[]utils.ScheduledRanking) (err error) { + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = rkS.cgrcfg.GeneralCfg().DefaultTenant + } + rkS.crnRQsMux.RLock() + defer rkS.crnRQsMux.RUnlock() + rankingIDsMp, has := rkS.crnRQs[tnt] + if !has { + return utils.ErrNotFound + } + var scheduledRankings []utils.ScheduledRanking + var entryIds map[string]cron.EntryID + if len(args.RankingIDPrefixes) == 0 { + entryIds = rankingIDsMp + } else { + entryIds = make(map[string]cron.EntryID) + for _, rkID := range args.RankingIDPrefixes { + for key, entryID := range rankingIDsMp { + if strings.HasPrefix(key, rkID) { + entryIds[key] = entryID + } + } + } + } + if len(entryIds) == 0 { + return utils.ErrNotFound + } + var entry cron.Entry + for id, entryID := range entryIds { + entry = rkS.crn.Entry(entryID) + if entry.ID == 0 { + continue + } + scheduledRankings = append(scheduledRankings, + utils.ScheduledRanking{ + RankingID: id, + Next: entry.Next, + Previous: entry.Prev, + }) + } + slices.SortFunc(scheduledRankings, func(a, b utils.ScheduledRanking) int { + return a.Next.Compare(b.Next) + }) + *schedRankings = scheduledRankings + return nil +} + +// V1GetRankingSummary returns a summary of ascending/descending stat of the last updated ranking +func (rS *RankingS) V1GetRankingSummary(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.RankingSummary) (err error) { + var rnk *utils.Ranking + if rnk, err = rS.dm.GetRanking(ctx, arg.Tenant, arg.ID, true, true, utils.NonTransactional); err != nil { + return + } + rnk.RLock() + rnkS := rnk.AsRankingSummary() + rnk.RUnlock() + *reply = *rnkS + return +} diff --git a/engine/rankings.go b/rankings/rankings.go similarity index 72% rename from engine/rankings.go rename to rankings/rankings.go index ae1e0d234..f38962f82 100644 --- a/engine/rankings.go +++ b/rankings/rankings.go @@ -16,26 +16,25 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -package engine +package rankings import ( "fmt" "runtime" - "slices" - "strings" "sync" "time" "github.com/cgrates/birpc/context" "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" "github.com/cgrates/cron" ) // NewRankingS is the constructor for RankingS -func NewRankingS(dm *DataManager, - connMgr *ConnManager, - filterS *FilterS, +func NewRankingS(dm *engine.DataManager, + connMgr *engine.ConnManager, + filterS *engine.FilterS, cgrcfg *config.CGRConfig) *RankingS { return &RankingS{ dm: dm, @@ -53,9 +52,9 @@ func NewRankingS(dm *DataManager, // RankingS is responsible of implementing the logic of RankingService type RankingS struct { - dm *DataManager - connMgr *ConnManager - filterS *FilterS + dm *engine.DataManager + connMgr *engine.ConnManager + filterS *engine.FilterS cgrcfg *config.CGRConfig crn *cron.Cron // cron reference @@ -74,7 +73,7 @@ type RankingS struct { // computeRanking will query the stats and build the Ranking for them // // it is to be called by Cron service -func (rkS *RankingS) computeRanking(ctx *context.Context, rkP *RankingProfile) { +func (rkS *RankingS) computeRanking(ctx *context.Context, rkP *utils.RankingProfile) { rk, err := rkS.dm.GetRanking(ctx, rkP.Tenant, rkP.ID, true, true, utils.NonTransactional) if err != nil { utils.Logger.Warning( @@ -83,10 +82,10 @@ func (rkS *RankingS) computeRanking(ctx *context.Context, rkP *RankingProfile) { utils.RankingS, rkP.Tenant, rkP.ID, err.Error())) return } - rk.rMux.Lock() - defer rk.rMux.Unlock() - if rk.rkPrfl == nil { - rk.rkPrfl = rkP + rk.Lock() + defer rk.Unlock() + if rk.Config() == nil { + rk.SetConfig(rkP) } rk.LastUpdate = time.Now() rk.Metrics = make(map[string]map[string]float64) // reset previous values @@ -103,9 +102,9 @@ func (rkS *RankingS) computeRanking(ctx *context.Context, rkP *RankingProfile) { utils.RankingS, rkP.Tenant, rkP.ID, statID, err.Error())) return } - if len(rk.metricIDs) != 0 { + if len(rk.MetricIDs()) != 0 { for metricID := range floatMetrics { - if _, has := rk.metricIDs[statID]; !has { + if _, has := rk.MetricIDs()[statID]; !has { delete(floatMetrics, metricID) } } @@ -118,7 +117,7 @@ func (rkS *RankingS) computeRanking(ctx *context.Context, rkP *RankingProfile) { rk.Metrics[statID][metricID] = val } } - if rk.SortedStatIDs, err = rankingSortStats(rkP.Sorting, + if rk.SortedStatIDs, err = utils.RankingSortStats(rkP.Sorting, rkP.SortingParameters, rk.Metrics); err != nil { utils.Logger.Warning( fmt.Sprintf( @@ -148,7 +147,7 @@ func (rkS *RankingS) computeRanking(ctx *context.Context, rkP *RankingProfile) { } // processThresholds will pass the Ranking event to ThresholdS -func (rkS *RankingS) processThresholds(rk *Ranking) (err error) { +func (rkS *RankingS) processThresholds(rk *utils.Ranking) (err error) { if len(rk.SortedStatIDs) == 0 { return } @@ -159,13 +158,13 @@ func (rkS *RankingS) processThresholds(rk *Ranking) (err error) { utils.MetaEventType: utils.RankingUpdate, } var thIDs []string - if len(rk.rkPrfl.ThresholdIDs) != 0 { - if len(rk.rkPrfl.ThresholdIDs) == 1 && - rk.rkPrfl.ThresholdIDs[0] == utils.MetaNone { + if len(rk.Config().ThresholdIDs) != 0 { + if len(rk.Config().ThresholdIDs) == 1 && + rk.Config().ThresholdIDs[0] == utils.MetaNone { return } - thIDs = make([]string, len(rk.rkPrfl.ThresholdIDs)) - copy(thIDs, rk.rkPrfl.ThresholdIDs) + thIDs = make([]string, len(rk.Config().ThresholdIDs)) + copy(thIDs, rk.Config().ThresholdIDs) } opts[utils.OptsThresholdsProfileIDs] = thIDs sortedStatIDs := make([]string, len(rk.SortedStatIDs)) @@ -196,7 +195,7 @@ func (rkS *RankingS) processThresholds(rk *Ranking) (err error) { } // processEEs will pass the Ranking event to EEs -func (rkS *RankingS) processEEs(rk *Ranking) (err error) { +func (rkS *RankingS) processEEs(rk *utils.Ranking) (err error) { if len(rk.SortedStatIDs) == 0 { return } @@ -234,7 +233,7 @@ func (rkS *RankingS) processEEs(rk *Ranking) (err error) { } // storeTrend will store or schedule the trend based on settings -func (rkS *RankingS) storeRanking(ctx *context.Context, rk *Ranking) (err error) { +func (rkS *RankingS) storeRanking(ctx *context.Context, rk *utils.Ranking) (err error) { if rkS.cgrcfg.RankingSCfg().StoreInterval == 0 { return } @@ -243,7 +242,7 @@ func (rkS *RankingS) storeRanking(ctx *context.Context, rk *Ranking) (err error) } // schedule the asynchronous save, relies for Ranking to be in cache rkS.sRksMux.Lock() - rkS.storedRankings.Add(rk.rkPrfl.TenantID()) + rkS.storedRankings.Add(rk.Config().TenantID()) rkS.sRksMux.Unlock() return } @@ -264,7 +263,7 @@ func (rkS *RankingS) storeRankings(ctx *context.Context) { if rkID == utils.EmptyString { break // no more keys, backup completed } - rkIf, ok := Cache.Get(utils.CacheRankings, rkID) + rkIf, ok := engine.Cache.Get(utils.CacheRankings, rkID) if !ok || rkIf == nil { utils.Logger.Warning( fmt.Sprintf("<%s> failed retrieving from cache Ranking with ID: %q", @@ -272,15 +271,15 @@ func (rkS *RankingS) storeRankings(ctx *context.Context) { failedRkIDs = append(failedRkIDs, rkID) // record failure so we can schedule it for next backup continue } - rk := rkIf.(*Ranking) - rk.rMux.RLock() + rk := rkIf.(*utils.Ranking) + rk.RLock() if err := rkS.dm.SetRanking(ctx, rk); err != nil { utils.Logger.Warning( fmt.Sprintf("<%s> failed storing Trend with ID: %q, err: %q", utils.RankingS, rkID, err)) failedRkIDs = append(failedRkIDs, rkID) // record failure so we can schedule it for next backup } - rk.rMux.RUnlock() + rk.RUnlock() // randomize the CPU load and give up thread control runtime.Gosched() } @@ -431,106 +430,3 @@ func (rkS *RankingS) scheduleRankingQueries(ctx *context.Context, } return } - -// V1ScheduleQueries is the query for manually re-/scheduling Ranking Queries -func (rkS *RankingS) V1ScheduleQueries(ctx *context.Context, args *utils.ArgScheduleRankingQueries, scheduled *int) (err error) { - if sched, errSched := rkS.scheduleRankingQueries(ctx, args.Tenant, args.RankingIDs); errSched != nil { - return errSched - } else { - *scheduled = sched - } - return -} - -// V1GetRanking is the API to return the Ranking instance -func (rkS *RankingS) V1GetRanking(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, retRanking *Ranking) (err error) { - if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing - return utils.NewErrMandatoryIeMissing(missing...) - } - var rk *Ranking - if rk, err = rkS.dm.GetRanking(ctx, arg.Tenant, arg.ID, true, true, utils.NonTransactional); err != nil { - return - } - rk.rMux.RLock() - defer rk.rMux.RUnlock() - retRanking.Tenant = rk.Tenant // avoid vet complaining for mutex copying - retRanking.ID = rk.ID - retRanking.Metrics = make(map[string]map[string]float64) - for statID, metrics := range rk.Metrics { - retRanking.Metrics[statID] = make(map[string]float64) - for metricID, val := range metrics { - retRanking.Metrics[statID][metricID] = val - } - } - retRanking.LastUpdate = rk.LastUpdate - retRanking.Sorting = rk.Sorting - - retRanking.SortingParameters = make([]string, len(rk.SortingParameters)) - copy(retRanking.SortingParameters, rk.SortingParameters) - - retRanking.SortedStatIDs = make([]string, len(rk.SortedStatIDs)) - copy(retRanking.SortedStatIDs, rk.SortedStatIDs) - return -} - -// V1GetSchedule returns the active schedule for Ranking queries -func (rkS *RankingS) V1GetSchedule(ctx *context.Context, args *utils.ArgScheduledRankings, schedRankings *[]utils.ScheduledRanking) (err error) { - tnt := args.Tenant - if tnt == utils.EmptyString { - tnt = rkS.cgrcfg.GeneralCfg().DefaultTenant - } - rkS.crnRQsMux.RLock() - defer rkS.crnRQsMux.RUnlock() - rankingIDsMp, has := rkS.crnRQs[tnt] - if !has { - return utils.ErrNotFound - } - var scheduledRankings []utils.ScheduledRanking - var entryIds map[string]cron.EntryID - if len(args.RankingIDPrefixes) == 0 { - entryIds = rankingIDsMp - } else { - entryIds = make(map[string]cron.EntryID) - for _, rkID := range args.RankingIDPrefixes { - for key, entryID := range rankingIDsMp { - if strings.HasPrefix(key, rkID) { - entryIds[key] = entryID - } - } - } - } - if len(entryIds) == 0 { - return utils.ErrNotFound - } - var entry cron.Entry - for id, entryID := range entryIds { - entry = rkS.crn.Entry(entryID) - if entry.ID == 0 { - continue - } - scheduledRankings = append(scheduledRankings, - utils.ScheduledRanking{ - RankingID: id, - Next: entry.Next, - Previous: entry.Prev, - }) - } - slices.SortFunc(scheduledRankings, func(a, b utils.ScheduledRanking) int { - return a.Next.Compare(b.Next) - }) - *schedRankings = scheduledRankings - return nil -} - -// V1GetRankingSummary returns a summary of ascending/descending stat of the last updated ranking -func (rS *RankingS) V1GetRankingSummary(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *RankingSummary) (err error) { - var rnk *Ranking - if rnk, err = rS.dm.GetRanking(ctx, arg.Tenant, arg.ID, true, true, utils.NonTransactional); err != nil { - return - } - rnk.rMux.RLock() - rnkS := rnk.asRankingSummary() - rnk.rMux.RUnlock() - *reply = *rnkS - return -} diff --git a/apis/rankings_it_test.go b/rankings/rankings_it_test.go similarity index 95% rename from apis/rankings_it_test.go rename to rankings/rankings_it_test.go index f60556eb4..f20846111 100644 --- a/apis/rankings_it_test.go +++ b/rankings/rankings_it_test.go @@ -19,7 +19,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -package apis +package rankings import ( "path" @@ -120,7 +120,7 @@ func testRankingsRPCConn(t *testing.T) { } func testRankingsGetRankingProfileBeforeSet(t *testing.T) { - var replyRankingProfile engine.RankingProfile + var replyRankingProfile utils.RankingProfile if err := raRPC.Call(context.Background(), utils.AdminSv1GetRankingProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{ @@ -132,7 +132,7 @@ func testRankingsGetRankingProfileBeforeSet(t *testing.T) { } func testRankingsGetRankingProfilesBeforeSet(t *testing.T) { - var replyRankingProfiles *[]*engine.RankingProfile + var replyRankingProfiles *[]*utils.RankingProfile if err := raRPC.Call(context.Background(), utils.AdminSv1GetRankingProfiles, &utils.ArgsItemIDs{ Tenant: "cgrates.org", @@ -164,9 +164,9 @@ func testRankingsGetRankingProfileCountBeforeSet(t *testing.T) { } func testRankingsSetRankingProfiles(t *testing.T) { - rankingProfiles := []*engine.RankingProfileWithAPIOpts{ + rankingProfiles := []*utils.RankingProfileWithAPIOpts{ { - RankingProfile: &engine.RankingProfile{ + RankingProfile: &utils.RankingProfile{ ID: "TestA_Ranking1", Tenant: "cgrates.org", @@ -175,7 +175,7 @@ func testRankingsSetRankingProfiles(t *testing.T) { }, }, { - RankingProfile: &engine.RankingProfile{ + RankingProfile: &utils.RankingProfile{ ID: "TestA_Ranking2", Tenant: "cgrates.org", @@ -184,7 +184,7 @@ func testRankingsSetRankingProfiles(t *testing.T) { }, }, { - RankingProfile: &engine.RankingProfile{ + RankingProfile: &utils.RankingProfile{ ID: "TestA_Ranking3", Tenant: "cgrates.org", @@ -193,7 +193,7 @@ func testRankingsSetRankingProfiles(t *testing.T) { }, }, { - RankingProfile: &engine.RankingProfile{ + RankingProfile: &utils.RankingProfile{ ID: "TestB_Ranking1", Tenant: "cgrates.org", Sorting: utils.MetaWeight, @@ -201,7 +201,7 @@ func testRankingsSetRankingProfiles(t *testing.T) { }, }, { - RankingProfile: &engine.RankingProfile{ + RankingProfile: &utils.RankingProfile{ ID: "TestB_Ranking2", Tenant: "cgrates.org", Sorting: utils.MetaWeight, @@ -222,13 +222,13 @@ func testRankingsSetRankingProfiles(t *testing.T) { } func testRankingsGetRankingProfileAfterSet(t *testing.T) { - expectedRankingProfile := engine.RankingProfile{ + expectedRankingProfile := utils.RankingProfile{ ID: "TestA_Ranking1", Tenant: "cgrates.org", Sorting: utils.MetaWeight, SortingParameters: []string{}, } - var replyRankingProfile engine.RankingProfile + var replyRankingProfile utils.RankingProfile if err := raRPC.Call(context.Background(), utils.AdminSv1GetRankingProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{ @@ -319,7 +319,7 @@ func testRankingsGetRankingProfileCountAfterSet(t *testing.T) { } func testRankingsGetRankingProfilesAfterSet(t *testing.T) { - expectedRankingProfiles := []*engine.RankingProfile{ + expectedRankingProfiles := []*utils.RankingProfile{ { ID: "TestA_Ranking1", Tenant: "cgrates.org", @@ -352,7 +352,7 @@ func testRankingsGetRankingProfilesAfterSet(t *testing.T) { SortingParameters: []string{}, }, } - var replyRankingProfiles []*engine.RankingProfile + var replyRankingProfiles []*utils.RankingProfile if err := raRPC.Call(context.Background(), utils.AdminSv1GetRankingProfiles, &utils.ArgsItemIDs{ Tenant: "cgrates.org", @@ -390,7 +390,7 @@ func testRankingsRemoveRankingProfile(t *testing.T) { } func testRankingsGetRankingProfileAfterRemove(t *testing.T) { - var replyRankingProfile engine.RankingProfile + var replyRankingProfile utils.RankingProfile if err := raRPC.Call(context.Background(), utils.AdminSv1GetRankingProfile, &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{ @@ -478,7 +478,7 @@ func testRankingsGetRankingProfileCountAfterRemove(t *testing.T) { } func testRankingsGetRankingProfilesAfterRemove(t *testing.T) { - expectedRankingProfiles := []*engine.RankingProfile{ + expectedRankingProfiles := []*utils.RankingProfile{ { ID: "TestA_Ranking1", Tenant: "cgrates.org", @@ -504,7 +504,7 @@ func testRankingsGetRankingProfilesAfterRemove(t *testing.T) { SortingParameters: []string{}, }, } - var replyRankingProfiles []*engine.RankingProfile + var replyRankingProfiles []*utils.RankingProfile if err := raRPC.Call(context.Background(), utils.AdminSv1GetRankingProfiles, &utils.ArgsItemIDs{ Tenant: "cgrates.org", diff --git a/engine/rankings_test.go b/rankings/rankings_test.go similarity index 81% rename from engine/rankings_test.go rename to rankings/rankings_test.go index af2ca178f..ba730cc42 100644 --- a/engine/rankings_test.go +++ b/rankings/rankings_test.go @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see */ -package engine +package rankings import ( "testing" @@ -24,10 +24,12 @@ import ( "github.com/cgrates/birpc/context" "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" ) func TestTenantID(t *testing.T) { - rp := &RankingProfile{ + rp := &utils.RankingProfile{ Tenant: "cgrates.org", ID: "01", StatIDs: []string{"stat1", "stat2"}, @@ -47,7 +49,7 @@ func TestTenantID(t *testing.T) { } func TestRankingProfileWithAPIOpts(t *testing.T) { - rp := &RankingProfile{ + rp := &utils.RankingProfile{ Tenant: "cgrates.org", ID: "ID", StatIDs: []string{"stat1", "stat2"}, @@ -57,7 +59,7 @@ func TestRankingProfileWithAPIOpts(t *testing.T) { ThresholdIDs: []string{"threshold1"}, } - rpo := RankingProfileWithAPIOpts{ + rpo := utils.RankingProfileWithAPIOpts{ RankingProfile: rp, APIOpts: map[string]any{"option1": "value1"}, } @@ -87,7 +89,7 @@ func TestRankingProfileLockKey(t *testing.T) { } for _, test := range tests { - result := rankingProfileLockKey(test.tenant, test.id) + result := utils.RankingProfileLockKey(test.tenant, test.id) if result != test.expected { t.Errorf("rankingProfileLockKey(%q, %q) = %v; want %v", test.tenant, test.id, result, test.expected) @@ -96,10 +98,10 @@ func TestRankingProfileLockKey(t *testing.T) { } func TestNewRankingService(t *testing.T) { - dm := &DataManager{} + dm := &engine.DataManager{} cgrcfg := &config.CGRConfig{} - filterS := &FilterS{} - connMgr := &ConnManager{} + filterS := &engine.FilterS{} + connMgr := &engine.ConnManager{} rankingService := NewRankingS(dm, connMgr, filterS, cgrcfg) @@ -126,22 +128,21 @@ func TestNewRankingService(t *testing.T) { func TestStoreRanking(t *testing.T) { cfg := config.NewDefaultCGRConfig() - dataDB := NewInternalDB([]string{}, []string{}, map[string]*config.ItemOpts{}) - dm := NewDataManager(dataDB, cfg, nil) + dataDB := engine.NewInternalDB([]string{}, []string{}, map[string]*config.ItemOpts{}) + dm := engine.NewDataManager(dataDB, cfg, nil) rkg := NewRankingS(dm, nil, nil, cfg) - ranking := &Ranking{ - rkPrfl: &RankingProfile{ - Tenant: "cgrates.org", - ID: "ID1", - Schedule: "@every 1s", - StatIDs: []string{"stat1", "stat2"}, - MetricIDs: []string{"metric1", "metric2"}, - Sorting: "asc", - SortingParameters: []string{"metric1:true"}, - Stored: true, - ThresholdIDs: []string{"threshold1"}, - }, - } + ranking := &utils.Ranking{} + ranking.SetConfig(&utils.RankingProfile{ + Tenant: "cgrates.org", + ID: "ID1", + Schedule: "@every 1s", + StatIDs: []string{"stat1", "stat2"}, + MetricIDs: []string{"metric1", "metric2"}, + Sorting: "asc", + SortingParameters: []string{"metric1:true"}, + Stored: true, + ThresholdIDs: []string{"threshold1"}, + }) ctx := context.Background() cfg.RankingSCfg().StoreInterval = 0 if err := rkg.storeRanking(ctx, ranking); err != nil { diff --git a/services/rankings.go b/services/rankings.go index 3c5217c82..86cd380d6 100644 --- a/services/rankings.go +++ b/services/rankings.go @@ -25,6 +25,7 @@ import ( "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/rankings" "github.com/cgrates/cgrates/servmanager" "github.com/cgrates/cgrates/utils" ) @@ -40,7 +41,7 @@ func NewRankingService(cfg *config.CGRConfig) *RankingService { type RankingService struct { mu sync.RWMutex cfg *config.CGRConfig - ran *engine.RankingS + ran *rankings.RankingS stateDeps *StateDependencies // channel subscriptions for state changes } @@ -71,7 +72,7 @@ func (ran *RankingService) Start(shutdown *utils.SyncedChan, registry *servmanag ran.mu.Lock() defer ran.mu.Unlock() - ran.ran = engine.NewRankingS(dbs.DataManager(), cms.ConnManager(), fs.FilterS(), ran.cfg) + ran.ran = rankings.NewRankingS(dbs.DataManager(), cms.ConnManager(), fs.FilterS(), ran.cfg) if err := ran.ran.StartRankingS(context.TODO()); err != nil { return err } diff --git a/tpes/tpe_rankings.go b/tpes/tpe_rankings.go index cf2cdd4af..011be23ba 100644 --- a/tpes/tpe_rankings.go +++ b/tpes/tpe_rankings.go @@ -49,7 +49,7 @@ func (tpSts TPRankings) exportItems(ctx *context.Context, wrtr io.Writer, tnt st return } for _, rankingsID := range itmIDs { - var rankingPrf *engine.RankingProfile + var rankingPrf *utils.RankingProfile rankingPrf, err = tpSts.dm.GetRankingProfile(ctx, tnt, rankingsID, true, true, utils.NonTransactional) if err != nil { if err.Error() == utils.ErrNotFound.Error() { diff --git a/engine/librankings.go b/utils/rankings.go similarity index 79% rename from engine/librankings.go rename to utils/rankings.go index 669c63700..361c4335b 100644 --- a/engine/librankings.go +++ b/utils/rankings.go @@ -15,15 +15,13 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ -package engine +package utils import ( "sort" "strings" "sync" "time" - - "github.com/cgrates/cgrates/utils" ) type RankingProfileWithAPIOpts struct { @@ -44,7 +42,7 @@ type RankingProfile struct { } func (sgp *RankingProfile) TenantID() string { - return utils.ConcatenatedKey(sgp.Tenant, sgp.ID) + return ConcatenatedKey(sgp.Tenant, sgp.ID) } // Clone will clone a RankingProfile @@ -75,9 +73,9 @@ func (rkP *RankingProfile) Clone() (cln *RankingProfile) { return } -// rankingProfileLockKey returns the ID used to lock a RankingProfile with guardian -func rankingProfileLockKey(tnt, id string) string { - return utils.ConcatenatedKey(utils.CacheRankingProfiles, tnt, id) +// RankingProfileLockKey returns the ID used to lock a RankingProfile with guardian +func RankingProfileLockKey(tnt, id string) string { + return ConcatenatedKey(CacheRankingProfiles, tnt, id) } func NewRankingFromProfile(rkP *RankingProfile) (rk *Ranking) { rk = &Ranking{ @@ -87,7 +85,7 @@ func NewRankingFromProfile(rkP *RankingProfile) (rk *Ranking) { Metrics: make(map[string]map[string]float64), rkPrfl: rkP, - metricIDs: utils.NewStringSet(rkP.MetricIDs), + metricIDs: NewStringSet(rkP.MetricIDs), } if rkP.SortingParameters != nil { rk.SortingParameters = make([]string, len(rkP.SortingParameters)) @@ -115,16 +113,16 @@ type Ranking struct { SortedStatIDs []string rkPrfl *RankingProfile // store here the ranking profile so we can have it at hands further - metricIDs utils.StringSet // convert the metricIDs here for faster matching + metricIDs StringSet // convert the metricIDs here for faster matching } func (r *Ranking) TenantID() string { - return utils.ConcatenatedKey(r.Tenant, r.ID) + return ConcatenatedKey(r.Tenant, r.ID) } -// asRankingSummary converts the Ranking instance into a RankingSummary one -func (rk *Ranking) asRankingSummary() (rkSm *RankingSummary) { +// AsRankingSummary converts the Ranking instance into a RankingSummary one +func (rk *Ranking) AsRankingSummary() (rkSm *RankingSummary) { rkSm = &RankingSummary{ Tenant: rk.Tenant, ID: rk.ID, @@ -139,8 +137,8 @@ type rankingSorter interface { sortStatIDs() []string // sortStatIDs returns the sorted list of statIDs } -// rankingSortStats will return the list of sorted statIDs out of the sortingData map -func rankingSortStats(sortingType string, sortingParams []string, +// RankingSortStats will return the list of sorted statIDs out of the sortingData map +func RankingSortStats(sortingType string, sortingParams []string, Metrics map[string]map[string]float64) (sortedStatIDs []string, err error) { var rnkSrtr rankingSorter if rnkSrtr, err = newRankingSorter(sortingType, sortingParams, Metrics); err != nil { @@ -156,11 +154,11 @@ func newRankingSorter(sortingType string, sortingParams []string, Metrics map[string]map[string]float64) (rkStr rankingSorter, err error) { switch sortingType { default: - err = utils.ErrPrefixNotErrNotImplemented(sortingType) + err = ErrPrefixNotErrNotImplemented(sortingType) return - case utils.MetaDesc: + case MetaDesc: return newRankingDescSorter(sortingParams, Metrics), nil - case utils.MetaAsc: + case MetaAsc: return newRankingAscSorter(sortingParams, Metrics), nil } } @@ -169,11 +167,11 @@ func newRankingSorter(sortingType string, sortingParams []string, func newRankingDescSorter(sortingParams []string, Metrics map[string]map[string]float64) (rkDsrtr *rankingDescSorter) { clnSp := make([]string, len(sortingParams)) - sPReversed := make(utils.StringSet) + sPReversed := make(StringSet) for i, sP := range sortingParams { // clean the sortingParams, out of param:false or param:true definitions - sPSlc := strings.Split(sP, utils.InInFieldSep) + sPSlc := strings.Split(sP, InInFieldSep) clnSp[i] = sPSlc[0] - if len(sPSlc) > 1 && sPSlc[1] == utils.FalseStr { + if len(sPSlc) > 1 && sPSlc[1] == FalseStr { sPReversed.Add(sPSlc[0]) // param defined as param:false which should be added to reversing comparison } } @@ -191,7 +189,7 @@ func newRankingDescSorter(sortingParams []string, // rankingDescSorter will sort data descendent for metrics in sortingParams or random if all equal type rankingDescSorter struct { sMetricIDs []string - sMetricRev utils.StringSet // list of exceptios for sortingParams, reverting the sorting logic + sMetricRev StringSet // list of exceptios for sortingParams, reverting the sorting logic Metrics map[string]map[string]float64 statIDs []string // list of keys of the Metrics @@ -226,7 +224,7 @@ func (rkDsrtr *rankingDescSorter) sortStatIDs() []string { return ret } //in case that we have the same value for all params we return randomly - return utils.BoolGenerator().RandomBool() + return BoolGenerator().RandomBool() }) return rkDsrtr.statIDs } @@ -235,11 +233,11 @@ func (rkDsrtr *rankingDescSorter) sortStatIDs() []string { func newRankingAscSorter(sortingParams []string, Metrics map[string]map[string]float64) (rkASrtr *rankingAscSorter) { clnSp := make([]string, len(sortingParams)) - sPReversed := make(utils.StringSet) + sPReversed := make(StringSet) for i, sP := range sortingParams { // clean the sortingParams, out of param:false or param:true definitions - sPSlc := strings.Split(sP, utils.InInFieldSep) + sPSlc := strings.Split(sP, InInFieldSep) clnSp[i] = sPSlc[0] - if len(sPSlc) > 1 && sPSlc[1] == utils.FalseStr { + if len(sPSlc) > 1 && sPSlc[1] == FalseStr { sPReversed.Add(sPSlc[0]) // param defined as param:false which should be added to reversing comparison } } @@ -257,7 +255,7 @@ func newRankingAscSorter(sortingParams []string, // rankingAscSorter will sort data ascendent for metrics in sortingParams or randomly if all equal type rankingAscSorter struct { sMetricIDs []string - sMetricRev utils.StringSet // list of exceptios for sortingParams, reverting the sorting logic + sMetricRev StringSet // list of exceptios for sortingParams, reverting the sorting logic Metrics map[string]map[string]float64 statIDs []string // list of keys of the Metrics @@ -292,7 +290,7 @@ func (rkASrtr *rankingAscSorter) sortStatIDs() []string { return ret } //in case that we have the same value for all params we return randomly - return utils.BoolGenerator().RandomBool() + return BoolGenerator().RandomBool() }) return rkASrtr.statIDs } @@ -307,37 +305,37 @@ type RankingSummary struct { func (tp *RankingProfile) Set(path []string, val any, _ bool) (err error) { if len(path) != 1 { - return utils.ErrWrongPath + return ErrWrongPath } switch path[0] { default: - return utils.ErrWrongPath - case utils.Tenant: - tp.Tenant = utils.IfaceAsString(val) - case utils.ID: - tp.ID = utils.IfaceAsString(val) - case utils.Schedule: - tp.Schedule = utils.IfaceAsString(val) - case utils.StatIDs: + return ErrWrongPath + case Tenant: + tp.Tenant = IfaceAsString(val) + case ID: + tp.ID = IfaceAsString(val) + case Schedule: + tp.Schedule = IfaceAsString(val) + case StatIDs: var valA []string - valA, err = utils.IfaceAsStringSlice(val) + valA, err = IfaceAsStringSlice(val) tp.StatIDs = append(tp.StatIDs, valA...) - case utils.MetricIDs: + case MetricIDs: var valA []string - valA, err = utils.IfaceAsStringSlice(val) + valA, err = IfaceAsStringSlice(val) tp.MetricIDs = append(tp.MetricIDs, valA...) - case utils.Sorting: - tp.Sorting = utils.IfaceAsString(val) - case utils.SortingParameters: + case Sorting: + tp.Sorting = IfaceAsString(val) + case SortingParameters: var valA []string - valA, err = utils.IfaceAsStringSlice(val) + valA, err = IfaceAsStringSlice(val) tp.SortingParameters = append(tp.SortingParameters, valA...) - case utils.Stored: - tp.Stored, err = utils.IfaceAsBool(val) - case utils.ThresholdIDs: + case Stored: + tp.Stored, err = IfaceAsBool(val) + case ThresholdIDs: var valA []string - valA, err = utils.IfaceAsStringSlice(val) + valA, err = IfaceAsStringSlice(val) tp.ThresholdIDs = append(tp.ThresholdIDs, valA...) } return @@ -366,51 +364,84 @@ func (tp *RankingProfile) Merge(v2 any) { } } -func (tp *RankingProfile) String() string { return utils.ToJSON(tp) } +func (tp *RankingProfile) String() string { return ToJSON(tp) } func (tp *RankingProfile) FieldAsString(fldPath []string) (_ string, err error) { var val any if val, err = tp.FieldAsInterface(fldPath); err != nil { return } - return utils.IfaceAsString(val), nil + return IfaceAsString(val), nil } func (tp *RankingProfile) FieldAsInterface(fldPath []string) (_ any, err error) { if len(fldPath) != 1 { - return nil, utils.ErrNotFound + return nil, ErrNotFound } switch fldPath[0] { default: - fld, idx := utils.GetPathIndex(fldPath[0]) + fld, idx := GetPathIndex(fldPath[0]) if idx != nil { switch fld { - case utils.StatIDs: + case StatIDs: if *idx < len(tp.StatIDs) { return tp.StatIDs[*idx], nil } - case utils.MetricIDs: + case MetricIDs: if *idx < len(tp.MetricIDs) { return tp.MetricIDs[*idx], nil } - case utils.SortingParameters: + case SortingParameters: if *idx < len(tp.SortingParameters) { return tp.SortingParameters[*idx], nil } - case utils.ThresholdIDs: + case ThresholdIDs: if *idx < len(tp.ThresholdIDs) { return tp.ThresholdIDs[*idx], nil } } } - return nil, utils.ErrNotFound - case utils.Tenant: + return nil, ErrNotFound + case Tenant: return tp.Tenant, nil - case utils.ID: + case ID: return tp.ID, nil - case utils.Schedule: + case Schedule: return tp.Schedule, nil - case utils.Sorting: + case Sorting: return tp.Sorting, nil - case utils.Stored: + case Stored: return tp.Stored, nil } } + +// Config returns the ranking's profile configuration. +func (r *Ranking) Config() *RankingProfile { + return r.rkPrfl +} + +func (t *Ranking) SetConfig(rp *RankingProfile) { + t.rkPrfl = rp +} + +// Lock locks the ranking mutex. +func (r *Ranking) Lock() { + r.rMux.Lock() +} + +// Unlock unlocks the ranking mutex. +func (r *Ranking) Unlock() { + r.rMux.Unlock() +} + +// RLock locks the ranking mutex for reading. +func (r *Ranking) RLock() { + r.rMux.RLock() +} + +// RUnlock unlocks the read lock on the ranking mutex. +func (r *Ranking) RUnlock() { + r.rMux.RUnlock() +} + +func (r *Ranking) MetricIDs() StringSet { + return r.metricIDs +} diff --git a/engine/librankings_test.go b/utils/rankings_test.go similarity index 90% rename from engine/librankings_test.go rename to utils/rankings_test.go index 6fd47fac2..198462ce6 100644 --- a/engine/librankings_test.go +++ b/utils/rankings_test.go @@ -15,15 +15,13 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see */ -package engine +package utils import ( "encoding/json" "reflect" "testing" "time" - - "github.com/cgrates/cgrates/utils" ) func TestRankingProfileTenantID(t *testing.T) { @@ -112,7 +110,7 @@ func TestNewRankingFromProfile(t *testing.T) { t.Error("Expected Metrics map to be initialized, but it is nil") } - expectedMetricIDs := utils.NewStringSet(profile.MetricIDs) + expectedMetricIDs := NewStringSet(profile.MetricIDs) if !ranking.metricIDs.Equals(expectedMetricIDs) { t.Errorf("Expected metricIDs %v, got %v", expectedMetricIDs, ranking.metricIDs) } @@ -151,7 +149,7 @@ func TestRanking_asRankingSummary(t *testing.T) { SortedStatIDs: []string{"stat1", "stat2", "stat3"}, } - rkSummary := rk.asRankingSummary() + rkSummary := rk.AsRankingSummary() if rkSummary.Tenant != rk.Tenant { t.Errorf("Expected Tenant %s, but got %s", rk.Tenant, rkSummary.Tenant) @@ -196,14 +194,14 @@ func TestRankingSortStats(t *testing.T) { }{ { name: "Sort Descending by metric1", - sortingType: utils.MetaDesc, + sortingType: MetaDesc, sortingParams: []string{"metric1"}, expectedOrder: []string{"stat3", "stat1", "stat2"}, expectError: false, }, { name: "Sort Ascending by metric2", - sortingType: utils.MetaAsc, + sortingType: MetaAsc, sortingParams: []string{"metric2"}, expectedOrder: []string{"stat3", "stat1", "stat2"}, expectError: false, @@ -219,7 +217,7 @@ func TestRankingSortStats(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - sortedStatIDs, err := rankingSortStats(tt.sortingType, tt.sortingParams, metrics) + sortedStatIDs, err := RankingSortStats(tt.sortingType, tt.sortingParams, metrics) if tt.expectError { if err == nil { t.Errorf("expected error but got nil") @@ -327,20 +325,20 @@ func TestRankingProfileFieldAsString(t *testing.T) { err error val any }{ - {utils.ID, []string{utils.ID}, nil, "RP1"}, - {utils.Tenant, []string{utils.Tenant}, nil, "cgrates.org"}, - {utils.Schedule, []string{utils.Schedule}, nil, "@every 2s"}, - {utils.StatIDs, []string{utils.StatIDs + "[0]"}, nil, "Stat1"}, - {utils.StatIDs, []string{utils.StatIDs + "[1]"}, nil, "Stat2"}, - {utils.MetricIDs, []string{utils.MetricIDs + "[0]"}, nil, "*tcc"}, - {utils.MetricIDs, []string{utils.MetricIDs + "[1]"}, nil, "*acc"}, - {utils.Sorting, []string{utils.Sorting}, nil, "*asc"}, - {utils.Stored, []string{utils.Stored}, nil, false}, - {utils.SortingParameters, []string{utils.SortingParameters + "[0]"}, nil, "*acc"}, - {utils.SortingParameters, []string{utils.SortingParameters + "[1]"}, nil, "*pdd:false"}, - {utils.ThresholdIDs, []string{utils.ThresholdIDs + "[0]"}, nil, "Threshold1"}, - {utils.ThresholdIDs, []string{utils.ThresholdIDs + "[1]"}, nil, "Threshold2"}, - {"NonExistingField", []string{"Field1"}, utils.ErrNotFound, nil}, + {ID, []string{ID}, nil, "RP1"}, + {Tenant, []string{Tenant}, nil, "cgrates.org"}, + {Schedule, []string{Schedule}, nil, "@every 2s"}, + {StatIDs, []string{StatIDs + "[0]"}, nil, "Stat1"}, + {StatIDs, []string{StatIDs + "[1]"}, nil, "Stat2"}, + {MetricIDs, []string{MetricIDs + "[0]"}, nil, "*tcc"}, + {MetricIDs, []string{MetricIDs + "[1]"}, nil, "*acc"}, + {Sorting, []string{Sorting}, nil, "*asc"}, + {Stored, []string{Stored}, nil, false}, + {SortingParameters, []string{SortingParameters + "[0]"}, nil, "*acc"}, + {SortingParameters, []string{SortingParameters + "[1]"}, nil, "*pdd:false"}, + {ThresholdIDs, []string{ThresholdIDs + "[0]"}, nil, "Threshold1"}, + {ThresholdIDs, []string{ThresholdIDs + "[1]"}, nil, "Threshold2"}, + {"NonExistingField", []string{"Field1"}, ErrNotFound, nil}, } rp := &RankingProfile{ Tenant: "cgrates.org", @@ -390,13 +388,13 @@ func TestNewRankingSorter(t *testing.T) { expectSorterType string }{ { - sortingType: utils.MetaAsc, + sortingType: MetaAsc, sortingParams: []string{"*acc"}, expectErr: false, expectSorterType: "RankingAscSorter", }, { - sortingType: utils.MetaDesc, + sortingType: MetaDesc, sortingParams: []string{"*tcc"}, expectErr: false, expectSorterType: "RankingDescSorter", @@ -421,11 +419,11 @@ func TestNewRankingSorter(t *testing.T) { t.Errorf("Did not expect an error for sorting type %q, but got: %v", test.sortingType, err) } switch test.sortingType { - case utils.MetaAsc: + case MetaAsc: if _, ok := rkSorter.(*rankingAscSorter); !ok { t.Errorf("Expected sorter type 'rankingAscSorter', but got %T", rkSorter) } - case utils.MetaDesc: + case MetaDesc: if _, ok := rkSorter.(*rankingDescSorter); !ok { t.Errorf("Expected sorter type 'rankingDescSorter', but got %T", rkSorter) } @@ -444,63 +442,63 @@ func TestRankingProfileSet(t *testing.T) { }{ { name: "Set Tenant", - path: []string{utils.Tenant}, + path: []string{Tenant}, val: "cgrates.org", expectedErr: nil, expectedRP: RankingProfile{Tenant: "cgrates.org"}, }, { name: "Set ID", - path: []string{utils.ID}, + path: []string{ID}, val: "profile1", expectedErr: nil, expectedRP: RankingProfile{ID: "profile1"}, }, { name: "Set Schedule", - path: []string{utils.Schedule}, + path: []string{Schedule}, val: "0 0 * * *", expectedErr: nil, expectedRP: RankingProfile{Schedule: "0 0 * * *"}, }, { name: "Set StatIDs", - path: []string{utils.StatIDs}, + path: []string{StatIDs}, val: []string{"stat1", "stat2"}, expectedErr: nil, expectedRP: RankingProfile{StatIDs: []string{"stat1", "stat2"}}, }, { name: "Set MetricIDs", - path: []string{utils.MetricIDs}, + path: []string{MetricIDs}, val: []string{"metric1", "metric2"}, expectedErr: nil, expectedRP: RankingProfile{MetricIDs: []string{"metric1", "metric2"}}, }, { name: "Set Sorting", - path: []string{utils.Sorting}, + path: []string{Sorting}, val: "asc", expectedErr: nil, expectedRP: RankingProfile{Sorting: "asc"}, }, { name: "Set SortingParameters", - path: []string{utils.SortingParameters}, + path: []string{SortingParameters}, val: []string{"param1", "param2"}, expectedErr: nil, expectedRP: RankingProfile{SortingParameters: []string{"param1", "param2"}}, }, { name: "Set Stored", - path: []string{utils.Stored}, + path: []string{Stored}, val: true, expectedErr: nil, expectedRP: RankingProfile{Stored: true}, }, { name: "Set ThresholdIDs", - path: []string{utils.ThresholdIDs}, + path: []string{ThresholdIDs}, val: []string{"threshold1", "threshold2"}, expectedErr: nil, expectedRP: RankingProfile{ThresholdIDs: []string{"threshold1", "threshold2"}}, @@ -509,14 +507,14 @@ func TestRankingProfileSet(t *testing.T) { name: "Wrong path", path: []string{"wrongpath"}, val: "value", - expectedErr: utils.ErrWrongPath, + expectedErr: ErrWrongPath, expectedRP: RankingProfile{}, }, { name: "Empty path", path: []string{}, val: "value", - expectedErr: utils.ErrWrongPath, + expectedErr: ErrWrongPath, expectedRP: RankingProfile{}, }, } @@ -604,13 +602,13 @@ func TestRankingProfileStringJson(t *testing.T) { t.Run(tt.name, func(t *testing.T) { result := tt.rp.String() - var resultMap map[string]interface{} + var resultMap map[string]any err := json.Unmarshal([]byte(result), &resultMap) if err != nil { t.Errorf("Error unmarshalling result: %v", err) } - expectedMap := map[string]interface{}{} + expectedMap := map[string]any{} err = json.Unmarshal([]byte(tt.expectedJSON), &expectedMap) if err != nil { t.Errorf("Error unmarshalling expected JSON: %v", err) @@ -618,8 +616,8 @@ func TestRankingProfileStringJson(t *testing.T) { for key, value1 := range resultMap { if value2, exists := expectedMap[key]; exists { - if value1Slice, ok1 := value1.([]interface{}); ok1 { - if value2Slice, ok2 := value2.([]interface{}); ok2 { + if value1Slice, ok1 := value1.([]any); ok1 { + if value2Slice, ok2 := value2.([]any); ok2 { if len(value1Slice) != len(value2Slice) { t.Errorf("Test %s failed: slice length mismatch for key %s", tt.name, key) }